| /**************************************************************************** |
| * libs/libc/sched/sched_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 <nuttx/config.h> |
| |
| #ifndef CONFIG_ARCH_HAVE_BACKTRACE |
| |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <execinfo.h> |
| #include <unwind.h> |
| |
| #include <nuttx/sched.h> |
| |
| /**************************************************************************** |
| * Private Data Types |
| ****************************************************************************/ |
| |
| struct trace_arg |
| { |
| FAR void **array; |
| _Unwind_Word cfa; |
| int cnt; |
| int size; |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| weak_function |
| FAR void *unwind_arch_adjustment(FAR void *prev, FAR void *addr) |
| { |
| return addr; |
| } |
| |
| static _Unwind_Reason_Code |
| backtrace_helper(FAR struct _Unwind_Context *ctx, FAR void *a) |
| { |
| FAR struct trace_arg *arg = a; |
| |
| /* We are first called with address in the backtrace function. |
| * Skip it. |
| */ |
| |
| if (arg->cnt >= 0) |
| { |
| arg->array[arg->cnt] = (FAR void *)_Unwind_GetIP(ctx); |
| if (arg->cnt > 0) |
| { |
| arg->array[arg->cnt] |
| = unwind_arch_adjustment(arg->array[arg->cnt - 1], |
| arg->array[arg->cnt]); |
| } |
| |
| /* Check whether we make any progress. */ |
| |
| _Unwind_Word cfa = _Unwind_GetCFA(ctx); |
| |
| if (arg->cnt > 0 && |
| arg->array[arg->cnt - 1] == arg->array[arg->cnt] && |
| cfa == arg->cfa) |
| { |
| return _URC_END_OF_STACK; |
| } |
| |
| arg->cfa = cfa; |
| } |
| |
| if (++arg->cnt >= arg->size) |
| { |
| return _URC_END_OF_STACK; |
| } |
| |
| return _URC_NO_REASON; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: sched_backtrace |
| * |
| * Description: |
| * Get thread backtrace from specified tid. |
| * |
| ****************************************************************************/ |
| |
| int sched_backtrace(pid_t tid, FAR void **buffer, int size, int skip) |
| { |
| struct trace_arg arg; |
| |
| if (tid != _SCHED_GETTID()) |
| { |
| return 0; |
| } |
| |
| arg.array = buffer; |
| arg.cfa = 0; |
| arg.size = size; |
| arg.cnt = -skip - 1; |
| |
| if (size <= 0) |
| { |
| return 0; |
| } |
| |
| _Unwind_Backtrace(backtrace_helper, &arg); |
| |
| /* _Unwind_Backtrace seems to put NULL address above |
| * _start. Fix it up here. |
| */ |
| |
| if (arg.cnt > 1 && arg.array[arg.cnt - 1] == NULL) |
| { |
| --arg.cnt; |
| } |
| |
| return arg.cnt > 0 ? arg.cnt : 0; |
| } |
| |
| #endif /* !CONFIG_ARCH_HAVE_BACKTRACE */ |