1 | //===-- MinidumpParser.cpp ------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "MinidumpParser.h" |
10 | #include "NtStructures.h" |
11 | #include "RegisterContextMinidump_x86_32.h" |
12 | |
13 | #include "Plugins/Process/Utility/LinuxProcMaps.h" |
14 | #include "lldb/Utility/LLDBAssert.h" |
15 | #include "lldb/Utility/Log.h" |
16 | |
17 | // C includes |
18 | // C++ includes |
19 | #include <algorithm> |
20 | #include <map> |
21 | #include <vector> |
22 | #include <utility> |
23 | |
24 | using namespace lldb_private; |
25 | using namespace minidump; |
26 | |
27 | llvm::Expected<MinidumpParser> |
28 | MinidumpParser::Create(const lldb::DataBufferSP &data_sp) { |
29 | auto ExpectedFile = llvm::object::MinidumpFile::create( |
30 | llvm::MemoryBufferRef(toStringRef(data_sp->GetData()), "minidump" )); |
31 | if (!ExpectedFile) |
32 | return ExpectedFile.takeError(); |
33 | |
34 | return MinidumpParser(data_sp, std::move(*ExpectedFile)); |
35 | } |
36 | |
37 | MinidumpParser::MinidumpParser(lldb::DataBufferSP data_sp, |
38 | std::unique_ptr<llvm::object::MinidumpFile> file) |
39 | : m_data_sp(std::move(data_sp)), m_file(std::move(file)) {} |
40 | |
41 | llvm::ArrayRef<uint8_t> MinidumpParser::GetData() { |
42 | return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes(), |
43 | m_data_sp->GetByteSize()); |
44 | } |
45 | |
46 | llvm::ArrayRef<uint8_t> MinidumpParser::GetStream(StreamType stream_type) { |
47 | return m_file->getRawStream(stream_type) |
48 | .getValueOr(llvm::ArrayRef<uint8_t>()); |
49 | } |
50 | |
51 | UUID MinidumpParser::GetModuleUUID(const minidump::Module *module) { |
52 | auto cv_record = |
53 | GetData().slice(module->CvRecord.RVA, module->CvRecord.DataSize); |
54 | |
55 | // Read the CV record signature |
56 | const llvm::support::ulittle32_t *signature = nullptr; |
57 | Status error = consumeObject(cv_record, signature); |
58 | if (error.Fail()) |
59 | return UUID(); |
60 | |
61 | const CvSignature cv_signature = |
62 | static_cast<CvSignature>(static_cast<uint32_t>(*signature)); |
63 | |
64 | if (cv_signature == CvSignature::Pdb70) { |
65 | const UUID::CvRecordPdb70 *pdb70_uuid = nullptr; |
66 | Status error = consumeObject(cv_record, pdb70_uuid); |
67 | if (error.Fail()) |
68 | return UUID(); |
69 | if (GetArchitecture().GetTriple().isOSBinFormatELF()) { |
70 | if (pdb70_uuid->Age != 0) |
71 | return UUID::fromOptionalData(pdb70_uuid, sizeof(*pdb70_uuid)); |
72 | return UUID::fromOptionalData(&pdb70_uuid->Uuid, |
73 | sizeof(pdb70_uuid->Uuid)); |
74 | } |
75 | return UUID::fromCvRecord(*pdb70_uuid); |
76 | } else if (cv_signature == CvSignature::ElfBuildId) |
77 | return UUID::fromOptionalData(cv_record); |
78 | |
79 | return UUID(); |
80 | } |
81 | |
82 | llvm::ArrayRef<minidump::Thread> MinidumpParser::GetThreads() { |
83 | auto ExpectedThreads = GetMinidumpFile().getThreadList(); |
84 | if (ExpectedThreads) |
85 | return *ExpectedThreads; |
86 | |
87 | LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD), |
88 | ExpectedThreads.takeError(), |
89 | "Failed to read thread list: {0}" ); |
90 | return {}; |
91 | } |
92 | |
93 | llvm::ArrayRef<uint8_t> |
94 | MinidumpParser::GetThreadContext(const LocationDescriptor &location) { |
95 | if (location.RVA + location.DataSize > GetData().size()) |
96 | return {}; |
97 | return GetData().slice(location.RVA, location.DataSize); |
98 | } |
99 | |
100 | llvm::ArrayRef<uint8_t> |
101 | MinidumpParser::GetThreadContext(const minidump::Thread &td) { |
102 | return GetThreadContext(td.Context); |
103 | } |
104 | |
105 | llvm::ArrayRef<uint8_t> |
106 | MinidumpParser::GetThreadContextWow64(const minidump::Thread &td) { |
107 | // On Windows, a 32-bit process can run on a 64-bit machine under WOW64. If |
108 | // the minidump was captured with a 64-bit debugger, then the CONTEXT we just |
109 | // grabbed from the mini_dump_thread is the one for the 64-bit "native" |
110 | // process rather than the 32-bit "guest" process we care about. In this |
111 | // case, we can get the 32-bit CONTEXT from the TEB (Thread Environment |
112 | // Block) of the 64-bit process. |
113 | auto teb_mem = GetMemory(td.EnvironmentBlock, sizeof(TEB64)); |
114 | if (teb_mem.empty()) |
115 | return {}; |
116 | |
117 | const TEB64 *wow64teb; |
118 | Status error = consumeObject(teb_mem, wow64teb); |
119 | if (error.Fail()) |
120 | return {}; |
121 | |
122 | // Slot 1 of the thread-local storage in the 64-bit TEB points to a structure |
123 | // that includes the 32-bit CONTEXT (after a ULONG). See: |
124 | // https://msdn.microsoft.com/en-us/library/ms681670.aspx |
125 | auto context = |
126 | GetMemory(wow64teb->tls_slots[1] + 4, sizeof(MinidumpContext_x86_32)); |
127 | if (context.size() < sizeof(MinidumpContext_x86_32)) |
128 | return {}; |
129 | |
130 | return context; |
131 | // NOTE: We don't currently use the TEB for anything else. If we |
132 | // need it in the future, the 32-bit TEB is located according to the address |
133 | // stored in the first slot of the 64-bit TEB (wow64teb.Reserved1[0]). |
134 | } |
135 | |
136 | ArchSpec MinidumpParser::GetArchitecture() { |
137 | if (m_arch.IsValid()) |
138 | return m_arch; |
139 | |
140 | // Set the architecture in m_arch |
141 | llvm::Expected<const SystemInfo &> system_info = m_file->getSystemInfo(); |
142 | |
143 | if (!system_info) { |
144 | LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS), |
145 | system_info.takeError(), |
146 | "Failed to read SystemInfo stream: {0}" ); |
147 | return m_arch; |
148 | } |
149 | |
150 | // TODO what to do about big endiand flavors of arm ? |
151 | // TODO set the arm subarch stuff if the minidump has info about it |
152 | |
153 | llvm::Triple triple; |
154 | triple.setVendor(llvm::Triple::VendorType::UnknownVendor); |
155 | |
156 | switch (system_info->ProcessorArch) { |
157 | case ProcessorArchitecture::X86: |
158 | triple.setArch(llvm::Triple::ArchType::x86); |
159 | break; |
160 | case ProcessorArchitecture::AMD64: |
161 | triple.setArch(llvm::Triple::ArchType::x86_64); |
162 | break; |
163 | case ProcessorArchitecture::ARM: |
164 | triple.setArch(llvm::Triple::ArchType::arm); |
165 | break; |
166 | case ProcessorArchitecture::ARM64: |
167 | case ProcessorArchitecture::BP_ARM64: |
168 | triple.setArch(llvm::Triple::ArchType::aarch64); |
169 | break; |
170 | default: |
171 | triple.setArch(llvm::Triple::ArchType::UnknownArch); |
172 | break; |
173 | } |
174 | |
175 | // TODO add all of the OSes that Minidump/breakpad distinguishes? |
176 | switch (system_info->PlatformId) { |
177 | case OSPlatform::Win32S: |
178 | case OSPlatform::Win32Windows: |
179 | case OSPlatform::Win32NT: |
180 | case OSPlatform::Win32CE: |
181 | triple.setOS(llvm::Triple::OSType::Win32); |
182 | triple.setVendor(llvm::Triple::VendorType::PC); |
183 | break; |
184 | case OSPlatform::Linux: |
185 | triple.setOS(llvm::Triple::OSType::Linux); |
186 | break; |
187 | case OSPlatform::MacOSX: |
188 | triple.setOS(llvm::Triple::OSType::MacOSX); |
189 | triple.setVendor(llvm::Triple::Apple); |
190 | break; |
191 | case OSPlatform::IOS: |
192 | triple.setOS(llvm::Triple::OSType::IOS); |
193 | triple.setVendor(llvm::Triple::Apple); |
194 | break; |
195 | case OSPlatform::Android: |
196 | triple.setOS(llvm::Triple::OSType::Linux); |
197 | triple.setEnvironment(llvm::Triple::EnvironmentType::Android); |
198 | break; |
199 | default: { |
200 | triple.setOS(llvm::Triple::OSType::UnknownOS); |
201 | auto ExpectedCSD = m_file->getString(system_info->CSDVersionRVA); |
202 | if (!ExpectedCSD) { |
203 | LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS), |
204 | ExpectedCSD.takeError(), |
205 | "Failed to CSD Version string: {0}" ); |
206 | } else { |
207 | if (ExpectedCSD->find("Linux" ) != std::string::npos) |
208 | triple.setOS(llvm::Triple::OSType::Linux); |
209 | } |
210 | break; |
211 | } |
212 | } |
213 | m_arch.SetTriple(triple); |
214 | return m_arch; |
215 | } |
216 | |
217 | const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() { |
218 | llvm::ArrayRef<uint8_t> data = GetStream(StreamType::MiscInfo); |
219 | |
220 | if (data.size() == 0) |
221 | return nullptr; |
222 | |
223 | return MinidumpMiscInfo::Parse(data); |
224 | } |
225 | |
226 | llvm::Optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() { |
227 | llvm::ArrayRef<uint8_t> data = GetStream(StreamType::LinuxProcStatus); |
228 | |
229 | if (data.size() == 0) |
230 | return llvm::None; |
231 | |
232 | return LinuxProcStatus::Parse(data); |
233 | } |
234 | |
235 | llvm::Optional<lldb::pid_t> MinidumpParser::GetPid() { |
236 | const MinidumpMiscInfo *misc_info = GetMiscInfo(); |
237 | if (misc_info != nullptr) { |
238 | return misc_info->GetPid(); |
239 | } |
240 | |
241 | llvm::Optional<LinuxProcStatus> proc_status = GetLinuxProcStatus(); |
242 | if (proc_status.hasValue()) { |
243 | return proc_status->GetPid(); |
244 | } |
245 | |
246 | return llvm::None; |
247 | } |
248 | |
249 | llvm::ArrayRef<minidump::Module> MinidumpParser::GetModuleList() { |
250 | auto ExpectedModules = GetMinidumpFile().getModuleList(); |
251 | if (ExpectedModules) |
252 | return *ExpectedModules; |
253 | |
254 | LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES), |
255 | ExpectedModules.takeError(), |
256 | "Failed to read module list: {0}" ); |
257 | return {}; |
258 | } |
259 | |
260 | static bool |
261 | CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser, |
262 | std::vector<MemoryRegionInfo> ®ions) { |
263 | auto data = parser.GetStream(StreamType::LinuxMaps); |
264 | if (data.empty()) |
265 | return false; |
266 | |
267 | Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS); |
268 | ParseLinuxMapRegions( |
269 | llvm::toStringRef(data), |
270 | [®ions, &log](llvm::Expected<MemoryRegionInfo> region) -> bool { |
271 | if (region) |
272 | regions.push_back(*region); |
273 | else |
274 | LLDB_LOG_ERROR(log, region.takeError(), |
275 | "Reading memory region from minidump failed: {0}" ); |
276 | return true; |
277 | }); |
278 | return !regions.empty(); |
279 | } |
280 | |
281 | /// Check for the memory regions starting at \a load_addr for a contiguous |
282 | /// section that has execute permissions that matches the module path. |
283 | /// |
284 | /// When we load a breakpad generated minidump file, we might have the |
285 | /// /proc/<pid>/maps text for a process that details the memory map of the |
286 | /// process that the minidump is describing. This checks the sorted memory |
287 | /// regions for a section that has execute permissions. A sample maps files |
288 | /// might look like: |
289 | /// |
290 | /// 00400000-00401000 r--p 00000000 fd:01 2838574 /tmp/a.out |
291 | /// 00401000-00402000 r-xp 00001000 fd:01 2838574 /tmp/a.out |
292 | /// 00402000-00403000 r--p 00002000 fd:01 2838574 /tmp/a.out |
293 | /// 00403000-00404000 r--p 00002000 fd:01 2838574 /tmp/a.out |
294 | /// 00404000-00405000 rw-p 00003000 fd:01 2838574 /tmp/a.out |
295 | /// ... |
296 | /// |
297 | /// This function should return true when given 0x00400000 and "/tmp/a.out" |
298 | /// is passed in as the path since it has a consecutive memory region for |
299 | /// "/tmp/a.out" that has execute permissions at 0x00401000. This will help us |
300 | /// differentiate if a file has been memory mapped into a process for reading |
301 | /// and breakpad ends up saving a minidump file that has two module entries for |
302 | /// a given file: one that is read only for the entire file, and then one that |
303 | /// is the real executable that is loaded into memory for execution. For memory |
304 | /// mapped files they will typically show up and r--p permissions and a range |
305 | /// matcning the entire range of the file on disk: |
306 | /// |
307 | /// 00800000-00805000 r--p 00000000 fd:01 2838574 /tmp/a.out |
308 | /// 00805000-00806000 r-xp 00001000 fd:01 1234567 /usr/lib/libc.so |
309 | /// |
310 | /// This function should return false when asked about 0x00800000 with |
311 | /// "/tmp/a.out" as the path. |
312 | /// |
313 | /// \param[in] path |
314 | /// The path to the module to check for in the memory regions. Only sequential |
315 | /// memory regions whose paths match this path will be considered when looking |
316 | /// for execute permissions. |
317 | /// |
318 | /// \param[in] regions |
319 | /// A sorted list of memory regions obtained from a call to |
320 | /// CreateRegionsCacheFromLinuxMaps. |
321 | /// |
322 | /// \param[in] base_of_image |
323 | /// The load address of this module from BaseOfImage in the modules list. |
324 | /// |
325 | /// \return |
326 | /// True if a contiguous region of memory belonging to the module with a |
327 | /// matching path exists that has executable permissions. Returns false if |
328 | /// \a regions is empty or if there are no regions with execute permissions |
329 | /// that match \a path. |
330 | |
331 | static bool CheckForLinuxExecutable(ConstString path, |
332 | const MemoryRegionInfos ®ions, |
333 | lldb::addr_t base_of_image) { |
334 | if (regions.empty()) |
335 | return false; |
336 | lldb::addr_t addr = base_of_image; |
337 | MemoryRegionInfo region = MinidumpParser::GetMemoryRegionInfo(regions, addr); |
338 | while (region.GetName() == path) { |
339 | if (region.GetExecutable() == MemoryRegionInfo::eYes) |
340 | return true; |
341 | addr += region.GetRange().GetByteSize(); |
342 | region = MinidumpParser::GetMemoryRegionInfo(regions, addr); |
343 | } |
344 | return false; |
345 | } |
346 | |
347 | std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() { |
348 | Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES); |
349 | auto ExpectedModules = GetMinidumpFile().getModuleList(); |
350 | if (!ExpectedModules) { |
351 | LLDB_LOG_ERROR(log, ExpectedModules.takeError(), |
352 | "Failed to read module list: {0}" ); |
353 | return {}; |
354 | } |
355 | |
356 | // Create memory regions from the linux maps only. We do this to avoid issues |
357 | // with breakpad generated minidumps where if someone has mmap'ed a shared |
358 | // library into memory to accesss its data in the object file, we can get a |
359 | // minidump with two mappings for a binary: one whose base image points to a |
360 | // memory region that is read + execute and one that is read only. |
361 | MemoryRegionInfos linux_regions; |
362 | if (CreateRegionsCacheFromLinuxMaps(*this, linux_regions)) |
363 | llvm::sort(linux_regions); |
364 | |
365 | // map module_name -> filtered_modules index |
366 | typedef llvm::StringMap<size_t> MapType; |
367 | MapType module_name_to_filtered_index; |
368 | |
369 | std::vector<const minidump::Module *> filtered_modules; |
370 | |
371 | for (const auto &module : *ExpectedModules) { |
372 | auto ExpectedName = m_file->getString(module.ModuleNameRVA); |
373 | if (!ExpectedName) { |
374 | LLDB_LOG_ERROR(log, ExpectedName.takeError(), |
375 | "Failed to get module name: {0}" ); |
376 | continue; |
377 | } |
378 | |
379 | MapType::iterator iter; |
380 | bool inserted; |
381 | // See if we have inserted this module aready into filtered_modules. If we |
382 | // haven't insert an entry into module_name_to_filtered_index with the |
383 | // index where we will insert it if it isn't in the vector already. |
384 | std::tie(iter, inserted) = module_name_to_filtered_index.try_emplace( |
385 | *ExpectedName, filtered_modules.size()); |
386 | |
387 | if (inserted) { |
388 | // This module has not been seen yet, insert it into filtered_modules at |
389 | // the index that was inserted into module_name_to_filtered_index using |
390 | // "filtered_modules.size()" above. |
391 | filtered_modules.push_back(&module); |
392 | } else { |
393 | // We have a duplicate module entry. Check the linux regions to see if |
394 | // either module is not really a mapped executable. If one but not the |
395 | // other is a real mapped executable, prefer the executable one. This |
396 | // can happen when a process mmap's in the file for an executable in |
397 | // order to read bytes from the executable file. A memory region mapping |
398 | // will exist for the mmap'ed version and for the loaded executable, but |
399 | // only one will have a consecutive region that is executable in the |
400 | // memory regions. |
401 | auto dup_module = filtered_modules[iter->second]; |
402 | ConstString name(*ExpectedName); |
403 | bool is_executable = |
404 | CheckForLinuxExecutable(name, linux_regions, module.BaseOfImage); |
405 | bool dup_is_executable = |
406 | CheckForLinuxExecutable(name, linux_regions, dup_module->BaseOfImage); |
407 | |
408 | if (is_executable != dup_is_executable) { |
409 | if (is_executable) |
410 | filtered_modules[iter->second] = &module; |
411 | continue; |
412 | } |
413 | // This module has been seen. Modules are sometimes mentioned multiple |
414 | // times when they are mapped discontiguously, so find the module with |
415 | // the lowest "base_of_image" and use that as the filtered module. |
416 | if (module.BaseOfImage < dup_module->BaseOfImage) |
417 | filtered_modules[iter->second] = &module; |
418 | } |
419 | } |
420 | return filtered_modules; |
421 | } |
422 | |
423 | const minidump::ExceptionStream *MinidumpParser::GetExceptionStream() { |
424 | auto ExpectedStream = GetMinidumpFile().getExceptionStream(); |
425 | if (ExpectedStream) |
426 | return &*ExpectedStream; |
427 | |
428 | LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS), |
429 | ExpectedStream.takeError(), |
430 | "Failed to read minidump exception stream: {0}" ); |
431 | return nullptr; |
432 | } |
433 | |
434 | llvm::Optional<minidump::Range> |
435 | MinidumpParser::FindMemoryRange(lldb::addr_t addr) { |
436 | llvm::ArrayRef<uint8_t> data64 = GetStream(StreamType::Memory64List); |
437 | Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES); |
438 | |
439 | auto ExpectedMemory = GetMinidumpFile().getMemoryList(); |
440 | if (!ExpectedMemory) { |
441 | LLDB_LOG_ERROR(log, ExpectedMemory.takeError(), |
442 | "Failed to read memory list: {0}" ); |
443 | } else { |
444 | for (const auto &memory_desc : *ExpectedMemory) { |
445 | const LocationDescriptor &loc_desc = memory_desc.Memory; |
446 | const lldb::addr_t range_start = memory_desc.StartOfMemoryRange; |
447 | const size_t range_size = loc_desc.DataSize; |
448 | |
449 | if (loc_desc.RVA + loc_desc.DataSize > GetData().size()) |
450 | return llvm::None; |
451 | |
452 | if (range_start <= addr && addr < range_start + range_size) { |
453 | auto ExpectedSlice = GetMinidumpFile().getRawData(loc_desc); |
454 | if (!ExpectedSlice) { |
455 | LLDB_LOG_ERROR(log, ExpectedSlice.takeError(), |
456 | "Failed to get memory slice: {0}" ); |
457 | return llvm::None; |
458 | } |
459 | return minidump::Range(range_start, *ExpectedSlice); |
460 | } |
461 | } |
462 | } |
463 | |
464 | // Some Minidumps have a Memory64ListStream that captures all the heap memory |
465 | // (full-memory Minidumps). We can't exactly use the same loop as above, |
466 | // because the Minidump uses slightly different data structures to describe |
467 | // those |
468 | |
469 | if (!data64.empty()) { |
470 | llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list; |
471 | uint64_t base_rva; |
472 | std::tie(memory64_list, base_rva) = |
473 | MinidumpMemoryDescriptor64::ParseMemory64List(data64); |
474 | |
475 | if (memory64_list.empty()) |
476 | return llvm::None; |
477 | |
478 | for (const auto &memory_desc64 : memory64_list) { |
479 | const lldb::addr_t range_start = memory_desc64.start_of_memory_range; |
480 | const size_t range_size = memory_desc64.data_size; |
481 | |
482 | if (base_rva + range_size > GetData().size()) |
483 | return llvm::None; |
484 | |
485 | if (range_start <= addr && addr < range_start + range_size) { |
486 | return minidump::Range(range_start, |
487 | GetData().slice(base_rva, range_size)); |
488 | } |
489 | base_rva += range_size; |
490 | } |
491 | } |
492 | |
493 | return llvm::None; |
494 | } |
495 | |
496 | llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr, |
497 | size_t size) { |
498 | // I don't have a sense of how frequently this is called or how many memory |
499 | // ranges a Minidump typically has, so I'm not sure if searching for the |
500 | // appropriate range linearly each time is stupid. Perhaps we should build |
501 | // an index for faster lookups. |
502 | llvm::Optional<minidump::Range> range = FindMemoryRange(addr); |
503 | if (!range) |
504 | return {}; |
505 | |
506 | // There's at least some overlap between the beginning of the desired range |
507 | // (addr) and the current range. Figure out where the overlap begins and how |
508 | // much overlap there is. |
509 | |
510 | const size_t offset = addr - range->start; |
511 | |
512 | if (addr < range->start || offset >= range->range_ref.size()) |
513 | return {}; |
514 | |
515 | const size_t overlap = std::min(size, range->range_ref.size() - offset); |
516 | return range->range_ref.slice(offset, overlap); |
517 | } |
518 | |
519 | static bool |
520 | CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser, |
521 | std::vector<MemoryRegionInfo> ®ions) { |
522 | Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES); |
523 | auto ExpectedInfo = parser.GetMinidumpFile().getMemoryInfoList(); |
524 | if (!ExpectedInfo) { |
525 | LLDB_LOG_ERROR(log, ExpectedInfo.takeError(), |
526 | "Failed to read memory info list: {0}" ); |
527 | return false; |
528 | } |
529 | constexpr auto yes = MemoryRegionInfo::eYes; |
530 | constexpr auto no = MemoryRegionInfo::eNo; |
531 | for (const MemoryInfo &entry : *ExpectedInfo) { |
532 | MemoryRegionInfo region; |
533 | region.GetRange().SetRangeBase(entry.BaseAddress); |
534 | region.GetRange().SetByteSize(entry.RegionSize); |
535 | |
536 | MemoryProtection prot = entry.Protect; |
537 | region.SetReadable(bool(prot & MemoryProtection::NoAccess) ? no : yes); |
538 | region.SetWritable( |
539 | bool(prot & (MemoryProtection::ReadWrite | MemoryProtection::WriteCopy | |
540 | MemoryProtection::ExecuteReadWrite | |
541 | MemoryProtection::ExeciteWriteCopy)) |
542 | ? yes |
543 | : no); |
544 | region.SetExecutable( |
545 | bool(prot & (MemoryProtection::Execute | MemoryProtection::ExecuteRead | |
546 | MemoryProtection::ExecuteReadWrite | |
547 | MemoryProtection::ExeciteWriteCopy)) |
548 | ? yes |
549 | : no); |
550 | region.SetMapped(entry.State != MemoryState::Free ? yes : no); |
551 | regions.push_back(region); |
552 | } |
553 | return !regions.empty(); |
554 | } |
555 | |
556 | static bool |
557 | CreateRegionsCacheFromMemoryList(MinidumpParser &parser, |
558 | std::vector<MemoryRegionInfo> ®ions) { |
559 | Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES); |
560 | auto ExpectedMemory = parser.GetMinidumpFile().getMemoryList(); |
561 | if (!ExpectedMemory) { |
562 | LLDB_LOG_ERROR(log, ExpectedMemory.takeError(), |
563 | "Failed to read memory list: {0}" ); |
564 | return false; |
565 | } |
566 | regions.reserve(ExpectedMemory->size()); |
567 | for (const MemoryDescriptor &memory_desc : *ExpectedMemory) { |
568 | if (memory_desc.Memory.DataSize == 0) |
569 | continue; |
570 | MemoryRegionInfo region; |
571 | region.GetRange().SetRangeBase(memory_desc.StartOfMemoryRange); |
572 | region.GetRange().SetByteSize(memory_desc.Memory.DataSize); |
573 | region.SetReadable(MemoryRegionInfo::eYes); |
574 | region.SetMapped(MemoryRegionInfo::eYes); |
575 | regions.push_back(region); |
576 | } |
577 | regions.shrink_to_fit(); |
578 | return !regions.empty(); |
579 | } |
580 | |
581 | static bool |
582 | CreateRegionsCacheFromMemory64List(MinidumpParser &parser, |
583 | std::vector<MemoryRegionInfo> ®ions) { |
584 | llvm::ArrayRef<uint8_t> data = |
585 | parser.GetStream(StreamType::Memory64List); |
586 | if (data.empty()) |
587 | return false; |
588 | llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list; |
589 | uint64_t base_rva; |
590 | std::tie(memory64_list, base_rva) = |
591 | MinidumpMemoryDescriptor64::ParseMemory64List(data); |
592 | |
593 | if (memory64_list.empty()) |
594 | return false; |
595 | |
596 | regions.reserve(memory64_list.size()); |
597 | for (const auto &memory_desc : memory64_list) { |
598 | if (memory_desc.data_size == 0) |
599 | continue; |
600 | MemoryRegionInfo region; |
601 | region.GetRange().SetRangeBase(memory_desc.start_of_memory_range); |
602 | region.GetRange().SetByteSize(memory_desc.data_size); |
603 | region.SetReadable(MemoryRegionInfo::eYes); |
604 | region.SetMapped(MemoryRegionInfo::eYes); |
605 | regions.push_back(region); |
606 | } |
607 | regions.shrink_to_fit(); |
608 | return !regions.empty(); |
609 | } |
610 | |
611 | std::pair<MemoryRegionInfos, bool> MinidumpParser::BuildMemoryRegions() { |
612 | // We create the region cache using the best source. We start with |
613 | // the linux maps since they are the most complete and have names for the |
614 | // regions. Next we try the MemoryInfoList since it has |
615 | // read/write/execute/map data, and then fall back to the MemoryList and |
616 | // Memory64List to just get a list of the memory that is mapped in this |
617 | // core file |
618 | MemoryRegionInfos result; |
619 | const auto &return_sorted = [&](bool is_complete) { |
620 | llvm::sort(result); |
621 | return std::make_pair(std::move(result), is_complete); |
622 | }; |
623 | if (CreateRegionsCacheFromLinuxMaps(*this, result)) |
624 | return return_sorted(true); |
625 | if (CreateRegionsCacheFromMemoryInfoList(*this, result)) |
626 | return return_sorted(true); |
627 | if (CreateRegionsCacheFromMemoryList(*this, result)) |
628 | return return_sorted(false); |
629 | CreateRegionsCacheFromMemory64List(*this, result); |
630 | return return_sorted(false); |
631 | } |
632 | |
633 | #define ENUM_TO_CSTR(ST) \ |
634 | case StreamType::ST: \ |
635 | return #ST |
636 | |
637 | llvm::StringRef |
638 | MinidumpParser::GetStreamTypeAsString(StreamType stream_type) { |
639 | switch (stream_type) { |
640 | ENUM_TO_CSTR(Unused); |
641 | ENUM_TO_CSTR(ThreadList); |
642 | ENUM_TO_CSTR(ModuleList); |
643 | ENUM_TO_CSTR(MemoryList); |
644 | ENUM_TO_CSTR(Exception); |
645 | ENUM_TO_CSTR(SystemInfo); |
646 | ENUM_TO_CSTR(ThreadExList); |
647 | ENUM_TO_CSTR(Memory64List); |
648 | ENUM_TO_CSTR(CommentA); |
649 | ENUM_TO_CSTR(CommentW); |
650 | ENUM_TO_CSTR(HandleData); |
651 | ENUM_TO_CSTR(FunctionTable); |
652 | ENUM_TO_CSTR(UnloadedModuleList); |
653 | ENUM_TO_CSTR(MiscInfo); |
654 | ENUM_TO_CSTR(MemoryInfoList); |
655 | ENUM_TO_CSTR(ThreadInfoList); |
656 | ENUM_TO_CSTR(HandleOperationList); |
657 | ENUM_TO_CSTR(Token); |
658 | ENUM_TO_CSTR(JavascriptData); |
659 | ENUM_TO_CSTR(SystemMemoryInfo); |
660 | ENUM_TO_CSTR(ProcessVMCounters); |
661 | ENUM_TO_CSTR(LastReserved); |
662 | ENUM_TO_CSTR(BreakpadInfo); |
663 | ENUM_TO_CSTR(AssertionInfo); |
664 | ENUM_TO_CSTR(LinuxCPUInfo); |
665 | ENUM_TO_CSTR(LinuxProcStatus); |
666 | ENUM_TO_CSTR(LinuxLSBRelease); |
667 | ENUM_TO_CSTR(LinuxCMDLine); |
668 | ENUM_TO_CSTR(LinuxEnviron); |
669 | ENUM_TO_CSTR(LinuxAuxv); |
670 | ENUM_TO_CSTR(LinuxMaps); |
671 | ENUM_TO_CSTR(LinuxDSODebug); |
672 | ENUM_TO_CSTR(LinuxProcStat); |
673 | ENUM_TO_CSTR(LinuxProcUptime); |
674 | ENUM_TO_CSTR(LinuxProcFD); |
675 | ENUM_TO_CSTR(FacebookAppCustomData); |
676 | ENUM_TO_CSTR(FacebookBuildID); |
677 | ENUM_TO_CSTR(FacebookAppVersionName); |
678 | ENUM_TO_CSTR(FacebookJavaStack); |
679 | ENUM_TO_CSTR(FacebookDalvikInfo); |
680 | ENUM_TO_CSTR(FacebookUnwindSymbols); |
681 | ENUM_TO_CSTR(FacebookDumpErrorLog); |
682 | ENUM_TO_CSTR(FacebookAppStateLog); |
683 | ENUM_TO_CSTR(FacebookAbortReason); |
684 | ENUM_TO_CSTR(FacebookThreadName); |
685 | ENUM_TO_CSTR(FacebookLogcat); |
686 | } |
687 | return "unknown stream type" ; |
688 | } |
689 | |
690 | MemoryRegionInfo |
691 | MinidumpParser::GetMemoryRegionInfo(const MemoryRegionInfos ®ions, |
692 | lldb::addr_t load_addr) { |
693 | MemoryRegionInfo region; |
694 | auto pos = llvm::upper_bound(regions, load_addr); |
695 | if (pos != regions.begin() && |
696 | std::prev(pos)->GetRange().Contains(load_addr)) { |
697 | return *std::prev(pos); |
698 | } |
699 | |
700 | if (pos == regions.begin()) |
701 | region.GetRange().SetRangeBase(0); |
702 | else |
703 | region.GetRange().SetRangeBase(std::prev(pos)->GetRange().GetRangeEnd()); |
704 | |
705 | if (pos == regions.end()) |
706 | region.GetRange().SetRangeEnd(UINT64_MAX); |
707 | else |
708 | region.GetRange().SetRangeEnd(pos->GetRange().GetRangeBase()); |
709 | |
710 | region.SetReadable(MemoryRegionInfo::eNo); |
711 | region.SetWritable(MemoryRegionInfo::eNo); |
712 | region.SetExecutable(MemoryRegionInfo::eNo); |
713 | region.SetMapped(MemoryRegionInfo::eNo); |
714 | return region; |
715 | } |
716 | |