4

2CPU と 4GB RAM を搭載した Ubuntu 12.04.1 LTS 64 ビットでhttperf 0.9.0 ( Google Code からダウンロード) を実行しています。Web サーバーのベンチマークを実行しようとしていますが、次のようなバッファ オーバーフローの問題が発生しました。

端末コマンド:

httperf --timeout=5 --client=0/1 --server=localhost --port=9090 --uri=/?value=benchmarks --rate=1200 --send-buffer=4096 --recv-buffer=16384 --num-conns=5000 --num-calls=10

数秒間実行した後、クラッシュします。

*** buffer overflow detected ***: httperf terminated
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x37)[0x7f1f5efa1007]
/lib/x86_64-linux-gnu/libc.so.6(+0x107f00)[0x7f1f5ef9ff00]
/lib/x86_64-linux-gnu/libc.so.6(+0x108fbe)[0x7f1f5efa0fbe]
httperf[0x404054]
httperf[0x404e9f]
httperf[0x406953]
httperf[0x406bd1]
httperf[0x40639f]
httperf[0x4054d5]
httperf[0x40285e]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7f1f5eeb976d]
httperf[0x4038f1]
======= Memory map: ========
...
...
7f1f5fd74000-7f1f5fd79000 rw-p 00000000 00:00 0 
7f1f5fd91000-7f1f5fd95000 rw-p 00000000 00:00 0 
7f1f5fd95000-7f1f5fd96000 r--p 00022000 08:03 4849686                    /lib/x86_64-linux-gnu/ld-2.15.so
7f1f5fd96000-7f1f5fd98000 rw-p 00023000 08:03 4849686                    /lib/x86_64-linux-gnu/ld-2.15.so
7fff10452000-7fff10473000 rw-p 00000000 00:00 0                          [stack]
7fff1054f000-7fff10550000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted

次のように、gdb でコア ダンプ ファイルを確認しました。

(gdb) list
198   event_signal (EV_PERF_SAMPLE, 0, callarg);
199 
200   /* prepare for next sample interval: */
201   perf_sample_start = timer_now ();
202   timer_schedule (perf_sample, regarg, RATE_INTERVAL);
203 }
204 
205 int
206 main (int argc, char **argv)
207 {
(gdb) bt
#0  0x00007f33d4643445 in raise () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007f33d4646bab in abort () from /lib/x86_64-linux-gnu/libc.so.6
#2  0x00007f33d4680e2e in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#3  0x00007f33d4716007 in __fortify_fail () from /lib/x86_64-linux-gnu/libc.so.6
#4  0x00007f33d4714f00 in __chk_fail () from /lib/x86_64-linux-gnu/libc.so.6
#5  0x00007f33d4715fbe in __fdelt_warn () from /lib/x86_64-linux-gnu/libc.so.6
#6  0x0000000000404054 in set_active (s=<optimized out>, fdset=0x612bc0) at core.c:367
#7  0x0000000000404e9f in core_connect (s=0x17e7100) at core.c:980
#8  0x0000000000406953 in make_conn (arg=...) at conn_rate.c:64
#9  0x0000000000406bd1 in tick (t=<optimized out>, arg=...) at rate.c:94
#10 0x000000000040639f in timer_tick () at timer.c:104
#11 0x00000000004054d5 in core_loop () at core.c:1255
#12 0x000000000040285e in main (argc=11, argv=<optimized out>) at httperf.c:971

ソースコードを少し追跡したところ、それFD_SETが原因であることがわかりました。

最後に、低レート (--rate=100または など--rate=500) の場合、httperf は問題なく動作します。さまざまな Web サーバーのベンチマークを行っていますが、クラッシュの発生率が異なります。私のレートは 100 から 1200 までさまざまです。

詳細については、Roberto Ostinelli が行った実験を実際に再現しようとしており、すでに TCP 設定を調整し、彼のブログ投稿に記載されているパッチを適用しています。

この問題の原因について何か考えはありますか? ありがとう!

4

4 に答える 4

3

> 1024 の fd を使用しようとしています。負荷が低い場合、それほど多くの fd は必要ありません/使用しません。負荷が高いと、より多くの fds が必要になり、最終的には 1024 になり、それが問題の原因になります。

__FD_SETSIZE を増やしてもこの問題が発生するので、バインドチェックを実行しているコードに実際にバグがあると思います (gcc/llvm?)

于 2012-11-03T04:26:53.803 に答える
3

新しいバージョンの glibc は、FD_SET (httperf から呼び出される) に対して内部的に独自のチェックを行いますが、それらのチェックは失敗し、異常終了します。httperf は別の __FD_SET_SIZE でビルドされていましたが、glibc はコンパイルに使用された元のものを使用していました。

これを回避するために、__FD_ELT がチェックを実行する前の古いバージョンの sys/select.h と bits/select.h を掘り起こし、それらを httperf の src/ ディレクトリ (sys/ と bits 内) にドロップしました。このように、httperf は、アボートの原因となるチェックを実行しない古い FD_SET マクロを使用します。私は glibc-2.14.1 を使用しましたが、bits/select2.h を持たないバージョンであれば問題なく動作するはずです。

私の友人は、https: //github.com/klueska/httperf で、私たち自身 (そしてあなたのもの) で使用するために、httperf に対するこの修正とその他の修正を収集しています。

于 2014-10-15T23:11:07.150 に答える
2

同様のクラッシュがあり、私の場合は select() を epoll() に置き換えて解決しました。

リリース コンパイルでのみ問題を再現できました (私は Eclipse を devenv として使用しています。Eclipse にデバッグとリリースの両方のコンパイラ オプションを設定させました)。

クラッシュは次のとおりです。

1354813976/SBNotificationServer terminated
06/12 21:13:54 > ======= Backtrace: =========
06/12 21:13:54 > /lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x37)[0x7f4e10f90807]
06/12 21:13:54 > /lib/x86_64-linux-gnu/libc.so.6(+0x109700)[0x7f4e10f8f700]
06/12 21:13:54 > /lib/x86_64-linux-gnu/libc.so.6(+0x10a7be)[0x7f4e10f907be]
06/12 21:13:54 > 1354813976/SBNotificationServer[0x49db90]
06/12 21:13:54 > 1354813976/SBNotificationServer[0x49de05]
06/12 21:13:54 > 1354813976/SBNotificationServer[0x4a4b07]
06/12 21:13:54 > 1354813976/SBNotificationServer[0x4a5318]
06/12 21:13:54 > 1354813976/SBNotificationServer[0x4a2628]
06/12 21:13:54 > /lib/x86_64-linux-gnu/libpthread.so.0(+0x7e9a)[0x7f4e10c70e9a]
06/12 21:13:54 > /lib/x86_64-linux-gnu/libc.so.6(clone+0x6d)[0x7f4e10f79cbd]
06/12 21:13:54 > ======= Memory map: ========
06/12 21:13:54 > 00400000-00507000 r-xp 00000000 ca:01 141328         /usr/share/spotbros/SBNotServer/1354813976/SBNotificationServer
06/12 21:13:54 > 00706000-00707000 r--p 00106000 ca:01 141328 /usr/share/spotbros/SBNotServer/1354813976/SBNotificationServer
06/12 21:13:54 > 00707000-00708000 rw-p 00107000 ca:01 141328     /usr/share/spotbros/SBNotServer/1354813976/SBNotificationServer
06/12 21:13:54 > 00708000-0070d000 rw-p 00000000 00:00 0
06/12 21:13:54 > 0120d000-01314000 rw-p 00000000 00:00 0 [heap]
06/12 21:13:54 > 7f49f8000000-7f49f8021000 rw-p 00000000 00:00 0
.......
.......

この後、コア ダンプを生成することができました。

warning: Can't read pathname for load map: Input/output error.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `./SBNotificationServer ../config.xml'.
Program terminated with signal 6, Aborted.
0 0x00007feb90d67425 in raise () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) 
(gdb) bt
0 0x00007feb90d67425 in raise () from /lib/x86_64-linux-gnu/libc.so.6
1 0x00007feb90d6ab10 in abort () from /lib/x86_64-linux-gnu/libc.so.6
2 0x00007feb90da539e in ?? () from /lib/x86_64-linux-gnu/libc.so.6
3 0x00007feb90e3b807 in __fortify_fail () from /lib/x86_64-linux-gnu/libc.so.6
4 0x00007feb90e3a700 in __chk_fail () from /lib/x86_64-linux-gnu/libc.so.6
5 0x00007feb90e3b7be in __fdelt_warn () from /lib/x86_64-linux-gnu/libc.so.6
6 0x000000000049e290 in CPhpResponseReader::WaitUntilReadable(int, int&, bool&) ()
7 0x000000000049e505 in CPhpResponseReader::Read(CReallocableBuffer&, int) ()
8 0x00000000004a5207 in CHttpPostBufferInterface::Flush() ()
9 0x00000000004a5a18 in CPhpRequestJob::Execute() ()
10 0x00000000004a2d28 in CThreadPool::Worker(void*) ()
11 0x00007feb90b1be9a in start_thread () from /lib/x86_64-linux-gnu/libpthread.so.0
12 0x00007feb90e24cbd in clone () from /lib/x86_64-linux-gnu/libc.so.6

詳細:

WaitUntilReadable (バックトレースを参照) は、基本的に select() を使用して、Web サーバーからデータが読み取られるのを待つ関数です。代わりに epoll() を使用して、その関数を再度書き直しました。

私のプログラムは、何千ものクライアントとの接続を維持するサーバーです。クライアントはサーバーにリクエストを送信し、サーバーはそれらのリクエストを Web サーバーに渡し、クライアントに応答を返します。ウェブサーバーへのリクエストのために、私のサーバーにはスレッドプールがあります。ここで、もう 1 つの重要な詳細を説明します。スレッドのスレッドプール数を高い数値に設定すると、クラッシュが発生します。これは、多くの select() 呼び出しが同時に行われることを意味します (1024 スレッドに設定して再現しました)。次に、テスト用に作成したクライアント シミュレーターを起動し、1000 のクライアント スレッドを起動して、できるだけ高速にリクエストを送信しました。これにより、select() を使用してリリースでコンパイルするとクラッシュが速くなりましたが、epoll() を導入した後は問題ありませんでした。

プール内の各スレッドは、1 つの fd で select() 呼び出しを行っていることに注意してください。

この問題を解決するために変更した関数は次のとおりです。

bool WaitUntilReadableDeprecated(int timeoutMSecs, int& elapsedMSecs, bool& closed)
{
    CTimeLapsus t;
    fd_set fileDescriptorSet;
    struct timeval timeStruct;
    closed = false;

    timeStruct.tv_sec = timeoutMSecs / 1000;
    timeStruct.tv_usec = 0;

    FD_ZERO(&fileDescriptorSet);
    FD_SET(m_socket, &fileDescriptorSet);

    int sel = select(m_socket + 1, &fileDescriptorSet, NULL, NULL, &timeStruct);

    if(sel == 0)
    {
        LogDebug("[CPhpResponseReader::WaitUntilReadable] select() returned 0, no data available");
        elapsedMSecs = t.GetElapsedMilis();
        return false;
    }
    else if(sel == -1)
    {
        if(errno ==  EBADF)
        {
            LogDebug("[CPhpResponseReader::WaitUntilReadable] select() returned -1, errno is EBADF, connection reset by host?");
            closed = true;
            elapsedMSecs = t.GetElapsedMilis();
            return true;
        }

        throw "CPhpResponseReader::WaitUntilReadable select error";
    }

    elapsedMSecs = t.GetElapsedMilis();
    return true;
}

bool WaitUntilReadableEpoll(int timeoutMSecs, int& elapsedMSecs, bool& closed)
{
    CIoPoller poller(8);
    CTimeLapsus t;
    closed = false;

    if(poller.Add(m_socket, EPOLLIN) == -1)
        LogError("[CPhpResponseReader::WaitUntilReadableEpoll] poller.Add(%d, EPOLLIN) failed", m_socket);

    int nfds = poller.Wait(timeoutMSecs);
    if (nfds > 0)
    {
        int theSocket = poller.GetEvents()[0].data.fd;
        uint32_t event = poller.GetEvents()[0].events;
        if(theSocket != m_socket)
        {
            LogError("[CPhpResponseReader::WaitUntilReadableEpoll] socket is different than expected", m_socket);
            elapsedMSecs = t.GetElapsedMilis();
            return false;
        }
        if((event & EPOLLERR) || (event & EPOLLHUP))
        {
            LogWarning("[CPhpResponseReader::WaitUntilReadableEpoll] Disconnected socket %d (event %d)", m_socket, event);
            elapsedMSecs = t.GetElapsedMilis();
            closed = true;
            return false;
        }
        if(event & EPOLLIN)
        {
            // ok
        }
    }
    else if (nfds == -1)
    {
        if(errno ==  EBADF)
        {
            LogWarning("[CPhpResponseReader::WaitUntilReadableEpoll] poller.Wait() returned -1, errno is EBADF, maybe connection reset by host");
            closed = true;
            elapsedMSecs = t.GetElapsedMilis();
            return true;
        }
        LogError("[CPhpResponseReader::WaitUntilReadableEpoll] poller.Wait() failed");
        elapsedMSecs = t.GetElapsedMilis();
        closed = true;
        return false;
    }
    else
    {
        LogDebug("[CPhpResponseReader::WaitUntilReadableEpoll] poller.Wait() returned 0, no data available");
        elapsedMSecs = t.GetElapsedMilis();
        return false;
    }

    elapsedMSecs = t.GetElapsedMilis();
    return true;
}

CIOPoller は、epoll の単なる C++ ラッパーです。

Ubuntu のバージョン:

Distributor ID: Ubuntu
Description:    Ubuntu 12.04.1 LTS
Release:    12.04
于 2012-12-08T16:10:41.167 に答える