• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            loop_in_codes

            低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

            基于內存查看STL常用容器內容

            有時候在線上使用gdb調試程序core問題時,可能沒有符號文件,拿到的僅是一個內存地址,如果這個指向的是一個STL對象,那么如何查看這個對象的內容呢?

            只需要知道STL各個容器的數據結構實現,就可以查看其內容。本文描述了SGI STL實現中常用容器的數據結構,以及如何在gdb中查看其內容。

            string

            string,即basic_string bits/basic_string.h

            mutable _Alloc_hider  _M_dataplus;
                ... 
                  const _CharT*
                  c_str() const
                  { return _M_data(); }
                ...    
                  _CharT*
                  _M_data() const 
                  { return  _M_dataplus._M_p; }
            
                ...
                  struct _Alloc_hider : _Alloc
                  {
                _Alloc_hider(_CharT* __dat, const _Alloc& __a)
                : _Alloc(__a), _M_p(__dat) { }
            
                _CharT* _M_p; // The actual data.
                  };
               
                  size_type
                  length() const
                  { return _M_rep()->_M_length; }
            
                  _Rep*
                  _M_rep() const
                  { return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); }
            
                  ...
                   struct _Rep_base
                  {
                size_type       _M_length;
                size_type       _M_capacity;
                _Atomic_word        _M_refcount;
                  };
            
                  struct _Rep : _Rep_base

            即,string內有一個指針,指向實際的字符串位置,這個位置前面有一個_Rep結構,其內保存了字符串的長度、可用內存以及引用計數。當我們拿到一個string對象的地址時,可以通過以下代碼獲取相關值:

            void ds_str_i(void *p) {
                    char **raw = (char**)p;
                    char *s = *raw;
                    size_t len = *(size_t*)(s - sizeof(size_t) * 3);
                    printf("str: %s (%zd)\n", s, len);
                }
            
                size_t ds_str() {
                    std::string s = "hello";
                    ds_str_i(&s);
                    return s.size();
                }

            在gdb中拿到一個string的地址時,可以以下打印出該字符串及長度:

            (gdb) x/1a p
            0x7fffffffe3a0: 0x606028
            (gdb) p (char*)0x606028
            $2 = 0x606028 "hello"
            (gdb) x/1dg 0x606028-24
            0x606010:       5
            

            vector

            眾所周知vector實現就是一塊連續的內存,bits/stl_vector.h

            template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
                class vector : protected _Vector_base<_Tp, _Alloc>
            
                ...
                template<typename _Tp, typename _Alloc>
                struct _Vector_base
                {
                  typedef typename _Alloc::template rebind<_Tp>::other _Tp_alloc_type;
            
                  struct _Vector_impl
                  : public _Tp_alloc_type
                  {
                _Tp*           _M_start;
                _Tp*           _M_finish;
                _Tp*           _M_end_of_storage;
                _Vector_impl(_Tp_alloc_type const& __a)
                : _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0)
                { }
                  };
            
            
                  _Vector_impl _M_impl;

            可以看出sizeof(vector<xxx>)=24,其內也就是3個指針,_M_start指向首元素地址,_M_finish指向最后一個節點+1,_M_end_of_storage是可用空間最后的位置。

            iterator
                  end()
                  { return iterator (this->_M_impl._M_finish); }
                  const_iterator
                  ...
                  begin() const
                  { return const_iterator (this->_M_impl._M_start); }
                  ...
                  size_type
                  capacity() const
                  { return size_type(const_iterator(this->_M_impl._M_end_of_storage)
                         - begin()); }

            可以通過代碼從一個vector對象地址輸出其信息:

            template <typename T>
                void ds_vec_i(void *p) {
                    T *start = *(T**)p;
                    T *finish = *(T**)((char*)p + sizeof(void*));
                    T *end_storage = *(T**)((char*)p + 2 * sizeof(void*));
                    printf("vec size: %ld, avaiable size: %ld\n", finish - start, end_storage - start); 
                }
            
                size_t ds_vec() {
                    std::vector<int> vec;
                    vec.push_back(0x11);
                    vec.push_back(0x22);
                    vec.push_back(0x33);
                    ds_vec_i<int>(&vec);
                    return vec.size();
                }

            使用gdb輸出一個vector中的內容:

            (gdb) p p
            $3 = (void *) 0x7fffffffe380
            (gdb) x/1a p
            0x7fffffffe380: 0x606080
            (gdb) x/3xw 0x606080
            0x606080:       0x00000011      0x00000022      0x00000033
            

            list

            眾所周知list被實現為一個鏈表。準確來說是一個雙向鏈表。list本身是一個特殊節點,其代表end,其指向的下一個元素才是list真正的第一個節點:

            bits/stl_list.h

            bool
                  empty() const
                  { return this->_M_impl._M_node._M_next == &this->_M_impl._M_node; }
            
                  const_iterator
                  begin() const
                  { return const_iterator(this->_M_impl._M_node._M_next); }
            
                  iterator
                  end()
                  { return iterator(&this->_M_impl._M_node); }
            
                  ...
            
                struct _List_node_base
                {
                    _List_node_base* _M_next;   ///< Self-explanatory
                    _List_node_base* _M_prev;   ///< Self-explanatory
                    ...
                };
                     
                template<typename _Tp>
                struct _List_node : public _List_node_base
                {
                  _Tp _M_data;                ///< User's data.
                };
                  
                template<typename _Tp, typename _Alloc>
                class _List_base
                {
                    ...
                  struct _List_impl
                  : public _Node_alloc_type
                  {
                _List_node_base _M_node;
                    ...
                  };
            
                  _List_impl _M_impl;
            
                      
                template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
                class list : protected _List_base<_Tp, _Alloc>

            所以sizeof(list<xx>)=16,兩個指針。每一個真正的節點首先是包含兩個指針,然后是元素內容(_List_node)。

            通過代碼輸出list的內容:

            #define NEXT(ptr, T) do { \
                    void *n = *(char**)ptr; \
                    T val = *(T*)((char**)ptr + 2); \
                    printf("list item %p val: 0x%x\n", ptr, val); \
                    ptr = n; \
                } while (0)
            
                template <typename T>
                void ds_list_i(void *p) {
                    void *ptr = *(char**)p;
            
                    NEXT(ptr, T);
                    NEXT(ptr, T);
                    NEXT(ptr, T);
                }
            
                size_t ds_list() {
                    std::list<int> lst;
                    lst.push_back(0x11);
                    lst.push_back(0x22);
                    lst.push_back(0x33);
                    ds_list_i<int>(&lst);
                    return lst.size();
                }

            在gdb中可以以下方式遍歷該list:

            (gdb) p p
            $4 = (void *) 0x7fffffffe390
            (gdb) x/1a p
            0x7fffffffe390: 0x606080
            (gdb) x/1xw 0x606080+16         # 元素1 
            0x606090:       0x00000011
            (gdb) x/1a 0x606080
            0x606080:       0x6060a0
            (gdb) x/1xw 0x6060a0+16         # 元素2
            0x6060b0:       0x00000022
            

            map

            map使用的是紅黑樹實現,實際使用的是stl_tree.h實現:

            bits/stl_map.h

            typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,
                           key_compare, _Pair_alloc_type> _Rep_type;
                ...
                 _Rep_type _M_t;
                ... 
            
                  iterator
                  begin()
                  { return _M_t.begin(); }

            bits/stl_tree.h

            struct _Rb_tree_node_base
                  {
                    typedef _Rb_tree_node_base* _Base_ptr;
                    typedef const _Rb_tree_node_base* _Const_Base_ptr;
            
                    _Rb_tree_color  _M_color;
                    _Base_ptr       _M_parent;
                    _Base_ptr       _M_left;
                    _Base_ptr       _M_right;
                    
                    ...
                  };
            
                template<typename _Val>
                struct _Rb_tree_node : public _Rb_tree_node_base
                {
                  typedef _Rb_tree_node<_Val>* _Link_type;
                  _Val _M_value_field;
                };
            
            
                template<typename _Key_compare,
                       bool _Is_pod_comparator = std::__is_pod<_Key_compare>::__value>
                    struct _Rb_tree_impl : public _Node_allocator
                    {
                  _Key_compare      _M_key_compare;
                  _Rb_tree_node_base    _M_header;
                  size_type         _M_node_count; // Keeps track of size of tree.
                  ...
                    }
                
                _Rb_tree_impl<_Compare> _M_impl;
                ...
            
                  iterator
                  begin()
                  {
                return iterator(static_cast<_Link_type>
                        (this->_M_impl._M_header._M_left));
                  }

            所以可以看出,大部分時候(取決于_M_key_compare) sizeof(map<xx>)=48,主要的元素是:

            _Rb_tree_color  _M_color; // 節點顏色
                    _Base_ptr       _M_parent; // 父節點
                    _Base_ptr       _M_left; // 左節點
                    _Base_ptr       _M_right; // 右節點
                    _Val            _M_value_field // 同list中節點技巧一致,后面是實際的元素

            同list中的實現一致,map本身作為一個節點,其不是一個存儲數據的節點,

            _Rb_tree::end

            iterator
                  end()
                  { return iterator(static_cast<_Link_type>(&this->_M_impl._M_header)); }

            由于節點值在_Rb_tree_node_base后,所以任意時候拿到節點就可以偏移這個結構體拿到節點值,節點的值是一個pair,包含了key和value。

            在gdb中打印以下map的內容:

            size_t ds_map() {
                    std::map<std::string, int> imap;
                    imap["abc"] = 0xbbb;
                    return imap.size();
                }
            (gdb) p/x &imap
            $7 = 0x7fffffffe370
            (gdb) x/1a (char*)&imap+24       # _M_left 真正的節點
            0x7fffffffe388: 0x606040          
            (gdb) x/1xw 0x606040+32+8        # 偏移32字節是節點值的地址,再偏移8則是value的地址
            0x606068:       0x00000bbb
            (gdb) p *(char**)(0x606040+32)   # 偏移32字節是string的地址
            $8 = 0x606028 "abc"
            

            或者很多時候沒有必要這么裝逼+蛋疼:

            (gdb) p *(char**)(imap._M_t._M_impl._M_header._M_left+1)
            $9 = 0x606028 "abc"
            (gdb) x/1xw (char*)(imap._M_t._M_impl._M_header._M_left+1)+8
            0x606068:       0x00000bbb
            

            posted on 2014-12-03 22:08 Kevin Lynx 閱讀(3836) 評論(2)  編輯 收藏 引用 所屬分類: c/c++

            評論

            # re: 基于內存查看STL常用容器內容 2014-12-04 18:57 xxoo

            問一下,既然string的結構類型是
            (size_t,size_t,size_t,char*)
            為啥string地址里前4個自己是char*的地址,應該是size_t的前4個字節才對啊?  回復  更多評論   

            # re: 基于內存查看STL常用容器內容 2014-12-07 18:27 Kevin Lynx

            @xxoo
            "即,string內有一個指針,指向實際的字符串位置,這個位置前面有一個_Rep結構,其內保存了字符串的長度、可用內存以及引用計數。"  回復  更多評論   

            婷婷久久综合| 狠狠人妻久久久久久综合蜜桃 | 久久无码AV中文出轨人妻| 婷婷久久综合九色综合九七| 精品国产乱码久久久久久呢| 国产一区二区三区久久| 久久这里只有精品视频99| 亚洲精品蜜桃久久久久久| 国产激情久久久久影院小草 | 国产精品久久久久影院嫩草| 久久99精品国产麻豆婷婷| 伊人久久无码中文字幕| 国产ww久久久久久久久久| 久久久av波多野一区二区| 亚洲国产成人久久综合碰| 久久99国产精品久久| 亚洲中文久久精品无码ww16| 久久精品国产只有精品66| 99久久99久久精品免费看蜜桃| 久久人人爽人人爽人人片AV麻豆 | 99久久国产热无码精品免费久久久久| 亚洲综合久久夜AV | 久久黄色视频| 国产精品伦理久久久久久| 91视频国产91久久久| 国产A级毛片久久久精品毛片| 天天影视色香欲综合久久| 狠狠色伊人久久精品综合网| 日本免费一区二区久久人人澡| 色综合久久中文字幕无码| 精品一二三区久久aaa片| 久久国产欧美日韩精品免费| 日韩中文久久| 亚洲一区精品伊人久久伊人 | 亚洲伊人久久成综合人影院| 久久国产精品波多野结衣AV| 久久99精品免费一区二区| Xx性欧美肥妇精品久久久久久| 亚洲国产成人久久精品动漫| 久久精品国内一区二区三区| 免费国产99久久久香蕉|