Skip to content

Commit 9136472

Browse files
Mikhail Kushnerovnashif
authored andcommitted
arch: x86: ia32: Implement perf stack thrace func
Implement stack trace function for x86_32 arch, that get required thread register values and unwind stack with it. Signed-off-by: Mikhail Kushnerov <[email protected]>
1 parent 7343350 commit 9136472

File tree

3 files changed

+91
-7
lines changed

3 files changed

+91
-7
lines changed

arch/x86/core/ia32.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ zephyr_library_sources_ifdef(CONFIG_GDBSTUB ia32/gdbstub.c)
2525

2626
zephyr_library_sources_ifdef(CONFIG_DEBUG_COREDUMP ia32/coredump.c)
2727

28+
zephyr_library_sources_ifdef(CONFIG_PROFILING_PERF ia32/perf.c)
29+
2830
zephyr_library_sources_ifdef(
2931
CONFIG_X86_USE_THREAD_LOCAL_STORAGE
3032
ia32/tls.c

arch/x86/core/ia32/intstub.S

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,12 @@ SECTION_FUNC(PINNED_TEXT, _interrupt_enter)
112112
* EAX = isr_param, EDX = isr
113113
*/
114114

115-
/* Push EDI as we will use it for scratch space.
115+
/* Push EBP as we will use it for scratch space.
116+
* Also it helps in stack unwinding
116117
* Rest of the callee-saved regs get saved by invocation of C
117118
* functions (isr handler, arch_swap(), etc)
118119
*/
119-
pushl %edi
120+
pushl %ebp
120121

121122
/* load %ecx with &_kernel */
122123

@@ -131,17 +132,17 @@ SECTION_FUNC(PINNED_TEXT, _interrupt_enter)
131132
jne alreadyOnIntStack
132133

133134
/*
134-
* switch to base of the interrupt stack: save esp in edi, then load
135+
* switch to base of the interrupt stack: save esp in ebp, then load
135136
* irq_stack pointer
136137
*/
137138

138-
movl %esp, %edi
139+
movl %esp, %ebp
139140
movl _kernel_offset_to_irq_stack(%ecx), %esp
140141

141142

142143
/* save thread's stack pointer onto base of interrupt stack */
143144

144-
pushl %edi /* Save stack pointer */
145+
pushl %ebp /* Save stack pointer */
145146

146147
#ifdef CONFIG_PM
147148
cmpl $0, _kernel_offset_to_idle(%ecx)
@@ -265,7 +266,7 @@ alreadyOnIntStack:
265266
#endif /* CONFIG_LAZY_FPU_SHARING */
266267

267268
/* Restore volatile registers and return to the interrupted thread */
268-
popl %edi
269+
popl %ebp
269270
popl %ecx
270271
popl %edx
271272
popl %eax
@@ -298,7 +299,7 @@ noReschedule:
298299
*/
299300

300301
nestedInterrupt:
301-
popl %edi
302+
popl %ebp
302303
popl %ecx /* pop volatile registers in reverse order */
303304
popl %edx
304305
popl %eax

arch/x86/core/ia32/perf.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2023 KNS Group LLC (YADRO)
3+
* Copyright (c) 2020 Yonatan Goldschmidt <[email protected]>
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <zephyr/kernel.h>
9+
10+
static bool valid_stack(uintptr_t addr, k_tid_t current)
11+
{
12+
return current->stack_info.start <= addr &&
13+
addr < current->stack_info.start + current->stack_info.size;
14+
}
15+
16+
/* interruption stack frame */
17+
struct isf {
18+
uint32_t ebp;
19+
uint32_t ecx;
20+
uint32_t edx;
21+
uint32_t eax;
22+
uint32_t eip;
23+
};
24+
25+
/*
26+
* This function use frame pointers to unwind stack and get trace of return addresses.
27+
* Return addresses are translated in corresponding function's names using .elf file.
28+
* So we get function call trace
29+
*/
30+
size_t arch_perf_current_stack_trace(uintptr_t *buf, size_t size)
31+
{
32+
if (size < 1U)
33+
return 0;
34+
35+
size_t idx = 0;
36+
37+
const struct isf * const isf =
38+
*((struct isf **)(((void **)_current_cpu->irq_stack)-1));
39+
/*
40+
* In x86 (arch/x86/core/ia32/intstub.S) %eip and %ebp
41+
* are saved at the beginning of _interrupt_enter in order, that described
42+
* in struct esf. Core switch %esp to
43+
* _current_cpu->irq_stack and push %esp on irq stack
44+
*
45+
* The following lines lines do the reverse things to get %eip and %ebp
46+
* from thread stack
47+
*/
48+
void **fp = (void **)isf->ebp;
49+
50+
/*
51+
* %ebp is frame pointer.
52+
*
53+
* stack frame in memory:
54+
* (addresses growth up)
55+
* ....
56+
* ra
57+
* %ebp (next) <- %ebp (curr)
58+
* ....
59+
*/
60+
61+
buf[idx++] = (uintptr_t)isf->eip;
62+
while (valid_stack((uintptr_t)fp, _current)) {
63+
if (idx >= size)
64+
return 0;
65+
66+
buf[idx++] = (uintptr_t)fp[1];
67+
void **new_fp = (void **)fp[0];
68+
69+
/*
70+
* anti-infinity-loop if
71+
* new_fp can't be smaller than fp, cause the stack is growing down
72+
* and trace moves deeper into the stack
73+
*/
74+
if (new_fp <= fp) {
75+
break;
76+
}
77+
fp = new_fp;
78+
}
79+
80+
return idx;
81+
}

0 commit comments

Comments
 (0)