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

实例

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

实例地址:

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

GitHubhttps://github.com/yngzMiao/protobuf-parser-tool

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

mkdir build && cd build
cmake ..
make

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

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


前提

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

g++ -g hello.cpp -o hello

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

add_definitions("-Wall -g")

代码内容

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

#include <iostream>
#include "General_buf_read.h"
#include "General_buf_write.h"
#include "Person.pb.h" 

namespace PersonProto {
  class Person;
};

namespace GeneralBuf {
  template <typename T>
  class GeneralProtoWriter;
  typedef GeneralProtoWriter<PersonProto::Person> PersonProtoWriter;
  template <typename T>
  class GeneralProtoReader;
  typedef GeneralProtoReader<PersonProto::Person> PersonProtoReader;
};

int main(int argc, char const *argv[])
{
  GeneralBuf::PersonProtoWriter writer;
  std::string filename = "Person_test.proto";
  int64_t version = 20191001;
  writer.startWriter(filename, version);

  PersonProto::Person *person1 = new PersonProto::Person();
  person1->set_id(100000);
  person1->set_name("zhangsan");
  person1->set_age(20);

  person1->add_email("123456@qq.com");
  person1->add_email("234567@qq.com");
  PersonProto::PhoneNumber *phone1 = person1->add_phone();
  PersonProto::PhoneNumber *phone2 = person1->add_phone();
  phone1->set_number("987654");
  phone1->set_type(PersonProto::PhoneType::MOBILE);
  phone2->set_number("876543");
  phone2->set_type(PersonProto::PhoneType::HOME);

  PersonProto::Address *addr = person1->mutable_address();
  addr->set_country("china");
  addr->set_detail("beijing");

  writer.write(person1);
  writer.stopWriter();

  GeneralBuf::PersonProtoReader reader;
  reader.startReader(filename);

  std::cout << "version : " << reader.getVersion() << "\n";
  std::cout << "cnt : " << reader.getFrameCount() << "\n";
  reader.setFrameIndex(0);
  PersonProto::Person *person = new PersonProto::Person();
  reader.read(person);
  person->PrintDebugString();

  return 0;
}

使用GDB调试

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

yngzmiao@yngzmiao-virtual-machine:~/github/protobuf-parser-tool/c++/build$ gdb example_person  <--------启动GDB
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from example_person...done.
(gdb) l                                                     <---------------list,查看源码
6	namespace PersonProto {
7	  class Person;
8	};
9	
10	namespace GeneralBuf {
11	  template <typename T>
12	  class GeneralProtoWriter;
13	  typedef GeneralProtoWriter<PersonProto::Person> PersonProtoWriter;
14	  template <typename T>
15	  class GeneralProtoReader;
(gdb)                                                       <---------------直接回车,重复上一条命令
16	  typedef GeneralProtoReader<PersonProto::Person> PersonProtoReader;
17	};
18	
19	int main(int argc, char const *argv[])
20	{
21	  GeneralBuf::PersonProtoWriter writer;
22	  std::string filename = "Person_test.proto";
23	  int64_t version = 20191001;
24	  writer.startWriter(filename, version);
25	
(gdb)                                                        <---------------直接回车,重复上一条命令
26	  PersonProto::Person *person1 = new PersonProto::Person();
27	  person1->set_id(100000);
28	  person1->set_name("zhangsan");
29	  person1->set_age(20);
30	
31	  person1->add_email("123456@qq.com");
32	  person1->add_email("234567@qq.com");
33	  PersonProto::PhoneNumber *phone1 = person1->add_phone();
34	  PersonProto::PhoneNumber *phone2 = person1->add_phone();
35	  phone1->set_number("987654");
(gdb)                                                        <---------------直接回车,重复上一条命令
36	  phone1->set_type(PersonProto::PhoneType::MOBILE);
37	  phone2->set_number("876543");
38	  phone2->set_type(PersonProto::PhoneType::HOME);
39	
40	  PersonProto::Address *addr = person1->mutable_address();
41	  addr->set_country("china");
42	  addr->set_detail("beijing");
43	
44	  writer.write(person1);
45	  writer.stopWriter();
(gdb)                                                        <---------------直接回车,重复上一条命令
46	
47	  GeneralBuf::PersonProtoReader reader;
48	  reader.startReader(filename);
49	
50	  std::cout << "version : " << reader.getVersion() << "\n";
51	  std::cout << "cnt : " << reader.getFrameCount() << "\n";
52	  reader.setFrameIndex(0);
53	  PersonProto::Person *person = new PersonProto::Person();
54	  reader.read(person);
55	  person->PrintDebugString();
(gdb)                                                        <---------------直接回车,重复上一条命令
56	
57	  return 0;
58	}
(gdb) b 44                                                   <---------------break,设置断点
Breakpoint 1 at 0x40dd2d: file /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp, line 44.
(gdb) b 55                                                   <---------------break,设置断点
Breakpoint 2 at 0x40de34: file /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp, line 55.
(gdb) i b                                                    <---------------info break,查看断点信息
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040dd2d in main(int, char const**) at /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp:44
2       breakpoint     keep y   0x000000000040de34 in main(int, char const**) at /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp:55
(gdb) r                                                      <---------------run,运行程序,在断点处停止
Starting program: /home/yngzmiao/github/protobuf-parser-tool/c++/build/example_person 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main (argc=1, argv=0x7fffffffdc88) at /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp:44
44	  writer.write(person1);
(gdb) p person1                                              <---------------print,打印
$1 = (PersonProto::Person *) 0x6375d0
(gdb) p *person1                                             <---------------print,打印
$2 = {<google::protobuf::Message> = {<No data fields>}, static kIdFieldNumber = 1, static kNameFieldNumber = 2, static kAgeFieldNumber = 3, static kEmailFieldNumber = 4, static kPhoneFieldNumber = 5, 
  static kAddressFieldNumber = 6, _unknown_fields_ = {fields_ = 0x0}, _has_bits_ = {39}, _cached_size_ = 0, name_ = 0x636fd0, id_ = 100000, age_ = 20, 
  email_ = {<google::protobuf::internal::RepeatedPtrFieldBase> = {static kInitialSize = 0, elements_ = 0x636830, current_size_ = 2, allocated_size_ = 2, total_size_ = 4}, <No data fields>}, 
  phone_ = {<google::protobuf::internal::RepeatedPtrFieldBase> = {static kInitialSize = 0, elements_ = 0x634180, current_size_ = 2, allocated_size_ = 2, total_size_ = 4}, <No data fields>}, 
  address_ = 0x631f60, static default_instance_ = 0x6324c0}
(gdb) p *(PersonProto::Person *) 0x6375d0                    <---------------print,打印
$3 = {<google::protobuf::Message> = {<No data fields>}, static kIdFieldNumber = 1, static kNameFieldNumber = 2, static kAgeFieldNumber = 3, static kEmailFieldNumber = 4, static kPhoneFieldNumber = 5, 
  static kAddressFieldNumber = 6, _unknown_fields_ = {fields_ = 0x0}, _has_bits_ = {39}, _cached_size_ = 0, name_ = 0x636fd0, id_ = 100000, age_ = 20, 
  email_ = {<google::protobuf::internal::RepeatedPtrFieldBase> = {static kInitialSize = 0, elements_ = 0x636830, current_size_ = 2, allocated_size_ = 2, total_size_ = 4}, <No data fields>}, 
  phone_ = {<google::protobuf::internal::RepeatedPtrFieldBase> = {static kInitialSize = 0, elements_ = 0x634180, current_size_ = 2, allocated_size_ = 2, total_size_ = 4}, <No data fields>}, 
  address_ = 0x631f60, static default_instance_ = 0x6324c0}
(gdb) p person1->                                            <---------------tab tab,两次tab,代码补全功能
ByteSize                         SharedCtor                       clear_email                      has_address                      mutable_name                     set_email
Clear                            SharedDtor                       clear_has_address                has_age                          mutable_phone                    set_has_address
CopyFrom                         Swap                             clear_has_age                    has_id                           mutable_unknown_fields           set_has_age
GetCachedSize                    _cached_size_                    clear_has_id                     has_name                         name                             set_has_id
GetMetadata                      _has_bits_                       clear_has_name                   id                               name_                            set_has_name
InitAsDefaultInstance            _unknown_fields_                 clear_id                         id_                              operator=                        set_id
IsInitialized                    add_email                        clear_name                       kAddressFieldNumber              phone                            set_name
MergeFrom                        add_phone                        clear_phone                      kAgeFieldNumber                  phone_                           unknown_fields
MergePartialFromCodedStream      address                          default_instance                 kEmailFieldNumber                phone_size                       ~Person
New                              address_                         default_instance_                kIdFieldNumber                   release_address                  
Person                           age                              descriptor                       kNameFieldNumber                 release_name                     
SerializeWithCachedSizes         age_                             email                            kPhoneFieldNumber                set_age                          
SerializeWithCachedSizesToArray  clear_address                    email_                           mutable_address                  set_allocated_address            
SetCachedSize                    clear_age                        email_size                       mutable_email                    set_allocated_name               
(gdb) p person1->ByteSize()                                  <---------------print,打印
$4 = 88
(gdb) n                                                      <---------------next,单条语句执行
45	  writer.stopWriter();
(gdb) n                                                      <---------------next,单条语句执行
47	  GeneralBuf::PersonProtoReader reader;
(gdb) n                                                      <---------------next,单条语句执行
48	  reader.startReader(filename);
(gdb) c                                                      <---------------continue,继续运行程序,下一个断点处停止
Continuing.
version : 20191001
cnt : 1

Breakpoint 2, main (argc=1, argv=0x7fffffffdc88) at /home/yngzmiao/github/protobuf-parser-tool/c++/example_person.cpp:55
55	  person->PrintDebugString();
(gdb) c                                                      <---------------continue,继续运行程序,下一个断点处停止
Continuing.
id: 100000
name: "zhangsan"
age: 20
email: "123456@qq.com"
email: "234567@qq.com"
phone {
  number: "987654"
  type: MOBILE
}
phone {
  number: "876543"
  type: HOME
}
address {
  country: "china"
  detail: "beijing"
}
[Inferior 1 (process 3734) exited normally]
(gdb) q                                                      <---------------quit,退出GDB
yngzmiao@yngzmiao-virtual-machine:~/github/protobuf-parser-tool/c++/build$ 

最基本的GDB命令

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

命令全称解释
llist查看源码
bbreak设置断点
rrun运行程序,在断点处停止
nnext单条语句执行
ccontinue继续运行程序,下一个断点处停止
pprint打印
qquit退出GDB

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

©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:上身试试 返回首页