Skip to content
Closed
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
8 changes: 7 additions & 1 deletion include/linux/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ struct module {
/* Unique handle for this module */
char name[MODULE_NAME_LEN];

#ifdef CONFIG_STACKTRACE_BUILD_ID
#ifdef CONFIG_MODULE_BUILD_ID
/* Module build ID */
unsigned char build_id[BUILD_ID_SIZE_MAX];
#endif
Expand Down Expand Up @@ -625,6 +625,7 @@ static inline bool module_is_coming(struct module *mod)

struct module *__module_text_address(unsigned long addr);
struct module *__module_address(unsigned long addr);
struct module *__module_build_id(unsigned char *build_id, int size);
bool is_module_address(unsigned long addr);
bool __is_module_percpu_address(unsigned long addr, unsigned long *can_addr);
bool is_module_percpu_address(unsigned long addr);
Expand Down Expand Up @@ -794,6 +795,11 @@ static inline struct module *__module_text_address(unsigned long addr)
return NULL;
}

static inline struct module *__module_build_id(unsigned char *build_id, int size)
{
return NULL;
}

static inline bool is_module_address(unsigned long addr)
{
return false;
Expand Down
5 changes: 5 additions & 0 deletions include/linux/trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ struct trace_export {
int flags;
};

struct ftrace_rel_caller {
int offset;
unsigned int build_id32;
} __packed;

struct trace_array;

#ifdef CONFIG_TRACING
Expand Down
3 changes: 3 additions & 0 deletions kernel/module/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ if MODULES
config MODULE_DEBUGFS
bool

config MODULE_BUILD_ID
bool

config MODULE_DEBUG
bool "Module debugging"
depends on DEBUG_FS
Expand Down
4 changes: 2 additions & 2 deletions kernel/module/kallsyms.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ void add_kallsyms(struct module *mod, const struct load_info *info)
mod->core_kallsyms.num_symtab = ndst;
}

#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
#if IS_ENABLED(CONFIG_MODULE_BUILD_ID)
void init_build_id(struct module *mod, const struct load_info *info)
{
const Elf_Shdr *sechdr;
Expand Down Expand Up @@ -335,7 +335,7 @@ int module_address_lookup(unsigned long addr,
if (modname)
*modname = mod->name;
if (modbuildid) {
#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
#if IS_ENABLED(CONFIG_MODULE_BUILD_ID)
*modbuildid = mod->build_id;
#else
*modbuildid = NULL;
Expand Down
29 changes: 29 additions & 0 deletions kernel/module/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3798,6 +3798,35 @@ struct module *__module_text_address(unsigned long addr)
return mod;
}

/**
* __module_build_id() - get the module whose build_id start with an array.
* @build_id: the first part of the build_id.
* @size: the size of @build_id.
*
* Must be called with preempt disabled or module mutex held so that
* module doesn't get freed during this.
*/
struct module *__module_build_id(unsigned char *build_id, int size)
{
#ifdef CONFIG_MODULE_BUILD_ID
struct module *mod;

if (size < 0)
return NULL;

if (size > BUILD_ID_SIZE_MAX)
size = BUILD_ID_SIZE_MAX;

list_for_each_entry_rcu(mod, &modules, list) {
if (mod->state == MODULE_STATE_UNFORMED)
continue;
if (!memcmp(mod->build_id, build_id, size))
return mod;
}
#endif
return NULL;
}

/* Don't grab lock, we're oopsing. */
void print_modules(void)
{
Expand Down
1 change: 1 addition & 0 deletions kernel/trace/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ config TRACING
bool
select RING_BUFFER
select STACKTRACE if STACKTRACE_SUPPORT
select MODULE_BUILD_ID if STACKTRACE_SUPPORT
select TRACEPOINTS
select NOP_TRACER
select BINARY_PRINTF
Expand Down
50 changes: 44 additions & 6 deletions kernel/trace/trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -2916,16 +2916,41 @@ struct ftrace_stacks {
static DEFINE_PER_CPU(struct ftrace_stacks, ftrace_stacks);
static DEFINE_PER_CPU(int, ftrace_stack_reserve);

static void record_as_offset(struct ftrace_rel_caller *caller, unsigned long addr)
{
if (likely(core_kernel_text(addr))) {
caller->build_id32 = 0;
caller->offset = addr - (unsigned long)_stext;
} else if (addr == FTRACE_TRAMPOLINE_MARKER) {
caller->build_id32 = 0;
caller->offset = (int)FTRACE_TRAMPOLINE_MARKER;
} else {
struct module *mod = __module_text_address(addr);

if (mod) {
unsigned long base = (unsigned long)mod->mem[MOD_TEXT].base;

caller->offset = addr - base;
caller->build_id32 = *(unsigned int *)mod->build_id;
} else {
caller->build_id32 = 0;
caller->offset = addr - (unsigned long)_stext;
}
}
}

static void __ftrace_trace_stack(struct trace_array *tr,
struct trace_buffer *buffer,
unsigned int trace_ctx,
int skip, struct pt_regs *regs)
{
struct rel_stack_entry *rel_entry;
struct ring_buffer_event *event;
unsigned int size, nr_entries;
struct ftrace_stack *fstack;
struct stack_entry *entry;
int stackidx;
int type;

/*
* Add one, for this function and the call to save_stack_trace()
Expand All @@ -2937,6 +2962,7 @@ static void __ftrace_trace_stack(struct trace_array *tr,
#endif

preempt_disable_notrace();
type = (tr->trace_flags & TRACE_ITER_REL_STACK) ? TRACE_REL_STACK : TRACE_STACK;

stackidx = __this_cpu_inc_return(ftrace_stack_reserve) - 1;

Expand Down Expand Up @@ -2977,16 +3003,28 @@ static void __ftrace_trace_stack(struct trace_array *tr,
}
#endif

event = __trace_buffer_lock_reserve(buffer, TRACE_STACK,
struct_size(entry, caller, nr_entries),
if (type == TRACE_REL_STACK)
size = struct_size(rel_entry, caller, nr_entries);
else
size = struct_size(entry, caller, nr_entries);

event = __trace_buffer_lock_reserve(buffer, type, size,
trace_ctx);
if (!event)
goto out;
entry = ring_buffer_event_data(event);

entry->size = nr_entries;
memcpy(&entry->caller, fstack->calls,
flex_array_size(entry, caller, nr_entries));
if (type == TRACE_REL_STACK) {
rel_entry = ring_buffer_event_data(event);
rel_entry->size = nr_entries;
for (int i = 0; i < nr_entries; i++)
record_as_offset((struct ftrace_rel_caller *)&rel_entry->caller[i],
fstack->calls[i]);
} else {
entry = ring_buffer_event_data(event);
entry->size = nr_entries;
memcpy(&entry->caller, fstack->calls,
flex_array_size(entry, caller, nr_entries));
}

__buffer_unlock_commit(buffer, event);

Expand Down
3 changes: 3 additions & 0 deletions kernel/trace/trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum trace_type {
TRACE_TIMERLAT,
TRACE_RAW_DATA,
TRACE_FUNC_REPEATS,
TRACE_REL_STACK,

__TRACE_LAST_TYPE,
};
Expand Down Expand Up @@ -511,6 +512,7 @@ extern void __ftrace_bad_type(void);
IF_ASSIGN(var, ent, struct ftrace_entry, TRACE_FN); \
IF_ASSIGN(var, ent, struct ctx_switch_entry, 0); \
IF_ASSIGN(var, ent, struct stack_entry, TRACE_STACK); \
IF_ASSIGN(var, ent, struct rel_stack_entry, TRACE_REL_STACK);\
IF_ASSIGN(var, ent, struct userstack_entry, TRACE_USER_STACK);\
IF_ASSIGN(var, ent, struct print_entry, TRACE_PRINT); \
IF_ASSIGN(var, ent, struct bprint_entry, TRACE_BPRINT); \
Expand Down Expand Up @@ -1350,6 +1352,7 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
C(TRACE_PRINTK, "trace_printk_dest"), \
C(PAUSE_ON_TRACE, "pause-on-trace"), \
C(HASH_PTR, "hash-ptr"), /* Print hashed pointer */ \
C(REL_STACK, "relative-stacktrace"), \
FUNCTION_FLAGS \
FGRAPH_FLAGS \
STACK_FLAGS \
Expand Down
18 changes: 18 additions & 0 deletions kernel/trace/trace_entries.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,24 @@ FTRACE_ENTRY(kernel_stack, stack_entry,
(void *)__entry->caller[6], (void *)__entry->caller[7])
);

FTRACE_ENTRY(kernel_rel_stack, rel_stack_entry,

TRACE_REL_STACK,

F_STRUCT(
__field( int, size )
__stack_array( u64, caller, FTRACE_STACK_ENTRIES, size)
),

F_printk("\t=> %ps\n\t=> %ps\n\t=> %ps\n"
"\t=> %ps\n\t=> %ps\n\t=> %ps\n"
"\t=> %ps\n\t=> %ps\n",
(void *)__entry->caller[0], (void *)__entry->caller[1],
(void *)__entry->caller[2], (void *)__entry->caller[3],
(void *)__entry->caller[4], (void *)__entry->caller[5],
(void *)__entry->caller[6], (void *)__entry->caller[7])
);

FTRACE_ENTRY(user_stack, userstack_entry,

TRACE_USER_STACK,
Expand Down
62 changes: 62 additions & 0 deletions kernel/trace/trace_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,67 @@ static struct trace_event trace_stack_event = {
.funcs = &trace_stack_funcs,
};

/* TRACE_REL_STACK */

static enum print_line_t trace_rel_stack_print(struct trace_iterator *iter,
int flags, struct trace_event *event)
{
struct ftrace_rel_caller *p;
struct rel_stack_entry *field;
struct trace_seq *s = &iter->seq;
unsigned long base;
struct module *mod;

trace_assign_type(field, iter->ent);

trace_seq_puts(s, "<stack trace>\n");

for (int i = 0; i < field->size; i++) {
p = (struct ftrace_rel_caller *)&field->caller[i];

if (trace_seq_has_overflowed(s))
break;

trace_seq_puts(s, " => ");
if (p->offset == FTRACE_TRAMPOLINE_MARKER) {
trace_seq_puts(s, "[FTRACE TRAMPOLINE]\n");
continue;
}
if (p->build_id32) {
unsigned char build_id32[4];

guard(rcu)();
*(unsigned int *)build_id32 = p->build_id32;
mod = __module_build_id(build_id32, 4);
if (!mod) {
trace_seq_printf(s, "%x [MODULE %02x%02x%02x%02x]\n",
p->offset, build_id32[0], build_id32[1],
build_id32[2], build_id32[3]);
continue;
}
base = (unsigned long)mod->mem[MOD_TEXT].base;
} else {
mod = NULL;
base = (unsigned long)_stext;
}
seq_print_ip_sym(s, base + p->offset, flags);
if (mod)
trace_seq_printf(s, " [%s]", mod->name);
trace_seq_putc(s, '\n');
}

return trace_handle_return(s);
}

static struct trace_event_functions trace_rel_stack_funcs = {
.trace = trace_rel_stack_print,
};

static struct trace_event trace_rel_stack_event = {
.type = TRACE_REL_STACK,
.funcs = &trace_rel_stack_funcs,
};

/* TRACE_USER_STACK */
static enum print_line_t trace_user_stack_print(struct trace_iterator *iter,
int flags, struct trace_event *event)
Expand Down Expand Up @@ -1724,6 +1785,7 @@ static struct trace_event *events[] __initdata = {
&trace_ctx_event,
&trace_wake_event,
&trace_stack_event,
&trace_rel_stack_event,
&trace_user_stack_event,
&trace_bputs_event,
&trace_bprint_event,
Expand Down
1 change: 1 addition & 0 deletions lib/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ config PRINTK_CALLER
config STACKTRACE_BUILD_ID
bool "Show build ID information in stacktraces"
depends on PRINTK
select MODULE_BUILD_ID if MODULES
help
Selecting this option adds build ID information for symbols in
stacktraces printed with the printk format '%p[SR]b'.
Expand Down
Loading