内存泄露、死锁检测工具来了

  博主介绍:程序喵大人


项目背景

现实困境

做C、C++开发的朋友应该都知道,C、C++中的内存是手动管理的,手动内存管理是一把双刃剑,虽然提供了极致性能,但可能由于开发者的一点点疏忽,就导致内存泄露。据非官方统计,全球每年因内存泄露导致的系统崩溃事故超过120万次。

C、C++开发者面临以下痛点时经常束手无策:

  • 幽灵式内存泄露:程序运行数天后,出现内存耗尽,因为程序是一点点释放的,不太容易发现具体问题所在。

  • 多线程竞态问题:死锁导致的服务假死,并且不好复现。

现有方案的局限

传统工具,Asanvalgrindgdb功能非常强大,可以检测基本的问题,但也恰恰是因为功能太过丰富且强大,所以性能损耗非常高,无法用于线上环境,并且难以捕获随机出现的死锁场景。

项目目标

开发一个零侵入、高性能、全维度的运行时诊断系统:

  • 内存监控:可以实时追踪每个内存块的完整生命周期。

  • 死锁检测:可以检测出死锁,并能检测出哪个线程的哪几把锁出现了死锁,哪个线程由于等待的哪把锁而出现的死锁,可以精确关联源代码位置。

  • 内存泄露检测:可以检测出具体哪块内存出现了泄露,并精确关联到源代码位置。

项目介绍

整体架构如图:

图片

内存检测

直接看代码,下面代码会发生内存泄露:

extern "C"int TestMemoryLeak()
{
    int *ptr = (int *)malloc(100);
    printf("TestMemoryLeak: %p\n", ptr);
    free(ptr);
    return 0;
}

extern"C"int TestMemoryLeak2()
{
    int *ptr = (int *)malloc(110);
    printf("TestMemoryLeak2: %p\n", ptr);
    int *p = newint[10];

    auto q = std::make_unique<int>(10);
    return 0;
}

集成了工具后:

int main()
{
    OpenDynamicExample();
    MemoryDetector detect("/mnt/d/project/camping/detector/libdynamic_example.so");

    detect.StartTracking();

    UseDynamicExample();

    detect.StopTracking(); // 会打印 lib1.so 的内存使用情况
    CloseDynamicExample();

    return 0;
}

直接就可以检测这个动态库的内存情况:

图片

本工具可以检测出程序申请了多少内存,申请了多少块内存,以及具体哪里发生了内存泄露,可以精确到具体的源代码位置。

它不仅可以检测mallocfree申请和释放的内存,即便是C++的newdeletenew[]delete[]std::make_uniquestd::make_shared,也可以,不管程序是通过哪种方式申请和释放的内存,只要发生了内存泄露,工具都可以检测到。

整体采用Hook方案,基本流程如图:

图片

死锁检测

看这段发生死锁的代码:

static void *ThreadFunc1(void *)
{
    pthread_mutex_lock(&mutexA);
    std::cout << "Thread 1: Locked A\n";
    sleep(1);

    std::cout << "Thread 1: Trying to lock B\n";
    pthread_mutex_lock(&mutexB);
    std::cout << "Thread 1: Locked B\n";

    pthread_mutex_unlock(&mutexB);
    pthread_mutex_unlock(&mutexA);
    return nullptr;
}

static void *ThreadFunc2(void *)
{
    pthread_mutex_lock(&mutexB);
    std::cout << "Thread 2: Locked B\n";
    sleep(1);

    std::cout << "Thread 2: Trying to lock A\n";
    pthread_mutex_lock(&mutexA);
    std::cout << "Thread 2: Locked A\n";

    pthread_mutex_unlock(&mutexA);
    pthread_mutex_unlock(&mutexB);
    return nullptr;
}

static void *ThreadFunc3(void *)
{
    std::mutex mtx;
    std::cout << "Thread 3: Trying to lock mutex\n";
    mtx.lock();
    std::cout << "Thread 3: Locked mutex\n";
    sleep(1);
    mtx.unlock();
    return nullptr;
}

// 导出的函数,用于创建死锁场景
static void CreateDeadlock()
{
    pthread_t t1, t2, t3;
    pthread_attr_t attr;

    // 初始化线程属性
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    // 创建分离的线程
    pthread_create(&t1, &attr, ThreadFunc1, nullptr);
    pthread_create(&t2, &attr, ThreadFunc2, nullptr);
    pthread_create(&t3, &attr, ThreadFunc3, nullptr);

    // 销毁线程属性
    pthread_attr_destroy(&attr);

    // 等待一段时间让死锁发生
    sleep(3);
}

从代码中可以看到,Thread1Thread2会发生死锁,集成工具后:

LockHook lock_hook("./libdynamic_example.so");
if (!lock_hook.StartTracking())
{
    std::cerr << "Failed to start lock tracking\n";
    dlclose(handle);
    return 1;
}
lock_hook.StopTracking();

结果如图:

工具可以检测出哪里发生了死锁、哪个线程持有了哪把锁、以及哪把锁被哪个线程持有了。

且无论你是通过pthread_lock、还是mutex.lock、还是unique_lock或者lock_guard,只要发生了死锁,工具都可以检测到,并且可以定位到源代码位置。

整体也采用Hook方案,流程如图所示:

图片

项目收获

项目代码量不大,核心代码大概2000行左右,但涉及到的技术内容非常丰富且硬核。

通过本项目,你可以收获到:

  1. 提升C、C++的编码能力、内存管理黑科技、多线程调试技巧

  2. ELF 文件结构,包括section 和 segment的概念以及具体作用等。

  3. 编译链接技术,动态链接与静态链接的区别。

  4. 动态链接与加载,了解动态链接器如何在运行时解析符号和加载动态库。

  5. PLT机制,与GOT之间的关系。

  6. GOT作用,如何存储动态链接的函数地址。

  7. 函数调用约定,不同架构下的函数调用约定。

  8. 内存保护机制,了解Linux上的内存保护机制(如DEP、ASLR),以及如何影响代码注入和钩子技术。

  9. 调试工具,使用工具(如objdump、gdb)分析二进制文件,理解如何定位和修改PLT。

  10. Hook技术,如何将自定义代码注入到目标进程中,以实现钩子功能。

  11. 钩子的安全性,钩子技术是否有风险。

  12. 编写和测试,学习如何编写钩子代码,并在不同环境中进行测试。

  13. 钩子技术的性能分析。

  14. 动态库的加载过程,详细了解共享库的加载过程,包括如何在运行时解析依赖关系。

  15. 符号解析与重定位,符号解析的机制以及重定位表的作用。

  16. 内存管理和分配机制,内存管理机制,特别是如何安全地分配和修改内存以实现钩子。

  17. 内存泄漏检测技术,理解了内存管理分配机制,可以实现检测内存泄漏的能力。

  18. 锁机制,锁的底层实现原理,如何实现加解锁相关的钩子。

  19. 死锁检测技术,理解了加解锁的底层机制,可以实现检测程序是否产生了死锁。

  20. 编译链接技术,Debug模式和Release模式的区别。

  21. 符号管理机制,调试符号信息的作用。

  22. 调用栈技术,如何获取线程的调用堆栈,如何根据地址解析出对应代码函数名和行号。

本项目为C++训练营专属,感兴趣可以看文末,备注训练营

  码字不易,欢迎大家点赞关注评论,谢谢!


C++训练营

专为校招、社招3年工作经验的同学打造的1V1 C++训练营,量身定制学习计划、每日代码review,简历优化,面试辅导,已帮助多名学员获得大厂offer!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序喵大人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值