Skip to content

Commit

Permalink
fix(ebpf): fix issue when a cls arg is a cell (#3280)
Browse files Browse the repository at this point in the history
  • Loading branch information
korniltsev authored May 8, 2024
1 parent a189d57 commit 6e7a297
Show file tree
Hide file tree
Showing 18 changed files with 288 additions and 25 deletions.
1 change: 1 addition & 0 deletions ebpf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ go/test/arm64: ebpf.arm64.test
uname -m | grep aarch64
./ebpf.arm64.test


.phony: rideshare/gen
rideshare/gen:
git submodule update --init --recursive
Expand Down
3 changes: 3 additions & 0 deletions ebpf/bpf/pyoffsets.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ typedef struct {
int16_t PyInterpreterFrame_owner;
int16_t PyASCIIObject_size; // sizeof(PyASCIIObject)
int16_t PyCompactUnicodeObject_size; // sizeof(PyCompactUnicodeObject)
int16_t PyCellObject_ob_ref;

uint64_t base;
uint64_t PyCell_Type;// absolute address of PyCell_Type
} py_offset_config;

#endif //PYROEBPF_PYOFFSETS_H
29 changes: 24 additions & 5 deletions ebpf/bpf/pyperf.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ struct global_config_t {
};

const volatile struct global_config_t global_config;
#define log_error(fmt, ...) if (global_config.bpf_log_err) bpf_printk(fmt, ##__VA_ARGS__)
#define log_debug(fmt, ...) if (global_config.bpf_log_debug) bpf_printk(fmt, ##__VA_ARGS__)
#define log_error(fmt, ...) if (global_config.bpf_log_err) bpf_printk("[> error <] " fmt, ##__VA_ARGS__)
#define log_debug(fmt, ...) if (global_config.bpf_log_debug) bpf_printk("[ debug ] " fmt, ##__VA_ARGS__)

typedef struct {
uint32_t major;
Expand Down Expand Up @@ -261,6 +261,7 @@ static __always_inline int pyperf_collect_impl(struct bpf_perf_event_data* ctx,
if (get_thread_state(pid_data, &thread_state)) {
return submit_error_sample(PY_ERROR_THREAD_STATE);
}
log_debug("thread_state %llx", thread_state);

// pre-initialize event struct in case any subprogram below fails
event->stack_len = 0;
Expand Down Expand Up @@ -329,6 +330,7 @@ static __always_inline int check_first_arg(void *code_ptr,
&args_ptr, sizeof(void *), args_ptr + offsets->PyTupleObject_ob_item)) {
return -1;
}
*((uint64_t *)&symbol->name) = 0;
if (pystr_read(args_ptr, offsets, symbol->name, sizeof(symbol->name), &symbol->name_type)) {
return -1;
}
Expand All @@ -337,6 +339,8 @@ static __always_inline int check_first_arg(void *code_ptr,
char cls_str[4] = {'c', 'l', 's', '\0'};
*out_first_self = *(int32_t *) symbol->name == *(int32_t *) self_str;
*out_first_cls = *(int32_t *) symbol->name == *(int32_t *) cls_str;
log_debug("first arg %s ", symbol->name);


return 0;
}
Expand Down Expand Up @@ -367,7 +371,7 @@ static __always_inline int get_names(

// Read class name from $frame->f_localsplus[0]->ob_type->tp_name.
if (first_self || first_cls) {
void *ptr;
void *ptr = NULL, *ptr_ob_type = NULL;
if (bpf_probe_read_user(
&ptr, sizeof(void *), (void *) (cur_frame + offsets->VFrame_localsplus))) {
bpf_dbg_printk("failed to read f_localsplus at %x\n", cur_frame + offsets->VFrame_localsplus);
Expand All @@ -381,16 +385,30 @@ static __always_inline int get_names(
return -PY_ERROR_CLASS_NAME;
}
}
if (bpf_probe_read_user(&ptr_ob_type, sizeof(void *), ptr + offsets->PyObject_ob_type)) {
log_error("failed to read ob_type at %x", ptr);
return -PY_ERROR_CLASS_NAME;
}
if (ptr_ob_type == (void*) offsets->PyCell_Type) {
log_debug("cell type %llx", ptr);
if (bpf_probe_read_user(&ptr, sizeof(void *), ptr + offsets->PyCellObject_ob_ref)) {
log_error("failed to read cell ref at %x\n", ptr);
return -PY_ERROR_CLASS_NAME;
}
log_debug("ob_ref %Llx", ptr);
}

// https://github.com/python/cpython/blob/d73501602f863a54c872ce103cd3fa119e38bac9/Include/cpython/object.h#L106
if (bpf_probe_read_user(&ptr, sizeof(void *), ptr + offsets->PyTypeObject_tp_name)) {
bpf_dbg_printk("failed to read tp_name at %x\n", ptr);
return -PY_ERROR_CLASS_NAME;
}
long len = bpf_probe_read_user_str(&symbol->classname, sizeof(symbol->classname), ptr);
if (len < 0) {
bpf_dbg_printk("failed to read class name at %x\n", ptr);
log_error("failed to read class name at %x\n", ptr);
return -PY_ERROR_CLASS_NAME;
}
log_debug("class name %s", symbol->classname);
symbol->classname_type.type = PYSTR_TYPE_UTF8;
symbol->classname_type.size_codepoints = len - 1;
} else {
Expand Down Expand Up @@ -473,7 +491,7 @@ static __always_inline int get_frame_data(
if (!code_ptr) {
return 0; // todo learn when this happens, c extension?
}

log_debug("code %llx", code_ptr);
int res = get_names(cur_frame, code_ptr, offsets, symbol, ctx);
if (res < 0) {
return res;
Expand Down Expand Up @@ -530,6 +548,7 @@ int read_python_stack(struct bpf_perf_event_data *ctx) {
py_symbol *sym = &state->sym;
#pragma unroll
for (int i = 0; i < PYTHON_STACK_FRAMES_PER_PROG; i++) {
log_debug("frame %d %llx", sample->stack_len, state->frame_ptr);
last_res = get_frame_data((void **) &state->frame_ptr, &state->offsets, sym, ctx);
if (last_res < 0) {
return submit_error_sample((uint8_t) (-last_res));
Expand Down
21 changes: 12 additions & 9 deletions ebpf/cmd/playground/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ func getProcessTargets() []sd.DiscoveryTarget {
}
continue
}
cwd = strings.TrimSpace(cwd)

exe, err := os.Readlink(fmt.Sprintf("/proc/%s/exe", spid))
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
Expand All @@ -330,20 +332,21 @@ func getProcessTargets() []sd.DiscoveryTarget {
_ = level.Error(logger).Log("err", err, "msg", "reading comm", "pid", spid)
}
}
cgroup, err := os.ReadFile(fmt.Sprintf("/proc/%s/cgroup", spid))
cmdline, err := os.ReadFile(fmt.Sprintf("/proc/%s/cmdline", spid))
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
_ = level.Error(logger).Log("err", err, "msg", "reading cgroup", "pid", spid)
_ = level.Error(logger).Log("err", err, "msg", "reading cmdline", "pid", spid)
}
} else {
cmdline = bytes.ReplaceAll(cmdline, []byte{0}, []byte(" "))
}
target := sd.DiscoveryTarget{
"__process_pid__": spid,
"__meta_process_cwd": cwd,
"__meta_process_exe": strings.TrimSpace(exe),
"__meta_process_comm": strings.TrimSpace(string(comm)),
"__meta_process_cgroup": strings.TrimSpace(string(cgroup)),
"pid": spid,
"exe": exe,
"__process_pid__": spid,
"cwd": cwd,
"comm": strings.TrimSpace(string(comm)),
"pid": spid,
"exe": exe,
"service_name": fmt.Sprintf("%s @ %s", cmdline, cwd),
}
res = append(res, target)
}
Expand Down
3 changes: 3 additions & 0 deletions ebpf/cmd/python_dwarfdump/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ var pythonFields = []dwarfdump.Need{
}},
{Name: "PyASCIIObject", PrettyName: "PyASCIIObject", Size: true},
{Name: "PyCompactUnicodeObject", PrettyName: "PyCompactUnicodeObject", Size: true},
{Name: "PyCellObject", Fields: []dwarfdump.NeedField{
{"ob_ref", "PyCellObject__ob_ref"},
}},

//{Name: "_is", PrettyName: "PyInterpreterState", Fields: []string{}},
}
9 changes: 6 additions & 3 deletions ebpf/python/perf_bpfel_arm64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified ebpf/python/perf_bpfel_arm64.o
Binary file not shown.
9 changes: 6 additions & 3 deletions ebpf/python/perf_bpfel_x86.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified ebpf/python/perf_bpfel_x86.o
Binary file not shown.
7 changes: 6 additions & 1 deletion ebpf/python/pyperf_pid_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func GetPyPerfPidData(l log.Logger, pid uint32, collectKernel bool) (*PerfPyPidD

data := &PerfPyPidData{}
var (
autoTLSkeyAddr, pyRuntimeAddr uint64
autoTLSkeyAddr, pyRuntimeAddr, PyCellType uint64
)
baseAddr := base_.StartAddr
if ef.FileHeader.Type == elf.ET_EXEC {
Expand All @@ -72,6 +72,8 @@ func GetPyPerfPidData(l log.Logger, pid uint32, collectKernel bool) (*PerfPyPidD
autoTLSkeyAddr = baseAddr + symbol.Value
case "_PyRuntime":
pyRuntimeAddr = baseAddr + symbol.Value
case "PyCell_Type":
PyCellType = baseAddr + symbol.Value
default:
continue
}
Expand Down Expand Up @@ -142,6 +144,9 @@ func GetPyPerfPidData(l log.Logger, pid uint32, collectKernel bool) (*PerfPyPidD
PyVarObjectObSize: offsets.PyVarObject_ob_size,
PyObjectObType: offsets.PyObject_ob_type,
PyTypeObjectTpName: offsets.PyTypeObject_tp_name,
PyCellObjectObRef: offsets.PyCellObject__ob_ref,
Base: baseAddr,
PyCellType: PyCellType,
}
if collectKernel {
data.CollectKernel = 1
Expand Down
Loading

0 comments on commit 6e7a297

Please sign in to comment.