Skip to content

Commit 0f87b48

Browse files
mhiramatmcgrof
authored andcommitted
tracing: Add relative-stacktrace option
Add "relative-stacktrace" option to save the stacktrace entries as offset from the _stext or module's text section. For the module address, each entry also saves the first 4bytes of the build_id of the module so that it can find the appropriate symbol from the offset. By using this build_id, we can also easily find the corresponding modules off-line. Thus this stacktrace data is in the persistent ring buffer, the kernel can find appropriate modules after reboot without saving previous live information. This feature also shows the module name after the symbol as below. event-sample-1622 [005] ...1. 2112.978105: <stack trace> => event_triggers_post_call => trace_event_raw_event_foo_bar [trace_events_sample] => do_simple_thread_func [trace_events_sample] => simple_thread [trace_events_sample] => kthread => ret_from_fork => ret_from_fork_asm The downside is the cost of time and memory (on 32bit arch). This needs to find the module if the address in the module, and save both of 32bit offset and 32bit hash for each entry, this may consume twice memory on 32bit arch. On 64bit arch, the amount of consuming memory is the same. Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
1 parent 64c8ae7 commit 0f87b48

6 files changed

Lines changed: 133 additions & 6 deletions

File tree

include/linux/trace.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ struct trace_export {
2626
int flags;
2727
};
2828

29+
struct ftrace_rel_caller {
30+
int offset;
31+
unsigned int build_id32;
32+
} __packed;
33+
2934
struct trace_array;
3035

3136
#ifdef CONFIG_TRACING

kernel/trace/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ config TRACING
169169
bool
170170
select RING_BUFFER
171171
select STACKTRACE if STACKTRACE_SUPPORT
172+
select MODULE_BUILD_ID if STACKTRACE_SUPPORT
172173
select TRACEPOINTS
173174
select NOP_TRACER
174175
select BINARY_PRINTF

kernel/trace/trace.c

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2916,16 +2916,41 @@ struct ftrace_stacks {
29162916
static DEFINE_PER_CPU(struct ftrace_stacks, ftrace_stacks);
29172917
static DEFINE_PER_CPU(int, ftrace_stack_reserve);
29182918

2919+
static void record_as_offset(struct ftrace_rel_caller *caller, unsigned long addr)
2920+
{
2921+
if (likely(core_kernel_text(addr))) {
2922+
caller->build_id32 = 0;
2923+
caller->offset = addr - (unsigned long)_stext;
2924+
} else if (addr == FTRACE_TRAMPOLINE_MARKER) {
2925+
caller->build_id32 = 0;
2926+
caller->offset = (int)FTRACE_TRAMPOLINE_MARKER;
2927+
} else {
2928+
struct module *mod = __module_text_address(addr);
2929+
2930+
if (mod) {
2931+
unsigned long base = (unsigned long)mod->mem[MOD_TEXT].base;
2932+
2933+
caller->offset = addr - base;
2934+
caller->build_id32 = *(unsigned int *)mod->build_id;
2935+
} else {
2936+
caller->build_id32 = 0;
2937+
caller->offset = addr - (unsigned long)_stext;
2938+
}
2939+
}
2940+
}
2941+
29192942
static void __ftrace_trace_stack(struct trace_array *tr,
29202943
struct trace_buffer *buffer,
29212944
unsigned int trace_ctx,
29222945
int skip, struct pt_regs *regs)
29232946
{
2947+
struct rel_stack_entry *rel_entry;
29242948
struct ring_buffer_event *event;
29252949
unsigned int size, nr_entries;
29262950
struct ftrace_stack *fstack;
29272951
struct stack_entry *entry;
29282952
int stackidx;
2953+
int type;
29292954

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

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

29412967
stackidx = __this_cpu_inc_return(ftrace_stack_reserve) - 1;
29422968

@@ -2977,16 +3003,28 @@ static void __ftrace_trace_stack(struct trace_array *tr,
29773003
}
29783004
#endif
29793005

2980-
event = __trace_buffer_lock_reserve(buffer, TRACE_STACK,
2981-
struct_size(entry, caller, nr_entries),
3006+
if (type == TRACE_REL_STACK)
3007+
size = struct_size(rel_entry, caller, nr_entries);
3008+
else
3009+
size = struct_size(entry, caller, nr_entries);
3010+
3011+
event = __trace_buffer_lock_reserve(buffer, type, size,
29823012
trace_ctx);
29833013
if (!event)
29843014
goto out;
2985-
entry = ring_buffer_event_data(event);
29863015

2987-
entry->size = nr_entries;
2988-
memcpy(&entry->caller, fstack->calls,
2989-
flex_array_size(entry, caller, nr_entries));
3016+
if (type == TRACE_REL_STACK) {
3017+
rel_entry = ring_buffer_event_data(event);
3018+
rel_entry->size = nr_entries;
3019+
for (int i = 0; i < nr_entries; i++)
3020+
record_as_offset((struct ftrace_rel_caller *)&rel_entry->caller[i],
3021+
fstack->calls[i]);
3022+
} else {
3023+
entry = ring_buffer_event_data(event);
3024+
entry->size = nr_entries;
3025+
memcpy(&entry->caller, fstack->calls,
3026+
flex_array_size(entry, caller, nr_entries));
3027+
}
29903028

29913029
__buffer_unlock_commit(buffer, event);
29923030

kernel/trace/trace.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ enum trace_type {
5555
TRACE_TIMERLAT,
5656
TRACE_RAW_DATA,
5757
TRACE_FUNC_REPEATS,
58+
TRACE_REL_STACK,
5859

5960
__TRACE_LAST_TYPE,
6061
};
@@ -511,6 +512,7 @@ extern void __ftrace_bad_type(void);
511512
IF_ASSIGN(var, ent, struct ftrace_entry, TRACE_FN); \
512513
IF_ASSIGN(var, ent, struct ctx_switch_entry, 0); \
513514
IF_ASSIGN(var, ent, struct stack_entry, TRACE_STACK); \
515+
IF_ASSIGN(var, ent, struct rel_stack_entry, TRACE_REL_STACK);\
514516
IF_ASSIGN(var, ent, struct userstack_entry, TRACE_USER_STACK);\
515517
IF_ASSIGN(var, ent, struct print_entry, TRACE_PRINT); \
516518
IF_ASSIGN(var, ent, struct bprint_entry, TRACE_BPRINT); \
@@ -1350,6 +1352,7 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
13501352
C(TRACE_PRINTK, "trace_printk_dest"), \
13511353
C(PAUSE_ON_TRACE, "pause-on-trace"), \
13521354
C(HASH_PTR, "hash-ptr"), /* Print hashed pointer */ \
1355+
C(REL_STACK, "relative-stacktrace"), \
13531356
FUNCTION_FLAGS \
13541357
FGRAPH_FLAGS \
13551358
STACK_FLAGS \

kernel/trace/trace_entries.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,24 @@ FTRACE_ENTRY(kernel_stack, stack_entry,
229229
(void *)__entry->caller[6], (void *)__entry->caller[7])
230230
);
231231

232+
FTRACE_ENTRY(kernel_rel_stack, rel_stack_entry,
233+
234+
TRACE_REL_STACK,
235+
236+
F_STRUCT(
237+
__field( int, size )
238+
__stack_array( u64, caller, FTRACE_STACK_ENTRIES, size)
239+
),
240+
241+
F_printk("\t=> %ps\n\t=> %ps\n\t=> %ps\n"
242+
"\t=> %ps\n\t=> %ps\n\t=> %ps\n"
243+
"\t=> %ps\n\t=> %ps\n",
244+
(void *)__entry->caller[0], (void *)__entry->caller[1],
245+
(void *)__entry->caller[2], (void *)__entry->caller[3],
246+
(void *)__entry->caller[4], (void *)__entry->caller[5],
247+
(void *)__entry->caller[6], (void *)__entry->caller[7])
248+
);
249+
232250
FTRACE_ENTRY(user_stack, userstack_entry,
233251

234252
TRACE_USER_STACK,

kernel/trace/trace_output.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,67 @@ static struct trace_event trace_stack_event = {
12811281
.funcs = &trace_stack_funcs,
12821282
};
12831283

1284+
/* TRACE_REL_STACK */
1285+
1286+
static enum print_line_t trace_rel_stack_print(struct trace_iterator *iter,
1287+
int flags, struct trace_event *event)
1288+
{
1289+
struct ftrace_rel_caller *p;
1290+
struct rel_stack_entry *field;
1291+
struct trace_seq *s = &iter->seq;
1292+
unsigned long base;
1293+
struct module *mod;
1294+
1295+
trace_assign_type(field, iter->ent);
1296+
1297+
trace_seq_puts(s, "<stack trace>\n");
1298+
1299+
for (int i = 0; i < field->size; i++) {
1300+
p = (struct ftrace_rel_caller *)&field->caller[i];
1301+
1302+
if (trace_seq_has_overflowed(s))
1303+
break;
1304+
1305+
trace_seq_puts(s, " => ");
1306+
if (p->offset == FTRACE_TRAMPOLINE_MARKER) {
1307+
trace_seq_puts(s, "[FTRACE TRAMPOLINE]\n");
1308+
continue;
1309+
}
1310+
if (p->build_id32) {
1311+
unsigned char build_id32[4];
1312+
1313+
guard(rcu)();
1314+
*(unsigned int *)build_id32 = p->build_id32;
1315+
mod = __module_build_id(build_id32, 4);
1316+
if (!mod) {
1317+
trace_seq_printf(s, "%x [MODULE %02x%02x%02x%02x]\n",
1318+
p->offset, build_id32[0], build_id32[1],
1319+
build_id32[2], build_id32[3]);
1320+
continue;
1321+
}
1322+
base = (unsigned long)mod->mem[MOD_TEXT].base;
1323+
} else {
1324+
mod = NULL;
1325+
base = (unsigned long)_stext;
1326+
}
1327+
seq_print_ip_sym(s, base + p->offset, flags);
1328+
if (mod)
1329+
trace_seq_printf(s, " [%s]", mod->name);
1330+
trace_seq_putc(s, '\n');
1331+
}
1332+
1333+
return trace_handle_return(s);
1334+
}
1335+
1336+
static struct trace_event_functions trace_rel_stack_funcs = {
1337+
.trace = trace_rel_stack_print,
1338+
};
1339+
1340+
static struct trace_event trace_rel_stack_event = {
1341+
.type = TRACE_REL_STACK,
1342+
.funcs = &trace_rel_stack_funcs,
1343+
};
1344+
12841345
/* TRACE_USER_STACK */
12851346
static enum print_line_t trace_user_stack_print(struct trace_iterator *iter,
12861347
int flags, struct trace_event *event)
@@ -1724,6 +1785,7 @@ static struct trace_event *events[] __initdata = {
17241785
&trace_ctx_event,
17251786
&trace_wake_event,
17261787
&trace_stack_event,
1788+
&trace_rel_stack_event,
17271789
&trace_user_stack_event,
17281790
&trace_bputs_event,
17291791
&trace_bprint_event,

0 commit comments

Comments
 (0)