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

source code of lldb/source/Plugins/Process/minidump/MinidumpParser.cpp