2014年

6月

01日

GLXでメモリリークが発生しただけ【OpenGL・Linux】

以前も書いたとおり、LinuxでOpenGLネタがやりたくてX Windowアプリケーションのプログラミングをしてたところ、問題が発生しまして・・・

 

 

以下が問題のコード↓

/**

 * コンストラクタ

 **/

XwinForm::XwinForm(

                int _argc,

                char* _argv[],

                char* _title,

                unsigned int _client_width,

                unsigned int _client_height,

                unsigned int _background,

                char* _icon_file) {

        

        // 初期化

        display = NULL;

        window = None;

        glxwindow = None;

        icon = None;

        c_map = None;

        context = NULL;

        visual = NULL;

        configs = None;

        clear_r = ((_background & 0xff0000) >> 16) / 255.0f;

        clear_g = ((_background & 0xff00) >> 8) / 255.0f;

        clear_b = ((_background & 0xff)) / 255.0f;

        

        // X Serverに接続

        printf(">>>Connect XServer......");

        display = XOpenDisplay(NULL);

        

        // エラーチェック

        if(NULL == display){

                printf("failed.\n");

                return;

        }

        

        printf("success. name=\"%s\".\n", XDisplayName(NULL));

        

        // ビジュアル作成

        printf(">>>Choose Visual......");

        

        int attributes[] = {

        GLX_RGBA, True,
        GLX_DOUBLEBUFFER, True,
        GLX_RED_SIZE, 8,
        GLX_GREEN_SIZE, 8,
        GLX_BLUE_SIZE, 8,
        GLX_ALPHA_SIZE, 8,
        GLX_DEPTH_SIZE, 24,
        GLX_STENCIL_SIZE, 8,
        None
    };

 

    visual = glXChooseVisual(display, DefaultScreen(display), attributes);

        

        // エラーチェック

        if(NULL == visual) {

                printf("failed.\n");

                return;

        }

        

        printf("success. Visual:%ld\n", visual->visualid);

        

        // ウィンドウ属性

        printf(">>>Create Colormap......");

        c_map = XCreateColormap(display, RootWindow(display, visual->screen), visual->visual, AllocNone);

        

        switch(c_map) {

                case BadAlloc:

                        printf("faild. BadAlloc.\n");

                        return;

                        

                case BadColor:

                        printf("failed. BadColor.\n");

                        return;

                

                default:

                        printf("success. Color:%d\n", (int)c_map);

                        break;

        }

        

        XSetWindowAttributes winAttribute;

        winAttribute.colormap = c_map;

        winAttribute.event_mask = ExposureMask;

        winAttribute.background_pixel = winAttribute.border_pixel = 0;

        

        // ウィンドウ作成

        printf(">>>Create Window......");

        window = XCreateWindow(

                display,

                RootWindow(display, visual->screen), 

                0, 0,

                _client_width, _client_height,

                0,

                visual->depth,

                InputOutput,

                visual->visual,

                WINDOW_ATTRIBUTE_MASK,

                &winAttribute);

                

        switch(window) {

                case BadAlloc:

                        printf("faild. BadAlloc.\n");

                        return;

                        

                case BadMatch:

                        printf("faild. BadMatch.\n");

                        return;

                        

                case BadValue:

                        printf("faild. BadValue.\n");

                        return;

                        

                case BadWindow:

                        printf("faild. BadWindow.\n");

                        return;

                        

                default:

                        printf("success. ID:%d.\n", (int)window);

                        break;

        }

 

       /** 以下省略 **/

}

 

/**

 * デストラクタ

 **/

XwinForm::~XwinForm() {

        Release();

}

 

/**

 * 開放

 **/

void XwinForm::Release() {

 

        if(NULL != display) {

        

                // ウィンドウを非表示にする

                if(None != window) {

                        XUnmapWindow(display, window);

                        printf(">>>Unmap Window.\n");

                        

                        glXMakeCurrent(display, 0, 0);

                        

                        // ウィンドウを破棄

                        XDestroyWindow(display, window);

                        window = None;

                        printf(">>>Release Window.\n");

                }

                

                // アイコン開放

                if(None != icon) {

                        XFreePixmap(display, icon);

                        icon = None;

                        printf(">>>Release Icon.\n");

 

                }

 

                // カラーマップを破棄

                if(None != c_map) {

                        XFreeColormap(display, c_map);

                        c_map = None;

                        printf(">>>Release Colormap.\n");

                }

 

                // コンテキストを破棄

                if(NULL != context) {

                        glXDestroyContext(display, context);

                        context = NULL;

                        printf(">>>Release Context.\n");

                }

 

                // ビジュアル開放

                if(NULL != visual) {

                        XFree(visual);

                        visual = NULL;

                        printf(">>>Release Visual.\n");

                }

                

                // ディスプレイ開放&Xサーバーからの切断

                XCloseDisplay(display);

                display = NULL;

                printf(">>>Release Display.\n");

        }

}

これを「mtrace」と「valgrind」を使ってデバッグしてみたところ、メモリリークが検出された。

 

●mtraceの検出結果
Memory not freed:
-----------------
   Address     Size     Caller
0x09586738     0x21  at 0xb771a963
0x09586760     0x1c  at 0xb771cbbd
0x095869d0      0xc  at 0xb725c63b
0x09586a18     0x3c  at 0xb759d680
0x09586b10      0x4  at 0xb74b9a47
0x09586c88     0x26  at 0xb771a963
0x09586cb8     0x20  at 0xb7715056
0x09586ce0     0x20  at 0xb771a963
0x09586d08     0x25  at 0xb7715056
0x09592108     0x14  at 0xb727d3e6
0x09592120     0x21  at 0xb7715056
0x09592148     0x26  at 0xb7715056
0x09592178      0x8  at 0xb77179c4
0x0959ec08    0x279  at 0xb771a6a9
0x0959ee88    0x27e  at 0xb771a6a9
0x0959f110    0x278  at 0xb771a6a9

以下省略

 

mtracはC言語のソースならCallerの部分に「呼び出し元のソースファイル名と行数」出力してくれるのだが、今回はC++で組んでいたのでアドレス出力されていた。

 

発生箇所はわからないもののメモリリークは発生したらしい。

 

 

●valgrindの検出結果

==6827== Memcheck, a memory error detector
==6827== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==6827== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==6827== Command: ./debug.out
==6827== Parent PID: 6823
==6827==
==6827==
==6827== HEAP SUMMARY:
==6827==     in use at exit: 15,653 bytes in 97 blocks
==6827==   total heap usage: 2,890 allocs, 2,793 frees, 1,337,117 bytes allocated
==6827==


途中省略

 

==6827== 4 bytes in 1 blocks are still reachable in loss record 3 of 53
==6827==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==6827==    by 0x4E794DB: cso_hash_create (in /usr/lib/i386-linux-gnu/libgallium.so.0.0.0)
==6827==    by 0x4EED466: util_hash_table_create (in /usr/lib/i386-linux-gnu/libgallium.so.0.0.0)
==6827==    by 0x4D16D9F: radeon_drm_winsys_create (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4A8613B: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4D10C9B: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4A87335: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x41CC30A: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A4D23: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A129C: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A1B1D: glXChooseVisual (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x8048D77: XwinForm::XwinForm(int, char**, char*, unsigned int, unsigned int, unsigned int, char*) (XwinForm.cpp:99)


途中省略

 

 12 bytes in 1 blocks are still reachable in loss record 6 of 53
==6827==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==6827==    by 0x44ED63A: XextCreateExtension (in /usr/lib/i386-linux-gnu/libXext.so.6.4.0)
==6827==    by 0x41CD13C: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41CD42B: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41CD02C: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A4E52: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A129C: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A1B1D: glXChooseVisual (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x8048D77: XwinForm::XwinForm(int, char**, char*, unsigned int, unsigned int, unsigned int, char*) (XwinForm.cpp:99)
==6827==    by 0x8048B0D: main (main.cpp:20)
==6827==


途中省略


==6827== 12 bytes in 1 blocks are still reachable in loss record 11 of 53
==6827==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==6827==    by 0x4EED45B: util_hash_table_create (in /usr/lib/i386-linux-gnu/libgallium.so.0.0.0)
==6827==    by 0x4D16D9F: radeon_drm_winsys_create (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4A8613B: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4D10C9B: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4A87335: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x41CC30A: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A4D23: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A129C: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A1B1D: glXChooseVisual (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x8048D77: XwinForm::XwinForm(int, char**, char*, unsigned int, unsigned int, unsigned int, char*) (XwinForm.cpp:99)
==6827==    by 0x8048B0D: main (main.cpp:20)

 

途中省略

 

 20 bytes in 1 blocks are still reachable in loss record 23 of 53
==6827==    at 0x402C109: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==6827==    by 0x44C53E5: _dlerror_run (dlerror.c:141)
==6827==    by 0x44C4D70: dlopen@@GLIBC_2.1 (dlopen.c:87)
==6827==    by 0x41D07E5: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41D0849: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41D0C71: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41CBF84: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A4D23: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A129C: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A1B1D: glXChooseVisual (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x8048D77: XwinForm::XwinForm(int, char**, char*, unsigned int, unsigned int, unsigned int, char*) (XwinForm.cpp:99)
==6827==    by 0x8048B0D: main (main.cpp:20)


途中省略

 

==6827== 24 bytes in 1 blocks are still reachable in loss record 28 of 53
==6827==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==6827==    by 0x4E794ED: cso_hash_create (in /usr/lib/i386-linux-gnu/libgallium.so.0.0.0)
==6827==    by 0x4EED466: util_hash_table_create (in /usr/lib/i386-linux-gnu/libgallium.so.0.0.0)
==6827==    by 0x4D16D9F: radeon_drm_winsys_create (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4A8613B: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4D10C9B: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4A87335: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x41CC30A: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A4D23: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A129C: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A1B1D: glXChooseVisual (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x8048D77: XwinForm::XwinForm(int, char**, char*, unsigned int, unsigned int, unsigned int, char*) (XwinForm.cpp:99)


途中省略

 

==6827== 60 bytes in 1 blocks are definitely lost in loss record 37 of 53
==6827==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==6827==    by 0x41D067F: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A4E81: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A129C: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A1B1D: glXChooseVisual (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x8048D77: XwinForm::XwinForm(int, char**, char*, unsigned int, unsigned int, unsigned int, char*) (XwinForm.cpp:99)
==6827==    by 0x8048B0D: main (main.cpp:20)


途中省略

 

==6827== 68 bytes in 1 blocks are still reachable in loss record 38 of 53
==6827==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==6827==    by 0x4E79275: ??? (in /usr/lib/i386-linux-gnu/libgallium.so.0.0.0)
==6827==    by 0x4E79436: cso_hash_insert (in /usr/lib/i386-linux-gnu/libgallium.so.0.0.0)
==6827==    by 0x4EED58D: util_hash_table_set (in /usr/lib/i386-linux-gnu/libgallium.so.0.0.0)
==6827==    by 0x4D16DE5: radeon_drm_winsys_create (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4A8613B: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4D10C9B: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x4A87335: ??? (in /usr/lib/i386-linux-gnu/dri/r600_dri.so)
==6827==    by 0x41CC30A: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A4D23: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A129C: ??? (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)
==6827==    by 0x41A1B1D: glXChooseVisual (in /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0)


途中省略

 

==6827== LEAK SUMMARY:
==6827==    definitely lost: 60 bytes in 1 blocks
==6827==    indirectly lost: 0 bytes in 0 blocks
==6827==      possibly lost: 0 bytes in 0 blocks
==6827==    still reachable: 15,593 bytes in 96 blocks
==6827==         suppressed: 0 bytes in 0 blocks
==6827==
==6827== For counts of detected and suppressed errors, rerun with: -v
==6827== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

 

上記のとおりglXChooseVisualでメモリリークが発生していることが確認できた。ログを確認すると、glXChooseVisualで取得したビジュアル情報はXFreeでちゃんと開放されているのにリークしていた。

 

試しにビジュアル情報を取得する処理をコメントアウトしてみたところ、メモリリークは発生しなくなった。

 

glXChooseVisualを使わずにウィンドウを作成するために幾つかの処理も書いてみたが、その度に別の関数からメモリリークが検出された。

 

以下はメモリリークの発生が確認できたGLXの関数一覧。

 

glXChooseVisual

glXChooseFBConfig

glXGetFBConfigs

glXGetConfig


結果として、コンフィグ情報を取得しようとするとメモリリークが発生するらしい。コンフィグは「OpenGLで使用できるか?」、「ダブルバッファリン グに対応しているか?」、「RGBA形式に対応しているか?」等、ウィンドウを作る上で必要なビジュアルの詳細情報を持っているのだが、メモリリークが発 生するとなるとうかつに呼べ出せない。どうすればいいのだろうか・・・

今回のコードで使ったOpenGL環境は以下のようになっています。若干古いです。

ビデオカードはオープンソースドライバを使ってます。

 

公式のLinux向けAMDドライバを使いたいのですが、インストールすると解像度やフレームレートがおかしくなってCCCもなぜか起動しませんでした。どうもバグが存在するようです。

 

GL_VERSION : 3.0 Mesa 10.1.3

GL_VENDOR : X.Org

GL_RENDERER : Gallium 0.4 on AMD RV730

GL_SHADING_LANGUAGE_VERSION : 1.30

 

調べてみたところ同様にglXChooseVisualでメモリリークが発生した例を幾つか見つけましたが、どれも未解決のようでした(もちろん日本語は無い)。

 

他の開発者の方々はどうしているんでしょうか・・・?

 

GLXを使っている人自体が少ないから情報が皆無なのか、同様にメモリリークは発生しているけど気づいてないだけのか、私の環境が古いから不具合が出てるのか、よくわかりません。Linuxで遊べるゲームでOpenGLを使ってるものって結構あるはずなんですけど、大丈夫なんでしょうかね?何かしらの解決方法があるのでしょうか?

 

ちなみにX.Orgにもメモリリークがあるらしいです。LinuxのOpenGLアプリ開発って危険ですね・・・

 

GLXを使わずにGLUTを使おうかとも考えましたが、内部でGLXを使ってるそうです。まぁ仕組みをよく考えてみると当たり前なんですが、お手上げです。こうなるとラッパークラスライブラリのTao FrameworkやOpenTKもうかつに使えません。

 

X.Orgの不具合なのか、GLXの不具合なのかはわかりませんが、メモリリークが出る以上、残念ですが今後LinuxでOpenGLの開発をするのは止めておこうと思います。

追記

 

今回の記事を書く上で完成した最終的なソースコードをアップロードしておきます。

source.zip
圧縮Zipフォーマット 8.1 KB