linux应用中,在一个进程内如何获取本进程内其它线程的堆栈信息、

linux应用中,在一个进程内如何获取本进程内其它线程的堆栈信息、,第1张

先用ps看目前存在的所有进程的进程号,然后可以对具体进程采用以下这些 *** 作:(11345就是对应具体的进程号)
只查看该进程:ps -ef | grep 11345
查看该进程打开的文件:lsof -p 11345
查看内存分配:lcat /proc/11345/maps
查看堆栈:pstack 11345
查看发出的系统调用:strace -p 11345
查看调用库函数:ltrace -p 11345

结合反汇编窗口的数据结构。以及数据窗口的跟随。还要寄存器窗口的变化。来判断是不是你需要的。
堆栈窗口存储了重要的数据地址。可以再想看的那一条堆栈用反汇编窗口跟随。来查看它的上下结构。用数据窗口跟随。查看其数据。比如关键码。账号密码。注册码之类。有时候能查看到。有时候堆栈反复入栈出栈在寄存器上能看到其地址送入了哪个寄存器。得知某些地址经常使用。关键处可能在那。
不过总的来说。LZ的基础太差了。你一句话哪个函数是需要的。我得说明很多。因为问题很广。仅仅一个广阔的问题。包涵了几十个要点。甚至包括几十个问题。还要多看书。我基本功也很不扎实。

分辨程序领空最简单的办法就是00XXXXXX地址很大可能是程序领空。如果是7FXXXXXX这一类的就是系统领空。有时候OD上标题显示你程序的名称。这时你所在的地址是程序领空。还有就是。其它OD标题上出现类似函数的字母。或者XXXXDLL表示你目前在系统领空。

以上2点。教程和书上介绍写的很清楚。而且还有。不过堆栈的话。还要狠了解函数。牵扯到win32下编程函数。楼主至少要看懂汇编语言和win32下编程2本书。还要熟练运用OD。经常看看经验贴。相信你不会再问OD的事情了。

函数名可以查到,vc工具的depend就可以了,其他也有不少工具可以查到,声明的没有,声明没有编译到dll里的,声明包括函数名、参数和返回值。函数名说了,参数得通过反汇编得到,由于函数里通过堆栈来获得参数值,通过反汇编该函数,从函数头开始找出取堆栈的代码,可以找出该函数的参数个数和类型。参数的含意得读懂函数的反汇编代码,了解各参数的使用才能确定参数用途了。返回值在windows中是通过寄存器eax来返回的,如果返回的值小于32位,就用eax保存值,大于32位就用eax保存返回值的地址,所以要知道返回值的类型,得了解函数最好把什么存到eax中,存的值是什么才能知道返回值。

在Go语言中有一些调试技巧能帮助我们快速找到问题,有时候你想尽可能多的记录异常但仍觉得不够,搞清楚堆栈的意义有助于定位Bug或者记录更完整的信息。

本文将讨论堆栈跟踪信息以及如何在堆栈中识别函数所传递的参数。
Functions
先从这段代码开始:


Listing 1

01 package main
02
03 func main() {
04     slice := make([]string, 2, 4)
05     Example(slice, "hello", 10)
06 }
07
08 func Example(slice []string, str string, i int) {
09     panic("Want stack trace")
10 }

Example函数定义了3个参数,1个string类型的slice, 1个string和1个integer, 并且抛出了panic,运行这段代码可以看到这样的结果:
Listing 2

Panic: Want stack trace
goroutine 1 [running]:
mainExample(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
/Users/bill/Spaces/Go/Projects/src/githubcom/goinaction/code/
temp/maingo:9 +0x64
mainmain()
/Users/bill/Spaces/Go/Projects/src/githubcom/goinaction/code/
temp/maingo:5 +0x85
goroutine 2 [runnable]:
runtimeforcegchelper()
/Users/bill/go/src/runtime/procgo:90
runtimegoexit()
/Users/bill/go/src/runtime/asm_amd64s:2232 +0x1
goroutine 3 [runnable]:
runtimebgsweep()
/Users/bill/go/src/runtime/mgc0go:82
runtimegoexit()
/Users/bill/go/src/runtime/asm_amd64s:2232 +0x1

堆栈信息中显示了在panic抛出这个时间所有的goroutines状态,发生的panic的goroutine会显示在最上面。
Listing 3

01 goroutine 1 [running]:
02 mainExample(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
/Users/bill/Spaces/Go/Projects/src/githubcom/goinaction/code/
temp/maingo:9 +0x64
03 mainmain()
/Users/bill/Spaces/Go/Projects/src/githubcom/goinaction/code/
temp/maingo:5 +0x85

第1行显示最先发出panic的是goroutine 1, 第二行显示panic位于mainExample中, 并能定位到该行代码,在本例中第9行引发了panic。

下面我们关注参数是如何传递的:
Listing 4

// Declaration
mainExample(slice []string, str string, i int)
// Call to Example by main
slice := make([]string, 2, 4)
Example(slice, "hello", 10)
// Stack trace
mainExample(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

这里展示了在main中带参数调用Example函数时的堆栈信息,比较就能发现两者的参数数量并不相同,Example定义了3个参数,堆栈中显示了6个参数。现在的关键问题是我们要弄清楚它们是如何匹配的。

第1个参数是string类型的slice,我们知道在Go语言中slice是引用类型,即slice变量结构会包含三个部分:指针、长度(Lengthe)、容量(Capacity)
Listing 5

// Slice parameter value
slice := make([]string, 2, 4)
// Slice header values
Pointer:  0x2080c3f50
Length:   0x2
Capacity: 0x4
// Declaration
mainExample(slice []string, str string, i int)
// Stack trace
mainExample(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

因此,前面3个参数会匹配slice, 如下图所示:
Figure 1

figure provided by Georgi Knox

我们现在来看第二个参数,它是string类型,string类型也是引用类型,它包括两部分:指针、长度。
Listing 6

// String parameter value
"hello"
// String header values
Pointer: 0x425c0
Length:  0x5
// Declaration
mainExample(slice []string, str string, i int)
// Stack trace
mainExample(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

可以确定,堆栈信息中第4、5两个参数对应代码中的string参数,如下图所示:
Figure 2

figure provided by Georgi Knox

最后一个参数integer是single word值。
Listing 7

// Integer parameter value
10
// Integer value
Base 16: 0xa
// Declaration
mainExample(slice []string, str string, i int)
// Stack trace
mainExample(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

现在我们可以匹配代码中的参数到堆栈信息了。
Figure 3

figure provided by Georgi Knox
Methods

如果我们将Example作为结构体的方法会怎么样呢
Listing 8

01 package main
02
03 import "fmt"
04
05 type trace struct{}
06
07 func main() {
08     slice := make([]string, 2, 4)
09
10     var t trace
11     tExample(slice, "hello", 10)
12 }
13
14 func (t trace) Example(slice []string, str string, i int) {
15     fmtPrintf("Receiver Address: %p\n", t)
16     panic("Want stack trace")
17 }


如上所示修改代码,将Example定义为trace的方法,并通过trace的实例t来调用Example。

再次运行程序,会发现堆栈信息有一点不同:
Listing 9

Receiver Address: 0x1553a8
panic: Want stack trace
01 goroutine 1 [running]:
02 main(trace)Example(0x1553a8, 0x2081b7f50, 0x2, 0x4, 0xdc1d0, 0x5, 0xa)
/Users/bill/Spaces/Go/Projects/src/githubcom/goinaction/code/
temp/maingo:16 +0x116
03 mainmain()
/Users/bill/Spaces/Go/Projects/src/githubcom/goinaction/code/
temp/maingo:11 +0xae

首先注意第2行的方法调用使用了pointer receiver,在package名字和方法名之间多出了"trace"字样。另外,参数列表的第1个参数标明了结构体(t)地址。我们从堆栈信息中看到了内部实现细节。
Packing

如果有多个参数可以填充到一个single word, 则这些参数值会合并打包:
Listing 10

01 package main
02
03 func main() {
04     Example(true, false, true, 25)
05 }
06 
07 func Example(b1, b2, b3 bool, i uint8) {
08     panic("Want stack trace")
09 }

这个例子修改Example函数为4个参数:3个bool型和1个八位无符号整型。bool值也是用8个bit表示,所以在32位和64位架构下,4个参数可以合并为一个single word。
Listing 11

01 goroutine 1 [running]:
02 mainExample(0x19010001)
/Users/bill/Spaces/Go/Projects/src/githubcom/goinaction/code/
temp/maingo:8 +0x64
03 mainmain()
/Users/bill/Spaces/Go/Projects/src/githubcom/goinaction/code/
temp/maingo:4 +0x32

这是本例的堆栈信息,看下图的具体分析:
Listing 12

// Parameter values
true, false, true, 25
// Word value
Bits    Binary      Hex   Value
00-07   0000 0001   01    true
08-15   0000 0000   00    false
16-23   0000 0001   01    true
24-31   0001 1001   19    25
// Declaration
mainExample(b1, b2, b3 bool, i uint8)
// Stack trace
mainExample(0x19010001)

以上展示了参数值是如何匹配到4个参数的。当我们看到堆栈信息中包括十六进制值,需要知道这些值是如何传递的。

堆(heap)和栈(stack)有什么区别
简单的可以理解为:
heap:是由malloc之类函数分配的空间所在地。地址是由低向高增长的。
stack:是自动分配变量,以及函数调用的时候所使用的一些空间。地址是由高向低减少的。
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其 *** 作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

C语言中的堆和栈都是一种数据项按序排列的数据结构。

栈就像装数据的桶或箱子

我们先从大家比较熟悉的栈说起吧,它是一种具有后进先出性质的数据结构,也就是说后存放的先取,先存放的后取。

这就如同我们要取出放在箱子里面底下的东西(放入的比较早的物体),我们首先要移开压在它上面的物体(放入的比较晚的物体)。

堆像一棵倒过来的树

而堆就不同了,堆是一种经过排序的树形数据结构,每个结点都有一个值。

通常我们所说的堆的数据结构,是指二叉堆。堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。

由于堆的这个特性,常用来实现优先队列,堆的存取是随意,这就如同我们在图书馆的书架上取书。

虽然书的摆放是有顺序的,但是我们想取任意一本时不必像栈一样,先取出前面所有的书,书架这种机制不同于箱子,我们可以直接取出我们想要的书。

扩展资料:

关于堆和栈区别的比喻

使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。

使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

参考资料来源:百度百科-堆栈


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/13394488.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-07-27
下一篇 2023-07-27

发表评论

登录后才能评论

评论列表(0条)

保存