You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
5.7 KiB
176 lines
5.7 KiB
#!/usr/bin/env python
|
|
#
|
|
# USAGE: test_usdt2.py
|
|
#
|
|
# Copyright 2017 Facebook, Inc
|
|
# Licensed under the Apache License, Version 2.0 (the "License")
|
|
|
|
from __future__ import print_function
|
|
from bcc import BPF, USDT
|
|
from unittest import main, TestCase
|
|
from subprocess import Popen, PIPE
|
|
from tempfile import NamedTemporaryFile
|
|
import ctypes as ct
|
|
import inspect
|
|
import os
|
|
import signal
|
|
|
|
class TestUDST(TestCase):
|
|
def setUp(self):
|
|
# Application, minimum, to define three trace points
|
|
app_text = b"""
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include "folly/tracing/StaticTracepoint.h"
|
|
|
|
int main(int argc, char **argv) {
|
|
int t = atoi(argv[1]);
|
|
while (1) {
|
|
FOLLY_SDT(test, probe_point_1, t);
|
|
FOLLY_SDT(test, probe_point_2, t + 1);
|
|
FOLLY_SDT(test, probe_point_3, t + 2);
|
|
sleep(1);
|
|
}
|
|
return 1;
|
|
}
|
|
"""
|
|
# BPF program
|
|
self.bpf_text = """
|
|
#include <uapi/linux/ptrace.h>
|
|
|
|
BPF_PERF_OUTPUT(event1);
|
|
BPF_PERF_OUTPUT(event2);
|
|
BPF_PERF_OUTPUT(event3);
|
|
BPF_PERF_OUTPUT(event4);
|
|
BPF_PERF_OUTPUT(event5);
|
|
BPF_PERF_OUTPUT(event6);
|
|
|
|
int do_trace1(struct pt_regs *ctx) {
|
|
u32 pid = bpf_get_current_pid_tgid();
|
|
int result = 0;
|
|
bpf_usdt_readarg(1, ctx, &result);
|
|
if (FILTER)
|
|
event1.perf_submit(ctx, &result, sizeof(result));
|
|
else
|
|
event4.perf_submit(ctx, &result, sizeof(result));
|
|
return 0;
|
|
};
|
|
int do_trace2(struct pt_regs *ctx) {
|
|
u32 pid = bpf_get_current_pid_tgid();
|
|
int result = 0;
|
|
bpf_usdt_readarg(1, ctx, &result);
|
|
if (FILTER)
|
|
event2.perf_submit(ctx, &result, sizeof(result));
|
|
else
|
|
event5.perf_submit(ctx, &result, sizeof(result));
|
|
return 0;
|
|
}
|
|
int do_trace3(struct pt_regs *ctx) {
|
|
u32 pid = bpf_get_current_pid_tgid();
|
|
int result = 0;
|
|
bpf_usdt_readarg(1, ctx, &result);
|
|
if (FILTER)
|
|
event3.perf_submit(ctx, &result, sizeof(result));
|
|
else
|
|
event6.perf_submit(ctx, &result, sizeof(result));
|
|
return 0;
|
|
}
|
|
"""
|
|
|
|
# Compile and run the application
|
|
self.ftemp = NamedTemporaryFile(delete=False)
|
|
self.ftemp.close()
|
|
comp = Popen(["gcc", "-I", "%s/include" % os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))),
|
|
"-x", "c", "-o", self.ftemp.name, "-"],
|
|
stdin=PIPE)
|
|
comp.stdin.write(app_text)
|
|
comp.stdin.close()
|
|
self.assertEqual(comp.wait(), 0)
|
|
|
|
# create 3 applications, 2 applications will have usdt attached and
|
|
# the third one does not, and the third one should not call into
|
|
# bpf program.
|
|
self.app = Popen([self.ftemp.name, "1"])
|
|
self.app2 = Popen([self.ftemp.name, "11"])
|
|
self.app3 = Popen([self.ftemp.name, "21"])
|
|
|
|
def test_attach1(self):
|
|
# Enable USDT probe from given PID and verifier generated BPF programs.
|
|
u = USDT(pid=int(self.app.pid))
|
|
u.enable_probe(probe="probe_point_1", fn_name="do_trace1")
|
|
u.enable_probe(probe="probe_point_2", fn_name="do_trace2")
|
|
u2 = USDT(pid=int(self.app2.pid))
|
|
u2.enable_probe(probe="probe_point_2", fn_name="do_trace2")
|
|
u2.enable_probe(probe="probe_point_3", fn_name="do_trace3")
|
|
self.bpf_text = self.bpf_text.replace("FILTER", "pid == %d" % self.app.pid)
|
|
b = BPF(text=self.bpf_text, usdt_contexts=[u, u2])
|
|
|
|
# Event states for each event:
|
|
# 0 - probe not caught, 1 - probe caught with correct value,
|
|
# 2 - probe caught with incorrect value
|
|
self.evt_st_1 = 0
|
|
self.evt_st_2 = 0
|
|
self.evt_st_3 = 0
|
|
self.evt_st_4 = 0
|
|
self.evt_st_5 = 0
|
|
self.evt_st_6 = 0
|
|
|
|
def check_event_val(data, event_state, expected_val):
|
|
result = ct.cast(data, ct.POINTER(ct.c_int)).contents
|
|
if result.value == expected_val:
|
|
if (event_state == 0 or event_state == 1):
|
|
return 1
|
|
return 2
|
|
|
|
def print_event1(cpu, data, size):
|
|
self.evt_st_1 = check_event_val(data, self.evt_st_1, 1)
|
|
|
|
def print_event2(cpu, data, size):
|
|
self.evt_st_2 = check_event_val(data, self.evt_st_2, 2)
|
|
|
|
def print_event3(cpu, data, size):
|
|
self.evt_st_3 = check_event_val(data, self.evt_st_3, 3)
|
|
|
|
def print_event4(cpu, data, size):
|
|
self.evt_st_4 = check_event_val(data, self.evt_st_4, 11)
|
|
|
|
def print_event5(cpu, data, size):
|
|
self.evt_st_5 = check_event_val(data, self.evt_st_5, 12)
|
|
|
|
def print_event6(cpu, data, size):
|
|
self.evt_st_6 = check_event_val(data, self.evt_st_6, 13)
|
|
|
|
# loop with callback to print_event
|
|
b["event1"].open_perf_buffer(print_event1)
|
|
b["event2"].open_perf_buffer(print_event2)
|
|
b["event3"].open_perf_buffer(print_event3)
|
|
b["event4"].open_perf_buffer(print_event4)
|
|
b["event5"].open_perf_buffer(print_event5)
|
|
b["event6"].open_perf_buffer(print_event6)
|
|
|
|
# three iterations to make sure we get some probes and have time to process them
|
|
for i in range(3):
|
|
b.perf_buffer_poll()
|
|
|
|
# note that event1 and event4 do not really fire, so their state should be 0
|
|
# use separate asserts so that if test fails we know which one is the culprit
|
|
self.assertTrue(self.evt_st_1 == 1)
|
|
self.assertTrue(self.evt_st_2 == 1)
|
|
self.assertTrue(self.evt_st_3 == 0)
|
|
self.assertTrue(self.evt_st_4 == 0)
|
|
self.assertTrue(self.evt_st_5 == 1)
|
|
self.assertTrue(self.evt_st_6 == 1)
|
|
|
|
def tearDown(self):
|
|
# kill the subprocess, clean the environment
|
|
self.app.kill()
|
|
self.app.wait()
|
|
self.app2.kill()
|
|
self.app2.wait()
|
|
self.app3.kill()
|
|
self.app3.wait()
|
|
os.unlink(self.ftemp.name)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|