xcode调试可执行程序

文章目录

  • 应用场景
  • 配置及说明

应用场景

  1. 插件开发,我们开发的是一个动态库,可执行程序会动态加载我们的动态库。
  2. 已经用其他构件工具(Ninja、Cmake、Makefile)编译好的可执行程序,有些开源项目自带了构建工具能方便的编译出测试程序,我们没必要再使用xcode来编译了,这样的情况下直接使用xcode调试就好了。

配置及说明

  1. 新建一个空工程:Xcode -> File -> New -> Project…
  2. 配置Scheme:Xcode -> Product -> Scheme -> Edit Scheme…
  3. 配置Scheme Info:指定可执行程序路径和LLDB路径,初始化文件的设置就是对应lldb--local-lldbinit选项
    xcode调试可执行程序_第1张图片
  4. 配置Scheme Arguments(参数和环境变量):以下配置类似于:$ DYLD_LIBRARY_PATH=/Users/stone/mediacore/out/mac ./media_service --port 9000 --log
    xcode调试可执行程序_第2张图片
  5. 配置Scheme Options:这里的选项有位置模拟、StoreKit文件配置、GPU抓取方式、语言设置、区域设置、XPC开关、日志输出设置等,可以根据自己的情况设置,我没有这方面的需求,所以这部分不深究。
    xcode调试可执行程序_第3张图片
  6. 配置Scheme Diagnostics:分为三部分:Runtime Sanitization、Runtime API Checking和Memory Management
    xcode调试可执行程序_第4张图片
  • Runtime Sanitization:这部分的选项是需要重新编译代码的,因为它的实现需要在编译的时候设置-fsanitize。Address Sanitizer和Thread Sanitizer不能同时使用
    • Address Sanitizer:地址检测,当发生内存错误的时候会自动停止应用程序在发生错误的行,并指出错误原因。Address Sanitizer可以发现的问题:访问释放的指针、堆溢出和栈的缓冲区上溢和下溢等内存问题。实际上编译器检测模块就是在编译的时候加上-fsanitize=address,关于AddressSanitizer可以阅读llvm-AddressSanitizer
    • Thread Sanitizer:线程之间的数据竞争检测,它由一个编译器检测模块和一个运行时库组成。实际上编译器检测模块就是在编译的时候加上-fsanitize=thread,关于ThreadSanitizer可以阅读llvm-ThreadSanitizer
    • Undefined Behavior Sanitizer:未定义行为检查,实际上编译器检测模块就是在编译的时候加上-fsanitize=undefined,关于UndefinedBehaviorSanitizer可以阅读llvm-UndefinedBehaviorSanitizer
void AddressSanitizer()
{
     
    char p[5];
    p[5] = 'a'; // AddressSanitizer: stack-buffer-overflow on address 0x7ffeefbff1e5 at pc 0x000100001c69 bp 0x7ffeefbff1b0 sp 0x7ffeefbff1a8
    *(&p[0] - 1) = 'a'; // AddressSanitizer: stack-buffer-underflow on address 0x7ffeefbff1df at pc 0x000100001c69 bp 0x7ffeefbff1b0 sp 0x7ffeefbff1a8
    char *ptr = new char[5];
    ptr[5] = 'a'; // AddressSanitizer: heap-buffer-overflow on address 0x602000000c35 at pc 0x000100001cd7 bp 0x7ffeefbff250 sp 0x7ffeefbff248
    *(ptr - 1) = 'a'; // AddressSanitizer: heap-buffer-overflow on address 0x602000000c2f at pc 0x000100001cd7 bp 0x7ffeefbff250 sp 0x7ffeefbff248
    delete []ptr;
    ptr[0] = 'a'; // AddressSanitizer: heap-use-after-free on address 0x602000000c30 at pc 0x000100001cdc bp 0x7ffeefbff250 sp 0x7ffeefbff248
}

void ThreadSanitizer()
{
     
    int Global = 0;
    std::thread t = std::thread([&](){
     
        Global = 42; // Data race in RunOnMainThread()::$_0::operator()() const
    });
    Global = 43;
    t.join();
}

void UndefinedBehaviorSanitizer()
{
     
    int a[4];
    a[4] = 4; // runtime error: index 4 out of bounds for type 'int [4]'
    int k = 0x7fffffff;
    k += 1; // runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
    char stack[5];
    *(stack - 1) = 1; // Index -1 out of bounds for type 'char [5]'
    stack[5] = 1; // Index 5 out of bounds for type 'char [5]'
}
  • Runtime API Checking:检测是否有后台线程执行UI API,如果出现这样的情况就会暂停在执行的位置
  • Memory Management:这部分选项的实现是通过运行的时候使用特别版本库(libgmalloc)替换掉标准库。**它使用多种技术尝试在发生内存错误的特定点使你的应用程序崩溃。**例如,它将单独的内存分配放在不同的虚拟内存页面上,然后在释放内存时删除整个页面。随后尝试访问已释放的内存会导致立即发生内存异常,而不是盲目访问现在可能包含其他数据的内存。当崩溃发生时,你可以去检查调试器中的故障点以识别问题。这个库提供的调试功能有:追踪内存破坏错误、堆损坏、引用已经释放的内存和内存(栈和堆)越界。这些功能是通过环境变量控制的,具体请看官方文档MallocDebug,同时还可以阅读man libgmalloc了解是如何替换标准库的、Xcode是如何使用它的,不同版本间有什么区别。
    • Malloc Scribble:如果设置,则malloc将新分配的块的每个字节设置为值0xAA,则将free的区域(block)的每个字节设置为0x55
    • Malloc Guard Edges:如果设置,则malloc在大型分配之前和之后添加保护页(具体是什么内容我也不知道)。当出现上溢访问的时候会奔溃在访问的位置。
    • Guard Malloc:如果设置了,当malloc的时候它会记录此地址,free的时候它会检查此地址有没有在登记列表中(如果不在列表中它会有这样的打印was not claimed by any registered)。当出现下溢访问的时候会奔溃在访问的位置。它还会在分配区域的前8个字节写入特定的值(每四个字节的字符是0xdeadbeef)。此选项还需要要求Logging模式为All Allocation and Free History否则不起作用。
    • Zombie Objects:僵尸对象检查
    • Malloc Stack Logging:Logging有两种模式:Live Allocations Only(简化版)和All Allocation and Free History(完整版)
void MallocScribble()
{
     
    uint8_t* p = new uint8_t[5];
    printf("0x%x 0x%x 0x%x 0x%x 0x%x\n", p[0], p[1], p[2], p[3], p[4]); // 0xaa 0xaa 0xaa 0xaa 0xaa
    delete [] p;
    printf("0x%x 0x%x 0x%x 0x%x 0x%x\n", p[0], p[1], p[2], p[3], p[4]); // 0x55 0x55 0x55 0x55 0x55
}

void MallocGuardEdges()
{
     
    uint8_t* p = new uint8_t[100 * 1024];
    uint8_t* before = p - 8;
    before[0] = 1; // Thread 1: EXC_BAD_ACCESS (code=2, address=0x100371ff8)
}

void GuardMalloc()
{
     
    uint8_t* p = new uint8_t[100 * 1024];
    printf("%p\n", p); // 0x10efc0000
    uint8_t* before = p - 8;
    printf("0x%x\n", *reinterpret_cast<uint32_t*>(before)); // 0xdeadbeef
    printf("0x%x\n", *(reinterpret_cast<uint32_t*>(before) + 1)); // 0xdeadbeef
    before[0] = 1; // 这里能运行通过,说明GuardMalloc检查不出内存上溢
    uint8_t* after = p + 100 * 1024;
    // Live Allocations Only选项时不会奔溃,All Allocation and Free History选项会奔溃
    after[0] = 1; // Thread 1: EXC_BAD_ACCESS (code=1, address=0x10f100000)
    delete [] (p + 1); // GuardMalloc[MediaService-99664]: attempted free of pointer 0x10efc0001 that was not claimed by any registered malloc zone
}

你可能感兴趣的:(C/C++,Memory,Sanitizer,GuardMalloc,libgmalloc,Scribble)