mirror of
https://github.com/torvalds/linux.git
synced 2025-04-12 16:47:42 +00:00

Perf probe on vfs_fstatat fails as below on a powerpc system $ ./perf probe -nf --max-probes=512 -a 'vfs_fstatat $params' Segmentation fault (core dumped) This is observed while running perftool-testsuite_probe testcase. While running with verbose, its observed that segfault happens at: synthesize_probe_trace_arg () synthesize_probe_trace_command () probe_file.add_event () apply_perf_probe_events () __cmd_probe () cmd_probe () run_builtin () handle_internal_command () main () Code in synthesize_probe_trace_arg() access a null value and results in segfault. Data structure which is null: struct probe_trace_arg arg->value We are hitting a case where arg->value is null in probe point: "vfs_fstatat $params". This is happening since 'commit e896474fe485 ("getname_maybe_null() - the third variant of pathname copy-in")' Before the commit, probe point for vfs_fstatat was getting added only for one location: Writing event: p:probe/vfs_fstatat _text+6345404 dfd=%gpr3:s32 filename=%gpr4:x64 stat=%gpr5:x64 flags=%gpr6:s32 With this change, vfs_fstatat code is inlined for other locations in the code: Probe point found: __do_sys_lstat64+48 Probe point found: __do_sys_stat64+48 Probe point found: __do_sys_newlstat+48 Probe point found: __do_sys_newstat+48 Probe point found: vfs_fstatat+0 When trying to find matching dwarf information entry (DIE) from the debuginfo, the code incorrectly picks DIE which is not referring to vfs_fstatat. Snippet from dwarf entry in vmlinux debuginfo file. The main abstract die is: <1><4214883>: Abbrev Number: 147 (DW_TAG_subprogram) <4214885> DW_AT_external : 1 <4214885> DW_AT_name : (indirect string, offset: 0x17b9f3): vfs_fstatat With formal parameters: <2><4214896>: Abbrev Number: 51 (DW_TAG_formal_parameter) <4214897> DW_AT_name : dfd <2><42148a3>: Abbrev Number: 23 (DW_TAG_formal_parameter) <42148a4> DW_AT_name : (indirect string, offset: 0x8fda9): filename <2><42148b0>: Abbrev Number: 23 (DW_TAG_formal_parameter) <42148b1> DW_AT_name : (indirect string, offset: 0x16bd9c): stat <2><42148bd>: Abbrev Number: 23 (DW_TAG_formal_parameter) <42148be> DW_AT_name : (indirect string, offset: 0x39832b): flags While collecting variables/parameters for a probe point, the function copy_variables_cb() also looks at dwarf debug entries based on the instruction address. Snippet if (dwarf_haspc(die_mem, vf->pf->addr)) return DIE_FIND_CB_CONTINUE; else return DIE_FIND_CB_SIBLING; But incase of inlined function instance for vfs_fstatat, there are two entries which has the instruction address entry point as same. Instance 1: which is for vfs_fstatat and DW_AT_abstract_origin points to 0x4214883 (reference above for main abstract die) <3><42131fa>: Abbrev Number: 59 (DW_TAG_inlined_subroutine) <42131fb> DW_AT_abstract_origin: <0x4214883> <42131ff> DW_AT_entry_pc : 0xc00000000062b1e0 Instance 2: which is not for vfs_fstatat but for getname <5><4213270>: Abbrev Number: 39 (DW_TAG_inlined_subroutine) <4213271> DW_AT_abstract_origin: <0x4215b6b> <4213275> DW_AT_entry_pc : 0xc00000000062b1e0 But the copy_variables_cb() continues to add parameters from second instance also based on the dwarf_haspc() check. This results in formal parameters for getname also appended to params. But while filling in the args->value for these parameters, since these args are not part of dwarf with offset "42131fa". Hence value will be null. This incorrect args results in segfault when value field is accessed. Save the dwarf dieoffset of the actual DW_TAG_subprogram as part of "struct probe_finder". In copy_variables_cb(), include check to make sure the DW_AT_abstract_origin points to the correct entry if the dwarf_haspc() matches the instruction address. Signed-off-by: Athira Rajeev <atrajeev@linux.ibm.com> Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Link: https://lore.kernel.org/r/20250225123042.37263-1-atrajeev@linux.ibm.com Signed-off-by: Namhyung Kim <namhyung@kernel.org>
115 lines
3.3 KiB
C
115 lines
3.3 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _PROBE_FINDER_H
|
|
#define _PROBE_FINDER_H
|
|
|
|
#include <stdbool.h>
|
|
#include "intlist.h"
|
|
#include "build-id.h"
|
|
#include "probe-event.h"
|
|
#include <linux/ctype.h>
|
|
|
|
#define MAX_PROBE_BUFFER 1024
|
|
#define MAX_PROBES 128
|
|
#define MAX_PROBE_ARGS 128
|
|
|
|
#define PROBE_ARG_VARS "$vars"
|
|
#define PROBE_ARG_PARAMS "$params"
|
|
|
|
static inline int is_c_varname(const char *name)
|
|
{
|
|
/* TODO */
|
|
return isalpha(name[0]) || name[0] == '_';
|
|
}
|
|
|
|
#ifdef HAVE_LIBDW_SUPPORT
|
|
|
|
#include "dwarf-aux.h"
|
|
#include "debuginfo.h"
|
|
|
|
/* Check the language code is known C */
|
|
bool is_known_C_lang(int lang);
|
|
|
|
/* Find probe_trace_events specified by perf_probe_event from debuginfo */
|
|
int debuginfo__find_trace_events(struct debuginfo *dbg,
|
|
struct perf_probe_event *pev,
|
|
struct probe_trace_event **tevs);
|
|
|
|
/* Find a perf_probe_point from debuginfo */
|
|
int debuginfo__find_probe_point(struct debuginfo *dbg, u64 addr,
|
|
struct perf_probe_point *ppt);
|
|
|
|
/* Find a line range */
|
|
int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr);
|
|
|
|
/* Find available variables */
|
|
int debuginfo__find_available_vars_at(struct debuginfo *dbg,
|
|
struct perf_probe_event *pev,
|
|
struct variable_list **vls);
|
|
|
|
/* Find a src file from a DWARF tag path */
|
|
int find_source_path(const char *raw_path, const char *sbuild_id,
|
|
const char *comp_dir, char **new_path);
|
|
|
|
struct probe_finder {
|
|
struct perf_probe_event *pev; /* Target probe event */
|
|
struct debuginfo *dbg;
|
|
|
|
/* Callback when a probe point is found */
|
|
int (*callback)(Dwarf_Die *sc_die, struct probe_finder *pf);
|
|
|
|
/* For function searching */
|
|
int lno; /* Line number */
|
|
Dwarf_Addr addr; /* Address */
|
|
const char *fname; /* Real file name */
|
|
Dwarf_Die cu_die; /* Current CU */
|
|
Dwarf_Die sp_die;
|
|
Dwarf_Off abstrace_dieoffset;
|
|
struct intlist *lcache; /* Line cache for lazy match */
|
|
|
|
/* For variable searching */
|
|
/* Call Frame Information from .eh_frame. Owned by this struct. */
|
|
Dwarf_CFI *cfi_eh;
|
|
/* Call Frame Information from .debug_frame. Not owned. */
|
|
Dwarf_CFI *cfi_dbg;
|
|
Dwarf_Op *fb_ops; /* Frame base attribute */
|
|
unsigned int e_machine; /* ELF target machine arch */
|
|
unsigned int e_flags; /* ELF target machine flags */
|
|
struct perf_probe_arg *pvar; /* Current target variable */
|
|
struct probe_trace_arg *tvar; /* Current result variable */
|
|
bool skip_empty_arg; /* Skip non-exist args */
|
|
};
|
|
|
|
struct trace_event_finder {
|
|
struct probe_finder pf;
|
|
Dwfl_Module *mod; /* For solving symbols */
|
|
struct probe_trace_event *tevs; /* Found trace events */
|
|
int ntevs; /* Number of trace events */
|
|
int max_tevs; /* Max number of trace events */
|
|
};
|
|
|
|
struct available_var_finder {
|
|
struct probe_finder pf;
|
|
Dwfl_Module *mod; /* For solving symbols */
|
|
struct variable_list *vls; /* Found variable lists */
|
|
int nvls; /* Number of variable lists */
|
|
int max_vls; /* Max no. of variable lists */
|
|
bool child; /* Search child scopes */
|
|
};
|
|
|
|
struct line_finder {
|
|
struct line_range *lr; /* Target line range */
|
|
|
|
const char *fname; /* File name */
|
|
int lno_s; /* Start line number */
|
|
int lno_e; /* End line number */
|
|
Dwarf_Die cu_die; /* Current CU */
|
|
Dwarf_Die sp_die;
|
|
int found;
|
|
};
|
|
|
|
#else
|
|
#define is_known_C_lang(lang) (false)
|
|
#endif /* HAVE_LIBDW_SUPPORT */
|
|
|
|
#endif /*_PROBE_FINDER_H */
|