Trace rwlocks of netdata (#12785)
* with -DNETDATA_INTERNAL_CHECKS=1 enable rwlocks tracing * fix strings alignment on terminal * remove wrong addition * removed formating warning; now counting active locks per thread; tracing is enabled with -DNETDATA_TRACE_RWLOCKS=1 * added the missing netdata_mutex_destroy() * optimized clocks usage in locks * added also main * fixed formatting warning * add compiler warning when compiling with -DNETDATA_TRACE_RWLOCKS=1 * cleanup and documentation * fix for old variable * >= not just > to allow proper comparisons * dont print 0x twice and print the lock pointer on every line * trace locks deeper
Costa Tsaousis committed
May 3, 2022 at 00:31 UTC
6bd2dcd29d469274c738d584e2c4cb0f8b6f3da4
8 files changed
+681
-147
daemon/main.c
+3
-2
@@ -1087,8 +1087,9 @@ int main(int argc, char **argv) {
1087
if(i > 0)
1088
mallopt(M_ARENA_MAX, 1);
1089
#endif
1090
- test_clock_boottime();
1091
- test_clock_monotonic_coarse();
1090
+
1091
+ // initialize the system clocks
1092
+ clocks_init();
1093
1094
// prepare configuration environment variables for the plugins
1095
libnetdata/avl/avl.c
+2
-2
@@ -372,9 +372,9 @@ void avl_destroy_lock(avl_tree_lock *tree) {
372
int lock;
373
374
#ifdef AVL_LOCK_WITH_MUTEX
375
- lock = pthread_mutex_destroy(&tree->mutex);
375
+ lock = netdata_mutex_destroy(&tree->mutex);
376
#else
377
- lock = pthread_rwlock_destroy(&tree->rwlock);
377
+ lock = netdata_rwlock_destroy(&tree->rwlock);
378
#endif
379
380
if(lock != 0)
libnetdata/clocks/clocks.c
+40
-23
@@ -2,8 +2,10 @@
2
3
#include "../libnetdata.h"
4
5
-static int clock_boottime_valid = 1;
6
-static int clock_monotonic_coarse_valid = 1;
5
+// defaults are for compatibility
6
+// call clocks_init() once, to optimize these default settings
7
+static clockid_t clock_boottime_to_use = CLOCK_MONOTONIC;
8
+static clockid_t clock_monotonic_to_use = CLOCK_MONOTONIC;
9
10
#ifndef HAVE_CLOCK_GETTIME
11
inline int clock_gettime(clockid_t clk_id, struct timespec *ts) {
@@ -18,19 +20,39 @@ inline int clock_gettime(clockid_t clk_id, struct timespec *ts) {
20
}
21
#endif
22
21
-void test_clock_boottime(void) {
23
+// When running a binary with CLOCK_MONOTONIC_COARSE defined on a system with a linux kernel older than Linux 2.6.32 the
24
+// clock_gettime(2) system call fails with EINVAL. In that case it must fall-back to CLOCK_MONOTONIC.
25
+
26
+static void test_clock_monotonic_coarse(void) {
27
struct timespec ts;
23
- if(clock_gettime(CLOCK_BOOTTIME, &ts) == -1 && errno == EINVAL)
24
- clock_boottime_valid = 0;
28
+ if(clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == -1 && errno == EINVAL)
29
+ clock_monotonic_to_use = CLOCK_MONOTONIC;
30
+ else
31
+ clock_monotonic_to_use = CLOCK_MONOTONIC_COARSE;
32
}
33
27
-void test_clock_monotonic_coarse(void) {
34
+// When running a binary with CLOCK_BOOTTIME defined on a system with a linux kernel older than Linux 2.6.39 the
35
+// clock_gettime(2) system call fails with EINVAL. In that case it must fall-back to CLOCK_MONOTONIC.
36
+
37
+static void test_clock_boottime(void) {
38
struct timespec ts;
29
- if(clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == -1 && errno == EINVAL)
30
- clock_monotonic_coarse_valid = 0;
39
+ if(clock_gettime(CLOCK_BOOTTIME, &ts) == -1 && errno == EINVAL)
40
+ clock_boottime_to_use = clock_monotonic_to_use;
41
+ else
42
+ clock_boottime_to_use = CLOCK_BOOTTIME;
43
+}
44
+
45
+// perform any initializations required for clocks
46
+
47
+void clocks_init(void) {
48
+ // monotonic coarse has to be tested before boottime
49
+ test_clock_monotonic_coarse();
50
+
51
+ // boottime has to be tested after monotonic coarse
52
+ test_clock_boottime();
53
}
54
33
-static inline time_t now_sec(clockid_t clk_id) {
55
+inline time_t now_sec(clockid_t clk_id) {
56
struct timespec ts;
57
if(unlikely(clock_gettime(clk_id, &ts) == -1)) {
58
error("clock_gettime(%d, ×pec) failed.", clk_id);
@@ -39,7 +61,7 @@ static inline time_t now_sec(clockid_t clk_id) {
61
return ts.tv_sec;
62
}
63
42
-static inline usec_t now_usec(clockid_t clk_id) {
64
+inline usec_t now_usec(clockid_t clk_id) {
65
struct timespec ts;
66
if(unlikely(clock_gettime(clk_id, &ts) == -1)) {
67
error("clock_gettime(%d, ×pec) failed.", clk_id);
@@ -48,7 +70,7 @@ static inline usec_t now_usec(clockid_t clk_id) {
70
return (usec_t)ts.tv_sec * USEC_PER_SEC + (ts.tv_nsec % NSEC_PER_SEC) / NSEC_PER_USEC;
71
}
72
51
-static inline int now_timeval(clockid_t clk_id, struct timeval *tv) {
73
+inline int now_timeval(clockid_t clk_id, struct timeval *tv) {
74
struct timespec ts;
75
76
if(unlikely(clock_gettime(clk_id, &ts) == -1)) {
@@ -76,15 +98,15 @@ inline int now_realtime_timeval(struct timeval *tv) {
98
}
99
100
inline time_t now_monotonic_sec(void) {
79
- return now_sec(likely(clock_monotonic_coarse_valid) ? CLOCK_MONOTONIC_COARSE : CLOCK_MONOTONIC);
101
+ return now_sec(clock_monotonic_to_use);
102
}
103
104
inline usec_t now_monotonic_usec(void) {
83
- return now_usec(likely(clock_monotonic_coarse_valid) ? CLOCK_MONOTONIC_COARSE : CLOCK_MONOTONIC);
105
+ return now_usec(clock_monotonic_to_use);
106
}
107
108
inline int now_monotonic_timeval(struct timeval *tv) {
87
- return now_timeval(likely(clock_monotonic_coarse_valid) ? CLOCK_MONOTONIC_COARSE : CLOCK_MONOTONIC, tv);
109
+ return now_timeval(clock_monotonic_to_use, tv);
110
}
111
112
inline time_t now_monotonic_high_precision_sec(void) {
@@ -100,19 +122,15 @@ inline int now_monotonic_high_precision_timeval(struct timeval *tv) {
122
}
123
124
inline time_t now_boottime_sec(void) {
103
- return now_sec(likely(clock_boottime_valid) ? CLOCK_BOOTTIME :
104
- likely(clock_monotonic_coarse_valid) ? CLOCK_MONOTONIC_COARSE : CLOCK_MONOTONIC);
125
+ return now_sec(clock_boottime_to_use);
126
}
127
128
inline usec_t now_boottime_usec(void) {
108
- return now_usec(likely(clock_boottime_valid) ? CLOCK_BOOTTIME :
109
- likely(clock_monotonic_coarse_valid) ? CLOCK_MONOTONIC_COARSE : CLOCK_MONOTONIC);
129
+ return now_usec(clock_boottime_to_use);
130
}
131
132
inline int now_boottime_timeval(struct timeval *tv) {
113
- return now_timeval(likely(clock_boottime_valid) ? CLOCK_BOOTTIME :
114
- likely(clock_monotonic_coarse_valid) ? CLOCK_MONOTONIC_COARSE : CLOCK_MONOTONIC,
115
- tv);
133
+ return now_timeval(clock_boottime_to_use, tv);
134
}
135
136
inline usec_t timeval_usec(struct timeval *tv) {
@@ -137,8 +155,7 @@ inline usec_t dt_usec(struct timeval *now, struct timeval *old) {
155
return (ts1 > ts2) ? (ts1 - ts2) : (ts2 - ts1);
156
}
157
140
-inline void heartbeat_init(heartbeat_t *hb)
141
-{
158
+inline void heartbeat_init(heartbeat_t *hb) {
159
hb->monotonic = hb->realtime = 0ULL;
160
}
161
libnetdata/clocks/clocks.h
+5
-11
@@ -133,7 +133,6 @@ extern int now_boottime_timeval(struct timeval *tv);
133
extern time_t now_boottime_sec(void);
134
extern usec_t now_boottime_usec(void);
135
136
-
136
extern usec_t timeval_usec(struct timeval *tv);
137
extern msec_t timeval_msec(struct timeval *tv);
138
@@ -152,17 +151,12 @@ extern usec_t heartbeat_monotonic_dt_to_now_usec(heartbeat_t *hb);
151
152
extern int sleep_usec(usec_t usec);
153
155
-/*
156
- * When running a binary with CLOCK_BOOTTIME defined on a system with a linux kernel older than Linux 2.6.39 the
157
- * clock_gettime(2) system call fails with EINVAL. In that case it must fall-back to CLOCK_MONOTONIC.
158
- */
159
-void test_clock_boottime(void);
154
+extern void clocks_init(void);
155
161
-/*
162
- * When running a binary with CLOCK_MONOTONIC_COARSE defined on a system with a linux kernel older than Linux 2.6.32 the
163
- * clock_gettime(2) system call fails with EINVAL. In that case it must fall-back to CLOCK_MONOTONIC.
164
- */
165
-void test_clock_monotonic_coarse(void);
156
+// lower level functions - avoid using directly
157
+extern time_t now_sec(clockid_t clk_id);
158
+extern usec_t now_usec(clockid_t clk_id);
159
+extern int now_timeval(clockid_t clk_id, struct timeval *tv);
160
161
extern collected_number uptime_msec(char *filename);
162
libnetdata/locks/README.md
+95
@@ -2,4 +2,99 @@
2
custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/locks/README.md
3
-->
4
5
+## How to trace netdata locks
6
+
7
+To enable tracing rwlocks in netdata, compile netdata by setting `CFLAGS="-DNETDATA_TRACE_RWLOCKS=1"`, like this:
8
+
9
+```
10
+CFLAGS="-O1 -ggdb -DNETDATA_TRACE_RWLOCKS=1" ./netdata-installer.sh
11
+```
12
+
13
+During compilation, the compiler will log:
14
+
15
+```
16
+libnetdata/locks/locks.c:105:2: warning: #warning NETDATA_TRACE_RWLOCKS ENABLED - EXPECT A LOT OF OUTPUT [-Wcpp]
17
+ 105 | #warning NETDATA_TRACE_RWLOCKS ENABLED - EXPECT A LOT OF OUTPUT
18
+ | ^~~~~~~
19
+```
20
+
21
+Once compiled, netdata will do the following:
22
+
23
+Every call to `netdata_rwlock_*()` is now measured in time.
24
+
25
+### logging of slow locks/unlocks
26
+
27
+If any call takes more than 10 usec, it will be logged like this:
28
+
29
+```
30
+RW_LOCK ON LOCK 0x0x7fbe1f2e5190: 4157038, 'ACLK_Query_2' (function build_context_param_list() 99@web/api/formatters/rrd2json.c) WAITED to UNLOCK for 29 usec.
31
+```
32
+
33
+The time can be changed by setting this `-DNETDATA_TRACE_RWLOCKS_WAIT_TIME_TO_IGNORE_USEC=20` (or whatever number) to the CFLAGS.
34
+
35
+### logging of long hold times
36
+
37
+If any lock is holded for more than 10000 usec, it will be logged like this:
38
+
39
+```
40
+RW_LOCK ON LOCK 0x0x55a20afc1b20: 4187198, 'ANALYTICS' (function analytics_gather_mutable_meta_data() 532@daemon/analytics.c) holded a 'R' for 13232 usec.
41
+```
42
+
43
+The time can be changed by setting this `-DNETDATA_TRACE_RWLOCKS_HOLD_TIME_TO_IGNORE_USEC=20000` (or whatever number) to the CFLAGS.
44
+
45
+### logging for probable pauses (predictive)
46
+
47
+The library maintains a linked-list of all the lock holders (one entry per thread). For this linked-list a mutex is used. So every call to the r/w locks now also has a mutex lock.
48
+
49
+If any call is expected to pause the caller (ie the caller is attempting a read lock while there is a write lock in place and vice versa), the library will log something like this:
50
+
51
+```
52
+RW_LOCK ON LOCK 0x0x5651c9fcce20: 4190039 'HEALTH' (function init_pending_foreach_alarms() 661@health/health.c) WANTS a 'W' lock (while holding 1 rwlocks and 1 mutexes).
53
+There are 7 readers and 0 writers are holding the lock:
54
+ => 1: RW_LOCK: process 4190091 'WEB_SERVER[static14]' (function web_client_api_request_v1_data() 526@web/api/web_api_v1.c) is having 1 'R' lock for 709847 usec.
55
+ => 2: RW_LOCK: process 4190079 'WEB_SERVER[static6]' (function web_client_api_request_v1_data() 526@web/api/web_api_v1.c) is having 1 'R' lock for 709869 usec.
56
+ => 3: RW_LOCK: process 4190084 'WEB_SERVER[static10]' (function web_client_api_request_v1_data() 526@web/api/web_api_v1.c) is having 1 'R' lock for 709948 usec.
57
+ => 4: RW_LOCK: process 4190076 'WEB_SERVER[static3]' (function web_client_api_request_v1_data() 526@web/api/web_api_v1.c) is having 1 'R' lock for 710190 usec.
58
+ => 5: RW_LOCK: process 4190092 'WEB_SERVER[static15]' (function web_client_api_request_v1_data() 526@web/api/web_api_v1.c) is having 1 'R' lock for 710195 usec.
59
+ => 6: RW_LOCK: process 4190077 'WEB_SERVER[static4]' (function web_client_api_request_v1_data() 526@web/api/web_api_v1.c) is having 1 'R' lock for 710208 usec.
60
+ => 7: RW_LOCK: process 4190044 'WEB_SERVER[static1]' (function web_client_api_request_v1_data() 526@web/api/web_api_v1.c) is having 1 'R' lock for 710221 usec.
61
+```
62
+
63
+And each of the above is paired with a `GOT` log, like this:
64
+
65
+```
66
+RW_LOCK ON LOCK 0x0x5651c9fcce20: 4190039 'HEALTH' (function init_pending_foreach_alarms() 661@health/health.c) GOT a 'W' lock (while holding 2 rwlocks and 1 mutexes).
67
+There are 0 readers and 1 writers are holding the lock:
68
+ => 1: RW_LOCK: process 4190039 'HEALTH' (function init_pending_foreach_alarms() 661@health/health.c) is having 1 'W' lock for 36 usec.
69
+```
70
+
71
+Keep in mind that the lock and log are not atomic. The list of callers is indicative (and sometimes just empty because the original holders of the lock, unlocked it until we had the chance to print their names).
72
+
73
+### POSIX compliance check
74
+
75
+The library may also log messages about POSIX unsupported cases, like this:
76
+
77
+```
78
+RW_LOCK FATAL ON LOCK 0x0x622000109290: 3609368 'PLUGIN[proc]' (function __rrdset_check_rdlock() 10@database/rrdset.c) attempts to acquire a 'W' lock.
79
+But it is not supported by POSIX because: ALREADY HAS THIS LOCK
80
+At this attempt, the task is holding 1 rwlocks and 1 mutexes.
81
+There are 1 readers and 0 writers are holding the lock requested now:
82
+ => 1: RW_LOCK: process 3609368 'PLUGIN[proc]' (function rrdset_done() 1398@database/rrdset.c) is having 1 'R' lock for 0 usec.
83
+```
84
+
85
+### nested read locks
86
+
87
+When compiled with `-DNETDATA_TRACE_RWLOCKS_LOG_NESTED=1` the library will also detect nested read locks and print them like this:
88
+
89
+```
90
+RW_LOCK ON LOCK 0x0x7ff6ea46d190: 4140225 'WEB_SERVER[static14]' (function rrdr_json_wrapper_begin() 34@web/api/formatters/json_wrapper.c) NESTED READ LOCK REQUEST a 'R' lock (while holding 1 rwlocks and 1 mutexes).
91
+There are 5 readers and 0 writers are holding the lock:
92
+ => 1: RW_LOCK: process 4140225 'WEB_SERVER[static14]' (function rrdr_lock_rrdset() 70@web/api/queries/rrdr.c) is having 1 'R' lock for 216667 usec.
93
+ => 2: RW_LOCK: process 4140211 'WEB_SERVER[static6]' (function rrdr_lock_rrdset() 70@web/api/queries/rrdr.c) is having 1 'R' lock for 220001 usec.
94
+ => 3: RW_LOCK: process 4140218 'WEB_SERVER[static8]' (function rrdr_lock_rrdset() 70@web/api/queries/rrdr.c) is having 1 'R' lock for 220001 usec.
95
+ => 4: RW_LOCK: process 4140224 'WEB_SERVER[static13]' (function rrdr_lock_rrdset() 70@web/api/queries/rrdr.c) is having 1 'R' lock for 220001 usec.
96
+ => 5: RW_LOCK: process 4140227 'WEB_SERVER[static16]' (function rrdr_lock_rrdset() 70@web/api/queries/rrdr.c) is having 1 'R' lock for 220001 usec.
97
+```
98
+
99
+
100
libnetdata/locks/locks.c
+481
-98
@@ -2,11 +2,32 @@
2
3
#include "../libnetdata.h"
4
5
+#ifdef NETDATA_TRACE_RWLOCKS
6
+
7
+#ifndef NETDATA_TRACE_RWLOCKS_WAIT_TIME_TO_IGNORE_USEC
8
+#define NETDATA_TRACE_RWLOCKS_WAIT_TIME_TO_IGNORE_USEC 10
9
+#endif
10
+
11
+#ifndef NETDATA_TRACE_RWLOCKS_HOLD_TIME_TO_IGNORE_USEC
12
+#define NETDATA_TRACE_RWLOCKS_HOLD_TIME_TO_IGNORE_USEC 10000
13
+#endif
14
+
15
+#ifndef NETDATA_THREAD_LOCKS_ARRAY_SIZE
16
+#define NETDATA_THREAD_LOCKS_ARRAY_SIZE 10
17
+#endif
18
+static __thread netdata_rwlock_t *netdata_thread_locks[NETDATA_THREAD_LOCKS_ARRAY_SIZE];
19
+
20
+
21
+#endif // NETDATA_TRACE_RWLOCKS
22
+
23
// ----------------------------------------------------------------------------
24
// automatic thread cancelability management, based on locks
25
26
static __thread int netdata_thread_first_cancelability = 0;
9
-static __thread int netdata_thread_lock_cancelability = 0;
27
+static __thread int netdata_thread_nested_disables = 0;
28
+
29
+static __thread size_t netdata_locks_acquired_rwlocks = 0;
30
+static __thread size_t netdata_locks_acquired_mutexes = 0;
31
32
inline void netdata_thread_disable_cancelability(void) {
33
int old;
@@ -14,18 +35,19 @@ inline void netdata_thread_disable_cancelability(void) {
35
if(ret != 0)
36
error("THREAD_CANCELABILITY: pthread_setcancelstate() on thread %s returned error %d", netdata_thread_tag(), ret);
37
else {
17
- if(!netdata_thread_lock_cancelability)
38
+ if(!netdata_thread_nested_disables)
39
netdata_thread_first_cancelability = old;
40
20
- netdata_thread_lock_cancelability++;
41
+ netdata_thread_nested_disables++;
42
}
43
}
44
45
inline void netdata_thread_enable_cancelability(void) {
25
- if(netdata_thread_lock_cancelability < 1) {
26
- error("THREAD_CANCELABILITY: netdata_thread_enable_cancelability(): invalid thread cancelability count %d on thread %s - results will be undefined - please report this!", netdata_thread_lock_cancelability, netdata_thread_tag());
46
+ if(netdata_thread_nested_disables < 1) {
47
+ error("THREAD_CANCELABILITY: netdata_thread_enable_cancelability(): invalid thread cancelability count %d on thread %s - results will be undefined - please report this!",
48
+ netdata_thread_nested_disables, netdata_thread_tag());
49
}
28
- else if(netdata_thread_lock_cancelability == 1) {
50
+ else if(netdata_thread_nested_disables == 1) {
51
int old = 1;
52
int ret = pthread_setcancelstate(netdata_thread_first_cancelability, &old);
53
if(ret != 0)
@@ -35,10 +57,10 @@ inline void netdata_thread_enable_cancelability(void) {
57
error("THREAD_CANCELABILITY: netdata_thread_enable_cancelability(): old thread cancelability on thread %s was changed, expected DISABLED (%d), found %s (%d) - please report this!", netdata_thread_tag(), PTHREAD_CANCEL_DISABLE, (old == PTHREAD_CANCEL_ENABLE)?"ENABLED":"UNKNOWN", old);
58
}
59
38
- netdata_thread_lock_cancelability = 0;
60
+ netdata_thread_nested_disables = 0;
61
}
62
else
41
- netdata_thread_lock_cancelability--;
63
+ netdata_thread_nested_disables--;
64
}
65
66
// ----------------------------------------------------------------------------
@@ -51,6 +73,13 @@ int __netdata_mutex_init(netdata_mutex_t *mutex) {
73
return ret;
74
}
75
76
+int __netdata_mutex_destroy(netdata_mutex_t *mutex) {
77
+ int ret = pthread_mutex_destroy(mutex);
78
+ if(unlikely(ret != 0))
79
+ error("MUTEX_LOCK: failed to destroy (code %d).", ret);
80
+ return ret;
81
+}
82
+
83
int __netdata_mutex_lock(netdata_mutex_t *mutex) {
84
netdata_thread_disable_cancelability();
85
@@ -59,6 +88,9 @@ int __netdata_mutex_lock(netdata_mutex_t *mutex) {
88
netdata_thread_enable_cancelability();
89
error("MUTEX_LOCK: failed to get lock (code %d)", ret);
90
}
91
+ else
92
+ netdata_locks_acquired_mutexes++;
93
+
94
return ret;
95
}
96
@@ -68,6 +100,8 @@ int __netdata_mutex_trylock(netdata_mutex_t *mutex) {
100
int ret = pthread_mutex_trylock(mutex);
101
if(ret != 0)
102
netdata_thread_enable_cancelability();
103
+ else
104
+ netdata_locks_acquired_mutexes++;
105
106
return ret;
107
}
@@ -76,93 +110,105 @@ int __netdata_mutex_unlock(netdata_mutex_t *mutex) {
110
int ret = pthread_mutex_unlock(mutex);
111
if(unlikely(ret != 0))
112
error("MUTEX_LOCK: failed to unlock (code %d).", ret);
79
- else
113
+ else {
114
+ netdata_locks_acquired_mutexes--;
115
netdata_thread_enable_cancelability();
116
+ }
117
118
return ret;
119
}
120
121
+#ifdef NETDATA_TRACE_RWLOCKS
122
+
123
+#warning NETDATA_TRACE_RWLOCKS ENABLED - EXPECT A LOT OF OUTPUT
124
+
125
int netdata_mutex_init_debug(const char *file __maybe_unused, const char *function __maybe_unused,
126
const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
87
- usec_t start = 0;
88
- (void)start;
89
-
90
- if(unlikely(debug_flags & D_LOCKS)) {
91
- start = now_boottime_usec();
92
- debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_init(0x%p) from %lu@%s, %s()", mutex, line, file, function);
93
- }
127
+ debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_init(%p) from %lu@%s, %s()", mutex, line, file, function);
128
129
int ret = __netdata_mutex_init(mutex);
130
97
- debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_init(0x%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, now_boottime_usec() - start, line, file, function);
131
+ debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_init(%p) = %d, from %lu@%s, %s()", mutex, ret, line, file, function);
132
133
return ret;
134
}
135
102
-int netdata_mutex_lock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
136
+int netdata_mutex_destroy_debug(const char *file __maybe_unused, const char *function __maybe_unused,
137
const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
104
- usec_t start = 0;
105
- (void)start;
138
+ debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_destroy(%p) from %lu@%s, %s()", mutex, line, file, function);
139
107
- if(unlikely(debug_flags & D_LOCKS)) {
108
- start = now_boottime_usec();
109
- debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_lock(0x%p) from %lu@%s, %s()", mutex, line, file, function);
110
- }
140
+ int ret = __netdata_mutex_destroy(mutex);
141
+
142
+ debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_destroy(%p) = %d, from %lu@%s, %s()", mutex, ret, line, file, function);
143
+
144
+ return ret;
145
+}
146
+
147
+int netdata_mutex_lock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
148
+ const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
149
+ debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_lock(%p) from %lu@%s, %s()", mutex, line, file, function);
150
151
+ usec_t start_s = now_monotonic_high_precision_usec();
152
int ret = __netdata_mutex_lock(mutex);
153
+ usec_t end_s = now_monotonic_high_precision_usec();
154
114
- debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_lock(0x%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, now_boottime_usec() - start, line, file, function);
155
+ // remove compiler unused variables warning
156
+ (void)start_s;
157
+ (void)end_s;
158
+
159
+ debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_lock(%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, end_s - start_s, line, file, function);
160
161
return ret;
162
}
163
164
int netdata_mutex_trylock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
165
const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
121
- usec_t start = 0;
122
- (void)start;
123
-
124
- if(unlikely(debug_flags & D_LOCKS)) {
125
- start = now_boottime_usec();
126
- debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_trylock(0x%p) from %lu@%s, %s()", mutex, line, file, function);
127
- }
166
+ debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_trylock(%p) from %lu@%s, %s()", mutex, line, file, function);
167
168
+ usec_t start_s = now_monotonic_high_precision_usec();
169
int ret = __netdata_mutex_trylock(mutex);
170
+ usec_t end_s = now_monotonic_high_precision_usec();
171
+
172
+ // remove compiler unused variables warning
173
+ (void)start_s;
174
+ (void)end_s;
175
131
- debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_trylock(0x%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, now_boottime_usec() - start, line, file, function);
176
+ debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_trylock(%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, end_s - start_s, line, file, function);
177
178
return ret;
179
}
180
181
int netdata_mutex_unlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
182
const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
138
- usec_t start = 0;
139
- (void)start;
140
-
141
- if(unlikely(debug_flags & D_LOCKS)) {
142
- start = now_boottime_usec();
143
- debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_unlock(0x%p) from %lu@%s, %s()", mutex, line, file, function);
144
- }
183
+ debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_unlock(%p) from %lu@%s, %s()", mutex, line, file, function);
184
185
+ usec_t start_s = now_monotonic_high_precision_usec();
186
int ret = __netdata_mutex_unlock(mutex);
187
+ usec_t end_s = now_monotonic_high_precision_usec();
188
+
189
+ // remove compiler unused variables warning
190
+ (void)start_s;
191
+ (void)end_s;
192
148
- debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_unlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, now_boottime_usec() - start, line, file, function);
193
+ debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_unlock(%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, end_s - start_s, line, file, function);
194
195
return ret;
196
}
197
198
+#endif // NETDATA_TRACE_RWLOCKS
199
200
// ----------------------------------------------------------------------------
155
-// r/w lock
201
+// rwlock
202
203
int __netdata_rwlock_destroy(netdata_rwlock_t *rwlock) {
158
- int ret = pthread_rwlock_destroy(rwlock);
204
+ int ret = pthread_rwlock_destroy(&rwlock->rwlock_t);
205
if(unlikely(ret != 0))
206
error("RW_LOCK: failed to destroy lock (code %d)", ret);
207
return ret;
208
}
209
210
int __netdata_rwlock_init(netdata_rwlock_t *rwlock) {
165
- int ret = pthread_rwlock_init(rwlock, NULL);
211
+ int ret = pthread_rwlock_init(&rwlock->rwlock_t, NULL);
212
if(unlikely(ret != 0))
213
error("RW_LOCK: failed to initialize lock (code %d)", ret);
214
return ret;
@@ -171,11 +217,13 @@ int __netdata_rwlock_init(netdata_rwlock_t *rwlock) {
217
int __netdata_rwlock_rdlock(netdata_rwlock_t *rwlock) {
218
netdata_thread_disable_cancelability();
219
174
- int ret = pthread_rwlock_rdlock(rwlock);
220
+ int ret = pthread_rwlock_rdlock(&rwlock->rwlock_t);
221
if(unlikely(ret != 0)) {
222
netdata_thread_enable_cancelability();
223
error("RW_LOCK: failed to obtain read lock (code %d)", ret);
224
}
225
+ else
226
+ netdata_locks_acquired_rwlocks++;
227
228
return ret;
229
}
@@ -183,21 +231,25 @@ int __netdata_rwlock_rdlock(netdata_rwlock_t *rwlock) {
231
int __netdata_rwlock_wrlock(netdata_rwlock_t *rwlock) {
232
netdata_thread_disable_cancelability();
233
186
- int ret = pthread_rwlock_wrlock(rwlock);
234
+ int ret = pthread_rwlock_wrlock(&rwlock->rwlock_t);
235
if(unlikely(ret != 0)) {
236
error("RW_LOCK: failed to obtain write lock (code %d)", ret);
237
netdata_thread_enable_cancelability();
238
}
239
+ else
240
+ netdata_locks_acquired_rwlocks++;
241
242
return ret;
243
}
244
245
int __netdata_rwlock_unlock(netdata_rwlock_t *rwlock) {
196
- int ret = pthread_rwlock_unlock(rwlock);
246
+ int ret = pthread_rwlock_unlock(&rwlock->rwlock_t);
247
if(unlikely(ret != 0))
248
error("RW_LOCK: failed to release lock (code %d)", ret);
199
- else
249
+ else {
250
netdata_thread_enable_cancelability();
251
+ netdata_locks_acquired_rwlocks--;
252
+ }
253
254
return ret;
255
}
@@ -205,9 +257,11 @@ int __netdata_rwlock_unlock(netdata_rwlock_t *rwlock) {
257
int __netdata_rwlock_tryrdlock(netdata_rwlock_t *rwlock) {
258
netdata_thread_disable_cancelability();
259
208
- int ret = pthread_rwlock_tryrdlock(rwlock);
260
+ int ret = pthread_rwlock_tryrdlock(&rwlock->rwlock_t);
261
if(ret != 0)
262
netdata_thread_enable_cancelability();
263
+ else
264
+ netdata_locks_acquired_rwlocks++;
265
266
return ret;
267
}
@@ -215,129 +269,458 @@ int __netdata_rwlock_tryrdlock(netdata_rwlock_t *rwlock) {
269
int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock) {
270
netdata_thread_disable_cancelability();
271
218
- int ret = pthread_rwlock_trywrlock(rwlock);
272
+ int ret = pthread_rwlock_trywrlock(&rwlock->rwlock_t);
273
if(ret != 0)
274
netdata_thread_enable_cancelability();
275
+ else
276
+ netdata_locks_acquired_rwlocks++;
277
278
return ret;
279
}
280
281
+#ifdef NETDATA_TRACE_RWLOCKS
282
+
283
+// ----------------------------------------------------------------------------
284
+// lockers list
285
+
286
+void not_supported_by_posix_rwlocks(const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock, char locktype, const char *reason) {
287
+ __netdata_mutex_lock(&rwlock->lockers_mutex);
288
+ fprintf(stderr,
289
+ "RW_LOCK FATAL ON LOCK %p: %d '%s' (function %s() %lu@%s) attempts to acquire a '%c' lock, but it is not supported by POSIX because: %s. At this attempt, the task is holding %zu rwlocks and %zu mutexes. There are %zu readers and %zu writers holding this lock:\n",
290
+ rwlock,
291
+ gettid(), netdata_thread_tag(),
292
+ function, line, file,
293
+ locktype,
294
+ reason,
295
+ netdata_locks_acquired_rwlocks, netdata_locks_acquired_mutexes,
296
+ rwlock->readers, rwlock->writers);
297
+
298
+ int i;
299
+ usec_t now = now_monotonic_high_precision_usec();
300
+ netdata_rwlock_locker *p;
301
+ for(i = 1, p = rwlock->lockers; p ;p = p->next, i++) {
302
+ fprintf(stderr,
303
+ " => %i: RW_LOCK %p: process %d '%s' (function %s() %lu@%s) is having %zu '%c' lock for %llu usec.\n",
304
+ i, rwlock,
305
+ p->pid, p->tag,
306
+ p->function, p->line, p->file,
307
+ p->callers, p->lock,
308
+ (now - p->start_s));
309
+ }
310
+ __netdata_mutex_unlock(&rwlock->lockers_mutex);
311
+}
312
+
313
+static void log_rwlock_lockers(const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock, const char *reason, char locktype) {
314
+
315
+ // this function can only be used by one thread at a time
316
+ // because otherwise, the threads may deadlock waiting for each other
317
+ static netdata_mutex_t log_lockers_mutex = NETDATA_MUTEX_INITIALIZER;
318
+ __netdata_mutex_lock(&log_lockers_mutex);
319
+
320
+ // now work on this locker
321
+ __netdata_mutex_lock(&rwlock->lockers_mutex);
322
+ fprintf(stderr,
323
+ "RW_LOCK ON LOCK %p: %d '%s' (function %s() %lu@%s) %s a '%c' lock (while holding %zu rwlocks and %zu mutexes). There are %zu readers and %zu writers holding this lock:\n",
324
+ rwlock,
325
+ gettid(), netdata_thread_tag(),
326
+ function, line, file,
327
+ reason, locktype,
328
+ netdata_locks_acquired_rwlocks, netdata_locks_acquired_mutexes,
329
+ rwlock->readers, rwlock->writers);
330
+
331
+ int i;
332
+ usec_t now = now_monotonic_high_precision_usec();
333
+ netdata_rwlock_locker *p;
334
+ for(i = 1, p = rwlock->lockers; p ;p = p->next, i++) {
335
+ fprintf(stderr,
336
+ " => %i: RW_LOCK %p: process %d '%s' (function %s() %lu@%s) is having %zu '%c' lock for %llu usec.\n",
337
+ i, rwlock,
338
+ p->pid, p->tag,
339
+ p->function, p->line, p->file,
340
+ p->callers, p->lock,
341
+ (now - p->start_s));
342
+
343
+ if(p->all_caller_locks) {
344
+ // find the lock in the netdata_thread_locks[]
345
+ // and remove it
346
+ int k;
347
+ for(k = 0; k < NETDATA_THREAD_LOCKS_ARRAY_SIZE ;k++) {
348
+ if (p->all_caller_locks[k] && p->all_caller_locks[k] != rwlock) {
349
+
350
+ // lock the other lock lockers list
351
+ __netdata_mutex_lock(&p->all_caller_locks[k]->lockers_mutex);
352
+
353
+ // print the list of lockers of the other lock
354
+ netdata_rwlock_locker *r;
355
+ int j;
356
+ for(j = 1, r = p->all_caller_locks[k]->lockers; r ;r = r->next, j++) {
357
+ fprintf(
358
+ stderr,
359
+ " ~~~> %i: RW_LOCK %p: process %d '%s' (function %s() %lu@%s) is having %zu '%c' lock for %llu usec.\n",
360
+ j,
361
+ p->all_caller_locks[k],
362
+ r->pid,
363
+ r->tag,
364
+ r->function,
365
+ r->line,
366
+ r->file,
367
+ r->callers,
368
+ r->lock,
369
+ (now - r->start_s));
370
+ }
371
+
372
+ // unlock the other lock lockers list
373
+ __netdata_mutex_unlock(&p->all_caller_locks[k]->lockers_mutex);
374
+ }
375
+ }
376
+ }
377
+
378
+ }
379
+ __netdata_mutex_unlock(&rwlock->lockers_mutex);
380
+
381
+ // unlock this function for other threads
382
+ __netdata_mutex_unlock(&log_lockers_mutex);
383
+}
384
+
385
+static netdata_rwlock_locker *add_rwlock_locker(const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock, char lock_type) {
386
+ netdata_rwlock_locker *p = mallocz(sizeof(netdata_rwlock_locker));
387
+ p->pid = gettid();
388
+ p->tag = netdata_thread_tag();
389
+ p->lock = lock_type;
390
+ p->file = file;
391
+ p->function = function;
392
+ p->line = line;
393
+ p->callers = 1;
394
+ p->all_caller_locks = netdata_thread_locks;
395
+ p->start_s = now_monotonic_high_precision_usec();
396
+
397
+ // find a slot in the netdata_thread_locks[]
398
+ int i;
399
+ for(i = 0; i < NETDATA_THREAD_LOCKS_ARRAY_SIZE ;i++) {
400
+ if (!netdata_thread_locks[i]) {
401
+ netdata_thread_locks[i] = rwlock;
402
+ break;
403
+ }
404
+ }
405
+
406
+ __netdata_mutex_lock(&rwlock->lockers_mutex);
407
+ p->next = rwlock->lockers;
408
+ rwlock->lockers = p;
409
+ if(lock_type == 'R') rwlock->readers++;
410
+ if(lock_type == 'W') rwlock->writers++;
411
+ __netdata_mutex_unlock(&rwlock->lockers_mutex);
412
+
413
+ return p;
414
+}
415
+
416
+static void remove_rwlock_locker(const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock, netdata_rwlock_locker *locker) {
417
+ usec_t end_s = now_monotonic_high_precision_usec();
418
+
419
+ if(locker->callers == 0)
420
+ fprintf(stderr,
421
+ "RW_LOCK ON LOCK %p: %d, '%s' (function %s() %lu@%s) callers should be positive but it is zero\n",
422
+ rwlock,
423
+ locker->pid, locker->tag,
424
+ locker->function, locker->line, locker->file);
425
+
426
+ if(locker->callers > 1 && locker->lock != 'R')
427
+ fprintf(stderr,
428
+ "RW_LOCK ON LOCK %p: %d, '%s' (function %s() %lu@%s) only 'R' locks support multiple holders, but here we have %zu callers holding a '%c' lock.\n",
429
+ rwlock,
430
+ locker->pid, locker->tag,
431
+ locker->function, locker->line, locker->file,
432
+ locker->callers, locker->lock);
433
+
434
+ __netdata_mutex_lock(&rwlock->lockers_mutex);
435
+ locker->callers--;
436
+
437
+ if(!locker->callers) {
438
+ int doit = 0;
439
+
440
+ if (rwlock->lockers == locker) {
441
+ rwlock->lockers = locker->next;
442
+ doit = 1;
443
+ } else {
444
+ netdata_rwlock_locker *p;
445
+ for (p = rwlock->lockers; p && p->next != locker; p = p->next)
446
+ ;
447
+ if (p && p->next == locker) {
448
+ p->next = locker->next;
449
+ doit = 1;
450
+ }
451
+ }
452
+ if(doit) {
453
+ if(locker->lock == 'R') rwlock->readers--;
454
+ if(locker->lock == 'W') rwlock->writers--;
455
+ }
456
+
457
+ if(!doit) {
458
+ fprintf(stderr,
459
+ "RW_LOCK ON LOCK %p: %d, '%s' (function %s() %lu@%s) with %zu x '%c' lock is not found.\n",
460
+ rwlock,
461
+ locker->pid, locker->tag,
462
+ locker->function, locker->line, locker->file,
463
+ locker->callers, locker->lock);
464
+ }
465
+ else {
466
+ // find the lock in the netdata_thread_locks[]
467
+ // and remove it
468
+ int i;
469
+ for(i = 0; i < NETDATA_THREAD_LOCKS_ARRAY_SIZE ;i++) {
470
+ if (netdata_thread_locks[i] == rwlock)
471
+ netdata_thread_locks[i] = NULL;
472
+ }
473
+
474
+ if(end_s - locker->start_s >= NETDATA_TRACE_RWLOCKS_HOLD_TIME_TO_IGNORE_USEC)
475
+ fprintf(stderr,
476
+ "RW_LOCK ON LOCK %p: %d, '%s' (function %s() %lu@%s) holded a '%c' for %llu usec.\n",
477
+ rwlock,
478
+ locker->pid, locker->tag,
479
+ locker->function, locker->line, locker->file,
480
+ locker->lock, end_s - locker->start_s);
481
+
482
+ freez(locker);
483
+ }
484
+ }
485
+
486
+ __netdata_mutex_unlock(&rwlock->lockers_mutex);
487
+}
488
+
489
+static netdata_rwlock_locker *find_rwlock_locker(const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
490
+ pid_t pid = gettid();
491
+ netdata_rwlock_locker *p;
492
+
493
+ __netdata_mutex_lock(&rwlock->lockers_mutex);
494
+ for(p = rwlock->lockers; p ;p = p->next) {
495
+ if(p->pid == pid) break;
496
+ }
497
+ __netdata_mutex_unlock(&rwlock->lockers_mutex);
498
+
499
+ return p;
500
+}
501
+
502
+static netdata_rwlock_locker *update_or_add_rwlock_locker(const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock, netdata_rwlock_locker *locker, char locktype) {
503
+ if(!locker) {
504
+ return add_rwlock_locker(file, function, line, rwlock, locktype);
505
+ }
506
+ else if(locker->lock == 'R' && locktype == 'R') {
507
+ __netdata_mutex_lock(&rwlock->lockers_mutex);
508
+ locker->callers++;
509
+ __netdata_mutex_unlock(&rwlock->lockers_mutex);
510
+ return locker;
511
+ }
512
+ else {
513
+ not_supported_by_posix_rwlocks(file, function, line, rwlock, locktype, "DEADLOCK - WANTS TO CHANGE LOCK TYPE BUT ALREADY HAS THIS LOCKED");
514
+ return locker;
515
+ }
516
+}
517
+
518
+// ----------------------------------------------------------------------------
519
+// debug versions of rwlock
520
521
int netdata_rwlock_destroy_debug(const char *file __maybe_unused, const char *function __maybe_unused,
522
const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
228
- usec_t start = 0;
229
- (void)start;
523
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_destroy(%p) from %lu@%s, %s()", rwlock, line, file, function);
524
231
- if(unlikely(debug_flags & D_LOCKS)) {
232
- start = now_boottime_usec();
233
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_destroy(0x%p) from %lu@%s, %s()", rwlock, line, file, function);
234
- }
525
+ if(rwlock->readers)
526
+ error("RW_LOCK: destroying a rwlock with %zu readers in it", rwlock->readers);
527
+ if(rwlock->writers)
528
+ error("RW_LOCK: destroying a rwlock with %zu writers in it", rwlock->writers);
529
530
int ret = __netdata_rwlock_destroy(rwlock);
531
+ if(!ret) {
532
+ while (rwlock->lockers)
533
+ remove_rwlock_locker(file, function, line, rwlock, rwlock->lockers);
534
+
535
+ if (rwlock->readers)
536
+ error("RW_LOCK: internal error - empty rwlock with %zu readers in it", rwlock->readers);
537
+ if (rwlock->writers)
538
+ error("RW_LOCK: internal error - empty rwlock with %zu writers in it", rwlock->writers);
539
+ }
540
238
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_destroy(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function);
541
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_destroy(%p) = %d, from %lu@%s, %s()", rwlock, ret, line, file, function);
542
543
return ret;
544
}
545
546
int netdata_rwlock_init_debug(const char *file __maybe_unused, const char *function __maybe_unused,
547
const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
245
- usec_t start = 0;
246
- (void)start;
247
-
248
- if(unlikely(debug_flags & D_LOCKS)) {
249
- start = now_boottime_usec();
250
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_init(0x%p) from %lu@%s, %s()", rwlock, line, file, function);
251
- }
548
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_init(%p) from %lu@%s, %s()", rwlock, line, file, function);
549
550
int ret = __netdata_rwlock_init(rwlock);
551
+ if(!ret) {
552
+ __netdata_mutex_init(&rwlock->lockers_mutex);
553
+ rwlock->lockers = NULL;
554
+ rwlock->readers = 0;
555
+ rwlock->writers = 0;
556
+ }
557
255
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_init(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function);
558
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_init(%p) = %d, from %lu@%s, %s()", rwlock, ret, line, file, function);
559
560
return ret;
561
}
562
563
int netdata_rwlock_rdlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
564
const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
262
- usec_t start = 0;
263
- (void)start;
565
265
- if(unlikely(debug_flags & D_LOCKS)) {
266
- start = now_boottime_usec();
267
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_rdlock(0x%p) from %lu@%s, %s()", rwlock, line, file, function);
566
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_rdlock(%p) from %lu@%s, %s()", rwlock, line, file, function);
567
+
568
+ netdata_rwlock_locker *locker = find_rwlock_locker(file, function, line, rwlock);
569
+
570
+#ifdef NETDATA_TRACE_RWLOCKS_LOG_NESTED
571
+ if(locker && locker->lock == 'R') {
572
+ log_rwlock_lockers(file, function, line, rwlock, "NESTED READ LOCK REQUEST", 'R');
573
+ }
574
+#endif // NETDATA_TRACE_RWLOCKS_LOG_NESTED
575
+
576
+ int log = 0;
577
+ if(rwlock->writers) {
578
+ log_rwlock_lockers(file, function, line, rwlock, "WANTS", 'R');
579
+ log = 1;
580
}
581
582
+ usec_t start_s = now_monotonic_high_precision_usec();
583
int ret = __netdata_rwlock_rdlock(rwlock);
584
+ usec_t end_s = now_monotonic_high_precision_usec();
585
+
586
+ if(!ret) {
587
+ locker = update_or_add_rwlock_locker(file, function, line, rwlock, locker, 'R');
588
+ if(log) log_rwlock_lockers(file, function, line, rwlock, "GOT", 'R');
589
+
590
+ }
591
272
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_rdlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function);
592
+ if(end_s - start_s >= NETDATA_TRACE_RWLOCKS_WAIT_TIME_TO_IGNORE_USEC)
593
+ fprintf(stderr,
594
+ "RW_LOCK ON LOCK %p: %d, '%s' (function %s() %lu@%s) WAITED for a READ lock for %llu usec.\n",
595
+ rwlock,
596
+ gettid(), netdata_thread_tag(),
597
+ function, line, file,
598
+ end_s - start_s);
599
+
600
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_rdlock(%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, end_s - start_s, line, file, function);
601
602
return ret;
603
}
604
605
int netdata_rwlock_wrlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
606
const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
279
- usec_t start = 0;
280
- (void)start;
607
282
- if(unlikely(debug_flags & D_LOCKS)) {
283
- start = now_boottime_usec();
284
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_wrlock(0x%p) from %lu@%s, %s()", rwlock, line, file, function);
608
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_wrlock(%p) from %lu@%s, %s()", rwlock, line, file, function);
609
+
610
+ netdata_rwlock_locker *locker = find_rwlock_locker(file, function, line, rwlock);
611
+ if(locker)
612
+ not_supported_by_posix_rwlocks(file, function, line, rwlock, 'W', "DEADLOCK - WANTS A WRITE LOCK BUT ALREADY HAVE THIS LOCKED");
613
+
614
+ int log = 0;
615
+ if(rwlock->readers) {
616
+ log_rwlock_lockers(file, function, line, rwlock, "WANTS", 'W');
617
+ log = 1;
618
}
619
620
+ usec_t start_s = now_monotonic_high_precision_usec();
621
int ret = __netdata_rwlock_wrlock(rwlock);
622
+ usec_t end_s = now_monotonic_high_precision_usec();
623
289
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_wrlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function);
624
+ if(!ret){
625
+ locker = update_or_add_rwlock_locker(file, function, line, rwlock, locker, 'W');
626
+ if(log) log_rwlock_lockers(file, function, line, rwlock, "GOT", 'W');
627
+ }
628
+
629
+ if(end_s - start_s >= NETDATA_TRACE_RWLOCKS_WAIT_TIME_TO_IGNORE_USEC)
630
+ fprintf(stderr,
631
+ "RW_LOCK ON LOCK %p: %d, '%s' (function %s() %lu@%s) WAITED for a WRITE lock for %llu usec.\n",
632
+ rwlock,
633
+ gettid(), netdata_thread_tag(),
634
+ function, line, file,
635
+ end_s - start_s);
636
+
637
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_wrlock(%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, end_s - start_s, line, file, function);
638
639
return ret;
640
}
641
642
int netdata_rwlock_unlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
643
const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
296
- usec_t start = 0;
297
- (void)start;
644
299
- if(unlikely(debug_flags & D_LOCKS)) {
300
- start = now_boottime_usec();
301
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_unlock(0x%p) from %lu@%s, %s()", rwlock, line, file, function);
302
- }
645
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_unlock(%p) from %lu@%s, %s()", rwlock, line, file, function);
646
647
+ netdata_rwlock_locker *locker = find_rwlock_locker(file, function, line, rwlock);
648
+ if(unlikely(!locker))
649
+ not_supported_by_posix_rwlocks(file, function, line, rwlock, 'U', "UNLOCK WITHOUT LOCK");
650
+
651
+ usec_t start_s = now_monotonic_high_precision_usec();
652
int ret = __netdata_rwlock_unlock(rwlock);
653
+ usec_t end_s = now_monotonic_high_precision_usec();
654
+
655
+ if(end_s - start_s >= NETDATA_TRACE_RWLOCKS_WAIT_TIME_TO_IGNORE_USEC)
656
+ fprintf(stderr,
657
+ "RW_LOCK ON LOCK %p: %d, '%s' (function %s() %lu@%s) WAITED to UNLOCK for %llu usec.\n",
658
+ rwlock,
659
+ gettid(), netdata_thread_tag(),
660
+ function, line, file,
661
+ end_s - start_s);
662
+
663
+ if(likely(!ret && locker)) remove_rwlock_locker(file, function, line, rwlock, locker);
664
306
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_unlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function);
665
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_unlock(%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, end_s - start_s, line, file, function);
666
667
return ret;
668
}
669
670
int netdata_rwlock_tryrdlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
671
const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
313
- usec_t start = 0;
314
- (void)start;
672
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_tryrdlock(%p) from %lu@%s, %s()", rwlock, line, file, function);
673
316
- if(unlikely(debug_flags & D_LOCKS)) {
317
- start = now_boottime_usec();
318
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_tryrdlock(0x%p) from %lu@%s, %s()", rwlock, line, file, function);
319
- }
674
+ netdata_rwlock_locker *locker = find_rwlock_locker(file, function, line, rwlock);
675
+ if(locker && locker->lock == 'W')
676
+ not_supported_by_posix_rwlocks(file, function, line, rwlock, 'R', "DEADLOCK - WANTS A READ LOCK BUT IT HAS A WRITE LOCK ALREADY");
677
678
+ usec_t start_s = now_monotonic_high_precision_usec();
679
int ret = __netdata_rwlock_tryrdlock(rwlock);
680
+ usec_t end_s = now_monotonic_high_precision_usec();
681
+
682
+ if(!ret)
683
+ locker = update_or_add_rwlock_locker(file, function, line, rwlock, locker, 'R');
684
323
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_tryrdlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function);
685
+ if(end_s - start_s >= NETDATA_TRACE_RWLOCKS_WAIT_TIME_TO_IGNORE_USEC)
686
+ fprintf(stderr,
687
+ "RW_LOCK ON LOCK %p: %d, '%s' (function %s() %lu@%s) WAITED to TRYREAD for %llu usec.\n",
688
+ rwlock,
689
+ gettid(), netdata_thread_tag(),
690
+ function, line, file,
691
+ end_s - start_s);
692
+
693
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_tryrdlock(%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, end_s - start_s, line, file, function);
694
695
return ret;
696
}
697
698
int netdata_rwlock_trywrlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
699
const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
330
- usec_t start = 0;
331
- (void)start;
700
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_trywrlock(%p) from %lu@%s, %s()", rwlock, line, file, function);
701
333
- if(unlikely(debug_flags & D_LOCKS)) {
334
- start = now_boottime_usec();
335
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_trywrlock(0x%p) from %lu@%s, %s()", rwlock, line, file, function);
336
- }
702
+ netdata_rwlock_locker *locker = find_rwlock_locker(file, function, line, rwlock);
703
+ if(locker)
704
+ not_supported_by_posix_rwlocks(file, function, line, rwlock, 'W', "ALREADY HAS THIS LOCK");
705
706
+ usec_t start_s = now_monotonic_high_precision_usec();
707
int ret = __netdata_rwlock_trywrlock(rwlock);
708
+ usec_t end_s = now_monotonic_high_precision_usec();
709
340
- debug(D_LOCKS, "RW_LOCK: netdata_rwlock_trywrlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function);
710
+ if(!ret)
711
+ locker = update_or_add_rwlock_locker(file, function, line, rwlock, locker, 'W');
712
+
713
+ if(end_s - start_s >= NETDATA_TRACE_RWLOCKS_WAIT_TIME_TO_IGNORE_USEC)
714
+ fprintf(stderr,
715
+ "RW_LOCK ON LOCK %p: %d, '%s' (function %s() %lu@%s) WAITED to TRYWRITE for %llu usec.\n",
716
+ rwlock,
717
+ gettid(), netdata_thread_tag(),
718
+ function, line, file,
719
+ end_s - start_s);
720
+
721
+ debug(D_LOCKS, "RW_LOCK: netdata_rwlock_trywrlock(%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, end_s - start_s, line, file, function);
722
723
return ret;
724
}
725
+
726
+#endif // NETDATA_TRACE_RWLOCKS
libnetdata/locks/locks.h
+53
-9
@@ -4,14 +4,55 @@
4
#define NETDATA_LOCKS_H 1
5
6
#include "../libnetdata.h"
7
+#include "../clocks/clocks.h"
8
9
typedef pthread_mutex_t netdata_mutex_t;
10
#define NETDATA_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
11
11
-typedef pthread_rwlock_t netdata_rwlock_t;
12
-#define NETDATA_RWLOCK_INITIALIZER PTHREAD_RWLOCK_INITIALIZER
12
+#ifdef NETDATA_TRACE_RWLOCKS
13
+typedef struct netdata_rwlock_locker {
14
+ pid_t pid;
15
+ const char *tag;
16
+ char lock; // 'R', 'W'
17
+ const char *file;
18
+ const char *function;
19
+ unsigned long line;
20
+ size_t callers;
21
+ usec_t start_s;
22
+ struct netdata_rwlock_t **all_caller_locks;
23
+ struct netdata_rwlock_locker *next;
24
+} netdata_rwlock_locker;
25
+
26
+typedef struct netdata_rwlock_t {
27
+ pthread_rwlock_t rwlock_t; // the lock
28
+ size_t readers; // the number of reader on the lock
29
+ size_t writers; // the number of writers on the lock
30
+ netdata_mutex_t lockers_mutex; // a mutex to protect the linked list of the lock holding threads
31
+ netdata_rwlock_locker *lockers; // the linked list of the lock holding threads
32
+} netdata_rwlock_t;
33
+
34
+#define NETDATA_RWLOCK_INITIALIZER { \
35
+ .rwlock_t = PTHREAD_RWLOCK_INITIALIZER, \
36
+ .readers = 0, \
37
+ .writers = 0, \
38
+ .lockers_mutex = NETDATA_MUTEX_INITIALIZER, \
39
+ .lockers = NULL \
40
+ }
41
+
42
+#else // NETDATA_TRACE_RWLOCKS
43
+
44
+typedef struct netdata_rwlock_t {
45
+ pthread_rwlock_t rwlock_t;
46
+} netdata_rwlock_t;
47
+
48
+#define NETDATA_RWLOCK_INITIALIZER { \
49
+ .rwlock_t = PTHREAD_RWLOCK_INITIALIZER \
50
+ }
51
+
52
+#endif // NETDATA_TRACE_RWLOCKS
53
54
extern int __netdata_mutex_init(netdata_mutex_t *mutex);
55
+extern int __netdata_mutex_destroy(netdata_mutex_t *mutex);
56
extern int __netdata_mutex_lock(netdata_mutex_t *mutex);
57
extern int __netdata_mutex_trylock(netdata_mutex_t *mutex);
58
extern int __netdata_mutex_unlock(netdata_mutex_t *mutex);
@@ -24,7 +65,13 @@ extern int __netdata_rwlock_unlock(netdata_rwlock_t *rwlock);
65
extern int __netdata_rwlock_tryrdlock(netdata_rwlock_t *rwlock);
66
extern int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock);
67
68
+extern void netdata_thread_disable_cancelability(void);
69
+extern void netdata_thread_enable_cancelability(void);
70
+
71
+#ifdef NETDATA_TRACE_RWLOCKS
72
+
73
extern int netdata_mutex_init_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex);
74
+extern int netdata_mutex_destroy_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex);
75
extern int netdata_mutex_lock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex);
76
extern int netdata_mutex_trylock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex);
77
extern int netdata_mutex_unlock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex);
@@ -37,12 +84,8 @@ extern int netdata_rwlock_unlock_debug( const char *file, const char *function,
84
extern int netdata_rwlock_tryrdlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock);
85
extern int netdata_rwlock_trywrlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock);
86
40
-extern void netdata_thread_disable_cancelability(void);
41
-extern void netdata_thread_enable_cancelability(void);
42
-
43
-#ifdef NETDATA_INTERNAL_CHECKS
44
-
87
#define netdata_mutex_init(mutex) netdata_mutex_init_debug(__FILE__, __FUNCTION__, __LINE__, mutex)
88
+#define netdata_mutex_destroy(mutex) netdata_mutex_init_debug(__FILE__, __FUNCTION__, __LINE__, mutex)
89
#define netdata_mutex_lock(mutex) netdata_mutex_lock_debug(__FILE__, __FUNCTION__, __LINE__, mutex)
90
#define netdata_mutex_trylock(mutex) netdata_mutex_trylock_debug(__FILE__, __FUNCTION__, __LINE__, mutex)
91
#define netdata_mutex_unlock(mutex) netdata_mutex_unlock_debug(__FILE__, __FUNCTION__, __LINE__, mutex)
@@ -55,9 +98,10 @@ extern void netdata_thread_enable_cancelability(void);
98
#define netdata_rwlock_tryrdlock(rwlock) netdata_rwlock_tryrdlock_debug(__FILE__, __FUNCTION__, __LINE__, rwlock)
99
#define netdata_rwlock_trywrlock(rwlock) netdata_rwlock_trywrlock_debug(__FILE__, __FUNCTION__, __LINE__, rwlock)
100
58
-#else // !NETDATA_INTERNAL_CHECKS
101
+#else // !NETDATA_TRACE_RWLOCKS
102
103
#define netdata_mutex_init(mutex) __netdata_mutex_init(mutex)
104
+#define netdata_mutex_destroy(mutex) __netdata_mutex_destroy(mutex)
105
#define netdata_mutex_lock(mutex) __netdata_mutex_lock(mutex)
106
#define netdata_mutex_trylock(mutex) __netdata_mutex_trylock(mutex)
107
#define netdata_mutex_unlock(mutex) __netdata_mutex_unlock(mutex)
@@ -70,6 +114,6 @@ extern void netdata_thread_enable_cancelability(void);
114
#define netdata_rwlock_tryrdlock(rwlock) __netdata_rwlock_tryrdlock(rwlock)
115
#define netdata_rwlock_trywrlock(rwlock) __netdata_rwlock_trywrlock(rwlock)
116
73
-#endif // NETDATA_INTERNAL_CHECKS
117
+#endif // NETDATA_TRACE_RWLOCKS
118
119
#endif //NETDATA_LOCKS_H
spawn/spawn_server.c
+2
-2
@@ -312,8 +312,8 @@ void spawn_server(void)
312
{
313
int error;
314
315
- test_clock_boottime();
316
- test_clock_monotonic_coarse();
315
+ // initialize the system clocks
316
+ clocks_init();
317
318
// close all open file descriptors, except the standard ones
319
// the caller may have left open files (lxc-attach has this issue)