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

在网上收集学习函数栈跟踪大部分都是描写叙述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. GDI+ Tutorial for Beginners

    原文 GDI+ Tutorial for Beginners GDI+ is next evolution of GDI. Using GDI objects in earlier versions ...

  2. 基于visual Studio2013解决C语言竞赛题之1055排序

       题目 解决代码及点评 /* 功能:已知A是有30个元素的整型数组,编写一个对A[I1]到A[I2](I1≤I2)之间的元素排序的函数(从大到小排序) 请调用上述函数先将A[5]至A[ ...

  3. javascript面向对象基础讲解(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式)

    面向对象可以把程序中的关键模块都视为对象,而模块拥有属性及方法.这样我们如果把一些属性及方法封装起来,日后使用将非常方便,也可以避免繁琐重复的工作.接下来将为大家讲解在JS中面向对象的实现.   工厂 ...

  4. UVA 10201 Adventures in Moving - Part IV(dp)

    Problem A: Adventures in Moving - Part IV To help you move from Waterloo to the big city, you are co ...

  5. centOS设为文本启动方式

    centOS图形界面须要点用系统大量的内存和CPU资源,对于server而言,高效率是最重要的,因此将Centos 默认启动改为文本方式. 在终端中输入: vi /etc/inittab 有段说明文字 ...

  6. Mac 安装工具包brew

    linux有命令行工具 apt-get ,Mac 下也有类似的brew 也就是HomeBrew 网址:http://brew.sh/index_zh-cn.html 可以看到mac安装的时候只需要执行 ...

  7. ANT编译Android Eclipse工程

    将Android SDK的tools/和platform-tools/目录包含在可执行文件的搜索路径中.Windows下,将其添加到PATH环境变量中 切换到Android Eclipse项目目录下, ...

  8. C++编程规范之11:隐藏信息

    摘要: 不要泄密,不要公开提供抽象的实体的内部信息. 为了尽量减少操作抽象的调用代码和抽象的实现之间的依赖性,必须隐藏实现内部的数据.否则,调用代码就能够访问该信息,或者更糟,操作该信息,而原来应属于 ...

  9. QT解析命令行(QCommandLineOption和QCommandLineParser类)

    Qt从5.2版开始提供了两个类QCommandLineOption和QCommandLineParser来解析应用的命令行参数. 一.命令行写法命令行:"-abc" 在QComma ...

  10. win7下让程序默认以管理员身份运行

    在win7中用自己写的程序读取MBR时,突然提示无法对磁盘进行操作,而在xp下并没有这个问题:最后点右键以管理员身份运行才可以正常运行.于是想办法让程序在双击启动时默认以管理员身份运行.具体方法: 1 ...