在嵌入式设备开发中。内核为内核模块的函数栈追踪已经提供了非常好的支持,但用户层的函数栈追踪确没有非常好的提供支持。

在网上收集学习函数栈跟踪大部分都是描写叙述INTER体系架构支持栈帧的实现机制。或者直接给出glibc的现成库函数。

但假设开发环境是broadcom相关方案,通常使用的是MIPS32的体系架构,而且C库使用的是更小的uclibc。尽管MIPS32体系架构中也定义了栈帧寄存器s8(类似于Inter体系架构中常见的ebp寄存器),但经过GCC编译器的优化选项控制后,通常在O1以上的优化就已经去除了s8栈帧的使用,所以给函数栈追踪的实现就带来了一点点小麻烦。

通常情况下。函数的返回值还是使用压栈实现,所以仅仅要知道了函数每次调用时,返回值(ra寄存器)与当前栈顶(sp寄存器)的偏移量,就能够实现函数栈追踪,对函数反汇编能够了解到这个偏移量通过sw ra, xxxx(sp)能够得到,还有一个难题是怎么得到函数每次调用时的当前栈顶(sp寄存器),通过函数反汇编能够了解到addiu sp, sp, xxxx指令就是每次给函数分配当前栈帧大小的,所以仅仅要得到这个栈帧大小然后用sp进行差值计算就能够往回推出上一个sp的值了。还剩下最后一个问题,一步步获取上一个函数的栈顶,什么时候结束?答案就是仅仅要栈顶sp的寄存器为0就追踪到头了。通过这些分析我们能够了解到当前实现机制根本没用到程序的栈段内容。对,全然从程序指令段做为线索,获取程序执行时指令一步步找出函数栈的调用。看起来非常酷。但现实也比較残酷,比方我们上面分析说要得到栈顶sp的寄存器为0表示追踪到头,我当前调试环境在__start(能够參考一些链接载入的相关技术资料了解,其他main并非c的起始函数。__start才是c语言的起始函数)函数中使得sp为0的汇编指令为addu
ra, zero, zero,我还不清楚其他编译器是否会使用其他指令方法进行设置。所以我们当前实现的这个函数栈追跟踪功能的实现代码并非非常标准的。假设读者有幸參考该代码,请一定要在理解上面描写叙述的原理基础上。另外须要在您自己的编译开发环境进行调试,确保这个追踪功能代码自己不会引发Crash再使用,否则那玩笑就开的大了。

剩下就不多说了。直接付上我当前的开发环境及实现源代码,以及终于在broadcom6838板上执行的測试程序的效果图。



开发环境:

体系结构:mips32
GCC版本号:gcc-4.6
内核版本号:linux-3.4
C库版本号:uclibc-0.9.32
工具版本号:binutils-2.21

实现源代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <ucontext.h> #define abs(s) ((s) < 0 ? -(s) : (s)) #define CALL_TRACE_MAX_SIZE (10) #define MIPS_ADDIU_SP_SP_XXXX (0x27bd0000) /* instruction code for addiu sp, sp, xxxx */
#define MIPS_SW_RA_XXXX_SP (0xafbf0000) /* instruction code for sw ra, xxxx(sp) */
#define MIPS_ADDU_RA_ZERO_ZERO (0x0000f821) /* instruction code for addu ra, zero, zero */ void getCodeIn(unsigned long codeAddr, char *pCodeIn, int iCodeInSize, unsigned long *pOffset)
{
FILE *pFile = NULL;
char szLine[1000] = {0}; pFile = fopen("/proc/self/maps", "r");
if ( pFile != NULL )
{
while (fgets(szLine, sizeof(szLine), pFile))
{
char *pTmp = NULL;
char szAddr[500] = {0};
char szCodeIn[500] = {0};
unsigned long begin = 0;
unsigned long end = 0; sscanf(szLine, "%s %*s %*s %*s %*s %s", szAddr, szCodeIn); pTmp = strchr(szAddr, '-');
if ( pTmp != NULL )
{
*pTmp++ = '\0';
begin = strtoul(szAddr, NULL, 16);
end = strtoul(pTmp, NULL, 16);
} if ( codeAddr >= begin && codeAddr <= end )
{
strncpy(pCodeIn, szCodeIn, iCodeInSize);
*pOffset = codeAddr - begin;
return;
}
} fclose(pFile);
} strncpy(pCodeIn, "unknown", iCodeInSize);
*pOffset = 0;
} int backtrace_mips32(void **buffer, int size, ucontext_t *uc)
{
unsigned long *tmpl = NULL;
unsigned long *addr = NULL;
unsigned long *ra = NULL;
unsigned long *sp = NULL;
unsigned long *pc = NULL;
size_t ra_offset = 0;
size_t stack_size = 0;
int depth = 0; if (size == 0)
{
return 0;
} if (buffer == NULL || size < 0 || uc == NULL)
{
return -1;
} pc = (unsigned long *)(unsigned long)uc->uc_mcontext.pc;
ra = (unsigned long *)(unsigned long)uc->uc_mcontext.gregs[31];
sp = (unsigned long *)(unsigned long)uc->uc_mcontext.gregs[29]; buffer[0] = pc; if ( size == 1 )
{
return 1;
} for ( addr = pc; !ra_offset || !stack_size; --addr )
{
if ( ((*addr) & 0xffff0000) == MIPS_SW_RA_XXXX_SP)
{
ra_offset = (short)((*addr) & 0xffff);
}
else if ( ((*addr) & 0xffff0000) == MIPS_ADDIU_SP_SP_XXXX)
{
stack_size = abs((short)((*addr) & 0xffff));
}
else if ( (*addr) == MIPS_ADDU_RA_ZERO_ZERO )
{
return 1;
}
} if ( ra_offset > 0 )
{
tmpl = (unsigned long *)((char *)sp + ra_offset);
ra = (unsigned long *)(*tmpl);
} if ( stack_size > 0 )
{
sp = (unsigned long *)((unsigned long)sp + stack_size);
} for (depth = 1; depth < size && ra; ++depth)
{
buffer[depth] = ra; ra_offset = 0;
stack_size = 0; for (addr = ra; !ra_offset || !stack_size; --addr)
{
if ( ((*addr) & 0xffff0000) == MIPS_SW_RA_XXXX_SP)
{
ra_offset = (short)((*addr) & 0xffff);
}
else if ( ((*addr) & 0xffff0000) == MIPS_ADDIU_SP_SP_XXXX)
{
stack_size = abs((short)((*addr) & 0xffff));
}
else if ( (*addr) == MIPS_ADDU_RA_ZERO_ZERO )
{
return depth + 1;
}
} tmpl = (unsigned long *)((char *)sp + ra_offset);
ra = (unsigned long *)(*tmpl);
sp = (unsigned long *)((unsigned long)sp + stack_size);
} return depth;
} void signal_process(int sig_no, siginfo_t *sig_info, void *ucontext)
{
int i = 0;
unsigned long *callStack[CALL_TRACE_MAX_SIZE] = {0}; printf("\r\n*******************************************\r\n"); printf("recv signo %d\r\n", sig_no); backtrace_mips32((void **)callStack, CALL_TRACE_MAX_SIZE, (ucontext_t *)ucontext); printf("\r\ncall tracing:\r\n");
for ( i = 0; i < CALL_TRACE_MAX_SIZE; i++ )
{
char szCodeIn[100] = {0};
unsigned long addrOffset = 0; if ( callStack[i] == 0 )
{
break;
} getCodeIn((unsigned long)callStack[i], szCodeIn, sizeof(szCodeIn), &addrOffset); printf("\t[%02d] addr 0x%08x(offset 0x%08x), in %s\r\n", i, callStack[i], addrOffset, szCodeIn);
} printf("*******************************************\r\n"); if (sig_no == SIGSEGV)
{
signal(sig_no, SIG_DFL);
} sleep(3);
} void c(void)
{
int *p = 0;
printf("I am function [c]\r\n");
*p = 888;
} void b(void)
{
printf("I am function [b]\r\n");
c();
} void a(void)
{
printf("I am function [a]\r\n");
b();
} int main(int argc, char *argv[])
{
struct sigaction act = {0}; act.sa_sigaction = signal_process;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &act, NULL); printf("I am function [main]\r\n"); a(); return 0;
}

执行效果图:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1amlhbmZlbmcxOTg0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

broadcom6838开发环境实现函数栈追踪的更多相关文章

  1. 转-subl配置全栈开发环境

    为 Sublime Text 3 设置 Python 的全栈开发环境 Sublime Text 3 (ST3) 是一个轻量级的跨平台文字编辑器,尤以其轻快的速度,易用性和强大的社区支持而著称.它一经面 ...

  2. Win10构建Python全栈开发环境With WSL

    目录 Win10构建Python全栈开发环境With WSL 启动WSL 总结 对<Dev on Windows with WSL>的补充 Win10构建Python全栈开发环境With ...

  3. Vue+koa2开发一款全栈小程序(5.服务端环境搭建和项目初始化)

    1.微信公众平台小程序关联腾讯云 腾讯云的开发环境是给免费的一个后台,但是只能够用于开发,如果用于生产是需要花钱的,我们先用开发环境吧 1.用小程序开发邮箱账号登录微信公众平台 2.[设置]→[开发者 ...

  4. python全栈开发 生成器 :生成器函数,推导式及生成器表达式

    python 全栈开发 1.生成器函数 2.推导式 3.生成器表达式 一.生成器函数 1.生成器: 生成器的本质就是迭代器 (1)生成器的特点和迭代器一样.取值方式和迭代器一样(__next__(), ...

  5. python全栈开发之匿名函数和递归函数

    python 匿名函数和递归函数 python全栈开发,匿名函数,递归函数 匿名函数 lambda函数也叫匿名函数,即函数没有具体的名称.是为了解决一些功能很简单需求而设计的一句话函数.如下: #这段 ...

  6. 痞子衡嵌入式:在IAR开发环境下将关键函数重定向到RAM中执行的三种方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下将关键函数重定向到RAM中执行的三种方法. 嵌入式项目里应用程序代码正常是放在 Flash 中执行的,但有时候也需要将 ...

  7. 痞子衡嵌入式:在MDK开发环境下将关键函数重定向到RAM中执行的几种方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在MDK开发环境下将关键函数重定向到RAM中执行的几种方法. 这个关键函数重定向到 RAM 中执行系列文章,痞子衡已经写过 <IA ...

  8. 痞子衡嵌入式:在IAR开发环境下RT-Thread工程函数重定向失效分析

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下RT-Thread工程函数重定向失效分析. 痞子衡旧文 <在IAR下将关键函数重定向到RAM中执行的方法> ...

  9. Python 全栈开发 -- 开发环境篇

    开发环境是一个文本编辑器和 Python 解释器的组合.文本编辑器用来写代码,解释器提供了一种方法来运行编写的代码.一个文本编辑器可以像 Windows 上的 Notepad 一样简单,或是一个复杂的 ...

随机推荐

  1. java运行脚本语言demo

    public class Test { /** * @param args * @throws IOException  */ public static void main(String[] arg ...

  2. Delphi - SEH研究

    技术交流,DH讲解. 前几天一个朋友在弄游戏外挂想带NP调试,就像自己来捕获游戏的异常.好像就要用到SEH这方面的知识.一起研究了一下,这里看下研究 和 在网上找的资料吧.SEH就是Structure ...

  3. axure网格设置

    Axure默认的界面是没有吧网格显示出来,没有网格在制作原型的时候,对齐方面不是很好,个人习惯还是把网格显示出来,便于组件对齐和布局. 其实本来这篇文章应该叫做网格与参考线,只是本人对参考线的应用还很 ...

  4. Linux的grep命令详解

    简介 grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它 ...

  5. 一道c++小编程题,

    题目: 编写一个小程序,从标准输入读入一系列string对象,寻找连续重复出现的单词,程序应该找出满足以下条件的单词的输入位置:该单词的后面紧跟着再次出现自己本身,跟 踪重复次数量多的单词及其重复次数 ...

  6. C语言总结之---关键字

    我记得我开始学习C语言的时候,那时候还在读高中,我们老师就把C语言的关键字,全部写在黑板上,老师说我们下面的两节课的内容就是(把它给记下来) 你还记得标准C有多少个关键字吗? 第一:关键字描述 C99 ...

  7. poj 2126 Factoring a Polynomial 数学多项式分解

    题意: 给一个多项式,求它在实数域内的可分解性. 分析: 代数基本定理. 代码: //poj 2126 //sep9 #include <iostream> using namespace ...

  8. java对象引用传递和值传递的一些总结

    1.对象作为函数的参数传递过去的时候,是以原对象的引用的方式传递的,更改参数对象的值,会影响原来的对象. 2.对象作为函数的返回值的时候,传递过来的也是一个引用传递,更改传递过来的对象的时候,会影响原 ...

  9. c#中,DataTable 过滤重复行

    虽然网上有很多DataTable过滤重复行的方法,但是本菜还是认为自己写的这个方法最靠谱,这里的参数是传递的DataTable值,返回的是一个已经过滤相同字段StuId,ExamNum的DataTab ...

  10. 快速排序的时间复杂度nlogn是如何推导的??

    本文以快速排序为例,推导了快排的时间复杂度nlogn是如何得来的,其它算法与其类似. 对数据Data = { x1, x2... xn }: T(n)是QuickSort(n)消耗的时间: P(n)是 ...