Compare commits

...

11 Commits

Author SHA1 Message Date
esyr 3c44a9c36c
Merge e348012e99 into e08b83cbb3 2025-07-29 17:06:26 +02:00
Eugene Syromiatnikov e348012e99 util/analyze-contention-log.sh: print status output to stderr
Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
2025-07-16 13:15:02 +02:00
Eugene Syromiatnikov 0154dc12a5 crypto/threads_pthread: rewrite contention data storage to per-tid
This eliminates locking during writing out of the lock contation report
data, which claws back some of the lost performance degradation imposed
by the lock contention reporting instrumentation:

    [Without -DREPORT_RWLOCK_CONTENTION]
    ~/dev/perftools/source$ ./evp_fetch 100
    Average time per fetch call: 4.502162us
    ~/dev/perftools/source$ ./evp_fetch 200
    Average time per fetch call: 8.224920us

    [Before]
    ~/dev/perftools/source$ ./evp_fetch 100
    Average time per fetch call: 13.079795us
    ~/dev/perftools/source$ ./evp_fetch 200
    Average time per fetch call: 23.420235us

    [After]
    ~/dev/perftools/source$ ./evp_fetch 100
    Average time per fetch call: 6.557428us
    ~/dev/perftools/source$ ./evp_fetch 200
    Average time per fetch call: 13.415148us

The downside is that it produces a file for each TID, which floods
the working directory with debug files, but that mich be an acceptable
trade-off.
2025-07-16 13:15:02 +02:00
Eugene Syromiatnikov db6565e5b1 util/analyze-contention-log: call the bash interpreter with -eu flags
Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
2025-07-16 13:11:51 +02:00
Eugene Syromiatnikov 707a966f70 crypto/threads_lock_contention: factor out lock contention recording
Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
2025-07-16 13:11:51 +02:00
Eugene Syromiatnikov a4d9183593 crypto/threads_lock_contention: factor out obtaining the stack traces data pointer
It also drops the premature initalisation of it in
ossl_init_rwlock_contention_data(), deferring it to on-demand one
in ossl_rwlock_{rd,wr}lock(), which seems to shave some of the incurred
overhead:

    [Before]
    ~/dev/perftools/source$ ./evp_fetch 100
    Average time per fetch call: 16.944004us
    ~/dev/perftools/source$ ./evp_fetch 200
    Average time per fetch call: 26.325767us

    [After]
    ~/dev/perftools/source$ ./evp_fetch 100
    Average time per fetch call: 13.079795us
    ~/dev/perftools/source$ ./evp_fetch 200
    Average time per fetch call: 23.420235us

Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
2025-07-16 13:11:49 +02:00
Eugene Syromiatnikov d4710397a9 crypto/threads_lock_contention: condition file suffix on FIPS_MODULE and not fopen() call
Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
2025-07-16 13:11:18 +02:00
Eugene Syromiatnikov eb8547c65f crypto/threads_lock_contention: typo: s/stack_info/stack_traces/ in ossl_init_rwlock_contention_data
Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
2025-07-16 13:10:54 +02:00
Eugene Syromiatnikov efee781894 crypto/threads_lock_contention: Remove duplicating code
Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
2025-07-16 13:10:54 +02:00
Eugene Syromiatnikov 849dd3bfad Factor out the lock contention reporting facility implementation
Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
2025-07-16 13:10:52 +02:00
Eugene Syromiatnikov b3365c5f9c Move BROKEN_CLANG_ATOMICS definition to threads_common.h
In anticipation of usage in different header.

Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
2025-07-16 12:35:25 +02:00
6 changed files with 355 additions and 259 deletions

View File

@ -97,10 +97,10 @@ SOURCE[../providers/libfips.a]=$CORE_COMMON
# Central utilities
$UTIL_COMMON=\
cryptlib.c params.c params_from_text.c bsearch.c ex_data.c o_str.c \
threads_pthread.c threads_win.c threads_none.c threads_common.c \
initthread.c context.c sparse_array.c asn1_dsa.c packet.c \
param_build.c param_build_set.c der_writer.c threads_lib.c \
params_dup.c time.c
threads_lock_contention.c threads_pthread.c threads_win.c \
threads_none.c threads_common.c initthread.c context.c sparse_array.c \
asn1_dsa.c packet.c param_build.c param_build_set.c der_writer.c \
threads_lib.c params_dup.c time.c
SOURCE[../libcrypto]=$UTIL_COMMON \
mem.c mem_sec.c \

View File

@ -0,0 +1,254 @@
/*
* Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include "internal/common.h"
#include "internal/threads_lock_contention.h"
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) && \
!defined(OPENSSL_SYS_WINDOWS) && defined(REPORT_RWLOCK_CONTENTION)
# define _GNU_SOURCE
# include <assert.h>
# include <execinfo.h>
# include <fcntl.h>
# include <stdbool.h>
# include <unistd.h>
# include <sys/syscall.h>
# include <sys/uio.h>
# include "internal/time.h"
# define BT_BUF_SIZE 1024
/*
* Normally we would use a BIO here to do this, but we create locks during
* library initialization, and creating a bio too early, creates a recursive set
* of stack calls that leads us to call CRYPTO_thread_run_once while currently
* executing the init routine for various run_once functions, which leads to
* deadlock. Avoid that by just using a FILE pointer. Also note that we
* directly use a pthread_mutex_t to protect access from multiple threads
* to the contention log file. We do this because we want to avoid use
* of the CRYPTO_THREAD api so as to prevent recursive blocking reports.
*/
static CRYPTO_ONCE init_contention_data_flag = CRYPTO_ONCE_STATIC_INIT;
pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
CRYPTO_THREAD_LOCAL thread_contention_data;
struct stack_info {
unsigned int nptrs;
int write;
OSSL_TIME start;
OSSL_TIME duration;
char **strings;
};
# define STACKS_COUNT 32
struct stack_traces {
int fd;
int lock_depth;
size_t idx;
struct stack_info stacks[STACKS_COUNT];
};
/* The glibc gettid() definition presents only since 2.30. */
static pid_t get_tid()
{
return syscall(SYS_gettid);
}
# ifdef FIPS_MODULE
# define FIPS_SFX "-fips"
# else
# define FIPS_SFX ""
# endif
static void *init_contention_data(void)
{
struct stack_traces *traces;
char fname_fmt[] = "lock-contention-log" FIPS_SFX ".%d.txt";
char fname[sizeof(fname_fmt) + sizeof(int) * 3];
traces = OPENSSL_zalloc(sizeof(struct stack_traces));
snprintf(fname, sizeof(fname), fname_fmt, get_tid());
traces->fd = open(fname, O_WRONLY | O_APPEND | O_CLOEXEC | O_CREAT, 0600);
return traces;
}
static void destroy_contention_data(void *data)
{
struct stack_traces *st = data;
close(st->fd);
OPENSSL_free(data);
}
static void init_contention_data_once(void)
{
/*
* Create a thread local key here to store our list of stack traces
* to be printed when we unlock the lock we are holding
*/
CRYPTO_THREAD_init_local(&thread_contention_data, destroy_contention_data);
return;
}
static struct stack_traces *get_stack_traces(bool init)
{
struct stack_traces *traces = CRYPTO_THREAD_get_local(&thread_contention_data);
if (!traces && init) {
traces = init_contention_data();
CRYPTO_THREAD_set_local(&thread_contention_data, traces);
}
return traces;
}
static void print_stack_traces(struct stack_traces *traces)
{
unsigned int j;
struct iovec *iov;
int iovcnt;
while (traces != NULL && traces->idx >= 1) {
traces->idx--;
dprintf(traces->fd,
"lock blocked on %s for %zu usec at time %zu tid %d\n",
traces->stacks[traces->idx].write == 1 ? "WRITE" : "READ",
ossl_time2us(traces->stacks[traces->idx].duration),
ossl_time2us(traces->stacks[traces->idx].start),
get_tid());
if (traces->stacks[traces->idx].strings != NULL) {
static const char lf = '\n';
iovcnt = traces->stacks[traces->idx].nptrs * 2 + 1;
iov = alloca(iovcnt * sizeof(*iov));
for (j = 0; j < traces->stacks[traces->idx].nptrs; j++) {
iov[2 * j].iov_base = traces->stacks[traces->idx].strings[j];
iov[2 * j].iov_len = strlen(traces->stacks[traces->idx].strings[j]);
iov[2 * j + 1].iov_base = (char *) &lf;
iov[2 * j + 1].iov_len = 1;
}
iov[traces->stacks[traces->idx].nptrs * 2].iov_base = (char *) &lf;
iov[traces->stacks[traces->idx].nptrs * 2].iov_len = 1;
} else {
static const char no_bt[] = "No stack trace available\n\n";
iovcnt = 1;
iov = alloca(iovcnt * sizeof(*iov));
iov[0].iov_base = (char *) no_bt;
iov[0].iov_len = sizeof(no_bt) - 1;
}
writev(traces->fd, iov, iovcnt);
free(traces->stacks[traces->idx].strings);
}
}
void ossl_init_rwlock_contention_data(void)
{
CRYPTO_THREAD_run_once(&init_contention_data_flag, init_contention_data_once);
}
void ossl_free_rwlock_contention_data(void)
{
}
static int record_lock_contention(pthread_rwlock_t *lock,
struct stack_traces *traces, bool write)
{
void *buffer[BT_BUF_SIZE];
OSSL_TIME start, end;
int ret;
start = ossl_time_now();
ret = (write ? pthread_rwlock_wrlock : pthread_rwlock_rdlock)(lock);
if (ret)
return ret;
end = ossl_time_now();
traces->stacks[traces->idx].nptrs = backtrace(buffer, BT_BUF_SIZE);
traces->stacks[traces->idx].strings = backtrace_symbols(buffer,
traces->stacks[traces->idx].nptrs);
traces->stacks[traces->idx].duration = ossl_time_subtract(end, start);
traces->stacks[traces->idx].start = start;
traces->stacks[traces->idx].write = write;
traces->idx++;
if (traces->idx >= STACKS_COUNT) {
fprintf(stderr, "STACK RECORD OVERFLOW!\n");
print_stack_traces(traces);
}
return 0;
}
int ossl_rwlock_rdlock(pthread_rwlock_t *lock)
{
struct stack_traces *traces = get_stack_traces(true);
if (ossl_unlikely(traces == NULL))
return 1;
traces->lock_depth++;
if (pthread_rwlock_tryrdlock(lock)) {
int ret = record_lock_contention(lock, traces, false);
if (ret)
traces->lock_depth--;
return ret;
}
return 0;
}
int ossl_rwlock_wrlock(pthread_rwlock_t *lock)
{
struct stack_traces *traces = get_stack_traces(true);
if (ossl_unlikely(traces == NULL))
return 1;
traces->lock_depth++;
if (pthread_rwlock_trywrlock(lock)) {
int ret = record_lock_contention(lock, traces, true);
if (ret)
traces->lock_depth--;
return ret;
}
return 0;
}
int ossl_rwlock_unlock(pthread_rwlock_t *lock)
{
int ret;
ret = pthread_rwlock_unlock(lock);
if (ret)
return ret;
{
struct stack_traces *traces = get_stack_traces(false);
if (traces != NULL) {
traces->lock_depth--;
assert(traces->lock_depth >= 0);
if (traces->lock_depth == 0)
print_stack_traces(traces);
}
}
return 0;
}
#endif /* OPENSSL_THREADS && !CRYPTO_TDEBUG && !OPENSSL_SYS_WINDOWS && REPORT_RWLOCK_CONTENTION */

View File

@ -10,36 +10,13 @@
/* We need to use the OPENSSL_fork_*() deprecated APIs */
#define OPENSSL_SUPPRESS_DEPRECATED
#if !defined(__GNUC__) || !defined(__ATOMIC_ACQ_REL) || \
defined(BROKEN_CLANG_ATOMICS) || defined(OPENSSL_NO_STDIO)
/*
* we only enable REPORT_RWLOCK_CONTENTION on clang/gcc when we have
* atomics available. We do this because we need to use an atomic to track
* when we can close the log file. We could use the CRYPTO_atomic_ api
* but that requires lock creation which gets us into a bad recursive loop
* when we try to initialize the file pointer
*/
# ifdef REPORT_RWLOCK_CONTENTION
# warning "RWLOCK CONTENTION REPORTING NOT SUPPORTED, Disabling"
# undef REPORT_RWLOCK_CONTENTION
# endif
#endif
#ifdef REPORT_RWLOCK_CONTENTION
# define _GNU_SOURCE
# include <execinfo.h>
# include <unistd.h>
#endif
#include <openssl/crypto.h>
#include <crypto/cryptlib.h>
#include <crypto/sparse_array.h>
#include "internal/cryptlib.h"
#include "internal/threads_common.h"
#include "internal/threads_lock_contention.h"
#include "internal/rcu.h"
#ifdef REPORT_RWLOCK_CONTENTION
# include "internal/time.h"
#endif
#include "rcu_internal.h"
#if defined(__clang__) && defined(__has_feature)
@ -64,18 +41,6 @@ __tsan_mutex_post_lock((x), 0, 0)
# include <atomic.h>
#endif
#if defined(__apple_build_version__) && __apple_build_version__ < 6000000
/*
* OS/X 10.7 and 10.8 had a weird version of clang which has __ATOMIC_ACQUIRE and
* __ATOMIC_ACQ_REL but which expects only one parameter for __atomic_is_lock_free()
* rather than two which has signature __atomic_is_lock_free(sizeof(_Atomic(T))).
* All of this makes impossible to use __atomic_is_lock_free here.
*
* See: https://github.com/llvm/llvm-project/commit/a4c2602b714e6c6edb98164550a5ae829b2de760
*/
# define BROKEN_CLANG_ATOMICS
#endif
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) && !defined(OPENSSL_SYS_WINDOWS)
# if defined(OPENSSL_SYS_UNIX)
@ -599,80 +564,12 @@ void ossl_rcu_lock_free(CRYPTO_RCU_LOCK *lock)
OPENSSL_free(rlock);
}
# ifdef REPORT_RWLOCK_CONTENTION
/*
* Normally we would use a BIO here to do this, but we create locks during
* library initialization, and creating a bio too early, creates a recursive set
* of stack calls that leads us to call CRYPTO_thread_run_once while currently
* executing the init routine for various run_once functions, which leads to
* deadlock. Avoid that by just using a FILE pointer. Also note that we
* directly use a pthread_mutex_t to protect access from multiple threads
* to the contention log file. We do this because we want to avoid use
* of the CRYPTO_THREAD api so as to prevent recursive blocking reports.
*/
static FILE *contention_fp = NULL;
static CRYPTO_ONCE init_contention_fp = CRYPTO_ONCE_STATIC_INIT;
static int rwlock_count = 0;
pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
CRYPTO_THREAD_LOCAL thread_contention_data;
static void destroy_contention_data(void *data)
{
OPENSSL_free(data);
}
struct stack_info {
unsigned int nptrs;
int write;
OSSL_TIME start;
OSSL_TIME duration;
char **strings;
};
# define STACKS_COUNT 32
struct stack_traces {
int lock_depth;
size_t idx;
struct stack_info stacks[STACKS_COUNT];
};
static void init_contention_fp_once(void)
{
# ifdef FIPS_MODULE
contention_fp = fopen("lock-contention-log-fips.txt", "w");
# else
contention_fp = fopen("lock-contention-log.txt", "w");
# endif
if (contention_fp == NULL)
fprintf(stderr, "Contention log file could not be opened, log will not be recorded\n");
/*
* Create a thread local key here to store our list of stack traces
* to be printed when we unlock the lock we are holding
*/
CRYPTO_THREAD_init_local(&thread_contention_data, destroy_contention_data);
return;
}
# endif
CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void)
{
# ifdef USE_RWLOCK
CRYPTO_RWLOCK *lock;
# ifdef REPORT_RWLOCK_CONTENTION
CRYPTO_THREAD_run_once(&init_contention_fp, init_contention_fp_once);
__atomic_add_fetch(&rwlock_count, 1, __ATOMIC_ACQ_REL);
{
struct stack_info *thread_stack_info;
thread_stack_info = CRYPTO_THREAD_get_local(&thread_contention_data);
if (thread_stack_info == NULL) {
thread_stack_info = OPENSSL_zalloc(sizeof(struct stack_traces));
CRYPTO_THREAD_set_local(&thread_contention_data, thread_stack_info);
}
}
# endif
ossl_init_rwlock_contention_data();
if ((lock = OPENSSL_zalloc(sizeof(pthread_rwlock_t))) == NULL)
/* Don't set error, to avoid recursion blowup. */
@ -714,75 +611,11 @@ CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void)
return lock;
}
# ifdef REPORT_RWLOCK_CONTENTION
static void print_stack_traces(struct stack_traces *traces, FILE *fptr)
{
unsigned int j;
pthread_mutex_lock(&log_lock);
while (traces != NULL && traces->idx >= 1) {
traces->idx--;
fprintf(fptr, "lock blocked on %s for %zu usec at time %zu tid %d\n",
traces->stacks[traces->idx].write == 1 ? "WRITE" : "READ",
ossl_time2us(traces->stacks[traces->idx].duration),
ossl_time2us(traces->stacks[traces->idx].start),
gettid());
if (traces->stacks[traces->idx].strings != NULL) {
for (j = 0; j < traces->stacks[traces->idx].nptrs; j++)
fprintf(fptr, "%s\n", traces->stacks[traces->idx].strings[j]);
free(traces->stacks[traces->idx].strings);
} else {
fprintf(fptr, "No stack trace available\n");
}
fprintf(contention_fp, "\n");
}
pthread_mutex_unlock(&log_lock);
}
# endif
# define BT_BUF_SIZE 1024
__owur int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock)
{
# ifdef USE_RWLOCK
# ifdef REPORT_RWLOCK_CONTENTION
struct stack_traces *traces = CRYPTO_THREAD_get_local(&thread_contention_data);
if (ossl_unlikely(traces == NULL)) {
traces = OPENSSL_zalloc(sizeof(struct stack_traces));
CRYPTO_THREAD_set_local(&thread_contention_data, traces);
if (ossl_unlikely(traces == NULL))
return 0;
}
traces->lock_depth++;
if (pthread_rwlock_tryrdlock(lock)) {
void *buffer[BT_BUF_SIZE];
OSSL_TIME start, end;
start = ossl_time_now();
if (!ossl_assert(pthread_rwlock_rdlock(lock) == 0)) {
traces->lock_depth--;
return 0;
}
end = ossl_time_now();
traces->stacks[traces->idx].duration = ossl_time_subtract(end, start);
traces->stacks[traces->idx].nptrs = backtrace(buffer, BT_BUF_SIZE);
traces->stacks[traces->idx].strings = backtrace_symbols(buffer,
traces->stacks[traces->idx].nptrs);
traces->stacks[traces->idx].duration = ossl_time_subtract(end, start);
traces->stacks[traces->idx].start = start;
traces->stacks[traces->idx].write = 0;
traces->idx++;
if (traces->idx >= STACKS_COUNT) {
fprintf(stderr, "STACK RECORD OVERFLOW!\n");
print_stack_traces(traces, contention_fp);
}
}
# else
if (!ossl_assert(pthread_rwlock_rdlock(lock) == 0))
if (!ossl_assert(ossl_rwlock_rdlock(lock) == 0))
return 0;
# endif
# else
if (pthread_mutex_lock(lock) != 0) {
assert(errno != EDEADLK && errno != EBUSY);
@ -796,43 +629,8 @@ __owur int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock)
__owur int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock)
{
# ifdef USE_RWLOCK
# ifdef REPORT_RWLOCK_CONTENTION
struct stack_traces *traces = CRYPTO_THREAD_get_local(&thread_contention_data);
if (ossl_unlikely(traces == NULL)) {
traces = OPENSSL_zalloc(sizeof(struct stack_traces));
CRYPTO_THREAD_set_local(&thread_contention_data, traces);
if (ossl_unlikely(traces == NULL))
return 0;
}
traces->lock_depth++;
if (pthread_rwlock_trywrlock(lock)) {
void *buffer[BT_BUF_SIZE];
OSSL_TIME start, end;
start = ossl_time_now();
if (!ossl_assert(pthread_rwlock_wrlock(lock) == 0)) {
traces->lock_depth--;
return 0;
}
end = ossl_time_now();
traces->stacks[traces->idx].nptrs = backtrace(buffer, BT_BUF_SIZE);
traces->stacks[traces->idx].strings = backtrace_symbols(buffer,
traces->stacks[traces->idx].nptrs);
traces->stacks[traces->idx].duration = ossl_time_subtract(end, start);
traces->stacks[traces->idx].start = start;
traces->stacks[traces->idx].write = 1;
traces->idx++;
if (traces->idx >= STACKS_COUNT) {
fprintf(stderr, "STACK RECORD OVERFLOW!\n");
print_stack_traces(traces, contention_fp);
}
}
# else
if (!ossl_assert(pthread_rwlock_wrlock(lock) == 0))
if (!ossl_assert(ossl_rwlock_wrlock(lock) == 0))
return 0;
# endif
# else
if (pthread_mutex_lock(lock) != 0) {
assert(errno != EDEADLK && errno != EBUSY);
@ -846,20 +644,8 @@ __owur int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock)
int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock)
{
# ifdef USE_RWLOCK
if (pthread_rwlock_unlock(lock) != 0)
if (ossl_rwlock_unlock(lock) != 0)
return 0;
# ifdef REPORT_RWLOCK_CONTENTION
{
struct stack_traces *traces = CRYPTO_THREAD_get_local(&thread_contention_data);
if (contention_fp != NULL && traces != NULL) {
traces->lock_depth--;
assert(traces->lock_depth >= 0);
if (traces->lock_depth == 0)
print_stack_traces(traces, contention_fp);
}
}
# endif
# else
if (pthread_mutex_unlock(lock) != 0) {
assert(errno != EPERM);
@ -874,22 +660,9 @@ void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock)
{
if (lock == NULL)
return;
# ifdef REPORT_RWLOCK_CONTENTION
/*
* Note: It's possible here that OpenSSL may allocate a lock and immediately
* free it, in which case we would erroneously close the contention log
* prior to the library going on to do more real work. In practice
* that never happens though, and since this is a debug facility
* we don't worry about that here.
*/
if (__atomic_add_fetch(&rwlock_count, -1, __ATOMIC_ACQ_REL) == 0) {
fclose(contention_fp);
contention_fp = NULL;
}
# endif
# ifdef USE_RWLOCK
ossl_free_rwlock_contention_data();
pthread_rwlock_destroy(lock);
# else
pthread_mutex_destroy(lock);

View File

@ -10,6 +10,18 @@
#ifndef _CRYPTO_THREADS_COMMON_H_
# define _CRYPTO_THREADS_COMMON_H_
# if defined(__apple_build_version__) && __apple_build_version__ < 6000000
/*
* OS/X 10.7 and 10.8 had a weird version of clang which has __ATOMIC_ACQUIRE and
* __ATOMIC_ACQ_REL but which expects only one parameter for __atomic_is_lock_free()
* rather than two which has signature __atomic_is_lock_free(sizeof(_Atomic(T))).
* All of this makes impossible to use __atomic_is_lock_free here.
*
* See: https://github.com/llvm/llvm-project/commit/a4c2602b714e6c6edb98164550a5ae829b2de760
*/
# define BROKEN_CLANG_ATOMICS
# endif
typedef enum {
CRYPTO_THREAD_LOCAL_RCU_KEY = 0,
CRYPTO_THREAD_LOCAL_DRBG_PRIV_KEY,

View File

@ -0,0 +1,65 @@
/*
* Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#ifndef _CRYPTO_THREADS_LOCK_CONTENTION_H_
# define _CRYPTO_THREADS_LOCK_CONTENTION_H_
# include <openssl/configuration.h>
# if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) && !defined(OPENSSL_SYS_WINDOWS)
# include "internal/threads_common.h"
# if defined(OPENSSL_NO_STDIO)
# ifdef REPORT_RWLOCK_CONTENTION
# warning "RWLOCK CONTENTION REPORTING NOT SUPPORTED, Disabling"
# undef REPORT_RWLOCK_CONTENTION
# endif
# endif
# include <pthread.h>
# ifdef REPORT_RWLOCK_CONTENTION
void ossl_init_rwlock_contention_data(void);
void ossl_free_rwlock_contention_data(void);
int ossl_rwlock_rdlock(pthread_rwlock_t *);
int ossl_rwlock_wrlock(pthread_rwlock_t *);
int ossl_rwlock_unlock(pthread_rwlock_t *);
# else /* !REPORT_RWLOCK_CONTENTION */
static inline void ossl_init_rwlock_contention_data(void)
{
}
static inline void ossl_free_rwlock_contention_data(void)
{
}
static inline int ossl_rwlock_rdlock(pthread_rwlock_t *rwlock)
{
return pthread_rwlock_rdlock(rwlock);
}
static inline int ossl_rwlock_wrlock(pthread_rwlock_t *rwlock)
{
return pthread_rwlock_wrlock(rwlock);
}
static inline int ossl_rwlock_unlock(pthread_rwlock_t *rwlock)
{
return pthread_rwlock_unlock(rwlock);
}
# endif /* REPORT_RWLOCK_CONTENTION */
# endif /* OPENSSL_THREADS && !CRYPTO_TDEBUG && !OPENSSL_SYS_WINDOWS */
#endif /* _CRYPTO_THREADS_LOCK_CONTENTION_H_ */

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/bash -eu
# Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License").
@ -15,29 +15,21 @@
#
TEMPDIR=$(mktemp -d /tmp/contention.XXXXXX)
LOGFILE=$1
trap "rm -rf $TEMPDIR" EXIT
if [ ! -f $LOGFILE ]
then
echo "No log file found"
exit 1
fi
LOGFILEBASE=$(basename $LOGFILE)
echo "Splitting files"
echo "Splitting files" > /dev/stderr
#
#start by splitting the log into separate stack traces
#
mkdir $TEMPDIR/individual_files
cp $LOGFILE $TEMPDIR/individual_files/$LOGFILEBASE
mkdir "$TEMPDIR/individual_files"
cat "$@" > "$TEMPDIR/individual_files/log"
pushd $TEMPDIR/individual_files/ > /dev/null
awk '
BEGIN {RS = ""; FS = "\n"}
{file_num++; print > ("stacktrace" file_num ".txt")}' ./$LOGFILEBASE
{file_num++; print > ("stacktrace" file_num ".txt")}' ./log
popd > /dev/null
rm $TEMPDIR/individual_files/$LOGFILEBASE
rm -f $TEMPDIR/individual_files/log
#
# Make some associative arrays to track our stats
@ -46,9 +38,9 @@ declare -A filenames
declare -A total_latency
declare -A latency_counts
echo "Gathering latencies"
echo "Gathering latencies" > /dev/stderr
FILECOUNT=$(ls $TEMPDIR/individual_files/stacktrace*.* | wc -l)
let currentidx=0
currentidx=0
#
# Look at every stack trace, get and record its latency, and hash value
@ -61,8 +53,8 @@ do
#now compute its sha1sum
SHA1SUM=$(sha1sum $i | awk '{print $1}')
filenames["$SHA1SUM"]=$i
let CUR_LATENCY=0
let LATENCY_COUNT=0
CUR_LATENCY=0
LATENCY_COUNT=0
#
# If we already have a latency total for this hash value
@ -71,8 +63,8 @@ do
#
if [[ -v total_latency["$SHA1SUM"] ]]
then
let CUR_LATENCY=${total_latency["$SHA1SUM"]}
let LATENCY_COUNT=${latency_counts["$SHA1SUM"]}
CUR_LATENCY=${total_latency["$SHA1SUM"]}
LATENCY_COUNT=${latency_counts["$SHA1SUM"]}
fi
#
@ -81,8 +73,8 @@ do
#
total_latency["$SHA1SUM"]=$(dc -e "$CUR_LATENCY $LATENCY + p")
latency_counts["$SHA1SUM"]=$(dc -e "$LATENCY_COUNT 1 + p")
echo -e -n "FILE $currentidx/$FILECOUNT \r"
let currentidx=$currentidx+1
echo -e -n "FILE $currentidx/$FILECOUNT \r" > /dev/stderr
currentidx=$((currentidx + 1))
done
#