From 27e7aeab5d7b9f0f4259fb0f996274af0521243f Mon Sep 17 00:00:00 2001 From: Marko Myllynen Date: Wed, 26 Sep 2018 20:09:07 +0300 Subject: [PATCH] tools: continue adding --ebpf support (#1986) Use argparse in cachestat, add --ebpf support. Add --ebpf support for u* tools, finalize language sorting. Remove sole --ebpf string on usage line in tcpsubnet. --- tools/cachestat.py | 64 ++++++++++++++++++------------------------- tools/lib/ucalls.py | 8 ++++-- tools/lib/uflow.py | 9 ++++-- tools/lib/ugc.py | 43 ++++++++++++++++------------- tools/lib/uobjnew.py | 45 ++++++++++++++++-------------- tools/lib/ustat.py | 6 +++- tools/lib/uthreads.py | 9 ++++-- tools/tcpsubnet.py | 2 +- 8 files changed, 102 insertions(+), 84 deletions(-) diff --git a/tools/cachestat.py b/tools/cachestat.py index 573c697b6998..b00c80434fa2 100755 --- a/tools/cachestat.py +++ b/tools/cachestat.py @@ -19,6 +19,7 @@ from __future__ import print_function from bcc import BPF from time import sleep, strftime +import argparse import signal import re from sys import argv @@ -48,43 +49,25 @@ def get_meminfo(): hits = 0 debug = 0 -# args -def usage(): - print("USAGE: %s [-T] [ interval [count] ]" % argv[0]) - exit() - # arguments -interval = 5 -count = -1 -tstamp = 0 - -if len(argv) > 1: - if str(argv[1]) == '-T': - tstamp = 1 - -if len(argv) > 1 and tstamp == 0: - try: - if int(argv[1]) > 0: - interval = int(argv[1]) - if len(argv) > 2: - if int(argv[2]) > 0: - count = int(argv[2]) - except: - usage() - -elif len(argv) > 2 and tstamp == 1: - try: - if int(argv[2]) > 0: - interval = int(argv[2]) - if len(argv) >= 4: - if int(argv[3]) > 0: - count = int(argv[3]) - except: - usage() - -# load BPF program +parser = argparse.ArgumentParser( + description="Count cache kernel function calls", + formatter_class=argparse.RawDescriptionHelpFormatter) +parser.add_argument("-T", "--timestamp", action="store_true", + help="include timestamp on output") +parser.add_argument("interval", nargs="?", default=5, + help="output interval, in seconds") +parser.add_argument("count", nargs="?", default=-1, + help="number of outputs") +parser.add_argument("--ebpf", action="store_true", + help=argparse.SUPPRESS) +args = parser.parse_args() +count = int(args.count) +tstamp = args.timestamp +interval = int(args.interval) + +# define BPF program bpf_text = """ - #include struct key_t { u64 ip; @@ -102,6 +85,13 @@ def usage(): } """ + +if debug or args.ebpf: + print(bpf_text) + if args.ebpf: + exit() + +# load BPF program b = BPF(text=bpf_text) b.attach_kprobe(event="add_to_page_cache_lru", fn_name="do_count") b.attach_kprobe(event="mark_page_accessed", fn_name="do_count") @@ -129,7 +119,7 @@ def usage(): # as cleanup can take many seconds, trap Ctrl-C: signal.signal(signal.SIGINT, signal_ignore) - counts = b.get_table("counts") + counts = b["counts"] for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): if re.match(b'mark_page_accessed', b.ksym(k.ip)) is not None: @@ -175,7 +165,7 @@ def usage(): cached = int(mem["Cached"]) / 1024 buff = int(mem["Buffers"]) / 1024 - if tstamp == 1: + if tstamp: print("%-8s " % strftime("%H:%M:%S"), end="") print("%8d %8d %8d %8d %12.0f %10.0f" % (total, misses, hits, mbd, buff, cached)) diff --git a/tools/lib/ucalls.py b/tools/lib/ucalls.py index b2d165c20018..1a0fe219d8a7 100755 --- a/tools/lib/ucalls.py +++ b/tools/lib/ucalls.py @@ -49,6 +49,8 @@ help="verbose mode: print the BPF program (for debugging purposes)") parser.add_argument("-m", "--milliseconds", action="store_true", help="report times in milliseconds (default is microseconds)") +parser.add_argument("--ebpf", action="store_true", + help=argparse.SUPPRESS) args = parser.parse_args() language = args.language @@ -243,10 +245,12 @@ else: usdt = None -if args.verbose: - if usdt: +if args.ebpf or args.verbose: + if args.verbose and usdt: print(usdt.get_text()) print(program) + if args.ebpf: + exit() bpf = BPF(text=program, usdt_contexts=[usdt] if usdt else []) if args.syscalls: diff --git a/tools/lib/uflow.py b/tools/lib/uflow.py index 8419c8822bc2..fb306e31d018 100755 --- a/tools/lib/uflow.py +++ b/tools/lib/uflow.py @@ -39,6 +39,8 @@ help="trace only calls to classes starting with this prefix") parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode: print the BPF program (for debugging purposes)") +parser.add_argument("--ebpf", action="store_true", + help=argparse.SUPPRESS) args = parser.parse_args() usdt = USDT(pid=args.pid) @@ -165,9 +167,12 @@ def enable_probe(probe_name, func_name, read_class, read_method, is_return): print("No language detected; use -l to trace a language.") exit(1) -if args.verbose: - print(usdt.get_text()) +if args.ebpf or args.verbose: + if args.verbose: + print(usdt.get_text()) print(program) + if args.ebpf: + exit() bpf = BPF(text=program, usdt_contexts=[usdt]) print("Tracing method calls in %s process %d... Ctrl-C to quit." % diff --git a/tools/lib/ugc.py b/tools/lib/ugc.py index 9f4d2582f0d5..828891082766 100755 --- a/tools/lib/ugc.py +++ b/tools/lib/ugc.py @@ -4,7 +4,7 @@ # ugc Summarize garbage collection events in high-level languages. # For Linux, uses BCC, eBPF. # -# USAGE: ugc [-v] [-m] [-M MSEC] [-F FILTER] {java,python,ruby,node} pid +# USAGE: ugc [-v] [-m] [-M MSEC] [-F FILTER] {java,node,python,ruby} pid # # Copyright 2016 Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") @@ -18,7 +18,7 @@ import time import os -languages = ["java", "python", "ruby", "node"] +languages = ["java", "node", "python", "ruby"] examples = """examples: ./ugc -l java 185 # trace Java GCs in process 185 @@ -40,6 +40,8 @@ help="display only GCs longer than this many milliseconds") parser.add_argument("-F", "--filter", type=str, help="display only GCs whose description contains this text") +parser.add_argument("--ebpf", action="store_true", + help=argparse.SUPPRESS) args = parser.parse_args() usdt = USDT(pid=args.pid) @@ -150,6 +152,21 @@ def formatter(e): probes.append(Probe("gc__begin", "gc__end", "", "", lambda _: "no additional info available")) # +# Node +# +elif language == "node": + end_save = """ + u32 gc_type = 0; + bpf_usdt_readarg(1, ctx, &gc_type); + event.field1 = gc_type; + """ + descs = {"GC scavenge": 1, "GC mark-sweep-compact": 2, + "GC incremental mark": 4, "GC weak callbacks": 8} + probes.append(Probe("gc__start", "gc__done", "", end_save, + lambda e: str.join(", ", + [desc for desc, val in descs.items() + if e.field1 & val != 0]))) +# # Python # elif language == "python": @@ -179,21 +196,6 @@ def formatter(event): "", "", lambda _: "GC mark stage")) probes.append(Probe("gc__sweep__begin", "gc__sweep__end", "", "", lambda _: "GC sweep stage")) -# -# Node -# -elif language == "node": - end_save = """ - u32 gc_type = 0; - bpf_usdt_readarg(1, ctx, &gc_type); - event.field1 = gc_type; - """ - descs = {"GC scavenge": 1, "GC mark-sweep-compact": 2, - "GC incremental mark": 4, "GC weak callbacks": 8} - probes.append(Probe("gc__start", "gc__done", "", end_save, - lambda e: str.join(", ", - [desc for desc, val in descs.items() - if e.field1 & val != 0]))) else: print("No language detected; use -l to trace a language.") @@ -204,9 +206,12 @@ def formatter(event): program += probe.generate() probe.attach() -if args.verbose: - print(usdt.get_text()) +if args.ebpf or args.verbose: + if args.verbose: + print(usdt.get_text()) print(program) + if args.ebpf: + exit() bpf = BPF(text=program, usdt_contexts=[usdt]) print("Tracing garbage collections in %s process %d... Ctrl-C to quit." % diff --git a/tools/lib/uobjnew.py b/tools/lib/uobjnew.py index bcd69b433f59..e3b61fff7356 100755 --- a/tools/lib/uobjnew.py +++ b/tools/lib/uobjnew.py @@ -4,7 +4,7 @@ # uobjnew Summarize object allocations in high-level languages. # For Linux, uses BCC, eBPF. # -# USAGE: uobjnew [-h] [-T TOP] [-v] {java,ruby,c} pid [interval] +# USAGE: uobjnew [-h] [-T TOP] [-v] {c,java,ruby} pid [interval] # # Copyright 2016 Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") @@ -18,7 +18,7 @@ import os # C needs to be the last language. -languages = ["java", "ruby", "c"] +languages = ["c", "java", "ruby"] examples = """examples: ./uobjnew -l java 145 # summarize Java allocations in process 145 @@ -41,6 +41,8 @@ help="number of largest types by allocated bytes to print") parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode: print the BPF program (for debugging purposes)") +parser.add_argument("--ebpf", action="store_true", + help=argparse.SUPPRESS) args = parser.parse_args() language = args.language @@ -68,10 +70,25 @@ usdt = USDT(pid=args.pid) +# +# C +# +if language == "c": + program += """ +int alloc_entry(struct pt_regs *ctx, size_t size) { + struct key_t key = {}; + struct val_t *valp, zero = {}; + key.size = size; + valp = allocs.lookup_or_init(&key, &zero); + valp->total_size += size; + valp->num_allocs += 1; + return 0; +} + """ # # Java # -if language == "java": +elif language == "java": program += """ int alloc_entry(struct pt_regs *ctx) { struct key_t key = {}; @@ -120,30 +137,18 @@ program += create_template.replace("THETHING", thing) usdt.enable_probe_or_bail("%s__create" % thing, "%s_alloc_entry" % thing) -# -# C -# -elif language == "c": - program += """ -int alloc_entry(struct pt_regs *ctx, size_t size) { - struct key_t key = {}; - struct val_t *valp, zero = {}; - key.size = size; - valp = allocs.lookup_or_init(&key, &zero); - valp->total_size += size; - valp->num_allocs += 1; - return 0; -} - """ else: print("No language detected; use -l to trace a language.") exit(1) -if args.verbose: - print(usdt.get_text()) +if args.ebpf or args.verbose: + if args.verbose: + print(usdt.get_text()) print(program) + if args.ebpf: + exit() bpf = BPF(text=program, usdt_contexts=[usdt]) if language == "c": diff --git a/tools/lib/ustat.py b/tools/lib/ustat.py index 8b2f80fe4803..7ac0967ee0d4 100755 --- a/tools/lib/ustat.py +++ b/tools/lib/ustat.py @@ -147,6 +147,8 @@ def _parse_args(self): help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, type=int, help="number of outputs") + parser.add_argument("--ebpf", action="store_true", + help=argparse.SUPPRESS) self.args = parser.parse_args() def _create_probes(self): @@ -197,8 +199,10 @@ def _create_probes(self): def _attach_probes(self): program = str.join('\n', [p.get_program() for p in self.probes]) - if self.args.debug: + if self.args.debug or self.args.ebpf: print(program) + if self.args.ebpf: + exit() for probe in self.probes: print("Attached to %s processes:" % probe.language, str.join(', ', map(str, probe.targets))) diff --git a/tools/lib/uthreads.py b/tools/lib/uthreads.py index a96750fdd896..fb5459919614 100755 --- a/tools/lib/uthreads.py +++ b/tools/lib/uthreads.py @@ -34,6 +34,8 @@ parser.add_argument("pid", type=int, help="process id to attach to") parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode: print the BPF program (for debugging purposes)") +parser.add_argument("--ebpf", action="store_true", + help=argparse.SUPPRESS) args = parser.parse_args() usdt = USDT(pid=args.pid) @@ -88,9 +90,12 @@ usdt.enable_probe_or_bail("thread__start", "trace_start") usdt.enable_probe_or_bail("thread__stop", "trace_stop") -if args.verbose: - print(usdt.get_text()) +if args.ebpf or args.verbose: + if args.verbose: + print(usdt.get_text()) print(program) + if args.ebpf: + exit() bpf = BPF(text=program, usdt_contexts=[usdt]) print("Tracing thread events in process %d (language: %s)... Ctrl-C to quit." % diff --git a/tools/tcpsubnet.py b/tools/tcpsubnet.py index 328f426cc4be..5f2a8062bdff 100755 --- a/tools/tcpsubnet.py +++ b/tools/tcpsubnet.py @@ -4,7 +4,7 @@ # tcpsubnet Summarize TCP bytes sent to different subnets. # For Linux, uses BCC, eBPF. Embedded C. # -# USAGE: tcpsubnet [-h] [-v] [--ebpf] [-J] [-f FORMAT] [-i INTERVAL] [subnets] +# USAGE: tcpsubnet [-h] [-v] [-J] [-f FORMAT] [-i INTERVAL] [subnets] # # This uses dynamic tracing of kernel functions, and will need to be updated # to match kernel changes.