erlang各种有用的函数包括一些有用nif封装,还有一些性能测试case。
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

475 líneas
16 KiB

hace 5 años
hace 4 años
hace 4 años
hace 4 años
hace 4 años
hace 5 años
  1. -module(utVMInfo).
  2. -compile([export_all, nowarn_export_all]).
  3. %% 打印并排序各个表的缓存消耗
  4. show_cache() ->
  5. io:format("table name | memory | size~n", []),
  6. lists:reverse(lists:keysort(2, [{T, ets:info(T, memory), ets:info(T, size)} || T <- ets:all()])).
  7. %% 打印进程消耗内存的信息
  8. show_process() ->
  9. lists:reverse(lists:keysort(2, [{erlang:process_info(P, registered_name), erlang:process_info(P, heap_size)} || P <- erlang:processes()])).
  10. %% 打印当前进程数量
  11. show_process_count() ->
  12. length(erlang:processes()).
  13. %% 反编译
  14. %% 确认线上运行代码是否正确,reltools没掌握好,升级偶尔出现问题
  15. decompile(Mod) ->
  16. {ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(code:which(Mod), [abstract_code]),
  17. io:format("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).
  18. %% 进程栈
  19. %% 类似于jstack,发现大量进程挂起,进程数过高,运行慢,hang住等问题用到
  20. pstack(Reg) when is_atom(Reg) ->
  21. case whereis(Reg) of
  22. undefined -> undefined;
  23. Pid -> pstack(Pid)
  24. end;
  25. pstack(Pid) ->
  26. io:format("~s~n", [element(2, process_info(Pid, backtrace))]).
  27. %% ====================================================================
  28. %% etop
  29. %% 分析内存、cpu占用进程,即使数十w进程node 也能正常使用
  30. %% 进程CPU占用排名
  31. %% --------------------------------------------------------------------
  32. etop() ->
  33. spawn(fun() -> etop:start([{output, text}, {interval, 10}, {lines, 20}, {sort, reductions}]) end).
  34. %% 进程Mem占用排名
  35. etop_mem() ->
  36. spawn(fun() -> etop:start([{output, text}, {interval, 10}, {lines, 20}, {sort, memory}]) end).
  37. %% 停止etop
  38. etop_stop() ->
  39. etop:stop().
  40. %% ====================================================================
  41. %% 对所有process做gc
  42. %% 进程内存过高时,来一发,看看是内存泄露还是gc不过来
  43. gc_all() ->
  44. [erlang:garbage_collect(Pid) || Pid <- processes()].
  45. %% 对MFA 执行分析,会严重减缓运行,建议只对小量业务执行
  46. %% 结果:
  47. %% fprof 结果比较详细,能够输出热点调用路径
  48. fprof(M, F, A) ->
  49. fprof:start(),
  50. fprof:apply(M, F, A),
  51. fprof:profile(),
  52. fprof:analyse(),
  53. fprof:stop().
  54. %% 对整个节点内所有进程执行eprof, eprof 对线上业务有一定影响,慎用!
  55. %% 建议TimeoutSec<10s,且进程数< 1000,否则可能导致节点crash
  56. %% 结果:
  57. %% 输出每个方法实际执行时间(不会累计方法内其他mod调用执行时间)
  58. %% 只能得到mod - Fun 执行次数 执行耗时
  59. eprof_all(TimeoutSec) ->
  60. eprof(processes() -- [whereis(eprof)], TimeoutSec).
  61. eprof(Pids, TimeoutSec) ->
  62. eprof:start(),
  63. eprof:start_profiling(Pids),
  64. timer:sleep(TimeoutSec),
  65. eprof:stop_profiling(),
  66. eprof:analyze(total),
  67. eprof:stop().
  68. %% scheduler usage
  69. %% 统计下1s每个调度器CPU的实际利用率(因为有spin wait、调度工作, 可能usage 比top显示低很多)
  70. scheduler_usage() ->
  71. scheduler_usage(1000).
  72. scheduler_usage(RunMs) ->
  73. erlang:system_flag(scheduler_wall_time, true),
  74. Ts0 = lists:sort(erlang:statistics(scheduler_wall_time)),
  75. timer:sleep(RunMs),
  76. Ts1 = lists:sort(erlang:statistics(scheduler_wall_time)),
  77. erlang:system_flag(scheduler_wall_time, false),
  78. Cores = lists:map(fun({{_I, A0, T0}, {I, A1, T1}}) ->
  79. {I, (A1 - A0) / (T1 - T0)} end, lists:zip(Ts0, Ts1)),
  80. {A, T} = lists:foldl(fun({{_, A0, T0}, {_, A1, T1}}, {Ai,Ti}) ->
  81. {Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0, Ts1)),
  82. Total = A/T,
  83. io:format("~p~n", [[{total, Total} | Cores]]).
  84. %% 进程调度
  85. %% 统计下1s内调度进程数量(含义:第一个数字执行进程数量,第二个数字迁移进程数量)
  86. scheduler_stat() ->
  87. scheduler_stat(1000).
  88. scheduler_stat(RunMs) ->
  89. erlang:system_flag(scheduling_statistics, enable),
  90. Ts0 = erlang:system_info(total_scheduling_statistics),
  91. timer:sleep(RunMs),
  92. Ts1 = erlang:system_info(total_scheduling_statistics),
  93. erlang:system_flag(scheduling_statistics, disable),
  94. lists:map(fun({{_Key, In0, Out0}, {Key, In1, Out1}}) ->
  95. {Key, In1 - In0, Out1 - Out0} end, lists:zip(Ts0, Ts1)).
  96. %% ====================================================================
  97. %% trace 日志
  98. %% 会把mod 每次调用详细MFA log 下来,args 太大就不好看了
  99. %% trace Mod 所有方法的调用
  100. %% --------------------------------------------------------------------
  101. trace(Mod) ->
  102. dbg:tracer(),
  103. dbg:tpl(Mod, '_', []),
  104. dbg:p(all, c).
  105. %% trace Node上指定 Mod 所有方法的调用, 结果将输出到本地shell
  106. trace(Node, Mod) ->
  107. dbg:tracer(),
  108. dbg:n(Node),
  109. dbg:tpl(Mod, '_', []),
  110. dbg:p(all, c).
  111. %% 停止trace
  112. trace_stop() ->
  113. dbg:stop_clear().
  114. %% ====================================================================
  115. %% 内存高OOM 排查工具
  116. %% etop 无法应对10w+ 进程节点, 下面代码就没问题了;找到可疑proc后通过pstack、message_queu_len 排查原因
  117. proc_mem_all(SizeLimitKb) ->
  118. Procs = [{undefined, Pid} || Pid<- erlang:processes()],
  119. proc_mem(Procs, SizeLimitKb).
  120. proc_mem(SizeLimitKb) ->
  121. Procs = [{Name, Pid} || {_, Name, Pid, _} <- release_handler_1:get_supervised_procs(),
  122. is_process_alive(Pid)],
  123. proc_mem(Procs, SizeLimitKb).
  124. proc_mem(Procs, SizeLimitKb) ->
  125. SizeLimit = SizeLimitKb * 1024,
  126. {R, Total} = lists:foldl(fun({Name, Pid}, {Acc, TotalSize}) ->
  127. case erlang:process_info(Pid, total_heap_size) of
  128. {_, Size0} ->
  129. Size = Size0*8,
  130. case Size > SizeLimit of
  131. true -> {[{Name, Pid, Size} | Acc], TotalSize+Size};
  132. false -> {Acc, TotalSize}
  133. end;
  134. _ -> {Acc, TotalSize}
  135. end
  136. end, {[], 0}, Procs),
  137. R1 = lists:keysort(3, R),
  138. {Total, lists:reverse(R1)}.
  139. show(N) ->
  140. F = fun(P) ->
  141. case catch process_info(P, [memory, dictionary]) of
  142. [{_, Memory}, {_, Dict}] ->
  143. InitStart = util:prop_get_value('$initial_call', Dict, null),
  144. {InitStart, Memory};
  145. _ -> {null, 0}
  146. end
  147. end,
  148. Infos1 = lists:map(F, processes()),
  149. Infos2 = [{Name, M} || {Name, M} <- Infos1, Name =/= null],
  150. SortFun = fun({_, M1}, {_, M2}) -> M1 > M2 end,
  151. Infos3 = lists:sort(SortFun, Infos2),
  152. Infos4 = lists:sublist(Infos3, N),
  153. [io:format("~p : ~p ~n", [Name, M]) || {Name, M} <- Infos4],
  154. ok.
  155. show(N, SkipNames) ->
  156. F = fun(P) ->
  157. case catch process_info(P, [memory, dictionary]) of
  158. [{_, Memory}, {_, Dict}] ->
  159. InitStart = util:prop_get_value('$initial_call', Dict, null),
  160. case catch tuple_to_list(InitStart) of
  161. [Name | _] ->
  162. case lists:member(Name, SkipNames) of
  163. true -> {null, 0};
  164. false -> {InitStart, Memory}
  165. end;
  166. _ -> {null, 0}
  167. end;
  168. _ -> {null, 0}
  169. end
  170. end,
  171. Infos1 = lists:map(F, processes()),
  172. Infos2 = [{Name, M} || {Name, M} <- Infos1, Name =/= null],
  173. SortFun = fun({_, M1}, {_, M2}) -> M1 > M2 end,
  174. Infos3 = lists:sort(SortFun, Infos2),
  175. Infos4 = lists:sublist(Infos3, N),
  176. [io:format("~p : ~p ~n", [Name, M]) || {Name, M} <- Infos4],
  177. ok.
  178. show1(N) ->
  179. F = fun(P, Acc) ->
  180. case catch process_info(P, [memory, dictionary]) of
  181. [{_, Memory}, {_, Dict}] ->
  182. InitStart = util:prop_get_value('$initial_call', Dict, null),
  183. case lists:keyfind(InitStart, 1, Acc) of
  184. false -> [{InitStart, Memory, 1} | Acc];
  185. {InitStart, Memory1, Num} -> lists:keystore(InitStart, 1, Acc, {InitStart, Memory + Memory1, Num + 1})
  186. end;
  187. _ -> Acc
  188. end
  189. end,
  190. Infos1 = lists:foldl(F, [], processes()),
  191. Infos2 = [{Name, M, Num} || {Name, M, Num} <- Infos1, Name =/= null],
  192. SortFun = fun({_, M1, _}, {_, M2, _}) -> M1 > M2 end,
  193. Infos3 = lists:sort(SortFun, Infos2),
  194. Infos4 = lists:sublist(Infos3, N),
  195. [io:format("~p : per_memory=~p process_num=~p ~n", [Name, (M div Num), Num]) || {Name, M, Num} <- Infos4],
  196. ok.
  197. %% 得到CPU核数
  198. coreCnt() ->
  199. erlang:system_info(schedulers).
  200. %% 获取当前进程运行的核id
  201. coreIndex() ->
  202. erlang:system_info(scheduler_id).
  203. %% @doc 节点所有进程信息
  204. process_infos() ->
  205. filelib:ensure_dir("./logs/"),
  206. File = "./logs/processes_infos.log",
  207. {ok, Fd} = file:open(File, [write, raw, binary, append]),
  208. Fun = fun(Pi) ->
  209. Info = io_lib:format("=>~p \n\n", [Pi]),
  210. case filelib:is_file(File) of
  211. true -> file:write(Fd, Info);
  212. false ->
  213. file:close(Fd),
  214. {ok, NewFd} = file:open(File, [write, raw, binary, append]),
  215. file:write(NewFd, Info)
  216. end,
  217. timer:sleep(20)
  218. end,
  219. [Fun(erlang:process_info(P)) || P <- erlang:processes()].
  220. rfc1123_local_date() ->
  221. rfc1123_local_date(os:timestamp()).
  222. rfc1123_local_date({A, B, C}) ->
  223. rfc1123_local_date(calendar:now_to_local_time({A, B, C}));
  224. rfc1123_local_date({{YYYY, MM, DD}, {Hour, Min, Sec}}) ->
  225. DayNumber = calendar:day_of_the_week({YYYY, MM, DD}),
  226. lists:flatten(
  227. io_lib:format("~s, ~2.2.0w ~3.s ~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT",
  228. [httpd_util:day(DayNumber), DD, httpd_util:month(MM), YYYY, Hour, Min, Sec]));
  229. rfc1123_local_date(Epoch) when erlang:is_integer(Epoch) ->
  230. rfc1123_local_date(calendar:gregorian_seconds_to_datetime(Epoch + 62167219200)).
  231. %% @doc erlang_dump
  232. crash_dump() ->
  233. Date = erlang:list_to_binary(rfc1123_local_date()),
  234. Header = binary:list_to_bin([<<"=erl_crash_dump:0.2\n">>, Date, <<"\nSystem version: ">>]),
  235. Ets = ets_info(),
  236. Report = binary:list_to_bin([Header, erlang:list_to_binary(erlang:system_info(system_version)),
  237. erlang:system_info(info), erlang:system_info(procs), Ets, erlang:system_info(dist),
  238. <<"=loaded_modules\n">>, binary:replace(erlang:system_info(loaded),
  239. <<"\n">>, <<"\n=mod:">>, [global])]),
  240. file:write_file("erl_crash.dump", Report).
  241. ets_info() ->
  242. binary:list_to_bin([ets_table_info(T) || T <- ets:all()]).
  243. ets_table_info(Table) ->
  244. Info = ets:info(Table),
  245. Owner = erlang:list_to_binary(erlang:pid_to_list(proplists:get_value(owner, Info))),
  246. TableN = erlang:list_to_binary(erlang:atom_to_list(proplists:get_value(name, Info))),
  247. Name = erlang:list_to_binary(erlang:atom_to_list(proplists:get_value(name, Info))),
  248. Objects = erlang:list_to_binary(erlang:integer_to_list(proplists:get_value(size, Info))),
  249. binary:list_to_bin([<<"=ets:">>, Owner, <<"\nTable: ">>, TableN, <<"\nName: ">>, Name,
  250. <<"\nObjects: ">>, Objects, <<"\n">>]).
  251. %% 检查溢出的内存,强制gc, 并写入日志分析
  252. check_mem(MemLim) ->
  253. lists:foreach(
  254. fun(P) ->
  255. case is_pid(P) andalso erlang:is_process_alive(P) of
  256. true ->
  257. {memory, Mem} = erlang:process_info(P, memory),
  258. case Mem > MemLim of
  259. true ->
  260. erlang:garbage_collect(P);
  261. false ->
  262. []
  263. end;
  264. false ->
  265. []
  266. end
  267. end, erlang:processes()).
  268. %% @spec top() -> ok
  269. %% @doc 查看系统当前的综合信息
  270. top() ->
  271. Release = erlang:system_info(otp_release),
  272. SchedNum = erlang:system_info(schedulers),
  273. ProcCount = erlang:system_info(process_count),
  274. ProcLimit = erlang:system_info(process_limit),
  275. ProcMemUsed = erlang:memory(processes_used),
  276. EtsMemAlc = erlang:memory(ets),
  277. MemTot = erlang:memory(total),
  278. %PetNum = all_pets(),
  279. io:format(
  280. "++++++++++++++++++++++++++++++++++++++++++~n"
  281. " Node: ~p~n"
  282. " Erlang Ver: ~p~n"
  283. " Free Threads: ~p~n"
  284. " Process Used Memory: ~pMb~n"
  285. " Ets Used Memory: ~pMb~n"
  286. " Erlang VM Used Memory: ~pMb~n"
  287. " Process Limit: ~p~n"
  288. " Process Used: ~p~n"
  289. "++++++++++++++++++++++++++++++++++++++++++~n"
  290. , [node(), Release, SchedNum, ProcMemUsed / 1024 / 1024, EtsMemAlc / 1024 / 1024, MemTot / 1024 / 1024, ProcLimit, ProcCount]),
  291. ok.
  292. %% @doc 运维要用
  293. top_back() ->
  294. Release = erlang:system_info(otp_release),
  295. SchedNum = erlang:system_info(schedulers),
  296. ProcCount = erlang:system_info(process_count),
  297. ProcLimit = erlang:system_info(process_limit),
  298. ProcMemUsed = erlang:memory(processes_used),
  299. EtsMemAlc = erlang:memory(ets),
  300. MemTot = erlang:memory(total),
  301. Str = io_lib:format(
  302. " Erlang 版本: ~p~n"
  303. " 可使用的调度线程: ~p~n"
  304. " 所有进程使用的内存: ~pMb~n"
  305. " 所有ets使用的内存: ~pMb~n"
  306. " Erlang系统占用内存: ~pMb~n"
  307. " 可创建进程数量上限: ~p~n"
  308. " 当前进程数: ~p~n"
  309. , [Release, SchedNum, ProcMemUsed / 1024 / 1024, EtsMemAlc / 1024 / 1024, MemTot / 1024 / 1024, ProcLimit, ProcCount]),
  310. binary_to_list(list_to_binary(Str)).
  311. %% @spec ets_mem() -> term()
  312. %% @doc 查看内存占用最多的30张ets表
  313. ets_mem() ->
  314. L = ets:all(),
  315. Mems = lists:map(fun(Tab) ->
  316. Info = ets:info(Tab),
  317. case lists:keyfind(memory, 1, Info) of
  318. {memory, Mem} -> {Tab, Mem};
  319. _ -> {Tab, 0}
  320. end
  321. end, L),
  322. L1 = lists:sublist(lists:reverse(lists:keysort(2, Mems)), 30),
  323. io:format("~n--------------------------------------------------~n"
  324. "~-30w ~w~n--------------------------------------------------~n"
  325. , [table, used_memory]),
  326. lists:foreach(
  327. fun({Tab, Mem}) ->
  328. io:format("~-30w ~wKb~n", [Tab, Mem / 1024])
  329. end, L1).
  330. %% @spec tcp_links() -> Info
  331. %% @doc 统计tcp链接
  332. tcp_links() ->
  333. L = erlang:ports(),
  334. F = fun(P) ->
  335. Pinfo = erlang:port_info(P),
  336. case lists:keyfind(name, 1, Pinfo) of
  337. {name, "tcp_inet"} -> true;
  338. _ -> false
  339. end
  340. end,
  341. L1 = lists:filter(F, L),
  342. io:format("~n当前socket数量(包括链接数据库的socket): ~w~n", [length(L1)]).
  343. %% @doc 备份进程信息
  344. dump_process_info(Pid) ->
  345. {{Year, Month, Day}, {Hour, Minutes, Second}} = util:local_time(),
  346. {ok, FileHandle} = file:open(util:fbin("~s-~w-~w-~w-~w-~w-~w", [<<"../logs/pid_info.dump">>, Year, Month, Day, Hour, Minutes, Second]), write),
  347. case erlang:process_info(Pid) of
  348. Info when is_list(Info) ->
  349. lists:foreach(fun({messages, Messages}) ->
  350. case Messages =:= [] of
  351. true ->
  352. io:format(FileHandle, "~w~n", [{messages, Messages}]);
  353. _ ->
  354. io:format(FileHandle, "{messages,~n", []),
  355. lists:foreach(fun(M) ->
  356. io:format(FileHandle, " ~w~n", [M])
  357. end, Messages),
  358. io:format(FileHandle, "}~n", [])
  359. end;
  360. ({dictionary, Dics}) ->
  361. case Dics =:= [] of
  362. true ->
  363. io:format(FileHandle, "~w~n", [{dictionary, Dics}]);
  364. _ ->
  365. io:format(FileHandle, "{dictionary,~n", []),
  366. lists:foreach(fun(M) ->
  367. io:format(FileHandle, " ~w~n", [M])
  368. end, Dics),
  369. io:format(FileHandle, "}~n", [])
  370. end;
  371. (E) ->
  372. io:format(FileHandle, "~w~n", [E])
  373. end, Info);
  374. _ ->
  375. io:format("not find pid info")
  376. end,
  377. file:close(FileHandle).
  378. get_process_info_and_zero_value(InfoName) ->
  379. PList = erlang:processes(),
  380. ZList = lists:filter(
  381. fun(T) ->
  382. case erlang:process_info(T, InfoName) of
  383. {InfoName, 0} -> false;
  384. _ -> true
  385. end
  386. end, PList),
  387. ZZList = lists:map(
  388. fun(T) -> {T, erlang:process_info(T, InfoName), erlang:process_info(T, registered_name)}
  389. end, ZList),
  390. [length(PList), InfoName, length(ZZList), ZZList].
  391. get_process_info_and_large_than_value(InfoName, Value) ->
  392. PList = erlang:processes(),
  393. ZList = lists:filter(
  394. fun(T) ->
  395. case erlang:process_info(T, InfoName) of
  396. {InfoName, VV} ->
  397. if VV > Value -> true;
  398. true -> false
  399. end;
  400. _ -> true
  401. end
  402. end, PList),
  403. ZZList = lists:map(
  404. fun(T) -> {T, erlang:process_info(T, InfoName), erlang:process_info(T, registered_name)}
  405. end, ZList),
  406. [length(PList), InfoName, Value, length(ZZList), ZZList].
  407. get_msg_queue() ->
  408. io:fwrite("process count:~p~n~p value is not 0 count:~p~nLists:~p~n",
  409. get_process_info_and_zero_value(message_queue_len)).
  410. get_memory() ->
  411. io:fwrite("process count:~p~n~p value is large than ~p count:~p~nLists:~p~n",
  412. get_process_info_and_large_than_value(memory, 1048576)).
  413. get_memory(Value) ->
  414. io:fwrite("process count:~p~n~p value is large than ~p count:~p~nLists:~p~n",
  415. get_process_info_and_large_than_value(memory, Value)).
  416. get_heap() ->
  417. io:fwrite("process count:~p~n~p value is large than ~p count:~p~nLists:~p~n",
  418. get_process_info_and_large_than_value(heap_size, 1048576)).
  419. get_heap(Value) ->
  420. io:fwrite("process count:~p~n~p value is large than ~p count:~p~nLists:~p~n",
  421. get_process_info_and_large_than_value(heap_size, Value)).
  422. get_processes() ->
  423. io:fwrite("process count:~p~n~p value is large than ~p count:~p~nLists:~p~n",
  424. get_process_info_and_large_than_value(memory, 0)).