目录

memwatch 是一个用于检测内存泄漏和其他内存相关问题的轻量级库。它主要用于 C 语言程序,但也可以用在简单的 C++ 程序中。以下是如何安装和使用 memwatch,以及一个示例程序演示其功能。

安装 memwatch

  1. 下载 memwatch

    memwatch 可以从其官方网站或其他软件资源网站下载。通常,它是以单个 memwatch.c 和 memwatch.h 文件的形式提供。

    下载链接:https://github.com/502110983/MemWatch

  2. 将文件添加到项目中

    将 memwatch.c 和 memwatch.h 文件复制到你的项目目录中。

使用 memwatch

  1. 包含头文件

    在要监控内存的源文件中包含 memwatch.h:

    #include "memwatch.h"
    
  2. 编译时定义 MEMWATCH

    在编译时定义 MEMWATCH 和 MW_STDIO 来启用 memwatch 功能。这可以通过修改编译命令来实现。

示例程序

以下是一个使用 memwatch 的简单 C++ 示例程序:

示例程序:leak_example.cpp

#include <iostream>
#include "memwatch.h"

void memory_leak() {
    int* leaky_array = new int[100];
    // 未释放分配的内存
}

int main() {
    memory_leak();
    std::cout << "Hello, Memwatch!" << std::endl;
    return 0;
}

编译和运行程序

  1. 编译程序

    确保在编译时定义了 MEMWATCH 和 MW_STDIO:

    g++ -DMEMWATCH -DMW_STDIO -o leak_example leak_example.cpp memwatch.c
    
  2. 运行程序

    运行编译后的程序:

    ./leak_example
    

检查输出

memwatch 会生成一个名为 memwatch.log 的日志文件,其中包含所有检测到的内存泄漏和其他内存相关问题的详细信息。

示例日志输出:memwatch.log

MEMWATCH LOG FILE - by Langley at Apr 23 2023 - 12:34:56

Memory allocation statistics:

  alloc:     1 blocks (400 bytes)        free:      0 blocks (0 bytes)
  grow:      0 blocks (0 bytes)          shrink:    0 blocks (0 bytes)
  errors:    1

Memory still allocated: 400 bytes in 1 blocks
 At file leak_example.cpp, line 6: malloc(400)
 --> CONTINUED AT MEMORY BLOCK 0x7f9b18400660 (4 bytes)

总结

通过以上步骤,你可以在 C++ 项目中使用 memwatch 来检测内存泄漏和其他内存问题。如果有更多问题或需要进一步帮助,请随时告诉我!

代码解读

p = malloc(210);
free(p);
p = malloc(20);
p = malloc(200);    /* causes unfreed error */
p[-1] = 0;          /* causes underflow error */
free(p);

这段代码确实会导致内存管理中的一些典型错误,如未释放的内存(unfreed error)和缓冲区下溢(underflow error)。下面是对代码的逐步分析:

代码分析:

1. p = malloc(210);

  • 这行代码分配了 210 字节的内存块,并将指针 p 指向这块内存。

2. free(p);

  • 释放 p 指向的内存块。此时,p 指向的 210 字节内存已被释放,p 变成悬空指针(dangling pointer),但由于未将 p 置为 NULL,p 仍指向已释放的内存区域。

3. p = malloc(20);

  • 再次调用 malloc() 分配 20 字节的内存,并将指针 p 指向新的内存块。这会覆盖之前 p 的悬空状态。

4. p = malloc(200); /* causes unfreed error */

  • 又一次调用 malloc() 分配 200 字节的内存,并将指针 p 指向新的内存块。由于在之前的步骤中,p 已经指向了分配的 20 字节内存,而这次没有先 free(p) 就直接覆盖了 p,导致之前分配的 20 字节内存未释放,造成了 内存泄漏(unfreed error)。

5. p[-1] = 0; /* causes underflow error */

  • 这行代码通过 p[-1] 访问 p 指向内存块之前的一个字节,这是非法的,因为该字节不属于当前分配的内存块。
  • 这会导致 缓冲区下溢(underflow error),并可能引发程序崩溃或其他不可预见的行为。

6. free(p);

  • 释放 p 指向的 200 字节内存。这行代码本身是合法的,但由于之前的 p[-1] = 0; 操作,可能已经破坏了内存管理结构,从而导致释放过程中的不可预测行为,可能会导致段错误(segmentation fault)。
p = malloc(100);
p[ -(int)(sizeof(long)*8) ] = -1; /* try to damage MW's heap chain */
free( p ); /* should cause relink */

这段代码试图通过非法的内存访问操作破坏内存堆管理结构(heap chain),从而测试内存管理工具(如 MEMWATCH)的鲁棒性。这种操作通常会引发内存错误或程序崩溃。

代码分析:

1. p = malloc(100);

  • 这行代码分配了 100 字节的内存块,并将指针 p 指向这块内存。

2. p[ -(int)(sizeof(long)*8) ] = -1;

  • 这行代码试图通过 p[-(int)(sizeof(long)*8)] 访问并修改 p 指向内存块之前的某个内存位置。
  • (int)(sizeof(long)*8) 计算出一个较大的负偏移量,通常为 -32 或 -64 字节(具体值取决于 long 类型在系统中的大小,如 4 字节或 8 字节)。
  • 通过这个负偏移量,代码实际上是在尝试修改内存堆管理结构中的某些关键区域,意图“破坏”内存堆的结构(heap chain)。
  • 这种操作是非法的,因为它访问了程序未分配或不应访问的内存区域。这可能会导致以下结果:
    • 破坏内存管理结构:如果 p 指向的内存块之前的内存区域包含内存管理器用来维护堆链表的元数据,这种写入可能会破坏这些元数据,导致内存管理系统混乱。
    • 引发未定义行为:修改程序未授权访问的内存通常会引发未定义行为,可能导致程序崩溃、数据损坏、或者其他不可预测的错误。

3. free(p);

  • 这行代码试图释放 p 指向的 100 字节内存块。
  • 内存管理系统的反应:
    • 如果内存管理结构已经被破坏,free(p) 操作可能会失败,导致程序崩溃或生成错误消息。
    • 某些高级内存调试工具(如 MEMWATCH)可能会检测到内存堆结构的损坏,并尝试修复或报告错误。

结果和影响:

  • 破坏内存管理结构:如果成功破坏了内存管理结构,free(p) 可能会导致内存管理器试图修复被破坏的堆链表(relink)。在没有强大内存保护机制的情况下,这种破坏可能会导致严重的系统错误或程序崩溃。
  • 未定义行为:由于程序访问并修改了其未分配的内存区域,任何行为都是未定义的,可能会出现程序崩溃、数据损坏、甚至安全漏洞。

总结:

这段代码故意进行了非法的内存操作,试图破坏内存堆管理结构。执行这段代码后,通常会引发内存错误或程序崩溃,具体取决于内存管理器的实现和鲁棒性。这种代码通常用于测试内存调试工具的健壮性,而不应在实际生产环境中使用。

总结:

这段代码会导致以下问题:

  • 内存泄漏:由于没有释放之前分配的 20 字节内存而直接覆盖了 p。
  • 缓冲区下溢:通过非法访问 p[-1] 修改了 p 指向内存块之前的内存,可能破坏了程序的内存结构。

这些错误在程序中可能会导致崩溃、不可预测的行为或安全漏洞。在实际代码中应避免此类错误,确保每次分配的内存块在不使用时正确释放,并且不对未分配的内存区域进行读写操作。

============= MEMWATCH 2.71 Copyright (C) 1992-1999 Johan Lindh =============

Started at Fri Aug 23 20:57:41 2024

Modes: __STDC__ 64-bit mwDWORD==(unsigned int)
mwROUNDALLOC==8 sizeof(mwData)==56 mwDataSize==56

statistics: now collecting on a line basis
Hello world!
underflow: <5> test.c(62), 200 bytes alloc'd at <4> test.c(60)
relink: <7> test.c(66) attempting to repair MW-0x56300bd6aab0...
relink: MW-0x56300bd6aab0 is the head (first) allocation
relink: MW-0x56300bd6aab0 prev pointer is non-NULL, you have a wild pointer
relink: successful, no allocations lost
assert trap: <8> test.c(69), 1==2

Stopped at Fri Aug 23 20:57:45 2024

unfreed: <3> test.c(59), 20 bytes at 0x56300bd6a8e0    {FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE ................}

Memory usage statistics (global):
 N)umber of allocations made: 4
 L)argest memory usage      : 220
 T)otal of all alloc() calls: 530
 U)nfreed bytes totals      : 20

Memory usage statistics (detailed):
 Module/Line                                Number   Largest  Total    Unfreed 
 test.c                                     4        220      530      20      
  64                                        1        100      100      0       
  60                                        1        200      200      0       
  59                                        1        20       20       20      
  57                                        1        210      210      0       
解读一下日志输出

这个日志是由 MEMWATCH 工具生成的,MEMWATCH 是一个内存调试工具,用于检测内存泄漏、内存访问违规等问题。以下是对日志输出的逐步解读:

日志概述

  • 工具信息: MEMWATCH 2.71 Copyright (C) 1992-1999 Johan Lindh
    • 这是 MEMWATCH 工具的版本信息和版权声明。
  • 启动时间: Started at Fri Aug 23 20:57:41 2024
    • 程序启动并开始监控内存操作的时间。
  • 模式信息:
    • __STDC__ 64-bit mwDWORD==(unsigned int): 程序使用了 64 位标准 C (__STDC__) 编译,mwDWORD 定义为 unsigned int。
    • mwROUNDALLOC==8 sizeof(mwData)==56 mwDataSize==56: MEMWATCH 每次内存分配向上对齐到 8 字节边界,内存监控结构(mwData)的大小为 56 字节。
  • 统计模式: statistics: now collecting on a line basis
    • MEMWATCH 现在按代码行号收集内存分配的统计数据。

日志详细内容

  1. "Hello world!"
    • TRACE("Hello world!\n"); 打印输出,表示程序已经开始运行。
  2. underflow: <5> test.c(62), 200 bytes alloc'd at <4> test.c(60)
    • 含义: 在 test.c 文件的第 62 行发生了缓冲区下溢错误(underflow),这个内存块是在第 60 行分配的(分配了 200 字节)。
    • 问题: 代码访问了分配内存块之前的区域(下溢),这种操作通常是非法的,会破坏内存管理系统。
  3. relink: <7> test.c(66) attempting to repair MW-0x56300bd6aab0...
    • 含义: MEMWATCH 发现了内存堆管理结构的问题,尝试在 test.c 第 66 行修复分配在 MW-0x56300bd6aab0 地址处的内存。
    • 详细描述:
      • MW-0x56300bd6aab0 is the head (first) allocation: 被修复的内存块是内存堆中的第一个分配块。
      • MW-0x56300bd6aab0 prev pointer is non-NULL, you have a wild pointer: 内存块的前一个指针不为 NULL,这意味着有一个非法或未初始化的指针(称为“野指针”)。
      • relink: successful, no allocations lost: 修复成功,没有丢失内存分配。
  4. assert trap: <8> test.c(69), 1==2
    • 含义: 在 test.c 第 69 行触发了断言(assert)错误,断言条件是 1==2(显然是不成立的条件)。
    • 解释: 这是一个故意的断言错误,用于测试 MEMWATCH 的断言处理功能。

结束和内存统计信息

  • 结束时间: Stopped at Fri Aug 23 20:57:45 2024
    • 程序在该时间点停止并生成内存使用报告。

内存使用统计(全局):

  • N)umber of allocations made: 4: 程序总共进行了 4 次内存分配。
  • L)argest memory usage: 220: 程序在运行期间最大内存使用量为 220 字节。
  • T)otal of all alloc() calls: 530: 程序通过所有 malloc() 调用分配的总内存为 530 字节。
  • U)nfreed bytes totals: 20: 在程序结束时,仍有 20 字节的内存未被释放(内存泄漏)。

内存使用统计(详细):

  • Module/Line Number Largest Total Unfreed: 按文件和代码行号列出的内存使用统计。
    • test.c 4 220 530 20: test.c 文件中共有 4 次内存分配,最大分配 220 字节,总共分配 530 字节,有 20 字节未释放。
      • 第 64 行: 1 次分配,最大 100 字节,总共 100 字节,无未释放内存。
      • 第 60 行: 1 次分配,200 字节,总共 200 字节,无未释放内存。
      • 第 59 行: 1 次分配,20 字节,总共 20 字节,有 20 字节未释放。
      • 第 57 行: 1 次分配,210 字节,总共 210 字节,无未释放内存。

总结

  • 主要问题: 代码中存在缓冲区下溢错误和内存泄漏。MEMWATCH 成功检测并报告了这些问题,还尝试修复了内存堆链表结构。
  • 断言错误: 代码故意触发了断言错误以测试 MEMWATCH 的错误处理功能。
  • 内存泄漏: 程序中未释放的 20 字节内存发生在第 59 行。

api解释

mwStatistics(2); 是 MEMWATCH 工具中的一个函数调用,用于配置 MEMWATCH 的统计信息输出模式。

含义说明:

mwStatistics() 函数的参数决定了 MEMWATCH 如何收集和报告内存使用的统计数据。传入的参数 2 指定了统计信息的收集方式。根据 MEMWATCH 的典型用法,参数的意义通常如下:

  • 0: 禁止统计信息的收集(即不生成任何内存统计信息)。
  • 1: 按照调用的函数来收集统计信息。这意味着统计数据是基于调用 malloc()、free()、realloc() 等内存操作函数的调用次数。
  • 2: 按照代码中的行号来收集统计信息。每当进行内存操作时,MEMWATCH 会记录发生内存操作的具体代码行号。这样,可以更容易地追踪到哪些行的代码可能导致了内存泄漏或其他内存问题。

mwStatistics(2); 的具体作用:

在这段代码中,mwStatistics(2); 命令 MEMWATCH 按照代码的行号来收集和报告内存使用的统计信息。这对于调试和分析内存问题非常有用,因为它可以帮助开发人员快速定位问题代码的具体行号。

总结:

调用 mwStatistics(2); 的目的是为了让 MEMWATCH 在运行过程中跟踪内存操作,并按照代码行号报告内存使用统计信息,这样开发人员可以更容易地识别和修复内存相关的问题。