22_GDB调试记录(未完成)

GDB调试

该文件调试的程序文件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文件,查看程序崩溃的堆栈信息

复制代码
相关推荐
222you2 小时前
SpringAOP的介绍和入门
java·开发语言·spring
程序员zgh2 小时前
代码重构 —— 读后感
运维·c语言·开发语言·c++·重构
liulilittle2 小时前
moodycamel::ConcurrentQueue 清空队列的方法论
开发语言·c++
Violet_YSWY2 小时前
哪些常量用枚举,哪些用类
java
shoubepatien2 小时前
JAVA -- 09
java·开发语言
kong79069282 小时前
Java新特性-(三)程序流程控制
java·java新特性
愿你天黑有灯下雨有伞2 小时前
Spring Boot 使用FastExcel实现多级表头动态数据填充导出
java·faseexcel
阿拉斯攀登2 小时前
自定义 Spring Boot 自动配置
java·spring boot
CodeAmaz2 小时前
Spring编程式事务详解
java·数据库·spring