BCC-stackcount(1)

语法

stackcount语法与funccount相似都是由选项和eventname构成

stackcount [options] eventname

eventname的语法是:

  • name或p:name:对内核函数name()进行插装。
  • lib:name或p:lib:name:对用户态lib库中的函数name进行插桩。
  • path:name:对于path路径下文件的用户态函数name进行插桩。
  • t:system:name:对名为system:name的内核跟踪点进行插装。
  • u:lib:name:对lib库中名为name的USDT探针进行插桩。
  • *:用来匹配任意字符的通配符。

选项:

  • -h、–help:显示帮助信息
  • -p PID、–pid PID:只对该PID进行跟踪
  • -i INTERVAL、–interval INTERVAL:统计总数,每INTERVAL
  • -d DURATION、–duration DURATION:总共跟踪的秒数
  • -T、–timestamp:输出时间
  • -r、–regexp:使用正则表达式
  • -s、–offset:显示地址偏移
  • -P、–perpid:对每个进程分别显示栈信息
  • -K、–kernel-stacks-only:仅显示内核堆栈信息
  • -U、–user-stacks-only:仅显示用户态的堆栈信息
  • -v、–verbose:显示行地址
  • -d、–delimited:插入分隔符在内核栈和用户栈间
  • -f、–folded:输出折叠格式
  • –debug:启动前打印BPF程序信息

示例

对创建块I/O的函数调用栈进行计数:

stackcount-bpfcc t:block:block_rq_insert

对发送IP数据包的调用栈进行计数

stackcount-bpfcc ip_output

对发送IP数据包的调用栈进行计数,同时显示对应的PID:

stackcount-bpfcc -P ip_output

对导致线程阻塞并且导致脱离CPU的调用栈进行计数:

stackcount-bpfcc t:sched:sched_switch

对导致系统调用read的调用栈进行计数:

stackcount-bpfcc t:syscalls:sys_enter_read

源码

from __future__ import print_function
from bcc import BPF, USDT
from time import sleep, strftime
import argparse
import re
import signal
import sys
import traceback

debug = False

class Probe(object):
    def \_\_init\_\_(self, pattern, kernel_stack, user_stack, use_regex=False,
                 pid=None, per_pid=False, cpu=None):
        """Init a new probe.

 Init the probe from the pattern provided by the user. The supported
 patterns mimic the 'trace' and 'argdist' tools, but are simpler because
 we don't have to distinguish between probes and retprobes.

 func -- probe a kernel function
 lib:func -- probe a user-space function in the library 'lib'
 p::func -- same thing as 'func'
 p:lib:func -- same thing as 'lib:func'
 t:cat:event -- probe a kernel tracepoint
 u:lib:probe -- probe a USDT tracepoint
 """
        self.kernel_stack = kernel_stack
        self.user_stack = user_stack
        parts = pattern.split(':')
        if len(parts) == 1:
            parts = ["p", "", parts[0]]
        elif len(parts) == 2:
            parts = ["p", parts[0], parts[1]]
        elif len(parts) == 3:
            if parts[0] == "t":
                parts = ["t", "", "%s:%s" % tuple(parts[1:])]
            if parts[0] not in ["p", "t", "u"]:
                raise Exception("Type must be 'p', 't', or 'u', but got %s" %
                                parts[0])
        else:
            raise Exception("Too many ':'-separated components in pattern %s" %
                            pattern)

        (self.type, self.library, self.pattern) = parts
        if not use_regex:
            self.pattern = self.pattern.replace('\*', '.\*')
            self.pattern = '^' + self.pattern + '$'

        if (self.type == "p" and self.library) or self.type == "u":
            libpath = BPF.find_library(self.library)
            if libpath is None:
                # This might be an executable (e.g. 'bash')
                libpath = BPF.find_exe(self.library)
            if libpath is None or len(libpath) == 0:
                raise Exception("unable to find library %s" % self.library)
            self.library = libpath

        self.pid = pid
        self.per_pid = per_pid
        self.cpu = cpu
        self.matched = 0

    def is\_kernel\_probe(self):
        return self.type == "t" or (self.type == "p" and self.library == "")

    def attach(self):
        if self.type == "p":
            if self.library:
                self.bpf.attach_uprobe(name=self.library,
                                       sym_re=self.pattern,
                                       fn_name="trace\_count",
                                       pid=self.pid or -1)
                self.matched = self.bpf.num_open_uprobes()
            else:
                self.bpf.attach_kprobe(event_re=self.pattern,
                                       fn_name="trace\_count")
                self.matched = self.bpf.num_open_kprobes()
        elif self.type == "t":
            self.bpf.attach_tracepoint(tp_re=self.pattern,
                                       fn_name="trace\_count")
            self.matched = self.bpf.num_open_tracepoints()
        elif self.type == "u":
            pass    # Nothing to do -- attach already happened in `load`

        if self.matched == 0:
            raise Exception("No functions matched by pattern %s" %
                            self.pattern)

    def load(self):
        ctx_name = "ctx"
        stack_trace = ""
        if self.user_stack:
                stack_trace += """
 key.user\_stack\_id = stack\_traces.get\_stackid(
 %s, BPF\_F\_USER\_STACK
 );""" % (ctx_name)
        else:
                stack_trace += "key.user\_stack\_id = -1;"
        if self.kernel_stack:
                stack_trace += """
 key.kernel\_stack\_id = stack\_traces.get\_stackid(
 %s, 0
 );""" % (ctx_name)
        else:
                stack_trace += "key.kernel\_stack\_id = -1;"

        trace_count_text = """
int trace\_count(void \*ctx) {
 FILTER
 struct key\_t key = {};
 key.tgid = GET\_TGID;
 STORE\_COMM
 %s
 counts.atomic\_increment(key);
 return 0;
}
 """
        trace_count_text = trace_count_text % (stack_trace)

        bpf_text = """#include <uapi/linux/ptrace.h>
#include <linux/sched.h>

struct key\_t {
 // no pid (thread ID) so that we do not needlessly split this key
 u32 tgid;
 int kernel\_stack\_id;
 int user\_stack\_id;
 char name[TASK\_COMM\_LEN];
};

BPF\_HASH(counts, struct key\_t);
BPF\_STACK\_TRACE(stack\_traces, 1024);
 """

        filter_text = []
        # We really mean the tgid from the kernel's perspective, which is in
        # the top 32 bits of bpf\_get\_current\_pid\_tgid().
        if self.is_kernel_probe() and self.pid:
            filter_text.append('u32 pid; pid = bpf\_get\_current\_pid\_tgid() >> 32; ' +
                               'if (pid != %d) { return 0; }' % self.pid)

        if self.is_kernel_probe() and self.cpu:
            filter_text.append('struct task\_struct \*task; task = (struct task\_struct\*)bpf\_get\_current\_task(); ' +
                               'if (task->cpu != %d) { return 0; }' % self.cpu)

        trace_count_text = trace_count_text.replace('FILTER', '\n '.join(filter_text))

        # Do per-pid statistics iff -P is provided
        if self.per_pid:
            trace_count_text = trace_count_text.replace('GET\_TGID',
                                        'bpf\_get\_current\_pid\_tgid() >> 32')
            trace_count_text = trace_count_text.replace('STORE\_COMM',
                        'bpf\_get\_current\_comm(&key.name, sizeof(key.name));')
        else:
            # skip splitting on PID so these aggregate
            # together, and don't store the process name.
            trace_count_text = trace_count_text.replace(
                                    'GET\_TGID', '0xffffffff')
            trace_count_text = trace_count_text.replace('STORE\_COMM', '')

        self.usdt = None
        if self.type == "u":
            self.usdt = USDT(path=self.library, pid=self.pid)
            for probe in self.usdt.enumerate_probes():
                if not self.pid and (probe.bin_path != self.library):
                    continue
                if re.match(self.pattern, probe.name):
                    # This hack is required because the bpf\_usdt\_readarg
                    # functions generated need different function names for
                    # each attached probe. If we just stick to trace\_count,
                    # we'd get multiple bpf\_usdt\_readarg helpers with the same
                    # name when enabling more than one USDT probe.
                    new_func = "trace\_count\_%d" % self.matched
                    bpf_text += trace_count_text.replace(
                                            "trace\_count", new_func)
                    self.usdt.enable_probe(probe.name, new_func)
                    self.matched += 1
            if debug:
                print(self.usdt.get_text())
        else:
            bpf_text += trace_count_text

        if debug:
            print(bpf_text)
        self.bpf = BPF(text=bpf_text,
                       usdt_contexts=[self.usdt] if self.usdt else [])

class Tool(object):
    def \_\_init\_\_(self):
        examples = """examples:
 ./stackcount submit\_bio # count kernel stack traces for submit\_bio
 ./stackcount -d ip\_output # include a user/kernel stack delimiter
 ./stackcount -s ip\_output # show symbol offsets
 ./stackcount -sv ip\_output # show offsets and raw addresses (verbose)
 ./stackcount 'tcp\_send\*' # count stacks for funcs matching tcp\_send\*
 ./stackcount -r '^tcp\_send.\*' # same as above, using regular expressions
 ./stackcount -Ti 5 ip\_output # output every 5 seconds, with timestamps
 ./stackcount -p 185 ip\_output # count ip\_output stacks for PID 185 only
 ./stackcount -c 1 put\_prev\_entity # count put\_prev\_entity stacks for CPU 1 only
 ./stackcount -p 185 c:malloc # count stacks for malloc in PID 185
 ./stackcount t:sched:sched\_fork # count stacks for sched\_fork tracepoint
 ./stackcount -p 185 u:node:\* # count stacks for all USDT probes in node
 ./stackcount -K t:sched:sched\_switch # kernel stacks only
 ./stackcount -U t:sched:sched\_switch # user stacks only
 """
        parser = argparse.ArgumentParser(
            description="Count events and their stack traces",
            formatter_class=argparse.RawDescriptionHelpFormatter,
            epilog=examples)
        parser.add_argument("-p", "--pid", type=int,
            help="trace this PID only")
        parser.add_argument("-c", "--cpu", type=int,
            help="trace this CPU only")
        parser.add_argument("-i", "--interval",
            help="summary interval, seconds")
        parser.add_argument("-D", "--duration",
            help="total duration of trace, seconds")
        parser.add_argument("-T", "--timestamp", action="store\_true",
            help="include timestamp on output")
        parser.add_argument("-r", "--regexp", action="store\_true",
            help="use regular expressions. Default is \"\*\" wildcards only.")
        parser.add_argument("-s", "--offset", action="store\_true",
            help="show address offsets")
        parser.add_argument("-P", "--perpid", action="store\_true",
            help="display stacks separately for each process")
        parser.add_argument("-K", "--kernel-stacks-only",
            action="store\_true", help="kernel stack only", default=False)
        parser.add_argument("-U", "--user-stacks-only",
            action="store\_true", help="user stack only", default=False)
        parser.add_argument("-v", "--verbose", action="store\_true",
            help="show raw addresses")
        parser.add_argument("-d", "--delimited", action="store\_true",
            help="insert delimiter between kernel/user stacks")
        parser.add_argument("-f", "--folded", action="store\_true",
            help="output folded format")
        parser.add_argument("--debug", action="store\_true",
            help="print BPF program before starting (for debugging purposes)")
        parser.add_argument("pattern",
            help="search expression for events")
        self.args = parser.parse_args()
        global debug
        debug = self.args.debug

        if self.args.duration and not self.args.interval:
            self.args.interval = self.args.duration
        if not self.args.interval:
            self.args.interval = 99999999

        if self.args.kernel_stacks_only and self.args.user_stacks_only:
            print("ERROR: -K and -U are mutually exclusive. If you want " +
                "both stacks, that is the default.")
            exit()
        if not self.args.kernel_stacks_only and not self.args.user_stacks_only:
            self.kernel_stack = True
            self.user_stack = True
        else:
            self.kernel_stack = self.args.kernel_stacks_only
            self.user_stack = self.args.user_stacks_only

        # For tracing single processes in isolation, explicitly set perpid
        # to True, if not already set. This is required to generate the correct
        # BPF program that can store pid in the tgid field of the key\_t object.
        if self.args.pid is not None and self.args.pid > 0:
            self.args.perpid = True

        self.probe = Probe(self.args.pattern,
                           self.kernel_stack, self.user_stack,
                           self.args.regexp, self.args.pid, self.args.perpid, self.args.cpu)
        self.need_delimiter = self.args.delimited and not (
                    self.args.kernel_stacks_only or self.args.user_stacks_only)

    def \_print\_kframe(self, addr):
        print(" ", end="")
        if self.args.verbose:
            print("%-16x " % addr, end="")
        if self.args.offset:
            print("%s" % self.probe.bpf.ksym(addr, show_offset=True))
        else:
            print("%s" % self.probe.bpf.ksym(addr))

    def \_print\_uframe(self, addr, pid):
        print(" ", end="")
        if self.args.verbose:
            print("%-16x " % addr, end="")
        if self.args.offset:
            print("%s" % self.probe.bpf.sym(addr, pid, show_offset=True))
        else:
            print("%s" % self.probe.bpf.sym(addr, pid))

    @staticmethod
    def \_signal\_ignore(signal, frame):
        print()

    def \_print\_comm(self, comm, pid):
        print(" %s [%d]" % (comm, pid))

    def run(self):
        self.probe.load()
        self.probe.attach()
        if not self.args.folded:
            print("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." %
### 回答1: bcc-csm2-mr 数据处理是指对bcc-csm2-mr全球气候模型生成的数据进行处理和分析的过程。bcc-csm2-mr是由中国气象科学研究院(北京)开发的一个耦合全球气候模型,能够模拟地球气候系统的运行情况。 在数据处理过程中,首先需要对模型生成的原始数据进行清洗和预处理。这包括检查数据的完整性、有效性和一致性,去除无效或错误的数据,填补缺失值,并进行数据格式转换和标准化。清洗和预处理后,数据才能被用于后续的分析和应用。 接下来,可以对处理后的数据进行多种分析方法的应用。例如,可以使用统计学方法来计算和求取数据的统计特征,例如平均值、方差、相关系数等。还可以利用时间序列分析方法来研究数据的变化趋势和周期性。此外,还可以进行空间分析,以探索地区之间的差异和相关性。 在数据处理过程中,还可以利用可视化技术将结果呈现出来,使得数据更加直观和易于理解。例如,可以使用地图、图表等方式展示数据的分布和变化,帮助研究人员更好地理解数据的内在规律和趋势。 最后,数据处理的结果可以为气候科学研究、气候变化预测和决策支持等领域提供重要信息和依据。例如,可以用于评估气候变化对人类社会和生态环境的影响,研究气候变化的机理和驱动因素,以及制定应对气候变化的政策和措施。 总之,bcc-csm2-mr 数据处理是一个复杂而关键的过程,通过对模型生成的数据进行清洗、预处理、分析和可视化,为气候科学研究和应用提供重要基础和支持。 ### 回答2: bcc-csm2-mr 是一种用于数据处理的模型。 数据处理是指将原始数据进行清洗、分析和转换,以便提取有用的信息和知识的过程。对于 bcc-csm2-mr 模型而言,它主要用于处理气候数据。 首先,该模型会对原始气候观测数据进行质量控制,删除有错误和缺失的数据,并进行填补处理,以确保数据的准确性和完整性。接下来,模型会对数据进行重采样,将原始观测数据转换为更适合模型输入的格式和时间分辨率。同时,还会进行空间插值,将数据在不同地点之间进行插值,以填补空白区域。 然后,模型会对数据进行预处理,包括数据去噪、滤波和降维等操作。这些操作旨在减少数据的噪声和冗余,提高数据的可用性和可解释性。接着,模型会进行统计分析和建模,利用各种算法和技术来寻找数据中的模式和趋势,以便对气候系统进行建模和预测。 最后,模型会对处理后的数据进行后处理和可视化,将结果以图表、图像和报告的形式展示出来,以便人们更好地理解和利用这些数据。同时,模型还支持数据的存储和共享,以便其他研究人员或机构能够使用这些数据进行不同的研究和应用。 总之,bcc-csm2-mr 是一个用于处理气候数据的模型,它通过一系列的数据清洗、分析、转换和建模等操作,从原始数据中提取有用的信息和知识,为气候研究和预测提供支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值