mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-02-10 13:45:07 +08:00
* Add combined on-CPU and off-CPU profiler script - Implemented a new profiling tool that captures both on-CPU and off-CPU activity for a specified process. - The script runs 'oncputime' and 'offcputime' tools simultaneously and combines their results into a unified flamegraph. - Added functionality to discover threads and profile them individually if the application is multi-threaded. - Included error handling for tool execution and output processing. - Created methods for generating flamegraph data and SVG visualizations. - Added command-line argument parsing for user-defined profiling parameters. - Implemented detailed analysis reports for both individual threads and overall profiling results. * feat: integrate blazesym v0.2.0 for improved symbol resolution and add test program for memory leak detection * docs: update README to enhance wall clock profiling tutorial with detailed explanations and examples * feat: add wallclock-profiler tests to CI workflow for tool validation * fix: rename combined_profiler.py to wallclock_profiler.py in test scripts and usage examples
129 lines
3.2 KiB
C
129 lines
3.2 KiB
C
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
|
/*
|
|
* Copyright (c) 2022 LG Electronics
|
|
*
|
|
* Based on profile from BCC by Brendan Gregg and others.
|
|
* 28-Dec-2021 Eunseon Lee Created this.
|
|
*/
|
|
#include <vmlinux.h>
|
|
#include <bpf/bpf_helpers.h>
|
|
#include <bpf/bpf_core_read.h>
|
|
#include <bpf/bpf_tracing.h>
|
|
#include "oncputime.h"
|
|
|
|
#define EEXIST 17
|
|
|
|
const volatile bool kernel_stacks_only = false;
|
|
const volatile bool user_stacks_only = false;
|
|
const volatile bool include_idle = false;
|
|
const volatile bool filter_by_pid = false;
|
|
const volatile bool filter_by_tid = false;
|
|
const volatile bool use_pidns = false;
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
|
|
__type(key, u32);
|
|
} stackmap SEC(".maps");
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_HASH);
|
|
__type(key, struct key_t);
|
|
__type(value, u64);
|
|
__uint(max_entries, MAX_ENTRIES);
|
|
} counts SEC(".maps");
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_HASH);
|
|
__type(key, u32);
|
|
__type(value, u8);
|
|
__uint(max_entries, MAX_PID_NR);
|
|
} pids SEC(".maps");
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_HASH);
|
|
__type(key, u32);
|
|
__type(value, u8);
|
|
__uint(max_entries, MAX_TID_NR);
|
|
} tids SEC(".maps");
|
|
|
|
|
|
static __always_inline void *
|
|
bpf_map_lookup_or_try_init(void *map, const void *key, const void *init)
|
|
{
|
|
void *val;
|
|
/* bpf helper functions like bpf_map_update_elem() below normally return
|
|
* long, but using int instead of long to store the result is a workaround
|
|
* to avoid incorrectly evaluating err in cases where the following criteria
|
|
* is met:
|
|
* the architecture is 64-bit
|
|
* the helper function return type is long
|
|
* the helper function returns the value of a call to a bpf_map_ops func
|
|
* the bpf_map_ops function return type is int
|
|
* the compiler inlines the helper function
|
|
* the compiler does not sign extend the result of the bpf_map_ops func
|
|
*
|
|
* if this criteria is met, at best an error can only be checked as zero or
|
|
* non-zero. it will not be possible to check for a negative value or a
|
|
* specific error value. this is because the sign bit would have been stuck
|
|
* at the 32nd bit of a 64-bit long int.
|
|
*/
|
|
int err;
|
|
|
|
val = bpf_map_lookup_elem(map, key);
|
|
if (val)
|
|
return val;
|
|
|
|
err = bpf_map_update_elem(map, key, init, BPF_NOEXIST);
|
|
if (err && err != -EEXIST)
|
|
return 0;
|
|
|
|
return bpf_map_lookup_elem(map, key);
|
|
}
|
|
|
|
SEC("perf_event")
|
|
int do_perf_event(struct bpf_perf_event_data *ctx)
|
|
{
|
|
u64 *valp;
|
|
static const u64 zero;
|
|
struct key_t key = {};
|
|
u64 id;
|
|
u32 pid;
|
|
u32 tid;
|
|
struct bpf_pidns_info ns = {};
|
|
|
|
id = bpf_get_current_pid_tgid();
|
|
pid = id >> 32;
|
|
tid = id;
|
|
|
|
if (!include_idle && tid == 0)
|
|
return 0;
|
|
|
|
if (filter_by_pid && !bpf_map_lookup_elem(&pids, &pid))
|
|
return 0;
|
|
|
|
if (filter_by_tid && !bpf_map_lookup_elem(&tids, &tid))
|
|
return 0;
|
|
|
|
key.pid = pid;
|
|
bpf_get_current_comm(&key.name, sizeof(key.name));
|
|
|
|
if (user_stacks_only)
|
|
key.kern_stack_id = -1;
|
|
else
|
|
key.kern_stack_id = bpf_get_stackid(&ctx->regs, &stackmap, 0);
|
|
|
|
if (kernel_stacks_only)
|
|
key.user_stack_id = -1;
|
|
else
|
|
key.user_stack_id = bpf_get_stackid(&ctx->regs, &stackmap,
|
|
BPF_F_USER_STACK);
|
|
|
|
valp = bpf_map_lookup_or_try_init(&counts, &key, &zero);
|
|
if (valp)
|
|
__sync_fetch_and_add(valp, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
char LICENSE[] SEC("license") = "GPL";
|