Skip to content

Instantly share code, notes, and snippets.

@mfleming
Created September 8, 2021 13:52
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save mfleming/7c6cfd225c305c0e44cc7357cdc481f3 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
#
# Copyright 2021 Matt Fleming
#
# 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.
#
from __future__ import print_function
from bcc import BPF
import argparse
import sys
import time
import os
import signal
source="""
#include <uapi/linux/ptrace.h>
#include <linux/ktime.h>
#include <linux/hrtimer.h>
#include <linux/timer.h>
struct cancel_event {
u64 expires;
u64 now;
u64 timer;
};
BPF_ARRAY(events, struct cancel_event, 1);
BPF_PERCPU_ARRAY(clock_base, struct hrtimer_clock_base, 1);
int hrtimer_cancel_probe(struct hrtimer *timer)
{
struct cancel_event *existing;
u64 now;
u32 pid;
// Only trace the user-supplied pid
pid = bpf_get_current_pid_tgid();
if (pid != TARGET_PID)
return 0;
int key = 0;
existing = events.lookup(&key);
if (existing != 0 && existing->expires == 0) {
existing->expires = ktime_to_ns(hrtimer_get_softexpires(timer));
struct hrtimer_clock_base *base;
base = clock_base.lookup(&key);
if (base != 0) {
bpf_probe_read(base, sizeof(*base), timer->base);
existing->timer = base->clockid;
}
}
return 0;
}
int do_futex_return(struct pt_regs *ctx, u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time)
{
struct cancel_event *existing;
u32 pid;
// Only trace the user-supplied pid
pid = bpf_get_current_pid_tgid();
if (pid != TARGET_PID)
return 0;
int key = 0;
existing = events.lookup(&key);
if (existing != 0 && existing->expires != 0) {
ktime_t t;
bpf_probe_read(&t, sizeof(t), abs_time);
existing->timer = t;
}
return 0;
}
"""
def pretty_print(time_ns):
"""
Take a relative time in nanoseconds and print a human readable
string.
"""
final_string = ""
suffix = ""
if time_ns <= 1000:
suffix = "ns"
if time_ns <= 1000000:
time_ns /= 1000
suffix = "us"
if time_ns <= 1000000000:
time_ns /= 1000000000
suffix = "ms"
if time_ns <= 1000000000000:
time_ns /= 1000000000000
suffix = "s"
if time_ns <= 1000000000000000:
time_ns /= 1000000000000000
suffix = "mins"
print(str(time_ns) + suffix)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="Print hrtimer_cancel calls for a pid/tid",
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("pid", help="pid/tid to trace")
args = parser.parse_args()
print("Tracing pid " + args.pid + "...")
b = BPF(text=source.replace("TARGET_PID", args.pid))
b.attach_kprobe(event="hrtimer_cancel", fn_name="hrtimer_cancel_probe")
b.attach_kprobe(event="futex_wait", fn_name="do_futex_return")
time.sleep(1)
print("Sending SIGSTOP")
os.kill(int(args.pid), signal.SIGSTOP)
os.kill(int(args.pid), signal.SIGCONT)
time.sleep(5)
events = b.get_table("events")
print("")
for k, v in events.items():
print(str(v.expires) + " expires")
sys.exit(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment