查看源代码 lcnt (工具 v4.1.1)
一个运行时系统锁分析工具。
lcnt
模块用于分析 Erlang 运行时系统中的内部线程锁。启用 lcnt
后,每次获取锁时都会更新运行时系统中的内部计数器。计数器存储有关获取尝试次数以及获取尝试期间发生的冲突次数的信息。计数器还会记录当发生冲突时,锁导致阻塞线程的等待时间。
锁计数器产生的数据将估计在测试的场景中,运行时系统从并行化的角度来看表现如何。此工具主要用于帮助 Erlang 运行时开发人员解决潜在的通用瓶颈。
仿真器中的锁根据它们保护的资源类型以及它们在仿真器中的初始化位置命名,这些是锁“类”。大多数这些锁也会被多次实例化,并被赋予唯一的标识符,以增加锁的粒度。通常,实例化的锁保护资源的不同的集合,例如 ets 表、进程或端口。在其他情况下,它保护特定范围的资源,例如 pix_lock
,它保护进程映射的索引,并在类中被赋予唯一的编号。lcnt
中的唯一锁由名称(类)和标识符引用:{Name, Id}
。
系统中的一些锁是静态的,并保护全局资源,例如 bif_timers
和 run_queue
锁。其他锁是动态的,不一定长期存在,例如进程锁和 ets 表锁。当删除锁时,可以单独存储来自短期锁的统计数据。默认情况下,此行为被禁用以节省内存,但可以通过 lcnt:rt_opt({copy_save, true})
启用。 lcnt:apply/1,2,3
函数在分析期间启用此行为。
另请参阅
概要
便捷函数
等效于 apply(Fun, [])
。
设置锁计数器,使用 Args
应用 Fun
,然后进行清理。
创建一个创建号为 0 的进程 ID。
等效于 port(node(), Id)
。
创建一个创建号为 0 的端口 ID。
内部运行时锁计数器控制器
清除内部计数器。
返回原始锁计数器数据的列表。
返回当前节点的当前类别掩码。
为当前节点设置当前锁类别掩码,或检索远程节点的当前掩码。
根据节点 Node
上的 Categories
设置锁类别掩码。
在节点 Node
上设置单个选项。
函数
等效于 clear(node())
。
从运行时系统中清除内部锁统计信息。
等效于 collect(node())
。
从运行时系统收集锁统计信息。
等效于 conflicts([])
。
打印内部锁及其统计信息的列表。
打印 lcnt
服务器状态以及有关收集的锁统计信息的通用信息。
打印特定锁的内部锁计数器列表。
将先前保存的数据还原到服务器。
等效于 locations([])
。
按源代码位置打印内部锁计数器列表。
将收集的数据保存到文件。
启动锁分析器服务器。
停止锁分析器服务器。
交换端口和进程的 Name
和 Id
空间的位置。
类型
-type category_atom() :: atom().
-type lock_counter_data() :: term().
-type option() :: {sort, Sort :: sort()} | {reverse, boolean()} | {locations, boolean()} | {thresholds, Thresholds :: [threshold()]} | {print, PrintOptions :: [print() | {print(), non_neg_integer()}]} | {max_locks, MaxLocks :: non_neg_integer() | none} | {combine, boolean()}.
-type print() :: colls | duration | entry | id | name | ratio | time | tries | type.
-type sort() :: colls | entry | id | name | ratio | time | tries | type.
-type threshold() :: {colls, non_neg_integer()} | {time, non_neg_integer()} | {tries, non_neg_integer()}.
便捷函数
-spec apply(Fun) -> term() when Fun :: fun().
等效于 apply(Fun, [])
。
设置锁计数器,使用 Args
应用 Fun
,然后进行清理。
清除锁计数器,然后设置检测以保存所有销毁的锁。设置完成后,将调用该函数,并将 Args
中的元素作为参数传递。当函数返回时,统计信息会立即收集到服务器。收集后,检测将恢复到之前的行为。返回应用函数的结果。
警告
此函数仅应用于微基准测试;它在调用期间将
copy_save
设置为true
,这可能会很快导致内存耗尽。
创建一个创建号为 0 的进程 ID。
等效于 port(node(), Id)
。
创建一个创建号为 0 的端口 ID。
内部运行时锁计数器控制器
-spec rt_clear() -> ok.
等效于 rt_clear(node())
。
-spec rt_clear(Node) -> ok when Node :: node().
清除内部计数器。
等效于 lcnt:clear(Node)
。
-spec rt_collect() -> [lock_counter_data()].
等效于 rt_collect(node())
。
-spec rt_collect(Node) -> [lock_counter_data()] when Node :: node().
返回原始锁计数器数据的列表。
-spec rt_mask() -> [category_atom()].
返回当前节点的当前类别掩码。
-spec rt_mask(Node) -> [category_atom()] when Node :: node(); (Categories) -> ok | {error, copy_save_enabled} when Categories :: [category_atom()].
为当前节点设置当前锁类别掩码,或检索远程节点的当前掩码。
如果 Arg
是一个原子,则假定它是一个节点,此调用返回节点 Arg
的当前锁类别掩码。
如果 Arg
是一个列表,则此调用等效于 rt_mask(node(), Arg)
。
-spec rt_mask(Node, Categories) -> ok | {error, copy_save_enabled} when Node :: node(), Categories :: [category_atom()].
根据节点 Node
上的 Categories
设置锁类别掩码。
如果启用了 copy_save
选项,则此调用将失败;请参阅 lcnt:rt_opt/2
。
有效的类别有
allocator
db
(ETS 表)debug
distribution
generic
io
process
scheduler
此列表可能会随时更改,任何给定锁所属的类别也可能会更改。
-spec rt_opt(Node, Option) -> boolean() when Node :: node(), Option :: {Type, Value :: boolean()}, Type :: copy_save | process_locks.
在节点 Node
上设置单个选项。
选项说明
{copy_save, boolean()}
- 保留已销毁锁的统计信息。
默认值:false
警告
启用此选项会使用大量内存,必须使用
lcnt:rt_clear/0,1
回收。请注意,它不区分已销毁的锁和已禁用计数的锁,因此启用此选项将禁用对锁类别掩码的更改。{process_locks, boolean()}
- 分析进程锁,等同于将process
添加到锁类别掩码;请参阅lcnt:rt_mask/2
。
默认值:true
函数
-spec clear() -> ok.
等效于 clear(node())
。
-spec clear(Node) -> ok when Node :: node().
从运行时系统中清除内部锁统计信息。
这将清除运行时系统中的数据,但不清除服务器中的数据。所有静态锁的计数器都将归零,所有当前活动的动态锁都将归零,并且所有保存的现在已销毁的锁都将被删除。它还会重置持续时间计时器。
-spec collect() -> ok.
等效于 collect(node())
。
-spec collect(Node) -> ok when Node :: node().
从运行时系统收集锁统计信息。
如果服务器尚未启动,则此函数会启动服务器。然后,它会用锁统计信息填充服务器。如果服务器在收集之前保存了任何锁统计数据,则该数据将丢失。
-spec conflicts() -> ok.
等效于 conflicts([])
。
-spec conflicts(Options) -> ok when Options :: [option()].
打印内部锁及其统计信息的列表。
有关选项说明,请参阅 lcnt:inspect/2
。
-spec information() -> ok.
打印 lcnt
服务器状态以及有关收集的锁统计信息的通用信息。
-spec inspect(Lock) -> ok when Lock :: Name | {Name, Id | [Id]}, Name :: atom() | pid() | port(), Id :: atom() | integer() | pid() | port().
等效于 inspect(Lock, [])
。
-spec inspect(Lock, Options) -> ok when Lock :: Name | {Name, Id | [Id]}, Name :: atom() | pid() | port(), Id :: atom() | integer() | pid() | port(), Options :: [option()].
打印特定锁的内部锁计数器列表。
端口和进程的锁 Name
和 Id
可以通过使用 lcnt:swap_pid_keys/0
互换使用,这也是为什么 pid/0
和 port/0
选项可以在 Name
和 Id
空间中使用的原因。 pid 和端口都是具有剥离创建的特殊标识符,可以使用 lcnt:pid/2,3
和 lcnt:port/1,2
重新创建。
选项说明
{combine, boolean()}
- 合并来自锁类不同实例的统计信息。
默认值:true
{locations, boolean()}
- 按源文件和行号打印统计信息。
默认值:false
{max_locks, MaxLocks}
- 打印的最大锁数量,如果为none
则无限制。
默认值:20
{print, PrintOptions}
- 打印选项name
- 命名的锁或命名的锁集合(类)。与在 VM 中初始化锁时使用的名称相同。id
- 锁集合的内部 ID,并非总是唯一。这可能是 ets 表的表名 (db_tab)、端口的端口 ID、分配器的整数标识符等。type
- 锁的类型:rw_mutex
、mutex
、spinlock
、rw_spinlock
或proclock
。entry
- 与{locations, true}
结合使用时,此选项会打印锁操作的源文件和行号入口点,以及每个入口点的统计信息。tries
- 获取此锁的次数。colls
- 当线程尝试获取此锁时发生的冲突次数。这指的是 trylock 返回 EBUSY、在持有读锁的 rw_lock 上进行写尝试、在持有写锁的 rw_lock 上进行读尝试,以及线程尝试锁定已锁定的锁。(内部状态会监视这种情况。)ratio
- 冲突次数与尝试(获取)次数之比,以百分比表示。time
- 此锁的累积等待时间。这可能大于实际的挂钟时间,它是为所有线程累积的。Trylock 冲突不会累积时间。duration
- 累积等待时间占挂钟时间的百分比。此百分比可能高于 100%,因为累积时间来自所有线程。
默认值:
[name,id,tries,colls,ratio,time,duration]
{reverse, boolean()}
- 反转排序顺序。
默认值:false
{sort, Sort}
- 列排序顺序。
默认值:time
{thresholds, Thresholds}
- 过滤阈值。任何高于阈值的值都会通过。
默认值:[{tries, 0}, {colls, 0}, {time, 0}]
-spec load(Filename) -> ok when Filename :: file:filename().
将先前保存的数据还原到服务器。
-spec locations() -> ok.
等效于 locations([])
。
-spec locations(Options) -> ok when Options :: [option()].
按源代码位置打印内部锁计数器列表。
有关选项说明,请参阅 lcnt:inspect/2
。
-spec save(Filename) -> ok when Filename :: file:filename().
将收集的数据保存到文件。
-spec start() -> {ok, Pid} | {error, {already_started, Pid}} when Pid :: pid().
启动锁分析器服务器。
服务器仅充当用户的媒介,并执行由 lcnt:collect/1
收集的数据的过滤和打印。
-spec stop() -> ok.
停止锁分析器服务器。
-spec swap_pid_keys() -> ok.
交换端口和进程的 Name
和 Id
空间的位置。