1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2002-2021. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(observer_backend).
21
22%% General
23-export([vsn/0]).
24
25%% observer stuff
26-export([socket_info/0,
27	 sys_info/0, get_port_list/0, get_socket_list/0, procs_info/1,
28	 get_table/3, get_table_list/2, fetch_stats/2]).
29
30%% etop stuff
31-export([etop_collect/1]).
32-include("observer_backend.hrl").
33
34%% ttb stuff
35-export([ttb_init_node/3,
36	 ttb_write_trace_info/3,
37	 ttb_write_binary/2,
38	 ttb_stop/1,
39	 ttb_fetch/2,
40	 ttb_fetch/3,
41         ttb_resume_trace/0,
42	 ttb_get_filenames/1]).
43
44-define(CHUNKSIZE,8191). % 8 kbytes - 1 byte
45
46-ifndef(ESOCK_DONT_SHOW_UNSUPPORTED_OPTS).
47-define(ESOCK_KEEP_UNSUPPORTED_OPT(_),             true).
48-else.
49-define(ESOCK_KEEP_UNSUPPORTED_OPT(__SUPPORTED__), __SUPPORTED__).
50-endif.
51
52vsn() ->
53    case application:load(runtime_tools) of
54	R when R=:=ok; R=:={error,{already_loaded,runtime_tools}} ->
55	    application:get_key(runtime_tools,vsn);
56	Error -> Error
57    end.
58
59%%
60%% observer backend
61%%
62
63socket_info() ->
64    Info0             = socket:info(),
65    {Counters, Info1} = maps:take(counters, Info0),
66    IovMax            = maps:get(iov_max , Info1),
67    NumMons           = socket:number_of_monitors(),
68    [{iov_max, IovMax}, {num_monitors, NumMons} | maps:to_list(Counters)].
69
70
71
72sys_info() ->
73    MemInfo = try erlang:memory() of
74		  Mem -> Mem
75	      catch _:_ -> []
76	      end,
77
78    SchedulersOnline = erlang:system_info(schedulers_online),
79    SchedulersAvailable = case erlang:system_info(multi_scheduling) of
80			      enabled -> SchedulersOnline;
81			      _ -> 1
82			  end,
83
84    {{_,Input},{_,Output}} = erlang:statistics(io),
85    [{uptime, element(1, erlang:statistics(wall_clock))},
86     {run_queue, erlang:statistics(run_queue)},
87     {io_input, Input},
88     {io_output,  Output},
89
90     {logical_processors, erlang:system_info(logical_processors)},
91     {logical_processors_online, erlang:system_info(logical_processors_online)},
92     {logical_processors_available, erlang:system_info(logical_processors_available)},
93     {schedulers, erlang:system_info(schedulers)},
94     {schedulers_online, SchedulersOnline},
95     {schedulers_available, SchedulersAvailable},
96
97     {otp_release, erlang:system_info(otp_release)},
98     {version, erlang:system_info(version)},
99     {system_architecture, erlang:system_info(system_architecture)},
100     {kernel_poll, erlang:system_info(kernel_poll)},
101     {smp_support, erlang:system_info(smp_support)},
102     {threads, erlang:system_info(threads)},
103     {thread_pool_size, erlang:system_info(thread_pool_size)},
104     {wordsize_internal, erlang:system_info({wordsize, internal})},
105     {wordsize_external, erlang:system_info({wordsize, external})},
106     {alloc_info, alloc_info()},
107     {process_count, erlang:system_info(process_count)},
108     {atom_limit,  erlang:system_info(atom_limit)},
109     {atom_count, erlang:system_info(atom_count)},
110     {process_limit, erlang:system_info(process_limit)},
111     {process_count, erlang:system_info(process_count)},
112     {port_limit, erlang:system_info(port_limit)},
113     {port_count, erlang:system_info(port_count)},
114     {ets_limit,  erlang:system_info(ets_limit)},
115     {ets_count, erlang:system_info(ets_count)},
116     {dist_buf_busy_limit, erlang:system_info(dist_buf_busy_limit)}
117     | MemInfo].
118
119alloc_info() ->
120    AlcuAllocs = erlang:system_info(alloc_util_allocators),
121    try erlang:system_info({allocator_sizes, AlcuAllocs}) of
122	Allocators -> Allocators
123    catch _:_ -> []
124    end.
125
126get_table(Parent, Table, Module) ->
127    spawn(fun() ->
128		  link(Parent),
129		  get_table2(Parent, Table, Module)
130	  end).
131
132get_table2(Parent, Table, Type) ->
133    Size = case Type of
134	       ets -> ets:info(Table, size);
135	       mnesia -> mnesia:table_info(Table, size)
136	   end,
137    case Size =/= undefined andalso Size > 0 of
138	false ->
139	    Parent ! {self(), '$end_of_table'},
140	    normal;
141	true when Type =:= ets ->
142	    Mem = ets:info(Table, memory),
143	    Average = Mem div Size,
144	    NoElements = max(10, 20000 div Average),
145	    get_ets_loop(Parent, ets:match(Table, '$1', NoElements));
146	true ->
147	    Mem = mnesia:table_info(Table, memory),
148	    Average = Mem div Size,
149	    NoElements = max(10, 20000 div Average),
150	    Ms = [{'$1', [], ['$1']}],
151	    Get = fun() ->
152			  get_mnesia_loop(Parent, mnesia:select(Table, Ms, NoElements, read))
153		  end,
154	    %% Not a transaction, we don't want to grab locks when inspecting the table
155	    mnesia:async_dirty(Get)
156    end.
157
158get_ets_loop(Parent, '$end_of_table') ->
159    Parent ! {self(), '$end_of_table'};
160get_ets_loop(Parent, {Match, Cont}) ->
161    Parent ! {self(), Match},
162    get_ets_loop(Parent, ets:match(Cont)).
163
164get_mnesia_loop(Parent, '$end_of_table') ->
165    Parent ! {self(), '$end_of_table'};
166get_mnesia_loop(Parent, {Match, Cont}) ->
167    Parent ! {self(), Match},
168    get_mnesia_loop(Parent, mnesia:select(Cont)).
169
170get_port_list() ->
171    ExtraItems = [monitors,monitored_by,parallelism,locking,queue_size,memory],
172    [begin
173	 [{port_id,P}|erlang:port_info(P)] ++
174             port_info(P,ExtraItems) ++
175             inet_port_extra(erlang:port_info(P, name), P)
176     end || P <- erlang:ports()].
177
178port_info(P,[Item|Items]) ->
179    case erlang:port_info(P,Item) of
180        undefined -> port_info(P,Items);
181        Value -> [Value|port_info(P,Items)]
182    end;
183port_info(_,[]) ->
184    [].
185
186inet_port_extra({_,Type},Port) when Type =:= "udp_inet";
187                                    Type =:= "tcp_inet";
188                                    Type =:= "sctp_inet" ->
189    Data =
190        case inet:getstat(Port) of
191            {ok, Stats} -> [{statistics, Stats}];
192            _ -> []
193        end ++
194        case inet:peername(Port) of
195            {ok, {RAddr,RPort}} when is_tuple(RAddr), is_integer(RPort) ->
196                [{remote_address,RAddr},{remote_port,RPort}];
197            {ok, RAddr} ->
198                [{remote_address,RAddr}];
199            {error, _} ->  []
200        end ++
201        case inet:sockname(Port) of
202            {ok, {LAddr,LPort}} when is_tuple(LAddr), is_integer(LPort) ->
203                [{local_address,LAddr},{local_port,LPort}];
204            {ok, LAddr} ->
205                [{local_address,LAddr}];
206            {error, _} -> []
207        end ++
208        case inet:getopts(Port,
209                          [active, broadcast, buffer, bind_to_device,
210                           delay_send, deliver, dontroute, exit_on_close,
211                           header, high_msgq_watermark, high_watermark,
212                           ipv6_v6only, keepalive, linger, low_msgq_watermark,
213                           low_watermark, mode, netns, nodelay, packet,
214                           packet_size, priority, read_packets, recbuf,
215                           reuseaddr, send_timeout, send_timeout_close,
216                           show_econnreset, sndbuf, tos, tclass]) of
217            {ok, Opts} -> [{options, Opts}];
218            {error, _} -> []
219        end,
220    [{inet,Data}];
221inet_port_extra(_,_) ->
222    [].
223
224
225get_socket_list() ->
226    GetOpt = fun(_Sock, {Opt, false}) ->
227		     {Opt, "Not Supported"};
228		(Sock, {Opt, true}) ->
229		     case socket:getopt(Sock, Opt) of
230			 %% Convert to string?
231			 {ok, Value0} ->
232			     %% d("get_socket_list -> ok: "
233			     %% 	"~n   Option: ~p"
234			     %% 	"~n   Value:  ~p", [Opt, Value]),
235			     Value =
236				 if
237				     (Value0 =:= []) -> "-";
238				     true -> Value0
239				 end,
240			     {Opt, Value};
241			 %% We need to handle error cases and convert them
242			 %% to something useful ("Not Supported")
243			 {error, enotsup} = _ERROR ->
244			     %% d("get_socket_list -> error: enotsup"),
245			     {Opt, "Not Supported"};
246			 {error, enoprotoopt} = _ERROR ->
247			     %% d("get_socket_list -> error: enoprotoopt"),
248			     {Opt, "Not Supported"};
249			 {error, enotconn} = _ERROR ->
250			     %% d("get_socket_list -> error: enotconn"),
251			     {Opt, "Not Connected"};
252			 {error, {invalid, _}} = _ERROR ->
253			     %% d("get_socket_list -> error: invalid"),
254			     {Opt, "Not Implemented"};
255			 {error, Reason} ->
256			     %% d("get_socket_list -> error: "
257			     %% 	"~n   Option: ~p"
258			     %% 	"~n   Reason: ~p", [Opt, _Reason]),
259			     {Opt, f("error:~p", [Reason])}
260		     end
261	     end,
262    [begin
263	 Kind  = socket:which_socket_kind(S),
264	 FD    = case socket:getopt(S, otp, fd) of
265		     {ok, FD0} ->
266			 FD0;
267		     _ ->
268			 -1
269		 end,
270	 Info0  = socket:info(S),
271	 IdStr0 = socket:to_list(S),
272	 IdStr  = case Info0 of
273		      #{type     := stream,
274			protocol := tcp} when (Kind =:= compat) ->
275			  %% Faketi fake
276			  "#Socket" ++ Id = IdStr0,
277			  "#InetSocket" ++ Id;
278		      _ ->
279			  IdStr0
280		  end,
281	 {Counters0, Info1} = maps:take(counters, Info0),
282	 Counters = maps:to_list(Counters0),
283	 Info2 = maps:remove(ctype,         Info1),
284	 Info3 = maps:remove(num_acceptors, Info2),
285	 Info4 = maps:remove(num_readers,   Info3),
286	 Info5 = maps:remove(num_writers,   Info4),
287	 Info6 =
288	     case socket:peername(S) of
289		 {ok, RAddr} ->
290		     RAddrStr = sockaddr_to_list(RAddr),
291		     maps:put(raddress, RAddrStr, Info5);
292		 _ ->
293		     Info5
294	     end,
295	 Info7 =
296	     case socket:sockname(S) of
297		 {ok, LAddr} ->
298		     LAddrStr = sockaddr_to_list(LAddr),
299		     maps:put(laddress, LAddrStr, Info6);
300		 _ ->
301		     Info6
302	     end,
303	 SockOpts =
304	     [{{socket, Opt}, Supported} ||
305		 {Opt, Supported} <-
306		     socket:supports(options, socket),
307		 ?ESOCK_KEEP_UNSUPPORTED_OPT(Supported)],
308	 DomainOpts =
309	     case Info7 of
310		 #{domain := inet6} ->
311		     [{{ipv6, Opt}, Supported} ||
312			 {Opt, Supported} <-
313			     socket:supports(options, ipv6),
314			 ?ESOCK_KEEP_UNSUPPORTED_OPT(Supported)];
315		 _ ->
316		     [{{ip, Opt}, Supported} ||
317			 {Opt, Supported} <-
318			     socket:supports(options, ip),
319			 ?ESOCK_KEEP_UNSUPPORTED_OPT(Supported)]
320	     end,
321	 ProtoOpts =
322	     case Info7 of
323		 #{domain   := Domain,
324		   type     := stream,
325		   protocol := tcp} when (Domain =:= inet) orelse
326					 (Domain =:= inet6) ->
327		     [{{tcp, Opt}, Supported} ||
328			 {Opt, Supported} <-
329			     socket:supports(options, tcp),
330			 ?ESOCK_KEEP_UNSUPPORTED_OPT(Supported)];
331		 #{domain   := Domain,
332		   type     := dgram,
333		   protocol := udp} when (Domain =:= inet) orelse
334					 (Domain =:= inet6) ->
335		     [{{udp, Opt}, Supported} ||
336			 {Opt, Supported} <-
337			     socket:supports(options, udp),
338			 ?ESOCK_KEEP_UNSUPPORTED_OPT(Supported)];
339		 #{domain   := Domain,
340		   type     := seqpacket,
341		   protocol := sctp} when (Domain =:= inet) orelse
342					  (Domain =:= inet6) ->
343		     [{{sctp, Opt}, Supported} ||
344			 {Opt, Supported} <-
345			     socket:supports(options, sctp),
346			 ?ESOCK_KEEP_UNSUPPORTED_OPT(Supported)];
347		 _ ->
348		     []
349	     end,
350	 Opts    = SockOpts ++ DomainOpts ++ ProtoOpts,
351	 Options = [GetOpt(S, Opt) || Opt <- Opts],
352	 %% d("get_socket_list -> "
353	 %%   "~n   Options:  ~p", [Options]),
354	 Info7#{id           => S,
355		id_str       => IdStr,
356		fd           => FD,
357		kind         => Kind,
358		monitored_by => socket:monitored_by(S),
359		statistics   => Counters,
360		options      => lists:sort(Options)}
361     end || S <- socket:which_sockets()].
362
363sockaddr_to_list(#{family := local, path := PathBin}) when is_binary(PathBin) ->
364    erlang:binary_to_list(PathBin);
365sockaddr_to_list(#{family := local, path := Path}) when is_list(Path) ->
366    Path;
367sockaddr_to_list(#{family := inet, addr := Addr, port := Port}) ->
368    inet_parse:ntoa(Addr) ++ " : " ++ erlang:integer_to_list(Port);
369sockaddr_to_list(#{family := inet6, addr := Addr, port := Port,
370		   flowinfo := FI, scope_id := SID}) ->
371    inet_parse:ntoa(Addr) ++ " : " ++
372	erlang:integer_to_list(Port) ++
373	" , " ++ erlang:integer_to_list(FI) ++
374	" , " ++ erlang:integer_to_list(SID);
375sockaddr_to_list(Addr) ->
376    f("~p", [Addr]).
377
378
379get_table_list(ets, Opts) ->
380    HideUnread = proplists:get_value(unread_hidden, Opts, true),
381    HideSys = proplists:get_value(sys_hidden, Opts, true),
382    Info = fun(Id, Acc) ->
383		   try
384		       TabId = case ets:info(Id, named_table) of
385				   true -> ignore;
386				   false -> Id
387			       end,
388		       Name = ets:info(Id, name),
389		       Protection = ets:info(Id, protection),
390		       ignore(HideUnread andalso Protection == private, unreadable),
391		       Owner = ets:info(Id, owner),
392		       RegName = case catch process_info(Owner, registered_name) of
393				     [] -> ignore;
394				     {registered_name, ProcName} -> ProcName
395				 end,
396		       ignore(HideSys andalso ordsets:is_element(RegName, sys_processes()), system_tab),
397		       ignore(HideSys andalso ordsets:is_element(Name, sys_tables()), system_tab),
398		       ignore((RegName == mnesia_monitor)
399			      andalso Name /= schema
400			      andalso is_atom((catch mnesia:table_info(Name, where_to_read))), mnesia_tab),
401		       Memory = ets:info(Id, memory) * erlang:system_info(wordsize),
402		       Tab = [{name,Name},
403			      {id,TabId},
404			      {protection,Protection},
405			      {owner,Owner},
406			      {size,ets:info(Id, size)},
407			      {reg_name,RegName},
408			      {type,ets:info(Id, type)},
409			      {keypos,ets:info(Id, keypos)},
410			      {heir,ets:info(Id, heir)},
411			      {memory,Memory},
412			      {compressed,ets:info(Id, compressed)},
413			      {fixed,ets:info(Id, fixed)}
414			     ],
415		       [Tab|Acc]
416		   catch _:_What ->
417			   %% io:format("Skipped ~p: ~p ~n",[Id, _What]),
418			   Acc
419		   end
420	   end,
421    lists:foldl(Info, [], ets:all());
422
423get_table_list(mnesia, Opts) ->
424    HideSys = proplists:get_value(sys_hidden, Opts, true),
425    Owner = ets:info(schema, owner),
426    Owner /= undefined orelse
427	throw({error, "Mnesia is not running on: " ++ atom_to_list(node())}),
428    {registered_name, RegName} = process_info(Owner, registered_name),
429    Info = fun(Id, Acc) ->
430		   try
431		       Name = Id,
432		       ignore(HideSys andalso ordsets:is_element(Name, mnesia_tables()), system_tab),
433		       ignore(Name =:= schema, mnesia_tab),
434		       Storage = mnesia:table_info(Id, storage_type),
435		       Tab0 = [{name,Name},
436			       {owner,Owner},
437			       {size,mnesia:table_info(Id, size)},
438			       {reg_name,RegName},
439			       {type,mnesia:table_info(Id, type)},
440			       {keypos,2},
441			       {memory,mnesia:table_info(Id, memory) * erlang:system_info(wordsize)},
442			       {storage,Storage},
443			       {index,mnesia:table_info(Id, index)}
444			      ],
445		       Tab = if Storage == disc_only_copies ->
446				     [{fixed, dets:info(Id, safe_fixed)}|Tab0];
447				(Storage == ram_copies) orelse
448				(Storage == disc_copies) ->
449				     [{fixed, ets:info(Id, fixed)},
450				      {compressed, ets:info(Id, compressed)}|Tab0];
451				true -> Tab0
452			     end,
453		       [Tab|Acc]
454		   catch _:_What ->
455			   %% io:format("Skipped ~p: ~p ~p ~n",[Id, _What, Stacktrace]),
456			   Acc
457		   end
458	   end,
459    lists:foldl(Info, [], mnesia:system_info(tables)).
460
461fetch_stats(Parent, Time) ->
462    process_flag(trap_exit, true),
463    fetch_stats_loop(Parent, Time).
464
465fetch_stats_loop(Parent, Time) ->
466    erlang:system_flag(scheduler_wall_time, true),
467    receive
468	_Msg ->
469	    erlang:system_flag(scheduler_wall_time, false),
470	    ok
471    after Time ->
472	    _M = Parent ! {stats, 1,
473			   erlang:statistics(scheduler_wall_time),
474			   erlang:statistics(io),
475			   try erlang:memory() catch _:_ -> [] end},
476	    fetch_stats_loop(Parent, Time)
477    end.
478
479%%
480%% Chunk sending process info to etop/observer
481%%
482procs_info(Collector) ->
483    All = processes(),
484    Send = fun Send (Pids) ->
485                   try lists:split(10000, Pids) of
486                       {First, Rest} ->
487                           Collector ! {procs_info, self(), etop_collect(First, [])},
488                           Send(Rest)
489                   catch _:_ ->
490                           Collector ! {procs_info, self(), etop_collect(Pids, [])}
491                   end
492           end,
493    Send(All).
494
495%%
496%% etop backend
497%%
498etop_collect(Collector) ->
499    %% If this is the first time and the scheduler_wall_time flag is
500    %% false, SchedulerWallTime will be 'undefined' (and show 0 cpu
501    %% utilization in etop). Next time the flag will be true and then
502    %% there will be a measurement.
503    SchedulerWallTime = erlang:statistics(scheduler_wall_time),
504    ProcInfo = etop_collect(processes(), []),
505
506    Collector ! {self(),#etop_info{now = erlang:timestamp(),
507				   n_procs = length(ProcInfo),
508				   run_queue = erlang:statistics(run_queue),
509				   runtime = SchedulerWallTime,
510				   memi = etop_memi(),
511				   procinfo = ProcInfo
512				  }},
513
514    case SchedulerWallTime of
515	undefined ->
516            spawn(fun() -> flag_holder_proc(Collector) end),
517            ok;
518	_ ->
519	    ok
520    end.
521
522flag_holder_proc(Collector) ->
523    erlang:system_flag(scheduler_wall_time,true),
524    Ref = erlang:monitor(process,Collector),
525    receive
526	{'DOWN',Ref,_,_,_} ->
527	    erlang:system_flag(scheduler_wall_time,false),
528	    ok
529    end.
530
531etop_memi() ->
532    try
533	[{total, c:memory(total)},
534	 {processes, c:memory(processes)},
535	 {ets, c:memory(ets)},
536	 {atom, c:memory(atom)},
537	 {code, c:memory(code)},
538	 {binary, c:memory(binary)}]
539    catch
540	error:notsup ->
541	    undefined
542    end.
543
544etop_collect([P|Ps], Acc) when P =:= self() ->
545    etop_collect(Ps, Acc);
546etop_collect([P|Ps], Acc) ->
547    Fs = [registered_name,initial_call,memory,reductions,current_function,message_queue_len],
548    case process_info(P, Fs) of
549	undefined ->
550	    etop_collect(Ps, Acc);
551	[{registered_name,Reg},{initial_call,Initial},{memory,Mem},
552	 {reductions,Reds},{current_function,Current},{message_queue_len,Qlen}] ->
553	    Name = case Reg of
554		       [] -> initial_call(Initial, P);
555		       _ -> Reg
556		   end,
557	    Info = #etop_proc_info{pid=P,mem=Mem,reds=Reds,name=Name,
558				   cf=Current,mq=Qlen},
559	    etop_collect(Ps, [Info|Acc])
560    end;
561etop_collect([], Acc) -> Acc.
562
563initial_call({proc_lib, init_p, _}, Pid) ->
564    proc_lib:translate_initial_call(Pid);
565initial_call(Initial, _Pid) ->
566    Initial.
567
568%%
569%% ttb backend
570%%
571ttb_init_node(MetaFile_0,PI,Traci) ->
572    if
573	is_list(MetaFile_0);
574	is_atom(MetaFile_0) ->
575	    {ok, Cwd} = file:get_cwd(),
576	    MetaFile = filename:join(Cwd, MetaFile_0),
577	    file:delete(MetaFile);
578	true -> 				% {local,_,_}
579	    MetaFile = MetaFile_0
580    end,
581    case proplists:get_value(resume, Traci) of
582        {true, _} -> (autostart_module()):write_config(Traci);
583        _    -> ok
584    end,
585    Self = self(),
586    MetaPid = spawn(fun() -> ttb_meta_tracer(MetaFile,PI,Self,Traci) end),
587    receive {MetaPid,started} -> ok end,
588    MetaPid ! {metadata,Traci},
589    case PI of
590	true ->
591	    MetaPid ! {metadata,pnames()},
592            ok;
593	false ->
594	    ok
595    end,
596    {ok,MetaFile,MetaPid}.
597
598ttb_write_trace_info(MetaPid,Key,What) ->
599    MetaPid ! {metadata,Key,What},
600    ok.
601
602ttb_meta_tracer(MetaFile,PI,Parent,SessionData) ->
603    erlang:monitor(process, proplists:get_value(ttb_control, SessionData)),
604    case PI of
605	true ->
606	    ReturnMS = [{'_',[],[{return_trace}]}],
607	    erlang:trace_pattern({erlang,spawn,3},ReturnMS,[meta]),
608	    erlang:trace_pattern({erlang,spawn_link,3},ReturnMS,[meta]),
609	    erlang:trace_pattern({erlang,spawn_opt,4},ReturnMS,[meta]),
610	    erlang:trace_pattern({erts_internal,spawn_init,1},[],[meta]),
611	    erlang:trace_pattern({erts_internal,dist_spawn_init,1},[],[meta]),
612	    erlang:trace_pattern({erlang,register,2},[],[meta]),
613	    erlang:trace_pattern({global,register_name,2},[],[meta]),
614            ok;
615	false ->
616	    ok
617    end,
618    Parent ! {self(),started},
619    case proplists:get_value(overload_check, SessionData) of
620        {Ms, M, F} ->
621            catch M:F(init),
622            erlang:send_after(Ms, self(), overload_check),
623            ok;
624        _ ->
625            ok
626    end,
627    ttb_meta_tracer_loop(MetaFile,PI,dict:new(),SessionData).
628
629ttb_meta_tracer_loop(MetaFile,PI,Acc,State) ->
630    receive
631	{trace_ts,_,call,{erlang,register,[Name,Pid]},_} ->
632	    ok = ttb_store_meta({pid,{Pid,Name}},MetaFile),
633	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
634	{trace_ts,_,call,{global,register_name,[Name,Pid]},_} ->
635	    ok = ttb_store_meta({pid,{Pid,{global,Name}}},MetaFile),
636	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
637	{trace_ts,CallingPid,call,{erlang,spawn_opt,[M,F,Args,_]},_} ->
638	    MFA = {M,F,length(Args)},
639	    NewAcc = dict:update(CallingPid,
640				 fun(Old) -> [MFA|Old] end, [MFA],
641				 Acc),
642	    ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State);
643	{trace_ts,CallingPid,return_from,{erlang,spawn_opt,_Arity},Ret,_} ->
644	    case Ret of
645		{NewPid,_Mref} when is_pid(NewPid) -> ok;
646		NewPid when is_pid(NewPid) -> ok
647	    end,
648	    NewAcc =
649		dict:update(CallingPid,
650			    fun([H|T]) ->
651				    ok = ttb_store_meta({pid,{NewPid,H}},MetaFile),
652				    T
653			    end,
654			    Acc),
655	    ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State);
656	{trace_ts,CallingPid,call,{erlang,Spawn,[M,F,Args]},_}
657	when Spawn==spawn;Spawn==spawn_link ->
658	    MFA = {M,F,length(Args)},
659	    NewAcc = dict:update(CallingPid,
660				 fun(Old) -> [MFA|Old] end, [MFA],
661				 Acc),
662	    ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State);
663
664	{trace_ts,CallingPid,return_from,{erlang,Spawn,_Arity},NewPid,_}
665	when Spawn==spawn;Spawn==spawn_link ->
666	    NewAcc =
667		dict:update(CallingPid,
668			    fun([H|T]) ->
669				    ok = ttb_store_meta({pid,{NewPid,H}},MetaFile),
670				    T
671			    end,
672			    Acc),
673	    ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State);
674
675	{trace_ts,CallingPid,call,{erts_internal,spawn_init,[{M,F,Args}]},_} ->
676            %% Local spawn_request()...
677            ok = ttb_store_meta({pid,{CallingPid,{M,F,length(Args)}}},MetaFile),
678	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
679
680	{trace_ts,CallingPid,call,{erts_internal, dist_spawn_init, [MFnoA]},_} ->
681            %% Distributed spawn_request()...
682            ok = ttb_store_meta({pid,{CallingPid,MFnoA}},MetaFile),
683	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
684
685	{metadata,Data} when is_list(Data) ->
686	    ok = ttb_store_meta(Data,MetaFile),
687	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
688
689	{metadata,Key,Fun} when is_function(Fun) ->
690	    ok = ttb_store_meta([{Key,Fun()}],MetaFile),
691	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
692
693	{metadata,Key,What} ->
694	    ok = ttb_store_meta([{Key,What}],MetaFile),
695	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
696        overload_check ->
697            {Ms, M, F} = proplists:get_value(overload_check, State),
698            case catch M:F(check) of
699                true ->
700                    erlang:trace(all, false, [all]),
701                    ControlPid = proplists:get_value(ttb_control, State),
702                    ControlPid ! {node_overloaded, node()},
703                    catch M:F(stop),
704                    ttb_meta_tracer_loop(MetaFile,PI,Acc,lists:keydelete(overload_check, 1, State));
705                _ ->
706                    erlang:send_after(Ms, self(), overload_check),
707                    ttb_meta_tracer_loop(MetaFile,PI,Acc, State)
708            end;
709     {'DOWN', _, _, _, _} ->
710            _ = stop_seq_trace(),
711            self() ! stop,
712            ttb_meta_tracer_loop(MetaFile,PI,Acc, State);
713     stop when PI=:=true ->
714            try_stop_resume(State),
715            try_stop_overload_check(State),
716            erlang:trace_pattern({erlang,spawn,3},false,[meta]),
717	    erlang:trace_pattern({erlang,spawn_link,3},false,[meta]),
718	    erlang:trace_pattern({erlang,spawn_opt,4},false,[meta]),
719	    erlang:trace_pattern({erts_internal,spawn_init,1},false,[meta]),
720	    erlang:trace_pattern({erts_internal,dist_spawn_init,1},false,[meta]),
721	    erlang:trace_pattern({erlang,register,2},false,[meta]),
722	    erlang:trace_pattern({global,register_name,2},false,[meta]);
723	stop ->
724            try_stop_resume(State),
725            try_stop_overload_check(State)
726    end.
727
728try_stop_overload_check(State) ->
729    case proplists:get_value(overload, State) of
730        undefined -> ok;
731        {_, M, F} -> catch M:F(stop)
732    end.
733
734pnames() ->
735    Processes = processes(),
736    Globals = lists:map(fun(G) -> {global:whereis_name(G),G} end,
737			global:registered_names()),
738    lists:flatten(lists:foldl(fun(Pid,Acc) -> [pinfo(Pid,Globals)|Acc] end,
739			      [], Processes)).
740
741pinfo(P,Globals) ->
742    case process_info(P,registered_name) of
743	[] ->
744	    case lists:keysearch(P,1,Globals) of
745		{value,{P,G}} -> {pid,{P,{global,G}}};
746		false ->
747		    case process_info(P,initial_call) of
748			{_,I} -> {pid,{P,I}};
749			undefined -> [] % the process has terminated
750		    end
751	    end;
752	{_,R} -> {pid,{P,R}};
753	undefined -> [] % the process has terminated
754    end.
755
756autostart_module() ->
757    element(2, application:get_env(runtime_tools, ttb_autostart_module)).
758
759try_stop_resume(State) ->
760    case proplists:get_value(resume, State) of
761        true -> (autostart_module()):delete_config();
762        _    -> ok
763    end.
764
765ttb_resume_trace() ->
766    case (autostart_module()):read_config() of
767        {error, _} ->
768            ok;
769        {ok, Data} ->
770            Pid = proplists:get_value(ttb_control, Data),
771            {_, Timeout} = proplists:get_value(resume, Data),
772            case rpc:call(node(Pid), erlang, whereis, [ttb]) of
773                Pid ->
774                    Pid ! {noderesumed, node(), self()},
775                    wait_for_fetch_ready(Timeout);
776                _ ->
777                    ok
778            end,
779            (autostart_module()):delete_config(),
780            ok
781    end.
782
783wait_for_fetch_ready(Timeout) ->
784    receive
785        trace_resumed ->
786            ok
787    after Timeout ->
788            ok
789    end.
790
791ttb_store_meta(Data,{local,MetaFile,Port}) when is_list(Data) ->
792    ttb_send_to_port(Port,MetaFile,Data);
793ttb_store_meta(Data,MetaFile) when is_list(Data) ->
794    {ok,Fd} = file:open(MetaFile,[raw,append]),
795    ttb_write_binary(Fd,Data),
796    file:close(Fd);
797ttb_store_meta(Data,MetaFile) ->
798    ttb_store_meta([Data],MetaFile).
799
800ttb_write_binary(Fd,[H|T]) ->
801    ok = file:write(Fd,ttb_make_binary(H)),
802    ttb_write_binary(Fd,T);
803ttb_write_binary(_Fd,[]) ->
804    ok.
805
806ttb_send_to_port(Port,MetaFile,[H|T]) ->
807    B1 = ttb_make_binary(H),
808    B2 = term_to_binary({metadata,MetaFile,B1}),
809    erlang:port_command(Port,B2),
810    ttb_send_to_port(Port,MetaFile,T);
811ttb_send_to_port(_Port,_MetaFile,[]) ->
812    ok.
813
814ttb_make_binary(Term) ->
815    B = term_to_binary(Term),
816    SizeB = byte_size(B),
817    if SizeB > 255 ->
818	    %% size is bigger than 8 bits, must therefore add an extra
819	    %% size field
820	    SB = term_to_binary({'$size',SizeB}),
821	    <<(byte_size(SB)):8, SB/binary, B/binary>>;
822        true ->
823	    <<SizeB:8, B/binary>>
824    end.
825
826
827%% Stop ttb
828ttb_stop(MetaPid) ->
829    Delivered = erlang:trace_delivered(all),
830    receive
831	{trace_delivered,all,Delivered} -> ok
832    end,
833    Ref = erlang:monitor(process,MetaPid),
834    MetaPid ! stop,
835
836    %% Must wait for the process to terminate there
837    %% because dbg will be stopped when this function
838    %% returns, and then the Port (in {local,MetaFile,Port})
839    %% cannot be accessed any more.
840    receive {'DOWN', Ref, process, MetaPid, _Info} -> ok end,
841    stop_seq_trace().
842
843stop_seq_trace() ->
844    seq_trace:reset_trace(),
845    seq_trace:set_system_tracer(false).
846
847%% Fetch ttb logs from remote node
848ttb_fetch(MetaFile,{Port,Host}) ->
849    ttb_fetch(MetaFile,{Port,Host},undefined).
850ttb_fetch(MetaFile,{Port,Host},MasterEnc) ->
851    erlang:process_flag(priority,low),
852    Files = ttb_get_filenames(MetaFile),
853    {ok, Sock} = gen_tcp:connect(Host, Port, [binary, {packet, 2}]),
854    send_files({Sock,Host},Files,MasterEnc,file:native_name_encoding()),
855    ok = gen_tcp:close(Sock).
856
857
858send_files({Sock,Host},[File|Files],MasterEnc,MyEnc) ->
859    {ok,Fd} = file:open(File,[raw,read,binary]),
860    Basename = filename:basename(File),
861    {Code,FilenameBin} = encode_filename(Basename,MasterEnc,MyEnc),
862    ok = gen_tcp:send(Sock,<<Code,FilenameBin/binary>>),
863    send_chunks(Sock,Fd),
864    ok = file:delete(File),
865    send_files({Sock,Host},Files,MasterEnc,MyEnc);
866send_files({_Sock,_Host},[],_MasterEnc,_MyEnc) ->
867    done.
868
869encode_filename(Basename,undefined,MyEnc) ->
870    %% Compatible with old version of ttb.erl, but no longer crashing
871    %% for code points > 255.
872    {1,unicode:characters_to_binary(Basename,MyEnc,MyEnc)};
873encode_filename(Basename,MasterEnc,MyEnc) ->
874    case unicode:characters_to_binary(Basename,MyEnc,MasterEnc) of
875        Bin when is_binary(Bin) ->
876            %% Encoding succeeded
877            {2,Bin};
878        _ ->
879            %% Can't convert Basename from my encoding to the master
880            %% node's encoding. Doing my best and hoping that master
881            %% node can fix it...
882            {3,unicode:characters_to_binary(Basename,MyEnc,MyEnc)}
883    end.
884
885send_chunks(Sock,Fd) ->
886    case file:read(Fd,?CHUNKSIZE) of
887	{ok,Bin} ->
888	    ok = gen_tcp:send(Sock, <<0,Bin/binary>>),
889	    send_chunks(Sock,Fd);
890	eof ->
891	    ok;
892	{error,Reason} ->
893	    ok = gen_tcp:send(Sock, <<2,(term_to_binary(Reason))/binary>>)
894    end.
895
896ttb_get_filenames(MetaFile) ->
897    Dir = filename:dirname(MetaFile),
898    Root = filename:rootname(filename:basename(MetaFile)),
899    {ok,List} = file:list_dir(Dir),
900    match_filenames(Dir,Root,List,[]).
901
902match_filenames(Dir,MetaFile,[H|T],Files) ->
903    case lists:prefix(MetaFile,H) of
904	true -> match_filenames(Dir,MetaFile,T,[filename:join(Dir,H)|Files]);
905	false -> match_filenames(Dir,MetaFile,T,Files)
906    end;
907match_filenames(_Dir,_MetaFile,[],Files) ->
908    Files.
909
910
911%%%%%%%%%%%%%%%%%
912
913sys_tables() ->
914    [ac_tab,  asn1,
915     cdv_dump_index_table,  cdv_menu_table,  cdv_decode_heap_table,
916     cell_id,  cell_pos,  clist,
917     cover_internal_data_table,   cover_collected_remote_data_table, cover_binary_code_table,
918     code, code_names,  cookies,
919     corba_policy,  corba_policy_associations,
920     dets, dets_owners, dets_registry,
921     disk_log_names, disk_log_pids,
922     eprof,  erl_atom_cache, erl_epmd_nodes,
923     etop_accum_tab,  etop_tr,
924     ets_coverage_data,
925     file_io_servers,
926     gs_mapping, gs_names,  gstk_db,
927     gstk_grid_cellid, gstk_grid_cellpos, gstk_grid_id,
928     httpd,
929     id,
930     ign_req_index, ign_requests,
931     index,
932     inet_cache, inet_db, inet_hosts,
933     'InitialReferences',
934     int_db,
935     interpreter_includedirs_macros,
936     ir_WstringDef,
937     lmcounter,  locks,
938						%     mnesia_decision,
939     mnesia_gvar, mnesia_stats,
940						%     mnesia_transient_decision,
941     pg2_table,
942     pg,
943     queue,
944     schema,
945     shell_records,
946     snmp_agent_table, snmp_local_db2, snmp_mib_data, snmp_note_store, snmp_symbolic_ets,
947     tkFun, tkLink, tkPriv,
948     ttb, ttb_history_table,
949     udp_fds, udp_pids
950    ].
951
952sys_processes() ->
953    [auth, code_server, global_name_server, inet_db,
954     mnesia_recover, net_kernel, pg, timer_server, wxe_master].
955
956mnesia_tables() ->
957    [ir_AliasDef, ir_ArrayDef, ir_AttributeDef, ir_ConstantDef,
958     ir_Contained, ir_Container, ir_EnumDef, ir_ExceptionDef,
959     ir_IDLType, ir_IRObject, ir_InterfaceDef, ir_ModuleDef,
960     ir_ORB, ir_OperationDef, ir_PrimitiveDef, ir_Repository,
961     ir_SequenceDef, ir_StringDef, ir_StructDef, ir_TypedefDef,
962     ir_UnionDef, logTable, logTransferTable, mesh_meas,
963     mesh_type, mnesia_clist, orber_CosNaming,
964     orber_objkeys, user
965    ].
966
967ignore(true, Reason) -> throw(Reason);
968ignore(_,_ ) -> ok.
969
970f(F, A) ->
971    lists:flatten(io_lib:format(F, A)).
972
973%% d(F) ->
974%%     d(F, []).
975
976%% d(Debug, F) when is_boolean(Debug) andalso is_list(F) ->
977%%     d(Debug, F, []);
978%% d(F, A) when is_list(F) andalso is_list(A) ->
979%%     d(get(debug), F, A).
980
981%% d(true, F, A) ->
982%%     io:format("[ob] " ++ F ++ "~n", A);
983%% d(_, _, _) ->
984%%     ok.
985
986
987