| /* Copyright (C) 2005-2014 Free Software Foundation, Inc. |
| |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public License as |
| published by the Free Software Foundation; either version 2.1 of the |
| License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <sysdep.h> |
| #include <signal.h> |
| #include <execinfo.h> |
| |
| extern int |
| _identify_sighandler (unsigned long fp, unsigned long pc, |
| unsigned long *pprev_fp, unsigned long *pprev_pc, |
| unsigned long *retaddr); |
| |
| inline long |
| get_frame_size (unsigned long instr) |
| { |
| return abs ((short signed) (instr & 0xFFFF)); |
| } |
| |
| static unsigned long * |
| find_frame_creation (unsigned long *pc) |
| { |
| int i; |
| |
| /* NOTE: Distance to search is arbitrary. |
| 250 works well for most things, |
| 750 picks up things like tcp_recvmsg, |
| 1000 needed for fat_fill_super. */ |
| for (i = 0; i < 1000; i++, pc--) |
| { |
| unsigned long instr; |
| unsigned long frame_size; |
| |
| instr = *pc; |
| |
| /* Is the instruction of the form |
| addik r1, r1, foo ? */ |
| if ((instr & 0xFFFF0000) != 0x30210000) |
| continue; |
| |
| frame_size = get_frame_size (instr); |
| |
| if ((frame_size < 8) || (frame_size & 3)) |
| return NULL; |
| |
| return pc; |
| } |
| return NULL; |
| } |
| |
| static int |
| lookup_prev_stack_frame (unsigned long fp, unsigned long pc, |
| unsigned long *pprev_fp, unsigned long *pprev_pc, |
| unsigned long *retaddr) |
| { |
| unsigned long *prologue = NULL; |
| |
| int is_signalhandler = _identify_sighandler (fp, pc, pprev_fp, |
| pprev_pc, retaddr); |
| |
| if (!is_signalhandler) |
| { |
| prologue = find_frame_creation ((unsigned long *) pc); |
| |
| if (prologue) |
| { |
| long frame_size = get_frame_size (*prologue); |
| *pprev_fp = fp + frame_size; |
| if (*retaddr != 0) |
| *pprev_pc = *retaddr; |
| else |
| *pprev_pc = *(unsigned long *) fp; |
| |
| *retaddr = 0; |
| if (!*pprev_pc || (*pprev_pc & 3)) |
| prologue=0; |
| } |
| else |
| { |
| *pprev_pc = 0; |
| *pprev_fp = fp; |
| *retaddr = 0; |
| } |
| } |
| return (!*pprev_pc || (*pprev_pc & 3)) ? -1 : 0; |
| } |
| |
| int |
| __backtrace (void **array, int size) |
| { |
| unsigned long pc, fp; |
| unsigned long ppc, pfp; |
| /* Return address(r15) is required in the signal handler case, since the |
| return address of the function which causes the signal may not be |
| recorded in the stack. */ |
| unsigned long retaddr; |
| |
| int count; |
| int rc = 0; |
| |
| __asm__ __volatile__ ("mfs %0, rpc" |
| : "=r"(pc)); |
| |
| __asm__ __volatile__ ("add %0, r1, r0" |
| : "=r"(fp)); |
| |
| array[0] = (void *) pc; |
| retaddr = 0; |
| for (count = 1; count < size; count++) |
| { |
| rc = lookup_prev_stack_frame (fp, pc, &pfp, &ppc, &retaddr); |
| |
| fp = pfp; |
| pc = ppc; |
| array[count] = (void *) pc; |
| if (rc) |
| return count; |
| } |
| return count; |
| } |
| |
| weak_alias (__backtrace, backtrace) |
| libc_hidden_def (__backtrace) |