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# 7# USAGE: uthreads [-l {c,java,none}] [-v] pid 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 14from __future__ import print_function 15import argparse 16from bcc import BPF, USDT, utils 17import ctypes as ct 18import time 19import os 20 21languages = ["c", "java"] 22 23examples = """examples: 24 ./uthreads -l java 185 # trace Java threads in process 185 25 ./uthreads -l none 12245 # trace only pthreads in process 12245 26""" 27parser = argparse.ArgumentParser( 28 description="Trace thread creation/destruction events in " + 29 "high-level languages.", 30 formatter_class=argparse.RawDescriptionHelpFormatter, 31 epilog=examples) 32parser.add_argument("-l", "--language", choices=languages + ["none"], 33 help="language to trace (none for pthreads only)") 34parser.add_argument("pid", type=int, help="process id to attach to") 35parser.add_argument("-v", "--verbose", action="store_true", 36 help="verbose mode: print the BPF program (for debugging purposes)") 37parser.add_argument("--ebpf", action="store_true", 38 help=argparse.SUPPRESS) 39args = parser.parse_args() 40 41usdt = USDT(pid=args.pid) 42 43program = """ 44struct thread_event_t { 45 u64 runtime_id; 46 u64 native_id; 47 char type[8]; 48 char name[80]; 49}; 50 51BPF_PERF_OUTPUT(threads); 52 53int trace_pthread(struct pt_regs *ctx) { 54 struct thread_event_t te = {}; 55 u64 start_routine = 0; 56 char type[] = "pthread"; 57 te.native_id = bpf_get_current_pid_tgid() & 0xFFFFFFFF; 58 bpf_usdt_readarg(2, ctx, &start_routine); 59 te.runtime_id = start_routine; // This is really a function pointer 60 __builtin_memcpy(&te.type, type, sizeof(te.type)); 61 threads.perf_submit(ctx, &te, sizeof(te)); 62 return 0; 63} 64""" 65usdt.enable_probe_or_bail("pthread_start", "trace_pthread") 66 67language = args.language 68if not language: 69 language = utils.detect_language(languages, args.pid) 70 71if language == "c": 72 # Nothing to add 73 pass 74elif language == "java": 75 template = """ 76int %s(struct pt_regs *ctx) { 77 char type[] = "%s"; 78 struct thread_event_t te = {}; 79 u64 nameptr = 0, id = 0, native_id = 0; 80 bpf_usdt_readarg(1, ctx, &nameptr); 81 bpf_usdt_readarg(3, ctx, &id); 82 bpf_usdt_readarg(4, ctx, &native_id); 83 bpf_probe_read(&te.name, sizeof(te.name), (void *)nameptr); 84 te.runtime_id = id; 85 te.native_id = native_id; 86 __builtin_memcpy(&te.type, type, sizeof(te.type)); 87 threads.perf_submit(ctx, &te, sizeof(te)); 88 return 0; 89} 90 """ 91 program += template % ("trace_start", "start") 92 program += template % ("trace_stop", "stop") 93 usdt.enable_probe_or_bail("thread__start", "trace_start") 94 usdt.enable_probe_or_bail("thread__stop", "trace_stop") 95 96if args.ebpf or args.verbose: 97 if args.verbose: 98 print(usdt.get_text()) 99 print(program) 100 if args.ebpf: 101 exit() 102 103bpf = BPF(text=program, usdt_contexts=[usdt]) 104print("Tracing thread events in process %d (language: %s)... Ctrl-C to quit." % 105 (args.pid, language or "none")) 106print("%-8s %-16s %-8s %-30s" % ("TIME", "ID", "TYPE", "DESCRIPTION")) 107 108class ThreadEvent(ct.Structure): 109 _fields_ = [ 110 ("runtime_id", ct.c_ulonglong), 111 ("native_id", ct.c_ulonglong), 112 ("type", ct.c_char * 8), 113 ("name", ct.c_char * 80), 114 ] 115 116start_ts = time.time() 117 118def print_event(cpu, data, size): 119 event = ct.cast(data, ct.POINTER(ThreadEvent)).contents 120 name = event.name 121 if event.type == "pthread": 122 name = bpf.sym(event.runtime_id, args.pid, show_module=True) 123 tid = event.native_id 124 else: 125 tid = "R=%s/N=%s" % (event.runtime_id, event.native_id) 126 print("%-8.3f %-16s %-8s %-30s" % ( 127 time.time() - start_ts, tid, event.type, name)) 128 129bpf["threads"].open_perf_buffer(print_event) 130while 1: 131 bpf.perf_buffer_poll() 132