Skip to content

Commit 96ff82a

Browse files
authored
Merge pull request #609 from bgoglin/pid-children
combine hwloc-ps and lstopo for more customizable lstopo --top
2 parents cdf3380 + 6206d16 commit 96ff82a

File tree

8 files changed

+306
-16
lines changed

8 files changed

+306
-16
lines changed

contrib/completion/bash/hwloc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright © 2018-2022 Inria. All rights reserved.
2+
# Copyright © 2018-2023 Inria. All rights reserved.
33
# See COPYING in top-level directory.
44
#
55

@@ -80,6 +80,7 @@ _lstopo() {
8080
--export-xml-flags
8181
--export-synthetic-flags
8282
--ps --top
83+
--misc-from
8384
--version
8485
-h --help
8586
)
@@ -480,6 +481,7 @@ complete -F _hwloc_distrib hwloc-distrib
480481
_hwloc_ps(){
481482
local OPTIONS=(-a
482483
--pid
484+
--children-of-pid
483485
--name
484486
--uid
485487
-l --logical
@@ -491,6 +493,7 @@ _hwloc_ps(){
491493
--pid-cmd
492494
--short-name
493495
--disallowed --whole-system
496+
--lstopo-misc
494497
--json-server
495498
--json-port
496499
-v --verbose
@@ -510,7 +513,7 @@ _hwloc_ps(){
510513
--uid)
511514
COMPREPLY=( "<uid>" "all" "" )
512515
;;
513-
--pid)
516+
--pid|--children-of-pid)
514517
COMPREPLY=( "<pid>" "" )
515518
;;
516519
--pid-cmd)

utils/hwloc/common-ps.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,3 +393,63 @@ int hwloc_ps_foreach_process(hwloc_topology_t topology, hwloc_const_bitmap_t top
393393
return -1;
394394
#endif /* HAVE_DIRENT_H */
395395
}
396+
397+
int hwloc_ps_foreach_child(hwloc_topology_t topology, hwloc_const_bitmap_t topocpuset,
398+
long pid,
399+
void (*callback)(hwloc_topology_t topology, struct hwloc_ps_process *proc, void *cbdata),
400+
void *cbdata,
401+
unsigned long flags, const char *only_name, long uid)
402+
{
403+
#ifdef HAVE_DIRENT_H
404+
struct hwloc_ps_process proc;
405+
DIR *taskdir;
406+
char path[512];
407+
408+
proc.pid = pid;
409+
proc.cpuset = NULL;
410+
proc.nthreads = 0;
411+
proc.nboundthreads = 0;
412+
proc.threads = NULL;
413+
if (hwloc_ps_read_process(topology, topocpuset, &proc, flags) < 0)
414+
goto next;
415+
if (only_name && !strstr(proc.name, only_name))
416+
goto next;
417+
if (uid != HWLOC_PS_ALL_UIDS && proc.uid != HWLOC_PS_ALL_UIDS && proc.uid != uid)
418+
goto next;
419+
callback(topology, &proc, cbdata);
420+
next:
421+
hwloc_ps_free_process(&proc);
422+
423+
snprintf(path, sizeof(path), "/proc/%ld/task", proc.pid);
424+
taskdir = opendir(path); /* should be enough for the vast majority of cases */
425+
if (taskdir) {
426+
struct dirent *taskdirent;
427+
while ((taskdirent = readdir(taskdir))) {
428+
char pidline[4096];
429+
FILE *file;
430+
char *begin;
431+
size_t len;
432+
snprintf(path, sizeof(path), "/proc/%ld/task/%s/children", proc.pid, taskdirent->d_name);
433+
file = fopen(path, "r");
434+
if (!file)
435+
continue;
436+
len = fread(pidline, 1, sizeof(pidline)-1, file);
437+
fclose(file);
438+
pidline[len] = '\0';
439+
begin = pidline;
440+
while (1) {
441+
char *end;
442+
long childpid = strtoul(begin, &end, 10);
443+
if (end == begin)
444+
break;
445+
hwloc_ps_foreach_child(topology, topocpuset, childpid, callback, cbdata, flags, only_name, uid);
446+
begin = end;
447+
}
448+
}
449+
closedir(taskdir);
450+
}
451+
return 0;
452+
#else /* HAVE_DIRENT_H */
453+
return -1;
454+
#endif /* HAVE_DIRENT_H */
455+
}

utils/hwloc/common-ps.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2009-2021 Inria. All rights reserved.
2+
* Copyright © 2009-2023 Inria. All rights reserved.
33
* Copyright © 2009-2012 Université Bordeaux
44
* Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved.
55
* See COPYING in top-level directory.
@@ -45,6 +45,12 @@ int hwloc_ps_foreach_process(hwloc_topology_t topology, hwloc_const_bitmap_t top
4545
void *cbdata,
4646
unsigned long flags, const char *only_name, long only_uid);
4747

48+
int hwloc_ps_foreach_child(hwloc_topology_t topology, hwloc_const_bitmap_t topocpuset,
49+
long pid,
50+
void (*callback)(hwloc_topology_t topology, struct hwloc_ps_process *proc, void *cbdata),
51+
void *cbdata,
52+
unsigned long flags, const char *only_name, long only_uid);
53+
4854
void hwloc_ps_pidcmd(struct hwloc_ps_process *proc, const char *pidcmd);
4955

5056
void hwloc_ps_free_process(struct hwloc_ps_process *proc);

utils/hwloc/hwloc-ps.1in

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.\" -*- nroff -*-
2-
.\" Copyright © 2010-2021 Inria. All rights reserved.
2+
.\" Copyright © 2010-2023 Inria. All rights reserved.
33
.\" Copyright © 2009-2010 Cisco Systems, Inc. All rights reserved.
44
.\" See COPYING in top-level directory.
55
.TH HWLOC-PS "1" "%HWLOC_DATE%" "%PACKAGE_VERSION%" "%PACKAGE_NAME%"
@@ -28,6 +28,10 @@ specific part of the machine.
2828
Only show process of PID \fI<pid>\fR,
2929
even if it is not bound to any specific part of the machine.
3030
.TP
31+
\fB\-\-children\-of\-pid <pid>\fR
32+
Only show process of PID \fI<pid>\fR and its hierarchy of children,
33+
even if they are not bound to any specific part of the machine.
34+
.TP
3135
\fB\-\-name <name>\fR
3236
Only show processes whose name contains \fI<name>\fR,
3337
even if they are not bound to any specific part of the machine.
@@ -82,6 +86,10 @@ in each process and display it at the end of the line.
8286
On Linux, try to find the process MPI rank (by querying some widespread
8387
environment variables) and display it at the end of the line.
8488

89+
.TP
90+
\fB\-\-lstopo\-misc\fR <file>
91+
Output a file that may be given to \fBlstopo \-\-misc\-from\fR for displaying
92+
processes/threads as Misc objects. See EXAMPLES below.
8593
.TP
8694
\fB\-\-json\-server\fR
8795
Run the tool as a JSON server that waits for other process' requests
@@ -171,6 +179,27 @@ it only appears in the thread-aware output (or if explicitly selected):
171179
$ hwloc-ps --pid 4759
172180
4759 Machine:0 myprogram
173181

182+
The output may be a file that lstopo uses for adding Misc objects
183+
(more flexible version of lstopo --top):
184+
185+
$ hwloc-ps --misc-from foo
186+
$ cat foo
187+
name=12444 myprogram
188+
cpuset=0x000000f0
189+
subtype=Process
190+
191+
name=12444 mythread1
192+
cpuset=0x00000050
193+
subtype=Thread
194+
195+
name=12444 mythread2
196+
cpuset=0x000000a0
197+
subtype=Thread
198+
199+
This may be directly given to lstopo:
200+
201+
$ hwloc-ps --misc-from - | lstopo --misc-from -
202+
174203
On Linux, hwloc-ps may also display some process specific environment
175204
variable at the end of the line. This is for instance useful
176205
for identify MPI ranks among processes:

utils/hwloc/hwloc-ps.c

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2009-2022 Inria. All rights reserved.
2+
* Copyright © 2009-2023 Inria. All rights reserved.
33
* Copyright © 2009-2012 Université Bordeaux
44
* Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved.
55
* See COPYING in top-level directory.
@@ -35,18 +35,21 @@ static int logical = 1;
3535
static int single_ancestor = 0;
3636
#define NO_ONLY_PID -1
3737
static long only_pid = NO_ONLY_PID;
38+
static long children_of_pid = NO_ONLY_PID;
3839
static long only_uid;
3940
static int json_server = 0;
4041
static int json_port = JSON_PORT;
4142
static FILE *json_output = NULL;
4243
static int verbose = 0;
44+
static FILE *lstopo_misc_output = NULL;
4345

4446
void usage(const char *name, FILE *where)
4547
{
4648
fprintf (where, "Usage: %s [ options ] ...\n", name);
4749
fprintf (where, "Options:\n");
4850
fprintf (where, " -a Show all processes, including those that are not bound\n");
4951
fprintf (where, " --pid <pid> Only show process of pid number <pid>\n");
52+
fprintf (where, " --children-of-pid <pid> Only show process of pid number <pid> and its children\n");
5053
fprintf (where, " --name <name> Only show processes whose name contains <name>\n");
5154
#ifdef HWLOC_LINUX_SYS
5255
fprintf (where, " --uid <uid> Only show processes of the user with the given uid\n");
@@ -64,6 +67,7 @@ void usage(const char *name, FILE *where)
6467
fprintf (where, " --pid-cmd <cmd> Append the output of <cmd> <pid> to each PID line\n");
6568
fprintf (where, " --short-name Show only the process short name instead of the path\n");
6669
fprintf (where, " --disallowed Include objects disallowed by administrative limitations\n");
70+
fprintf (where, " --lstopo-misc <file> Output Misc object to be given to lstopo --misc-from <file>\n");
6771
fprintf (where, " --json-server Run as a JSON server\n");
6872
fprintf (where, " --json-port <n> Use port <n> for JSON server (default is %d)\n", JSON_PORT);
6973
fprintf (where, " -v --verbose Increase verbosity\n");
@@ -133,6 +137,45 @@ static void print_process(hwloc_topology_t topology,
133137
print_task(topology, proc->threads[i].tid, proc->threads[i].name, proc->threads[i].cpuset, NULL, 1);
134138
}
135139

140+
static void print_process_lstopo_misc(hwloc_topology_t topology __hwloc_attribute_unused,
141+
struct hwloc_ps_process *proc)
142+
{
143+
/* sort of similar to foreach_process_cb() in lstopo.c */
144+
char name[100];
145+
char *s;
146+
unsigned i;
147+
148+
snprintf(name, sizeof(name), "%ld", proc->pid);
149+
if (*proc->name)
150+
snprintf(name, sizeof(name), "%ld %s", proc->pid, proc->name);
151+
hwloc_bitmap_asprintf(&s, proc->cpuset);
152+
fprintf(lstopo_misc_output,
153+
"name=%s\n"
154+
"cpuset=%s\n"
155+
"subtype=Process\n"
156+
"\n",
157+
name, s);
158+
free(s);
159+
160+
if (proc->nthreads)
161+
for(i=0; i<proc->nthreads; i++)
162+
if (proc->threads[i].cpuset) {
163+
char task_name[150];
164+
if (*proc->threads[i].name)
165+
snprintf(task_name, sizeof(task_name), "%s %li %s", name, proc->threads[i].tid, proc->threads[i].name);
166+
else
167+
snprintf(task_name, sizeof(task_name), "%s %li", name, proc->threads[i].tid);
168+
hwloc_bitmap_asprintf(&s, proc->threads[i].cpuset);
169+
fprintf(lstopo_misc_output,
170+
"name=%s\n"
171+
"cpuset=%s\n"
172+
"subtype=Thread\n"
173+
"\n",
174+
task_name, s);
175+
free(s);
176+
}
177+
}
178+
136179
static void print_process_json(hwloc_topology_t topology,
137180
struct hwloc_ps_process *proc)
138181
{
@@ -197,22 +240,28 @@ static void foreach_process_cb(hwloc_topology_t topology,
197240
const char *pidcmd = cbdata;
198241

199242
/* don't print anything if the process isn't bound and if no threads are bound and if not showing all */
200-
if (!proc->bound && (!proc->nthreads || !proc->nboundthreads) && !show_all && !only_name)
243+
if (!proc->bound && (!proc->nthreads || !proc->nboundthreads) && !show_all && !only_name && children_of_pid == NO_ONLY_PID)
201244
return;
202245

203246
if (pidcmd)
204247
hwloc_ps_pidcmd(proc, pidcmd);
205248

206249
if (json_output)
207250
print_process_json(topology, proc);
251+
else if (lstopo_misc_output)
252+
print_process_lstopo_misc(topology, proc);
208253
else
209254
print_process(topology, proc);
210255
}
211256

212257
static int run(hwloc_topology_t topology, hwloc_const_bitmap_t topocpuset,
213258
unsigned long psflags, char *pidcmd)
214259
{
215-
if (only_pid == NO_ONLY_PID) {
260+
if (children_of_pid != NO_ONLY_PID) {
261+
/* show children */
262+
return hwloc_ps_foreach_child(topology, topocpuset, children_of_pid, foreach_process_cb, pidcmd, psflags, only_name, only_uid);
263+
264+
} else if (only_pid == NO_ONLY_PID) {
216265
/* show all */
217266
return hwloc_ps_foreach_process(topology, topocpuset, foreach_process_cb, pidcmd, psflags, only_name, only_uid);
218267

@@ -235,6 +284,8 @@ static int run(hwloc_topology_t topology, hwloc_const_bitmap_t topocpuset,
235284

236285
if (json_output)
237286
print_process_json(topology, &proc);
287+
else if (lstopo_misc_output)
288+
print_process_lstopo_misc(topology, &proc);
238289
else
239290
print_process(topology, &proc);
240291
}
@@ -317,6 +368,7 @@ run_json_server(hwloc_topology_t topology, hwloc_const_bitmap_t topocpuset)
317368

318369
only_name = NULL;
319370
only_pid = NO_ONLY_PID;
371+
children_of_pid = NO_ONLY_PID;
320372
current = req;
321373
while (*current) {
322374
if (!strncmp(current, "lastcpulocation ", 16)) {
@@ -338,6 +390,10 @@ run_json_server(hwloc_topology_t topology, hwloc_const_bitmap_t topocpuset)
338390
psflags |= HWLOC_PS_FLAG_THREADS;
339391
show_all = 1;
340392
break;
393+
} else if (!strncmp(current, "childrenofpid=", 14)) {
394+
children_of_pid = atoi(current+14);
395+
show_all = 1;
396+
break;
341397
} else if (!strncmp(current, "name=", 5)) {
342398
only_name = current+5;
343399
show_all = 1;
@@ -418,6 +474,13 @@ int main(int argc, char *argv[])
418474
}
419475
only_pid = strtol(argv[1], NULL, 10);
420476
opt = 1;
477+
} else if (!strcmp(argv[0], "--children-of-pid")) {
478+
if (argc < 2) {
479+
usage(callname, stderr);
480+
exit(EXIT_FAILURE);
481+
}
482+
children_of_pid = strtol(argv[1], NULL, 10);
483+
opt = 1;
421484
} else if (!strcmp(argv[0], "--name")) {
422485
if (argc < 2) {
423486
usage(callname, stderr);
@@ -449,6 +512,21 @@ int main(int argc, char *argv[])
449512
pidcmd = argv[1];
450513
opt = 1;
451514

515+
} else if (!strcmp (argv[0], "--lstopo-misc")) {
516+
if (argc < 2) {
517+
usage(callname, stderr);
518+
exit(EXIT_FAILURE);
519+
}
520+
if (!strcmp(argv[1], "-"))
521+
lstopo_misc_output = stdout;
522+
else
523+
lstopo_misc_output = fopen(argv[1], "w");
524+
if (!lstopo_misc_output) {
525+
fprintf(stderr, "Failed to open --lstopo-misc output `%s' for writing (%s)\n", argv[1], strerror(errno));
526+
exit(EXIT_FAILURE);
527+
}
528+
opt = 1;
529+
452530
} else if (!strcmp (argv[0], "--json-server")) {
453531
json_server = 1;
454532
} else if (!strcmp (argv[0], "--json-port")) {
@@ -522,5 +600,7 @@ int main(int argc, char *argv[])
522600
out_with_topology:
523601
hwloc_topology_destroy(topology);
524602
out:
603+
if (lstopo_misc_output && lstopo_misc_output != stdout)
604+
fclose(lstopo_misc_output);
525605
return err;
526606
}

utils/lstopo/lstopo-draw.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,8 @@ lstopo_set_object_color(struct lstopo_output *loutput,
11391139
}
11401140

11411141
case HWLOC_OBJ_MISC:
1142-
if (loutput->show_process_color && obj->subtype && !strcmp(obj->subtype, "Process"))
1142+
if (loutput->show_process_color && obj->subtype &&
1143+
(!strcmp(obj->subtype, "Process") || !strcmp(obj->subtype, "Thread")))
11431144
s->bg = &loutput->palette->process;
11441145
else
11451146
s->bg = &loutput->palette->misc;

0 commit comments

Comments
 (0)