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