dmp文件读取(五)- Memory
| 阅读 | 共 2063 字,阅读约
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(®ion_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(®ion_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}