1 /*
2 * Copyright © 2009-2017 Inria. All rights reserved.
3 * Copyright © 2009-2012 Université Bordeaux
4 * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved.
5 * See COPYING in top-level directory.
6 */
7
8 #include <private/autogen/config.h>
9 #include <hwloc.h>
10 #ifdef HWLOC_LINUX_SYS
11 #include <hwloc/linux.h>
12 #endif
13
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #ifdef HAVE_DIRENT_H
18 #include <dirent.h>
19 #endif
20 #include <fcntl.h>
21
22 #include "misc.h"
23
24 static int show_cpuset = 0;
25 static int show_all = 0;
26 static int show_threads = 0;
27 static int get_last_cpu_location = 0;
28 #define NO_ONLY_PID -1
29 static long only_pid = NO_ONLY_PID;
30 static char *only_name = NULL;
31 static char *pidcmd = NULL;
32 static int logical = 1;
33
34 #define TIDNAME_MAX 16 /* specified in pthread_setname_np.3 */
35
usage(const char * name,FILE * where)36 void usage(const char *name, FILE *where)
37 {
38 fprintf (where, "Usage: %s [ options ] ...\n", name);
39 fprintf (where, "Options:\n");
40 fprintf (where, " -a Show all processes, including those that are not bound\n");
41 fprintf (where, " --pid <pid> Only show process of pid number <pid>\n");
42 fprintf (where, " --name <name> Only show processes whose name contains <name>\n");
43 fprintf (where, " -l --logical Use logical object indexes (default)\n");
44 fprintf (where, " -p --physical Use physical object indexes\n");
45 fprintf (where, " -c --cpuset Show cpuset instead of objects\n");
46 #ifdef HWLOC_LINUX_SYS
47 fprintf (where, " -t --threads Show threads\n");
48 #endif
49 fprintf (where, " -e --get-last-cpu-location\n");
50 fprintf (where, " Retrieve the last processors where the tasks ran\n");
51 fprintf (where, " --pid-cmd <cmd> Append the output of <cmd> <pid> to each PID line\n");
52 fprintf (where, " --whole-system Do not consider administration limitations\n");
53 }
54
print_task(hwloc_topology_t topology,long pid,const char * name,hwloc_bitmap_t cpuset,char * pidoutput,int thread)55 static void print_task(hwloc_topology_t topology,
56 long pid, const char *name, hwloc_bitmap_t cpuset,
57 char *pidoutput,
58 int thread)
59 {
60 printf("%s%ld\t", thread ? " " : "", pid);
61
62 if (show_cpuset) {
63 char *cpuset_str = NULL;
64 hwloc_bitmap_asprintf(&cpuset_str, cpuset);
65 printf("%s", cpuset_str);
66 free(cpuset_str);
67 } else {
68 hwloc_bitmap_t remaining = hwloc_bitmap_dup(cpuset);
69 int first = 1;
70 while (!hwloc_bitmap_iszero(remaining)) {
71 char type[64];
72 unsigned idx;
73 hwloc_obj_t obj = hwloc_get_first_largest_obj_inside_cpuset(topology, remaining);
74 /* don't show a cache if there's something equivalent and nicer */
75 while (obj->type == HWLOC_OBJ_CACHE && obj->arity == 1)
76 obj = obj->first_child;
77 hwloc_obj_type_snprintf(type, sizeof(type), obj, 1);
78 idx = logical ? obj->logical_index : obj->os_index;
79 if (idx == (unsigned) -1)
80 printf("%s%s", first ? "" : " ", type);
81 else
82 printf("%s%s:%u", first ? "" : " ", type, idx);
83 hwloc_bitmap_andnot(remaining, remaining, obj->cpuset);
84 first = 0;
85 }
86 hwloc_bitmap_free(remaining);
87 }
88
89 printf("\t\t%s%s%s\n", name, pidoutput ? "\t" : "", pidoutput ? pidoutput : "");
90 }
91
one_process(hwloc_topology_t topology,hwloc_const_bitmap_t topocpuset,long pid)92 static void one_process(hwloc_topology_t topology, hwloc_const_bitmap_t topocpuset,
93 long pid)
94 {
95 char pidoutput[1024];
96 char name[64] = "";
97 /* management of threads */
98 unsigned boundthreads = 0, i;
99 struct hwlocpstid { long tid; hwloc_bitmap_t cpuset; char name[TIDNAME_MAX]; } *tids = NULL;
100 hwloc_bitmap_t cpuset;
101 char *end;
102
103 cpuset = hwloc_bitmap_alloc();
104 if (!cpuset)
105 return;
106
107 {
108 unsigned pathlen = 6 + 21 + 1 + 7 + 1;
109 char *path;
110 int file;
111 ssize_t n;
112
113 path = malloc(pathlen);
114 snprintf(path, pathlen, "/proc/%ld/cmdline", pid);
115 file = open(path, O_RDONLY);
116 free(path);
117
118 if (file >= 0) {
119 n = read(file, name, sizeof(name) - 1);
120 close(file);
121
122 if (n <= 0)
123 /* Ignore kernel threads and errors */
124 goto out;
125
126 name[n] = 0;
127
128 if (only_name && !strstr(name, only_name))
129 goto out;
130 }
131 }
132
133 if (show_threads) {
134 #ifdef HWLOC_LINUX_SYS
135 /* check if some threads must be displayed */
136 unsigned pathlen = 6 + 21 + 1 + 4 + 1;
137 char *path;
138 DIR *taskdir;
139
140 path = malloc(pathlen);
141 snprintf(path, pathlen, "/proc/%ld/task", pid);
142 taskdir = opendir(path);
143 if (taskdir) {
144 struct dirent *taskdirent;
145 long tid;
146 unsigned n = 0;
147 /* count threads */
148 while ((taskdirent = readdir(taskdir))) {
149 tid = strtol(taskdirent->d_name, &end, 10);
150 if (*end)
151 /* Not a number */
152 continue;
153 n++;
154 }
155 if (n > 1) {
156 /* if there's more than one thread, see if some are bound */
157 tids = calloc(n+1, sizeof(*tids));
158 if (tids) {
159 /* reread the directory but gather info now */
160 rewinddir(taskdir);
161 i = 0;
162 while ((taskdirent = readdir(taskdir))) {
163 tid = strtol(taskdirent->d_name, &end, 10);
164 if (*end)
165 /* Not a number */
166 continue;
167 if (get_last_cpu_location) {
168 if (hwloc_linux_get_tid_last_cpu_location(topology, tid, cpuset))
169 continue;
170 } else {
171 if (hwloc_linux_get_tid_cpubind(topology, tid, cpuset))
172 continue;
173 }
174 hwloc_bitmap_and(cpuset, cpuset, topocpuset);
175 tids[i].tid = tid;
176 tids[i].cpuset = hwloc_bitmap_dup(cpuset);
177 i++;
178 if (hwloc_bitmap_iszero(cpuset))
179 continue;
180 if (hwloc_bitmap_isequal(cpuset, topocpuset) && !show_all && only_pid == NO_ONLY_PID && !only_name)
181 continue;
182 boundthreads++;
183
184 {
185 unsigned path2len = pathlen + 1 + 21 + 1 + 4 + 1;
186 char *path2 = malloc(path2len);
187 if (path2) {
188 int commfile;
189 snprintf(path2, path2len, "%s/%ld/comm", path, tid);
190 commfile = open(path2, O_RDWR);
191 if (commfile >= 0) {
192 read(commfile, tids[i-1].name, TIDNAME_MAX);
193 close(commfile);
194 tids[i-1].name[TIDNAME_MAX-1] = '\0';
195 end = strchr(tids[i-1].name, '\n');
196 if (end)
197 *end = '\0';
198 }
199 free(path2);
200 }
201 }
202
203 if (i == n)
204 /* ignore the lastly created threads, we need cpuset==NULL if the last slot */
205 break;
206 }
207 } else {
208 /* failed to alloc, behave as if there were no threads */
209 }
210 }
211 closedir(taskdir);
212 }
213 free(path);
214 #endif /* HWLOC_LINUX_SYS */
215 }
216
217 if (get_last_cpu_location) {
218 if (hwloc_get_proc_last_cpu_location(topology, pid, cpuset, 0))
219 goto out;
220 } else {
221 if (hwloc_get_proc_cpubind(topology, pid, cpuset, 0))
222 goto out;
223 }
224
225 hwloc_bitmap_and(cpuset, cpuset, topocpuset);
226 if (hwloc_bitmap_iszero(cpuset))
227 goto out;
228
229 /* don't print anything if the process isn't bound and if no threads are bound and if not showing all */
230 if (hwloc_bitmap_isequal(cpuset, topocpuset) && (!tids || !boundthreads) && !show_all && only_pid == NO_ONLY_PID && !only_name)
231 goto out;
232
233 pidoutput[0] = '\0';
234 if (pidcmd) {
235 char *cmd;
236 FILE *file;
237 cmd = malloc(strlen(pidcmd)+1+5+2+1);
238 sprintf(cmd, "%s %u", pidcmd, (unsigned) pid);
239 file = popen(cmd, "r");
240 if (file) {
241 if (fgets(pidoutput, sizeof(pidoutput), file)) {
242 end = strchr(pidoutput, '\n');
243 if (end)
244 *end = '\0';
245 }
246 pclose(file);
247 }
248 free(cmd);
249 }
250
251 /* print the process */
252 print_task(topology, pid, name, cpuset, pidoutput[0] == '\0' ? NULL : pidoutput, 0);
253 if (tids)
254 /* print each tid we found with a cpuset, the n+1-th is NULL */
255 for(i=0; tids[i].cpuset != NULL; i++) {
256 print_task(topology, tids[i].tid, tids[i].name, tids[i].cpuset, NULL, 1);
257 hwloc_bitmap_free(tids[i].cpuset);
258 }
259
260 out:
261 /* free threads stuff */
262 free(tids);
263 hwloc_bitmap_free(cpuset);
264 }
265
main(int argc,char * argv[])266 int main(int argc, char *argv[])
267 {
268 const struct hwloc_topology_support *support;
269 hwloc_topology_t topology;
270 hwloc_const_bitmap_t topocpuset;
271 unsigned long flags = 0;
272 DIR *dir;
273 struct dirent *dirent;
274 char *callname;
275 int err;
276 int opt;
277
278 callname = strrchr(argv[0], '/');
279 if (!callname)
280 callname = argv[0];
281 else
282 callname++;
283 /* skip argv[0], handle options */
284 argc--;
285 argv++;
286
287 while (argc >= 1) {
288 opt = 0;
289 if (!strcmp(argv[0], "-a"))
290 show_all = 1;
291 else if (!strcmp(argv[0], "-l") || !strcmp(argv[0], "--logical")) {
292 logical = 1;
293 } else if (!strcmp(argv[0], "-p") || !strcmp(argv[0], "--physical")) {
294 logical = 0;
295 } else if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--cpuset")) {
296 show_cpuset = 1;
297 } else if (!strcmp(argv[0], "-e") || !strncmp(argv[0], "--get-last-cpu-location", 10)) {
298 get_last_cpu_location = 1;
299 } else if (!strcmp(argv[0], "-t") || !strcmp(argv[0], "--threads")) {
300 #ifdef HWLOC_LINUX_SYS
301 show_threads = 1;
302 #else
303 fprintf (stderr, "Listing threads is currently only supported on Linux\n");
304 #endif
305 } else if (!strcmp(argv[0], "--pid")) {
306 if (argc < 2) {
307 usage(callname, stdout);
308 exit(EXIT_FAILURE);
309 }
310 only_pid = strtol(argv[1], NULL, 10);
311 opt = 1;
312 } else if (!strcmp(argv[0], "--name")) {
313 if (argc < 2) {
314 usage(callname, stdout);
315 exit(EXIT_FAILURE);
316 }
317 only_name = argv[1];
318 opt = 1;
319 } else if (!strcmp (argv[0], "--whole-system")) {
320 flags |= HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM;
321 } else if (!strcmp (argv[0], "--pid-cmd")) {
322 if (argc < 2) {
323 usage(callname, stdout);
324 exit(EXIT_FAILURE);
325 }
326 pidcmd = argv[1];
327 opt = 1;
328 } else {
329 fprintf (stderr, "Unrecognized option: %s\n", argv[0]);
330 usage (callname, stderr);
331 exit(EXIT_FAILURE);
332 }
333 argc -= opt+1;
334 argv += opt+1;
335 }
336
337 err = hwloc_topology_init(&topology);
338 if (err)
339 goto out;
340
341 hwloc_topology_set_flags(topology, flags);
342
343 err = hwloc_topology_load(topology);
344 if (err)
345 goto out_with_topology;
346
347 support = hwloc_topology_get_support(topology);
348
349 if (get_last_cpu_location) {
350 if (!support->cpubind->get_proc_last_cpu_location)
351 goto out_with_topology;
352 } else {
353 if (!support->cpubind->get_proc_cpubind)
354 goto out_with_topology;
355 }
356
357 topocpuset = hwloc_topology_get_topology_cpuset(topology);
358
359 dir = opendir("/proc");
360 if (!dir)
361 goto out_with_topology;
362
363 if (only_pid == NO_ONLY_PID) {
364 /* show all */
365 while ((dirent = readdir(dir))) {
366 long pid;
367 char *end;
368
369 pid = strtol(dirent->d_name, &end, 10);
370 if (*end)
371 /* Not a number */
372 continue;
373
374 one_process(topology, topocpuset, pid);
375 }
376 } else {
377 /* show only one */
378 one_process(topology, topocpuset, only_pid);
379 }
380
381 err = 0;
382
383 closedir(dir);
384 out_with_topology:
385 hwloc_topology_destroy(topology);
386 out:
387 return err;
388 }
389