Files
bpf-developer-tutorial/src/32-wallclock-profiler/common.h
云微 70451702f0 Add wall clock analysis (#184)
* 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
2025-09-30 22:11:52 -07:00

226 lines
6.1 KiB
C

// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
#ifndef __COMMON_H
#define __COMMON_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
#include "blazesym.h"
/* Types needed for both profile and offcputime */
#define TASK_COMM_LEN 16
#define MAX_PID_NR 30
#define MAX_TID_NR 30
/* Common helper functions for BPF tools */
/**
* split_convert - Split a string by a delimiter and convert each token
* @s: String to split
* @delim: Delimiter string
* @elems: Array to store the converted elements
* @elems_size: Size of the elems array in bytes
* @elem_size: Size of each element in bytes
* @convert: Function to convert each token to the desired type
*
* Return: 0 on success, negative error code on failure
*/
static inline int split_convert(char *s, const char* delim, void *elems, size_t elems_size,
size_t elem_size, int (*convert)(const char *, void *))
{
char *token;
int ret;
char *pos = (char *)elems;
if (!s || !delim || !elems)
return -1;
token = strtok(s, delim);
while (token) {
if (pos + elem_size > (char*)elems + elems_size)
return -ENOBUFS;
ret = convert(token, pos);
if (ret)
return ret;
pos += elem_size;
token = strtok(NULL, delim);
}
return 0;
}
/**
* str_to_int - Convert a string to an integer
* @src: Source string
* @dest: Pointer to store the converted integer
*
* Return: 0 on success, negative error code on failure
*/
static inline int str_to_int(const char *src, void *dest)
{
*(int*)dest = strtol(src, NULL, 10);
return 0;
}
/**
* show_stack_trace - Display a stack trace with symbol resolution
* @symbolizer: Blazesym symbolizer instance
* @stack: Array of stack addresses
* @stack_sz: Size of the stack array
* @pid: Process ID (0 for kernel)
*/
static void show_stack_trace(blaze_symbolizer *symbolizer, __u64 *stack, int stack_sz, pid_t pid)
{
const struct blaze_syms *syms;
int i;
// Choose symbolization source based on pid
if (pid) {
const struct blaze_symbolize_src_process src = {
.type_size = sizeof(src),
.pid = pid,
};
syms = blaze_symbolize_process_abs_addrs(symbolizer, &src, (const uint64_t *)stack, stack_sz);
} else {
const struct blaze_symbolize_src_kernel src = {
.type_size = sizeof(src),
.kallsyms = NULL,
.vmlinux = NULL,
.debug_syms = false,
.reserved = {0},
};
syms = blaze_symbolize_kernel_abs_addrs(symbolizer, &src, (const uint64_t *)stack, stack_sz);
}
if (!syms) {
fprintf(stderr, "Failed to symbolize stack trace\n");
return;
}
for (i = 0; i < stack_sz; i++) {
if (!stack[i])
continue;
if (i >= syms->cnt) {
printf(" [unknown]\n");
continue;
}
const struct blaze_sym *sym = &syms->syms[i];
if (sym->name) {
printf(" %s\n", sym->name);
} else {
printf(" [unknown]\n");
}
}
blaze_syms_free(syms);
}
/**
* show_stack_trace_folded - Display a stack trace in folded format for flamegraphs
* @symbolizer: Blazesym symbolizer instance
* @stack: Array of stack addresses
* @stack_sz: Size of the stack array
* @pid: Process ID (0 for kernel)
* @separator: Character to use as separator between frames (typically ';')
* @reverse: Whether to print the stack in reverse order (true for flamegraphs)
*/
static void show_stack_trace_folded(blaze_symbolizer *symbolizer, __u64 *stack, int stack_sz,
pid_t pid, char separator, bool reverse)
{
const struct blaze_syms *syms;
int i;
bool first = true;
// Choose symbolization source based on pid
if (pid) {
const struct blaze_symbolize_src_process src = {
.type_size = sizeof(src),
.pid = pid,
};
syms = blaze_symbolize_process_abs_addrs(symbolizer, &src, (const uint64_t *)stack, stack_sz);
} else {
const struct blaze_symbolize_src_kernel src = {
.type_size = sizeof(src),
.kallsyms = NULL,
.vmlinux = NULL,
.debug_syms = false,
.reserved = {0},
};
syms = blaze_symbolize_kernel_abs_addrs(symbolizer, &src, (const uint64_t *)stack, stack_sz);
}
if (!syms) {
fprintf(stderr, "Failed to symbolize stack trace\n");
return;
}
/* For flamegraphs, we need to print the stack in reverse order */
if (reverse) {
for (i = stack_sz - 1; i >= 0; i--) {
if (!stack[i])
continue;
if (i >= syms->cnt || !syms->syms[i].name) {
if (!first) {
printf("%c", separator);
}
printf("[unknown]");
first = false;
continue;
}
const struct blaze_sym *sym = &syms->syms[i];
if (!first) {
printf("%c", separator);
}
printf("%s", sym->name);
first = false;
}
} else {
/* Print stack in normal order */
for (i = 0; i < stack_sz; i++) {
if (!stack[i])
continue;
if (i >= syms->cnt || !syms->syms[i].name) {
if (!first) {
printf("%c", separator);
}
printf("[unknown]");
first = false;
continue;
}
const struct blaze_sym *sym = &syms->syms[i];
if (!first) {
printf("%c", separator);
}
printf("%s", sym->name);
first = false;
}
}
blaze_syms_free(syms);
}
/* Safe string duplication */
static inline char *safe_strdup(const char *s)
{
char *ret = strdup(s);
if (!ret) {
fprintf(stderr, "failed to allocate memory\n");
exit(1);
}
return ret;
}
#endif /* __COMMON_H */