7

実行中のアプリケーションを考慮して、現在登録されている完全なハンドラーに関する情報を抽出したいと考えています。

ハンドラーはクラス A によって登録されています。例:

boost::asio::async_read(s, b, boost::bind(&A::F, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));

デバッガーの下で、適切な io_service 変数にアクセスできます。まだ完了していない操作の (A::F, this, s, b) を計算する方法。

4

1 に答える 1

23

質問の範囲を少し広げて、最終的な目標であると私が信じているもの、つまり非同期ハンドラーのデバッグの代替案をカバーしたいと思います。


サンプルプログラム

例を介してデバッグを示すために、ポート 4321 をリッスンする基本的な UDP エコー サーバーから始めましょう。

#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

using boost::asio::ip::udp;

class udp_echo
{
public:
  udp_echo(boost::asio::io_service& service,
           unsigned int port)
    : socket_(service, udp::endpoint(udp::v4(), port))
  {
    socket_.async_receive_from(
      boost::asio::buffer(buffer_), sender_,
      boost::bind(&udp_echo::handle_receive, this,
                  boost::asio::placeholders::error,
                  boost::asio::placeholders::bytes_transferred));
  }

  void handle_receive(const boost::system::error_code& error,
                      std::size_t bytes_transferred)
  {
    socket_.async_send_to(
      boost::asio::buffer(buffer_, bytes_transferred), sender_,
      boost::bind(&udp_echo::handle_send, this,
                  boost::asio::placeholders::error,
                  boost::asio::placeholders::bytes_transferred));
  }

  void handle_send(const boost::system::error_code& error,
                   std::size_t bytes_transferred)
  {
    socket_.close();  
  }

private:
  udp::socket socket_;
  boost::array<char, 128> buffer_;
  udp::endpoint sender_;
};

int main()
{
  boost::asio::io_service service;
  udp_echo echo(service, 4321);
  service.run();
}

この単純なプログラムには、単一の非同期呼び出しチェーンがあります。

udp_echo::udp_echo()
{
  socket_.async_receive_from(...); --.
}                                    |
             .-----------------------'
             v
void udp_echo::handle_receive(...)
{
  socket_.async_send_to(...);  ------.
}                                    |
             .-----------------------'
             v
void udp_echo::handle_send()
{
  socket_.close(); 
}

ハンドラ追跡

Boost 1.47 では、ハンドラー トラッキングが導入されました。定義するだけBOOST_ASIO_ENABLE_HANDLER_TRACKINGで、Boost.Asio はタイムスタンプを含むデバッグ出力を標準エラー ストリームに書き込みます。プログラミングを実行し、UDP 経由で「hello world」を送信すると、次の出力が生成されました。

@asio|1363273821.846895|0*1|socket@0xbf8c4e3c.async_receive_from // 1
@asio|1363273829.288883|>1|ec=system:0,bytes_transferred=12      // 2
@asio|1363273829.288931|1*2|socket@0xbf8c4e3c.async_send_to      // 3
@asio|1363273829.289013|<1|                                      // 4
@asio|1363273829.289026|>2|ec=system:0,bytes_transferred=12      // 5
@asio|1363273829.289035|2|socket@0xbf8c4e3c.close                // 6
@asio|1363273829.289075|<2|                                      // 7

次のように行ごとに読み取ることができます。

  1. 非ハンドラ (0) が呼び出されsocket.async_receive_from()、ハンドラ 1 が作成されました。
  2. エラーなしでハンドラ 1socket.async_receive_from()に入り、12 バイトが受信されました。
  3. ハンドラ 1がsocket.async_receive_from()呼び出されsocket.async_send_to()、ハンドラ 2 が作成されました。
  4. ハンドラ 1 を終了しsocket.async_receive_from()ます。
  5. エラーなしでハンドラ 2socket.async_send_to()に入り、12 バイトが送信されました。
  6. ハンドラ 2 が呼び出されsocket.close()ました。
  7. ハンドラ 2 を終了しsocket.async_send_to()ます。

そして、視覚的に次のようにマップします。

udp_echo::udp_echo()
{
  socket_.async_receive_from(...); --. // 1
}                                    |
             .-----------------------'
             v
void udp_echo::handle_receive(...)
{                                      // 2
  socket_.async_send_to(...);  ------. // 3
}                                    | // 4
             .-----------------------'
             v
void udp_echo::handle_send()
{                                      // 5
  socket_.close();                     // 6
}                                      // 7

GDB

GDB を介してデバッグするには、複数のレイヤーを掘り下げる必要があります。Boost.Asio の実装の詳細のいくつかを理解しておくと役に立ちます。以下にいくつかの概念を示します。

  • io_service実行する準備ができているハンドラーのみが含まれます。
  • reactorは通常、作業操作と、実行する準備ができていない完了ハンドラーへのハンドルが含まれます。
  • reactor自身を に登録しますio_service

デバッグ セッションは次のとおりです。

(gdb) bt
#0  0x00ab1402 in __kernel_vsyscall ()
#1  0x00237ab8 in __epoll_wait_nocancel () from /lib/libc.so.6
#2  0x080519c3 in boost::asio::detail::epoll_reactor::run (this=0x80560b0, 
    block=true, ops=...)
    at /opt/boost/include/boost/asio/detail/impl/epoll_reactor.ipp:392
#3  0x08051c2d in boost::asio::detail::task_io_service::do_run_one (
    this=0x8056030, lock=..., this_thread=..., ec=...)
    at /opt/boost/include/boost/asio/detail/impl/task_io_service.ipp:396
#4  0x08051e8a in boost::asio::detail::task_io_service::run (this=0x8056030, 
    ec=...)
    at /opt/boost/include/boost/asio/detail/impl/task_io_service.ipp:153
#5  0x08051f50 in boost::asio::io_service::run (this=0xbfffe818)
    at /opt/boost/include/boost/asio/impl/io_service.ipp:59
#6  0x08049a44 in main () at example.cpp:48
(gdb) frame 6
#6  0x08049a44 in main () at example.cpp:48
48        service.run();

まず、リアクター サービスを特定する必要があります。ダウンキャストが発生する必要があるため、デバッガーを使用していくつかの型を見つけてみましょう。

(gdb) p service.service_registry_.init_keytab
init_key
init_key<boost::asio::datagram_socket_service<boost::asio::ip::udp> >
init_key< boost::asio::detail::epoll_reactor >
init_key<boost::asio::detail::task_io_service>

各キーは特定のサービスに関連付けられており、すべてのサービスは 内のリンクされたリストに保持されますservice.service_registry_。タイプ情報が関連付けられているため、目的のサービスを識別できます。

(gdb) set $service = service.service_registry_.first_service_
(gdb) p $service.key_.type_info_.__name
$1 = 0x8052b60
"N5boost4asio6detail14typeid_wrapperINS0_23datagram_socket_serviceINS0_2ip3udpEEEEE"

これが ですboost::asio::datagram_socket_service<boost::asio::ip::udp>。次に進みます。

(gdb) set $service = $service.next_
(gdb) p $service.key_.type_info_.__name
$2 = 0x8052cc0 "N5boost4asio6detail14typeid_wrapperINS1_13epoll_reactorEEE"

$service現在、原子炉サービスを指しています。init_keytype 引数に基づいて、サービスをダウンキャストします。

(gdb) set $service = *(' boost::asio::detail::epoll_reactor '*) $service

作業を伴う未処理のハンドラーは、リアクター内の操作のリンクされたリストにあります。

(gdb) set $ops = $service.registered_descriptors_.live_list_.op_queue_
(gdb) set $op = $ops.front_
(gdb) p *$op
$3 = {<boost::asio::detail::task_io_service_operation> = {next_ = 0x0,
    func_ = 0x804c256
    < boost::asio::detail::reactive_socket_recvfrom_op<
    ブースト::asio::mutable_buffers_1、ブースト::asio::ip::basic_endpoint<
    boost::asio::ip::udp>, boost::_bi::bind_t<void,
    boost::_mfi::mf2<void, udp_echo, boost::system::error_code const&,
    unsigned int>, boost::_bi::list3<boost::_bi::value<udp_echo*>,
    boost::arg<1> (*)()、boost::arg<2> (*)()> > > ::
    do_complete(boost::asio::io_service::io_service_impl*,
    boost::asio::detail::epoll_reactor::descriptor_state::operation*,
    boost::system::error_code const&, size_t)>, task_result_ = 0}, ec_ = {
    m_val = 11, m_cat = 0x13b2c8}, bytes_transferred_ = 0, perform_func_ =
    0x80514c8 <boost::asio::detail::reactive_socket_recvfrom_op_base<
    ブースト::asio::mutable_buffers_1、  
    boost::asio::ip::basic_endpoint<boost::asio::ip::udp>
    >::do_perform(boost::asio::detail::reactor_op*)>}

別のダウンキャストが必要です。メンバー関数ポインターが属する$opクラスにキャストします。func_

(gdb) set $op = *(' boost::asio::detail::reactive_socket_recvfrom_op<
ブースト::asio::mutable_buffers_1、ブースト::asio::ip::basic_endpoint<
boost::asio::ip::udp>、boost::_bi::bind_t<void、boost::_mfi::mf2<
void、udp_echo、boost::system::error_code const&、unsigned int>、
 boost::_bi::list3<boost::_bi::value<udp_echo*>,
 ブースト::arg<1> (*)()、ブースト::arg<2> (*)()> > > '*) $op

この操作には、必要な情報が含まれています。

バッファ:

(gdb) p $op.buffers_ 
$4 = {<boost::asio::mutable_buffer> = {data_ = 0xbfffe77c, 
    size_ = 128}, <No data fields>}
(gdb) p &echo.buffer_
$5 = (boost::array<char, 128u> *) 0xbfffe77c

thisインスタンス:

(gdb) p $op.handler_.l_.a1_.t_ 
$6 = (udp_echo *) 0xbfffe768
(gdb) p &echo
$7 = (udp_echo *) 0xbfffe768

メンバー関数ポインター:

(gdb) p $op.handler_.f_.f_
$8 = (void (udp_echo::*)(udp_echo *, const boost::system::error_code &, 
    unsigned int)) 0x80505b0 <
    udp_echo::handle_receive(boost::system::error_code const&, size_t)>

ソケット情報:

(gdb) p $op.socket_ 
$9 = 10
(gdb) p echo.socket_.implementation.socket_ 
$10 = 10

この場合、操作はネイティブ ソケット表現 (ファイル記述子) についてのみ認識します。それがどのソケットであるかを判別する 1 つの便利な方法は、lsof を照会することです。

$/usr/sbin/lsof -i -P | grep a.out 
a.out     4265 ghost   10u  IPv4 1166143       UDP *:4321

したがって、ファイル記述子 10 は UDP 4321 でリッスンしています。

于 2013-03-14T19:03:59.870 に答える