xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
26 using namespace lldb_private;
27 using namespace minidump;
28 
29 llvm::Expected<MinidumpParser>
Create(const lldb::DataBufferSP & data_sp)30 MinidumpParser::Create(const lldb::DataBufferSP &data_sp) {
31   auto ExpectedFile = llvm::object::MinidumpFile::create(
32       llvm::MemoryBufferRef(toStringRef(data_sp->GetData()), "minidump"));
33   if (!ExpectedFile)
34     return ExpectedFile.takeError();
35 
36   return MinidumpParser(data_sp, std::move(*ExpectedFile));
37 }
38 
MinidumpParser(lldb::DataBufferSP data_sp,std::unique_ptr<llvm::object::MinidumpFile> file)39 MinidumpParser::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 
GetData()43 llvm::ArrayRef<uint8_t> MinidumpParser::GetData() {
44   return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes(),
45                                  m_data_sp->GetByteSize());
46 }
47 
GetStream(StreamType stream_type)48 llvm::ArrayRef<uint8_t> MinidumpParser::GetStream(StreamType stream_type) {
49   return m_file->getRawStream(stream_type).value_or(llvm::ArrayRef<uint8_t>());
50 }
51 
52 std::optional<llvm::ArrayRef<uint8_t>>
GetRawStream(StreamType stream_type)53 MinidumpParser::GetRawStream(StreamType stream_type) {
54   return m_file->getRawStream(stream_type);
55 }
56 
GetModuleUUID(const minidump::Module * module)57 UUID MinidumpParser::GetModuleUUID(const minidump::Module *module) {
58   auto cv_record =
59       GetData().slice(module->CvRecord.RVA, module->CvRecord.DataSize);
60 
61   // Read the CV record signature
62   const llvm::support::ulittle32_t *signature = nullptr;
63   Status error = consumeObject(cv_record, 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(cv_record, 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 
GetThreads()87 llvm::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 
97 llvm::ArrayRef<uint8_t>
GetThreadContext(const LocationDescriptor & location)98 MinidumpParser::GetThreadContext(const LocationDescriptor &location) {
99   if (location.RVA + location.DataSize > GetData().size())
100     return {};
101   return GetData().slice(location.RVA, location.DataSize);
102 }
103 
104 llvm::ArrayRef<uint8_t>
GetThreadContext(const minidump::Thread & td)105 MinidumpParser::GetThreadContext(const minidump::Thread &td) {
106   return GetThreadContext(td.Context);
107 }
108 
109 llvm::ArrayRef<uint8_t>
GetThreadContextWow64(const minidump::Thread & td)110 MinidumpParser::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(td.EnvironmentBlock, sizeof(TEB64));
118   if (teb_mem.empty())
119     return {};
120 
121   const TEB64 *wow64teb;
122   Status error = consumeObject(teb_mem, 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(wow64teb->tls_slots[1] + 4, 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 
GetArchitecture()140 ArchSpec 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(llvm::Triple::ArchType::x86);
162     break;
163   case ProcessorArchitecture::AMD64:
164     triple.setArch(llvm::Triple::ArchType::x86_64);
165     break;
166   case ProcessorArchitecture::ARM:
167     triple.setArch(llvm::Triple::ArchType::arm);
168     break;
169   case ProcessorArchitecture::ARM64:
170   case ProcessorArchitecture::BP_ARM64:
171     triple.setArch(llvm::Triple::ArchType::aarch64);
172     break;
173   default:
174     triple.setArch(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(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("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 
GetMiscInfo()219 const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() {
220   llvm::ArrayRef<uint8_t> data = GetStream(StreamType::MiscInfo);
221 
222   if (data.size() == 0)
223     return nullptr;
224 
225   return MinidumpMiscInfo::Parse(data);
226 }
227 
GetLinuxProcStatus()228 std::optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() {
229   llvm::ArrayRef<uint8_t> data = GetStream(StreamType::LinuxProcStatus);
230 
231   if (data.size() == 0)
232     return std::nullopt;
233 
234   return LinuxProcStatus::Parse(data);
235 }
236 
GetPid()237 std::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 
GetModuleList()251 llvm::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 
261 static bool
CreateRegionsCacheFromLinuxMaps(MinidumpParser & parser,std::vector<MemoryRegionInfo> & regions)262 CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser,
263                                 std::vector<MemoryRegionInfo> &regions) {
264   auto data = parser.GetStream(StreamType::LinuxMaps);
265   if (data.empty())
266     return false;
267 
268   Log *log = GetLog(LLDBLog::Expressions);
269   ParseLinuxMapRegions(
270       llvm::toStringRef(data),
271       [&regions, &log](llvm::Expected<MemoryRegionInfo> region) -> bool {
272         if (region)
273           regions.push_back(*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 
CheckForLinuxExecutable(ConstString path,const MemoryRegionInfos & regions,lldb::addr_t base_of_image)332 static 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, 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, addr);
344   }
345   return false;
346 }
347 
GetFilteredModuleList()348 std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() {
349   Log *log = GetLog(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(*this, linux_regions))
364     llvm::sort(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(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(iter, inserted) = module_name_to_filtered_index.try_emplace(
386         *ExpectedName, 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(&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(name, linux_regions, module.BaseOfImage);
406       bool dup_is_executable =
407           CheckForLinuxExecutable(name, linux_regions, 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 
424 llvm::iterator_range<ExceptionStreamsIterator>
GetExceptionStreams()425 MinidumpParser::GetExceptionStreams() {
426   return GetMinidumpFile().getExceptionStreams();
427 }
428 
429 std::optional<minidump::Range>
FindMemoryRange(lldb::addr_t addr)430 MinidumpParser::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 
PopulateMemoryRanges()442 void MinidumpParser::PopulateMemoryRanges() {
443   Log *log = GetLog(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(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(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(StreamType::Memory64List).empty()) {
466     llvm::Error err = llvm::Error::success();
467     for (const auto &memory_desc : GetMinidumpFile().getMemory64List(err)) {
468       m_memory_ranges.Append(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 
GetMemory(lldb::addr_t addr,size_t size)481 llvm::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(size, range->range_ref.size() - offset);
497   return range->range_ref.slice(offset, overlap);
498 }
499 
500 llvm::iterator_range<FallibleMemory64Iterator>
GetMemory64Iterator(llvm::Error & err)501 MinidumpParser::GetMemory64Iterator(llvm::Error &err) {
502   llvm::ErrorAsOutParameter ErrAsOutParam(&err);
503   return m_file->getMemory64List(err);
504 }
505 
506 static bool
CreateRegionsCacheFromMemoryInfoList(MinidumpParser & parser,std::vector<MemoryRegionInfo> & regions)507 CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser,
508                                      std::vector<MemoryRegionInfo> &regions) {
509   Log *log = GetLog(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(region);
539   }
540   return !regions.empty();
541 }
542 
543 static bool
CreateRegionsCacheFromMemoryList(MinidumpParser & parser,std::vector<MemoryRegionInfo> & regions)544 CreateRegionsCacheFromMemoryList(MinidumpParser &parser,
545                                  std::vector<MemoryRegionInfo> &regions) {
546   Log *log = GetLog(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(region);
563     }
564   }
565 
566   if (!parser.GetStream(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(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 
BuildMemoryRegions()589 std::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(result);
599     return std::make_pair(std::move(result), is_complete);
600   };
601   if (CreateRegionsCacheFromLinuxMaps(*this, result))
602     return return_sorted(true);
603   if (CreateRegionsCacheFromMemoryInfoList(*this, result))
604     return return_sorted(true);
605   CreateRegionsCacheFromMemoryList(*this, result);
606   return return_sorted(false);
607 }
608 
609 #define ENUM_TO_CSTR(ST)                                                       \
610   case StreamType::ST:                                                         \
611     return #ST
612 
GetStreamTypeAsString(StreamType stream_type)613 llvm::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 
666 MemoryRegionInfo
GetMemoryRegionInfo(const MemoryRegionInfos & regions,lldb::addr_t load_addr)667 MinidumpParser::GetMemoryRegionInfo(const MemoryRegionInfos &regions,
668                                     lldb::addr_t load_addr) {
669   MemoryRegionInfo region;
670   auto pos = llvm::upper_bound(regions, load_addr);
671   if (pos != regions.begin() &&
672       std::prev(pos)->GetRange().Contains(load_addr)) {
673     return *std::prev(pos);
674   }
675 
676   if (pos == regions.begin())
677     region.GetRange().SetRangeBase(0);
678   else
679     region.GetRange().SetRangeBase(std::prev(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