From d4667d383964c1550e0a91d64b674e84f6d07e3b Mon Sep 17 00:00:00 2001 From: Matthias Lang Date: Thu, 19 Aug 2010 15:12:26 +0200 Subject: Extend erlang:port_info/1,2 to show the OS pid of a spawned process When spawning OS (unix) processes with erlang:open_port, store the resulting unix pid so that it can be queried later on using erlang:port_info/1,2. --- erts/doc/src/erlang.xml | 4 ++++ erts/emulator/beam/atom.names | 1 + erts/emulator/beam/erl_bif_info.c | 16 ++++++++++++- erts/emulator/beam/global.h | 1 + erts/emulator/beam/io.c | 1 + erts/emulator/sys/unix/sys.c | 2 ++ erts/emulator/test/port_bif_SUITE.erl | 28 +++++++++++++++++++++- .../test/small_SUITE_data/results/port_info_test | 3 ++- .../test/small_SUITE_data/src/port_info_test.erl | 6 ++++- lib/hipe/cerl/erl_bif_types.erl | 3 ++- 10 files changed, 60 insertions(+), 5 deletions(-) diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 30e3b02a0a..3664bdf76d 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -3084,6 +3084,10 @@ os_prompt%

Bytes is the total number of bytes written to the port.

+ {os_pid, Integer} + +

Integer is the OS pid of the spawned process (Unix systems only).

+

Failure: badarg if Port is not a local port.

diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 7be40976f6..2c6e80a3c3 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -390,6 +390,7 @@ atom opt atom or atom ordered_set atom orelse +atom os_pid atom os_type atom os_version atom ose_bg_proc diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index ebd475f73a..e198be67c7 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2741,7 +2741,8 @@ port_info_1(BIF_ALIST_1) am_id, am_connected, am_input, - am_output + am_output, + am_os_pid }; Eterm items[ASIZE(keys)]; Eterm result = NIL; @@ -2798,6 +2799,7 @@ port_info_1(BIF_ALIST_1) ** name String ** input Number of bytes input from port program ** output Number of bytes output to the port program +** os_pid The child's process ID */ BIF_RETTYPE port_info_2(BIF_ALIST_2) @@ -2898,6 +2900,18 @@ static BIF_RETTYPE port_info(Process* p, Eterm portid, Eterm item) hp = HAlloc(p, hsz); res = erts_bld_uint(&hp, NULL, n); } + else if (item == am_os_pid) { + if (prt->os_pid >= 0) { + Uint hsz = 3; + Uint n = prt->os_pid; + (void) erts_bld_uint(NULL, &hsz, n); + hp = HAlloc(p, hsz); + res = erts_bld_uint(&hp, NULL, n); + } else { + hp = HAlloc(p, 3); + res = am_undefined; + } + } else if (item == am_registered_name) { RegProc *reg; reg = prt->reg; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 6eac0db363..1431e08610 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -174,6 +174,7 @@ struct port { char *name; /* String used in the open */ erts_driver_t* drv_ptr; UWord drv_data; + pid_t os_pid; /* Child process ID */ ErtsProcList *suspended; /* List of suspended processes. */ LineBuf *linebuf; /* Buffer to hold data not ready for process to get (line oriented I/O)*/ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index b23b1f628d..3deb386621 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -425,6 +425,7 @@ setup_port(Port* prt, Eterm pid, erts_driver_t *driver, sys_strcpy(new_name, name); erts_smp_runq_lock(runq); erts_smp_port_state_lock(prt); + prt->os_pid = -1; prt->status = ERTS_PORT_SFLG_CONNECTED | xstatus; prt->snapshot = erts_smp_atomic32_read_nob(&erts_ports_snapshot); old_name = prt->name; diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index f94e0f2296..bf69f3bf90 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -1163,6 +1163,8 @@ static int set_driver_data(int port_num, report_exit_list = report_exit; } + erts_port[port_num].os_pid = pid; + if (read_write & DO_READ) { driver_data[ifd].packet_bytes = packet_bytes; driver_data[ifd].port_num = port_num; diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index d9c82aba0e..f4ba502be5 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -24,6 +24,7 @@ init_per_group/2,end_per_group/2, command/1, command_e_1/1, command_e_2/1, command_e_3/1, command_e_4/1, port_info1/1, port_info2/1, + port_info_os_pid/1, connect/1, control/1, echo_to_busy/1]). -export([do_command_e_1/1, do_command_e_2/1, do_command_e_4/1]). @@ -41,7 +42,7 @@ all() -> groups() -> [{command_e, [], [command_e_1, command_e_2, command_e_3, command_e_4]}, - {port_info, [], [port_info1, port_info2]}]. + {port_info, [], [port_info1, port_info2, port_info_os_pid]}]. init_per_suite(Config) -> Config. @@ -196,6 +197,7 @@ port_info1(Config) when is_list(Config) -> ?line {value,{connected,_}}=lists:keysearch(connected, 1, A), ?line {value,{input,0}}=lists:keysearch(input, 1, A), ?line {value,{output,0}}=lists:keysearch(output, 1, A), + ?line {value,{os_pid,undefined}}=lists:keysearch(os_pid, 1, A), % linked-in driver doesn't have a OS pid ?line true=erlang:port_close(P), ok. @@ -215,6 +217,7 @@ port_info2(Config) when is_list(Config) -> ?line {connected, Me} = erlang:port_info(P, connected), ?line {input, 0}=erlang:port_info(P, input), ?line {output,0}=erlang:port_info(P, output), + ?line {os_pid, undefined}=erlang:port_info(P, os_pid), % linked-in driver doesn't have a OS pid ?line erlang:port_control(P, $i, "abc"), ?line receive @@ -229,6 +232,29 @@ port_info2(Config) when is_list(Config) -> ?line true = erlang:port_close(P), ok. +%% Tests the port_info/1,2 os_pid option BIF +port_info_os_pid(Config) when is_list(Config) -> + case os:type() of + {unix,_} -> + do_port_info_os_pid(); + _ -> + {skip,"Only on Unix."} + end. + +do_port_info_os_pid() -> + ?line P = open_port({spawn, "echo $$"}, [eof]), + ?line A = erlang:port_info(P), + ?line {os_pid, InfoOSPid} = erlang:port_info(P, os_pid), + ?line EchoPidStr = receive + {P, {data, EchoPidStr0}} -> EchoPidStr0 + after 10000 -> ?line test_server:fail(timeout) + end, + ?line {ok, [EchoPid], []} = io_lib:fread("~u\n", EchoPidStr), + ?line {value,{os_pid, InfoOSPid}}=lists:keysearch(os_pid, 1, A), + ?line EchoPid = InfoOSPid, + ?line true = erlang:port_close(P), + ok. + output_test(_, _, Input, Output) when Output > 16#1fffffff -> io:format("~p bytes received\n", [Input]); output_test(P, Bin, Input0, Output0) -> diff --git a/lib/dialyzer/test/small_SUITE_data/results/port_info_test b/lib/dialyzer/test/small_SUITE_data/results/port_info_test index 9ee863f9eb..8a4ce0fc66 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/port_info_test +++ b/lib/dialyzer/test/small_SUITE_data/results/port_info_test @@ -3,4 +3,5 @@ port_info_test.erl:10: The pattern {'connected', 42} can never match the type 'u port_info_test.erl:14: The pattern {'registered_name', "42"} can never match the type 'undefined' | {'registered_name',atom()} port_info_test.erl:19: The pattern {'output', 42} can never match the type 'undefined' | {'connected',pid()} port_info_test.erl:24: Guard test 'links' =:= Atom::'connected' can never succeed -port_info_test.erl:28: The pattern {'gazonk', _} can never match the type 'undefined' | {'connected' | 'id' | 'input' | 'links' | 'name' | 'output' | 'registered_name',atom() | pid() | [pid() | char()] | integer()} +port_info_test.erl:28: The pattern {'gazonk', _} can never match the type 'undefined' | {'connected' | 'id' | 'input' | 'links' | 'name' | 'os_pid' | 'output' | 'registered_name',atom() | pid() | [pid() | char()] | integer()} +port_info_test.erl:32: The pattern {'os_pid', "42"} can never match the type 'undefined' | {'os_pid',integer()} diff --git a/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl b/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl index 2ee9a3a6e2..07f22256c9 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl @@ -3,7 +3,7 @@ %% and the quality of the warnings that Dialyzer spits out %% -module(port_info_test). --export([t1/1, t2/1, t3/1, t4/1, t5/2, buggy/1]). +-export([t1/1, t2/1, t3/1, t4/1, t5/2, t6/1, buggy/1]). %% The following errors are correctly caught, but the messages are a bit weird t1(X) when is_port(X) -> @@ -28,6 +28,10 @@ t5(X, Atom) when is_port(X) -> {gazonk, _} = erlang:port_info(X, Atom); t5(_, _) -> ok. +t6(X) when is_port(X) -> + {os_pid, "42"} = erlang:port_info(X, os_pid); +t6(_) -> ok. + %% The type system is not strong enough to catch the following errors buggy(X) when is_atom(X) -> {links, X} = erlang:port_info(foo, X). diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 3d1e3e8137..7ddbf56dd4 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -780,6 +780,7 @@ type(erlang, port_info, 2, Xs) -> ['input'] -> t_tuple([Item, t_integer()]); ['links'] -> t_tuple([Item, t_list(t_pid())]); ['name'] -> t_tuple([Item, t_string()]); + ['os_pid'] -> t_tuple([Item, t_integer()]); ['output'] -> t_tuple([Item, t_integer()]); ['registered_name'] -> t_tuple([Item, t_atom()]); List when is_list(List) -> @@ -2282,7 +2283,7 @@ arg_types(erlang, port_info, 1) -> arg_types(erlang, port_info, 2) -> [t_sup(t_port(), t_atom()), t_atoms(['registered_name', 'id', 'connected', - 'links', 'name', 'input', 'output'])]; + 'links', 'name', 'input', 'output', 'os_pid'])]; %% Guard bif, needs to be here. arg_types(erlang, round, 1) -> [t_number()]; -- cgit v1.2.3