Erlang:我可以获得所有当前注册的原子列表吗?

Sni*_*gus 12 erlang

我的项目已经超过了最大的1M原子,我们已经达到极限,但我需要对人们提交的关于list_to_atom及其朋友的代码应用一些理智.我想从获得所有已注册原子的列表开始,这样我就可以看到最大的罪犯在哪里.有没有办法做到这一点.我必须对我如何做到这一点有创意,所以我最终不会尝试在实时控制台中转储1-2M行.

谢谢.

leg*_*cia 36

您可以使用外部术语格式的未记录功能来获取所有原子.

TL; DR:将以下行粘贴到正在运行的节点的Erlang shell中.继续阅读以获得解释和非简洁版本的代码.

(fun F(N)->try binary_to_term(<<131,75,N:24>>) of A->[A]++F(N+1) catch error:badarg->[]end end)(0).
Run Code Online (Sandbox Code Playgroud)

Ivar Vong的 Elixir版本:

for i <- 0..:erlang.system_info(:atom_count)-1, do: :erlang.binary_to_term(<<131,75,i::24>>)
Run Code Online (Sandbox Code Playgroud)

以外部术语格式编码的Erlang术语以字节131开始,然后是标识类型的字节,然后是实际数据.我发现EEP-43提到了所有可能的类型,包括ATOM_INTERNAL_REF3类型字节75,这在外部术语格式官方文档中没有提到.

因为ATOM_INTERNAL_REF3,数据是原子表的索引,编码为24位整数.我们可以轻松创建这样的二进制文件<<131,75,N:24>>

例如,在我的Erlang VM中,false似乎是原子表中的第0个原子:

> binary_to_term(<<131,75,0:24>>).
false
Run Code Online (Sandbox Code Playgroud)

找到原子表*中当前原子的数量没有简单的方法,但我们可以继续增加数字,直到我们得到badarg错误.

所以这个小模块给你一个所有原子的列表:

-module(all_atoms).

-export([all_atoms/0]).

atom_by_number(N) ->
    binary_to_term(<<131,75,N:24>>).

all_atoms() ->
    atoms_starting_at(0).

atoms_starting_at(N) ->
    try atom_by_number(N) of
        Atom ->
            [Atom] ++ atoms_starting_at(N + 1)
    catch
        error:badarg ->
            []
    end.
Run Code Online (Sandbox Code Playgroud)

输出如下:

> all_atoms:all_atoms().
[false,true,'_',nonode@nohost,'$end_of_table','','fun',
 infinity,timeout,normal,call,return,throw,error,exit,
 undefined,nocatch,undefined_function,undefined_lambda,
 'DOWN','UP','EXIT',aborted,abs_path,absoluteURI,ac,accessor,
 active,all|...]
> length(v(-1)).
9821
Run Code Online (Sandbox Code Playgroud)

*在Erlang/OTP 20.0中,您可以致电erlang:system_info(atom_count):

> length(all_atoms:all_atoms()) == erlang:system_info(atom_count).
true
Run Code Online (Sandbox Code Playgroud)


Tad*_*mas 6

我不确定是否有办法在实时系统上执行此操作,但如果您可以在测试环境中运行它,则应该能够通过崩溃转储获取列表.原子表接近崩溃转储格式的末尾.您可以通过erlang:halt/1创建故障转储,但这会导致整个运行时系统崩溃.