Implementation of the Erlang Timer Module

The timer module provides useful functions related to time. For example:

send_after: send a message to a process after some time
send_interval: send a message to a process repeatedly after some time
cancel: cancels a previously requested timeout
sleep: suspend the current process for some time
tc: measure the execution time for an MFA
now_diff: calculate the difference of two erlang:now() timestamps

The timer module is a gen_server callback module. timer:start_link/0
spawns a gen_server process and registers it as ‘timer_server’.

The timer_server process maintains 2 ETS tables:
– timer_tab
An ordered set table holding timing requests and timer objects.
– timer_interval_tab:
A set table holding the timer objects.

When timer_server is started, it creates the 2 ETS tables and then waits
for requests infinitely.

We only concerns about 2 timing request: apply_after and apply_interval.
send_after, exit_after, and kill_after are implemented over apply_after.

send_after(T, Pid, Msg) => apply_after(T, timer, send, [Pid, Msg])
exit_after(T, Pid, Reason) => apply_after(T, erlang, exit, [Pid, Reason])
kill_after(T, Pid) => exit_after(T, Pid, kill)

Similarly, send_interval is implemented using apply_interval.

When handle_call receives an apply_after request, it stores the request in
timer_tab. When it receives an apply_interval request, it stores the request
in timer_tab and timer_interval_tab. Then handle_call looks up timer_tab for
a nearest timeout value using private function timer_timeout, and returns
that value as Timeout to gen_server module. After Timeout amount of time has
elapsed, handle_info(timeout,_) will be called.

1> timer:send_interval(10000, self(), foo).
2> timer:send_interval(15000, self(), bar).
3> ets:i().
 id              name                  type  size   mem      owner
 timer_interval_tab timer_interval_tab set   2      358      timer_server
 timer_tab       timer_tab          ordered_set 2      160      timer_server
4> ets:i(timer_interval_tab).               
EOT  (q)uit (p)Digits (k)ill /Regexp -->q
5> ets:i(timer_tab).                        
 {  ...
 {  ...
EOT  (q)uit (p)Digits (k)ill /Regexp -->q
%% timer_timeout(SysTime)
%% Apply and remove already timed-out timers. A timer is a tuple
%% {Time, BRef, Op, MFA}, where Time is in microseconds.
%% Returns {Timeout, Timers}, where Timeout is in milliseconds.
timer_timeout(SysTime) ->
    case ets:first(?TIMER_TAB) of
    '$end_of_table' -> 
    {Time, _Ref} when Time > SysTime ->
        Timeout = (Time - SysTime) div 1000,
        %% Returned timeout must fit in a small int
        erlang:min(Timeout, ?MAX_TIMEOUT);
    Key ->
        case ets:lookup(?TIMER_TAB, Key) of
        [{Key, timeout, MFA}] ->
        [{{Time, Ref}, Repeat = {repeat, Interv, To}, MFA}] ->
            NewTime = Time + Interv,
            %% Update the interval entry (last in table)
            ets:insert(?TIMER_TAB, {{NewTime, Ref}, Repeat, MFA}),

In fact, timer_timeout/1 is called in 3 situations:
1) handle_call(apply_after, …)
1) handle_call(apply_interval, …)
1) handle_info(timeout, …)

timer_timeout looks up timer_tab for a timeouted request, do what the
request wanted, delete the request if it’s apply_after or update it if
it’s apply_interval, and returns the next timeout point.

The function cancel just deletes a timing request from timer_tab (and
timer_interval_tab) using delete_ref.

delete_ref(BRef = {interval, _}) ->
    case ets:lookup(?INTERVAL_TAB, BRef) of
    [{_, BRef2, _Pid}] ->
        ets:delete(?INTERVAL_TAB, BRef),
        ets:delete(?TIMER_TAB, BRef2);
    _ -> % TimerReference does not exist, do nothing
delete_ref(BRef) ->
    ets:delete(?TIMER_TAB, BRef).

When you create a repeatable timer for a process using apply_interval,
timer_server will make a link to the target process. If the process
dies, timer_server will receive an ‘EXIT’ message, and then it calls
private function pid_delete/1 to delete all timer objects related with
that process from timer_tab and timer_interval_tab.

pid_delete(Pid) ->
    IntervalTimerList = ets:select(?INTERVAL_TAB,
                                   [{{'_', '_','$1'},
    lists:foreach(fun({IntKey, TimerKey, _ }) ->
              ets:delete(?INTERVAL_TAB, IntKey),
              ets:delete(?TIMER_TAB, TimerKey)
          end, IntervalTimerList).

The implementation of sleep/1 is very simple. It’s exactly the same as
that in Joe Armstrong’s book Programming Erlang.

sleep(T) ->
    after T -> ok

timer_server is a child of supervisor kernel_safe_sup. When timer_server
exits abnormaly it will be restarted. timer_server doesn’t exit even if
there exists no timing requests any more.

For more details about timer_server, please refer to the source code.

We can see that timer_server doesn’t use erlang:start_timer to create
a timer object. Instead, it uses gen_server’s timing ability to achieve
its goals.

How does gen_server generate timeout messages? The answer is after a
message is handled and the handlers (handle_call, handle_cast, etc.)
returns a Timeout value (the last element of the tuple returned from
handle_* in normal case), gen_server uses “receive … after …” clause
to wait for the next message. If the next message doesn’t come within
Timeout milliseconds, a timeout event is generated internally. It’s not
real message but it goes through the same routine as a real message does,
finally GenMod:handle_info/2 is called with the argument of ‘timeout’.

The manual of gen_server says:

Module:init(Args) -> Result
Module:handle_call(Request, From, State) -> Result
Module:handle_cast(Request, State) -> Result
Module:handle_info(Info, State) -> Result

Types Result = {ok,State} | {ok,State,Timeout} | {ok,State,hibernate}
| {stop,Reason} | ignore
Timeout = int()>=0 | infinity

If an integer timeout value is provided, a timeout will occur unless
a request or a message is received within Timeout milliseconds. A timeout
is represented by the atom timeout which should be handled by the
handle_info/2 callback function. The atom infinity can be used to wait
indefinitely, this is the default value.

And the source of gen_server:

%%% ---------------------------------------------------
%%% The MAIN loop of gen_server
%%% ---------------------------------------------------
loop(Parent, Name, State, Mod, hibernate, Debug) ->
    proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, Debug]);
loop(Parent, Name, State, Mod, Time, Debug) ->
    Msg = receive
              Input ->
          after Time ->
    decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, false).

The gen_server module will be studied in another article.

Posted in erlang | Tagged | Leave a comment

The erlangrc file

There could be an erlang resource file named as “.erlang” in your working
directory or home directory. This file is loaded and evaled when erlang is
started. So you can do some initialization in this file.

Let’s look into start_clean.script and start_sasl.script.

%% the next line is only present in start_sasl.script

After kernel, stdlib and sasl is started, c:erlangrc/0 will be called.

%% erlangrc(Home)
%%  Try to run a ".erlang" file, first in the current directory
%%  else in home directory.

erlangrc() ->
    case init:get_argument(home) of
    {ok,[[Home]]} ->
    _ ->
        f_p_e(["."], ".erlang")

erlangrc([Home]) ->
    f_p_e([".",Home], ".erlang").

f_p_e(P, F) ->
    case file:path_eval(P, F) of
    {error, enoent} = Enoent ->
    {error, E={Line, _Mod, _Term}} ->
        error("file:path_eval(~p,~p): error on line ~p: ~s~n",
          [P, F, Line, file:format_error(E)]),
    {error, E} ->
        error("file:path_eval(~p,~p): ~s~n",
          [P, F, file:format_error(E)]),
    Other ->

Something we should know about file:path_eval:

path_eval(Path, Filename) -> {ok, FullName} | {error, Reason}

Searches the path Path (a list of directory names) until the file
Filename is found. If Filename is an absolute file name, Path is
ignored. Then reads and evaluates Erlang expressions, separated
by ’.’ (or ’,’, a sequence of expressions is also an expression),
from the file. The actual result of evaluation is not returned; any
expression sequence in the file must be there for its side effect.
Returns one of the following:

c:erlangrc/0 is wrapped in shell_default, so you can type erlangrc([“.”]).
in the erlang shell to reload your .erlang file at any time.

Posted in erlang | Tagged | Leave a comment

How to get parent of an erlang process

The erlang application monitor, i.e. appmon, could build a process tree for an erlang application. To build such a tree, we need to find out all processes that belong to the same application and the hierarchy relations between those processes. We enumerate all processes and call application:application(Pid) on each pid to construct some disjoint sets for applications. And we call erlang:process_info(Pid, dictionary) on each pid to get its ancestor processes. Processes created by proc_lib:spawn* store ancestor information in their process dictionaries.

11> process_info(whereis(timer_server),dictionary).

The following code fragment is quoted from otp_src_R14B01/lib/stdlib/src/proc_lib.erl:

spawn(M,F,A) when is_atom(M), is_atom(F), is_list(A) ->
    Parent = get_my_name(),
    Ancestors = get_ancestors(),
    erlang:spawn(?MODULE, init_p, [Parent,Ancestors,M,F,A]).

init_p(Parent, Ancestors, M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
    put('$ancestors', [Parent|Ancestors]),
    put('$initial_call', trans_init(M, F, A)),
    init_p_do_apply(M, F, A).

get_ancestors() ->
    case get('$ancestors') of
    A when is_list(A) -> A;
    _                 -> []

The following code fragment is quoted from otp_src_R14B01/lib/stdlib/src/gen_server.erl:

get_parent() ->
    case get('$ancestors') of
    [Parent | _] when is_pid(Parent)->
        [Parent | _] when is_atom(Parent)->
    _ ->

Generally there is no standard way to find the parent of a certain process. If you want a process to send message to its parent, you’d better pass the parent pid as an argument to spawn*(), and the child process should memorize this pid for later use.

Posted in erlang | Tagged | Leave a comment

Getting Process Information of Erlang Runtime System

In an erlang shell we can get process information by calling i().

1> i().
Pid       Initial Call                        Heap    Reds  Msgs  Registered             Current Function                Stack
   otp_ring0:start/2                    610    2419     0  init                   init:loop/1                         2
   erlang:apply/2                      1597  169165     0  erl_prim_loader        erl_prim_loader:loop/3              6
   gen_event:init_it/6                  377     226     0  error_logger           gen_event:fetch_msg/5               8
   erlang:apply/2                      1597     444     0  application_controlle  gen_server:loop/6                   7
   application_master:init/4            377      44     0                         application_master:main_loop/2      6
   application_master:start_it/4        233      72     0                         application_master:loop_it/4        5
  supervisor:kernel/1                  987    1413     0  kernel_sup             gen_server:loop/6                   9
  rpc:init/1                           233      36     0  rex                    gen_server:loop/6                   9
  global:init/1                        233      53     0  global_name_server     gen_server:loop/6                   9
  erlang:apply/2                       233      21     0                         global:loop_the_locker/1            4
  erlang:apply/2                       233       4     0                         global:collect_deletions/2          6
  erlang:apply/2                       233       3     0                         global:loop_the_registrar/0         2
  inet_db:init/1                       233     255     0  inet_db                gen_server:loop/6                   9
  global_group:init/1                  233      60     0  global_group           gen_server:loop/6                   9
  file_server:init/1                   233      86     0  file_server_2          gen_server:loop/6                   9
  erlang:apply/2                      6765   99301     0  code_server            code_server:loop/1                  3
  supervisor_bridge:standard_error/    233      40     0  standard_error_sup     gen_server:loop/6                   9
  standard_error:server/2              233       7     0  standard_error         standard_error:server_loop/1        2
  supervisor_bridge:user_sup/1         233      59     0                         gen_server:loop/6                   9
  user_drv:server/2                    987     596     1  user_drv               user_drv:server_loop/5              8
  group:server/3                       233      38     0  user                   group:server_loop/3                 4
  group:server/3                      2584   11212     0                         group:server_loop/3                 4
  erlang:apply/2                     17711    4410     0                         shell:shell_rep/4                  17
  kernel_config:init/1                 233     268     0                         gen_server:loop/6                   9
  supervisor:kernel/1                  233      57     0  kernel_safe_sup        gen_server:loop/6                   9
  erlang:apply/2                      1597   10157     0                         c:pinfo/1                          50
Total                                        38684  300446     1                                                           224

The function i() obtains a pid list by calling erlang:processes/0. Then it calls erlang:process_info/1 on each pid.
[erlang:process_info(Pid)|| Pid <- erlang:processes()].

The function erlang:process_info/1 provides some information about a process.

2> erlang:process_info(pid(0,0,0)).

The following fields is extraced from the tuple list returned from erlang:process_info/1:
After that these fields along with the process id are formated into a table and then written to stdout.

Another function ni() does similar work, however, it shows processes running on all nodes (including local node). Processes running on a remote node can be easily distinguished from their pids. Let

  A = pid_to_list(Pid).
  B = lists:sublist(A,2,length(A)-2).
  [C,_,_] = [list_to_integer(S) || S <- string:tokens(B,[$.])].

Then C is always 0 for a local process and greater than 0 for a remote pid.

To show all registered processes, use the function regs(). This function prints registered ports too.

3> regs().

** Registered procs on node nonode@nohost **
Name                  Pid          Initial Call                      Reds Msgs
application_controlle       erlang:apply/2                     444    0
code_server                erlang:apply/2                  102634    0
erl_prim_loader             erlang:apply/2                  174063    0
error_logger                gen_event:init_it/6                226    0
file_server_2              file_server:init/1                  86    0
global_group               global_group:init/1                 60    0
global_name_server         global:init/1                       53    0
inet_db                    inet_db:init/1                     255    0
init                        otp_ring0:start/2                 2419    0
kernel_safe_sup            supervisor:kernel/1                 57    0
kernel_sup                 supervisor:kernel/1               1413    0
rex                        rpc:init/1                          36    0
standard_error             standard_error:server/2              7    0
standard_error_sup         supervisor_bridge:standar           40    0
user                       group:server/3                      38    0
user_drv                   user_drv:server/2                10988    0

** Registered ports on node nonode@nohost **
Name                  Id              Command

The function regs() gets a list of registered process names by calling erlang:registered(). Then it calls erlang:whereis/1 to translate each name into a pid. The remaining job is almost the same as i(), but regs() also collects information of ports by using erlang:port_info/1. There is a distributed version of regs, say nregs(), just as ni() to i().

Refer to OTP source code otp_src_R14B01/lib/stdlib/src/c.erl

Posted in erlang | Tagged | Leave a comment

erl_ddll and dlopen

In Erlang we can use module erl_ddll to dynamically load a native driver
library. The process is very complicated. It is implemented in these files:


Both erl_unix_sys_dll.c and erl_win32_sys_ddll.c have implemented a common
interface which is used in erl_bif_ddll.c. The interface consists of the
following functions:

// Invoke this function before we can use other functions
void erl_sys_ddll_init(void);
// Open a shared object
int erts_sys_ddll_open2(char *full_name, void **handle, ErtsSysDdllError* err);
int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err);
// Find a symbol in the shared object
int erts_sys_ddll_sym2(void *handle, char *func_name, void **function, ErtsSysDdllError* err);
// Load the driver init function, might appear under different names
// depending on object arch...
int erts_sys_ddll_load_driver_init(void *handle, void **function);
int erts_sys_ddll_load_nif_init(void *handle, void **function, ErtsSysDdllError* err);
// Call the driver_init function, whatever it's really called, simple
// on unix...
void *erts_sys_ddll_call_init(void *function);
void *erts_sys_ddll_call_nif_init(void *function);
// Close a shared object
int erts_sys_ddll_close2(void *handle, ErtsSysDdllError* err);
// Return string that describes the (current) error
char *erts_sys_ddll_error(int code);
void erts_sys_ddll_free_error(ErtsSysDdllError* err);

These functions are implemented over the standard dynamic library loading
interface of unix. It defines a small set of API calls including dlopen,
dlclose, dlsym, dlerror, dlinfo, etc. Below is a hello-world example.

// foo.c: the souce of
int num = 10000;

int add(int a, int b)
    return a + b;

char *str = "hello, world!";
// end of foo.c
// test.c: a program that uses
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

typedef int(*IntBinFuncPtr)(int,int);

int main()
    void *handle;
    IntBinFuncPtr add_p;
    int aa = 123, *bb_p, cc;
    char **str_p, *str;

    handle = dlopen("/home/zq2010/", RTLD_LAZY);
    if(handle == NULL) {
        fprintf(stderr, "cannot load\n");

    add_p = (IntBinFuncPtr) dlsym(handle, "add");
    if(add_p == NULL) {
        fprintf(stderr, "cannot find symbol 'add'\n");

    bb_p = (int*) dlsym(handle, "num");
    if(bb_p == NULL) {
        fprintf(stderr, "cannot find symbol 'num'\n");

    cc = (*add_p)(aa, *bb_p);
    printf("cc = %d\n", cc);

    str_p = (char**) dlsym(handle, "str");
    if(str_p == NULL) {
        fprintf(stderr, "cannot find symbol 'str'\n");

    str = *str_p;
    printf("str = %s\n", str);

    if(dlclose(handle) > 0) {
        fprintf(stderr, "cannot close\n");

    return 0;
// end of test.c

## Makefile
all: test foo.o
    gcc -shared -fpic foo.o -o
foo.o: foo.c
    gcc -c foo.c -o foo.o
test.o: test.c
    gcc -c test.c -o test.o
test: test.o
    gcc test.o -ldl -o test
## end of Makefile

And from erl_unix_sys_dll.c and erl_win32_sys_ddll.c We can see that the
interface of dynamic object file loading in unix is similar to that in win32
environment. There is a bijection between them:

    dlopen     <—>    LoadLibrary
    dlsym      <—>    GetProcAddress
    dlclose    <—>    FreeLibrary
    dlerror    <—>    GetLastError*


1.Dynamic Object File Loading

2.Windows vs. Unix: Linking dynamic load modules

3.dlopen(3) – Linux man page

4.Port drivers

5.erl_ddll man page

Posted in erlang | Tagged , | Leave a comment

Another ehttpd Performance Test

1. The hello-world http server – ehttpd

%%% ehttpd.erl

start() ->
start(Port) ->
    N = erlang:system_info(schedulers),
    listen(Port, N),
    io:format("ehttpd ready with ~b schedulers on port ~b~n", [N, Port]),

    register(?MODULE, self()),
    receive Any -> io:format("~p~n", [Any]) end. %% to stop: ehttpd!stop.

listen(Port, N) ->
    Opts = [{active, false},
            {backlog, 256},
            {packet, http_bin},
            {raw,6,9,<<1:32/native>>}, %defer accept
            {reuseaddr, true}],

    {ok, S} = gen_tcp:listen(Port, Opts),
    Spawn = fun(I) ->
        register(list_to_atom("acceptor_" ++ integer_to_list(I)),
            spawn_opt(?MODULE, accept, [S, I], [link, {scheduler, I}]))

    lists:foreach(Spawn, lists:seq(1, N)).

accept(S, I) ->
    case gen_tcp:accept(S) of
    {ok, Socket} ->
        spawn_opt(?MODULE, loop, [Socket], [{scheduler, I}]);
    Error ->
    accept(S, I).

loop(S) ->
    case gen_tcp:recv(S, 0) of
    {ok, http_eoh} ->
        Response = <<"HTTP/1.1 200 OK\r\nContent-Length: 14\r\n\r\n"
                     "hello, world!\n">>,
        gen_tcp:send(S, Response),
    {ok, _Data} ->
    Error ->
%%% end of ehttpd.erl

2. Start ehttpd

[root@localhost azunyanmoe]# ulimit -n 99999
[azunyanmoe@localhost ~]$ cat /proc/cpuinfo | grep GHz
model name : Pentium(R) Dual-Core  CPU      E5500  @ 2.80GHz
model name : Pentium(R) Dual-Core  CPU      E5500  @ 2.80GHz
[azunyanmoe@localhost ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:          1980       1276        703          0         34        635
-/+ buffers/cache:        606       1373
Swap:         4998         70       4928
[azunyanmoe@localhost ~]$ erlc ehttpd.erl
[azunyanmoe@localhost ~]$ taskset -c 1 erl +K true +h 99999  +P 99999
-smp enable +S 2:1 -s ehttpd
Erlang R14B01 (erts-5.8.2) [ source] [smp:2:1] [rq:2] [async-threads:0]
[hipe] [kernel-poll:true]

ehttpd ready with 2 schedulers on port 8888
Eshell V5.8.2  (abort with ^G)

3. Run test with ab from another host

azunyanmoe@localhost:~$ ab -c 60 -n 100000
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd,
Licensed to The Apache Software Foundation,

Benchmarking (be patient)
Completed 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests

Server Software:        
Server Hostname:
Server Port:            8888

Document Path:          /
Document Length:        14 bytes

Concurrency Level:      60
Time taken for tests:   12.928 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Total transferred:      5300636 bytes
HTML transferred:       1400168 bytes
Requests per second:    7735.35 [#/sec] (mean)
Time per request:       7.757 [ms] (mean)
Time per request:       0.129 [ms] (mean, across all concurrent requests)
Transfer rate:          400.41 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3  25.1      2    3000
Processing:     1    5   4.0      4     215
Waiting:        1    4   3.9      4     214
Total:          3    7  25.4      7    3004

Percentage of the requests served within a certain time (ms)
  50%      7
  66%      7
  75%      8
  80%      8
  90%      8
  95%      9
  98%     10
  99%     10
 100%   3004 (longest request)

4. Conclusion

  1) 7,735 req/s, not too bad, but still far from C10K.
  2) I tested nginx for the same task (and in the same environment), its
     performance was very close with ehttpd (about 7,500 ~ 8,000).
  3) Compiling ehttpd.erl with hipe option on seemed to be helpless.
  4) Maybe the bottleneck was elsewhere. At client side?

5. References


Posted in erlang | Tagged , | Leave a comment

Erlang VM Opcodes

The Opcodes of Erlang’s emulator BEAM are listed as follow:

-spec opname(1..152) -> {atom(),0..8}.

opname(1) -> {label,1};
opname(2) -> {func_info,3};
opname(3) -> {int_code_end,0};
opname(4) -> {call,2};
opname(5) -> {call_last,3};
opname(6) -> {call_only,2};
opname(7) -> {call_ext,2};
opname(8) -> {call_ext_last,3};
opname(9) -> {bif0,2};
opname(10) -> {bif1,4};
opname(11) -> {bif2,5};
opname(12) -> {allocate,2};
opname(13) -> {allocate_heap,3};
opname(14) -> {allocate_zero,2};
opname(15) -> {allocate_heap_zero,3};
opname(16) -> {test_heap,2};
opname(17) -> {init,1};
opname(18) -> {deallocate,1};
opname(19) -> {return,0};
opname(20) -> {send,0};
opname(21) -> {remove_message,0};
opname(22) -> {timeout,0};
opname(23) -> {loop_rec,2};
opname(24) -> {loop_rec_end,1};
opname(25) -> {wait,1};
opname(26) -> {wait_timeout,2};
opname(27) -> {m_plus,4};
opname(28) -> {m_minus,4};
opname(29) -> {m_times,4};
opname(30) -> {m_div,4};
opname(31) -> {int_div,4};
opname(32) -> {int_rem,4};
opname(33) -> {int_band,4};
opname(34) -> {int_bor,4};
opname(35) -> {int_bxor,4};
opname(36) -> {int_bsl,4};
opname(37) -> {int_bsr,4};
opname(38) -> {int_bnot,3};
opname(39) -> {is_lt,3};
opname(40) -> {is_ge,3};
opname(41) -> {is_eq,3};
opname(42) -> {is_ne,3};
opname(43) -> {is_eq_exact,3};
opname(44) -> {is_ne_exact,3};
opname(45) -> {is_integer,2};
opname(46) -> {is_float,2};
opname(47) -> {is_number,2};
opname(48) -> {is_atom,2};
opname(49) -> {is_pid,2};
opname(50) -> {is_reference,2};
opname(51) -> {is_port,2};
opname(52) -> {is_nil,2};
opname(53) -> {is_binary,2};
opname(54) -> {is_constant,2};
opname(55) -> {is_list,2};
opname(56) -> {is_nonempty_list,2};
opname(57) -> {is_tuple,2};
opname(58) -> {test_arity,3};
opname(59) -> {select_val,3};
opname(60) -> {select_tuple_arity,3};
opname(61) -> {jump,1};
opname(62) -> {‘catch’,2};
opname(63) -> {catch_end,1};
opname(64) -> {move,2};
opname(65) -> {get_list,3};
opname(66) -> {get_tuple_element,3};
opname(67) -> {set_tuple_element,3};
opname(68) -> {put_string,3};
opname(69) -> {put_list,3};
opname(70) -> {put_tuple,2};
opname(71) -> {put,1};
opname(72) -> {badmatch,1};
opname(73) -> {if_end,0};
opname(74) -> {case_end,1};
opname(75) -> {call_fun,1};
opname(76) -> {make_fun,3};
opname(77) -> {is_function,2};
opname(78) -> {call_ext_only,2};
opname(79) -> {bs_start_match,2};
opname(80) -> {bs_get_integer,5};
opname(81) -> {bs_get_float,5};
opname(82) -> {bs_get_binary,5};
opname(83) -> {bs_skip_bits,4};
opname(84) -> {bs_test_tail,2};
opname(85) -> {bs_save,1};
opname(86) -> {bs_restore,1};
opname(87) -> {bs_init,2};
opname(88) -> {bs_final,2};
opname(89) -> {bs_put_integer,5};
opname(90) -> {bs_put_binary,5};
opname(91) -> {bs_put_float,5};
opname(92) -> {bs_put_string,2};
opname(93) -> {bs_need_buf,1};
opname(94) -> {fclearerror,0};
opname(95) -> {fcheckerror,1};
opname(96) -> {fmove,2};
opname(97) -> {fconv,2};
opname(98) -> {fadd,4};
opname(99) -> {fsub,4};
opname(100) -> {fmul,4};
opname(101) -> {fdiv,4};
opname(102) -> {fnegate,3};
opname(103) -> {make_fun2,1};
opname(104) -> {‘try’,2};
opname(105) -> {try_end,1};
opname(106) -> {try_case,1};
opname(107) -> {try_case_end,1};
opname(108) -> {raise,2};
opname(109) -> {bs_init2,6};
opname(110) -> {bs_bits_to_bytes,3};
opname(111) -> {bs_add,5};
opname(112) -> {apply,1};
opname(113) -> {apply_last,2};
opname(114) -> {is_boolean,2};
opname(115) -> {is_function2,3};
opname(116) -> {bs_start_match2,5};
opname(117) -> {bs_get_integer2,7};
opname(118) -> {bs_get_float2,7};
opname(119) -> {bs_get_binary2,7};
opname(120) -> {bs_skip_bits2,5};
opname(121) -> {bs_test_tail2,3};
opname(122) -> {bs_save2,2};
opname(123) -> {bs_restore2,2};
opname(124) -> {gc_bif1,5};
opname(125) -> {gc_bif2,6};
opname(126) -> {bs_final2,2};
opname(127) -> {bs_bits_to_bytes2,2};
opname(128) -> {put_literal,2};
opname(129) -> {is_bitstr,2};
opname(130) -> {bs_context_to_binary,1};
opname(131) -> {bs_test_unit,3};
opname(132) -> {bs_match_string,4};
opname(133) -> {bs_init_writable,0};
opname(134) -> {bs_append,8};
opname(135) -> {bs_private_append,6};
opname(136) -> {trim,2};
opname(137) -> {bs_init_bits,6};
opname(138) -> {bs_get_utf8,5};
opname(139) -> {bs_skip_utf8,4};
opname(140) -> {bs_get_utf16,5};
opname(141) -> {bs_skip_utf16,4};
opname(142) -> {bs_get_utf32,5};
opname(143) -> {bs_skip_utf32,4};
opname(144) -> {bs_utf8_size,3};
opname(145) -> {bs_put_utf8,3};
opname(146) -> {bs_utf16_size,3};
opname(147) -> {bs_put_utf16,3};
opname(148) -> {bs_put_utf32,3};
opname(149) -> {on_load,0};
opname(150) -> {recv_mark,1};
opname(151) -> {recv_set,1};
opname(152) -> {gc_bif3,7};
opname(Number) -> erlang:error(badarg, [Number]).

(Refer to: otp_src_R14B01/lib/compiler/src/beam_opcodes.erl)

You may compile an erlang source file into BEAM assembly code.
The erlc option  +’S’  does this job. For example:

  $ erlc “+’S'” foo.erl
  $ ls
  foo.erl  foo.S

Posted in erlang | Tagged | Leave a comment