Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # @lint-avoid-python-3-compatibility-imports |
| 3 | # |
| 4 | # uthreads Trace thread creation/destruction events in high-level languages. |
| 5 | # For Linux, uses BCC, eBPF. |
| 6 | # |
Sasha Goldshtein | bf0cf12 | 2016-10-25 04:28:06 -0700 | [diff] [blame] | 7 | # USAGE: uthreads [-l {java}] [-v] pid |
Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 8 | # |
| 9 | # Copyright 2016 Sasha Goldshtein |
| 10 | # Licensed under the Apache License, Version 2.0 (the "License") |
| 11 | # |
| 12 | # 25-Oct-2016 Sasha Goldshtein Created this. |
| 13 | |
| 14 | from __future__ import print_function |
| 15 | import argparse |
| 16 | from bcc import BPF, USDT |
| 17 | import ctypes as ct |
| 18 | import time |
| 19 | |
| 20 | examples = """examples: |
Sasha Goldshtein | bf0cf12 | 2016-10-25 04:28:06 -0700 | [diff] [blame] | 21 | ./uthreads -l java 185 # trace Java threads in process 185 |
| 22 | ./uthreads 12245 # trace only pthreads in process 12245 |
Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 23 | """ |
| 24 | parser = argparse.ArgumentParser( |
| 25 | description="Trace thread creation/destruction events in " + |
| 26 | "high-level languages.", |
| 27 | formatter_class=argparse.RawDescriptionHelpFormatter, |
| 28 | epilog=examples) |
Sasha Goldshtein | bf0cf12 | 2016-10-25 04:28:06 -0700 | [diff] [blame] | 29 | parser.add_argument("-l", "--language", choices=["java"], |
| 30 | help="language to trace (none for pthreads only)") |
Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 31 | parser.add_argument("pid", type=int, help="process id to attach to") |
| 32 | parser.add_argument("-v", "--verbose", action="store_true", |
| 33 | help="verbose mode: print the BPF program (for debugging purposes)") |
| 34 | args = parser.parse_args() |
| 35 | |
| 36 | usdt = USDT(pid=args.pid) |
| 37 | |
| 38 | program = """ |
| 39 | struct thread_event_t { |
| 40 | u64 runtime_id; |
| 41 | u64 native_id; |
| 42 | char type[8]; |
| 43 | char name[80]; |
| 44 | }; |
| 45 | |
| 46 | BPF_PERF_OUTPUT(threads); |
Sasha Goldshtein | bf0cf12 | 2016-10-25 04:28:06 -0700 | [diff] [blame] | 47 | |
| 48 | int trace_pthread(struct pt_regs *ctx) { |
| 49 | struct thread_event_t te = {}; |
| 50 | u64 start_routine = 0; |
| 51 | char type[] = "pthread"; |
| 52 | te.native_id = bpf_get_current_pid_tgid() & 0xFFFFFFFF; |
| 53 | bpf_usdt_readarg(2, ctx, &start_routine); |
| 54 | te.runtime_id = start_routine; // This is really a function pointer |
| 55 | __builtin_memcpy(&te.type, type, sizeof(te.type)); |
| 56 | threads.perf_submit(ctx, &te, sizeof(te)); |
| 57 | return 0; |
| 58 | } |
Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 59 | """ |
Sasha Goldshtein | dc3a57c | 2017-02-08 16:02:11 -0500 | [diff] [blame] | 60 | usdt.enable_probe_or_bail("pthread_start", "trace_pthread") |
Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 61 | |
| 62 | if args.language == "java": |
| 63 | template = """ |
| 64 | int %s(struct pt_regs *ctx) { |
Sasha Goldshtein | bf0cf12 | 2016-10-25 04:28:06 -0700 | [diff] [blame] | 65 | char type[] = "%s"; |
Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 66 | struct thread_event_t te = {}; |
| 67 | u64 nameptr = 0, id = 0, native_id = 0; |
| 68 | bpf_usdt_readarg(1, ctx, &nameptr); |
| 69 | bpf_usdt_readarg(3, ctx, &id); |
| 70 | bpf_usdt_readarg(4, ctx, &native_id); |
| 71 | bpf_probe_read(&te.name, sizeof(te.name), (void *)nameptr); |
| 72 | te.runtime_id = id; |
| 73 | te.native_id = native_id; |
Sasha Goldshtein | bf0cf12 | 2016-10-25 04:28:06 -0700 | [diff] [blame] | 74 | __builtin_memcpy(&te.type, type, sizeof(te.type)); |
Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 75 | threads.perf_submit(ctx, &te, sizeof(te)); |
| 76 | return 0; |
| 77 | } |
| 78 | """ |
| 79 | program += template % ("trace_start", "start") |
| 80 | program += template % ("trace_stop", "stop") |
Sasha Goldshtein | dc3a57c | 2017-02-08 16:02:11 -0500 | [diff] [blame] | 81 | usdt.enable_probe_or_bail("thread__start", "trace_start") |
| 82 | usdt.enable_probe_or_bail("thread__stop", "trace_stop") |
Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 83 | |
| 84 | if args.verbose: |
| 85 | print(usdt.get_text()) |
| 86 | print(program) |
| 87 | |
| 88 | bpf = BPF(text=program, usdt_contexts=[usdt]) |
Sasha Goldshtein | bf0cf12 | 2016-10-25 04:28:06 -0700 | [diff] [blame] | 89 | print("Tracing thread events in process %d (language: %s)... Ctrl-C to quit." % |
| 90 | (args.pid, args.language or "none")) |
Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 91 | print("%-8s %-16s %-8s %-30s" % ("TIME", "ID", "TYPE", "DESCRIPTION")) |
| 92 | |
| 93 | class ThreadEvent(ct.Structure): |
| 94 | _fields_ = [ |
| 95 | ("runtime_id", ct.c_ulonglong), |
| 96 | ("native_id", ct.c_ulonglong), |
| 97 | ("type", ct.c_char * 8), |
| 98 | ("name", ct.c_char * 80), |
| 99 | ] |
| 100 | |
| 101 | start_ts = time.time() |
| 102 | |
| 103 | def print_event(cpu, data, size): |
| 104 | event = ct.cast(data, ct.POINTER(ThreadEvent)).contents |
Sasha Goldshtein | bf0cf12 | 2016-10-25 04:28:06 -0700 | [diff] [blame] | 105 | name = event.name |
| 106 | if event.type == "pthread": |
Sasha Goldshtein | b1bff01 | 2017-02-08 23:25:28 -0500 | [diff] [blame^] | 107 | name = bpf.sym(event.runtime_id, args.pid, show_module=True) |
Sasha Goldshtein | bf0cf12 | 2016-10-25 04:28:06 -0700 | [diff] [blame] | 108 | tid = event.native_id |
| 109 | else: |
| 110 | tid = "R=%s/N=%s" % (event.runtime_id, event.native_id) |
Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 111 | print("%-8.3f %-16s %-8s %-30s" % ( |
Sasha Goldshtein | bf0cf12 | 2016-10-25 04:28:06 -0700 | [diff] [blame] | 112 | time.time() - start_ts, tid, event.type, name)) |
Sasha Goldshtein | 989057d | 2016-10-25 04:08:10 -0700 | [diff] [blame] | 113 | |
| 114 | bpf["threads"].open_perf_buffer(print_event) |
| 115 | while 1: |
| 116 | bpf.kprobe_poll() |