kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp
sar -n DEV 1

gdb 没有debug信息step单步调试

一、如果有函数调用,他会进入该函数

1
step <count>

单步跟踪,如果有函数调用,他会进入该函数。进入函数的前提是,此函数被编译有 debug信息。很像 VC等工具中的 step in。后面可以加 count也可以不加,不加表示一条条地执行,加表示执行后面的 count条指令,然后再停住。

二、如果有函数调用,他不会进入该函数

1
next <count>

同样单步跟踪,如果有函数调用,他不会进入该函数。很像 VC等工具中的 step over。后面可以加 count也可以不加,不加表示一条条地执行,加表示执行后面的 count条指令,然后再停住。

三、无debuginfo时需要set step-mode [on/off]

1
2
3
4
5
6
set step-mode [on/off]
set step-mode on
#打开 step-mode模式,于是,在进行单步跟踪时,程序不会因为没有 debug信息而不停住。这个参数有很利于查看机器码。

set step-mode off
关闭 step-mode模式。

四、跟踪子进程

使用gdb调试的时候,gdb只能跟踪一个进程。

可以在fork函数调用之前,通过指令设置gdb调试工具跟踪父进程或子进程。

默认情况下gdb是跟踪父进程的。

1
2
set follow-fork-mode child   # 命令设置gdb在fork之后跟踪子进程。
set follow-fork-mode parent  # 设置跟踪父进程。

五、修改值

1
2
3
4
5
6
set $rax=0   # 寄存器

set m=6       # 变量,需要debuginfo

p $rsp+0x714  # 内存
set *0x7f962c1fa564 = 0x6

gdb x命令语法

可以使用examine命令(简写是x)来查看内存地址中的值。x命 令的语 法如下所示:

1
x/<n/f/u> <addr>  

n、f、u是可选的参数。
n是一个正整数,表示需要显示的内存单元的个数, 也就是说从当前地址向后显示几个 内存单元的内容,一个内存单元的大小由后面的u定义。
f 表示显示的格式,参见下面。如果地址所指的是字符串,那么格式可以是s,如果地十是指令地址,那么格式可以是i。
u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字 节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。
表示一个内存地址。

注意:严格区分n 和u的关系,n表示单元个数,u表示每个单元的大小。

n/f/u三个参数可以一起使用。例如:
命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示输出三个单位,u表示按十六进制显示。

输出格式

一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量 的中的位的情况。要做到这样,你可以使用GDB的数据显示格式:

1
2
3
4
5
6
7
8
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。

(gdb) help x Examine memory: x/FMT ADDRESS. ADDRESS is an expression for the memory address to examine. FMT is a repeat count followed by a format letter and a size letter. Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal), t(binary), f(float), a(address), i(instruction), c(char) and s(string). Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes). The specified number of objects of the specified size are printed according to the format.

Defaults for format and size letters are those previously used. Default count is 1. Default address is following last thing printed with this command or “print”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(gdb) p f1
$4 = 8.25
(gdb) p f2
$5 = 125.5
(gdb) x/x &f1
0xbffff380:   0x41040000
(gdb) x/xw &f1
0xbffff380:   0x41040000
(gdb) x/xw &f2
0xbffff384:   0x42fb0000
(gdb) x/2xw &f1
0xbffff380:   0x41040000   0x42fb0000
(gdb) x/4xw &f1
0xbffff380:   0x41040000   0x42fb0000   0xbffff408   0x00bcba66
(gdb) x/tw &f1
0xbffff380:   01000001000001000000000000000000
(gdb) x/2tw &f1
0xbffff380:   01000001000001000000000000000000   01000010111110110000000000000000
(gdb) p record
$10 = {12, 76, 48, 62, 94, 17, 32, 37, 52, 69}
(gdb) p &record
$11 = (int (*)[10]) 0x8049740
(gdb) x/4uw 0x8049740
0x8049740 <record>:   12   76   48   62
(gdb) x/6ow 0x8049740
0x8049740 <record>:   014   0114   060   076
0x8049750 <record+16>:   0136   021

gdb print 语法

1
2
print <expr>
print /<f> <expr>

<expr>是表达式,是你所调试的程序的语言的表达式(GDB可以调试多种编程语言),是输出的格式,比如,假如要把表达式按16进制的格式输出,那么就是/x。

一、表达式

print和许多GDB的命令一样,可以接受一个表达式,GDB会根据当前的程序运行的数据来计算这个表达式,既然是表达式,那么就可以是当前程序运行中的const常量、变量、函数等内容。可惜的是GDB不能使用你在程序中所定义的宏。

表达式的语法应该是当前所调试的语言的语法,由于C/C++是一种大众型的语言,所以,本文中的例子都是关于C/C++的。(而关于用GDB调试其它语言的章节,我将在后面介绍)

在表达式中,有几种GDB所支持的操作符,它们可以用在任何一种语言中。
@是一个和数组有关的操作符,在后面会有更具体的说明。
::指定一个在文件或是一个函数中的变量。
{<type>} <addr>表示一个指向内存地址的类型为type的一个对象。 p {char*} 0xffffffff12345678

二、程序变量

在GDB中,你可以随时查看以下三种变量的值:
1、全局变量(所有文件可见的)
2、静态全局变量(当前文件可见的)
3、局部变量(当前Scope可见的)
假如你的局部变量和全局变量发生冲突(也就是重名),一般情况下是局部变量会隐藏全局变量,也就是说,假如一个全局变量和一个函数中的局部变量同名时,假 如当前停止点在函数中,用print显示出的变量的值会是函数中的局部变量的值。假如此时你想查看全局变量的值时,你可以使用“::”操作符:

1
2
file::variable
function::variable

可以通过这种形式指定你所想查看的变量,是哪个文件中的或是哪个函数中的。例如,查看文件f2.c中的全局变量x的值:

1
gdb) p 'f2.c'::x

当然,“::”操作符会和C++中的发生冲突,GDB能自动识别“::” 是否C++的操作符,所以你不必担心在调试C++程序时会出现异常。

另外,需要注重的是,假如你的程序编译时开启了优化选项,那么在用GDB调试被优化过的程序时,可能会发生某些变量不能访问,或是取值错误码的情况。这个是很正常的,因为优化程序会删改你的程序,整理你程序的语句顺序,剔除一些无意义的变量等,所以在GDB调 试这种程序时,运行时的指令和你所编写指令就有不一样,也就会出现你所想象不到的结果。对付这种情况时,需要在编译程序时关闭编译优化。一般来说,几乎所 有的编译器都支持编译优化的开关,例如,GNU的C/C++编译器GCC,你可以使用“-gstabs”选项来解决这个问题。关于编译器的参数,还请查看 编译器的使用说明文档。

三、数组

有时候,你需要查看一段连续的内存空间的值。比如数组的一段,或是动态分配的数据的大小。你可以使用GDB的“@”操作符,“@”的左边是第一个内存的地址的值,“@”的右边则你你想查看内存的长度。例如,你的程序中有这样的语句:

1
int *array = (int *) malloc (len * sizeof (int));

于是,在GDB调试过程中,你可以以如下命令显示出这个动态数组的取值:

1
p *array@len

@的左边是数组的首地址的值,也就是变量array所指向的内容,右边则是数据的长度,其保存在变量len中,其输出结果,大约是下面这个样子的:

1
2
(gdb) p *array@len
$1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}

假如是静态数组的话,可以直接用print数组名,就可以显示数组中所有数据的内容了。