From: Mark Drayton Date: Wed, 20 Apr 2016 21:11:00 +0000 (-0700) Subject: Lua ports of biosnoop and stacksnoop X-Git-Tag: v0.2.0~87^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=75291d006fc2486a5208fa206c05fc7e33defd4c;p=platform%2Fupstream%2Fbcc.git Lua ports of biosnoop and stacksnoop --- diff --git a/tools/biosnoop.lua b/tools/biosnoop.lua new file mode 100644 index 0000000..ac08897 --- /dev/null +++ b/tools/biosnoop.lua @@ -0,0 +1,183 @@ +#!/usr/bin/env bcc-lua +--[[ +Copyright 2016 GitHub, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--]] + +local program = [[ +#include +#include + +struct val_t { + u32 pid; + char name[TASK_COMM_LEN]; +}; + +struct data_t { + u32 pid; + u64 rwflag; + u64 delta; + u64 sector; + u64 len; + u64 ts; + char disk_name[DISK_NAME_LEN]; + char name[TASK_COMM_LEN]; +}; + +BPF_HASH(start, struct request *); +BPF_HASH(infobyreq, struct request *, struct val_t); +BPF_PERF_OUTPUT(events); + +// cache PID and comm by-req +int trace_pid_start(struct pt_regs *ctx, struct request *req) +{ + struct val_t val = {}; + + if (bpf_get_current_comm(&val.name, sizeof(val.name)) == 0) { + val.pid = bpf_get_current_pid_tgid(); + infobyreq.update(&req, &val); + } + return 0; +} + +// time block I/O +int trace_req_start(struct pt_regs *ctx, struct request *req) +{ + u64 ts; + + ts = bpf_ktime_get_ns(); + start.update(&req, &ts); + + return 0; +} + +// output +int trace_req_completion(struct pt_regs *ctx, struct request *req) +{ + u64 *tsp, delta; + u32 *pidp = 0; + struct val_t *valp; + struct data_t data ={}; + u64 ts; + + // fetch timestamp and calculate delta + tsp = start.lookup(&req); + if (tsp == 0) { + // missed tracing issue + return 0; + } + ts = bpf_ktime_get_ns(); + data.delta = ts - *tsp; + data.ts = ts / 1000; + + valp = infobyreq.lookup(&req); + if (valp == 0) { + data.len = req->__data_len; + strcpy(data.name,"?"); + } else { + data.pid = valp->pid; + data.len = req->__data_len; + data.sector = req->__sector; + bpf_probe_read(&data.name, sizeof(data.name), valp->name); + bpf_probe_read(&data.disk_name, sizeof(data.disk_name), + req->rq_disk->disk_name); + } + + if (req->cmd_flags & REQ_WRITE) { + data.rwflag=1; + } else { + data.rwflag=0; + } + events.perf_submit(ctx,&data,sizeof(data)); + start.delete(&req); + infobyreq.delete(&req); + + return 0; +} +]] + +local ffi = require("ffi") + +return function(BPF, utils) + local bpf = BPF:new{text=program} + + bpf:attach_kprobe{event="blk_account_io_start", fn_name="trace_pid_start"} + bpf:attach_kprobe{event="blk_start_request", fn_name="trace_req_start"} + bpf:attach_kprobe{event="blk_mq_start_request", fn_name="trace_req_start"} + bpf:attach_kprobe{event="blk_account_io_completion", + fn_name="trace_req_completion"} + + print("%-14s %-14s %-6s %-7s %-2s %-9s %-7s %7s" % {"TIME(s)", "COMM", "PID", + "DISK", "T", "SECTOR", "BYTES", "LAT(ms)"}) + + local rwflg = "" + local start_ts = 0 + local prev_ts = 0 + local delta = 0 + + local function print_event(cpu, event) + local val = -1 + local event_pid = event.pid + local event_delta = tonumber(event.delta) + local event_sector = tonumber(event.sector) + local event_len = tonumber(event.len) + local event_ts = tonumber(event.ts) + local event_disk_name = ffi.string(event.disk_name) + local event_name = ffi.string(event.name) + + if event.rwflag == 1 then + rwflg = "W" + end + + if event.rwflag == 0 then + rwflg = "R" + end + + if not event_name:match("%?") then + val = event_sector + end + + if start_ts == 0 then + prev_ts = start_ts + end + + if start_ts == 1 then + delta = delta + (event_ts - prev_ts) + end + + print("%-14.9f %-14.14s %-6s %-7s %-2s %-9s %-7s %7.2f" % { + delta / 1000000, event_name, event_pid, event_disk_name, rwflg, val, + event_len, event_delta / 1000000}) + + prev_ts = event_ts + start_ts = 1 + end + + local TASK_COMM_LEN = 16 -- linux/sched.h + local DISK_NAME_LEN = 32 -- linux/genhd.h + + bpf:get_table("events"):open_perf_buffer(print_event, [[ + struct { + uint32_t pid; + uint64_t rwflag; + uint64_t delta; + uint64_t sector; + uint64_t len; + uint64_t ts; + char disk_name[%d]; + char name[%d]; + } + ]] % {DISK_NAME_LEN, TASK_COMM_LEN}) + bpf:kprobe_poll_loop() +end diff --git a/tools/stacksnoop.lua b/tools/stacksnoop.lua new file mode 100644 index 0000000..588b814 --- /dev/null +++ b/tools/stacksnoop.lua @@ -0,0 +1,92 @@ +#!/usr/bin/env bcc-lua +--[[ +Copyright 2016 GitHub, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--]] + +local program = [[ +#include + +BPF_STACK_TRACE(stack_traces, 128) + +void trace_stack(struct pt_regs *ctx) { + FILTER + int stack_id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID); + if (stack_id >= 0) + bpf_trace_printk("stack_id=%d\n", stack_id); +} +]] + +return function(BPF, utils) + local parser = utils.argparse("stacksnoop", + "Trace and print kernel stack traces for a kernel function") + parser:flag("-s --offset") + parser:flag("-v --verbose") + parser:option("-p --pid"):convert(tonumber) + parser:argument("function", "kernel function name"):target("fn") + + local args = parser:parse() + local ksym = BPF.SymbolCache() + local filter = "" + + if args.pid then + filter = [[ + u32 pid; + pid = bpf_get_current_pid_tgid(); + if (pid != %d) { return; } + ]] % args.pid + end + + local text = program:gsub("FILTER", filter) + local bpf = BPF:new{text=text} + bpf:attach_kprobe{event=args.fn, fn_name="trace_stack"} + + if BPF.num_open_kprobes() == 0 then + print("Function \"%s\" not found. Exiting." % args.fn) + return + end + + if args.verbose then + print("%-18s %-12s %-6s %-3s %s" % + {"TIME(s)", "COMM", "PID", "CPU", "SYSCALL"}) + else + print("%-18s %s" % {"TIME(s)", "SYSCALL"}) + end + + local stack_traces = bpf:get_table("stack_traces") + local pipe = bpf:pipe() + + while true do + local task, pid, cpu, flags, ts, msg = pipe:trace_fields() + local stack_id = string.match(msg, "stack_id=(%d+)") + + if stack_id then + if args.verbose then + print("%-18.9f %-12.12s %-6d %-3d %s" % {ts, task, pid, cpu, args.fn}) + else + print("%-18.9f %s" % {ts, args.fn}) + end + + for addr in stack_traces:walk(tonumber(stack_id)) do + local sym, offset = ksym:resolve(addr) + if args.offset then + print("\t%-16p %s+0x%x" % {addr, sym, tonumber(offset)}) + else + print("\t%-16p %s" % {addr, sym}) + end + end + end + print() + end +end