GDB调试
- 该文件调试的程序文件main.cpp,messagequeue.h,messagequeue.cpp
- 1.编译
- 2.GDB命令使用
-
- [2.1 gdb 程序名 --开始调试程序](#2.1 gdb 程序名 --开始调试程序)
- [2.2 list --打印源码文件](#2.2 list --打印源码文件)
- [2.3 break(b) --打断点](#2.3 break(b) --打断点)
- [2.4 bt --打印堆栈信息](#2.4 bt --打印堆栈信息)
- [2.5 info b --打印断点信息](#2.5 info b --打印断点信息)
- [2.6 next(n) --执行代码的下一行](#2.6 next(n) --执行代码的下一行)
- [2.7 print 变量名 --显示变量的当前信息](#2.7 print 变量名 --显示变量的当前信息)
- [2.8 continue (c) 继续执行,直到运行到下一个断点](#2.8 continue (c) 继续执行,直到运行到下一个断点)
- [2.9 delete(d) --删除断点](#2.9 delete(d) --删除断点)
- [2.10 打断点的其他用法](#2.10 打断点的其他用法)
-
-
-
-
- 按行号给其他文件加断点
- 按函数名给其他文件加断点
- [处理 "多文件同名函数"](#处理 “多文件同名函数”)
- [文件名带路径(避免重名 / 找不到文件)](#文件名带路径(避免重名 / 找不到文件))
- 先查看其他文件的代码,再加断点
- 给其他文件的断点绑定线程(多线程场景)
-
-
-
- [2.11 gdb -p 进程号 --程序运行中,gdb链接程序调试](#2.11 gdb -p 进程号 --程序运行中,gdb链接程序调试)
- 3.core文件的使用
- 4.程序运行出现异常的分析方法
该文件调试的程序文件main.cpp,messagequeue.h,messagequeue.cpp
main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <mosquitto.h>
#include "messagequeue.h"
#define BROKER_ADDRESS "127.0.0.1"
#define PORT 1883
#define SUBSCRIBE_TOPIC "test/topic/in"
#define PUBLISH_TOPIC "test/topic/out"
MessageQueue receive_queue;
MessageQueue send_queue;
int pub_times = 0;
void on_connect(struct mosquitto *mosq, void *userdata, int rc) {
printf("Connected with result code %d\n", rc);
mosquitto_subscribe(mosq, NULL, SUBSCRIBE_TOPIC, 0);
}
void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *msg) {
printf("Message received [%s]: %s\n", msg->topic, (char *)msg->payload);
enqueue(&receive_queue, (char *)msg->payload);
}
mosquitto* self_mosquitto_init()
{
int rc;
// Initialize the Mosquitto library
mosquitto_lib_init();
// Create a new Mosquitto client instance
struct mosquitto *mosq = mosquitto_new(NULL, true, NULL);
if (!mosq) {
fprintf(stderr, "Error: Out of memory.\n");
return NULL;
}
// Set callback functions
mosquitto_connect_callback_set(mosq, on_connect);
mosquitto_message_callback_set(mosq, on_message);
// Connect to the broker
if ((rc = mosquitto_connect(mosq, BROKER_ADDRESS, PORT, 60))) {
fprintf(stderr, "Unable to connect (%d).\n", rc);
return NULL;
}
// Start the network loop in a blocking call
mosquitto_loop_start(mosq);
return mosq;
}
void destroy_mosquitto(mosquitto* mosq)
{
mosquitto_disconnect(mosq);
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
}
void* process_received_messages(void *arg) {
while (1) {
char *message = dequeue(&receive_queue);
printf("Processed message: %s\n", message);
enqueue(&send_queue, message);
free(message);
}
return NULL;
}
void* publish_messages(void *arg) {
struct mosquitto *mosq = (mosquitto*)arg;
while (1){
char *message = dequeue(&send_queue);
printf("Publishing message: %s\n", message);
mosquitto_publish(mosq, NULL, PUBLISH_TOPIC, strlen(message), message, 0, false);
free(message);
pub_times++;
printf("printf message times %d\n", pub_times);
}
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t processor_thread;//处理接收到的mqtt消息
pthread_t publisher_thread;//处理等待发送的mqtt消息
struct mosquitto *mosq = NULL;
// Initialize message queues
init_message_queue(&receive_queue);
init_message_queue(&send_queue);
//mosquitto开启
mosq = self_mosquitto_init();
// Start a separate thread for processing received messages
if (pthread_create(&processor_thread, NULL, process_received_messages, NULL)) {
fprintf(stderr, "Failed to create thread\n");
return 1;
}
// Start a separate thread for publishing messages
if (pthread_create(&publisher_thread, NULL, publish_messages, mosq)) {
fprintf(stderr, "Failed to create thread\n");
return 1;
}
while(1)
{
enqueue(&send_queue, "hello world!!");
sleep(5);
}
// Clean up
destroy_mosquitto(mosq);
destroy_message_queue(&receive_queue);
destroy_message_queue(&send_queue);
return 0;
}
messagequeue.h
#ifndef MESSAGEQUEUE_H
#define MESSAGEQUEUE_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
typedef struct QueueNode{
char *message;
struct QueueNode *next;
}QueueNode;
typedef struct {
QueueNode *head;
QueueNode *tail;
pthread_mutex_t lock;
pthread_cond_t cond;
}MessageQueue;
void init_message_queue(MessageQueue *queue);
void enqueue(MessageQueue *queue, const char *message);
char* dequeue(MessageQueue *queue);
void destroy_message_queue(MessageQueue *queue);
#endif // MESSAGEQUEUE_H
messagequeue.cpp
#include "messagequeue.h"
void init_message_queue(MessageQueue *queue) {
queue->head = NULL;
queue->tail = NULL;
pthread_mutex_init(&queue->lock, NULL);
pthread_cond_init(&queue->cond, NULL);
}
void enqueue(MessageQueue *queue, const char *message) {
QueueNode *new_node = (QueueNode*)malloc(sizeof(QueueNode));
new_node->message = strdup(message);
new_node->next = NULL;
pthread_mutex_lock(&queue->lock);
if (queue->tail == NULL) {
queue->head = new_node;
queue->tail = new_node;
} else {
queue->tail->next = new_node;
queue->tail = new_node;
}
pthread_cond_signal(&queue->cond);
pthread_mutex_unlock(&queue->lock);
}
char* dequeue(MessageQueue *queue) {
pthread_mutex_lock(&queue->lock);
while (queue->head == NULL) {
pthread_cond_wait(&queue->cond, &queue->lock);
}
QueueNode *node = queue->head;
queue->head = node->next;
if (queue->head == NULL) {
queue->tail = NULL;
}
pthread_mutex_unlock(&queue->lock);
char *message = node->message;
free(node);
return message;
}
void destroy_message_queue(MessageQueue *queue) {
while (queue->head != NULL) {
QueueNode *temp = queue->head;
queue->head = temp->next;
free(temp->message);
free(temp);
}
pthread_mutex_destroy(&queue->lock);
pthread_cond_destroy(&queue->cond);
}
1.编译
GCC 常用编译参数功能说明
基础编译选项
-o <file>
指定生成的可执行文件或输出文件的名称。默认情况下生成 a.out(Linux)或 a.exe(Windows)。
-c
仅编译源文件生成目标文件(.o 或 .obj),不进行链接操作。
-E
只运行预处理器,将宏展开后的代码输出到标准输出,通常配合 -o 指定输出文件。
-S
生成汇编代码(.s 文件),不进行汇编和链接。
优化级别选项
-O0
关闭所有优化,编译速度最快,适合调试。
-O1
基础优化,在保证编译速度的同时减少代码体积和提高执行效率。
-O2
更高级优化,包括指令调度和循环优化,推荐生产环境使用。
-O3
激进优化,可能增加代码体积,适用于对性能要求极高的场景。
-Os
优化代码体积,优先减少生成的可执行文件大小。
-Ofast
启用所有 -O3 优化,并放宽严格的标准合规性(可能影响浮点精度)。
调试与诊断
-g
生成调试信息(如 gdb 需要的符号表),支持 -g1(最小)、-g3(最大宏信息)。
-Wall
启用大部分常见警告(未使用变量、未初始化的变量等)。
-Wextra
启用额外警告(如空语句、类型转换等)。
-Werror
将所有警告视为错误,编译失败。
-Wpedantic
严格遵循 ANSI/C 标准,拒绝非标准语法。
-v
显示详细的编译过程信息(如调用的子命令和版本)。
链接控制
-static
强制静态链接,将所有库打包到可执行文件中。
-shared
生成动态库(.so 或 .dll),通常与 -fPIC 配合使用。
-l<library>
链接指定的库(如 -lm 链接数学库)。
-L<path>
添加库搜索路径,优先在指定目录查找库文件。
-I<path>
添加头文件搜索路径,用于查找 #include 文件。
语言标准选项
-std=c11 / -std=c++17
指定使用的 C 或 C++ 语言标准版本(如 C11、C++17)。
-ansi
等价于 -std=c90,严格遵循 ANSI C 标准。
-fPIC
生成位置无关代码(Position Independent Code),用于动态库编译。
其他实用选项
-D<macro>
定义宏(如 -DDEBUG 等价于 #define DEBUG)。
-U<macro>
取消宏定义。
-MM / -MMD
生成依赖关系(.d 文件),用于 makefile 自动化构建。
-march=native
针对当前 CPU 架构生成优化代码(如启用 AVX 指令集)。
-pipe
使用管道替代临时文件,加速编译过程(但消耗更多内存)。
示例

//使用g++编译器 将两个文件编译成message可执行文件,保存可调式信息,打印编译信息,链接库mosquitto和pthread
g++ main.cpp messagequeue.cpp -o message -g -v -lmosquitto -lpthread
//ldd打印程序运行时需要的动态库
ldd message




2.GDB命令使用
2.1 gdb 程序名 --开始调试程序
gdb message

2.2 list --打印源码文件
list 函数名 //打印函数名周围的代码
list 行数1,行数2 //打印源码中指定行数之间的内容
list main //打印main函数周围的源代码,点击回车,会继续打印后续的代码

list 104,114 //打印104-114行的代码信息

2.3 break(b) --打断点
b 函数名 //当函数被调用位置过多时,建议不要使用该方法,会在所有调用位置都打上断点
b 行号 //推荐此方法,不过需要配合list命令,确定断点的位置(行号)
在程序中首先使用list,打印处理接收信息的函数源码,然后在其中找到指定的行号,使用break打上断点


发送mqtt消息,程序在68行停止

2.4 bt --打印堆栈信息

2.5 info b --打印断点信息

2.6 next(n) --执行代码的下一行
打印信息,运行到下一行

2.7 print 变量名 --显示变量的当前信息


2.8 continue © 继续执行,直到运行到下一个断点

再次触发

2.9 delete(d) --删除断点
info b
d 断点号
需要配合info b打印断点信息,然后使用 d 断点号,将断点删除

2.10 打断点的其他用法
按行号给其他文件加断点
# 给 messagequeue.cpp 第 50 行加断点(队列入队逻辑)
(gdb) b messagequeue.cpp:50
# 给 mqtt_client.cpp 第 88 行加断点(MQTT 连接逻辑)
(gdb) b mqtt_client.cpp:88
# 验证断点:查看所有断点,确认位置正确
(gdb) info break
# 典型输出:
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555562a0 in MessageQueue::push() at messagequeue.cpp:50
2 breakpoint keep y 0x00005555555578f0 in mqtt_connect() at mqtt_client.cpp:88
按函数名给其他文件加断点
# 给 messagequeue.cpp 中的 pop() 成员函数加断点
(gdb) b messagequeue.cpp:MessageQueue::pop
# 给 mqtt_client.cpp 中的 mqtt_subscribe 普通函数加断点
(gdb) b mqtt_client.cpp:mqtt_subscribe
处理 "多文件同名函数"
# 仅给 mqtt_client.cpp 中的 process() 加断点(精准)
(gdb) b mqtt_client.cpp:process
# 若不指定文件名,GDB 会提示并给所有同名函数加断点(慎用)
(gdb) b process
# 提示:Multiple match found. Use "break process <PC>" to specify one.
# 列出所有匹配的函数,选择对应编号即可
文件名带路径(避免重名 / 找不到文件)
# 相对路径:给 src/messagequeue.cpp 第 50 行加断点
(gdb) b src/messagequeue.cpp:50
# 绝对路径(适合文件路径复杂时)
(gdb) b /home/yourname/project/src/mqtt/mqtt_client.cpp:88
先查看其他文件的代码,再加断点
# 查看 messagequeue.cpp 第 45-55 行的代码
(gdb) list messagequeue.cpp:45,55
# 确认代码后,给第 50 行加断点
(gdb) b messagequeue.cpp:50
给其他文件的断点绑定线程(多线程场景)
# 给 messagequeue.cpp 第 50 行加断点,仅 3 号线程触发
(gdb) b messagequeue.cpp:50 thread 3
2.11 gdb -p 进程号 --程序运行中,gdb链接程序调试
首先,运行message,然后使用ps打印运行的进程信息,根据进程号进行调试
ps -ef | grep message

gdb -p 13950

调试操作
# 1. 查看进程当前的调用栈(定位卡住的位置)
(gdb) bt
# 2. 给其他文件加断点(比如 messagequeue.cpp 第50行)
(gdb) b messagequeue.cpp:50
# 3. 让进程继续运行(触发断点)
(gdb) c
# 4. 打印变量/线程信息
(gdb) info threads
(gdb) print mess_times
调试完成后处理
若想让进程继续运行(解除调试):执行 detach
(gdb) detach # 分离进程,进程恢复正常运行
(gdb) quit # 退出GDB
若想终止进程:执行 kill
(gdb) kill # 终止附加的进程
(gdb) quit
3.core文件的使用
4.程序运行出现异常的分析方法
4.1程序还在运行,但是进入死循环
gdb链接进去,打印堆栈进行分析,查看程序死循环的位置,进而查看代码进行分析
gdb -p 进程号
bt
4.2程序已经崩溃
4.3崩溃现象容易复现
使用b exit,在程序崩溃的地方打断点,该方法能够在程序退出的时候打印崩溃的堆栈信息
gdb 可执行文件 -ex "b exit" -ex "run"
4.4崩溃现象不容易复现
查看core文件,查看程序崩溃的堆栈信息