1
/* ``The contents of this file are subject to the Erlang Public License,
4
* Copyright Ericsson AB 2008-2009. All Rights Reserved.
6
* The contents of this file are subject to the Erlang Public License,
2
7
* Version 1.1, (the "License"); you may not use this file except in
3
8
* compliance with the License. You should have received a copy of the
4
9
* Erlang Public License along with this software. If not, it can be
5
* retrieved via the world wide web at http://www.erlang.org/.
10
* retrieved online at http://www.erlang.org/.
7
12
* Software distributed under the License is distributed on an "AS IS"
8
13
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
14
* the License for the specific language governing rights and limitations
10
15
* under the License.
12
* The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
* Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
* AB. All Rights Reserved.''
19
21
* Description: Statistics for locks.
76
static void lcnt_clear_stats(erts_lcnt_lock_stats_t *stats) {
77
ethr_atomic_set(&stats->tries, 0);
78
ethr_atomic_set(&stats->colls, 0);
82
stats->file = (char *)str_undefined;
72
86
static void lcnt_time(erts_lcnt_time_t *time) {
74
89
hr_time = sys_gethrtime();
75
90
time->s = (unsigned long)(hr_time / 1000000000LL);
76
91
time->ns = (unsigned long)(hr_time - 1000000000LL*time->s);
94
sys_gettimeofday(&tv);
96
time->ns = tv.tv_usec*1000LL;
100
static void lcnt_time_diff(erts_lcnt_time_t *d, erts_lcnt_time_t *t1, erts_lcnt_time_t *t0) {
105
dns = t1->ns - t0->ns;
107
/* the difference should not be able to get bigger than 1 sec in ns*/
118
/* difference d must be positive */
120
static void lcnt_time_add(erts_lcnt_time_t *t, erts_lcnt_time_t *d) {
121
unsigned long ngns = 0;
126
ngns = t->ns / 1000000000LL;
127
t->ns = t->ns % 1000000000LL;
79
132
static erts_lcnt_thread_data_t *lcnt_thread_data_alloc(void) {
80
133
erts_lcnt_thread_data_t *eltd;
82
135
eltd = (erts_lcnt_thread_data_t*)malloc(sizeof(erts_lcnt_thread_data_t));
83
136
eltd->timer_set = 0;
137
eltd->lock_in_conflict = 0;
84
139
eltd->id = lcnt_n_thr++;
140
/* set thread data to array */
141
lcnt_thread_data[eltd->id] = eltd;
97
154
static char* lock_opt(Uint16 flag) {
98
155
if ((flag & ERTS_LCNT_LO_WRITE) && (flag & ERTS_LCNT_LO_READ)) return "rw";
99
if (flag & ERTS_LCNT_LO_READ ) return "r ";
100
if (flag & ERTS_LCNT_LO_WRITE ) return " w";
156
if (flag & ERTS_LCNT_LO_READ ) return "r ";
157
if (flag & ERTS_LCNT_LO_WRITE) return " w";
104
static void print_colls(erts_lcnt_lock_t *lock, char *action, Uint16 opt) {
105
long r_state, w_state;
107
erts_lcnt_thread_data_t *eltd = NULL;
109
ethr_atomic_read(&lock->r_state, &r_state);
110
ethr_atomic_read(&lock->w_state, &w_state);
112
eltd = lcnt_get_thread_data();
115
sprintf(buffer, "[%d/%ld] %s %s:", eltd->id, (long)ethr_self(), action, lock_opt(opt));
116
print_lock(lock, buffer);
118
fprintf(stderr, "A null thread =(\r\n");
122
static void print_lock(erts_lcnt_lock_t *lock, char *action) {
161
static void print_lock_x(erts_lcnt_lock_t *lock, Uint16 flag, char *action, char *extra) {
123
162
long int colls, tries, w_state, r_state;
163
erts_lcnt_lock_stats_t *stats = NULL;
126
type = lock_type(lock->flag);
127
ethr_atomic_read(&lock->tries, &tries);
128
ethr_atomic_read(&lock->colls, &colls);
169
type = lcnt_lock_type(lock->flag);
129
170
ethr_atomic_read(&lock->r_state, &r_state);
130
171
ethr_atomic_read(&lock->w_state, &w_state);
132
173
if (tries > 0) rate = (float)(colls/(float)tries)*100;
133
174
else rate = 0.0f;
135
fprintf(stderr, "%8s [%25s] [type %12s] [r/w state %2ld/%2ld] [tries %9ld] [colls %9ld] [rate %3.3f %%] [acc %ld %ld (%ld)]\r\n",
136
action, lock->name, type, r_state, w_state, tries, colls, rate, lock->timer_s, lock->timer_ns, lock->timer_n);
176
if (lock->flag & flag) {
177
erts_printf("%20s [%30s] [r/w state %4ld/%4ld] id %T %s\r\n",
185
for(i = 0; i < lock->n_stats; i++) {
186
stats = &(lock->stats[i]);
187
ethr_atomic_read(&stats->tries, &tries);
188
ethr_atomic_read(&stats->colls, &colls);
189
fprintf(stderr, "%69s:%5d [tries %9ld] [colls %9ld] [timer_n %8ld] [timer %4ld s %6ld us]\r\n",
196
(unsigned long)stats->timer.ns/1000);
198
fprintf(stderr, "\r\n");
202
static void print_lock(erts_lcnt_lock_t *lock, char *action) {
203
print_lock_x(lock, ERTS_LCNT_LT_ALL, action, "");
208
static erts_lcnt_lock_stats_t *lcnt_get_lock_stats(erts_lcnt_lock_t *lock, char *file, unsigned int line) {
210
erts_lcnt_lock_stats_t *stats = NULL;
212
for (i = 0; i < lock->n_stats; i++) {
213
if ((lock->stats[i].file == file) && (lock->stats[i].line == line)) {
214
return &(lock->stats[i]);
217
if (lock->n_stats < ERTS_LCNT_MAX_LOCK_LOCATIONS) {
218
stats = &lock->stats[lock->n_stats];
225
return &lock->stats[0];
229
static void lcnt_update_stats(erts_lcnt_lock_stats_t *stats, int lock_in_conflict, erts_lcnt_time_t *time_wait) {
231
ethr_atomic_inc(&stats->tries);
233
/* beware of trylock */
234
if (lock_in_conflict) ethr_atomic_inc(&stats->colls);
237
lcnt_time_add(&(stats->timer), time_wait);
141
246
void erts_lcnt_init() {
142
247
erts_lcnt_thread_data_t *eltd = NULL;
231
342
/* interface to erl_threads.h */
232
343
/* only lock on init and destroy, all others should use atomics */
233
344
void erts_lcnt_init_lock(erts_lcnt_lock_t *lock, char *name, Uint16 flag ) {
234
erts_lcnt_init_lock_extra(lock, name, flag, am_undefined);
345
erts_lcnt_init_lock_x(lock, name, flag, am_undefined);
237
void erts_lcnt_init_lock_extra(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id) {
347
void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id) {
239
351
lock->next = NULL;
240
352
lock->prev = NULL;
241
353
lock->flag = flag;
242
354
lock->name = name;
247
357
ethr_atomic_init(&lock->r_state, 0);
248
358
ethr_atomic_init(&lock->w_state, 0);
249
ethr_atomic_init(&lock->tries, 0);
250
ethr_atomic_init(&lock->colls, 0);
361
ethr_atomic_init(&lock->flowstate, 0);
366
for (i = 0; i < ERTS_LCNT_MAX_LOCK_LOCATIONS; i++) {
367
lcnt_clear_stats(&lock->stats[i]);
258
369
erts_lcnt_list_insert(erts_lcnt_data->current_locks, lock);
264
374
void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) {
297
412
if (option & ERTS_LCNT_LO_READ) {
298
413
ethr_atomic_inc( &lock->r_state);
300
416
/* we cannot acquire w_lock if either w or r are taken */
301
417
/* we cannot acquire r_lock if w_lock is taken */
302
419
if ((w_state > 0) || (r_state > 0)){
303
ethr_atomic_inc(&lock->colls);
304
eltd = lcnt_get_thread_data();
305
lcnt_time(&eltd->timer);
420
eltd->lock_in_conflict = 1;
421
if (eltd->timer_set == 0) lcnt_time(&eltd->timer);
306
422
eltd->timer_set++;
307
if (eltd->timer_set > 1) abort();
424
eltd->lock_in_conflict = 0;
312
428
void erts_lcnt_lock(erts_lcnt_lock_t *lock) {
314
430
erts_lcnt_thread_data_t *eltd;
315
ethr_atomic_inc(&lock->tries);
316
/* perhaps a lock here instead of atomic? */
432
if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
317
434
ethr_atomic_read(&lock->w_state, &w_state);
318
435
ethr_atomic_inc( &lock->w_state);
437
eltd = lcnt_get_thread_data();
320
441
if (w_state > 0) {
321
ethr_atomic_inc(&lock->colls);
322
eltd = lcnt_get_thread_data();
323
lcnt_time(&eltd->timer);
442
eltd->lock_in_conflict = 1;
443
/* only set the timer if nobody else has it
444
* This should only happen when proc_locks aquires several locks
445
* 'atomicly'. All other locks will block the thread if w_state > 0
448
if (eltd->timer_set == 0) lcnt_time(&eltd->timer);
324
449
eltd->timer_set++;
325
if (eltd->timer_set > 1) abort();
452
eltd->lock_in_conflict = 0;
456
/* if a lock wasn't really a lock operation, bad bad process locks */
458
void erts_lcnt_lock_unaquire(erts_lcnt_lock_t *lock) {
459
/* should check if this thread was "waiting" */
461
if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
463
ethr_atomic_dec( &lock->w_state);
466
/* erts_lcnt_lock_post
467
* used when we get a lock (i.e. directly after a lock operation)
468
* if the timer was set then we had to wait for the lock
469
* lock_post will calculate the wait time.
331
471
void erts_lcnt_lock_post(erts_lcnt_lock_t *lock) {
472
erts_lcnt_lock_post_x(lock, (char*)str_undefined, 0);
475
void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line) {
332
476
erts_lcnt_thread_data_t *eltd;
333
477
erts_lcnt_time_t timer;
478
erts_lcnt_time_t time_wait;
479
erts_lcnt_lock_stats_t *stats;
484
if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
487
if (!(lock->flag & (ERTS_LCNT_LT_RWMUTEX | ERTS_LCNT_LT_RWSPINLOCK))) {
488
ethr_atomic_read(&lock->flowstate, &flowstate);
489
ASSERT(flowstate == 0);
490
ethr_atomic_inc( &lock->flowstate);
336
494
eltd = lcnt_get_thread_data();
498
/* if lock was in conflict, time it */
500
stats = lcnt_get_lock_stats(lock, file, line);
337
502
if (eltd->timer_set) {
338
503
lcnt_time(&timer);
340
ds = timer.s - eltd->timer.s;
341
dns = timer.ns - eltd->timer.ns;
348
505
eltd->timer_set--;
353
lock->timer_ns += dns;
355
if (lock->timer_ns > 1000000000) {
357
lock->timer_ns -= 1000000000;
360
if (eltd->timer_set < 0) abort();
507
lcnt_time_diff(&time_wait, &timer, &(eltd->timer));
508
lcnt_update_stats(stats, eltd->lock_in_conflict, &time_wait);
510
ASSERT(eltd->timer_set >= 0);
512
lcnt_update_stats(stats, eltd->lock_in_conflict, NULL);
366
519
void erts_lcnt_unlock_opt(erts_lcnt_lock_t *lock, Uint16 option) {
520
if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
367
521
if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_dec(&lock->w_state);
368
522
if (option & ERTS_LCNT_LO_READ ) ethr_atomic_dec(&lock->r_state);
371
525
void erts_lcnt_unlock(erts_lcnt_lock_t *lock) {
530
if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
533
ethr_atomic_read(&lock->flowstate, &flowstate);
534
ASSERT(flowstate == 1);
535
ethr_atomic_dec( &lock->flowstate);
538
ethr_atomic_read(&lock->w_state, &w_state);
372
541
ethr_atomic_dec(&lock->w_state);
377
546
void erts_lcnt_trylock_opt(erts_lcnt_lock_t *lock, int res, Uint16 option) {
547
if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
378
548
/* Determine lock_state via res instead of state */
380
ethr_atomic_inc(&lock->tries);
382
549
if (res != EBUSY) {
383
550
if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_inc(&lock->w_state);
384
551
if (option & ERTS_LCNT_LO_READ ) ethr_atomic_inc(&lock->r_state);
385
} else ethr_atomic_inc(&lock->colls);
552
lcnt_update_stats(&(lock->stats[0]), 0, NULL);
554
ethr_atomic_inc(&lock->stats[0].tries);
555
ethr_atomic_inc(&lock->stats[0].colls);
390
560
void erts_lcnt_trylock(erts_lcnt_lock_t *lock, int res) {
391
561
/* Determine lock_state via res instead of state */
393
ethr_atomic_inc(&lock->tries);
394
if (res != EBUSY) ethr_atomic_inc(&lock->w_state);
395
else ethr_atomic_inc(&lock->colls);
565
if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
569
ethr_atomic_read(&lock->flowstate, &flowstate);
570
ASSERT(flowstate == 0);
571
ethr_atomic_inc( &lock->flowstate);
573
ethr_atomic_inc(&lock->w_state);
575
lcnt_update_stats(&(lock->stats[0]), 0, NULL);
578
ethr_atomic_inc(&lock->stats[0].tries);
579
ethr_atomic_inc(&lock->stats[0].colls);
398
583
/* thread operations */
400
static void lcnt_thr_init(erts_lcnt_thread_data_t *eltd) {
401
void * (*function)(void *);
585
static void *lcnt_thr_init(erts_lcnt_thread_data_t *eltd) {
586
void *(*function)(void *);
404
589
function = eltd->function;
405
590
argument = eltd->argument;
407
res = ethr_tsd_set(lcnt_thr_data_key, eltd);
592
ethr_tsd_set(lcnt_thr_data_key, eltd);
594
res = (void *)function(argument);
427
616
/* bindings for bifs */
428
/* to block or not to block or lock perhaps */
618
Uint16 erts_lcnt_set_rt_opt(Uint16 opt) {
620
prev = (erts_lcnt_rt_options & opt);
621
erts_lcnt_rt_options |= opt;
625
Uint16 erts_lcnt_clear_rt_opt(Uint16 opt) {
627
prev = (erts_lcnt_rt_options & opt);
628
erts_lcnt_rt_options &= ~opt;
431
632
void erts_lcnt_clear_counters(void) {
432
633
erts_lcnt_lock_t *lock;
433
634
erts_lcnt_lock_list_t *list;
635
erts_lcnt_lock_stats_t *stats;
437
640
list = erts_lcnt_data->current_locks;
438
642
for (lock = list->head; lock != NULL; lock = lock->next) {
439
ethr_atomic_set(&lock->tries, 0);
440
ethr_atomic_set(&lock->colls, 0);
643
for( i = 0; i < ERTS_LCNT_MAX_LOCK_LOCATIONS; i++) {
644
stats = &lock->stats[i];
645
lcnt_clear_stats(stats);
447
650
/* empty deleted locks in lock list */
448
651
erts_lcnt_list_clear(erts_lcnt_data->deleted_locks);
653
lcnt_time(&timer_start);
453
658
erts_lcnt_data_t *erts_lcnt_get_data(void) {
659
erts_lcnt_time_t timer_stop;
663
lcnt_time(&timer_stop);
664
lcnt_time_diff(&(erts_lcnt_data->duration), &timer_stop, &timer_start);
454
668
return erts_lcnt_data;
457
671
char *erts_lcnt_lock_type(Uint16 type) {
458
return lock_type(type);
672
return lcnt_lock_type(type);
461
675
#endif /* ifdef ERTS_ENABLE_LOCK_COUNT */