dmp文件读取(五)- Memory


| 阅读 |,阅读约 5 分钟
| 复制链接:

Overview

dmp文件读取(五)

dmp文件中包含各种stream,前面已经介绍完线程、模块、异常这三种stream的读取,本章介绍另外一个很重要的stream:memory stream

Memory Stream概述

  • Memory Stream记录了进程崩溃时的内存信息
  • dmp中memory列表的stream类型为MD_MEMORY_LIST_STREAM,枚举值是5
  • memory分为很多的区域(region),每个区域使用MinidumpMemoryRegion对象表示

Memory Stream读取入口

  • dump的GetMemoryList方法,获取dmp文件中的内存信息
  • 读取的模块列表信息保存在MinidumpMemoryList
  • 所有内存信息会建立索引,保存在range_map_中
  • range_map_是一个范围map类型,breakpad中定义的,后续查询时,只要给定一个内存地址,能快速定位到属于哪个内存region
1ProcessResult MinidumpProcessor::Process(
2    Minidump *dump, ProcessState *process_state) {
3    ...
4    // 内存信息读取入口
5    MinidumpMemoryList *memory_list = dump->GetMemoryList();
6    ...
7}

MinidumpMemoryList

 1class MinidumpMemoryList : public MinidumpStream {
 2 public:
 3  // Sequential access to memory regions.
 4  MinidumpMemoryRegion* GetMemoryRegionAtIndex(unsigned int index);
 5
 6  // Random access to memory regions.  Returns the region encompassing
 7  // the address identified by address.
 8  virtual MinidumpMemoryRegion* GetMemoryRegionForAddress(uint64_t address);
 9
10  // Print a human-readable representation of the object to stdout.
11  void Print();
12
13 private:
14  // 内存区域数据在dmp文件中的描述符列表
15  typedef vector<MDMemoryDescriptor>   MemoryDescriptors;
16
17  // 内存区域对象列表
18  typedef vector<MinidumpMemoryRegion> MemoryRegions;
19
20  static const uint32_t kStreamType = MD_MEMORY_LIST_STREAM;
21
22  explicit MinidumpMemoryList(Minidump* minidump);
23
24  bool Read(uint32_t expected_size) override;
25
26  // The largest number of memory regions that will be read from a minidump.
27  // The default is 256.
28  static uint32_t max_regions_;
29
30  // Access to memory regions using addresses as the key.
31  RangeMap<uint64_t, unsigned int> *range_map_;
32
33  // The list of descriptors.  This is maintained separately from the list
34  // of regions, because MemoryRegion doesn't own its MemoryDescriptor, it
35  // maintains a pointer to it.  descriptors_ provides the storage for this
36  // purpose.
37  MemoryDescriptors *descriptors_;
38
39  // The list of regions.
40  MemoryRegions *regions_;
41  uint32_t region_count_;
42
43};

MinidumpMemoryRegion

  • MinidumpMemoryRegion表示每个内存区域实体对象
  • 核心数据结构为MDMemoryDescriptor
  • minidump文件中读取的数据存放到MDMemoryDescriptor结构中,并做一些初始化和索引操作,构成MinidumpMemoryRegion
 1class MinidumpMemoryRegion : public MinidumpObject,
 2                             public MemoryRegion {
 3 public:
 4
 5  // Returns a pointer to the base of the memory region.  Returns the
 6  // cached value if available, otherwise, reads the minidump file and
 7  // caches the memory region.
 8  const uint8_t* GetMemory() const;
 9
10  // The address of the base of the memory region.
11  uint64_t GetBase() const;
12
13  // The size, in bytes, of the memory region.
14  uint32_t GetSize() const;
15
16  // Frees the cached memory region, if cached.
17  void FreeMemory();
18
19  // Obtains the value of memory at the pointer specified by address.
20  bool GetMemoryAtAddress(uint64_t address, uint8_t*  value) const;
21  bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const;
22  bool GetMemoryAtAddress(uint64_t address, uint32_t* value) const;
23  bool GetMemoryAtAddress(uint64_t address, uint64_t* value) const;
24
25  // Print a human-readable representation of the object to stdout.
26  void Print() const;
27  void SetPrintMode(bool hexdump, unsigned int width);
28
29 protected:
30  explicit MinidumpMemoryRegion(Minidump* minidump);
31
32 private:
33
34  // Identify the base address and size of the memory region, and the
35  // location it may be found in the minidump file.
36  void SetDescriptor(MDMemoryDescriptor* descriptor);
37
38  // Implementation for GetMemoryAtAddress
39  template<typename T> bool GetMemoryAtAddressInternal(uint64_t address,
40                                                       T*        value) const;
41
42  // Knobs for controlling display of memory printing.
43  bool hexdump_;
44  unsigned int hexdump_width_;
45
46  // The largest memory region that will be read from a minidump.
47  static uint32_t max_bytes_;
48
49  // Base address and size of the memory region, and its position in the
50  // minidump file.
51  MDMemoryDescriptor* descriptor_;
52
53  // Cached memory.
54  mutable vector<uint8_t>* memory_;
55};

MDMemoryDescriptor

dmp文件中描述单个memory 区域的数据结构,包括:

  • start_of_memory_range:内存区域的起始地址
  • memory:内存数据在dmp中的位置
1typedef struct {
2  /* The base address of the memory range on the host that produced the
3   * minidump. */
4  uint64_t             start_of_memory_range;
5
6  MDLocationDescriptor memory;
7} MDMemoryDescriptor;  /* MINIDUMP_MEMORY_DESCRIPTOR */

Memory Read方法详解

  1bool MinidumpMemoryList::Read(uint32_t expected_size) {
  2  // 头4byte是区域的大小,所以读取二进制字节流的大小,一定是大于4byte的
  3  // 读取的字节数不能小于4byte,
  4  uint32_t region_count;
  5  if (expected_size < sizeof(region_count)) {
  6    BPLOG(ERROR) << "MinidumpMemoryList count size mismatch, " <<
  7                    expected_size << " < " << sizeof(region_count);
  8    return false;
  9  }
 10  // 前面章节介绍过,代码执行到这时,已经将文件读取的指针指到该stream的区域
 11  // 这里先读取头4byte,读出的数值为区域的数量
 12  if (!minidump_->ReadBytes(&region_count, sizeof(region_count))) {
 13    BPLOG(ERROR) << "MinidumpMemoryList could not read memory region count";
 14    return false;
 15  }
 16
 17  if (minidump_->swap())
 18    Swap(&region_count);
 19
 20  if (region_count >
 21          numeric_limits<uint32_t>::max() / sizeof(MDMemoryDescriptor)) {
 22    BPLOG(ERROR) << "MinidumpMemoryList region count " << region_count <<
 23                    " would cause multiplication overflow";
 24    return false;
 25  }
 26
 27  // 读取的字节大小是有固定公式计算的
 28  // 总字节数 = 4字节(表示region大小的int类型) + region数量 * 描述每个region的字节数(sizeof(MDMemoryDescriptor)
 29  // 不满足以上的字节数,出错返回
 30  if (expected_size != sizeof(region_count) +
 31                       region_count * sizeof(MDMemoryDescriptor)) {
 32    // may be padded with 4 bytes on 64bit ABIs for alignment
 33    if (expected_size == sizeof(region_count) + 4 +
 34                         region_count * sizeof(MDMemoryDescriptor)) {
 35      uint32_t useless;
 36      if (!minidump_->ReadBytes(&useless, 4)) {
 37        BPLOG(ERROR) << "MinidumpMemoryList cannot read memorylist padded "
 38                        "bytes";
 39        return false;
 40      }
 41    } else {
 42      BPLOG(ERROR) << "MinidumpMemoryList size mismatch, " << expected_size <<
 43                      " != " << sizeof(region_count) +
 44                      region_count * sizeof(MDMemoryDescriptor);
 45      return false;
 46    }
 47  }
 48
 49  // 最大region数量,限制为256个
 50  if (region_count > max_regions_) {
 51    BPLOG(ERROR) << "MinidumpMemoryList count " << region_count <<
 52                    " exceeds maximum " << max_regions_;
 53    return false;
 54  }
 55
 56  // 构造指定region梳理的列表对象,并读取region_count*每个region描述符大小的数据
 57  if (region_count != 0) {
 58    scoped_ptr<MemoryDescriptors> descriptors(
 59        new MemoryDescriptors(region_count));
 60
 61    // Read the entire array in one fell swoop, instead of reading one entry
 62    // at a time in the loop.
 63    if (!minidump_->ReadBytes(&(*descriptors)[0],
 64                              sizeof(MDMemoryDescriptor) * region_count)) {
 65      BPLOG(ERROR) << "MinidumpMemoryList could not read memory region list";
 66      return false;
 67    }
 68
 69    scoped_ptr<MemoryRegions> regions(
 70        new MemoryRegions(region_count, MinidumpMemoryRegion(minidump_)));
 71
 72    // 依次对每个region进行索引,便于后续分析时快速定位
 73    // 索引的数据结构为范围map(RangeMap)
 74    for (unsigned int region_index = 0;
 75         region_index < region_count;
 76         ++region_index) {
 77      MDMemoryDescriptor* descriptor = &(*descriptors)[region_index];
 78
 79      if (minidump_->swap())
 80        Swap(descriptor);
 81
 82      uint64_t base_address = descriptor->start_of_memory_range;
 83      uint32_t region_size = descriptor->memory.data_size;
 84
 85      // Check for base + size overflow or undersize.
 86      if (region_size == 0 ||
 87          region_size > numeric_limits<uint64_t>::max() - base_address) {
 88        BPLOG(ERROR) << "MinidumpMemoryList has a memory region problem, " <<
 89                        " region " << region_index << "/" << region_count <<
 90                        ", " << HexString(base_address) << "+" <<
 91                        HexString(region_size);
 92        return false;
 93      }
 94
 95      // 将region的起始地址,region大小,region index等信息建立缓存
 96      // 后面分析时,只要给定一个地址,就能快速定位到该地址属于哪个内存region
 97      if (!range_map_->StoreRange(base_address, region_size, region_index)) {
 98        BPLOG(ERROR) << "MinidumpMemoryList could not store memory region " <<
 99                        region_index << "/" << region_count << ", " <<
100                        HexString(base_address) << "+" <<
101                        HexString(region_size);
102        return false;
103      }
104
105      (*regions)[region_index].SetDescriptor(descriptor);
106    }
107
108    descriptors_ = descriptors.release();
109    regions_ = regions.release();
110  }
111
112  region_count_ = region_count;
113
114  valid_ = true;
115  return true;
116}