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