转自:http://blog.chinaunix.net/uid-27714502-id-3434761.html

简单实现dump_stack
0.首先确保你能写个内核模块:打印"hello kernel"
如果熟悉dump_stack的话,完全可以绕开此文,或者自己去看dump_stack代码实现之。
1.dump_stack是什么
经常调试内核一定对这个函数不陌生,因为我们大多数人调试内核的时候都受这个函数的
折磨,不信,那么我们调用下这个函数看看(随意写个内核模块调用dump_stack(),插入内核),
我们来看看输出:
Pid: 9982, comm: insmod Not tainted 2.6.31.5-127.fc12.i686.PAE #1
Call Trace:
[<f7e98008>] init+0x8/0xc [hello]
[<c040305b>] do_one_initcall+0x51/0x13f
[<c0462e2f>] sys_init_module+0xac/0x1bd
[<c0408f7b>] sysenter_do_call+0x12/0x28
看到输出,大家一定很熟悉, 没见过类似输出的,一定没把kernel搞崩过.
(来我教你:*(int *)NULL = 0xdead;)
其实不见得每次内核崩溃都会调用dump_stack,但是看到dump_stack的我们不应该被吓到,
反而应该高兴:内核在临挂前还喘口气给我们提示了宝贵的调试信息
2.构造dump_stack第一句
有的人已经不耐烦我这唠叨,自己开始查看代码了,但是为了满足我们小小的虚荣,看懂还不行,
自己也要来写个玩玩,不能老被dump_stack欺负阿。
我们看看dump_stack里面第一句代码:
printk("Pid: %d, comm: %.20s %s %s %.*s\n",
current->pid, current->comm, print_tainted(),
init_utsname()->release,
(int)strcspn(init_utsname()->version, " "),
init_utsname()->version);
这里就不解释printk每个参数了,代码本身就自解释了,剩下的请google,
对于不太理解print_tainted,请看下此函数实现的源码上方的注释:)
3.构造dump_stack的call trace
1)先来句printk("Call Trace:\n"),
2)接下来就神奇了,当初我就觉得能将函数执行流打印出来实在是很神奇,内核到底用了什么方法呢?
先不说,我们来看一句简单的代码:
printk("[<%p>] %pS\n", &printk, &printk);
观察输出:
[<c0776cf4>] printk+0x0/0x1c
你发现,这个输出结果和dump_stack输出的部分惊人的相似,但是我们传给printk的参数是确确实实的
地址值,原来prink自己能转换地址到相应的函数名,只要用参数"%pS"就可以了。
我相信看到这里的人,估计已经走开自己去实现dump_stack玩了。
但是输出也可能是:
[<c0776cf4>] c0776cf4S
如果你不幸看到这个,那么你还是升级下内核吧,或者仔细阅读下dump_stack代码,完全靠自己去实现
下,那么你收获一定会远超出这篇文章。
printk之所以能够识别函数地址,靠的是kallsyms子系统的帮助,
用过类似grep -w "printk" /proc/kallsyms命令的人,一定要好好谢谢这个子系统,
多亏它我们才能从内核导出symbol
深入研究kallsyms就靠大家了。
3)在刚刚的惊喜后,我们回到正题,怎么用printk把当前的执行流打印出来?
这里用到x86中堆栈对函数调用的帮助,详细信息请google,我们要知道一点:每次函数调用时候,
都会将函数的返回地址(调用函数指令的下一句指令的地址)压入堆栈,已备函数返回时。
我们就可以靠这个返回地址来帮助打印函数执行流。
但是这个地址并不是一个函数的准确地址呀?
%pS需要的参数不一定是准确的函数地址,在函数内部任意指令地址都可以,这就解释了输出形式是
"printk+0x0/0x1c",0x0表示参数地址相对于printk地址的偏移,0x1c表示printk函数大小。
你可以尝试下:printk("%pS\n", &printk + 1);
并且如果函数属于某个模块,还会在输出后面加上模块名称,类似:" [<f8cd40a5>] exit+0xd/0xf [hello]"
这里知道地址在堆栈里,那么怎么取堆栈呢?
其实很简单:
int stack_pointer;
我们只要取临时变量地址值 &stack_pointer 就可以了(也可以用内联汇编取esp值),然后只要循环遍历堆栈上所有值,然后判断该值是否在
内核代码段空间内,如果是那么就用%pS输出。
那么堆栈的结束地址是什么呢?
就是当前进程的内核态堆栈段,不懂的话请google,一定要搞清除这个。
这里我们记堆栈底为:
bottom = (unsigned int)current_thread_info() + THREAD_SIZE;
但是怎么判断地址值是否在内核代码段呢?
我们可以用kernel_text_address这个函数就可以了,但是很不幸的是此函数内核没有导出,我们不能使用,
那么我们就自己实现个kernel_text_address吧,但是更不幸的是此函数内部实现所依赖的变量_etext等也没有
被内核导出,其实我也没想到很好的方法,索性就用个笨办法:
手动找出此函数内核中的地址,
# grep kernel_text_address /proc/kallsyms
c044f107 T kernel_text_address
在代码中通过地址值调用kernel_text_address
int (*kernel_text_addressp)(unsigned int) = (int (*)(unsigned int))0xc044f107;
4)给出个较为完整的代码(在本机上写的:2.6.31.5-127.fc12.i686.PAE, 虚拟机上文档写的麻烦)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/utsname.h>
/* 
* change the value to real addr of kernel_text_address :
* grep -w kernel_text_address /proc/kallsyms
*/
static int (*kernel_text_addressp)(unsigned int) = (int (*)(unsigned int))0xc044f107;
void my_dump_stack(void)
{
unsigned int stack;
unsigned int bottom;
unsigned int addr;
printk("Pid: %d, comm: %.20s %s %s %.*s\n",
current->pid, current->comm, print_tainted(),
init_utsname()->release,
(int)strcspn(init_utsname()->version, " "),
init_utsname()->version); 
printk("Call Trace:\n");
/* get stack point */
stack = (unsigned int)&stack;
/* get stack bottom point */
bottom = (unsigned int)current_thread_info() + THREAD_SIZE;
for (; stack < bottom; stack += 4) {
addr = *(unsigned int *)stack;
if (kernel_text_addressp(addr))
printk(" [<%p>] %pS\n", (void *)addr, (void *)addr);
}
}
static int __init init(void)
{
/* test */
my_dump_stack();
return 0;
}
static void __exit exit(void)
{
/* test */
my_dump_stack();
}
MODULE_LICENSE("GPL");
module_init(init);
module_exit(exit);
3.改进
1)
如果你还没厌烦的话,这里有个改进的地方。
你会发现内核中的dump_stack会又类似如下输出:
[<c04cf4e6>] ? path_put+0x1a/0x1d
这里有个问号:这个表示堆栈中有确有此值(某个函数内部地址),但是并不代表此函数被
执行,也许这个值是个临时变量寄存在堆栈中。
要区分这个很容易,只要比较地址值所在堆栈的位置是否紧贴当前函数栈空间底的上方,
可以利用ebp(记录当前堆栈底)指针值来作比较,代码就留给大家写了。
2)
x86 64 实现要比x86 32复杂(见内核注释):
/*
* x86-64 can have up to three kernel stacks:
* process stack
* interrupt stack
* severe exception (double fault, nmi, stack fault, debug, mce) hardware stack
*/
4.后记
你可能认为作者在忽悠你,这就整一个dump_stack注释的文章呀,贯上了写dump_stack的头衔!
我只有一句话:
Just for fun!
本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/7651660.html,如需转载请自行联系原作者

写个dump_stack【转】相关推荐

  1. c语言 自动dump 函数,(转)写个dump_stack

    简单实现dump_stack 0.首先确保你能写个内核模块:打印"hello kernel" 如果熟悉dump_stack的话,完全可以绕开此文,或者自己去看dump_stack代 ...

  2. linux内核栈与用户栈及调用栈观察方法

    最近linux内核的中断部分,总是被书里的栈弄晕,一会儿内核栈,一会儿用户栈的--很是崩溃,在网上google了一下 找了一篇不错的文章拿来分享. 5.8 Linux 系统中堆栈的使用方法 本节内容概 ...

  3. 动态创建数组了解各种取值和取地址的问题以及感受内存地址

    image.png 源代码: #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include "cd.h"//严重 ...

  4. 64MQQ2440烧写MINI2440光盘中的内容同样可以跑起来

    2008年购买了QQ2440,后来出去工作了,2440压箱子底压了两年,现在拿出来想弄弄qtopia,可是一查友善的资料已经出到了ARM11的板子了,内核以及qtopia的支持根文件映像也不知升级了多 ...

  5. BUG_ON()、panic()、dump_stack()几种内核调试手段

    Linux内核有一些方法可以用来方便标记bug,提供断言并输出信息.最常用的两个是BUG()和BUG_ON(). 当被调用的时候,它们会引发oops,导致栈的回溯和错误信息的打印.这些声明会导致 oo ...

  6. 使用hello word写小说

    2021-11-12 使用hello word写小说

  7. java调用clang编译的so_写Java这么久,JDK源码编译过没?编译JDK源码踩坑纪实

    好奇害死羊 很多小伙伴们做Java开发,天天写Java代码,肯定离不开Java基础环境:JDK,毕竟我们写好的Java代码也是跑在JVM虚拟机上. 一般来说,我们学Java之前,第一步就是安装JDK环 ...

  8. java 手编线程池_死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  9. 使用vscode连接服务器写代码指南

    1 安装vscode 2 vscode中安装remote-ssh 3 按F1->输入SSH->选择相关选项(选择Connect to Host[图1],如果之前没有连接过服务器,可以选择A ...

最新文章

  1. React 中报错:Unexpected reserved word ‘await‘
  2. Uber AI简单方法实现大规模语言模型的精细控制
  3. 干货回顾丨TensorFlow四种Cross Entropy算法的实现和应用
  4. ap的ht模式_AP6256 STA模式操作示例
  5. MFC单文档框架编程(三): CTabView的使用
  6. vs2022 无法打开包括文件: “crtdbg.h” no such file or directory
  7. 项目管理中可能有的问题,以及如何去面对!
  8. 巨杉内核笔记(一)| SequoiaDB 会话(session)简介
  9. 微信js-sdk使用
  10. 【Python CheckiO 题解】Text Editor
  11. 建立你第一个 Outlook Add-in
  12. 常用c语言函数实现,常用的C语言库函数实现
  13. 从零开始学ArcGIS Server(三)--如何创建一个个人地理数据库ArcSDE Personal geodatabase...
  14. 第一届中国iPhone技术开发者交流大会-----chinapub技术沙龙第一期
  15. 【webssh】网页上的SSH终端
  16. 学习电商美工设计毕业以后可以干什么
  17. 【干货】github上十二款最著名的Android播放器开源项目
  18. linux查看隐藏大文件夹,Linux系统隐藏文件/文件夹操作教程
  19. Balsamiq Mockups注册码
  20. 速来了解头条号的推荐机制,让你的自媒体内容收下更多数据量!

热门文章

  1. 相机模型、相机标定及基于yolov5的单目测距实现
  2. 命令行导入导出Informatica工作流
  3. linux内核态real cred,Linux内核源码分析 -- 更新当前进程的 cred -- commit_creds
  4. 竞价排名和php是什么,竞价排名优缺点详解-金瑞帆高端建站
  5. mac安装cornerston3
  6. 计算机在语文教学中的用场,信息技术在语文教学中的作用
  7. 初出茅庐的小李第39篇博客之转载一篇有关unistd.h的介绍文章
  8. workspace\.metadata\.plugins\org.eclipse.wst.server.core
  9. 雪晴网上海沙龙+在线直播:如何成为数据科学家?
  10. 基于中国航信Eterm开发