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)
沒有留言:
張貼留言