nickva commented on a change in pull request #761: Add a debugging utilities 
for listing processes
URL: https://github.com/apache/couchdb/pull/761#discussion_r134807095
 
 

 ##########
 File path: src/couch/src/couch_debug.erl
 ##########
 @@ -50,3 +233,299 @@ opened_files_contains(FileNameFragment) ->
     lists:filter(fun({_Port, _Pid, _Fd, Path}) ->
         string:str(Path, FileNameFragment) > 0
     end, couch_debug:opened_files()).
+
+process_name(Pid) ->
+    case process_info(Pid, registered_name) of
+        {registered_name, Name} ->
+            iolist_to_list(io_lib:format("~s[~p]", [Name, Pid]));
+        _ ->
+            {dictionary, Dict} = process_info(Pid, dictionary),
+            case proplists:get_value('$initial_call', Dict) of
+                undefined ->
+                    {initial_call, {M, F, A}} = process_info(Pid, 
initial_call),
+                    iolist_to_list(io_lib:format("~p:~p/~p[~p]", [M, F, A, 
Pid]));
+                {M, F, A} ->
+                    iolist_to_list(io_lib:format("~p:~p/~p[~p]", [M, F, A, 
Pid]))
+            end
+    end.
+
+iolist_to_list(List) ->
+    binary_to_list(iolist_to_binary(List)).
+
+link_tree(RootPid) ->
+    link_tree(RootPid, []).
+
+link_tree(RootPid, Info) ->
+    link_tree(RootPid, Info, fun(_, Props) -> Props end).
+
+link_tree(RootPid, Info, Fun) ->
+    {_, Result} = link_tree(
+        RootPid, [links | Info], gb_trees:empty(), 0, [RootPid], Fun),
+    Result.
+
+link_tree(RootPid, Info, Visited0, Pos, [Pid | Rest], Fun) ->
+    case gb_trees:lookup(Pid, Visited0) of
+        {value, Props} ->
+            {Visited0, [{Pos, {Pid, Fun(Pid, Props), []}}]};
+        none when RootPid =< Pid ->
+            Props = info(Pid, Info),
+            Visited1 = gb_trees:insert(Pid, Props, Visited0),
+            {links, Children} = lists:keyfind(links, 1, Props),
+            {Visited2, NewTree} = link_tree(
+                RootPid, Info, Visited1, Pos + 1, Children, Fun),
+            {Visited3, Result} = link_tree(
+                RootPid, Info, Visited2, Pos, Rest, Fun),
+            {Visited3, [{Pos, {Pid, Fun(Pid, Props), NewTree}}]  ++ Result};
+        none ->
+            Props = info(Pid, Info),
+            Visited1 = gb_trees:insert(Pid, Props, Visited0),
+            {Visited2, Result} = link_tree(
+                RootPid, Info, Visited1, Pos, Rest, Fun),
+            {Visited2, [{Pos, {Pid, Fun(Pid, Props), []}}] ++ Result}
+    end;
+link_tree(_RootPid, _Info, Visited, _Pos, [], _Fun) ->
+    {Visited, []}.
+
+
+info(Pid, Info) when is_pid(Pid) ->
+    ValidProps = [
+        backtrace,
+        binary,
+        catchlevel,
+        current_function,
+        current_location,
+        current_stacktrace,
+        dictionary,
+        error_handler,
+        garbage_collection,
+        garbage_collection_info,
+        group_leader,
+        heap_size,
+        initial_call,
+        links,
+        last_calls,
+        memory,
+        message_queue_len,
+        messages,
+        min_heap_size,
+        min_bin_vheap_size,
+        monitored_by,
+        monitors,
+        message_queue_data,
+        priority,
+        reductions,
+        registered_name,
+        sequential_trace_token,
+        stack_size,
+        status,
+        suspending,
+        total_heap_size,
+        trace,
+        trap_exit
+    ],
+    Validated = lists:filter(fun(P) -> lists:member(P, ValidProps) end, Info),
+    process_info(Pid, lists:usort(Validated));
+info(Port, Info) when is_port(Port) ->
+    ValidProps = [
+        registered_name,
+        id,
+        connected,
+        links,
+        name,
+        input,
+        output,
+        os_pid
+    ],
+    Validated = lists:filter(fun(P) -> lists:member(P, ValidProps) end, Info),
+    erlang:port_info(Port, lists:usort(Validated)).
+
+mapfold_tree([], Acc, _Fun) ->
+    {[], Acc};
+mapfold_tree([{Pos, {Key, Value0, SubTree0}} | Rest0], Acc0, Fun) ->
+    {Value1, Acc1} = Fun(Key, Value0, Pos, Acc0),
+    {SubTree1, Acc2} = mapfold_tree(SubTree0, Acc1, Fun),
+    {Rest1, Acc3} = mapfold_tree(Rest0, Acc2, Fun),
+    {[{Pos, {Key, Value1, SubTree1}} | Rest1], Acc3}.
+
+map_tree(Tree, Fun) ->
+    {Result, _} = mapfold_tree(Tree, nil, fun(Key, Value, Pos, Acc) ->
+        {Fun(Key, Value, Pos), Acc}
+    end),
+    Result.
+
+fold_tree(Tree, Acc, Fun) ->
+    {_, Result} = mapfold_tree(Tree, Acc, fun(Key, Value, Pos, AccIn) ->
+        {Value, Fun(Key, Value, Pos, AccIn)}
+    end),
+    Result.
+
+linked_processes_info(Pid, Info) ->
+    link_tree(Pid, Info, fun(P, Props) -> {process_name(P), Props} end).
+
+print_linked_processes(couch_index_server) ->
+    print_couch_index_server_processes();
+print_linked_processes(Name) when is_atom(Name) ->
+    case whereis(Name) of
+        undefined -> {error, {unknown, Name}};
+        Pid -> print_linked_processes(Pid)
+    end;
+print_linked_processes(Pid) when is_pid(Pid) ->
+    Info = [reductions, message_queue_len, memory],
+    TableSpec = [
+        {50, left, name}, {12, centre, reductions},
+        {19, centre, message_queue_len}, {10, centre, memory}
+    ],
+    Tree = linked_processes_info(Pid, Info),
+    print_tree(Tree, TableSpec).
+
+id("couch_file:init" ++ _, Pid, _Props) ->
+    case couch_file:process_info(Pid) of
+        {{file_descriptor, prim_file, {Port, Fd}}, FilePath} ->
+            term2str([
+                term2str(Fd), ":",
+                term2str(Port), ":",
+                shorten_path(FilePath)]);
+        undefined ->
+            ""
+    end;
+id(_IdStr, _Pid, _Props) ->
+    "".
+
+print_couch_index_server_processes() ->
+    Info = [reductions, message_queue_len, memory],
+    TableSpec = [
+        {50, left, name}, {12, centre, reductions},
+        {19, centre, message_queue_len}, {14, centre, memory}, {id}
+    ],
+
+    Tree = link_tree(whereis(couch_index_server), Info, fun(P, Props) ->
+        IdStr = process_name(P),
+        {IdStr, [{id, id(IdStr, P, Props)} | Props]}
+    end),
+    print_tree(Tree, TableSpec).
+
+shorten_path(Path) ->
+    ViewDir = list_to_binary(config:get("couchdb", "view_index_dir")),
+    DatabaseDir = list_to_binary(config:get("couchdb", "database_dir")),
+    File = list_to_binary(Path),
+    Len = max(
+        binary:longest_common_prefix([File, DatabaseDir]),
+        binary:longest_common_prefix([File, ViewDir])
+    ),
+    <<_:Len/binary, Rest/binary>> = File,
+    binary_to_list(Rest).
+
+%% Pretty print functions
+
+%% Limmitations:
+%%   - The first column has to be specified as {Width, left, Something}
+print_tree(Tree, TableSpec) ->
+    io:format("~s~n", [format(TableSpec)]),
+    map_tree(Tree, fun(_, {Id, Props}, Pos) ->
+        io:format("~s~n", [table_row(Id, Pos * 2, Props, TableSpec)])
+    end),
+    ok.
+
+format(Spec) ->
+    Fields = [format_value(Format) || Format <- Spec],
+    string:join(Fields, "|").
+
+format_value({Value}) -> term2str(Value);
+format_value({Width, Align, Value}) -> string:Align(term2str(Value), Width).
+
+bind_value({K}, Props) when is_list(Props) ->
+    {element(2, lists:keyfind(K, 1, Props))};
+bind_value({Width, Align, K}, Props) when is_list(Props) ->
+    {Width, Align, element(2, lists:keyfind(K, 1, Props))}.
+
+term2str(Atom) when is_atom(Atom) -> atom_to_list(Atom);
+term2str(Binary) when is_binary(Binary) -> binary_to_list(Binary);
+term2str(Integer) when is_integer(Integer) -> integer_to_list(Integer);
+term2str(Float) when is_float(Float) -> float_to_list(Float);
+term2str(String) when is_list(String) -> lists:flatten(String);
+term2str(Term) -> iolist_to_list(io_lib:format("~p", [Term])).
+
+table_row(Key, Indent, Props, [{KeyWidth, Align, _} | Spec]) ->
+    Values = [bind_value(Format, Props) || Format <- Spec],
+    KeyStr = string:Align(term2str(Key), KeyWidth - Indent),
+    [string:copies(" ", Indent), KeyStr, "|" | format(Values)].
+
+-ifdef(TEST).
+-include_lib("couch/include/couch_eunit.hrl").
+
+random_processes() ->
+    random_processes([], 50).
+
+random_processes(Pids, 0) ->
+    Pids;
+random_processes(Acc, Left) ->
+    Pid = case oneof([spawn_monitor, spawn, spawn_link]) of
+        spawn_monitor ->
+            {P, _} = spawn_monitor(fun process_fun/0),
+            P;
+        spawn ->
+            spawn(fun process_fun/0);
+        spawn_link ->
+            spawn(fun process_fun/0)
 
 Review comment:
   spawn_link there? Or specifically didn't want to link to avoid crashing the 
test process?
 
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to