Skip to content

Commit 13156ad

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

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

arch/riscv/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ zephyr_library_sources_ifdef(CONFIG_USERSPACE userspace.S)
2727
zephyr_library_sources_ifdef(CONFIG_SEMIHOST semihost.c)
2828
zephyr_library_sources_ifdef(CONFIG_EXCEPTION_STACK_TRACE stacktrace.c)
2929
zephyr_linker_sources(ROM_START SORT_KEY 0x0vectors vector_table.ld)
30+
zephyr_library_sources_ifdef(CONFIG_PROFILING_PERF perf.c)

arch/riscv/core/perf.c

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright (c) 2023 KNS Group LLC (YADRO)
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
9+
static bool valid_stack(uintptr_t addr, k_tid_t current)
10+
{
11+
return current->stack_info.start <= addr &&
12+
addr < current->stack_info.start + current->stack_info.size;
13+
}
14+
15+
/*
16+
* This function use frame pointers to unwind stack and get trace of return addresses.
17+
* Return addresses are translated in corresponding function's names using .elf file.
18+
* So we get function call trace
19+
*/
20+
size_t arch_perf_current_stack_trace(uintptr_t *buf, size_t size)
21+
{
22+
if (size < 2U)
23+
return 0;
24+
25+
size_t idx = 0;
26+
27+
/*
28+
* In riscv (arch/riscv/core/isr.S) ra, ip($mepc) and fp($s0) are saved
29+
* at the beginning of _isr_wrapper in order, specified by z_arch_esf_t.
30+
* Then, before calling interruption handler, core switch $sp to
31+
* _current_cpu->irq_stack and save $sp with offset -16 on irq stack
32+
*
33+
* The following lines lines do the reverse things to get ra, ip anf fp
34+
* from thread stack
35+
*/
36+
const struct arch_esf * const esf =
37+
*((struct arch_esf **)(((uintptr_t)_current_cpu->irq_stack) - 16));
38+
39+
/*
40+
* $s0 is used as frame pointer.
41+
*
42+
* stack frame in memory (commonly):
43+
* (addresses growth up)
44+
* ....
45+
* [-] <- $fp($s0) (curr)
46+
* $ra
47+
* $fp($s0) (next)
48+
* ....
49+
*
50+
* If function do not call any other function, compiller may not save $ra,
51+
* then stack frame will be:
52+
* ....
53+
* [-] <- $fp($s0) (curr)
54+
* $fp($s0) (next)
55+
* ....
56+
*
57+
*/
58+
void **fp = (void **)esf->s0;
59+
void **new_fp = (void **)fp[-1];
60+
61+
buf[idx++] = (uintptr_t)esf->mepc;
62+
63+
/*
64+
* During function prologue and epilogue fp is equal to fp of
65+
* previous function stack frame, it looks like second function
66+
* from top is missed.
67+
* So saving $ra will help in case when irq occurred in
68+
* function prologue or epilogue.
69+
*/
70+
buf[idx++] = (uintptr_t)esf->ra;
71+
if (valid_stack((uintptr_t)new_fp, _current)) {
72+
fp = new_fp;
73+
}
74+
while (valid_stack((uintptr_t)fp, _current)) {
75+
if (idx >= size)
76+
return 0;
77+
78+
buf[idx++] = (uintptr_t)fp[-1];
79+
new_fp = (void **)fp[-2];
80+
81+
/*
82+
* anti-infinity-loop if
83+
* new_fp can't be smaller than fp, cause the stack is growing down
84+
* and trace moves deeper into the stack
85+
*/
86+
if (new_fp <= fp) {
87+
break;
88+
}
89+
fp = new_fp;
90+
}
91+
92+
return idx;
93+
}

0 commit comments

Comments
 (0)