Commit a67ef8aa authored by 4ast's avatar 4ast

Merge pull request #539 from markdrayton/lua-tools

Lua ports of biosnoop and stacksnoop
parents 10a89eaf 75291d00
#!/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 <uapi/linux/ptrace.h>
#include <linux/blkdev.h>
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
#!/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 <uapi/linux/ptrace.h>
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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment