2014/6/3

erlang dialyzer

dialyzer 是 DIscrepancy AnalYZer for ERlang programs 的縮寫,這是靜態分析工具,可在執行前,用來偵測一些錯誤狀況,例如資料型別定義錯誤、dead or unreachable code、不必要的測試等等。

plt: persisten lookup table

為了分析程式,我們可產生 plt (persistent lookup table) 檔案,用來減少程式碼分析的時間,plt 裡面儲存了資料型別與函數的資訊。

首先產生一個常用函式庫的 plt,裡面包含了 erts, kernel, stdlib, mnesia, crypto, sasi。

在 linux 可直接產生 plt 檔案,但在 Windows 會因為缺少環境變數 HOME 或 DIALYZER_PLT,而產生錯誤訊息:The HOME environment variable needs to be set so that Dialyzer knows where to find the default PLT,必須先設定環境變數,然後再產生 plt 檔案。

set HOME=%USERPROFILE%

再用以下指令產生 .dialyzer_plt 檔案

> dialyzer --build_plt --apps erts kernel stdlib mnesia crypto sasl

  Creating PLT c:/Users/username/.dialyzer_plt ...
Unknown functions:
  compile:file/2
  compile:forms/2
  compile:noenv_forms/2
  compile:output_generated/1
  xref:add_application/3
  xref:analyze/2
  xref:set_default/3
  xref:set_library_path/2
  xref:start/2
  xref:stop/1
Unknown types:
  compile:option/0
 done in 4m22.58s
done (passed successfully)

從錯誤訊息看出還缺少了一些函數,可以使用用以下指令,再把一些缺少的 module 增加到 plt 裡面。但增加後,還是會持續產生一些錯誤訊息,我們就不再增加那些函式庫的 plt

> dialyzer --add_to_plt --plt "%HOME%/.dialyzer_plt" --apps compiler tools webtool debugger inets

也可以使用以下這個方式,直接用 -c 指定 ebeam 的目錄。

> dialyzer --add_to_plt --plt "%HOME%/.dialyzer_plt" -c "%HOME%/lib/ssl-5.3.3/ebin"

如果要從 plt 中移除某個函式庫,則是用

dialyzer --remove_from_plt --plt "%HOME%/.dialyzer_plt" -c "%HOME%/lib/ssl-5.3.3/ebin"

進行 dialyzer 分析

以一個簡單的 test.erl 為例

%% test.erl
-module(test).
-export([bar/1, foo/0]).

bar(List) ->
    abc.

foo() ->
    V1 = bar([]),
    io:format("length: ~p~n", [length(V1)]).

由原始檔分析

> dialyzer --src -c test.erl
  Checking whether the PLT c:/Users/username/.dialyzer_plt is up-to-date... yes
  Proceeding with analysis...
test.erl:8: Function foo/0 has no local return
test.erl:10: The call erlang:length(V1::'abc') will never return since it differs in the 1st argument from the success typing arguments: ([any()])
Unknown functions:
  erlang:get_module_info/1
  erlang:get_module_info/2
  io:format/2
 done in 0m0.44s
done (warnings were emitted)

由以下兩行得知,程式發生資料型別的錯誤

test.erl:8: Function foo/0 has no local return
test.erl:10: The call erlang:length(V1::'abc') will never return since it differs in the 1st argument from the success typing arguments: ([any()])

將程式修改一下

-module(test).
-export([bar/1, foo/0]).

bar(List) ->
    [1,2,3].

foo() ->
    V1 = bar([]),
    io:format("length: ~p~n", [length(V1)]).

分析就會成功

> dialyzer --src -c test.erl
  Checking whether the PLT c:/Users/username/.dialyzer_plt is up-to-date... yes
  Proceeding with analysis...
Unknown functions:
  erlang:get_module_info/1
  erlang:get_module_info/2
  io:format/2
 done in 0m0.42s
done (passed successfully)

由目的檔分析

如果直接編譯,分析時會出現類似這樣的錯誤

Could not scan the following file(s):
  Could not get abstract code for: d:/temp/code/test.beam
  Recompile with +debug_info or analyze starting from source code

因此我們要在編譯時,增加參數 +debug_info

> erlc +debug_info test.erl

接下來就可以對 ebeam 進行分析

> dialyzer -r .
  Checking whether the PLT c:/Users/username/.dialyzer_plt is up-to-date... yes
  Proceeding with analysis...
test.erl:8: Function foo/0 has no local return
test.erl:10: The call erlang:length(V1::'abc') will never return since it differ
s in the 1st argument from the success typing arguments: ([any()])
Unknown functions:
  erlang:get_module_info/1
  erlang:get_module_info/2
  io:format/2
 done in 0m0.42s
done (warnings were emitted)