diff --git a/arch/Kconfig b/arch/Kconfig index 2ec252ecaed02..c4c095ddcc9ae 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -141,6 +141,7 @@ config ARCH_X86_64 select PCI_LATE_DRIVERS_REGISTER if PCI select LIBC_ARCH_ELF_64BIT if LIBC_ARCH_ELF select ARCH_TOOLCHAIN_GNU + select ARCH_HAVE_BACKTRACE ---help--- x86-64 architectures. diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index fad91e08cf04a..ebc840d94f396 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -49,6 +49,24 @@ config ARCH_CHIP_X86_64_CUSTOM endchoice +if SCHED_BACKTRACE +choice + prompt "Choose x86_64 unwinder" + default X86_64_UNWINDER_FRAME_POINTER + ---help--- + This determines which method will be used for unwinding nuttx stack + traces for debug. + +config X86_64_UNWINDER_FRAME_POINTER + bool "Frame pointer unwinder" + select FRAME_POINTER + ---help--- + This option enables the frame pointer unwinder for unwinding + nuttx stack traces. + +endchoice # Choose x86_64 unwinder +endif + # CPU features config ARCH_HAVE_MMX diff --git a/arch/x86_64/src/common/Toolchain.defs b/arch/x86_64/src/common/Toolchain.defs index b3f36be40f863..d7a07fb0feef3 100644 --- a/arch/x86_64/src/common/Toolchain.defs +++ b/arch/x86_64/src/common/Toolchain.defs @@ -28,6 +28,10 @@ else ifeq ($(CONFIG_DEBUG_FULLOPT),y) ARCHOPTIMIZATION += -Os endif +ifeq ($(CONFIG_FRAME_POINTER),y) + ARCHOPTIMIZATION += -fno-omit-frame-pointer -fno-optimize-sibling-calls +endif + ARCHCPUFLAGS = -fPIC -fno-stack-protector -mno-red-zone -mrdrnd ARCHPICFLAGS = -fPIC ARCHWARNINGS = -Wall -Wstrict-prototypes -Wshadow -Wundef diff --git a/arch/x86_64/src/intel64/CMakeLists.txt b/arch/x86_64/src/intel64/CMakeLists.txt index f5b1a76977269..8985d39ea85cb 100644 --- a/arch/x86_64/src/intel64/CMakeLists.txt +++ b/arch/x86_64/src/intel64/CMakeLists.txt @@ -48,6 +48,10 @@ set(SRCS intel64_check_capability.c intel64_cpu.c) +if(CONFIG_x86_64_UNWINDER_FRAME_POINTER) + list(APPEND SRCS intel64_backtrace_fp.c) +endif() + if(CONFIG_MM_PGALLOC) list(APPEND SRCS intel64_pgalloc.c) endif() diff --git a/arch/x86_64/src/intel64/Make.defs b/arch/x86_64/src/intel64/Make.defs index e1c8579c977c9..4700b9379432a 100644 --- a/arch/x86_64/src/intel64/Make.defs +++ b/arch/x86_64/src/intel64/Make.defs @@ -34,6 +34,10 @@ CHIP_CSRCS = intel64_start.c intel64_handlers.c intel64_idle.c intel64_lowsetup CHIP_CSRCS += intel64_serial.c intel64_rng.c intel64_check_capability.c CHIP_CSRCS += intel64_cpu.c +ifeq ($(CONFIG_x86_64_UNWINDER_FRAME_POINTER),y) + CMN_CSRCS += intel64_backtrace_fp.c +endif + ifeq ($(CONFIG_MM_PGALLOC),y) CHIP_CSRCS += intel64_pgalloc.c endif diff --git a/arch/x86_64/src/intel64/intel64_backtrace_fp.c b/arch/x86_64/src/intel64/intel64_backtrace_fp.c new file mode 100644 index 0000000000000..dab1f36a57872 --- /dev/null +++ b/arch/x86_64/src/intel64/intel64_backtrace_fp.c @@ -0,0 +1,169 @@ +/**************************************************************************** + * arch/x86_64/src/intel64/intel64_backtrace_fp.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include "sched/sched.h" + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Name: backtrace + * + * Description: + * backtrace() parsing the return address through frame pointer + * + ****************************************************************************/ + +nosanitize_address +static int backtrace(uintptr_t *base, uintptr_t *limit, + uintptr_t *rbp, uintptr_t *rip, + void **buffer, int size, int *skip) +{ + int i = 0; + + if (rip) + { + if ((*skip)-- <= 0) + { + buffer[i++] = rip; + } + } + + for (; i < size; rbp = (uintptr_t *)(*rbp)) + { + if (rbp > limit || rbp < base || + *(uintptr_t *)rbp == 0) + { + break; + } + + if ((*skip)-- <= 0) + { + buffer[i++] = (uintptr_t *)(*(rbp + 1)); + } + } + + return i; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_backtrace + * + * Description: + * up_backtrace() returns a backtrace for the TCB, in the array + * pointed to by buffer. A backtrace is the series of currently active + * function calls for the program. Each item in the array pointed to by + * buffer is of type void *, and is the return address from the + * corresponding stack frame. The size argument specifies the maximum + * number of addresses that can be stored in buffer. If the backtrace is + * larger than size, then the addresses corresponding to the size most + * recent function calls are returned; to obtain the complete backtrace, + * make sure that buffer and size are large enough. + * + * Input Parameters: + * tcb - Address of the task's TCB + * buffer - Return address from the corresponding stack frame + * size - Maximum number of addresses that can be stored in buffer + * skip - number of addresses to be skipped + * + * Returned Value: + * up_backtrace() returns the number of addresses returned in buffer + * + * Assumptions: + * Have to make sure tcb keep safe during function executing, it means + * 1. Tcb have to be self or not-running. In SMP case, the running task + * PC & SP cannot be backtrace, as whose get from tcb is not the newest. + * 2. Tcb have to keep not be freed. In task exiting case, have to + * make sure the tcb get from pid and up_backtrace in one critical + * section procedure. + * + ****************************************************************************/ + +int up_backtrace(struct tcb_s *tcb, + void **buffer, int size, int skip) +{ + struct tcb_s *rtcb = running_task(); + int ret; + + if (size <= 0 || !buffer) + { + return 0; + } + + if (tcb == NULL || tcb == rtcb) + { + if (up_interrupt_context()) + { +#if CONFIG_ARCH_INTERRUPTSTACK > 3 + void *istackbase = (void *)up_get_intstackbase(this_cpu()); + + ret = backtrace(istackbase, + istackbase + IRQ_STACK_SIZE, + (void *)__builtin_frame_address(0), + NULL, buffer, size, &skip); +#else + ret = backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (void *)__builtin_frame_address(0), + NULL, buffer, size, &skip); +#endif /* CONFIG_ARCH_INTERRUPTSTACK > 3 */ + if (ret < size) + { + ret += backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (void *)up_current_regs()[REG_RBP], + (void *)up_current_regs()[REG_RIP], + &buffer[ret], size - ret, &skip); + } + } + else + { + ret = backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (void *)__builtin_frame_address(0), + NULL, buffer, size, &skip); + } + } + else + { + ret = backtrace(tcb->stack_base_ptr, + tcb->stack_base_ptr + tcb->adj_stack_size, + (void *)tcb->xcp.regs[REG_RBP], + (void *)tcb->xcp.regs[REG_RIP], + buffer, size, &skip); + } + + return ret; +} diff --git a/arch/x86_64/src/intel64/intel64_handlers.c b/arch/x86_64/src/intel64/intel64_handlers.c index a4ad29c015e7f..28a77e1c73e3d 100644 --- a/arch/x86_64/src/intel64/intel64_handlers.c +++ b/arch/x86_64/src/intel64/intel64_handlers.c @@ -175,7 +175,7 @@ uint64_t *isr_handler(uint64_t *regs, uint64_t irq) "with error code %" PRId64 ":\n", irq, regs[REG_ERRCODE]); - up_dump_register(regs); + PANIC_WITH_REGS("panic", regs); up_trash_cpu(); break; diff --git a/arch/x86_64/src/intel64/intel64_initialstate.c b/arch/x86_64/src/intel64/intel64_initialstate.c index e03f6ada85f3b..196f9d329ef51 100644 --- a/arch/x86_64/src/intel64/intel64_initialstate.c +++ b/arch/x86_64/src/intel64/intel64_initialstate.c @@ -123,7 +123,7 @@ void up_initial_state(struct tcb_s *tcb) */ xcp->regs[REG_RSP] = (uint64_t)xcp->regs - 8; - xcp->regs[REG_RBP] = (uint64_t)xcp->regs - 8; + xcp->regs[REG_RBP] = 0; /* Save the task entry point */ diff --git a/arch/x86_64/src/intel64/intel64_regdump.c b/arch/x86_64/src/intel64/intel64_regdump.c index d1aa274a04d2b..87356f6e1395d 100644 --- a/arch/x86_64/src/intel64/intel64_regdump.c +++ b/arch/x86_64/src/intel64/intel64_regdump.c @@ -154,18 +154,5 @@ void up_dump_register(void *dumpregs) regs[REG_R14], regs[REG_R15]); _alert("Dumping Stack (+-64 bytes):\n"); - if (regs[REG_RSP] > 0 && regs[REG_RSP] < 0x1000000) - { - print_mem((void *)regs[REG_RSP] - 512, - 128 * 0x200000 - regs[REG_RSP] + 512); - } - else - { - print_mem((void *)regs[REG_RSP] - 512, 1024); - } - -#ifdef CONFIG_DEBUG_NOOPT - backtrace(regs[REG_RBP]); -#endif _alert("-----------------------------------------\n"); }