【GDB】手把手教你用gdb调试程序(超清晰流程)

水深无声 2023-06-06 12:02 103阅读 0赞

实例

我以自己曾经写的一段实际代码为例,来讲解究竟该怎么进行GDB调试。

实例地址:

码云:https://gitee.com/yngzMiao/protobuf-parser-tool

GitHub:https://github.com/yngzMiao/protobuf-parser-tool

实例的功能是生成和解析proto文件,分为C++python版本。其中,C++版本采用的是CMakeLists.txt进行编译,按照以下命令可以生成可执行文件:

  1. mkdir build && cd build
  2. cmake ..
  3. make

最终可执行文件example_person是在build文件夹下。

如果不太了解CMakeLists.txt的,可以阅读博文:【CMake】CMakeLists.txt的超傻瓜手把手教程(附实例源码)

前提

一般情况下,GDB主要调试的是C/C++的程序。要调试C/C++的程序,首先在编译时,必须要把符号表信息加到可执行文件中。使用编译器的-g参数可以做到这一点。比如:

  1. g++ -g hello.cpp -o hello

当然,本实例中,在最顶层的CMakeLists.txt添加以下语句:

  1. add_definitions("-Wall -g")

代码内容

接下来看一下主代码的内容,即example_person.cpp

  1. #include <iostream>
  2. #include "General_buf_read.h"
  3. #include "General_buf_write.h"
  4. #include "Person.pb.h"
  5. namespace PersonProto {
  6. class Person;
  7. };
  8. namespace GeneralBuf {
  9. template <typename T>
  10. class GeneralProtoWriter;
  11. typedef GeneralProtoWriter<PersonProto::Person> PersonProtoWriter;
  12. template <typename T>
  13. class GeneralProtoReader;
  14. typedef GeneralProtoReader<PersonProto::Person> PersonProtoReader;
  15. };
  16. int main(int argc, char const *argv[])
  17. {
  18. GeneralBuf::PersonProtoWriter writer;
  19. std::string filename = "Person_test.proto";
  20. int64_t version = 20191001;
  21. writer.startWriter(filename, version);
  22. PersonProto::Person *person1 = new PersonProto::Person();
  23. person1->set_id(100000);
  24. person1->set_name("zhangsan");
  25. person1->set_age(20);
  26. person1->add_email("123456@qq.com");
  27. person1->add_email("234567@qq.com");
  28. PersonProto::PhoneNumber *phone1 = person1->add_phone();
  29. PersonProto::PhoneNumber *phone2 = person1->add_phone();
  30. phone1->set_number("987654");
  31. phone1->set_type(PersonProto::PhoneType::MOBILE);
  32. phone2->set_number("876543");
  33. phone2->set_type(PersonProto::PhoneType::HOME);
  34. PersonProto::Address *addr = person1->mutable_address();
  35. addr->set_country("china");
  36. addr->set_detail("beijing");
  37. writer.write(person1);
  38. writer.stopWriter();
  39. GeneralBuf::PersonProtoReader reader;
  40. reader.startReader(filename);
  41. std::cout << "version : " << reader.getVersion() << "\n";
  42. std::cout << "cnt : " << reader.getFrameCount() << "\n";
  43. reader.setFrameIndex(0);
  44. PersonProto::Person *person = new PersonProto::Person();
  45. reader.read(person);
  46. person->PrintDebugString();
  47. return 0;
  48. }

使用GDB调试

下面使用GDB来对生成的可执行文件进行调试,调试命令如下,解释在后面进行了标注:

  1. yngzmiao@yngzmiao-virtual-machine:~/github/protobuf-parser-tool/c++/build$ gdb example_person <--------启动GDB
  2. GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
  3. Copyright (C) 2016 Free Software Foundation, Inc.
  4. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
  5. This is free software: you are free to change and redistribute it.
  6. There is NO WARRANTY, to the extent permitted by law. Type "show copying"
  7. and "show warranty" for details.
  8. This GDB was configured as "x86_64-linux-gnu".
  9. Type "show configuration" for configuration details.
  10. For bug reporting instructions, please see:
  11. <http://www.gnu.org/software/gdb/bugs/>.
  12. Find the GDB manual and other documentation resources online at:
  13. <http://www.gnu.org/software/gdb/documentation/>.
  14. For help, type "help".
  15. Type "apropos word" to search for commands related to "word"...
  16. Reading symbols from example_person...done.
  17. (gdb) l <---------------list,查看源码
  18. 6 namespace PersonProto {
  19. 7 class Person;
  20. 8 };
  21. 9
  22. 10 namespace GeneralBuf {
  23. 11 template <typename T>
  24. 12 class GeneralProtoWriter;
  25. 13 typedef GeneralProtoWriter<PersonProto::Person> PersonProtoWriter;
  26. 14 template <typename T>
  27. 15 class GeneralProtoReader;
  28. (gdb) <---------------直接回车,重复上一条命令
  29. 16 typedef GeneralProtoReader<PersonProto::Person> PersonProtoReader;
  30. 17 };
  31. 18
  32. 19 int main(int argc, char const *argv[])
  33. 20 {
  34. 21 GeneralBuf::PersonProtoWriter writer;
  35. 22 std::string filename = "Person_test.proto";
  36. 23 int64_t version = 20191001;
  37. 24 writer.startWriter(filename, version);
  38. 25
  39. (gdb) <---------------直接回车,重复上一条命令
  40. 26 PersonProto::Person *person1 = new PersonProto::Person();
  41. 27 person1->set_id(100000);
  42. 28 person1->set_name("zhangsan");
  43. 29 person1->set_age(20);
  44. 30
  45. 31 person1->add_email("123456@qq.com");
  46. 32 person1->add_email("234567@qq.com");
  47. 33 PersonProto::PhoneNumber *phone1 = person1->add_phone();
  48. 34 PersonProto::PhoneNumber *phone2 = person1->add_phone();
  49. 35 phone1->set_number("987654");
  50. (gdb) <---------------直接回车,重复上一条命令
  51. 36 phone1->set_type(PersonProto::PhoneType::MOBILE);
  52. 37 phone2->set_number("876543");
  53. 38 phone2->set_type(PersonProto::PhoneType::HOME);
  54. 39
  55. 40 PersonProto::Address *addr = person1->mutable_address();
  56. 41 addr->set_country("china");
  57. 42 addr->set_detail("beijing");
  58. 43
  59. 44 writer.write(person1);
  60. 45 writer.stopWriter();
  61. (gdb) <---------------直接回车,重复上一条命令
  62. 46
  63. 47 GeneralBuf::PersonProtoReader reader;
  64. 48 reader.startReader(filename);
  65. 49
  66. 50 std::cout << "version : " << reader.getVersion() << "\n";
  67. 51 std::cout << "cnt : " << reader.getFrameCount() << "\n";
  68. 52 reader.setFrameIndex(0);
  69. 53 PersonProto::Person *person = new PersonProto::Person();
  70. 54 reader.read(person);
  71. 55 person->PrintDebugString();
  72. (gdb) <---------------直接回车,重复上一条命令
  73. 56
  74. 57 return 0;
  75. 58 }
  76. (gdb) b 44 <---------------break,设置断点
  77. Breakpoint 1 at 0x40dd2d: file /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp, line 44.
  78. (gdb) b 55 <---------------break,设置断点
  79. Breakpoint 2 at 0x40de34: file /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp, line 55.
  80. (gdb) i b <---------------info break,查看断点信息
  81. Num Type Disp Enb Address What
  82. 1 breakpoint keep y 0x000000000040dd2d in main(int, char const**) at /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp:44
  83. 2 breakpoint keep y 0x000000000040de34 in main(int, char const**) at /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp:55
  84. (gdb) r <---------------run,运行程序,在断点处停止
  85. Starting program: /home/yngzmiao/github/protobuf-parser-tool/c++/build/example_person
  86. [Thread debugging using libthread_db enabled]
  87. Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
  88. Breakpoint 1, main (argc=1, argv=0x7fffffffdc88) at /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp:44
  89. 44 writer.write(person1);
  90. (gdb) p person1 <---------------print,打印
  91. $1 = (PersonProto::Person *) 0x6375d0
  92. (gdb) p *person1 <---------------print,打印
  93. $2 = {<google::protobuf::Message> = {<No data fields>}, static kIdFieldNumber = 1, static kNameFieldNumber = 2, static kAgeFieldNumber = 3, static kEmailFieldNumber = 4, static kPhoneFieldNumber = 5,
  94. static kAddressFieldNumber = 6, _unknown_fields_ = {fields_ = 0x0}, _has_bits_ = {39}, _cached_size_ = 0, name_ = 0x636fd0, id_ = 100000, age_ = 20,
  95. email_ = {<google::protobuf::internal::RepeatedPtrFieldBase> = {static kInitialSize = 0, elements_ = 0x636830, current_size_ = 2, allocated_size_ = 2, total_size_ = 4}, <No data fields>},
  96. phone_ = {<google::protobuf::internal::RepeatedPtrFieldBase> = {static kInitialSize = 0, elements_ = 0x634180, current_size_ = 2, allocated_size_ = 2, total_size_ = 4}, <No data fields>},
  97. address_ = 0x631f60, static default_instance_ = 0x6324c0}
  98. (gdb) p *(PersonProto::Person *) 0x6375d0 <---------------print,打印
  99. $3 = {<google::protobuf::Message> = {<No data fields>}, static kIdFieldNumber = 1, static kNameFieldNumber = 2, static kAgeFieldNumber = 3, static kEmailFieldNumber = 4, static kPhoneFieldNumber = 5,
  100. static kAddressFieldNumber = 6, _unknown_fields_ = {fields_ = 0x0}, _has_bits_ = {39}, _cached_size_ = 0, name_ = 0x636fd0, id_ = 100000, age_ = 20,
  101. email_ = {<google::protobuf::internal::RepeatedPtrFieldBase> = {static kInitialSize = 0, elements_ = 0x636830, current_size_ = 2, allocated_size_ = 2, total_size_ = 4}, <No data fields>},
  102. phone_ = {<google::protobuf::internal::RepeatedPtrFieldBase> = {static kInitialSize = 0, elements_ = 0x634180, current_size_ = 2, allocated_size_ = 2, total_size_ = 4}, <No data fields>},
  103. address_ = 0x631f60, static default_instance_ = 0x6324c0}
  104. (gdb) p person1-> <---------------tab tab,两次tab,代码补全功能
  105. ByteSize SharedCtor clear_email has_address mutable_name set_email
  106. Clear SharedDtor clear_has_address has_age mutable_phone set_has_address
  107. CopyFrom Swap clear_has_age has_id mutable_unknown_fields set_has_age
  108. GetCachedSize _cached_size_ clear_has_id has_name name set_has_id
  109. GetMetadata _has_bits_ clear_has_name id name_ set_has_name
  110. InitAsDefaultInstance _unknown_fields_ clear_id id_ operator= set_id
  111. IsInitialized add_email clear_name kAddressFieldNumber phone set_name
  112. MergeFrom add_phone clear_phone kAgeFieldNumber phone_ unknown_fields
  113. MergePartialFromCodedStream address default_instance kEmailFieldNumber phone_size ~Person
  114. New address_ default_instance_ kIdFieldNumber release_address
  115. Person age descriptor kNameFieldNumber release_name
  116. SerializeWithCachedSizes age_ email kPhoneFieldNumber set_age
  117. SerializeWithCachedSizesToArray clear_address email_ mutable_address set_allocated_address
  118. SetCachedSize clear_age email_size mutable_email set_allocated_name
  119. (gdb) p person1->ByteSize() <---------------print,打印
  120. $4 = 88
  121. (gdb) n <---------------next,单条语句执行
  122. 45 writer.stopWriter();
  123. (gdb) n <---------------next,单条语句执行
  124. 47 GeneralBuf::PersonProtoReader reader;
  125. (gdb) n <---------------next,单条语句执行
  126. 48 reader.startReader(filename);
  127. (gdb) c <---------------continue,继续运行程序,下一个断点处停止
  128. Continuing.
  129. version : 20191001
  130. cnt : 1
  131. Breakpoint 2, main (argc=1, argv=0x7fffffffdc88) at /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp:55
  132. 55 person->PrintDebugString();
  133. (gdb) c <---------------continue,继续运行程序,下一个断点处停止
  134. Continuing.
  135. id: 100000
  136. name: "zhangsan"
  137. age: 20
  138. email: "123456@qq.com"
  139. email: "234567@qq.com"
  140. phone {
  141. number: "987654"
  142. type: MOBILE
  143. }
  144. phone {
  145. number: "876543"
  146. type: HOME
  147. }
  148. address {
  149. country: "china"
  150. detail: "beijing"
  151. }
  152. [Inferior 1 (process 3734) exited normally]
  153. (gdb) q <---------------quit,退出GDB
  154. yngzmiao@yngzmiao-virtual-machine:~/github/protobuf-parser-tool/c++/build$

最基本的GDB命令

上文使用的是最基本的GDB命令,下面进行总结:














































命令 全称 解释
l list 查看源码
b break 设置断点
r run 运行程序,在断点处停止
n next 单条语句执行
c continue 继续运行程序,下一个断点处停止
p print 打印
q quit 退出GDB

其他的GDB命令,会在后面的博文中进行介绍。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly95bmd6bWlhby5ibG9nLmNzZG4ubmV0_size_16_color_FFFFFF_t_70

发表评论

表情:
评论列表 (有 0 条评论,103人围观)

还没有评论,来说两句吧...

相关阅读

    相关 GDB调试程序(四)

    查看栈信息 ————— 当程序被停住了,你需要做的第一件事就是查看程序是在哪里停住的。当你的程序调用了一个函数,函数的地址,函数参数,函数内的局部变量都会被压入“栈”(S

    相关 GDB调试程序(三)

    四、维护停止点 上面说了如何设置程序的停止点,GDB中的停止点也就是上述的三类。在GDB中,如果你觉得已定义好的停止点没有用了,你可以使用delete、clear、disab