erlang各种有用的函数包括一些有用nif封装,还有一些性能测试case。
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

476 行
16 KiB

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