Skip to content

EMQ源码分析(四):调试模块和fwrite

前言

有时候看不懂代码,可以直接去debug一些模块,写一些单元测试。虽然Erlang提供了专门的单元测试方法,但由于没有整体配置(暂时不想二次开发),有一些模块无法启动,所以干脆只把需要的代码拷贝出来做debug观察数据,从而理解原理。整个分析基于EMQ2.3.11。

一、调试方法

1、准备好一个空Erlang工程

参考《EMQ源码分析(一):在Windows上用IDEA搭建Erlang编译平台》,构建一个Helloworld就好。

2、拷贝代码

将需要测试的部分代码从EMQ里面拷贝出来,例如我想要了解订阅树如何构建的,就需要了解Topic如何被切分成三元组的,三元组具体的数据又是怎样的,把emqttd_trie、emqttd_topic相关代码拷贝出来:

erlang
-module(hello).
-author("BEWINDOWEB").
-import(lists, [reverse/1]).

%% API
-type(word()   :: '' | '+' | '#' | binary()).
-type(words()  :: list(word())).
-type(triple() :: {root | binary(), word(), binary()}).
-type(topic() :: binary()).

-export([start/0]).

-spec(triples(topic()) -> list(triple())).
triples(Topic) when is_binary(Topic) ->
  triples(words(Topic), root, []).

triples([], _Parent, Acc) ->
  reverse(Acc);

triples([W|Words], Parent, Acc) ->
  Node = join(Parent, W),
  triples(Words, Node, [{Parent, W, Node}|Acc]).

%% @doc Split Topic Path to Words
-spec(words(topic()) -> words()).
words(Topic) when is_binary(Topic) ->
  [word(W) || W <- binary:split(Topic, <<"/">>, [global])].

word(<<>>)    -> '';
word(<<"+">>) -> '+';
word(<<"#">>) -> '#';
word(Bin)     -> Bin.

join(root, W) ->
  bin(W);
join(Parent, W) ->
  <<(bin(Parent))/binary, $/, (bin(W))/binary>>.
bin('')  -> <<>>;
bin('+') -> <<"+">>;
bin('#') -> <<"#">>;
bin(B) when is_binary(B) -> B.

start() ->
  io:fwrite("Hello, world!\n"),
  Triples = triples(<<"a/b/c">>),
  Triples2 = triples(<<"a/+/c">>).

3、断点调试

打上断点调试,就能得到自己想看的数据,从而理解原理:

f86298398ccd48630157340e75d22081.png

二、控制台格式化输出

有时候debug的数据不正确(比如位串会以有符号8位来显示),这时需要fwrite,和printf类似。

fwrite可以类似printf去简单控制输出格式,标准格式如下:

erlang
~F.P.PadModC

1、不带参数示例

erlang
io:fwrite("123").

2、格式化参数示例

erlang
% io:fwrite("~F.P.C",data).
io:fwrite("|~10.5s|~n|",[<<"aaa">>]).
% |     aaa  |
% |
  • ~类似%
  • F:输出长度,10代表总共10位;
  • P:输出精度,5代表a有5位;
  • C:输出格式,s代表字符串输出,n代表换行符;
  • <<"aaa">><<$a,$a,$a>>的语法糖;
  • 其余字符按正常字符输出。

所以最后的第一行包含了|+5个空格+3个a+2个空格+|,第二行只有|

3、格式化参数的含义

符号C含义FP参数
~cascii码最大长度重复次数
~f浮点数最大长度精度
~e科学计数法最大长度精度
~n换行符最大次数重复次数
~s字符串最大长度截取长度
~w任意元最大长度字串长度
~W~w、限制打印深度最大长度字串长度深度
~p任意元、适当换行和缩进、列表输出为字串单行/多行缩进长度
~P~p、限制打印深度单行/多行缩进长度深度
~b进制整数(小写字母)最大长度进制
~B进制整数(大写字母)最大长度进制
~x带任意前缀进制整数(小写字母)最大长度进制前缀
~X带任意前缀进制整数(大写字母)最大长度进制前缀
~+带进制前缀整数(小写字母)最大长度进制
~#带进制前缀整数(大写字母)最大长度进制

4、格式化参数上下界的意义

符号C小于最大长度F超过最大长度F小于精度P超过精度P
~c左侧补空格报错右侧补空格截断
~f左侧补空格输出*小数点后补0小数点后截断
~e左侧补空格输出*小数点后补0小数点后截断
~n(最大次数)(最大次数)(重复次数)(重复次数)
~s左侧补空格报错右侧补空格从头部开始截断
~w左侧补空格输出*-输出*
~W左侧补空格输出*-输出*
~p(0单行/1多行)(0单行/1多行)(缩进长度)(缩进长度)
~P(0单行/1多行)(0单行/1多行)(缩进长度)(缩进长度)
~b左侧补空格输出*(进制)(进制)
~B左侧补空格输出*(进制)(进制)
~x左侧补空格输出*(进制)(进制)
~X左侧补空格输出*(进制)(进制)
~+左侧补空格输出*(进制)(进制)
~#左侧补空格输出*(进制)(进制)

5、格式化参数实例

erlang
start() ->
  io:fwrite("[c]~10.3c~n",[$a]),
  % [c]       aaa
  io:fwrite("[f]~10.1f~n",[1.23]),
  % [f]       1.2
  io:fwrite("[e]~10.2e~n",[1.23]),
  % [e]    1.2e+0
  io:fwrite("[n]~2.9n"),
  % [n]
  % 
  io:fwrite("[s]~10.3s~n",["aaabbbbbbb"]),
  % [s]       aaa
  io:fwrite("[w]~60.90w~n",[{"1231231231","aaaaaa"}]),
  % [w]       {[49,50,51,49,50,51,49,50,51,49],[97,97,97,97,97,97]}
  io:fwrite("[W]~60.30W~n",[{"1231231231","aaaaaa"},5]),
  % [W]                                {[49,50,51|...],[97,97|...]}
  io:fwrite("[p]~3.0p~n",[{"123123","123123","aaaa"}]),
  % [p]{"123123",
  % "123123",
  % "aaaa"}
  io:fwrite("[P]~1.20P~n",[{"123123","123123","aaaa"},10]),
  % [P]{"123123",
  %                     "123123",
  %                     "aaaa"}
  io:fwrite("[b]~10.16b~n", [109]),
  % [b]        6d
  io:fwrite("[B]~10.16B~n", [109]),
  % [B]        6D
  io:fwrite("[x]~10.16x~n", [109,"prefix"]),
  % [x]  prefix6d
  io:fwrite("[X]~10.16X~n", [109,"prefix"]),
  % [X]  prefix6D
  io:fwrite("[+]~10.16+~n", [109]),
  % [+]     16#6d
  io:fwrite("[#]~10.16#~n", [109]).
  % [#]     16#6D

6、Pad、Mod

Pad可以指定填充符号,默认空格:

erlang
io:fwrite("[c]~10.3.+c~n",[$a]).
% [c]+++++++aaa

Mod可以指定模式,只有t可以选择,t表示unicode:

erlang
io:format("~s",[<<"三颗豆子"/utf8>>]),
% 三颗豆子
io:format("~ts",[<<"三颗豆子"/utf8>>]).
% \x{4E09}\x{9897}\x{8C46}\x{5B50}
转载请注明出处https://bananaoven.com/articles/258.html | 香蕉微波炉
分享许可方式知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
重大发现:转载注明原文网址的同学刚买了彩票就中奖,刚写完代码就跑通,刚转身就遇到了真爱。