@cryptotaxi247 / netdata-1 / commits / 6bd2dcd29

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, &timespec) 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, &timespec) 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)