.TH lcnt 3erl "tools 3.6" "Ericsson AB" "Erlang Module Definition" .SH NAME lcnt \- A runtime system Lock Profiling tool. .SH DESCRIPTION .LP The \fIlcnt\fR\& module is used to profile the internal ethread locks in the Erlang Runtime System\&. With \fIlcnt\fR\& enabled, internal counters in the runtime system are updated each time a lock is taken\&. The counters stores information about the number of acquisition tries and the number of collisions that has occurred during the acquisition tries\&. The counters also record the waiting time a lock has caused for a blocked thread when a collision has occurred\&. .LP The data produced by the lock counters will give an estimate on how well the runtime system will behave from a parallelizable view point for the scenarios tested\&. This tool was mainly developed to help Erlang runtime developers iron out potential and generic bottlenecks\&. .LP Locks in the emulator are named after what type of resource they protect and where in the emulator they are initialized, those are lock \&'classes\&'\&. Most of those locks are also instantiated several times, and given unique identifiers, to increase locking granularity\&. Typically an instantiated lock protects a disjunct set of the resource, for example ets tables, processes or ports\&. In other cases it protects a specific range of a resource, for example \fIpix_lock\fR\& which protects index to process mappings, and is given a unique number within the class\&. A unique lock in \fIlcnt\fR\& is referenced by a name (class) and an identifier: \fI{Name, Id}\fR\&\&. .LP Some locks in the system are static and protects global resources, for example \fIbif_timers\fR\& and the \fIrun_queue\fR\& locks\&. Other locks are dynamic and not necessarily long lived, for example process locks and ets-table locks\&. The statistics data from short lived locks can be stored separately when the locks are deleted\&. This behavior is by default turned off to save memory but can be turned on via \fIlcnt:rt_opt({copy_save, true})\fR\&\&. The \fIlcnt:apply/1,2,3\fR\& functions enables this behavior during profiling\&. .SH EXPORTS .LP .nf .B start() -> {ok, Pid} | {error, {already_started, Pid}} .br .fi .br .RS .LP Types: .RS 3 Pid = pid() .br .RE .RE .RS .LP Starts the lock profiler server\&. The server only act as a medium for the user and performs filtering and printing of data collected by \fIlcnt:collect/1\fR\&\&. .RE .LP .nf .B stop() -> ok .br .fi .br .RS .LP Stops the lock profiler server\&. .RE .LP .nf .B collect() -> ok .br .fi .br .RS .LP Same as \fIcollect(node())\fR\&\&. .RE .LP .nf .B collect(Node) -> ok .br .fi .br .RS .LP Types: .RS 3 Node = node() .br .RE .RE .RS .LP Collects lock statistics from the runtime system\&. The function starts a server if it is not already started\&. It then populates the server with lock statistics\&. If the server held any lock statistics data before the collect then that data is lost\&. .RE .LP .nf .B clear() -> ok .br .fi .br .RS .LP Same as \fIclear(node())\fR\&\&. .RE .LP .nf .B clear(Node) -> ok .br .fi .br .RS .LP Types: .RS 3 Node = node() .br .RE .RE .RS .LP Clears the internal lock statistics from the runtime system\&. This does not clear the data on the server only on runtime system\&. All counters for static locks are zeroed, all dynamic locks currently alive are zeroed and all saved locks now destroyed are removed\&. It also resets the duration timer\&. .RE .LP .nf .B conflicts() -> ok .br .fi .br .RS .LP Same as \fIconflicts([])\fR\&\&. .RE .LP .nf .B conflicts(Options) -> ok .br .fi .br .RS .LP Types: .RS 3 Options = [option()] .br .nf \fBoption()\fR\& = .br {sort, Sort :: sort()} | .br {reverse, boolean()} | .br {locations, boolean()} | .br {thresholds, Thresholds :: [threshold()]} | .br {print, .br PrintOptions :: [print() | {print(), integer() >= 0}]} | .br {max_locks, MaxLocks :: integer() >= 0 | none} | .br {combine, boolean()} .fi .br .nf \fBprint()\fR\& = .br colls | duration | entry | id | name | ratio | time | tries | .br type .fi .br .nf \fBsort()\fR\& = .br colls | entry | id | name | ratio | time | tries | type .fi .br .nf \fBthreshold()\fR\& = .br {colls, integer() >= 0} | .br {time, integer() >= 0} | .br {tries, integer() >= 0} .fi .br .RE .RE .RS .LP Prints a list of internal locks and its statistics\&. .LP For option description, see \fIlcnt:inspect/2\fR\&\&. .RE .LP .nf .B locations() -> ok .br .fi .br .RS .LP Same as \fIlocations([])\fR\&\&. .RE .LP .nf .B locations(Options) -> ok .br .fi .br .RS .LP Types: .RS 3 Options = [option()] .br .nf \fBoption()\fR\& = .br {sort, Sort :: sort()} | .br {reverse, boolean()} | .br {locations, boolean()} | .br {thresholds, Thresholds :: [threshold()]} | .br {print, .br PrintOptions :: [print() | {print(), integer() >= 0}]} | .br {max_locks, MaxLocks :: integer() >= 0 | none} | .br {combine, boolean()} .fi .br .nf \fBprint()\fR\& = .br colls | duration | entry | id | name | ratio | time | tries | .br type .fi .br .nf \fBsort()\fR\& = .br colls | entry | id | name | ratio | time | tries | type .fi .br .nf \fBthreshold()\fR\& = .br {colls, integer() >= 0} | .br {time, integer() >= 0} | .br {tries, integer() >= 0} .fi .br .RE .RE .RS .LP Prints a list of internal lock counters by source code locations\&. .LP For option description, see \fIlcnt:inspect/2\fR\&\&. .RE .LP .nf .B inspect(Lock) -> ok .br .fi .br .RS .LP Types: .RS 3 Lock = Name | {Name, Id | [Id]} .br Name = atom() | pid() | port() .br Id = atom() | integer() | pid() | port() .br .RE .RE .RS .LP Same as \fIinspect(Lock, [])\fR\&\&. .RE .LP .nf .B inspect(Lock, Options) -> ok .br .fi .br .RS .LP Types: .RS 3 Lock = Name | {Name, Id | [Id]} .br Name = atom() | pid() | port() .br Id = atom() | integer() | pid() | port() .br Options = [option()] .br .nf \fBoption()\fR\& = .br {sort, Sort :: sort()} | .br {reverse, boolean()} | .br {locations, boolean()} | .br {thresholds, Thresholds :: [threshold()]} | .br {print, .br PrintOptions :: [print() | {print(), integer() >= 0}]} | .br {max_locks, MaxLocks :: integer() >= 0 | none} | .br {combine, boolean()} .fi .br .nf \fBprint()\fR\& = .br colls | duration | entry | id | name | ratio | time | tries | .br type .fi .br .nf \fBsort()\fR\& = .br colls | entry | id | name | ratio | time | tries | type .fi .br .nf \fBthreshold()\fR\& = .br {colls, integer() >= 0} | .br {time, integer() >= 0} | .br {tries, integer() >= 0} .fi .br .RE .RE .RS .LP Prints a list of internal lock counters for a specific lock\&. .LP Lock \fIName\fR\& and \fIId\fR\& for ports and processes are interchangeable with the use of \fIlcnt:swap_pid_keys/0\fR\& and is the reason why \fIpid()\fR\& and \fIport()\fR\& options can be used in both \fIName\fR\& and \fIId\fR\& space\&. Both pids and ports are special identifiers with stripped creation and can be recreated with \fIlcnt:pid/2,3\fR\& and \fIlcnt:port/1,2\fR\&\&. .LP Option description: .RS 2 .TP 2 .B \fI{combine, boolean()}\fR\&: Combine the statistics from different instances of a lock class\&. .br Default: \fItrue\fR\& .TP 2 .B \fI{locations, boolean()}\fR\&: Print the statistics by source file and line numbers\&. .br Default: \fIfalse\fR\& .TP 2 .B \fI{max_locks, MaxLocks}\fR\&: Maximum number of locks printed or no limit with \fInone\fR\&\&. .br Default: \fI20\fR\& .TP 2 .B \fI{print, PrintOptions}\fR\&: Printing options: .RS 2 .TP 2 .B \fIname\fR\&: Named lock or named set of locks (classes)\&. The same name used for initializing the lock in the VM\&. .TP 2 .B \fIid\fR\&: Internal id for set of locks, not always unique\&. This could be table name for ets tables (db_tab), port id for ports, integer identifiers for allocators, etc\&. .TP 2 .B \fItype\fR\&: Type of lock: \fIrw_mutex\fR\&, \fImutex\fR\&, \fIspinlock\fR\&, \fIrw_spinlock\fR\& or \fIproclock\fR\&\&. .TP 2 .B \fIentry\fR\&: In combination with \fI{locations, true}\fR\& this option prints the lock operations source file and line number entry-points along with statistics for each entry\&. .TP 2 .B \fItries\fR\&: Number of acquisitions of this lock\&. .TP 2 .B \fIcolls\fR\&: Number of collisions when a thread tried to acquire this lock\&. This is when a trylock is EBUSY, a write try on read held rw_lock, a try read on write held rw_lock, a thread tries to lock an already locked lock\&. (Internal states supervises this)\&. .TP 2 .B \fIratio\fR\&: The ratio between the number of collisions and the number of tries (acquisitions) in percentage\&. .TP 2 .B \fItime\fR\&: Accumulated waiting time for this lock\&. This could be greater than actual wall clock time, it is accumulated for all threads\&. Trylock conflicts does not accumulate time\&. .TP 2 .B \fIduration\fR\&: Percentage of accumulated waiting time of wall clock time\&. This percentage can be higher than 100% since accumulated time is from all threads\&. .RE .br Default: \fI[name,id,tries,colls,ratio,time,duration]\fR\& .TP 2 .B \fI{reverse, boolean()}\fR\&: Reverses the order of sorting\&. .br Default: \fIfalse\fR\& .TP 2 .B \fI{sort, Sort}\fR\&: Column sorting orders\&. .br Default: \fItime\fR\& .TP 2 .B \fI{thresholds, Thresholds}\fR\&: Filtering thresholds\&. Anything values above the threshold value are passed through\&. .br Default: \fI[{tries, 0}, {colls, 0}, {time, 0}]\fR\& .RE .RE .LP .nf .B information() -> ok .br .fi .br .RS .LP Prints lcnt server state and generic information about collected lock statistics\&. .RE .LP .nf .B swap_pid_keys() -> ok .br .fi .br .RS .LP Swaps places on \fIName\fR\& and \fIId\fR\& space for ports and processes\&. .RE .LP .nf .B load(Filename) -> ok .br .fi .br .RS .LP Types: .RS 3 Filename = file:filename() .br .RE .RE .RS .LP Restores previously saved data to the server\&. .RE .LP .nf .B save(Filename) -> ok .br .fi .br .RS .LP Types: .RS 3 Filename = file:filename() .br .RE .RE .RS .LP Saves the collected data to file\&. .RE .SH "CONVENIENCE FUNCTIONS" .LP The following functions are used for convenience\&. .SH EXPORTS .LP .nf .B apply(Fun) -> term() .br .fi .br .RS .LP Types: .RS 3 Fun = function() .br .RE .RE .RS .LP Same as \fIapply(Fun, [])\fR\&\&. .RE .LP .nf .B apply(Fun, Args) -> term() .br .fi .br .RS .LP Types: .RS 3 Fun = function() .br Args = [term()] .br .RE .RE .RS .LP Clears the lock counters and then setups the instrumentation to save all destroyed locks\&. After setup the function is called, passing the elements in \fIArgs\fR\& as arguments\&. When the function returns the statistics are immediately collected to the server\&. After the collection the instrumentation is returned to its previous behavior\&. The result of the applied function is returned\&. .LP .RS -4 .B Warning: .RE This function should only be used for micro-benchmarks; it sets \fIcopy_save\fR\& to \fItrue\fR\& for the duration of the call, which can quickly lead to running out of memory\&. .RE .LP .nf .B apply(Module, Function, Args) -> term() .br .fi .br .RS .LP Types: .RS 3 Module = module() .br Function = atom() .br Args = [term()] .br .RE .RE .RS .LP Same as \fIapply(fun() -> erlang:apply(Module, Function, Args) end)\fR\&\&. .RE .LP .nf .B pid(Id, Serial) -> pid() .br .fi .br .RS .LP Types: .RS 3 Id = Serial = integer() .br .RE .RE .RS .LP Same as \fIpid(node(), Id, Serial)\fR\&\&. .RE .LP .nf .B pid(Node, Id, Serial) -> pid() .br .fi .br .RS .LP Types: .RS 3 Node = node() .br Id = Serial = integer() .br .RE .RE .RS .LP Creates a process id with creation 0\&. .RE .LP .nf .B port(Id) -> port() .br .fi .br .RS .LP Types: .RS 3 Id = integer() .br .RE .RE .RS .LP Same as \fIport(node(), Id)\fR\&\&. .RE .LP .nf .B port(Node, Id) -> port() .br .fi .br .RS .LP Types: .RS 3 Node = node() .br Id = integer() .br .RE .RE .RS .LP Creates a port id with creation 0\&. .RE .SH "INTERNAL RUNTIME LOCK COUNTER CONTROLLERS" .LP The following functions control the behavior of the internal counters\&. .SH EXPORTS .LP .nf .B rt_collect() -> [lock_counter_data()] .br .fi .br .RS .LP Types: .RS 3 .nf \fBlock_counter_data()\fR\& = term() .fi .br .RE .RE .RS .LP Same as \fIrt_collect(node())\fR\&\&. .RE .LP .nf .B rt_collect(Node) -> [lock_counter_data()] .br .fi .br .RS .LP Types: .RS 3 Node = node() .br .nf \fBlock_counter_data()\fR\& = term() .fi .br .RE .RE .RS .LP Returns a list of raw lock counter data\&. .RE .LP .nf .B rt_clear() -> ok .br .fi .br .RS .LP Same as \fIrt_clear(node())\fR\&\&. .RE .LP .nf .B rt_clear(Node) -> ok .br .fi .br .RS .LP Types: .RS 3 Node = node() .br .RE .RE .RS .LP Clear the internal counters\&. Same as \fIlcnt:clear(Node)\fR\&\&. .RE .LP .nf .B rt_mask() -> [category_atom()] .br .fi .br .RS .LP Types: .RS 3 .nf \fBcategory_atom()\fR\& = atom() .fi .br .RE .RE .RS .LP Same as \fIrt_mask(node())\fR\&\&. .RE .LP .nf .B rt_mask(Node) -> [category_atom()] .br .fi .br .RS .LP Types: .RS 3 Node = node() .br .nf \fBcategory_atom()\fR\& = atom() .fi .br .RE .RE .RS .LP Refer to \fIrt_mask/2\fR\&\&. for a list of valid categories\&. All categories are enabled by default\&. .RE .LP .nf .B rt_mask(Categories) -> ok | {error, copy_save_enabled} .br .fi .br .RS .LP Types: .RS 3 Categories = [category_atom()] .br .nf \fBcategory_atom()\fR\& = atom() .fi .br .RE .RE .RS .LP Same as \fIrt_mask(node(), Categories)\fR\&\&. .RE .LP .nf .B rt_mask(Node, Categories) -> ok | {error, copy_save_enabled} .br .fi .br .RS .LP Types: .RS 3 Node = node() .br Categories = [category_atom()] .br .nf \fBcategory_atom()\fR\& = atom() .fi .br .RE .RE .RS .LP Sets the lock category mask to the given categories\&. .LP This will fail if the \fIcopy_save\fR\& option is enabled; see \fIlcnt:rt_opt/2\fR\&\&. .LP Valid categories are: .RS 2 .TP 2 * \fIallocator\fR\& .LP .TP 2 * \fIdb\fR\& (ETS tables) .LP .TP 2 * \fIdebug\fR\& .LP .TP 2 * \fIdistribution\fR\& .LP .TP 2 * \fIgeneric\fR\& .LP .TP 2 * \fIio\fR\& .LP .TP 2 * \fIprocess\fR\& .LP .TP 2 * \fIscheduler\fR\& .LP .RE .LP This list is subject to change at any time, as is the category any given lock may belong to\&. .RE .LP .nf .B rt_opt(Option) -> boolean() .br .fi .br .RS .LP Types: .RS 3 Option = {Type, Value :: boolean()} .br Type = copy_save | process_locks .br .RE .RE .RS .LP Same as \fIrt_opt(node(), {Type, Value})\fR\&\&. .RE .LP .nf .B rt_opt(Node, Option) -> boolean() .br .fi .br .RS .LP Types: .RS 3 Node = node() .br Option = {Type, Value :: boolean()} .br Type = copy_save | process_locks .br .RE .RE .RS .LP Option description: .RS 2 .TP 2 .B \fI{copy_save, boolean()}\fR\&: Retains the statistics of destroyed locks\&. .br Default: \fIfalse\fR\& .LP .RS -4 .B Warning: .RE This option will use a lot of memory when enabled, which must be reclaimed with \fIlcnt:rt_clear\fR\&\&. Note that it makes no distinction between locks that were destroyed and locks for which counting was disabled, so enabling this option will disable changes to the lock category mask\&. .TP 2 .B \fI{process_locks, boolean()}\fR\&: Profile process locks, equal to adding \fIprocess\fR\& to the lock category mask; see \fIlcnt:rt_mask/2\fR\& .br Default: \fItrue\fR\& .RE .RE .SH "SEE ALSO" .LP LCNT User\&'s Guide