Dialyzer: detect problematic code in Erlang source code

Dialyzer is a static analysis tool that identifies software discrepancies such as type errors, unreachable code, unnecessary tests, etc in single Erlang modules or entire (sets of) applications. This tool was developed in the Uppsala University, and is included in Erlang/OTP R11.

Dialyzer analyzes .beam files which where compiled with the debug_info option.

How to run Dialyzer for an Erlang module

  1. Compile your Erlang source code file with debug_info enabled:
    erlc +debug_info test.erl
  2. Run dialyzer:
    dialyzer -c test.beam

How to run Dialyzer for ejabberd trunk using Erlang/OTP R12

  1. Clean all beam files:
    make clean
  2. Prepare ejabberd compilation:
    ./configure --enable-odbc --enable-pam
  3. Compile ejabberd with debug enabled:
    make debug=true
    make debug=true
  4. Create a PLT from all the OTP libraries that will be used by ejabberd. This step is optional. Note that it can take almost 2 hours and a half in a powerful machine to create this PLT! Set the paths correctly for your system. Note that evertyime you update Erlang/OTP you will need to regenerate this PLT.
    export ERL_TOP=/usr/lib/erlang/
    
    dialyzer --build_plt --output_plt $HOME/.dialyzer_otp.plt -r $ERL_TOP/lib/stdlib/ebin 
      $ERL_TOP/lib/kernel/ebin $ERL_TOP/lib/mnesia/ebin $ERL_TOP/lib/ssl/ebin 
      $ERL_TOP/lib/asn1/ebin $ERL_TOP/lib/compiler/ebin $ERL_TOP/lib/crypto/ebin 
      $ERL_TOP/lib/syntax_tools/ebin $ERL_TOP/lib/inets/ebin $ERL_TOP/lib/sasl/ebin 
      $ERL_TOP/lib/odbc/ebin
  5. Create the ejabberd PLT file, based in the OTP file:
    dialyzer --add_to_plt --plt $HOME/.dialyzer_otp.plt --output_plt ejabberd.plt -c .
      Adding information from /home/bernar/.dialyzer_otp2.plt to ejabberd.plt...
    Unknown functions:
      ejabberd_logger:critical_msg/4
      ejabberd_logger:debug_msg/4
      ejabberd_logger:error_msg/4
      ejabberd_logger:info_msg/4
      ejabberd_logger:warning_msg/4
      mysql:get_result_affected_rows/1
      mysql:get_result_field_info/1
      mysql:get_result_reason/1
      mysql:get_result_rows/1
      mysql_conn:fetch/3
      mysql_conn:start/6
      odbc:connect/2
      odbc:sql_query/2
      pgsql:connect/5
      pgsql:squery/2
     done in 4m2.69s
    done (passed successfully)
  6. Now when the source code of ejabberd changes, you can update the PLT with:
    dialyzer --check_plt --plt ejabberd.plt -c .
  7. And finally use this PLT to analyze the beam files:
    dialyzer --plt ejabberd.plt -c .
      Checking whether the PLT ejabberd.plt is up-to-date... yes
      Proceeding with analysis...
    ELDAPv3.erl:532: The variable _ can never match since previous clauses completely covered the type 'mandatory'
    adhoc.erl:47: Guard test XData::'false' | {'xmlelement',_,maybe_improper_list(),_} =:= 'true' can never succeed.
    ...
    ram_file_io_server.erl:402: Guard test is_list(Buf::binary()) can never succeed
    treap.erl:61: The pattern 'nil' can never match the type {_,_,_,_,_}
    Unknown functions:
      ejabberd_logger:critical_msg/4
      ejabberd_logger:debug_msg/4
      ejabberd_logger:error_msg/4
      ejabberd_logger:info_msg/4
      ejabberd_logger:warning_msg/4
      mysql:get_result_affected_rows/1
      mysql:get_result_field_info/1
      mysql:get_result_reason/1
      mysql:get_result_rows/1
      mysql_conn:fetch/3
      mysql_conn:start/6
      odbc:connect/2
      odbc:sql_query/2
      pgsql:connect/5
      pgsql:squery/2
     done in 4m40.66s
    done (warnings were emitted)
    

How to run Dialyzer for ejabberd trunk using Erlang/OTP R11

  1. Clean all beam files:
    make clean
  2. Compile ejabberd with debug enabled:
    make debug=true
  3. Run Dialyzer:
    make dialyzer
  4. It will take some minutes. Finally you will get the results:
      Checking whether the initial PLT exists and is up-to-date... yes
      Proceeding with analysis...
    adhoc.erl:30: Guard test XData::'false' | {'xmlelement',_,_,_} =:= 'true' can never succeed.
    cyrsasl.erl:50: Function check_authzid/2 will never be called
    ejabberd_app.erl:53: Function init/0 has no local return
    ejabberd_app.erl:79: Function loop/1 has no local return
    ejabberd_ctl.erl:88: The pattern {'error', Reason} can never match the type 'ok'
    ejabberd_ctl.erl:195: The pattern 'false' can never match the type 'error' | string()
    ...
    Unknown functions: [{'PKIX1Explicit88',decode,2},
                        {'PKIX1Implicit88',decode,2},
                        {asn1rt,decode,3},
                        {asn1rt,encode,3},
                        ...
    done (warnings were emitted)
    make: *** [dialyzer] Error 2

How to run Dialyzer for an ejabberd-modules module

  1. Edit Emakefile and add the option 'debug_info' to the files you want to inspect:
    {'src/mod_presence',     [debug_info, {outdir, "ebin"},{i,"../../ejabberd-dev/trunk/include"}]}.
  2. Clean the binary directory:
    rm ebin/*.beam
  3. Compile as usual:
    ./build.sh
    Recompile: src/mod_presence
  4. Run Dialyzer:
    dialyzer -c ebin/mod_presence.beam   
      Checking whether the initial PLT exists and is up-to-date... yes
      Proceeding with analysis...
    mod_presence.erl:456: The variable _ can never match since previous clauses completely 
      covered the type {'bare',_,_} | {'sorted',_,_} | {'status',_,_} | {'xml',_,_}
    mod_presence.erl:478: The variable _ can never match since previous clauses completely 
      covered the type 'list' | 'xdata'
    Unknown functions: [{acl,match_rule,3},
                        {ejabberd_config,get_global_option,1},
                        ...
    done (warnings were emitted)

Related links

Syndicate content