93
93
%% processes that are left.
95
95
%%----------------------------------------------------------------------
99
96
-module(appmon_info).
100
%% For CC that doesn't understand fnutts.
102
-export([start_link/3, start_link2/3, stop/0]).
104
-export([app_ctrl/2, app_ctrl/4,
107
set_opts/1, set_opts/2,
112
-import(lists, [foldr/3]).
115
97
-behaviour(gen_server).
116
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
117
-export([code_change/3]).
100
-export([start_link/3, app/4, pinfo/4, load/4, app_ctrl/4]).
102
%% For internal use (RPC call)
103
-export([start_link2/3]).
108
%% gen_server callbacks
109
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
110
terminate/2, code_change/3]).
120
113
%%----------------------------------------------------------------------
161
154
start_link2(Starter, Client, Opts) ->
162
155
Name = {local, ?MODULE},
163
156
Args = {Starter, Opts, Client},
164
%% Check for existence because gen_server gives error when trie
166
case whereis(?MODULE) of
157
case gen_server:start(Name, ?MODULE, Args, []) of
160
{error, {already_started, Pid}} ->
168
161
register_client(Pid, Client),
171
case catch gen_server:start(Name, ?MODULE, Args, []) of
172
{ok, Pid} when pid(Pid) -> {ok, Pid};
173
{error, {already_started, Pid}} when pid(Pid) ->
174
register_client(Pid, Client),
187
168
%% Monitors which applications exist on a node
189
app_ctrl(OnOff, Opts) ->
190
app_ctrl(?MODULE, nil, OnOff, Opts).
191
170
app_ctrl(Serv, Aux, OnOff, Opts) ->
192
171
gen_server:cast(Serv, {self(), app_ctrl, Aux, OnOff, Opts}).
207
184
%% Monitors one application given by name (this ends up in a
210
app(AppName, OnOff, Opts) ->
211
app(?MODULE, AppName, OnOff, Opts).
212
187
app(Serv, AppName, OnOff, Opts) ->
213
188
gen_server:cast(Serv, {self(), app, AppName, OnOff, Opts}).
217
%% Set global options
220
set_opts(?MODULE, Opt).
221
set_opts(Serv, Opt) ->
222
gen_server:cast(Serv, {self(), set_option, Opt}).
226
192
%% Process or Port info
228
pinfo(Pid, OnOff, Opt) ->
229
pinfo(?MODULE, Pid, OnOff, Opt).
230
194
pinfo(Serv, Pid, OnOff, Opt) ->
231
195
gen_server:cast(Serv, {self(), pinfo, Pid, OnOff, Opt}).
254
218
%%----------------------------------------------------------------------
256
220
init({Starter, Opts, Pid}) ->
257
%%io:format("init opts: ~p, pid: ~p, ~n", [Opts, Pid]),
259
222
process_flag(trap_exit, true),
260
223
WorkStore = ets:new(workstore, [set, public]),
261
{ok, #state{starter=Starter, opts=Opts, work=WorkStore, clients=[Pid]}}.
224
{ok, #state{starter=Starter, opts=Opts, work=WorkStore,
263
terminate(Reason, State) ->
264
tell("Terminating ~p ~p~n", [?MODULE, node()], 5, State#state.opts),
227
terminate(_Reason, State) ->
265
228
ets:delete(State#state.work),
268
code_change(OldVsn, State, Extra) ->
231
code_change(_OldVsn, State, _Extra) ->
276
239
%%----------------------------------------------------------------------
278
handle_call(stop, From, State) ->
279
{reply, stop, normal, State};
280
handle_call({register_client, Pid}, From, State) ->
281
%% io:format("ns add client: Pid: ~p, List: ~p~n",
282
%% [Pid, State#state.clients]),
241
handle_call({register_client, Pid}, _From, State) ->
283
242
NewState = case lists:member(Pid, State#state.clients) of
285
244
_ -> State#state{clients=[Pid | State#state.clients]}
287
246
{reply, ok, NewState};
288
handle_call(Other, From, State) ->
289
tell("~p got unknown call: ~p~n", [?MODULE, Other], 1, State#state.opts),
247
handle_call(_Other, _From, State) ->
290
248
{reply, ok, State}.
292
250
%%----------------------------------------------------------------------
296
254
%%----------------------------------------------------------------------
256
%% Cmd = app_ctrl | load | app | pinfo
298
257
handle_cast({From, Cmd, Aux, OnOff, Opts}, State) ->
299
258
NewState = update_worklist(Cmd, Aux, From, OnOff, Opts, State),
300
259
{noreply, NewState};
301
260
handle_cast(status, State) ->
302
261
print_state(State),
303
262
{noreply, State};
304
handle_cast({From, set_option, Opt}, State) ->
305
NewOpts = ins_opt(Opt, State#state.opts),
306
{noreply, State#state{opts=NewOpts}};
307
handle_cast(Other, State) ->
308
tell("~p got unknown cast: ~p~n", [?MODULE, Other], 1, State#state.opts),
263
handle_cast(_Other, State) ->
309
264
{noreply, State}.
327
280
{stop, Reason, State};
329
282
Work = State#state.work,
330
del_work(ets:match(Work, {{'$1', '$2', Pid}, '_', '_', '_'}), Pid, Work),
283
del_work(ets:match(Work, {{'$1','$2',Pid}, '_', '_', '_'}),
331
285
case lists:delete(Pid, State#state.clients) of
332
286
[] -> case get_opt(stay_resident, State#state.opts) of
333
287
true -> {noreply, State#state{clients=[]}};
336
290
NewClients -> {noreply, State#state{clients=NewClients}}
339
handle_info(Other, State) ->
340
%% io:format("gad: ~p~n", [Other]),
341
tell("~p got unknown info: ~p~n", [?MODULE, Other], 1, State#state.opts),
293
handle_info(_Other, State) ->
342
294
{noreply, State}.
349
301
%%----------------------------------------------------------------------
351
303
do_work(Key, State) ->
352
%%io:format("Do work ~p~n", [Key]),
353
304
WorkStore = State#state.work,
354
{Cmd, Aux, From, OldRef, Old, Opts} = retreive(WorkStore, Key),
355
%%tell("Work: ~p, ~p, ~p~n", [Cmd, Aux, From], 5, State#state.opts),
356
{ok, Result} = do_work2(Cmd, Aux, From, Old, Opts, State),
305
{Cmd, Aux, From, _OldRef, Old, Opts} = retrieve(WorkStore, Key),
306
{ok, Result} = do_work2(Cmd, Aux, From, Old, Opts),
357
307
if Result==Old -> ok;
359
%%% tell("Result delivered:~n", [], 4, State#state.opts),
360
%%% tell(" cmd:~p, aux:~p, ", [Cmd, Aux], 4, State#state.opts),
361
%%% tell("from:~p, opts:~p, res:~p~n",
362
%%% [From, Opts, Result], 4, State#state.opts),
363
309
From ! {delivery, self(), Cmd, Aux, Result}
365
311
case get_opt(timeout, Opts) of
379
325
%% Maintenance Note: Add a clause here for each new task.
381
do_work2(load, Aux, From, Old, Opts, State) ->
382
calc_load(load, Aux, From, Old, Opts);
383
do_work2(app_ctrl, Aux, From, Old, Opts, State) ->
384
calc_app_on_node(app_ctr, Aux, From, Old, Opts);
385
do_work2(app, Aux, From, Old, Opts, State) ->
386
R = calc_app_tree(app, Aux, From, Old, Opts),
387
%%io:format("Result: ~p~n", [R]),
389
do_work2(pinfo, Aux, From, Old, Opts, State) ->
390
calc_pinfo(pinfo, Aux, From, Old, Opts);
391
do_work2(Cmd, Aux, From, Old, Opts, State) ->
392
tell("Do work: ~p~n", [Cmd], 5, State#state.opts),
327
do_work2(load, _Aux, _From, Old, Opts) ->
328
calc_load(Old, Opts);
329
do_work2(app_ctrl, _Aux, _From, _Old, _Opts) ->
331
do_work2(app, Aux, _From, _Old, Opts) ->
332
calc_app_tree(Aux, Opts);
333
do_work2(pinfo, Aux, _From, _Old, _Opts) ->
334
calc_pinfo(pinfo, Aux);
335
do_work2(Cmd, Aux, _From, _Old, _Opts) ->
396
retreive(Tab, Key) ->
397
%%io:format("Retrieve: ~p~n", [Key]),
339
retrieve(Tab, Key) ->
398
340
case ets:lookup(Tab, Key) of
399
341
[{{Cmd, Aux, From}, Ref, Old, Opts}] ->
400
342
{Cmd, Aux, From, Ref, Old, Opts};
416
358
update_worklist(Cmd, Aux, From, true, Opts, State) ->
417
359
add_task(Cmd, Aux, From, Opts, State),
419
update_worklist(Cmd, Aux, From, Other, Opts, State) ->
361
update_worklist(Cmd, Aux, From, _Other, _Opts, State) ->
420
362
del_task(Cmd, Aux, From, State#state.work),
431
373
store(WorkStore, Key, nil, nil, ins_opts(Opts, OldOpts)),
432
374
catch do_work(Key, State),
434
%%self() ! ?MK_DOIT(Key).
437
%% {ok, Ref} = timer:send_after(get_opt(timeout, Opts),
438
%% {do_it, Cmd, Aux, From}),
439
%%ets:insert(WorkStore, {{Cmd, Aux, From}, Ref, Opts}).
441
377
%% Delete a list of tasks belonging to a pid
442
378
del_work([[Cmd, Aux] | Ws], Pid, Work) ->
443
%% io:format("Deleting ~p ~p ~p~n", [Cmd, Aux, Pid]),
444
379
del_task(Cmd, Aux, Pid, Work),
445
380
del_work(Ws, Pid, Work);
446
del_work([], Pid, Work) -> ok.
381
del_work([], _Pid, _Work) -> ok.
448
383
%% Must return old options or empty list
449
384
del_task(Cmd, Aux, From, WorkStore) ->
450
385
del_task(?MK_KEY(Cmd, Aux, From, []), WorkStore).
451
386
del_task(Key, WorkStore) ->
452
OldStuff = retreive(WorkStore, Key),
387
OldStuff = retrieve(WorkStore, Key),
453
388
ets:delete(WorkStore, Key),
455
{Cmd, Aux, From, Ref, Old, Opts} ->
390
{_Cmd, _Aux, _From, Ref, _Old, Opts} ->
457
392
timer:cancel(Ref),
541
473
case get_next(DB) of
542
474
{{value, V}, DB2} ->
543
475
do_find_proc2(V, Mode, DB2, GL, Avoid);
544
%% DB3 = add_proc(V, Mode, DB2, GL, Avoid),
545
%% do_find_proc(Mode, DB3, GL, Avoid);
550
do_find_proc2(X, Mode, DB, GL, Avoid) when port(X) ->
551
DB2 = case is_proc(DB, X) of
552
[] -> add_port(DB, X);
555
do_find_proc(Mode, DB2, GL, Avoid);
480
do_find_proc2(X, Mode, DB, GL, Avoid) when is_port(X) ->
481
%% There used to be a broken attempt here to handle ports,
482
%% but the rest of appmon can't handle ports, so now we
483
%% explicitly ignore ports.
484
do_find_proc(Mode, DB, GL, Avoid);
556
485
do_find_proc2(X, Mode, DB, GL, Avoid) ->
557
486
Xpid = get_pid(X),
558
%% io:format("Adding ~p (~p)~n", [XX, X]),
559
487
DB2 = case is_proc(DB, Xpid) of
561
489
add_proc(DB, Xpid),
562
%% ets:insert(P, {Xpid}),
563
490
C1 = find_children(X, Mode),
564
%% ?ifthen(Mode==sup, io:format("find_children ret ~p~n",
566
491
add_children(C1, Xpid, DB, GL, Avoid, Mode);
580
505
%% better be a supervisor, we are smoked otherwise
581
506
supervisor:which_children(X);
582
507
find_children(X, link) when pid(X), node(X) /= node() ->
583
%% io:format("Proc on other node: ~p~n", [X]),
585
509
find_children(X, link) when pid(X) ->
586
510
case process_info(X, links) of
587
511
{links, Links} ->
589
512
lists:reverse(Links); % OTP-4082
592
515
find_children({master, X}, sup) ->
593
%% io:format("Adding master ~p~n", [X]),
594
516
case application_master:get_child(X) of
595
{Pid, Name} when pid(Pid) -> [Pid];
517
{Pid, _Name} when pid(Pid) -> [Pid];
596
518
Pid when pid(Pid) -> [Pid]
598
find_children({_, X, worker, _}, sup) -> [];
520
find_children({_, _X, worker, _}, sup) -> [];
599
521
find_children({_, X, supervisor, _}, sup) ->
600
522
lists:filter(fun(Thing) ->
601
523
Pid = get_pid(Thing),
609
531
%% Add links to primary (L1) or secondary (L2) sets and return an
610
532
%% updated queue. A link is considered secondary if its endpoint is in
611
533
%% the queue of un-visited but known processes.
612
add_children(CList, Paren, DB, GL, Avoid, sup) ->
616
add_prim(C, Paren, DB2);
534
add_children(CList, Paren, DB, _GL, _Avoid, sup) ->
535
lists:foldr(fun(C, DB2) ->
538
add_prim(C, Paren, DB2);
620
add_children(CList, Paren, DB, GL, Avoid, Mode) ->
622
maybe_add_child(C, Paren, DB2, GL, Avoid)
542
add_children(CList, Paren, DB, GL, Avoid, _Mode) ->
543
lists:foldr(fun(C, DB2) ->
544
maybe_add_child(C, Paren, DB2, GL, Avoid)
625
547
%% Check if the child is already in P
626
548
maybe_add_child(C, Paren, DB, GL, Avoid) ->
642
563
%% Check if child is on the avoid list
643
564
maybe_add_child_avoid(C, Paren, DB, GL, Avoid) ->
644
%% io:format("Try avoid: ~p~n", [C]),
645
565
case lists:member(C, Avoid) of
648
maybe_add_child_port(C, Paren, DB, GL, Avoid)
568
maybe_add_child_port(C, Paren, DB, GL)
651
571
%% Check if it is a port, then it is added
652
maybe_add_child_port(C, Paren, DB, GL, Avoid) ->
572
maybe_add_child_port(C, Paren, DB, GL) ->
654
574
add_prim(C, Paren, DB);
656
maybe_add_child_sasl(C, Paren, DB, GL, Avoid)
576
maybe_add_child_sasl(C, Paren, DB, GL)
659
579
%% Use SASL stuff if present
660
maybe_add_child_sasl(C, Paren, DB, GL, Avoid) ->
580
maybe_add_child_sasl(C, Paren, DB, GL) ->
661
581
case check_sasl_ancestor(Paren, C) of
663
%% io:format("Try: Was SASL primary ~p~n", [C]),
664
583
add_prim(C, Paren, DB);
665
584
no -> % Secondary
666
%% io:format("Try: was SASL sec ~p~n", [C]),
667
585
add_sec(C, Paren, DB);
669
maybe_add_child_gl(C, Paren, DB, GL, Avoid)
587
maybe_add_child_gl(C, Paren, DB, GL)
672
590
%% Check group leader
673
maybe_add_child_gl(C, Paren, DB, GL, Avoid) ->
674
%% io:format("Try gl: ~p, gl: ~p, cgl: ~p~n", [C, GL, groupl(get_pid(C))]),
591
maybe_add_child_gl(C, Paren, DB, GL) ->
675
592
case cmp_groupl(GL, groupl(C)) of
676
true -> maybe_add_child_sec(C, Paren, DB, GL, Avoid);
593
true -> maybe_add_child_sec(C, Paren, DB);
680
597
%% Check if the link should be a secondary one. Note that this part is
681
598
%% pretty much a guess.
682
maybe_add_child_sec(C, Paren, DB, GL, Avoid) ->
683
%% io:format("Try sec: ~p~n", [C]),
599
maybe_add_child_sec(C, Paren, DB) ->
684
600
case is_in_queue(DB, C) of
685
601
true -> % Yes, secondary
686
602
add_sec(C, Paren, DB);
733
647
add_sec(C, Paren, DB) ->
734
648
?add_link(C, Paren, DB#db.links2),
737
case ets:lookup(DB#db.p, P) of
741
is_in_queue(DB, P) -> % Should really be in queue.erl
743
case lists:member(P, L1) of
745
false -> lists:member(P, L2)
748
%add_children([C | Cs], Paren, Q, P, L1, L2, GL, Avoid) ->
749
% case lists:member(get_pid(C), Avoid) of
751
% case queue_member(C, Q) of
753
% case add_link(P, L1, Paren, GL, get_pid(C)) of
755
% [C | add_children(Cs, Paren, Q, P, L1, L2,
758
% add_children(Cs, Paren, Q, P, L1, L2, GL, Avoid)
761
% add_link(P, L2, Paren, GL, get_pid(C)),
762
% add_children(Cs, Paren, Q, P, L1, L2, GL, Avoid)
765
% add_children(Cs, Paren, Q, P, L1, L2, GL, Avoid)
767
%add_children([], Paren, Q, P, L1, L2, GL, Avoid) -> Q.
771
%% Add a link if . Do not add link if group leaders differ.
773
%% case ets:lookup(L, Child) of
775
%% case ets:lookup(P, Child) of
777
%% case cmp_groupl(GL, groupl(Child)) of
779
%% ets:insert(L, {Paren, Child}), added;
651
is_proc(#db{p=Tab}, P) ->
654
is_in_queue(#db{q={L1,L2}}, P) -> % Should really be in queue.erl
655
lists:member(P, L1) orelse lists:member(P, L2).
788
657
%% Group leader handling. No processes or Links to processes must be
789
658
%% added when group leaders differ. Note that catch all is needed
1021
880
%%**********************************************************************
1022
881
%%----------------------------------------------------------------------
1024
calc_pinfo(pinfo, Pid, From, Old, Opts) when pid(Pid) ->
883
calc_pinfo(pinfo, Pid) when pid(Pid) ->
1025
884
Info = process_info(Pid),
1026
{ok, io_lib:format("Node: ~p, Process: ~p~n~p~n~n", [node(), Pid, Info])};
1027
calc_pinfo(pinfo, Pid, From, Old, Opts) when port(Pid) ->
885
{ok, io_lib:format("Node: ~p, Process: ~p~n~p~n~n",
886
[node(), Pid, Info])};
887
calc_pinfo(pinfo, Pid) when port(Pid) ->
1028
888
Info = lists:map(fun(Key) ->erlang:port_info(Pid, Key) end,
1029
889
[id, name, connected, links, input, output]),
1031
891
{ok, io_lib:format("Node: ~p, Port: ~p~n~p~n~n",
1032
892
[node(), element(2, erlang:port_info(Pid, id)),
1034
calc_pinfo(pinfo, Pid, From, Old, Opts) ->
894
calc_pinfo(pinfo, _Pid) ->
1064
925
print_work([]) -> ok.
1070
928
%%----------------------------------------------------------------------
1072
930
%% Option handling
1074
932
%%----------------------------------------------------------------------
934
%% The only options ever set by a user is info_type, timeout,
935
%% load_scale and load_method.
1076
936
get_opt(Name, Opts) ->
1077
937
case lists:keysearch(Name, 1, Opts) of
1078
938
{value, Val} -> element(2, Val);
1079
_ -> case lists:member(Name, Opts) of
939
false -> default(Name)
1085
942
%% not all options have default values
943
default(info_type) -> link;
944
default(load_average) -> true;
1086
945
default(load_method) -> time;
1087
946
default(load_scale) -> prog;
1088
default(load_average) -> true;
1089
default(timeout) -> 2000;
1090
default(verbosity) -> 0;
1091
default(info_type) -> link;
1092
947
default(stay_resident) -> false;
1093
default({avoid, _}) -> false;
1094
default(method) -> poll.
948
default(timeout) -> 2000.
1096
950
ins_opts([Opt | Opts], Opts2) ->
1097
951
ins_opts(Opts, ins_opt(Opt, Opts2));
1100
954
ins_opt({Opt, Val}, [{Opt, _} | Os]) -> [{Opt, Val} | Os];
1101
955
ins_opt(Opt, [Opt2 | Os]) -> [Opt2 | ins_opt(Opt, Os)];
1102
956
ins_opt(Opt, []) -> [Opt].
1105
tell(F, A, Prio, Opts) ->
1106
P2 = get_opt(verbosity, Opts),
1107
if P2 > Prio -> io:format(F, A);