summaryrefslogtreecommitdiff
path: root/libcpu/risc-v/virt64/backtrace.c
blob: c10d9f352290f6def200fa2bb5fc0cbc8b9ffb5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 */

#include <rtthread.h>
#ifdef RT_USING_SMART
#include <lwp_arch.h>

#define TRANCE_LEVEL 20

extern rt_ubase_t __text_start[];
extern rt_ubase_t __text_end[];

static char *_get_elf_name(size_t sepc);

void rt_hw_backtrace(rt_uint32_t *ffp, rt_ubase_t sepc)
{
    rt_ubase_t *ra;
    rt_ubase_t *fp;
    rt_ubase_t vas, vae;
    int i, j;

    rt_kprintf("riscv64-unknown-linux-musl-addr2line -e %s -a -f", _get_elf_name(sepc));

    fp = (rt_ubase_t *)ffp;

    if (!fp)
    {
        asm volatile("mv %0, s0"
                     : "=r"(fp));
    }

    if (sepc)
    {
        rt_kprintf(" %p", sepc - 0x4);
    }

    if (fp > (rt_ubase_t *)USER_VADDR_START && fp < (rt_ubase_t *)USER_VADDR_TOP)
    {
        vas = USER_VADDR_START;
        vae = USER_VADDR_TOP;
    }
    else
    {
        vas = (rt_ubase_t)&__text_start;
        vae = (rt_ubase_t)&__text_end;
    }

    for (i = j = 0; i < TRANCE_LEVEL; i++)
    {
        if (RT_ALIGN((rt_ubase_t)fp, sizeof(void *)) != (rt_ubase_t)fp)
        {
            break;
        }

        ra = fp - 1;
        if (!rt_kmem_v2p(ra) || *ra < vas || *ra > vae)
            break;

        rt_kprintf(" %p", *ra - 0x04);

        fp = fp - 2;
        if (!rt_kmem_v2p(fp))
            break;
        fp = (rt_ubase_t *)(*fp);
        if (!fp)
            break;
    }

    rt_kputs("\r\n");
}

static void _assert_backtrace_cb(const char *ex, const char *func, rt_size_t line)
{
    rt_hw_interrupt_disable();
    rt_kprintf("(%s) assertion failed at function:%s, line number:%d \n", ex, func, line);

    rt_hw_backtrace(0, 0);
    rt_hw_cpu_shutdown();
}

static int rt_hw_backtrace_init(void)
{
    rt_assert_set_hook(_assert_backtrace_cb);

    return 0;
}
INIT_BOARD_EXPORT(rt_hw_backtrace_init);

static void backtrace_test(int args, char *argv[])
{
    int *p = (void *)-1;
    init_fn_t ft = 0;

    if (args < 2)
    {
        rt_kprintf("backtrace_test usage:backtrace_test a(assert)/m(invalid memory)/i(illegal instruction)\r\n");
        return;
    }

    if (!rt_strcmp(argv[1], "a"))
    {
        rt_kprintf("Assert test:\r\n", argv[1]);
        RT_ASSERT(0);
    }
    else if (!rt_strcmp(argv[1], "m"))
    {
        rt_kprintf("Access invalid memory:\r\n", argv[1]);
        *p = 0;
    }
    else if (!rt_strcmp(argv[1], "i"))
    {
        rt_kprintf("Illegal instruction:\r\n", argv[1]);
        ft();
    }
    else
    {
        rt_kprintf("Unknown cmd :%s.\r\n", argv[1]);
    }
}
MSH_CMD_EXPORT(backtrace_test, backtrace test case);

extern struct rt_thread *rt_current_thread;

#define IN_USERSPACE (sepc > USER_VADDR_START && sepc < USER_VADDR_TOP)

static char *_get_elf_name(size_t sepc)
{
    return IN_USERSPACE ? rt_current_thread->name : "rtthread.elf";
}

#endif /* RT_USING_SMART */