1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2002-2018. 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([sys_info/0, get_port_list/0, procs_info/1,
27	 get_table/3, get_table_list/2, fetch_stats/2]).
28
29%% etop stuff
30-export([etop_collect/1]).
31-include("observer_backend.hrl").
32
33%% ttb stuff
34-export([ttb_init_node/3,
35	 ttb_write_trace_info/3,
36	 ttb_write_binary/2,
37	 ttb_stop/1,
38	 ttb_fetch/2,
39	 ttb_fetch/3,
40         ttb_resume_trace/0,
41	 ttb_get_filenames/1]).
42-define(CHUNKSIZE,8191). % 8 kbytes - 1 byte
43
44vsn() ->
45    case application:load(runtime_tools) of
46	R when R=:=ok; R=:={error,{already_loaded,runtime_tools}} ->
47	    application:get_key(runtime_tools,vsn);
48	Error -> Error
49    end.
50
51%%
52%% observer backend
53%%
54sys_info() ->
55    MemInfo = try erlang:memory() of
56		  Mem -> Mem
57	      catch _:_ -> []
58	      end,
59
60    SchedulersOnline = erlang:system_info(schedulers_online),
61    SchedulersAvailable = case erlang:system_info(multi_scheduling) of
62			      enabled -> SchedulersOnline;
63			      _ -> 1
64			  end,
65
66    {{_,Input},{_,Output}} = erlang:statistics(io),
67    [{uptime, element(1, erlang:statistics(wall_clock))},
68     {run_queue, erlang:statistics(run_queue)},
69     {io_input, Input},
70     {io_output,  Output},
71
72     {logical_processors, erlang:system_info(logical_processors)},
73     {logical_processors_online, erlang:system_info(logical_processors_online)},
74     {logical_processors_available, erlang:system_info(logical_processors_available)},
75     {schedulers, erlang:system_info(schedulers)},
76     {schedulers_online, SchedulersOnline},
77     {schedulers_available, SchedulersAvailable},
78
79     {otp_release, erlang:system_info(otp_release)},
80     {version, erlang:system_info(version)},
81     {system_architecture, erlang:system_info(system_architecture)},
82     {kernel_poll, erlang:system_info(kernel_poll)},
83     {smp_support, erlang:system_info(smp_support)},
84     {threads, erlang:system_info(threads)},
85     {thread_pool_size, erlang:system_info(thread_pool_size)},
86     {wordsize_internal, erlang:system_info({wordsize, internal})},
87     {wordsize_external, erlang:system_info({wordsize, external})},
88     {alloc_info, alloc_info()},
89     {process_count, erlang:system_info(process_count)},
90     {atom_limit,  erlang:system_info(atom_limit)},
91     {atom_count, erlang:system_info(atom_count)},
92     {process_limit, erlang:system_info(process_limit)},
93     {process_count, erlang:system_info(process_count)},
94     {port_limit, erlang:system_info(port_limit)},
95     {port_count, erlang:system_info(port_count)},
96     {ets_limit,  erlang:system_info(ets_limit)},
97     {ets_count, erlang:system_info(ets_count)},
98     {dist_buf_busy_limit, erlang:system_info(dist_buf_busy_limit)}
99     | MemInfo].
100
101alloc_info() ->
102    AlcuAllocs = erlang:system_info(alloc_util_allocators),
103    try erlang:system_info({allocator_sizes, AlcuAllocs}) of
104	Allocators -> Allocators
105    catch _:_ -> []
106    end.
107
108get_table(Parent, Table, Module) ->
109    spawn(fun() ->
110		  link(Parent),
111		  get_table2(Parent, Table, Module)
112	  end).
113
114get_table2(Parent, Table, Type) ->
115    Size = case Type of
116	       ets -> ets:info(Table, size);
117	       mnesia -> mnesia:table_info(Table, size)
118	   end,
119    case Size =/= undefined andalso Size > 0 of
120	false ->
121	    Parent ! {self(), '$end_of_table'},
122	    normal;
123	true when Type =:= ets ->
124	    Mem = ets:info(Table, memory),
125	    Average = Mem div Size,
126	    NoElements = max(10, 20000 div Average),
127	    get_ets_loop(Parent, ets:match(Table, '$1', NoElements));
128	true ->
129	    Mem = mnesia:table_info(Table, memory),
130	    Average = Mem div Size,
131	    NoElements = max(10, 20000 div Average),
132	    Ms = [{'$1', [], ['$1']}],
133	    Get = fun() ->
134			  get_mnesia_loop(Parent, mnesia:select(Table, Ms, NoElements, read))
135		  end,
136	    %% Not a transaction, we don't want to grab locks when inspecting the table
137	    mnesia:async_dirty(Get)
138    end.
139
140get_ets_loop(Parent, '$end_of_table') ->
141    Parent ! {self(), '$end_of_table'};
142get_ets_loop(Parent, {Match, Cont}) ->
143    Parent ! {self(), Match},
144    get_ets_loop(Parent, ets:match(Cont)).
145
146get_mnesia_loop(Parent, '$end_of_table') ->
147    Parent ! {self(), '$end_of_table'};
148get_mnesia_loop(Parent, {Match, Cont}) ->
149    Parent ! {self(), Match},
150    get_mnesia_loop(Parent, mnesia:select(Cont)).
151
152get_port_list() ->
153    ExtraItems = [monitors,monitored_by,parallelism,locking,queue_size,memory],
154    [begin
155	 [{port_id,P}|erlang:port_info(P)] ++
156             port_info(P,ExtraItems) ++
157             inet_port_extra(erlang:port_info(P, name), P)
158     end || P <- erlang:ports()].
159
160port_info(P,[Item|Items]) ->
161    case erlang:port_info(P,Item) of
162        undefined -> port_info(P,Items);
163        Value -> [Value|port_info(P,Items)]
164    end;
165port_info(_,[]) ->
166    [].
167
168inet_port_extra({_,Type},Port) when Type =:= "udp_inet";
169                                    Type =:= "tcp_inet";
170                                    Type =:= "sctp_inet" ->
171    Data =
172        case inet:getstat(Port) of
173            {ok, Stats} -> [{statistics, Stats}];
174            _ -> []
175        end ++
176        case inet:peername(Port) of
177            {ok, {RAddr,RPort}} when is_tuple(RAddr), is_integer(RPort) ->
178                [{remote_address,RAddr},{remote_port,RPort}];
179            {ok, RAddr} ->
180                [{remote_address,RAddr}];
181            {error, _} ->  []
182        end ++
183        case inet:sockname(Port) of
184            {ok, {LAddr,LPort}} when is_tuple(LAddr), is_integer(LPort) ->
185                [{local_address,LAddr},{local_port,LPort}];
186            {ok, LAddr} ->
187                [{local_address,LAddr}];
188            {error, _} -> []
189        end ++
190        case inet:getopts(Port,
191                          [active, broadcast, buffer, bind_to_device,
192                           delay_send, deliver, dontroute, exit_on_close,
193                           header, high_msgq_watermark, high_watermark,
194                           ipv6_v6only, keepalive, linger, low_msgq_watermark,
195                           low_watermark, mode, netns, nodelay, packet,
196                           packet_size, priority, read_packets, recbuf,
197                           reuseaddr, send_timeout, send_timeout_close,
198                           show_econnreset, sndbuf, tos, tclass]) of
199            {ok, Opts} -> [{options, Opts}];
200            {error, _} -> []
201        end,
202    [{inet,Data}];
203inet_port_extra(_,_) ->
204    [].
205
206get_table_list(ets, Opts) ->
207    HideUnread = proplists:get_value(unread_hidden, Opts, true),
208    HideSys = proplists:get_value(sys_hidden, Opts, true),
209    Info = fun(Id, Acc) ->
210		   try
211		       TabId = case ets:info(Id, named_table) of
212				   true -> ignore;
213				   false -> Id
214			       end,
215		       Name = ets:info(Id, name),
216		       Protection = ets:info(Id, protection),
217		       ignore(HideUnread andalso Protection == private, unreadable),
218		       Owner = ets:info(Id, owner),
219		       RegName = case catch process_info(Owner, registered_name) of
220				     [] -> ignore;
221				     {registered_name, ProcName} -> ProcName
222				 end,
223		       ignore(HideSys andalso ordsets:is_element(RegName, sys_processes()), system_tab),
224		       ignore(HideSys andalso ordsets:is_element(Name, sys_tables()), system_tab),
225		       ignore((RegName == mnesia_monitor)
226			      andalso Name /= schema
227			      andalso is_atom((catch mnesia:table_info(Name, where_to_read))), mnesia_tab),
228		       Memory = ets:info(Id, memory) * erlang:system_info(wordsize),
229		       Tab = [{name,Name},
230			      {id,TabId},
231			      {protection,Protection},
232			      {owner,Owner},
233			      {size,ets:info(Id, size)},
234			      {reg_name,RegName},
235			      {type,ets:info(Id, type)},
236			      {keypos,ets:info(Id, keypos)},
237			      {heir,ets:info(Id, heir)},
238			      {memory,Memory},
239			      {compressed,ets:info(Id, compressed)},
240			      {fixed,ets:info(Id, fixed)}
241			     ],
242		       [Tab|Acc]
243		   catch _:_What ->
244			   %% io:format("Skipped ~p: ~p ~n",[Id, _What]),
245			   Acc
246		   end
247	   end,
248    lists:foldl(Info, [], ets:all());
249
250get_table_list(mnesia, Opts) ->
251    HideSys = proplists:get_value(sys_hidden, Opts, true),
252    Owner = ets:info(schema, owner),
253    Owner /= undefined orelse
254	throw({error, "Mnesia is not running on: " ++ atom_to_list(node())}),
255    {registered_name, RegName} = process_info(Owner, registered_name),
256    Info = fun(Id, Acc) ->
257		   try
258		       Name = Id,
259		       ignore(HideSys andalso ordsets:is_element(Name, mnesia_tables()), system_tab),
260		       ignore(Name =:= schema, mnesia_tab),
261		       Storage = mnesia:table_info(Id, storage_type),
262		       Tab0 = [{name,Name},
263			       {owner,Owner},
264			       {size,mnesia:table_info(Id, size)},
265			       {reg_name,RegName},
266			       {type,mnesia:table_info(Id, type)},
267			       {keypos,2},
268			       {memory,mnesia:table_info(Id, memory) * erlang:system_info(wordsize)},
269			       {storage,Storage},
270			       {index,mnesia:table_info(Id, index)}
271			      ],
272		       Tab = if Storage == disc_only_copies ->
273				     [{fixed, dets:info(Id, safe_fixed)}|Tab0];
274				(Storage == ram_copies) orelse
275				(Storage == disc_copies) ->
276				     [{fixed, ets:info(Id, fixed)},
277				      {compressed, ets:info(Id, compressed)}|Tab0];
278				true -> Tab0
279			     end,
280		       [Tab|Acc]
281		   catch _:_What ->
282			   %% io:format("Skipped ~p: ~p ~p ~n",[Id, _What, Stacktrace]),
283			   Acc
284		   end
285	   end,
286    lists:foldl(Info, [], mnesia:system_info(tables)).
287
288fetch_stats(Parent, Time) ->
289    process_flag(trap_exit, true),
290    fetch_stats_loop(Parent, Time).
291
292fetch_stats_loop(Parent, Time) ->
293    erlang:system_flag(scheduler_wall_time, true),
294    receive
295	_Msg ->
296	    erlang:system_flag(scheduler_wall_time, false),
297	    ok
298    after Time ->
299	    _M = Parent ! {stats, 1,
300			   erlang:statistics(scheduler_wall_time),
301			   erlang:statistics(io),
302			   try erlang:memory() catch _:_ -> [] end},
303	    fetch_stats_loop(Parent, Time)
304    end.
305
306%%
307%% Chunk sending process info to etop/observer
308%%
309procs_info(Collector) ->
310    All = processes(),
311    Send = fun Send (Pids) ->
312                   try lists:split(10000, Pids) of
313                       {First, Rest} ->
314                           Collector ! {procs_info, self(), etop_collect(First, [])},
315                           Send(Rest)
316                   catch _:_ ->
317                           Collector ! {procs_info, self(), etop_collect(Pids, [])}
318                   end
319           end,
320    Send(All).
321
322%%
323%% etop backend
324%%
325etop_collect(Collector) ->
326    %% If this is the first time and the scheduler_wall_time flag is
327    %% false, SchedulerWallTime will be 'undefined' (and show 0 cpu
328    %% utilization in etop). Next time the flag will be true and then
329    %% there will be a measurement.
330    SchedulerWallTime = erlang:statistics(scheduler_wall_time),
331    ProcInfo = etop_collect(processes(), []),
332
333    Collector ! {self(),#etop_info{now = erlang:timestamp(),
334				   n_procs = length(ProcInfo),
335				   run_queue = erlang:statistics(run_queue),
336				   runtime = SchedulerWallTime,
337				   memi = etop_memi(),
338				   procinfo = ProcInfo
339				  }},
340
341    case SchedulerWallTime of
342	undefined ->
343            spawn(fun() -> flag_holder_proc(Collector) end),
344            ok;
345	_ ->
346	    ok
347    end.
348
349flag_holder_proc(Collector) ->
350    erlang:system_flag(scheduler_wall_time,true),
351    Ref = erlang:monitor(process,Collector),
352    receive
353	{'DOWN',Ref,_,_,_} ->
354	    erlang:system_flag(scheduler_wall_time,false),
355	    ok
356    end.
357
358etop_memi() ->
359    try
360	[{total, c:memory(total)},
361	 {processes, c:memory(processes)},
362	 {ets, c:memory(ets)},
363	 {atom, c:memory(atom)},
364	 {code, c:memory(code)},
365	 {binary, c:memory(binary)}]
366    catch
367	error:notsup ->
368	    undefined
369    end.
370
371etop_collect([P|Ps], Acc) when P =:= self() ->
372    etop_collect(Ps, Acc);
373etop_collect([P|Ps], Acc) ->
374    Fs = [registered_name,initial_call,memory,reductions,current_function,message_queue_len],
375    case process_info(P, Fs) of
376	undefined ->
377	    etop_collect(Ps, Acc);
378	[{registered_name,Reg},{initial_call,Initial},{memory,Mem},
379	 {reductions,Reds},{current_function,Current},{message_queue_len,Qlen}] ->
380	    Name = case Reg of
381		       [] -> initial_call(Initial, P);
382		       _ -> Reg
383		   end,
384	    Info = #etop_proc_info{pid=P,mem=Mem,reds=Reds,name=Name,
385				   cf=Current,mq=Qlen},
386	    etop_collect(Ps, [Info|Acc])
387    end;
388etop_collect([], Acc) -> Acc.
389
390initial_call({proc_lib, init_p, _}, Pid) ->
391    proc_lib:translate_initial_call(Pid);
392initial_call(Initial, _Pid) ->
393    Initial.
394
395%%
396%% ttb backend
397%%
398ttb_init_node(MetaFile_0,PI,Traci) ->
399    if
400	is_list(MetaFile_0);
401	is_atom(MetaFile_0) ->
402	    {ok, Cwd} = file:get_cwd(),
403	    MetaFile = filename:join(Cwd, MetaFile_0),
404	    file:delete(MetaFile);
405	true -> 				% {local,_,_}
406	    MetaFile = MetaFile_0
407    end,
408    case proplists:get_value(resume, Traci) of
409        {true, _} -> (autostart_module()):write_config(Traci);
410        _    -> ok
411    end,
412    Self = self(),
413    MetaPid = spawn(fun() -> ttb_meta_tracer(MetaFile,PI,Self,Traci) end),
414    receive {MetaPid,started} -> ok end,
415    MetaPid ! {metadata,Traci},
416    case PI of
417	true ->
418	    MetaPid ! {metadata,pnames()},
419            ok;
420	false ->
421	    ok
422    end,
423    {ok,MetaFile,MetaPid}.
424
425ttb_write_trace_info(MetaPid,Key,What) ->
426    MetaPid ! {metadata,Key,What},
427    ok.
428
429ttb_meta_tracer(MetaFile,PI,Parent,SessionData) ->
430    erlang:monitor(process, proplists:get_value(ttb_control, SessionData)),
431    case PI of
432	true ->
433	    ReturnMS = [{'_',[],[{return_trace}]}],
434	    erlang:trace_pattern({erlang,spawn,3},ReturnMS,[meta]),
435	    erlang:trace_pattern({erlang,spawn_link,3},ReturnMS,[meta]),
436	    erlang:trace_pattern({erlang,spawn_opt,4},ReturnMS,[meta]),
437	    erlang:trace_pattern({erts_internal,spawn_init,1},[],[meta]),
438	    erlang:trace_pattern({erts_internal,dist_spawn_init,1},[],[meta]),
439	    erlang:trace_pattern({erlang,register,2},[],[meta]),
440	    erlang:trace_pattern({global,register_name,2},[],[meta]),
441            ok;
442	false ->
443	    ok
444    end,
445    Parent ! {self(),started},
446    case proplists:get_value(overload_check, SessionData) of
447        {Ms, M, F} ->
448            catch M:F(init),
449            erlang:send_after(Ms, self(), overload_check),
450            ok;
451        _ ->
452            ok
453    end,
454    ttb_meta_tracer_loop(MetaFile,PI,dict:new(),SessionData).
455
456ttb_meta_tracer_loop(MetaFile,PI,Acc,State) ->
457    receive
458	{trace_ts,_,call,{erlang,register,[Name,Pid]},_} ->
459	    ok = ttb_store_meta({pid,{Pid,Name}},MetaFile),
460	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
461	{trace_ts,_,call,{global,register_name,[Name,Pid]},_} ->
462	    ok = ttb_store_meta({pid,{Pid,{global,Name}}},MetaFile),
463	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
464	{trace_ts,CallingPid,call,{erlang,spawn_opt,[M,F,Args,_]},_} ->
465	    MFA = {M,F,length(Args)},
466	    NewAcc = dict:update(CallingPid,
467				 fun(Old) -> [MFA|Old] end, [MFA],
468				 Acc),
469	    ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State);
470	{trace_ts,CallingPid,return_from,{erlang,spawn_opt,_Arity},Ret,_} ->
471	    case Ret of
472		{NewPid,_Mref} when is_pid(NewPid) -> ok;
473		NewPid when is_pid(NewPid) -> ok
474	    end,
475	    NewAcc =
476		dict:update(CallingPid,
477			    fun([H|T]) ->
478				    ok = ttb_store_meta({pid,{NewPid,H}},MetaFile),
479				    T
480			    end,
481			    Acc),
482	    ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State);
483	{trace_ts,CallingPid,call,{erlang,Spawn,[M,F,Args]},_}
484	when Spawn==spawn;Spawn==spawn_link ->
485	    MFA = {M,F,length(Args)},
486	    NewAcc = dict:update(CallingPid,
487				 fun(Old) -> [MFA|Old] end, [MFA],
488				 Acc),
489	    ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State);
490
491	{trace_ts,CallingPid,return_from,{erlang,Spawn,_Arity},NewPid,_}
492	when Spawn==spawn;Spawn==spawn_link ->
493	    NewAcc =
494		dict:update(CallingPid,
495			    fun([H|T]) ->
496				    ok = ttb_store_meta({pid,{NewPid,H}},MetaFile),
497				    T
498			    end,
499			    Acc),
500	    ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State);
501
502	{trace_ts,CallingPid,call,{erts_internal,spawn_init,[{M,F,Args}]},_} ->
503            %% Local spawn_request()...
504            ok = ttb_store_meta({pid,{CallingPid,{M,F,length(Args)}}},MetaFile),
505	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
506
507	{trace_ts,CallingPid,call,{erts_internal, dist_spawn_init, [MFnoA]},_} ->
508            %% Distributed spawn_request()...
509            ok = ttb_store_meta({pid,{CallingPid,MFnoA}},MetaFile),
510	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
511
512	{metadata,Data} when is_list(Data) ->
513	    ok = ttb_store_meta(Data,MetaFile),
514	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
515
516	{metadata,Key,Fun} when is_function(Fun) ->
517	    ok = ttb_store_meta([{Key,Fun()}],MetaFile),
518	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
519
520	{metadata,Key,What} ->
521	    ok = ttb_store_meta([{Key,What}],MetaFile),
522	    ttb_meta_tracer_loop(MetaFile,PI,Acc,State);
523        overload_check ->
524            {Ms, M, F} = proplists:get_value(overload_check, State),
525            case catch M:F(check) of
526                true ->
527                    erlang:trace(all, false, [all]),
528                    ControlPid = proplists:get_value(ttb_control, State),
529                    ControlPid ! {node_overloaded, node()},
530                    catch M:F(stop),
531                    ttb_meta_tracer_loop(MetaFile,PI,Acc,lists:keydelete(overload_check, 1, State));
532                _ ->
533                    erlang:send_after(Ms, self(), overload_check),
534                    ttb_meta_tracer_loop(MetaFile,PI,Acc, State)
535            end;
536     {'DOWN', _, _, _, _} ->
537            _ = stop_seq_trace(),
538            self() ! stop,
539            ttb_meta_tracer_loop(MetaFile,PI,Acc, State);
540     stop when PI=:=true ->
541            try_stop_resume(State),
542            try_stop_overload_check(State),
543            erlang:trace_pattern({erlang,spawn,3},false,[meta]),
544	    erlang:trace_pattern({erlang,spawn_link,3},false,[meta]),
545	    erlang:trace_pattern({erlang,spawn_opt,4},false,[meta]),
546	    erlang:trace_pattern({erts_internal,spawn_init,1},false,[meta]),
547	    erlang:trace_pattern({erts_internal,dist_spawn_init,1},false,[meta]),
548	    erlang:trace_pattern({erlang,register,2},false,[meta]),
549	    erlang:trace_pattern({global,register_name,2},false,[meta]);
550	stop ->
551            try_stop_resume(State),
552            try_stop_overload_check(State)
553    end.
554
555try_stop_overload_check(State) ->
556    case proplists:get_value(overload, State) of
557        undefined -> ok;
558        {_, M, F} -> catch M:F(stop)
559    end.
560
561pnames() ->
562    Processes = processes(),
563    Globals = lists:map(fun(G) -> {global:whereis_name(G),G} end,
564			global:registered_names()),
565    lists:flatten(lists:foldl(fun(Pid,Acc) -> [pinfo(Pid,Globals)|Acc] end,
566			      [], Processes)).
567
568pinfo(P,Globals) ->
569    case process_info(P,registered_name) of
570	[] ->
571	    case lists:keysearch(P,1,Globals) of
572		{value,{P,G}} -> {pid,{P,{global,G}}};
573		false ->
574		    case process_info(P,initial_call) of
575			{_,I} -> {pid,{P,I}};
576			undefined -> [] % the process has terminated
577		    end
578	    end;
579	{_,R} -> {pid,{P,R}};
580	undefined -> [] % the process has terminated
581    end.
582
583autostart_module() ->
584    element(2, application:get_env(runtime_tools, ttb_autostart_module)).
585
586try_stop_resume(State) ->
587    case proplists:get_value(resume, State) of
588        true -> (autostart_module()):delete_config();
589        _    -> ok
590    end.
591
592ttb_resume_trace() ->
593    case (autostart_module()):read_config() of
594        {error, _} ->
595            ok;
596        {ok, Data} ->
597            Pid = proplists:get_value(ttb_control, Data),
598            {_, Timeout} = proplists:get_value(resume, Data),
599            case rpc:call(node(Pid), erlang, whereis, [ttb]) of
600                Pid ->
601                    Pid ! {noderesumed, node(), self()},
602                    wait_for_fetch_ready(Timeout);
603                _ ->
604                    ok
605            end,
606            (autostart_module()):delete_config(),
607            ok
608    end.
609
610wait_for_fetch_ready(Timeout) ->
611    receive
612        trace_resumed ->
613            ok
614    after Timeout ->
615            ok
616    end.
617
618ttb_store_meta(Data,{local,MetaFile,Port}) when is_list(Data) ->
619    ttb_send_to_port(Port,MetaFile,Data);
620ttb_store_meta(Data,MetaFile) when is_list(Data) ->
621    {ok,Fd} = file:open(MetaFile,[raw,append]),
622    ttb_write_binary(Fd,Data),
623    file:close(Fd);
624ttb_store_meta(Data,MetaFile) ->
625    ttb_store_meta([Data],MetaFile).
626
627ttb_write_binary(Fd,[H|T]) ->
628    ok = file:write(Fd,ttb_make_binary(H)),
629    ttb_write_binary(Fd,T);
630ttb_write_binary(_Fd,[]) ->
631    ok.
632
633ttb_send_to_port(Port,MetaFile,[H|T]) ->
634    B1 = ttb_make_binary(H),
635    B2 = term_to_binary({metadata,MetaFile,B1}),
636    erlang:port_command(Port,B2),
637    ttb_send_to_port(Port,MetaFile,T);
638ttb_send_to_port(_Port,_MetaFile,[]) ->
639    ok.
640
641ttb_make_binary(Term) ->
642    B = term_to_binary(Term),
643    SizeB = byte_size(B),
644    if SizeB > 255 ->
645	    %% size is bigger than 8 bits, must therefore add an extra
646	    %% size field
647	    SB = term_to_binary({'$size',SizeB}),
648	    <<(byte_size(SB)):8, SB/binary, B/binary>>;
649        true ->
650	    <<SizeB:8, B/binary>>
651    end.
652
653
654%% Stop ttb
655ttb_stop(MetaPid) ->
656    Delivered = erlang:trace_delivered(all),
657    receive
658	{trace_delivered,all,Delivered} -> ok
659    end,
660    Ref = erlang:monitor(process,MetaPid),
661    MetaPid ! stop,
662
663    %% Must wait for the process to terminate there
664    %% because dbg will be stopped when this function
665    %% returns, and then the Port (in {local,MetaFile,Port})
666    %% cannot be accessed any more.
667    receive {'DOWN', Ref, process, MetaPid, _Info} -> ok end,
668    stop_seq_trace().
669
670stop_seq_trace() ->
671    seq_trace:reset_trace(),
672    seq_trace:set_system_tracer(false).
673
674%% Fetch ttb logs from remote node
675ttb_fetch(MetaFile,{Port,Host}) ->
676    ttb_fetch(MetaFile,{Port,Host},undefined).
677ttb_fetch(MetaFile,{Port,Host},MasterEnc) ->
678    erlang:process_flag(priority,low),
679    Files = ttb_get_filenames(MetaFile),
680    {ok, Sock} = gen_tcp:connect(Host, Port, [binary, {packet, 2}]),
681    send_files({Sock,Host},Files,MasterEnc,file:native_name_encoding()),
682    ok = gen_tcp:close(Sock).
683
684
685send_files({Sock,Host},[File|Files],MasterEnc,MyEnc) ->
686    {ok,Fd} = file:open(File,[raw,read,binary]),
687    Basename = filename:basename(File),
688    {Code,FilenameBin} = encode_filename(Basename,MasterEnc,MyEnc),
689    ok = gen_tcp:send(Sock,<<Code,FilenameBin/binary>>),
690    send_chunks(Sock,Fd),
691    ok = file:delete(File),
692    send_files({Sock,Host},Files,MasterEnc,MyEnc);
693send_files({_Sock,_Host},[],_MasterEnc,_MyEnc) ->
694    done.
695
696encode_filename(Basename,undefined,MyEnc) ->
697    %% Compatible with old version of ttb.erl, but no longer crashing
698    %% for code points > 255.
699    {1,unicode:characters_to_binary(Basename,MyEnc,MyEnc)};
700encode_filename(Basename,MasterEnc,MyEnc) ->
701    case unicode:characters_to_binary(Basename,MyEnc,MasterEnc) of
702        Bin when is_binary(Bin) ->
703            %% Encoding succeeded
704            {2,Bin};
705        _ ->
706            %% Can't convert Basename from my encoding to the master
707            %% node's encoding. Doing my best and hoping that master
708            %% node can fix it...
709            {3,unicode:characters_to_binary(Basename,MyEnc,MyEnc)}
710    end.
711
712send_chunks(Sock,Fd) ->
713    case file:read(Fd,?CHUNKSIZE) of
714	{ok,Bin} ->
715	    ok = gen_tcp:send(Sock, <<0,Bin/binary>>),
716	    send_chunks(Sock,Fd);
717	eof ->
718	    ok;
719	{error,Reason} ->
720	    ok = gen_tcp:send(Sock, <<2,(term_to_binary(Reason))/binary>>)
721    end.
722
723ttb_get_filenames(MetaFile) ->
724    Dir = filename:dirname(MetaFile),
725    Root = filename:rootname(filename:basename(MetaFile)),
726    {ok,List} = file:list_dir(Dir),
727    match_filenames(Dir,Root,List,[]).
728
729match_filenames(Dir,MetaFile,[H|T],Files) ->
730    case lists:prefix(MetaFile,H) of
731	true -> match_filenames(Dir,MetaFile,T,[filename:join(Dir,H)|Files]);
732	false -> match_filenames(Dir,MetaFile,T,Files)
733    end;
734match_filenames(_Dir,_MetaFile,[],Files) ->
735    Files.
736
737
738%%%%%%%%%%%%%%%%%
739
740sys_tables() ->
741    [ac_tab,  asn1,
742     cdv_dump_index_table,  cdv_menu_table,  cdv_decode_heap_table,
743     cell_id,  cell_pos,  clist,
744     cover_internal_data_table,   cover_collected_remote_data_table, cover_binary_code_table,
745     code, code_names,  cookies,
746     corba_policy,  corba_policy_associations,
747     dets, dets_owners, dets_registry,
748     disk_log_names, disk_log_pids,
749     eprof,  erl_atom_cache, erl_epmd_nodes,
750     etop_accum_tab,  etop_tr,
751     ets_coverage_data,
752     file_io_servers,
753     gs_mapping, gs_names,  gstk_db,
754     gstk_grid_cellid, gstk_grid_cellpos, gstk_grid_id,
755     httpd,
756     id,
757     ign_req_index, ign_requests,
758     index,
759     inet_cache, inet_db, inet_hosts,
760     'InitialReferences',
761     int_db,
762     interpreter_includedirs_macros,
763     ir_WstringDef,
764     lmcounter,  locks,
765						%     mnesia_decision,
766     mnesia_gvar, mnesia_stats,
767						%     mnesia_transient_decision,
768     pg2_table,
769     pg,
770     queue,
771     schema,
772     shell_records,
773     snmp_agent_table, snmp_local_db2, snmp_mib_data, snmp_note_store, snmp_symbolic_ets,
774     tkFun, tkLink, tkPriv,
775     ttb, ttb_history_table,
776     udp_fds, udp_pids
777    ].
778
779sys_processes() ->
780    [auth, code_server, global_name_server, inet_db,
781     mnesia_recover, net_kernel, pg, timer_server, wxe_master].
782
783mnesia_tables() ->
784    [ir_AliasDef, ir_ArrayDef, ir_AttributeDef, ir_ConstantDef,
785     ir_Contained, ir_Container, ir_EnumDef, ir_ExceptionDef,
786     ir_IDLType, ir_IRObject, ir_InterfaceDef, ir_ModuleDef,
787     ir_ORB, ir_OperationDef, ir_PrimitiveDef, ir_Repository,
788     ir_SequenceDef, ir_StringDef, ir_StructDef, ir_TypedefDef,
789     ir_UnionDef, logTable, logTransferTable, mesh_meas,
790     mesh_type, mnesia_clist, orber_CosNaming,
791     orber_objkeys, user
792    ].
793
794ignore(true, Reason) -> throw(Reason);
795ignore(_,_ ) -> ok.
796