乐趣区

关于音视频:SRS带宽不足下内存泄漏

  • 最近解决了 SRS 中的一个 bug,特此记录一下。
  • SRS(4.0)服务器上,应用 RTMP 推流,在网页端用 webrtc 拉流。设置低带宽场景下存在内存大幅度继续一直上涨景象,应该是有内存透露。
  • 察看发现 2 个景象——1. 存上涨幅度与推流端码率成正比。2. 敞开 nack 后内存上涨幅度显著减小。
  • 管制台上打印 SRS 日志发现错误日志:

    [Warn][24154][x6w4gl27][62] handle udp pkt, count=1/1, err: code=1011 : size=104, data=[00 01 00 54 21 12 a4 42] : stun binding request failed : stun binding response send failed : sendto 
    thread [24154][x6w4gl27]: cycle() [src/app/srs_app_listener.cpp:630][errno=62]
    thread [24154][982648t3]: on_stun() [src/app/srs_app_rtc_conn.cpp:2113][errno=62]
    thread [24154][982648t3]: on_binding_request() [src/app/srs_app_rtc_conn.cpp:2773][errno=62]
    thread [24154][982648t3]: sendto() [src/app/srs_app_listener.cpp:347][errno=62]
  • 联合日志中代码调用门路,剖析可能是 SrsUdpMuxSocket::sendto 中 srs_error_new 中 new 的对象没有开释,批改函数代码将谬误间接返回:

    if (nb_write <= 0) {if (nb_write < 0 && errno == ETIME) {
          return err;
          //return srs_error_new(ERROR_SOCKET_TIMEOUT, "sendto timeout %d ms", srsu2msi(timeout));
      }   
    
      return srs_error_new(ERROR_SOCKET_WRITE, "sendto");
    }
  • 应用 gperf.gmp 工具剖析代码批改前和批改后的函数内存应用状况:

    // 原始代码剖析后果
    Using local file objs/srs.
    Using local file gperf.srs.gmp.0001.heap.
    Total: 9.8 MB
       2.3  23.2%  23.2%      2.3  23.2% SrsResourceManager::SrsResourceManager
       2.0  20.2%  43.5%      2.0  20.2% _st_new_stk_segment
       1.8  18.2%  61.7%      1.8  18.2% SrsCommonMessage::create_payload
       1.7  17.6%  79.4%      1.7  17.6% std::string::_Rep::_S_create
       1.4  14.5%  93.8%      1.4  14.5% SrsCplxError::create
       0.1   1.3%  95.1%      0.1   1.3% SrsFastStream::SrsFastStream
       0.1   0.7%  95.8%      0.1   0.7% av_malloc_array (inline)
       0.1   0.6%  96.4%      0.1   0.6% SrsUdpMuxSocket::SrsUdpMuxSocket
       0.1   0.6%  97.1%      0.1   0.6% SrsUdpMuxListener::SrsUdpMuxListener
    
    
    // 批改后代码剖析后果
    Using local file objs/srs.
    Using local file gperf.srs.gmp.0001.heap.
    Total: 6.8 MB
       2.3  33.7%  33.7%      2.3  33.7% SrsResourceManager::SrsResourceManager
       2.0  29.3%  63.0%      2.0  29.3% _st_new_stk_segment
       1.9  27.8%  90.8%      1.9  27.8% SrsCommonMessage::create_payload
       0.1   1.8%  92.7%      0.1   1.8% SrsFastStream::SrsFastStream
       0.1   1.0%  93.7%      0.1   1.1% av_malloc_array (inline)
       0.1   0.9%  94.6%      0.1   0.9% SrsUdpMuxSocket::SrsUdpMuxSocket
       0.1   0.9%  95.5%      0.1   0.9% SrsUdpMuxListener::SrsUdpMuxListener
  • 发现批改代码后 SrsCplxError::create 函数的内存应用就没有那么多了,联合代码根本就能定位问题,应该是有中央调用的 SrsCplxError::create,但没有在里面开释内存。查找所有调用了 SrsUdpMuxSocket::sendto 函数的中央发现华点——果然在 SrsRtcConnection::do_send_packet 函数中有一段代码没有解决这个谬误:

    // TODO: FIXME: Handle error.
    sendonly_skt->sendto(iov->iov_base, iov->iov_len, 0);
  • 这里加 trace 打印发现有频繁调用,而且开启 nack 后在弱网下调用更频繁,联合后面景象,根本能够确定问题就出在这里。批改如下:

    if ((err = sendonly_skt->sendto(iov->iov_base, iov->iov_len, 0)) != srs_success) {return srs_error_wrap(err, "send to");
    }
  • 批改后测试,同样场景下未见内存大幅度增长。
  • 这里不得不吐槽下 SRS 这种错误处理形式。首先说益处是,打印的谬误日志能够还原调用的过程,查找问题很不便。然而带来的问题就是,只有里面有一个中央没有 free 这个谬误指针就会存在内存透露。对于像 SrsUdpMuxSocket::sendto 这样频繁调用的函数内存增长还很显著,比拟好查,然而其余中央如果有这样的状况产生,然而内存只会一点点的增长,就很难查了。
  • 很奇怪这个问题应该很显著,而且作者还备注了须要批改,但我起初查看最新的代码这块也还没有批改,不晓得是基于什么思考,望大神指导。
退出移动版