linuxにおけるメモリ漏れの検出(三)カスタマイズされたnew/delete


『linuxにおけるメモリリークの検出(二)カスタマイズされたmalloc/free』における__wrap法はCの問題のみを解決しているが,この節ではC++におけるnew/deleteにもカウントコードを容易に挿入させる方法について説明する.
wrapメソッドの試み__wrap_new/__wrap_deleteを使ってもいいですか?やってみましょう.
こんなテストコードを書きました
#include <iostream>
using namespace std;

int count = 0;

void * __wrap_new(size_t size)
{
    count++;
    return __real_new(size);
}

void __wrap_delete(void *ptr)
{
    count--;
    __real_delete(ptr);
}

int main()
{
    count = 0;
    int *p1 = new int;
    int *p2 = new int;
    delete p1;
    if(count != 0)
        cout<<"memory leak!"<<endl;
        return 0;
}

そしてこのようにコンパイルして、g++ -o test test.cpp -Wl,--wrap,new -Wl,--wrap,delete、結果
cpptest.cpp: In functionvoid* __wrap_new(size_t)’:
cpptest.cpp:9:27: error: ‘__real_new’ was not declared in this scope
     return __real_new(size);
                           ^
cpptest.cpp: In functionvoid __wrap_delete(void*)’:
cpptest.cpp:15:22: error: ‘__real_delete’ was not declared in this scope
     __real_delete(ptr);
     ^

この方法は実行できないようです.これはnewとmallocの違いから言います.
new VS. malloc
mallocは、指定したサイズのメモリ領域を割り当てる役割を果たしていることをよく理解しています.
newの仕事は2つのステップに分かれています.
最初のステップは、指定したサイズのメモリ領域を割り当てることです.mallocと同じように、operator newという専用の名前があります.
2つ目は、割り当てられたメモリを指定した方法で初期化することです.これはmallocにはありません.placement newという専用の名前もあります.
ステップ
さぎょう
mallocとの関係
リロード可能かどうか
使用方法
operator new
指定したサイズのスペースを割り当てる
mallocに相当
リロード可能class *pA = operator new(100)のような単独で呼び出すことができ、class *pA = malloc(100);に相当する.
placement new
指定した方法でスペースを初期化
mallocではこのような機能は提供できません
リロード不可
空間のポインタをパラメータとして入力し、この動作を単独で呼び出して初期化操作を実行することができます.例えば、class *pA = new(buf) class();はclass::class()を使用してbufを初期化するメモリに相当します.
operator newとplacement newとその他の詳細については、より多くの文章を参照することができますが、newの機能は非常に複雑で、__wrap_new(size_t size)で解決できるものではありません.
operator newリロード
newの機能は複雑ですが、メモリの割り当てに関連する部分、つまりoperator newに注目しています.幸いなことに、リロードできます.
C++はリロードをサポートし、newのoperater newをリロードし、カウント機能を追加し、mallocでメモリ申請を実現することができます.
#include <iostream>
using namespace std;

#include <stdio.h>
#include <stdlib.h>

int count = 0;

void * operator new(size_t size)
{
    count++;
    return malloc(size);
}

void operator delete(void *ptr)
{
    count--;
    free(ptr);
}

int main()
{
    count = 0;
    int *p1 = new int;
    int *p2 = new int;
    delete p1;
    if(count != 0)
        cout<<"memory leak!"<<endl;
        return 0;
}

newもmallocを呼び出すことで実現される以上、operator newとmallocを別々に統計する必要はなく、mallocを統計するだけでよい.__wrap_symbol__real_symbolはC関数であるため、extern "C"はすべて使用されます.
#include <iostream>
using namespace std;

#include <stdio.h>
#include <stdlib.h>

int count = 0;

extern "C"
{
void* __real_malloc(int c); 
void * __wrap_malloc(int size)
{
    count++;
    return __real_malloc(size);
}

void __real_free(void *ptr);
void __wrap_free(void *ptr)
{
    count--;
    __real_free(ptr);
}
}

void * operator new(size_t size)
{
    return malloc(size);
}

void operator delete(void *ptr)
{
    free(ptr);
}

int main()
{
    count = 0;
    int *p1 = new int(3);
    int *p2 = new int(4);
    cout<<*p1<<' '<<*p2<<endl;
    delete p1;
    if(count != 0)
        cout<<"memory leak!"<<endl;
        return 0;
}

ぶんせき
  • の利点
  • (1)使い勝手-製品コードを変更する必要はなく、コンパイルオプションを変更するだけで完了します.
    (2)包括的な範囲:wrapはリンクオプションであり、静的ライブラリでも動的ライブラリでも、__wrap_mallocと__wrap_freeでリンクされたすべてのファイルに役立ちます.
    (3)cの検出はc++の検出とシームレスに互換性がある
  • 欠点
  • (1)この方法では,実行終了時に実行中に発生した印刷分析に対して結果を知ることが要求される.
    (2)漏洩の有無しか検出できないが,具体的な情報がない,例えばどれだけの空間が漏洩したか
    (3)どの行のコードがリークを起こしたのか説明できない
    (4)この方法はC++の置換問題を解決したが、新たな問題を導入した.C++では同一のポインタに対して出願と解放が行われているのに、出願と解放の大きさが等しくない可能性があり、メモリリークが検出できない場合がある.例えば(a)親(b)出願配列を解析して配列第1項を解放する
    改善
    どのように解決するかを知りたいなら,次の分解を見てみよう.