./offcputime 5 # trace for 5 seconds only
./offcputime -f 5 # 5 seconds, and output in folded format
./offcputime -p 185 # only trace threads for PID 185
+ ./offcputime -t 188 # only trace thread 188
./offcputime -u # only trace user threads (no kernel)
./offcputime -k # only trace kernel threads (no user)
./offcputime -U # only show user space stacks (no kernel)
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=examples)
thread_group = parser.add_mutually_exclusive_group()
-thread_group.add_argument("-p", "--pid", type=positive_int,
- help="trace this PID only")
+# Note: this script provides --pid and --tid flags but their arguments are
+# referred to internally using kernel nomenclature: TGID and PID.
+thread_group.add_argument("-p", "--pid", metavar="PID", dest="tgid",
+ help="trace this PID only", type=positive_int)
+thread_group.add_argument("-t", "--tid", metavar="TID", dest="pid",
+ help="trace this TID only", type=positive_int)
thread_group.add_argument("-u", "--user-threads-only", action="store_true",
help="user threads only (no kernel threads)")
thread_group.add_argument("-k", "--kernel-threads-only", action="store_true",
help="output folded format")
parser.add_argument("--stack-storage-size", default=1024,
type=positive_nonzero_int,
- help="the number of unique stack traces that can be stored and " \
- "displayed (default 1024)")
+ help="the number of unique stack traces that can be stored and "
+ "displayed (default 1024)")
parser.add_argument("duration", nargs="?", default=99999999,
type=positive_nonzero_int,
help="duration of trace, in seconds")
args = parser.parse_args()
+if args.pid and args.tgid:
+ parser.error("specify only one of -p and -t")
folded = args.folded
duration = int(args.duration)
struct key_t {
u32 pid;
+ u32 tgid;
int user_stack_id;
int kernel_stack_id;
char name[TASK_COMM_LEN];
int oncpu(struct pt_regs *ctx, struct task_struct *prev) {
u32 pid = prev->pid;
+ u32 tgid = prev->tgid;
u64 ts, *tsp;
// record previous thread sleep time
// get the current thread's start time
pid = bpf_get_current_pid_tgid();
+ tgid = bpf_get_current_pid_tgid() >> 32;
tsp = start.lookup(&pid);
if (tsp == 0) {
return 0; // missed start or filtered
struct key_t key = {};
key.pid = pid;
+ key.tgid = tgid;
key.user_stack_id = USER_STACK_GET;
key.kernel_stack_id = KERNEL_STACK_GET;
bpf_get_current_comm(&key.name, sizeof(key.name));
# set thread filter
thread_context = ""
-if args.pid is not None:
- thread_context = "PID %s" % args.pid
- thread_filter = 'pid == %s' % args.pid
+if args.tgid is not None:
+ thread_context = "PID %d" % args.tgid
+ thread_filter = 'tgid == %d' % args.tgid
+elif args.pid is not None:
+ thread_context = "TID %d" % args.pid
+ thread_filter = 'pid == %d' % args.pid
elif args.user_threads_only:
thread_context = "user threads"
thread_filter = '!(prev->flags & PF_KTHREAD)'
has_enomem = True
continue
+ # user stacks will be symbolized by tgid, not pid, to avoid the overhead
+ # of one symbol resolver per thread
user_stack = [] if k.user_stack_id < 0 else \
stack_traces.walk(k.user_stack_id)
kernel_stack = [] if k.kernel_stack_id < 0 else \
user_stack = list(user_stack)
kernel_stack = list(kernel_stack)
line = [k.name.decode()] + \
- [b.sym(addr, k.pid) for addr in reversed(user_stack)] + \
+ [b.sym(addr, k.tgid) for addr in reversed(user_stack)] + \
(need_delimiter and ["-"] or []) + \
[b.ksym(addr) for addr in reversed(kernel_stack)]
print("%s %d" % (";".join(line), v.value))
if need_delimiter:
print(" --")
for addr in user_stack:
- print(" %016x %s" % (addr, b.sym(addr, k.pid)))
+ print(" %016x %s" % (addr, b.sym(addr, k.tgid)))
print(" %-16s %s (%d)" % ("-", k.name, k.pid))
print(" %d\n" % v.value)
USAGE message:
# ./offcputime -h
-usage: offcputime.py [-h] [-p PID | -k | -u] [-K | -U] [-f]
+usage: offcputime.py [-h] [-p PID | -t TID | -u | -k] [-U | -K] [-d] [-f]
[--stack-storage-size STACK_STORAGE_SIZE]
[duration]
optional arguments:
-h, --help show this help message and exit
-p PID, --pid PID trace this PID only
+ -t TID, --tid TID trace this TID only
-u, --user-threads-only
user threads only (no kernel threads)
-k, --kernel-threads-only
-K, --kernel-stacks-only
show stacks from kernel space only (no user space
stacks)
+ -d, --delimited insert delimiter between kernel/user stacks
-f, --folded output folded format
--stack-storage-size STACK_STORAGE_SIZE
the number of unique stack traces that can be stored
./offcputime 5 # trace for 5 seconds only
./offcputime -f 5 # 5 seconds, and output in folded format
./offcputime -p 185 # only trace threads for PID 185
+ ./offcputime -t 188 # only trace thread 188
./offcputime -u # only trace user threads (no kernel)
./offcputime -k # only trace kernel threads (no user)
./offcputime -U # only show user space stacks (no kernel)