From f14bd44001db1abcf451192cce508f01ac514e16 Mon Sep 17 00:00:00 2001 From: "chao.an" Date: Wed, 25 Aug 2021 14:14:05 +0800 Subject: [PATCH] arch/armv7: add up_backtrace support based on frame pointer This feature depends on frame pointer, "-fno-omit-frame-pointer" is mandatory This feature can not be used in THUMB2 mode if you are using GCC toolchain, More details please refer: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92172 Signed-off-by: chao.an --- arch/arm/src/arm/arm_backtrace.c | 162 +++++++++++++++++++++++++++ arch/arm/src/armv7-a/arm_backtrace.c | 162 +++++++++++++++++++++++++++ arch/arm/src/armv7-r/arm_backtrace.c | 162 +++++++++++++++++++++++++++ 3 files changed, 486 insertions(+) create mode 100644 arch/arm/src/arm/arm_backtrace.c create mode 100644 arch/arm/src/armv7-a/arm_backtrace.c create mode 100644 arch/arm/src/armv7-r/arm_backtrace.c diff --git a/arch/arm/src/arm/arm_backtrace.c b/arch/arm/src/arm/arm_backtrace.c new file mode 100644 index 0000000000..28165b55c2 --- /dev/null +++ b/arch/arm/src/arm/arm_backtrace.c @@ -0,0 +1,162 @@ +/**************************************************************************** + * arch/arm/src/arm/arm_backtrace.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 + +#include "sched/sched.h" + +#include "arm_internal.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: backtrace + * + * Description: + * backtrace() parsing the return address through frame pointer + * + ****************************************************************************/ + +static int backtrace(FAR uintptr_t *base, FAR uintptr_t *limit, + FAR uintptr_t *fp, FAR uintptr_t *pc, + FAR void **buffer, int size) +{ + int i = 0; + + if (pc) + { + buffer[i++] = pc; + } + + for (; i < size; fp = (FAR uintptr_t *)*(fp - 1), i++) + { + if (fp > limit || fp < base || *fp == 0) + { + break; + } + + buffer[i] = (FAR void *)*fp; + } + + 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 + * + * Returned Value: + * up_backtrace() returns the number of addresses returned in buffer + * + ****************************************************************************/ + +int up_backtrace(FAR struct tcb_s *tcb, FAR void **buffer, int size) +{ + FAR struct tcb_s *rtcb = running_task(); +#if CONFIG_ARCH_INTERRUPTSTACK > 7 + FAR void *istacklimit; +#endif + irqstate_t flags; + int ret; + + if (size <= 0 || !buffer) + { + return 0; + } + + if (tcb == NULL || tcb == rtcb) + { + if (up_interrupt_context()) + { +#if CONFIG_ARCH_INTERRUPTSTACK > 7 +# ifdef CONFIG_SMP + istacklimit = arm_intstack_top(); +# else + istacklimit = &g_intstacktop; +# endif /* CONFIG_SMP */ + ret = backtrace(istacklimit - (CONFIG_ARCH_INTERRUPTSTACK & ~7), + istacklimit, + (FAR void *)__builtin_frame_address(0), + NULL, buffer, size); +#else + ret = backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (FAR void *)__builtin_frame_address(0), + NULL, buffer, size); +#endif /* CONFIG_ARCH_INTERRUPTSTACK > 7 */ + if (ret < size) + { + ret += backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (FAR void *)CURRENT_REGS[REG_FP], + (FAR void *)CURRENT_REGS[REG_PC], + &buffer[ret], size - ret); + } + } + else + { + ret = backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (FAR void *)__builtin_frame_address(0), + NULL, buffer, size); + } + } + else + { + flags = enter_critical_section(); + + ret = backtrace(tcb->stack_base_ptr, + tcb->stack_base_ptr + tcb->adj_stack_size, + (FAR void *)tcb->xcp.regs[REG_FP], + (FAR void *)tcb->xcp.regs[REG_PC], + buffer, size); + + leave_critical_section(flags); + } + + return ret; +} diff --git a/arch/arm/src/armv7-a/arm_backtrace.c b/arch/arm/src/armv7-a/arm_backtrace.c new file mode 100644 index 0000000000..8c505a59ba --- /dev/null +++ b/arch/arm/src/armv7-a/arm_backtrace.c @@ -0,0 +1,162 @@ +/**************************************************************************** + * arch/arm/src/armv7-a/arm_backtrace.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 + +#include "sched/sched.h" + +#include "arm_internal.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: backtrace + * + * Description: + * backtrace() parsing the return address through frame pointer + * + ****************************************************************************/ + +static int backtrace(FAR uintptr_t *base, FAR uintptr_t *limit, + FAR uintptr_t *fp, FAR uintptr_t *pc, + FAR void **buffer, int size) +{ + int i = 0; + + if (pc) + { + buffer[i++] = pc; + } + + for (; i < size; fp = (FAR uintptr_t *)*(fp - 1), i++) + { + if (fp > limit || fp < base || *fp == 0) + { + break; + } + + buffer[i] = (FAR void *)*fp; + } + + 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 + * + * Returned Value: + * up_backtrace() returns the number of addresses returned in buffer + * + ****************************************************************************/ + +int up_backtrace(FAR struct tcb_s *tcb, FAR void **buffer, int size) +{ + FAR struct tcb_s *rtcb = running_task(); +#if CONFIG_ARCH_INTERRUPTSTACK > 7 + FAR void *istacklimit; +#endif + irqstate_t flags; + int ret; + + if (size <= 0 || !buffer) + { + return 0; + } + + if (tcb == NULL || tcb == rtcb) + { + if (up_interrupt_context()) + { +#if CONFIG_ARCH_INTERRUPTSTACK > 7 +# ifdef CONFIG_SMP + istacklimit = arm_intstack_top(); +# else + istacklimit = &g_intstacktop; +# endif /* CONFIG_SMP */ + ret = backtrace(istacklimit - (CONFIG_ARCH_INTERRUPTSTACK & ~7), + istacklimit, + (FAR void *)__builtin_frame_address(0), + NULL, buffer, size); +#else + ret = backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (FAR void *)__builtin_frame_address(0), + NULL, buffer, size); +#endif /* CONFIG_ARCH_INTERRUPTSTACK > 7 */ + if (ret < size) + { + ret += backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (FAR void *)CURRENT_REGS[REG_FP], + (FAR void *)CURRENT_REGS[REG_PC], + &buffer[ret], size - ret); + } + } + else + { + ret = backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (FAR void *)__builtin_frame_address(0), + NULL, buffer, size); + } + } + else + { + flags = enter_critical_section(); + + ret = backtrace(tcb->stack_base_ptr, + tcb->stack_base_ptr + tcb->adj_stack_size, + (FAR void *)tcb->xcp.regs[REG_FP], + (FAR void *)tcb->xcp.regs[REG_PC], + buffer, size); + + leave_critical_section(flags); + } + + return ret; +} diff --git a/arch/arm/src/armv7-r/arm_backtrace.c b/arch/arm/src/armv7-r/arm_backtrace.c new file mode 100644 index 0000000000..bb54d216ff --- /dev/null +++ b/arch/arm/src/armv7-r/arm_backtrace.c @@ -0,0 +1,162 @@ +/**************************************************************************** + * arch/arm/src/armv7-r/arm_backtrace.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 + +#include "sched/sched.h" + +#include "arm_internal.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: backtrace + * + * Description: + * backtrace() parsing the return address through frame pointer + * + ****************************************************************************/ + +static int backtrace(FAR uintptr_t *base, FAR uintptr_t *limit, + FAR uintptr_t *fp, FAR uintptr_t *pc, + FAR void **buffer, int size) +{ + int i = 0; + + if (pc) + { + buffer[i++] = pc; + } + + for (; i < size; fp = (FAR uintptr_t *)*(fp - 1), i++) + { + if (fp > limit || fp < base || *fp == 0) + { + break; + } + + buffer[i] = (FAR void *)*fp; + } + + 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 + * + * Returned Value: + * up_backtrace() returns the number of addresses returned in buffer + * + ****************************************************************************/ + +int up_backtrace(FAR struct tcb_s *tcb, FAR void **buffer, int size) +{ + FAR struct tcb_s *rtcb = running_task(); +#if CONFIG_ARCH_INTERRUPTSTACK > 7 + FAR void *istacklimit; +#endif + irqstate_t flags; + int ret; + + if (size <= 0 || !buffer) + { + return 0; + } + + if (tcb == NULL || tcb == rtcb) + { + if (up_interrupt_context()) + { +#if CONFIG_ARCH_INTERRUPTSTACK > 7 +# ifdef CONFIG_SMP + istacklimit = arm_intstack_top(); +# else + istacklimit = &g_intstacktop; +# endif /* CONFIG_SMP */ + ret = backtrace(istacklimit - (CONFIG_ARCH_INTERRUPTSTACK & ~7), + istacklimit, + (FAR void *)__builtin_frame_address(0), + NULL, buffer, size); +#else + ret = backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (FAR void *)__builtin_frame_address(0), + NULL, buffer, size); +#endif /* CONFIG_ARCH_INTERRUPTSTACK > 7 */ + if (ret < size) + { + ret += backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (FAR void *)CURRENT_REGS[REG_FP], + (FAR void *)CURRENT_REGS[REG_PC], + &buffer[ret], size - ret); + } + } + else + { + ret = backtrace(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (FAR void *)__builtin_frame_address(0), + NULL, buffer, size); + } + } + else + { + flags = enter_critical_section(); + + ret = backtrace(tcb->stack_base_ptr, + tcb->stack_base_ptr + tcb->adj_stack_size, + (FAR void *)tcb->xcp.regs[REG_FP], + (FAR void *)tcb->xcp.regs[REG_PC], + buffer, size); + + leave_critical_section(flags); + } + + return ret; +}