Friday, February 21, 2020

[Tracing] Use BCC tools to do dynamic tracing in Linux user space

Here is a very simple example of using BCC to trace a program and get the data from the function's argument.

A Simple Test Code in C: "test.c"
#include <stdio.h>

// student structure
struct student {
  char id[15];
  char firstname[64];
  char lastname[64];
  float points;
};
// function declaration
void getDetail(struct student *);
void displayDetail(struct student *);

int main(void) {
  // student structure variable
  struct student std[1];
  // get student detail
  getDetail(std);
  // display student detail
  displayDetail(std);
  return 0;
}

// function definition
void getDetail(struct student *ptr) {
  int i;
  for (i = 0; i < 1; i++) {
    printf("Enter detail of student #%d\n", (i + 1));
    printf("Enter ID: ");
    scanf("%s", ptr->id);
    printf("Enter first name: ");
    scanf("%s", ptr->firstname);
    printf("Enter last name: ");
    scanf("%s", ptr->lastname);
    printf("Enter Points: ");
    scanf("%f", &ptr->points);
    // update pointer to point at next element
    // of the array std
    ptr++;
  }
}

void displayDetail(struct student *ptr) {
  int i;
  for (i = 0; i < 1; i++) {
    printf("\nDetail of student #%d\n", (i + 1));
    // display result via ptr variable
    printf("\nResult via ptr\n");
    printf("ID: %s\n", ptr->id);
    printf("First Name: %s\n", ptr->firstname);
    printf("Last Name: %s\n", ptr->lastname);
    printf("Points: %f\n", ptr->points);
    // update pointer to point at next element
    // of the array std
    ptr++;
  }
}

The simple BCC example code: "mytrace.py"
It will get and print out the string of firstname when it runs
from __future__ import print_function
from bcc import BPF
from time import strftime
import argparse


# load BPF program
bpf_text = """
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct str_t {
    u64 pid;
    char str[80];
};

// student structure
struct student {
  char id[15];
  char firstname[64];
  char lastname[64];
  float points;
};

BPF_PERF_OUTPUT(events);
int printret(struct pt_regs *ctx) {
    struct str_t data  = {};
    char comm[TASK_COMM_LEN] = {};
    u32 pid;
    if (!PT_REGS_RC(ctx))
        return 0;
    pid = bpf_get_current_pid_tgid();
    data.pid = pid;
    bpf_probe_read(&data.str, sizeof(data.str),
        ((struct student *)PT_REGS_RC(ctx))->firstname);
    bpf_get_current_comm(&comm, sizeof(comm));
    events.perf_submit(ctx,&data,sizeof(data));
    return 0;
};
"""

b = BPF(text=bpf_text)
b.attach_uprobe(name="<...your test binary file location...>/test",
        sym="displayDetail", fn_name="printret")

# header
print("%-9s %-6s %s" % ("TIME", "PID", "ARGUMENT"))

def print_event(cpu, data, size):
    event = b["events"].event(data)
    print("%-9s %-6d %s" % (strftime("%H:%M:%S"), event.pid,
                            event.str.decode('utf-8', 'replace')))

b["events"].open_perf_buffer(print_event)
while 1:
    try:
        b.perf_buffer_poll()
    except KeyboardInterrupt:
        exit()
Here is the steps:
$ sudo python mytrace.py

# open another terminal
$ gcc -o test test.c
$ ./test
Enter detail of student #1
Enter ID: 1234
Enter first name: Danny
Enter last name: Liu
Enter Points: 100

Detail of student #1

Result via ptr
ID: 1234
First Name: Danny
Last Name: Liu
Points: 100.000000
Then you will see the tracing message in your previous terminal as follows:
(print out the first name from function's argument)
$ sudo python mytrace.py
TIME      PID    ARGUMENT
17:01:32  7265   Danny

No comments: