1616#include "trace_helpers.h"
1717
1818static struct env {
19- pid_t pid ;
20- pid_t tid ;
19+ pid_t pids [ MAX_PID_NR ] ;
20+ pid_t tids [ MAX_TID_NR ] ;
2121 bool user_threads_only ;
2222 bool kernel_threads_only ;
2323 int stack_storage_size ;
@@ -28,8 +28,6 @@ static struct env {
2828 int duration ;
2929 bool verbose ;
3030} env = {
31- .pid = -1 ,
32- .tid = -1 ,
3331 .stack_storage_size = 1024 ,
3432 .perf_max_stack_depth = 127 ,
3533 .min_block_time = 1 ,
@@ -52,8 +50,8 @@ const char argp_program_doc[] =
5250" offcputime 5 # trace for 5 seconds only\n"
5351" offcputime -m 1000 # trace only events that last more than 1000 usec\n"
5452" offcputime -M 10000 # trace only events that last less than 10000 usec\n"
55- " offcputime -p 185 # only trace threads for PID 185\n"
56- " offcputime -t 188 # only trace thread 188\n"
53+ " offcputime -p 185,175,165 # only trace threads for PID 185,175,165 \n"
54+ " offcputime -t 188,120,134 # only trace threads 188,120,134 \n"
5755" offcputime -u # only trace user threads (no kernel)\n"
5856" offcputime -k # only trace kernel threads (no user)\n" ;
5957
@@ -62,8 +60,8 @@ const char argp_program_doc[] =
6260#define OPT_STATE 3 /* --state */
6361
6462static const struct argp_option opts [] = {
65- { "pid" , 'p' , "PID" , 0 , "Trace this PID only" , 0 },
66- { "tid" , 't' , "TID" , 0 , "Trace this TID only" , 0 },
63+ { "pid" , 'p' , "PID" , 0 , "Trace these PIDs only, comma-separated list " , 0 },
64+ { "tid" , 't' , "TID" , 0 , "Trace these TIDs only, comma-separated list " , 0 },
6765 { "user-threads-only" , 'u' , NULL , 0 ,
6866 "User threads only (no kernel threads)" , 0 },
6967 { "kernel-threads-only" , 'k' , NULL , 0 ,
@@ -82,9 +80,31 @@ static const struct argp_option opts[] = {
8280 {},
8381};
8482
83+ static int split_pidstr (char * s , char * delim , int max_split , pid_t * pids )
84+ {
85+ char * pid ;
86+ int nr = 0 ;
87+
88+ errno = 0 ;
89+ pid = strtok (s , delim );
90+ while (pid ) {
91+ if (nr >= max_split )
92+ return - ENOBUFS ;
93+
94+ pids [nr ++ ] = strtol (pid , NULL , 10 );
95+ if (errno )
96+ return - errno ;
97+
98+ pid = strtok (NULL , delim );
99+ }
100+
101+ return 0 ;
102+ }
103+
85104static error_t parse_arg (int key , char * arg , struct argp_state * state )
86105{
87106 static int pos_args ;
107+ int ret ;
88108
89109 switch (key ) {
90110 case 'h' :
@@ -94,18 +114,26 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
94114 env .verbose = true;
95115 break ;
96116 case 'p' :
97- errno = 0 ;
98- env .pid = strtol (arg , NULL , 10 );
99- if (errno ) {
100- fprintf (stderr , "invalid PID: %s\n" , arg );
117+ ret = split_pidstr (strdup (arg ), "," , MAX_PID_NR , env .pids );
118+ if (ret ) {
119+ if (ret == - ENOBUFS )
120+ fprintf (stderr , "the number of pid is too big, please "
121+ "increase MAX_PID_NR's value and recompile\n" );
122+ else
123+ fprintf (stderr , "invalid PID: %s\n" , arg );
124+
101125 argp_usage (state );
102126 }
103127 break ;
104128 case 't' :
105- errno = 0 ;
106- env .tid = strtol (arg , NULL , 10 );
107- if (errno || env .tid <= 0 ) {
108- fprintf (stderr , "Invalid TID: %s\n" , arg );
129+ ret = split_pidstr (strdup (arg ), "," , MAX_TID_NR , env .tids );
130+ if (ret ) {
131+ if (ret == - ENOBUFS )
132+ fprintf (stderr , "the number of tid is too big, please "
133+ "increase MAX_TID_NR's value and recompile\n" );
134+ else
135+ fprintf (stderr , "invalid TID: %s\n" , arg );
136+
109137 argp_usage (state );
110138 }
111139 break ;
@@ -281,6 +309,41 @@ static void print_map(struct ksyms *ksyms, struct syms_cache *syms_cache,
281309 free (ip );
282310}
283311
312+ static bool print_header_threads ()
313+ {
314+ int i ;
315+ bool printed = false;
316+
317+ if (env .pids [0 ]) {
318+ printf (" PID [" );
319+ for (i = 0 ; i < MAX_PID_NR && env .pids [i ]; i ++ )
320+ printf ("%d%s" , env .pids [i ], (i < MAX_PID_NR - 1 && env .pids [i + 1 ]) ? ", " : "]" );
321+ printed = true;
322+ }
323+
324+ if (env .tids [0 ]) {
325+ printf (" TID [" );
326+ for (i = 0 ; i < MAX_TID_NR && env .tids [i ]; i ++ )
327+ printf ("%d%s" , env .tids [i ], (i < MAX_TID_NR - 1 && env .tids [i + 1 ]) ? ", " : "]" );
328+ printed = true;
329+ }
330+
331+ return printed ;
332+ }
333+
334+ static void print_headers ()
335+ {
336+ printf ("Tracing off-CPU time (us) of" );
337+
338+ if (!print_header_threads ())
339+ printf (" all threads" );
340+
341+ if (env .duration < 99999999 )
342+ printf (" for %d secs.\n" , env .duration );
343+ else
344+ printf ("... Hit Ctrl-C to end.\n" );
345+ }
346+
284347int main (int argc , char * * argv )
285348{
286349 static const struct argp argp = {
@@ -291,7 +354,9 @@ int main(int argc, char **argv)
291354 struct syms_cache * syms_cache = NULL ;
292355 struct ksyms * ksyms = NULL ;
293356 struct offcputime_bpf * obj ;
294- int err ;
357+ int pids_fd , tids_fd ;
358+ int err , i ;
359+ __u8 val = 0 ;
295360
296361 err = argp_parse (& argp , argc , argv , 0 , NULL , NULL );
297362 if (err )
@@ -314,14 +379,18 @@ int main(int argc, char **argv)
314379 }
315380
316381 /* initialize global data (filtering options) */
317- obj -> rodata -> targ_tgid = env .pid ;
318- obj -> rodata -> targ_pid = env .tid ;
319382 obj -> rodata -> user_threads_only = env .user_threads_only ;
320383 obj -> rodata -> kernel_threads_only = env .kernel_threads_only ;
321384 obj -> rodata -> state = env .state ;
322385 obj -> rodata -> min_block_ns = env .min_block_time ;
323386 obj -> rodata -> max_block_ns = env .max_block_time ;
324387
388+ /* User space PID and TID correspond to TGID and PID in the kernel, respectively */
389+ if (env .pids [0 ])
390+ obj -> rodata -> filter_by_tgid = true;
391+ if (env .tids [0 ])
392+ obj -> rodata -> filter_by_pid = true;
393+
325394 bpf_map__set_value_size (obj -> maps .stackmap ,
326395 env .perf_max_stack_depth * sizeof (unsigned long ));
327396 bpf_map__set_max_entries (obj -> maps .stackmap , env .stack_storage_size );
@@ -331,6 +400,28 @@ int main(int argc, char **argv)
331400 fprintf (stderr , "failed to load BPF programs\n" );
332401 goto cleanup ;
333402 }
403+
404+ if (env .pids [0 ]) {
405+ /* User pids_fd points to the tgids map in the BPF program */
406+ pids_fd = bpf_map__fd (obj -> maps .tgids );
407+ for (i = 0 ; i < MAX_PID_NR && env .pids [i ]; i ++ ) {
408+ if (bpf_map_update_elem (pids_fd , & (env .pids [i ]), & val , BPF_ANY ) != 0 ) {
409+ fprintf (stderr , "failed to init pids map: %s\n" , strerror (errno ));
410+ goto cleanup ;
411+ }
412+ }
413+ }
414+ if (env .tids [0 ]) {
415+ /* User tids_fd points to the pids map in the BPF program */
416+ tids_fd = bpf_map__fd (obj -> maps .pids );
417+ for (i = 0 ; i < MAX_TID_NR && env .tids [i ]; i ++ ) {
418+ if (bpf_map_update_elem (tids_fd , & (env .tids [i ]), & val , BPF_ANY ) != 0 ) {
419+ fprintf (stderr , "failed to init tids map: %s\n" , strerror (errno ));
420+ goto cleanup ;
421+ }
422+ }
423+ }
424+
334425 ksyms = ksyms__load ();
335426 if (!ksyms ) {
336427 fprintf (stderr , "failed to load kallsyms\n" );
@@ -349,11 +440,8 @@ int main(int argc, char **argv)
349440
350441 signal (SIGINT , sig_handler );
351442
352- printf ("Tracing off-CPU time (us)" );
353- if (env .duration < 99999999 )
354- printf (" for %d secs.\n" , env .duration );
355- else
356- printf ("... Hit Ctrl-C to end.\n" );
443+ print_headers ();
444+
357445 /*
358446 * We'll get sleep interrupted when someone presses Ctrl-C (which will
359447 * be "handled" with noop by sig_handler).
0 commit comments