Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ebpf): fix issue when a cls arg is a cell #3280

Merged
merged 1 commit into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading