Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| #!/usr/bin/python | |
| # | |
| # mysqld_qslower MySQL server queries slower than a threshold. | |
| # For Linux, uses BCC, BPF. Embedded C. | |
| # | |
| # USAGE: mysqld_qslower PID [min_ms] | |
| # | |
| # By default, a threshold of 1.0 ms is used. Set this to 0 ms to trace all | |
| # queries (verbose). | |
| # | |
| # This uses USDT probes, and needs a MySQL server with -DENABLE_DTRACE=1. | |
| # | |
| # Copyright 2016 Netflix, Inc. | |
| # Licensed under the Apache License, Version 2.0 (the "License") | |
| # | |
| # 30-Jul-2016 Brendan Gregg Created this. | |
| from __future__ import print_function | |
| from bcc import BPF, USDT | |
| import sys | |
| import ctypes as ct | |
| # arguments | |
| def usage(): | |
| print("USAGE: mysqld_latency PID [min_ms]") | |
| exit() | |
| if len(sys.argv) < 2: | |
| usage() | |
| if sys.argv[1][0:1] == "-": | |
| usage() | |
| pid = int(sys.argv[1]) | |
| min_ns = 1 * 1000000 | |
| min_ms_text = 1 | |
| if len(sys.argv) == 3: | |
| min_ns = float(sys.argv[2]) * 1000000 | |
| min_ms_text = sys.argv[2] | |
| debug = 0 | |
| QUERY_MAX = 128 | |
| # load BPF program | |
| bpf_text = """ | |
| #include <uapi/linux/ptrace.h> | |
| #define QUERY_MAX """ + str(QUERY_MAX) + """ | |
| struct start_t { | |
| u64 ts; | |
| char *query; | |
| }; | |
| struct data_t { | |
| u64 pid; | |
| u64 ts; | |
| u64 delta; | |
| char query[QUERY_MAX]; | |
| }; | |
| BPF_HASH(start_tmp, u32, struct start_t); | |
| BPF_PERF_OUTPUT(events); | |
| int do_start(struct pt_regs *ctx) { | |
| u32 pid = bpf_get_current_pid_tgid(); | |
| struct start_t start = {}; | |
| start.ts = bpf_ktime_get_ns(); | |
| bpf_usdt_readarg(1, ctx, &start.query); | |
| start_tmp.update(&pid, &start); | |
| return 0; | |
| }; | |
| int do_done(struct pt_regs *ctx) { | |
| u32 pid = bpf_get_current_pid_tgid(); | |
| struct start_t *sp; | |
| sp = start_tmp.lookup(&pid); | |
| if (sp == 0) { | |
| // missed tracing start | |
| return 0; | |
| } | |
| // check if query exceeded our threshold | |
| u64 delta = bpf_ktime_get_ns() - sp->ts; | |
| if (delta >= """ + str(min_ns) + """) { | |
| // populate and emit data struct | |
| struct data_t data = {.pid = pid, .ts = sp->ts, .delta = delta}; | |
| bpf_probe_read(&data.query, sizeof(data.query), (void *)sp->query); | |
| events.perf_submit(ctx, &data, sizeof(data)); | |
| } | |
| start_tmp.delete(&pid); | |
| return 0; | |
| }; | |
| """ | |
| # enable USDT probe from given PID | |
| u = USDT(pid=pid) | |
| u.enable_probe(probe="query__start", fn_name="do_start") | |
| u.enable_probe(probe="query__done", fn_name="do_done") | |
| if debug: | |
| print(u.get_text()) | |
| print(bpf_text) | |
| # initialize BPF | |
| b = BPF(text=bpf_text, usdt_contexts=[u]) | |
| # header | |
| print("Tracing MySQL server queries for PID %d slower than %s ms..." % (pid, | |
| min_ms_text)) | |
| print("%-14s %-6s %8s %s" % ("TIME(s)", "PID", "MS", "QUERY")) | |
| class Data(ct.Structure): | |
| _fields_ = [ | |
| ("pid", ct.c_ulonglong), | |
| ("ts", ct.c_ulonglong), | |
| ("delta", ct.c_ulonglong), | |
| ("query", ct.c_char * QUERY_MAX) | |
| ] | |
| # process event | |
| start = 0 | |
| def print_event(cpu, data, size): | |
| global start | |
| event = ct.cast(data, ct.POINTER(Data)).contents | |
| if start == 0: | |
| start = event.ts | |
| print("%-14.6f %-6d %8.3f %s" % (float(event.ts - start) / 1000000000, | |
| event.pid, float(event.delta) / 1000000, event.query)) | |
| # loop with callback to print_event | |
| b["events"].open_perf_buffer(print_event, page_cnt=64) | |
| while 1: | |
| b.kprobe_poll() |