Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I get a list of all currently-registered atoms?

Tags:

erlang

My project has blown through the max 1M atoms, we've cranked up the limit, but I need to apply some sanity to the code that people are submitting with regard to list_to_atom and its friends. I'd like to start by getting a list of all the registered atoms so I can see where the largest offenders are. Is there any way to do this. I'll have to be creative about how I do it so I don't end up trying to dump 1-2M lines in a live console.

like image 273
Sniggerfardimungus Avatar asked Nov 20 '12 19:11

Sniggerfardimungus


1 Answers

You can get hold of all atoms by using an undocumented feature of the external term format.

TL;DR: Paste the following line into the Erlang shell of your running node. Read on for explanation and a non-terse version of the code.

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

Elixir version by Ivar Vong:

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

An Erlang term encoded in the external term format starts with the byte 131, then a byte identifying the type, and then the actual data. I found that EEP-43 mentions all the possible types, including ATOM_INTERNAL_REF3 with type byte 75, which isn't mentioned in the official documentation of the external term format.

For ATOM_INTERNAL_REF3, the data is an index into the atom table, encoded as a 24-bit integer. We can easily create such a binary: <<131,75,N:24>>

For example, in my Erlang VM, false seems to be the zeroth atom in the atom table:

> binary_to_term(<<131,75,0:24>>).
false

There's no simple way to find the number of atoms currently in the atom table*, but we can keep increasing the number until we get a badarg error.

So this little module gives you a list of all atoms:

-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.

The output looks like:

> 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

* In Erlang/OTP 20.0, you can call erlang:system_info(atom_count):

> length(all_atoms:all_atoms()) == erlang:system_info(atom_count).
true
like image 196
legoscia Avatar answered Sep 28 '22 03:09

legoscia