mirror of https://github.com/openssl/openssl.git
Compare commits
11 Commits
b2f53cc922
...
3c44a9c36c
| Author | SHA1 | Date |
|---|---|---|
|
|
3c44a9c36c | |
|
|
e348012e99 | |
|
|
0154dc12a5 | |
|
|
db6565e5b1 | |
|
|
707a966f70 | |
|
|
a4d9183593 | |
|
|
d4710397a9 | |
|
|
eb8547c65f | |
|
|
efee781894 | |
|
|
849dd3bfad | |
|
|
b3365c5f9c |
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
@ -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
|
||||
|
||||
#
|
||||
|
|
|
|||
Loading…
Reference in New Issue