Search code examples
c++boostgdbmulti-index

How to see boost::multi_index hashed index's data using gdb


I'd like to see the data contained by boost::multi_index(version 1.67.0) using gdb. First I tried https://github.com/ruediger/Boost-Pretty-Printer. It seems that hashed indexes such as hashed_unique are not supported.

I noticed that if the first index is a supported type such as sequenced, Boost-Pretty-Printer works fine. However, I cannot edit the code now. I need to debug a core file and a binary executable.

I tried to understand the internal structure of multi_index with hashed index.

I worte the following test code:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>

namespace mi = boost::multi_index;

struct t_hash{};

using elems = mi::multi_index_container<
    int,
    mi::indexed_by<
        mi::hashed_unique<
            mi::tag<t_hash>,
            mi::identity<int>
        >
    >
>;

int main() {
    elems es { 0x12, 0x34 };
    return 0; // set break point here and (gdb) p es
}

https://wandbox.org/permlink/UtMfVRI4rT5AXUOZ

When I print es, (gdb) p es I got the following output:

$1 = {
  <boost::base_from_member<std::allocator<boost::multi_index::detail::hashed_index_node<boost::multi_index::detail::index_node_base<int, std::allocator<int> >, boost::multi_index::detail::hashed_unique_tag> >, 0>> = {
    member = {
      <__gnu_cxx::new_allocator<boost::multi_index::detail::hashed_index_node<boost::multi_index::detail::index_node_base<int, std::allocator<int> >, boost::multi_index::detail::hashed_unique_tag> >> = {<No data fields>}, <No data fields>}
  },
  <boost::multi_index::detail::header_holder<boost::multi_index::detail::hashed_index_node<boost::multi_index::detail::index_node_base<int, std::allocator<int> >, boost::multi_index::detail::hashed_unique_tag>*, boost::multi_index::multi_index_container<int, boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::tag<t_hash, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, boost::multi_index::identity<int>, mpl_::na, mpl_::na>, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, std::allocator<int> > >> = {
    <boost::noncopyable_::noncopyable> = {<No data fields>},
    members of boost::multi_index::detail::header_holder<boost::multi_index::detail::hashed_index_node<boost::multi_index::detail::index_node_base<int, std::allocator<int> >, boost::multi_index::detail::hashed_unique_tag>*, boost::multi_index::multi_index_container<int, boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::tag<t_hash, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, boost::multi_index::identity<int>, mpl_::na, mpl_::na>, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, std::allocator<int> > >:
    member = 0x55555576ee70
  },
  <boost::multi_index::detail::hashed_index<boost::multi_index::identity<int>, boost::hash<int>, std::equal_to<int>, boost::multi_index::detail::nth_layer<1, int, boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::tag<t_hash, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, boost::multi_index::identity<int>, mpl_::na, mpl_::na>, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, std::allocator<int> >, boost::mpl::v_item<t_hash, boost::mpl::vector0<mpl_::na>, 0>, boost::multi_index::detail::hashed_unique_tag>> = {
    <boost::multi_index::detail::index_base<int, boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::tag<t_hash, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, boost::multi_index::identity<int>, mpl_::na, mpl_::na>, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, std::allocator<int> >> = {<No data fields>},
    members of boost::multi_index::detail::hashed_index<boost::multi_index::identity<int>, boost::hash<int>, std::equal_to<int>, boost::multi_index::detail::nth_layer<1, int, boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::tag<t_hash, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, boost::multi_index::identity<int>, mpl_::na, mpl_::na>, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, std::allocator<int> >, boost::mpl::v_item<t_hash, boost::mpl::vector0<mpl_::na>, 0>, boost::multi_index::detail::hashed_unique_tag>:
    key = {
      <boost::multi_index::detail::non_const_identity_base<int>> = {<No data fields>}, <No data fields>},
    hash_ = {
      <boost::hash_detail::hash_base<int>> = {
        <std::unary_function<int, unsigned long>> = {<No data fields>}, <No data fields>}, <No data fields>},
    eq_ = {
      <std::binary_function<int, int, bool>> = {<No data fields>}, <No data fields>},
    buckets = {
      <boost::multi_index::detail::bucket_array_base<true>> = {
        <boost::noncopyable_::noncopyable> = {<No data fields>},
      },
      members of boost::multi_index::detail::bucket_array<std::allocator<int> >:
      size_index_ = 0,
      spc = {
        <boost::noncopyable_::noncopyable> = {<No data fields>},
        members of boost::multi_index::detail::auto_space<boost::multi_index::detail::hashed_index_base_node_impl<std::allocator<char> >, std::allocator<int> >:
        al_ = {
          <__gnu_cxx::new_allocator<boost::multi_index::detail::hashed_index_base_node_impl<std::allocator<char> > >> = {<No data fields>}, <No data fields>},
        n_ = 54,
        data_ = 0x55555576ee90
      }
    },
    mlf = 1,
    max_load = 53
  },
  members of boost::multi_index::multi_index_container<int, boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::tag<t_hash, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, boost::multi_index::identity<int>, mpl_::na, mpl_::na>, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, std::allocator<int> >:
  node_count = 2
}

I found node_count = 2. It seems that the number of elements. I tried digging the member variables. e.g.) member, buckets, and so on. But I cannot found the data 0x12 and 0x34, so far.

How do I get them?


Edit: 2018/07/28 11:51 JST

Thanks for the comments, I found the solution. I summarize two approaches based on the comments.

Runtime shared library loading based on @sehe 's approach,

  1. Write a debug print function (debug_print()) as a shared library (dp.so).
  2. run gdb. gdb target_executable_file
  3. Do set environment LD_PRELOAD ./dp.so.
  4. Set breakpoints.
  5. Do r
  6. When the breakpoint hits, do call debug_print(data). data is the target you want to see.

This approach does't need to re-compile the target. However, when I load core file, debug_print() is no longer on memory. So this approach doesn't work with core file. I'm looking for forcibly load dp.so after core-file loaded, but I couldn't find the way, so far.

Trace multi_index's internal structure using gdb

This approach is work with core-file. This approach is based on @Joaquín M López Muñoz 's comment.

  1. Run gdb with core file. gdb target_executable_file core_file.
  2. Do the following command to access the data. T is multi_index's element type. data is multi_index container variable. N is number of indexes.

1st data

p *(T*)((char*)(*data.member).prior_ - sizeof(T) - 0x10 * (N - 1))

2nd data

p *(T*)((char*)(*(*data.member).prior_).prior_ - sizeof(T) - 0x10 * (N - 1))

...follow the same pattern.

Thank you very much @sele and @Joaquín M López Muñoz !!


Edit: 2018/07/28 15:22 JST

I implemented hashed indexes support for Boost-Pretty-Printer. It is based on the above approach.

I sent the pull request for that:

https://github.com/ruediger/Boost-Pretty-Printer/pull/36


Edit: 2018/07/28 15:42 JST

I figured out what 0x10 mean. It is pointer size multiplies 2. So in 64bit environment, 64bit = 8byte, 8 * 2 = 16 = 0x10. In 32bit environment 32bit = 4byte, 4 * 2 = 8 = 0x08.

I also updated the pull request.


Edit: 2018/08/02 09:30 JST

Finally, I sent two pull requests and both are merged. Now, we can simple use Boost-Pretty-Printer and print the multi_index container that has hashed_index as the first index.

https://github.com/ruediger/Boost-Pretty-Printer/pull/36

https://github.com/ruediger/Boost-Pretty-Printer/pull/37

Here is internal structure and iterating algorithm:

https://speakerdeck.com/redboltz/boost-multi-index-version-equals-1-dot-56-dot-0-internal-structure-and-iteration-algorithm-for-gdb-boost-prerry-printer


Solution

  • Solution for new Boost version

    If you are using Boost 1.56.0 or later, you can print the container using Boost-Pretty-Printer.

    Install Boost-Pretty-Printer

    The github site is https://github.com/ruediger/Boost-Pretty-Printer

    Installation manual is https://github.com/ruediger/Boost-Pretty-Printer#installation

    Do print command on gdb.

    Then, you can get the result.

    If you have already installed Boost-Pretty-Printer and couldn't getpretty print result, you need to update it.

    hashed_index support is introduced since https://github.com/ruediger/Boost-Pretty-Printer/commit/d8557f664e0dd3d11bb0464d8f670e99946e88b9

    Solution for old Boost version

    If you are using Boost version less than 1.56.0, you have some choices.

    1. Updating boost.

    Just update your boost library and re-compile your program.

    Pros

    • If you can do, it's the easiest way.

    Cons

    • It requires re-compile the target.
    • It might introduce comatibility problem to your application.
    • It doesn't work with existing binary executable and core file.

    2. Dynamic loading shared library

    Writing debug print function. Here is the code example:

    dp.cpp

    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/hashed_index.hpp>
    #include <boost/multi_index/identity.hpp>
    
    namespace mi = boost::multi_index;
    
    struct t_hash{};
    
    // define the same structure of the debug target
    using elems = mi::multi_index_container<
        int,
        mi::indexed_by<
            mi::hashed_unique<
                mi::tag<t_hash>,
                mi::identity<int>
            >
        >
    >;
    
    #include <iostream>
    
    // debug print function
    void dp(elems const& es) {
        for (auto const& e : es) {
            std::cout << e << std::endl;
        }
    }
    

    Compile it using the same compiler option as the target. And add -fPIC for shared library.

    clang++ -g -c dp.cpp -fPIC

    Then you get dp.o

    Link it as a shared library

    clang++ -shared -o dp.so dp.o

    Then you get dp.so

    Run gdb. gdb target_executable_file

    Do set environment LD_PRELOAD ./dp.so.

    Set breakpoints.

    Do r

    When the breakpoint hits, do call dp(data). data is the target you want to see

    Pros

    • Re-compile the target is not required.

    Cons

    • Doesn't work with core file.

    3. Develop Boost-Pretty-Print support for older version

    Unfortunately, current Boost-Pretty-Print hashed_indexes supports only Boost 1.56.0 or later. But you can implement older support by yourself.

    Here is the data structure description that is focused on iteration and iteration algorithm for >= 1.56 version. I think that it is a good hint to implement older version iteration algorithm.

    https://speakerdeck.com/redboltz/boost-multi-index-version-equals-1-dot-56-dot-0-internal-structure-and-iteration-algorithm-for-gdb-boost-prerry-printer

    And my pull requests (merged). https://github.com/ruediger/Boost-Pretty-Printer/pull/36

    https://github.com/ruediger/Boost-Pretty-Printer/pull/37

    For < 1.56 version, I wrote the program to check the internal structure.

    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/hashed_index.hpp>
    #include <boost/multi_index/identity.hpp>
    #include <boost/multi_index/member.hpp>
    #include <boost/lexical_cast.hpp>
    
    #include <iostream>
    #include <string>
    
    namespace mi = boost::multi_index;
    
    struct t_hash{};
    
    using elems = mi::multi_index_container<
        int,
        mi::indexed_by<
            mi::hashed_non_unique<
                mi::tag<t_hash>,
                mi::identity<int>
            >
        >
    >;
    
    int main(int argc, char** argv) {
        auto size = boost::lexical_cast<int>(argv[1]);
        elems es;
        for (int i = 0; i != size; ++i) {
            es.insert(i);
        }
        return 0; // break here
    }
    

    And I got the following gdb output:

    b 30
    r 5
    
    p es.member
    0x555555770e70
    
    p (*es.member).next_
    0x555555771038
    
    p (*(*es.member).next_).next_
    0x555555770e78
    
    p (*(*(*es.member).next_).next_).next_
    0x555555771038
    
    ...
    
    
    x /200xb 0x555555771030
    
    0x555555771030: 0x30    0x10    0x77    0x55    0x55    0x55    0x00    0x00
    0x555555771038: 0x78    0x0e    0x77    0x55    0x55    0x55    0x00    0x00
    0x555555771040: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    0x555555771048: 0x21    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    
    
    0x555555771050: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    0x555555771058: 0x90    0x0e    0x77    0x55    0x55    0x55    0x00    0x00
    0x555555771060: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    0x555555771068: 0x21    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    
    
    0x555555771070: 0x01    0x00    0x00    0x00    0x00    0x00    0x00    0x00
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    0x555555771078: 0x98    0x0e    0x77    0x55    0x55    0x55    0x00    0x00
    0x555555771080: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    0x555555771088: 0x21    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    
    
    0x555555771090: 0x02    0x00    0x00    0x00    0x00    0x00    0x00    0x00
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    0x555555771098: 0xa0    0x0e    0x77    0x55    0x55    0x55    0x00    0x00
    0x5555557710a0: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    0x5555557710a8: 0x21    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    
    
    0x5555557710b0: 0x03    0x00    0x00    0x00    0x00    0x00    0x00    0x00
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    0x5555557710b8: 0xa8    0x0e    0x77    0x55    0x55    0x55    0x00    0x00
    0x5555557710c0: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    0x5555557710c8: 0x21    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    
    
    0x5555557710d0: 0x04    0x00    0x00    0x00    0x00    0x00    0x00    0x00
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    0x5555557710d8: 0xb0    0x0e    0x77    0x55    0x55    0x55    0x00    0x00
    0x5555557710e0: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    0x5555557710e8: 0x21    0xef    0x00    0x00    0x00    0x00    0x00    0x00
    
    0x5555557710f0: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    ...
    

    This information and @Joaquín M López Muñoz's comment also could be a good hint to implement the < 1.56 Boost-Pretty-Printer.

    Pros

    • Re-compile the target is not required.
    • Work well with core file.

    Cons

    • You need to implement Boost-Pretty-Printer support.