• bcc 语法


    基本结构

    #导入库
    from bcc import BPF
    #使用BPF()执行bpf代码
    BPF(text="""
    #C语言代码段
    """
    )
    #对bpf的处理代码
    

    C语言代码编写

    不需要写main函数,目前知道可以写两种函数,以“kprobe__”开头的函数和自定义函数。bpf函数至少要包含一个参数“ctx”,即使不使用也应该存在,可以声明为“void *ctx”。

    以“kprobe__”开头的函数,其余的名称部分表示要检测的函数,比如“kprobe__sys_clone”,表示要检测的函数是“sys_clone()”。例:

    from bcc import BPF
    #
    BPF(text=
    """
    int kprobe__sys_clone(void *ctx) 
    {
        bpf_trace_printk("Hello, World!\\n"); 
        return 0; 
    }
    """
    ).trace_print()
    

    自定义函数需要另外指定其要关联的事件或其他,由”attach_*“类的函数实现。举例如下:

    bpf_text="""
    int hello(void *ctx) {
        bpf_trace_printk("Hello, World!\\n");
        return 0;
    }
    """
    b = BPF(text=bpf_text)
    #将“hello()”函数关联clone函数。
    b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello")
     
    print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE"))
    while 1:
        try:
            (task, pid, cpu, flags, ts, msg) = b.trace_fields()
        except ValueError:
            continue
        print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg))
    

    部分bpf函数说明

    bpf_trace_printk() : 相当于精简版的printk,将数据输出到trace_pipe(/sys/kernel/debug/tracing/trace_pipe),此函数有一定限制,最大3个参数,一个%s,但是由于trace_pipe是全局共享的,对于并发程序而言有冲突,因此更建议使用BPF_PERF_OUTPUT()。

    BPF_HASH() : 创建散列,例如“BPF_HASH(test)”创建了一个名为test的散列,相关操作函数如下:

    1. update() : 更新散列的内容,例如“u64 key=0,num=1;test.update(&key,&num);”,将num的值与key关联起来。

    2. delete() : 删除指定key,例如“u64 key=0;test.delete(&key);”,即删除key=0的列。

    3. lookup() : 获取指定key的值,如果对应的key没有值,则返回"NULL"。例如“u64 key=0,num;num=test.lookup(&key)”,获取key等于0的列的值。

    bpf_ktime_get_ns(): 获取当前时间,精确到纳秒。例如“u64 ts=bpf_ktime_get_ns()”。

    BPF_PERF_OUTPUT(): 命名输出管道。例如“BPF_PERF_OUTPUT(test)”,命名了一个名为"test"的管道。操作函数如下:

    1. perf_submit() : 将数据输出到perf环缓冲区读取。例如“test.perf_submit(ctx,&data,sizeof(data))”。ctx为函数的固定参数,data为自定义数据结构,最后一个参数为data的大小。

    bpf_get_current_pid_tgid() : 获取当前进程pid和tgid,其中pid位于低32位,tgid位于高32位,对于多线程程序而言,tgid是相同的,需要用pid来区分。

    bpf_get_current_comm() : 获取当前进程的进程名。例:

    #include <linux/sched.h>
    int hello()
    {
        char comm[TASK_COMM_LEN];
        bpf_get_current_comm(&comm,sizeof(comm));
        return 0;
    }
    

    部分bcc函数说明

    BPF() : 用于执行bpf程序的主要函数,使用方法“b = BPF(text=C代码文本)”。将会返回一个引用,可以用通过此引用控制bpf程序。部分操作函数如下:

    1. trace_print() : 打印出trace_pipe管道的内容。例如“b.trace_print()”。

    2. get_syscall_fnname() : 获取指定系统调用函数名。例如“b.get_syscall_fnname("clone")”,返回“__x64_sys_clone”。

    3. attach_kprobe() : 将函数与内核事件关联,用于检测指定事件。例如“b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello")”,event指定事件,fn_name,指定bpf代码里的函数名,比如这里就是hello()函数。

    4. trace_fields() : 从trace_pipe返回一组固定格式的字段,与trace_print()类似,但是官方建议使用 BPF_PERF_OUTPUT()。使用方法“(task, pid, cpu, flags, ts, msg) = b.trace_fields()”,其中msg为bpf代码中向trace_pipe输出的数据。

    5. open_perf_buffer() : 打开perf缓冲区,并指定一个回调函数,当有数据时,自动使用该函数处理。

    6. perf_buffer_poll() : 等待perf缓冲区的内容。

    7. event() : 获取从 BPF_PERF_OUTPUT()传递的事件。使用方法如下所示:

    from bcc import BPF
     
    b = BPF(text="""
    //。。。bpf代码。。。
    BPF_PERF_OUTPUT(events);
    //。。。bpf代码。。。
    """
    )
    #。。。bcc代码。。。
     
    #定义回调函数
    def print_event(cpu, data, size)
        event = b["events"].event(data) #获取由bpf函数perf_submit输出的数据
        #。。。代码省略。。。
     
    # 循环并回调到print_event
    b["events"].open_perf_buffer(print_event)
    while 1:
        b.perf_buffer_poll()
    

    示例

    以bcc example中的task_switch.py为例:
    task_switch.c代码:

    #include <uapi/linux/ptrace.h>
    #include <linux/sched.h>
    
    struct key_t {
        u32 prev_pid;
        u32 curr_pid;
    };
    
    BPF_HASH(stats, struct key_t, u64, 1024);
    int count_sched(struct pt_regs *ctx, struct task_struct *prev) {
        struct key_t key = {};
        u64 zero = 0, *val;
    
        key.curr_pid = bpf_get_current_pid_tgid();  //获取当前进程pid和tgid
        key.prev_pid = prev->pid;
    
        // could also use `stats.increment(key);`
        val = stats.lookup_or_init(&key, &zero);
        (*val)++;
        return 0;
    }
    
    

    task_switch.py代码:

    #!/usr/bin/python
    # Copyright (c) PLUMgrid, Inc.
    # Licensed under the Apache License, Version 2.0 (the "License")
    
    from bcc import BPF
    from time import sleep
    
    b = BPF(src_file="task_switch.c")
    b.attach_kprobe(event="finish_task_switch", fn_name="count_sched")
    
    # generate many schedule events
    for i in range(0, 100): sleep(0.01)
    
    for k, v in b["stats"].items():
        print("task_switch[%5d->%5d]=%u" % (k.prev_pid, k.curr_pid, v.value))
    
  • 相关阅读:
    个人开发框架总结(六)
    使用Forms身份验证
    接口IStateManager的使用心得
    Remoting中向服务器传送新对象时应注意的问题
    具有反色文本的进度条绘制方法
    中文姓名的VbScript验证方法
    个人开发框架总结(七)
    Asp.net MVC 实例
    直接对List对象排序,提高系统性能
    Spring 简单实现邮件发送
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/16758118.html
Copyright © 2020-2023  润新知