GDB调试程序实例(小寿转载)

2358阅读 0评论2009-12-07 xiaoshou330
分类:LINUX

上回话使用Makefile来组织源代码,这回简单地介绍下如何使用GDB来调试我们的程序;关于GDB的其它应用将在后续的章节中逐渐深入。

首先需要肯定一点,GDB是个功能异常强大的调试工具,其本身只能运行于字符模式,但是当前众多基于GUI的调试器/IDE,无论是自由软件还是商业软件,绝大多数都使用GDB作为其后端(但这些基于GUI的调试器都不太稳定),因此GDB是个不二的选择(笔者推荐的GUI调试器:insight和ddd)。

这里使用 hello_gdb.c 作为例子,如果你从前面一直看过来,对这个程序一定不会陌生,hello_gdb.c 主要在 hello_dubuntu2.c 的基础上,添加了几个整型和字符串型变量,来演示gdb的一些基本功能:
运行效果图:


hello_gdb.c 
/* 本例的主要目的是在窗口中显示一个按钮,
 * 单击按钮退出程序,并构建几个变量来演示GDB功能。
 */
#include

void
cb_button(GtkWidget *widget, gpointer data)
{// 按钮"button"的回调函数
        gint i=5;
        gint j=++i;
        gtk_main_quit();
}

//此函数用于演示GDB直接调用被调试程序的函数
gint
gdb_test(gint arg)
{
        g_print("arg=%d\n",arg);
        arg++;
        return arg;
}

int
main(int argc, char *argv[])
{
        GtkWidget *main_window; //主窗口对象
        GtkWidget *button;              //将要放置到主窗口中的按钮对象

        //构造两个变量用于演示GDB功能
        gint a=5;
        gchar *name="Dubuntu-6.06";
        //
        gtk_init(&argc, &argv);

        main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_window_set_title(GTK_WINDOW(main_window), "Hello,Dubuntu2!");
        //设置窗口的默认大小(宽200,高度50)
        gtk_window_set_default_size(GTK_WINDOW(main_window), 200,50);

        button = gtk_button_new_with_label("退出程序");
        gtk_container_add(GTK_CONTAINER(main_window), button);
        //为"button"连接“单击事件”要调用的回调函数
        g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(cb_button),NULL);

        gtk_widget_show(button);
        gtk_widget_show(main_window);
        //上边的两句可以合为 gtk_widget_show_all(window)

        g_signal_connect(G_OBJECT(main_window), "destroy", G_CALLBACK(cb_button),NULL);

        gtk_main();
        return 0;
}
 
编译:  gcc -g -Wall -o hello_gdb hello_gdb.c `pkg-config --cflags --libs gtk+-2.0`
注意:-g 参数用于为可执行文件生成调试信息;
         -Wall 用于在编译程序时打印所有的警告信息

好了,程序就是上边这个(hello_gdb.c),我们使用如下的命令对它进行编译:
gcc -g -Wall -o hello_gdb hello_gdb.c `pkg-config --cflags --libs gtk+-2.0`

编译完后,如果没有错误,将生成 hello_gdb 可执行文件,此可执行文件将携带 gdb 调试时所需要的调试信息,有了这些调试消息,我们就可以在调试程序的时候查看函数名,变量名,源代码。

好了,开始调试吧:
1)在命令行下,通过如下命令加载刚编译生成的 hello_gdb 程序:
gdb  ./hello_gdb
加载成功后,将得到如下提示信息,并进入 gdb 的命令行模式:
dubuntu@dubuntu:~/Desktop/gnome-gtk-prog/hello_gtk/src$ gdb ./hello_gdb
GNU gdb 6.5.50.20060605-cvs
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

(gdb)

2)好,现在已经成功启动gdb并且加载了可执行程序 hello_gdb,接下来的命令绝大部分都是争对 hello_gdb 程序,下面将列举几个最常用的命令:
小技巧:在gdb命令中,只需要输入命令或参数的前几个字符,再按键盘上的“TAB”键,那gdb将自动补齐命令或参数,如果有多个候选者,那么gdb将把它们都列举出来。

  • list 行号:将显示当前文件以“行号”为中心的前后10行代码,如:list 12
  • list 函数名:将显示“函数名”所在函数的源代码,如:list main
  • list :不带参数,将接着上一次 list 命令的,输出下边的内容。
注意:如果运行list 命令得到类似如下的打印,那是因为在编译程序时没有加入 -g 选项:
(gdb) list
1       ../sysdeps/i386/elf/start.S: No such file or directory.
        in ../sysdeps/i386/elf/start.S

  • break 行号:在当前文件的“行号”处设置断点,如:break  33
  • break 函数名:在用户定义的函数“函数名”处设置断点,如:break cb_button
  • info breakpoints:显示当前程序的断点设置情况
  • disable breakpoints Num:关闭断点“Num”,使其无效,其中“Num”为 info breakpoints 中显示的对应值
  • enable breakpoints Num:打开断点“Num”,使其重新生效
  • print a:将显示整数 a 的值
  • print ++a:将把 a 中的值加1,并显示出来
  • print name:将显示字符串 name 的值
  • print gdb_test(22):将以整数22作为参数调用 gdb_test() 函数
  • print gdb_test(a):将以变量 a 作为参数调用 gdb_test() 函数
  • layout src:显示源代码窗口
  • layout asm:显示反汇编窗口
  • layout regs:显示源代码/反汇编和CPU寄存器窗口
  • layout split:显示源代码和反汇编窗口
  • Ctrl + L:刷新窗口
当然,gdb的功能远不止这些,包括多进程/多线程/信号/远程调试等功能在这里均没有提及,有需要的读者可以参考其它信息,本文的后续章节也将涉及一些内容。

推荐阅读:

3)调试实例:
gdb ./hello_gdb           #启动gdb并载入被调试程序
list gdb_test                #显示函数 gdb_test 的代码
break cb_button         #在函数 cb_button 处设置断点
info breakpoints         #显示所有断点信息
enable breakpoints     #启动所有断点
run                              # 开始运行程序
#注:现在程序正常运行,当单击按钮“退出程序”后,将在函数“cb_button”处停止,因为这里被设置了一个断点
bt                                #显示当前的函数调用堆栈
display j                      #对变量 j 进行监视
next                            #运行下一条指令
print j                         #显示j的值
print gdb_test(j)         #调用函数 gdb_test 并使用 j 作为参数
call  gdb_test(++j)     #同上
next
finish                          #退出函数 cb_button
continue                     #继续运行完程序
quit                             # 退出gdb


下集预告: 下节将简单地介绍如何使用 vbox/hbox 进行窗口布局,这样就能解决当前一个按钮占满整个窗体的问题了。
上一篇:shell和c语言的参数变量传递!(小寿原创)
下一篇:GDB 多线程调试(小寿转载)