View source with formatted comments or as raw
    1/*  Part of SWISH
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        jan@swi-prolog.org
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2015-2024, VU University Amsterdam
    7			      SWI-Prolog Solutions b.v.
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(procps,
   37	  [ procps_stat/1,		% -Stat
   38	    procps_stat/2,		% +Pid, -Stat
   39	    procps_thread_stat/2,	% ?Thread, -Stat
   40	    procps_status/1,		% -Status
   41	    procps_status/2		% +Pid, -Status
   42	  ]).   43:- if(exists_source(library(unix))).   44:- use_module(library(unix)).   45:- endif.   46:- use_module(library(lists)).   47
   48/** <module> Get process statistics from Linux /proc
   49*/
   50
   51		 /*******************************
   52		 *	  /proc/[pid]/stat	*
   53		 *******************************/
   54
   55%!	procps_stat(-Stat:dict) is det.
   56%!	procps_stat(+PID, -Stat:dict) is det.
   57%!	procps_thread_stat(+Thread, -Stat:dict) is det.
   58%
   59%	Get data from the  `stat`  file   of  the  current  process, the
   60%	process identified by `PID` or the   Prolog thread identified by
   61%	`Thread`. In all cases, this returns   a dict tagged `stat` with
   62%	the field values as defined by ``man 5 proc``.
   63%
   64%	@error existence_error(source_sink, _) if the system does not
   65%	provide the ``/proc`` filesystem.
   66
   67procps_stat(Stat) :-
   68	stat_file_dict('/proc/self/stat', Stat).
   69procps_stat(Pid, Stat) :-
   70	atomic_list_concat(['/proc/', Pid, '/stat'], StatFile),
   71	stat_file_dict(StatFile, Stat).
   72
   73procps_thread_stat(Thread, Stat) :-
   74	thread_property(Thread, system_thread_id(TID)),
   75	atomic_list_concat(['/proc/self/task/', TID, '/stat'], StatFile),
   76	stat_file_dict(StatFile, Stat).
   77
   78stat_file_dict(StatFile, Stat) :-
   79	setup_call_cleanup(
   80	    open(StatFile, read, In),
   81	    read_string(In, _, String),
   82	    close(In)),
   83	split_string(String, " ", " \n", Parts),
   84	parts_pairs(Parts, 1, Pairs),
   85	dict_pairs(Stat, stat, Pairs).
   86
   87parts_pairs([], _, []).
   88parts_pairs([H0|T0], I0, [H|T]) :-
   89	part_pair(H0, I0, H),
   90	I is I0+1,
   91	parts_pairs(T0, I, T).
   92
   93part_pair(String, I, Key-Value) :-
   94	stat_field(Key, I), !,
   95	stat_field_value(Key, String, Value).
   96part_pair(String, I, I-String).
   97
   98stat_field_value(comm, String, Command) :- !,
   99	sub_string(String, 1, _, 1, Command).
  100stat_field_value(state, String, Atom) :- !,
  101	atom_string(Atom, String).
  102stat_field_value(Field, String, Seconds) :-
  103	time_field(Field), !,
  104	number_string(ClockTicks, String),
  105	clockticks(TicksPerSec),
  106	Seconds is ClockTicks/TicksPerSec.
  107stat_field_value(Field, String, Bytes) :-
  108	page_field(Field), !,
  109	number_string(Pages, String),
  110	pagesize(BytesPerPage),
  111	Bytes is Pages*BytesPerPage.
  112stat_field_value(_, String, Number) :-
  113	number_string(Number, String).
  114
  115:- if(current_predicate(sysconf/1)).  116% the weird way to call sysconf confuses ClioPatria's cpack code
  117% analysis enough to accept this ...
  118term_expansion(clockticks(sysconf), Expansion) :-
  119	(   member(Sysconf, [sysconf(clk_tck(TicksPerSec))]),
  120	    call(Sysconf)
  121	->  Expansion = clockticks(TicksPerSec)
  122	;   Expansion = clockticks(100)
  123	).
  124term_expansion(pagesize(sysconf), Expansion) :-
  125	(   member(Sysconf, [sysconf(pagesize(Bytes))]),
  126	    call(Sysconf)
  127	->  Expansion = pagesize(Bytes)
  128	;   Expansion = pagesize(4096)
  129	).
  130clockticks(sysconf).
  131pagesize(sysconf).
  132:- else.  133clockticks(100).
  134pagesize(4096).
  135:- endif.  136
  137time_field(utime).
  138time_field(stime).
  139time_field(cutime).
  140time_field(cstime).
  141time_field(starttime).
  142
  143page_field(rss).
  144
  145stat_field(pid,			  1).
  146stat_field(comm,		  2).
  147stat_field(state,		  3).
  148stat_field(ppid,		  4).
  149stat_field(pgrp,		  5).
  150stat_field(session,		  6).
  151stat_field(tty_nr,		  7).
  152stat_field(tpgid,		  8).
  153stat_field(flags,		  9).
  154stat_field(minflt,		  10).
  155stat_field(cminflt,		  11).
  156stat_field(majflt,		  12).
  157stat_field(cmajflt,		  13).
  158stat_field(utime,		  14).
  159stat_field(stime,		  15).
  160stat_field(cutime,		  16).
  161stat_field(cstime,		  17).
  162stat_field(priority,		  18).
  163stat_field(nice,		  19).
  164stat_field(num_threads,		  20).
  165stat_field(itrealvalue,		  21).
  166stat_field(starttime,		  22).
  167stat_field(vsize,		  23).
  168stat_field(rss,			  24).
  169stat_field(rsslim,		  25).
  170stat_field(startcode,		  26).
  171stat_field(endcode,		  27).
  172stat_field(startstack,		  28).
  173stat_field(kstkesp,		  29).
  174stat_field(kstkeip,		  30).
  175stat_field(signal,		  31).
  176stat_field(blocked,		  32).
  177stat_field(sigignore,		  33).
  178stat_field(sigcatch,		  34).
  179stat_field(wchan,		  35).
  180stat_field(nswap,		  36).
  181stat_field(cnswap,		  37).
  182stat_field(exit_signal,		  38).
  183stat_field(processor,		  39).
  184stat_field(rt_priority,		  40).
  185stat_field(policy,		  41).
  186stat_field(delayacct_blkio_ticks, 42).
  187stat_field(guest_time,		  43).
  188stat_field(cguest_time,		  44).
  189stat_field(start_data,		  45).
  190stat_field(end_data,		  46).
  191stat_field(start_brk,		  47).
  192stat_field(arg_start,		  48).
  193stat_field(arg_end,		  49).
  194stat_field(env_start,		  50).
  195stat_field(env_end,		  51).
  196stat_field(exit_code,		  52).
  197
  198
  199		 /*******************************
  200		 *	/proc/[pid]/status	*
  201		 *******************************/
  202
  203%!	procps_status(-Status:dict) is det.
  204%!	procps_status(+PID, -Status:dict) is det.
  205%
  206%	Get the data from ``/proc/self/status`` as a Prolog dict.
  207%
  208%	@tbd Not all fields are currently translated.
  209
  210procps_status(Stat) :-
  211	status_file_dict('/proc/self/status', Stat).
  212procps_status(Pid, Stat) :-
  213	atomic_list_concat(['/proc/', Pid, '/status'], StatFile),
  214	status_file_dict(StatFile, Stat).
  215
  216status_file_dict(StatFile, Status) :-
  217	setup_call_cleanup(
  218	    open(StatFile, read, In),
  219	    read_string(In, _, String),
  220	    close(In)),
  221	split_string(String, "\n", " \n", Lines),
  222	status_line_pairs(Lines, Pairs),
  223	dict_pairs(Status, status, Pairs).
  224
  225status_line_pairs([], []).
  226status_line_pairs([H0|T0], [Name-Value|T]) :-
  227	split_string(H0, ":", " \t", [NameS, ValueS]),
  228	string_lower(NameS, NameLS),
  229	atom_string(Name, NameLS),
  230	status_value(Name, ValueS, Value), !,
  231	status_line_pairs(T0, T).
  232status_line_pairs([_|T0], T) :-
  233	status_line_pairs(T0, T).
  234
  235status_value(state, ValueS, State) :- !,
  236	split_string(ValueS, " ", " ", [Vs|_]),
  237	atom_string(State, Vs).
  238status_value(Name, ValueS, Bytes) :-
  239	sub_atom(Name, 0, _, _, vm), !,
  240	split_string(ValueS, " ", " ", [Vs,"kB"]),
  241	number_string(Kb, Vs),
  242	Bytes is Kb*1024.
  243status_value(Name, ValueS, Value) :-
  244	status_field(Name, list(Type)),
  245	!,
  246	split_string(ValueS, " \t", " \t", Values),
  247	maplist(to_type(Type), Values, Value).
  248status_value(Name, ValueS, Value) :-
  249	status_field(Name, Type),
  250	to_type(Type, ValueS, Value).
  251
  252to_type(integer, String, Int) :-
  253	number_string(Int, String).
  254to_type(hex, String, Int) :-
  255	string_concat('0x', String, Hex),
  256	number_string(Int, Hex).
  257
  258status_field(uid, list(integer)).
  259status_field(gid, list(integer)).
  260status_field(groups, list(integer)).
  261status_field(cpus_allowed, hex).
  262status_field(fdsize, integer).
  263status_field(threads, integer)