keil中callstack在哪
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了keil中callstack在哪相关的知识,希望对你有一定的参考价值。
先建立一个工程,点击Project,选择第一个,建立新工程,给个名字,点击确定,出现如下界面。2、选择Atmel,找到AT89C51选中后,出现另一个界面。
3、此时窗口左侧出现工程栏,如果没出现,点击工具栏中“iew”择第三个”roject Window后即可出现,此时工程已建立好。
4、然后在工程中创建项目,操作如图所示,给项目添加一个名字,注意,如果项目用C语言写,则项目名后家“C”,如是C++写的项目,则在名字后加“Cpp”。
5、接着就可以在窗口写程序了,写完后点击窗口左上角的”build"和"rebuild“按钮进行编译,观察下面的“project output”,无错误则编译完成。
6、要想烧写程序,还需要生产HEX文件,具体做法是点击”option for target“按钮,进入界面,选择”Output”选中“Creat HEX file“即可。
Keil C51是美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统,与汇编相比,C语言在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用。
Keil提供了包括C编译器、宏汇编、链接器、库管理和一个功能强大的仿真调试器等在内的完整开发方案,通过一个集成开发环境(μVision)将这些部分组合在一起。运行Keil软件需要WIN98、NT、WIN2000、WINXP等操作系统。
如果你使用C语言编程,那么Keil几乎就是你的不二之选,即使不使用C语言而仅用汇编语言编程,其方便易用的集成环境、强大的软件仿真调试工具也会令你事半功倍。
keil在调试时watch 窗口里面的各个选项框有什么不同?
当进入程序状态时,观察窗口默认的页面是“locals",这个窗口页面会动态显示当前模块变量(即局部变量和全局变量),一旦模块变了(即 域 变了),属于这个模块(域)的局部变量就不会在在这窗口页面出现的。所以说这个窗口页面是动态显示就是这个原因。
观察窗口的其它页面,如"watch #1"等等,这里是观察指定的变量的地方,可以指定任何一个!,如:PC。当指定某一局部变量时,如果指定的这一时刻没有定义这个变量,这个指定变量就显示为“?????”,当定义后,它就有值。如果有两个同名的变量,有两种情况。第一:域窄的被域广的取当,即不能显示域窄的变量。第二:域没有关系,第一次出现的局部变量被显示,以后的同名变量不能被显示。
调试状态下,鼠标选中变量后点右键,弹出的菜单有增加到变量窗口一栏,添加到watch #1 watch #2都可以。
call stack是调用堆栈里的值 参考技术A 当进入程序状态时,观察窗口默认的页面是“locals",这个窗口页面会动态显示当前模块变量(即局部变量和全局变量),一旦模块变了(即 域 变了),属于这个模块(域)的局部变量就不会在在这窗口页面出现的。所以说这个窗口页面是动态显示就是这个原因。
观察窗口的其它页面,如"watch #1"等等,这里是观察指定的变量的地方,可以指定任何一个!,如:PC。当指定某一局部变量时,如果指定的这一时刻没有定义这个变量,这个指定变量就显示为“?????”,当定义后,它就有值。如果有两个同名的变量,有两种情况。第一:域窄的被域广的取当,即不能显示域窄的变量。第二:域没有关系,第一次出现的局部变量被显示,以后的同名变量不能被显示。
调试状态下,鼠标选中变量后点右键,弹出的菜单有增加到变量窗口一栏,添加到watch #1 watch #2都可以。
call stack是调用堆栈里的值。
如何重建一个损坏的调用堆栈(callstack)
原文时间:2011年07月04日
原文地址:http://blog.aaronballman.com/2011/07/reconstructing-a-corrupted-stack-crawl/
翻译:magictong
时间:2014年05月29日夜
后记:可惜原始的DUMP文件作者并没有上传
在我的日常工作中。我经常阅读来之微软WinQual(译注:https://sysdev.microsoft.com/ http://en.wikipedia.org/wiki/Winqual)的报告。
这些报告里面一般包括着dump文件(译注:崩溃转储文件,我们一般都是叫dump文件。是一种软件崩溃之后产生的文件,可用于事后调试)。从这些dump文件中面我能够分析出一些经常使用的软件里面究竟出了什么问题,造成它崩溃了。
总而言之。这是一个超赞的系统,我强烈建议各个独立软件开发商(原文:ISV)去上面注冊(尤其是这个系统对不论什么人都是免费的,仅仅要你的可运行文件是正确签名的)。
近期我拿到了一个堆栈已经被严重破坏了的dump文件,我想和大家讨论一下怎么使用Windbg工具来重建它的调用堆栈(callstack)。
在開始之前,让我们先看看一个原始的调用堆栈是什么样子的。在Windbg里面运行“k”命令就可以。
0:000> k
ChildEBP RetAddr
028b89cc 77c75350 ntdll!KiFastSystemCallRet
028b89d0 77c4b208 ntdll!ZwTerminateProcess+0xc
028b89e0 763e41ec ntdll!RtlExitUserProcess+0x7a
028b89f4 10056386 kernel32!ExitProcess+0x12
WARNING: Stack unwind information not available. Following frames may be wrong.
028b89fc 100565a0 EyeOneIO!I1_SynchronizeWhitebases+0xf0f6
028b8a0c 10054803 EyeOneIO!I1_SynchronizeWhitebases+0xf310
00000000 00000000 EyeOneIO!I1_SynchronizeWhitebases+0xd573
从上面的调用堆栈来看,有几个特征表明这个堆栈已经被破坏了。首先,调用堆栈的基址不可能从0x00000000開始。通常情况下。它从main函数的入口地址開始,或者从一个线程的入口地址開始,可是从上面的调用堆栈来看我们没看看到这个特征。另外,Windbg也发出了“Stack unwind information not available. Following frames may be wrong.”的警告(译注:这句警告的意思就是说。以下的栈帧可能是错误的)。
第一步。既然堆栈已经错误了。我们当然须要重建当前运行现成的堆栈,并找到当前现成堆栈的起始位置。
这里有个简单的扩展命令能够查看,使用!teb就可以(译注:!teb用于查看当前线程运行环境):
0:000> !teb
TEB at 7ffdb000
ExceptionList: 028b8a28
StackBase: 028c0000
StackLimit: 028b6000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffdb000
EnvironmentPointer: 00000000
ClientId: 00000a4c . 00000e3c
RpcHandle: 00000000
Tls Storage: 7ffdb02c
PEB Address: 7ffdf000
LastErrorValue: 14007
LastStatusValue: c0150008
Count Owned Locks: 0
HardErrorMode: 0
看上面!teb命令显示的结果里面,StackBase和StackLimit告诉了我们当前线程的堆栈在内存中的范围,因此我们如今能够转储这个范围内的地址。然后从里面寻找一些有意义和实用的东西(译注:就是把内存地址和相应的符号地址相应起来,然后寻找和当前的线程有关的调用堆栈)。
Windbg里面有个专门的dds命令就是用来做这个事情的,dds命令须要你指定一个起始地址,然后它从给定的起始地址開始转储一定范围内的地址。而且尝试把每一个地址里面的内容和符合(symbol)相应起来(译注:假如能够相应的话)。
dds转储的内容包括三列数据。第一列显示的是顺序递增的地址,第二列是显示地址里面的数据,第三列是符号名称,假设地址里面的数据能够被成功解析为一个符号的话。否则第三列就是显示的空白。
把真实的栈转储出来看看(省略了一些无关项):
(译注:使用命令 dds 028b6000,要显示更后面的内容能够在028b6000的后面加上一个偏移之后再对新地址使用 dds 命令)
028b6000 00000000
...
028bf9d8 00000000
028bf9dc 00000000
028bf9e0 79035b7f
028bf9e4 028bfa1c
028bf9e8 6e760b5b i1IO!i1IO::measureOneStrip+0xbb
028bf9ec 42b840fc
...
028bfa18 00000000
028bfa1c 028bfd98
028bfa20 6e763387 i1IO!i1IO::_measureSingleRowScanThreaded+0x1467
028bfa24 42b840fc
...
028bfd94 00000006
028bfd98 028bfe2c
028bfd9c 6e761062 i1IO!i1IO::_advancedMeasureThreaded+0x222
028bfda0 013a8520
028bfda4 79035e2e
...
028bfe28 00000000
028bfe2c 028bfe38
028bfe30 763ed0e9 kernel32!BaseThreadInitThunk+0xe
028bfe34 012118e0
028bfe38 028bfe78
028bfe3c 77c516c3 ntdll!__RtlUserThreadStart+0x23
028bfe40 012118e0
...
028bfe74 00000000
028bfe78 028bfe90
028bfe7c 77c51696 ntdll!_RtlUserThreadStart+0x1b
028bfe80 6e760e40 i1IO!i1IO::_advancedMeasureThreaded
...
028c0000 ????????
实际上转储出来的堆栈比上面列出来的大得多,只是为了简单起见。我仅仅保留一些相关的部分。
如今要做的第一件事情就是定位到callstack的起始位置。在这个样例里面,RtlUserThreadStart看起来非常像是这个起始位置。由于它是线程的起始调用函数。
在找到起始点之后,获取起始点的前一个堆栈地址A(第一列),然后在堆栈的内容里面(第二列)寻找是否有等于A的堆栈B(向低地址寻找,由于堆栈是向低地址增长的)。然后再在堆栈内容里面寻找是否有等于B的堆栈地址C……,依照这样的方法不停的搜索内存。直到不能再找到不论什么东西或者找到空地址。
(译注:这个就是利用的标准函数栈帧的基本原理。对此处不理解的能够去了解下标准函数栈帧,一般没有经过FPO优化的调用函数链,能够通过EBP的值在整个堆栈上面串联起来,事实上Windbg自己也是这么找的。而本文讨论的恰恰是由于堆栈被破坏之后。Windbg找不到正确的callstack之后,我们怎么手动恢复的问题)
在我们这个样例里面,我们从以下的堆栈開始找:
028bfe78 028bfe90
028bfe7c 77c51696 ntdll!_RtlUserThreadStart+0x1b
搜索地址028bfe78,得到以下的堆栈:
028bfe38 028bfe78
028bfe3c 77c516c3 ntdll!__RtlUserThreadStart+0x23
搜索地址028bfe38。得到以下的堆栈:
028bfe2c 028bfe38
028bfe30 763ed0e9 kernel32!BaseThreadInitThunk+0xe
搜索地址028bfe2c,得到以下的堆栈:
028bfd98 028bfe2c
028bfd9c 6e761062 i1IO!i1IO::_advancedMeasureThreaded+0x222
搜索地址028bfd98。得到以下的堆栈:
028bfa1c 028bfd98
028bfa20 6e763387 i1IO!i1IO::_measureSingleRowScanThreaded+0x1467
搜索地址028bfa1c。得到以下的堆栈:
028bf9e4 028bfa1c
028bf9e8 6e760b5b i1IO!i1IO::measureOneStrip+0xbb
如今。继续搜索028bf9e4已经不能再在堆栈里面找到信息了,也就是说我们可能已经找到了终于出问题的函数位置。我们能够使用Windbg尝试修复我们的callstack,当然我们须要给它我们上面找到的这些信息。事实上非常easy。仅仅要上面没找错,我们给 k 命令指明一个确定地址,通过 L 參数传递进去(译注:用上面我们最后找到的028bfa1c),那么Windbg立即就会给我们一个更加友好的callstack信息。
0:000> k L=028bf9e4
ChildEBP RetAddr
028b89cc 77c75350 ntdll!KiFastSystemCallRet
028b89d0 77c4b208 ntdll!ZwTerminateProcess+0xc
028bf9e4 6e760b5b ntdll!RtlExitUserProcess+0x7a
028bfa1c 6e763387 i1IO!i1IO::measureOneStrip+0xbb
028bfd98 6e761062 i1IO!i1IO::_measureSingleRowScanThreaded+0x1467
028bfe2c 763ed0e9 i1IO!i1IO::_advancedMeasureThreaded+0x222
028bfe38 77c516c3 kernel32!BaseThreadInitThunk+0xe
028bfe78 77c51696 ntdll!__RtlUserThreadStart+0x23
028bfe90 00000000 ntdll!_RtlUserThreadStart+0x1b
如今我们看到的callstack是不是更加完整而且合理了?!没有了调用栈帧错误的警告,而且callstack的调用基址也正常了。
希望上面介绍的这样的方法能给你的调试工作带来一些帮助。
以上是关于keil中callstack在哪的主要内容,如果未能解决你的问题,请参考以下文章