/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2008-2017. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* %CopyrightEnd%
*/
/*
* Description: Statistics for locks.
*
* Author: Björn-Egil Dahlberg
* Date: 2008-07-03
* Abstract:
* Locks statistics internal representation.
*
* Conceptual representation,
* - set name
* | - id (the unique lock)
* | | - lock type
* | | - statistics
* | | | - location (file and line number)
* | | | - tries
* | | | - collisions (including trylock busy)
* | | | - timer (time spent in waiting for lock)
* | | | - n_timer (collisions excluding trylock busy)
* | | | - histogram
* | | | | - # 0 = log2(lock wait_time ns)
* | | | | - ...
* | | | | - # n = log2(lock wait_time ns)
*
* Each instance of a lock is the unique lock, i.e. set and id in that set.
* For each lock there is a set of statistics with where and what impact
* the lock aqusition had.
*
* Runtime options
* - suspend, used when internal lock-counting can't be applied. For instance
* when allocating a term for the outside and halloc needs to be used.
* Default: off.
* - location, reserved and not used.
* - proclock, disable proclock counting. Used when performance might be an
* issue. Accessible from erts_debug:lock_counters({process_locks, bool()}).
* Default: off.
* - copysave, enable saving of destroyed locks (and thereby its statistics).
* If memory constraints is an issue this need to be disabled.
* Accessible from erts_debug:lock_counters({copy_save, bool()}).
* Default: off.
*
*/
#include "sys.h"
#ifndef ERTS_LOCK_COUNT_H__
#define ERTS_LOCK_COUNT_H__
#ifdef ERTS_ENABLE_LOCK_COUNT
#ifndef ERTS_ENABLE_LOCK_POSITION
/* Enable in order for _x variants of mtx functions to be used. */
#define ERTS_ENABLE_LOCK_POSITION 1
#endif
#include "ethread.h"
#define ERTS_LCNT_MAX_LOCK_LOCATIONS (10)
/* histogram */
#define ERTS_LCNT_HISTOGRAM_MAX_NS (((unsigned long)1LL << 28) - 1)
#if 0 || defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT)
#define ERTS_LCNT_HISTOGRAM_SLOT_SIZE (30)
#define ERTS_LCNT_HISTOGRAM_RSHIFT (0)
#else
#define ERTS_LCNT_HISTOGRAM_SLOT_SIZE (20)
#define ERTS_LCNT_HISTOGRAM_RSHIFT (10)
#endif
#define ERTS_LCNT_LT_SPINLOCK (((Uint16) 1) << 0)
#define ERTS_LCNT_LT_RWSPINLOCK (((Uint16) 1) << 1)
#define ERTS_LCNT_LT_MUTEX (((Uint16) 1) << 2)
#define ERTS_LCNT_LT_RWMUTEX (((Uint16) 1) << 3)
#define ERTS_LCNT_LT_PROCLOCK (((Uint16) 1) << 4)
#define ERTS_LCNT_LT_ALLOC (((Uint16) 1) << 5)
#define ERTS_LCNT_LO_READ (((Uint16) 1) << 6)
#define ERTS_LCNT_LO_WRITE (((Uint16) 1) << 7)
#define ERTS_LCNT_LO_READ_WRITE ( ERTS_LCNT_LO_READ \
| ERTS_LCNT_LO_WRITE )
#define ERTS_LCNT_LT_ALL ( ERTS_LCNT_LT_SPINLOCK \
| ERTS_LCNT_LT_RWSPINLOCK \
| ERTS_LCNT_LT_MUTEX \
| ERTS_LCNT_LT_RWMUTEX \
| ERTS_LCNT_LT_PROCLOCK )
#define ERTS_LCNT_LOCK_TYPE(lock) ((lock)->flag & ERTS_LCNT_LT_ALL)
#define ERTS_LCNT_IS_LOCK_INVALID(lock) (!((lock)->flag & ERTS_LCNT_LT_ALL))
#define ERTS_LCNT_CLEAR_FLAG(lock) ((lock)->flag = 0)
/* runtime options */
#define ERTS_LCNT_OPT_SUSPEND (((Uint16) 1) << 0)
#define ERTS_LCNT_OPT_LOCATION (((Uint16) 1) << 1)
#define ERTS_LCNT_OPT_PROCLOCK (((Uint16) 1) << 2)
#define ERTS_LCNT_OPT_PORTLOCK (((Uint16) 1) << 3)
#define ERTS_LCNT_OPT_COPYSAVE (((Uint16) 1) << 4)
typedef struct {
unsigned long s;
unsigned long ns;
} erts_lcnt_time_t;
extern erts_lcnt_time_t timer_start;
typedef struct {
Uint32 ns[ERTS_LCNT_HISTOGRAM_SLOT_SIZE]; /* log2 array of nano seconds occurences */
} erts_lcnt_hist_t;
typedef struct erts_lcnt_lock_stats_s {
/* "tries" and "colls" needs to be atomic since
* trylock busy does not acquire a lock and there
* is no post action to rectify the situation
*/
char *file; /* which file the lock was taken */
unsigned int line; /* line number in file */
ethr_atomic_t tries; /* n tries to get lock */
ethr_atomic_t colls; /* n collisions of tries to get lock */
unsigned long timer_n; /* #times waited for lock */
erts_lcnt_time_t timer; /* total wait time for lock */
erts_lcnt_hist_t hist;
} erts_lcnt_lock_stats_t;
/* rw locks uses both states, other locks only uses w_state */
typedef struct erts_lcnt_lock_s {
char *name; /* lock name */
Uint16 flag; /* lock type */
Eterm id; /* id if possible */
#ifdef DEBUG
ethr_atomic_t flowstate;
#endif
/* lock states */
ethr_atomic_t w_state; /* 0 not taken, otherwise n threads waiting */
ethr_atomic_t r_state; /* 0 not taken, > 0 -> writes will wait */
/* statistics */
unsigned int n_stats;
erts_lcnt_lock_stats_t stats[ERTS_LCNT_MAX_LOCK_LOCATIONS]; /* first entry is "undefined"*/
/* chains for list handling */
/* data is hold by lcnt_lock */
struct erts_lcnt_lock_s *prev;
struct erts_lcnt_lock_s *next;
} erts_lcnt_lock_t;
typedef struct {
erts_lcnt_lock_t *head;
erts_lcnt_lock_t *tail;
unsigned long n;
} erts_lcnt_lock_list_t;
typedef struct {
erts_lcnt_time_t duration; /* time since last clear */
erts_lcnt_lock_list_t *current_locks;
erts_lcnt_lock_list_t *deleted_locks;
} erts_lcnt_data_t;
typedef struct {
int id;
erts_lcnt_time_t timer; /* timer */
int timer_set; /* bool */
int lock_in_conflict; /* bool */
} erts_lcnt_thread_data_t;
/* globals */
extern Uint16 erts_lcnt_rt_options;
/* function declerations */
void erts_lcnt_init(void);
void erts_lcnt_late_init(void);
/* thread operations */
void erts_lcnt_thread_setup(void);
void erts_lcnt_thread_exit_handler(void);
/* list operations (local) */
erts_lcnt_lock_list_t *erts_lcnt_list_init(void);
void erts_lcnt_list_clear( erts_lcnt_lock_list_t *list);
void erts_lcnt_list_insert(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock);
void erts_lcnt_list_delete(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock);
/* lock operations (global) */
void erts_lcnt_init_lock(erts_lcnt_lock_t *lock, char *name, Uint16 flag);
void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id);
void erts_lcnt_init_lock_empty(erts_lcnt_lock_t *lock);
void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock);
void erts_lcnt_lock(erts_lcnt_lock_t *lock);
void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option);
void erts_lcnt_lock_post(erts_lcnt_lock_t *lock);
void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line);
void erts_lcnt_lock_unaquire(erts_lcnt_lock_t *lock);
void erts_lcnt_unlock(erts_lcnt_lock_t *lock);
void erts_lcnt_unlock_opt(erts_lcnt_lock_t *lock, Uint16 option);
void erts_lcnt_trylock_opt(erts_lcnt_lock_t *lock, int res, Uint16 option);
void erts_lcnt_trylock(erts_lcnt_lock_t *lock, int res);
/* bif interface */
Uint16 erts_lcnt_set_rt_opt(Uint16 opt);
Uint16 erts_lcnt_clear_rt_opt(Uint16 opt);
void erts_lcnt_clear_counters(void);
char *erts_lcnt_lock_type(Uint16 type);
erts_lcnt_data_t *erts_lcnt_get_data(void);
#endif /* ifdef ERTS_ENABLE_LOCK_COUNT */
#endif /* ifndef ERTS_LOCK_COUNT_H__ */