学会用gdb 调试程序,查看程序的运行过程,各个变量的值

【转】golang 并发程序写入map两种实现方式sync.Mutex和chan的效率对比_局部变量

 

 

【转】golang 并发程序写入map两种实现方式sync.Mutex和chan的效率对比_堆栈_02

 

 

-----------------------------------------------------------------------------------------------------------------------

 

golang原生的数据结构map,由于是通过hash方式实现的,不支持并发写入,但是在golang很多并发场景中,不可避免的需要写入map,下面介绍两种解决map并发写入的实现方式:

  1. sync.Mutex互斥锁(通过加锁解锁解决map不能并发写入的问题)
  2. chan (通过管道来解决map并发的问题),chan的存在完美解决goroutine之间的通信以及数据race问题,但是它的性能如何呢,下面让我们来测一下

首先先上代码:




代码很简洁,我就不多解释了。

看一下执行效率    

【转】golang 并发程序写入map两种实现方式sync.Mutex和chan的效率对比_堆栈_03

 

可以看到,chan的实现方式其性能并不理想,比sync.Mutex的实现方式的效率低了大约25%左右。 

chan的效率为什么不理想呢,其实原因很简单,下面看一下它的数据结构就一目了然了




​package​​main​​import​​(​​"fmt"​​)​​func​​main() {​​s := ​​"hello, world!"​​b := []byte(s)​​c := make(​​chan​​int, 1)​​c<-1​​fmt.Println(s, b, <-c)​​}​


 

【转】golang 并发程序写入map两种实现方式sync.Mutex和chan的效率对比_局部变量_04

我们可以看到channel的实现是通过互斥锁和数组的方式实现的,而且还有一些其他字段的同步,因此它的效率是不会比直接用互斥锁更快的。

很多看上去很高端很简洁的东西,其实是编辑器和runtime在后面为我们码农实现,当然与之相来是额外的内存和时间开销。

--------------------------------------------------------------------------------------------------------------

. gdb 运行初始化命令 -x usercmd

在.gdbinit 初始化命令中, 可以用#(同bash 注释符合)来注释

可以用source 在启动gdb 后运行gdb 命令文件

可以用file 在启动gdb 后再加载文件

2. gdb 传递命令行 参数

 a. run 

 b. set args  

可用 show args 查看

3. gdb 中宏的操作

默认编译的时候,调试过程是看不见宏的值的。编译时候需要给选项。-g3

gcc  -g3 -o test.c  test

gdb test

察看宏(macro)命令: p macroname(...)  

就像察看一个普通变量一样。如果只想看宏的展开形势,就用如下命令:

macro expand macroname(...) 查看展开形式

还可以用info macro macroname 查看宏定义。

4. gdb 临时断点: tbreak

5. gdb 设置显示选项 . 

例如 set print pretty

打印结构变量类型. ptype struct 类型名

----------------------------------------

6.  让GDB记录输出信息到文件

----------------------------------------

(gdb) set logging on    -> 打开记录功能。

(gdb) set logging off -> 关闭记录功能。

(gdb) show logging -> 显示记录功能中每个选项的设置。

Future logs will be written to gdb.txt.   ---> 表示log 没有打开

Currently logging to "gdb.txt".    --> 表示log 已经打开

----------------------------------------

7. gdb 调试运行的进程

gdb -p <进程号>

或者:

gdb

attach <进程号>

----------------------------------------

下面不常用的功能,供了解

set logging file -> 改变记录文件,默认记录文件是gdb.txt。

set logging overwrite [on|off] -> 默认这个项目是关闭,

也就是以添加的形式将记录信息写入文件,这样之前文件中的信息不会被覆盖掉。

注意:如果设置这个选项的时候记录功能已经打开,需要关闭记录功能再重新打开才能起作用。

set logging redirect [on|off] -> 设置输出信息只记录到文件不作显示。

默认这个项目是关闭的,GDB将输出信息到终端和记录文件。如果打开这个项目,GDB将只输出信息到记录文件。

注意:如果设置这个选项的时候记录功能已经打开,需要关闭记录功能再重新打开才能起作用。

注意:记录功能只能记录GDB的输出信息,被调试程序的输出信息仍然会输出到终端。

---------------------------------------

7. 为什么没有core文件生成呢?

---------------------------------------

有时候程序down了, 但是core文件却没有生成. 

core文件的生成跟你当前系统的环境设置有关系, 可以用下面的语句设置一下, 然后再运行程序便生成了core文件.

ulimit -c unlimited

gdb a.out core

----------------------------------------

8. gdb加载内核模块符号信息:

add-symbol-file <module_file>ADDR

例:

root@ubuntu:/home/hjj/software/uxfs_now/kern# cat debug

set remotebaud 115200

target remote /dev/ttyS1

add-symbol-file uxfs.ko 0xe0886000 -s .exit.text 0xe0888404

-------------------------------------------------

附录:gdb  常用调试命令

-------------------------------------------------

(gdb) l :(字母l)列出源码

(gdb) b n :在第n行处设置断点

(gdb) b func:在函数func()的入口处设置断点

(gdb) 条件断点:条件可以是任何合法的c 表达式。 例如 b n if val1==val2

          当已经设置了断点,可以用condition 命令对断点号添加条件, 例: condition 2 val1==val2 , 注意,没有if 单词

          当对变量的改变更感兴趣时,可以用watch 命令

 (gdb) info break: 查看断点信息

(gdb) r:运行程序

(gdb) n:单步执行

(gdb) s:单步调试如果有函数调用,则进入函数;与命令n不同,n是不进入调用的函数的

(gdb) c:继续运行

(gdb) p 变量 :打印变量的值     也能够修改变量的值(用 = 赋值) // 打印寄存器值。 p $eax

(gdb) x/nfu 显示内存  // n为个数,f 为格式,u为每单元长度

(gdb) bt:查看函数堆栈

(gdb) finish:退出函数

(gdb) display 每次中断或单步都显示你关心的变量

(gdb)undisplay <编号>

(gdb) shell 命令行:执行shell命令行

(gdb) set args 参数:指定运行时的参数

(gdb) show args:查看设置好的参数

(gdb)info program: 来查看程序的是否在运行,进程号,被暂停的原因。 // 打印寄存器数组, info reg,  简写 i reg

(gdb)clear 行号n:清除第n行的断点

(gdb)delete 断点号n:删除第n个断点

(gdb)disable 断点号n:暂停第n个断点

(gdb)enable 断点号n:开启第n个断点

----------------------------------------

gdb 条件调试

----------------------------------------

“break ... if cond" 命令

例: b fun() if i==20

ignore bnum count

watch var    // 此时监测var的变化

----------------------------------------

gdb 多线程调试

----------------------------------------

info threads

thread ID 来切换

set scheduler-locking off|on|step 估计实际使用过多线程调试的人都可以发现,

在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,

怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。

off 不锁定任何线程,也就是所有线程都执行,这是默认值。 

on 只有当前被调试程序会执行。 

设置 on 之后, step 在单步的时候, 只有当前线程会执行。

next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)。


1.gdb有个很友好的调式界面,就是layout方式调试,可以让你边调试变查看源码,

但是layout方式有时候有点bug,不过基本上还可以使用,

可以用ctrl+x和ctrl+a 来切换。可能layout会造成控制台花屏,使用ctrl+L清屏。

----------------------------------------

 GDB堆栈跟踪的方法

----------------------------------------

程序“调用堆栈”是当前函数之前的所有已调用函数的列表(包括当前函数)。每个函数及其变量都被分配了一个“帧”,

最近调用的函数在 0 号帧中(“底部”帧)。要打印堆栈,发出命令 'bt'('backtrace' [回溯] 的缩写):

(gdb) bt

#0  0x80483ea in wib (no1=8, no2=8) at eg1.c:7

#1  0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21

此结果显示了在 main() 的第 21 行中调用了函数 wib()(只要使用 'list 21' 就能证实这一点),而且 wib() 在 0 号帧中,

main() 在 1 号帧中。由于 wib() 在 0 号帧中,那么它就是执行程序时发生算术错误的函数。

实际上,发出 'info locals' 命令时,gdb 会打印出当前帧中的局部变量,缺省情况下,这个帧中的函数就是被中断的函数(0 号帧)。

可以使用命令 'frame' 打印当前帧。要查看 main 函数(在 1 号帧中)中的变量,可以发出 'frame 1' 切换到 1 号帧,

然后发出 'info locals' 命令:

(gdb) frame 1

#1  0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21

21          result = wib(value, div);

(gdb) info locals

value = 8

div = 8

result = 4

i = 2

total = 6

此信息显示了在第三次执行 "for" 循环时(i 等于 2)发生了错误,此时 "value" 等于 "div"。

可以通过如上所示在 'frame' 命令中明确指定号码,或者使用 'up' 命令在堆栈中上移以及 'down' 命令在堆栈中下移来切换帧。

要获取有关帧的进一步信息,如它的地址和程序语言,可以使用命令 'info frame'。

gdb 堆栈命令可以在程序执行期间使用,也可以在 core 文件中使用,因此对于复杂的程序,可以在程序运行时跟踪它是如何转到函数的。

 查看栈信息

当程序被停住了,你需要做的第一件事就是查看程序是在哪里停住的。 当你的程序调用了一个函数,

函数的地址,函数参数,函数内的局部变量都会被压入“栈”(Stack)中。你可以用GDB命令来查看当前的栈中的信息。

下面是一些查看函数调用栈信息的GDB命令:

    Backtrace,bt 打印当前的函数调用栈的所有信息。如:        

        (gdb) bt

        #0 func (n=250) at tst.c:6

        #1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30

        #2 0x400409ed in __libc_start_main () from /lib/libc.so.6        

从上可以看出函数的调用栈信息:__libc_start_main --> main() --> func()    

    backtrace , bt n是一个正整数,表示只打印栈顶上n层的栈信息。

    backtrace <-n> ,bt <-n> -n表一个负整数,表示只打印栈底下n层的栈信息。        

如果你要查看某一层的信息,你需要在切换当前的栈,一般来说,程序停止时,最顶层的栈就是当前栈,如果你要查看栈下面层的详细信息,首先要做的是切换当前栈。

frame ,f n是一个从0开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,frame 1,表示栈的第二层。

    up          表示向栈的上面移动n层,可以不打n,表示向上移动一层。

down 表示向栈的下面移动n层,可以不打n,表示向下移动一层。

上面的命令,都会打印出移动到的栈层的信息。如果你不想让其打出信息。你可以使用这三个命令:

    select-frame 对应于 frame 命令。

    up-silently 对应于 up 命令。

    down-silently 对应于 down 命令。

查看当前栈层的信息,你可以用以下GDB命令:

frame 或 f       会打印出这些信息:栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。

info frame,info f 这个命令会打印出更为详细的当前栈层的信息,只不过,大多数都是运行时的内内地址。比如:函数地址,调用函数的地址,被调用函数的地址,目前的函数是由什么样的程序语言写成的、函数参数地址及值、局部变量的地址等等。如:

       (gdb) info f

       Stack level 0, frame at 0xbffff5d4:

         eip = 0x804845d in func (tst.c:6); saved eip 0x8048524

         called by frame at 0xbffff60c

         source language c.

         Arglist at 0xbffff5d4, args: n=250

         Locals at 0xbffff5d4, Previous frame's sp is 0x0

         Saved registers:

         ebp at 0xbffff5d4, eip at 0xbffff5d8              

     info args      打印出当前函数的参数名及其值。     

     info locals     打印出当前函数中所有局部变量及其值。        

     info catch      打印出当前的函数中的异常处理信息。

gdb 跟踪远程调试命令交互过程

"set debug remote 1" 将显示gdb 远程调试的过程。

gdb 内存断点设置。

rwatch, watch, awatch 分别代表读,写,读写内存断点,用的是硬件断点。

------------------------------------------------------------------

gdbtui  gdb 图形化接口。方便调试

启动gdb, 按ctrl-x cta-a 也可进入tui 模式

------------------------------------------------------------------

 help layout

     layout src

     layout asm

     layout split

 help winheight

     winheight src +5

     winheight src -4

help focus

    focus cmd

    focus src

    focus next

简记为

    fs next

    fs src

    fs cmd

--------------------------------------------------------------------------------

help info

Generic command for showing things about the program being debugged.

关于被调试程序的一些信息

help show

Generic command for showing things about the debugger

关于debugger状态的一些信息

--------------------------------------------------------------------------------

常用命令:

set args

file

source

set logging overwrite on

set logging on

set print pretty

show logging

set pagination off   // 不要出现 Type to continue 的提示信息 

info functions

info variables

list

list [函数名称]

与调试控制相关的命令

  • continue    继续运行程序直到下一个断点(类似于VS里的F5)
  • next        逐过程步进,不会进入子函数(类似VS里的F10)
  • step        逐语句步进,会进入子函数(类似VS里的F11)
  • until        运行至当前语句块结束
  • finish  运行至函数结束并跳出,并打印函数的返回值(类似VS的Shift+F11)

s : step in

fin: step out, 跳出函数

until 行号。 可用于跳出循环,加快了调试速度。

----------------------------------------

gdb 调试跟踪多进程程序

----------------------------------------

gdb只能跟踪一个进程(默认是跟踪父进程),而不能同时跟踪多个进程,

可以设置gdb跟踪父进程还是子进程, 命令如下:

set follow-fork-mode parent 跟踪父进程, 默认

set follow-fork-mode child  跟踪子进程