MinidumpParsing: pid, modules, exceptions, strings

Summary:
Added parsing of the MiscInfo data stream.
The main member of it that we care about is the process_id
On Linux generated Minidump (from breakpad) we don't have
the MiscInfo, we have the /proc/$pid/status from where we can get the
pid.
Also parsing the module list - the list of all of the loaded
modules/shared libraries.
Parsing the exception stream.
Parsing MinidumpStrings.

I have unit tests for all of that.
Also added some tests using a Minidump generated from Windows tools (not
from breakpad)

Reviewers: labath, zturner

Subscribers: beanz, lldb-commits

Differential Revision: https://reviews.llvm.org/D24385

llvm-svn: 281348
diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
index 8610922..da3ad6a 100644
--- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
+++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
@@ -1,5 +1,4 @@
-//===-- MinidumpParser.cpp ---------------------------------------*- C++
-//-*-===//
+//===-- MinidumpParser.cpp ---------------------------------------*- C++ -*-===//
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -56,13 +55,12 @@
         directory->location;
   }
 
-  MinidumpParser parser(data_buf_sp, header, directory_map);
-  return llvm::Optional<MinidumpParser>(parser);
+  return MinidumpParser(data_buf_sp, header, std::move(directory_map));
 }
 
 MinidumpParser::MinidumpParser(
     const lldb::DataBufferSP &data_buf_sp, const MinidumpHeader *header,
-    const llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> &directory_map)
+    llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> &&directory_map)
     : m_data_sp(data_buf_sp), m_header(header), m_directory_map(directory_map) {
 }
 
@@ -70,50 +68,48 @@
   return m_data_sp->GetByteSize();
 }
 
-llvm::Optional<llvm::ArrayRef<uint8_t>>
+llvm::ArrayRef<uint8_t>
 MinidumpParser::GetStream(MinidumpStreamType stream_type) {
   auto iter = m_directory_map.find(static_cast<uint32_t>(stream_type));
   if (iter == m_directory_map.end())
-    return llvm::None;
+    return {};
 
   // check if there is enough data
   if (iter->second.rva + iter->second.data_size > m_data_sp->GetByteSize())
-    return llvm::None;
+    return {};
 
-  llvm::ArrayRef<uint8_t> arr_ref(m_data_sp->GetBytes() + iter->second.rva,
-                                  iter->second.data_size);
-  return llvm::Optional<llvm::ArrayRef<uint8_t>>(arr_ref);
+  return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes() + iter->second.rva,
+                                 iter->second.data_size);
 }
 
-llvm::Optional<std::vector<const MinidumpThread *>>
-MinidumpParser::GetThreads() {
-  llvm::Optional<llvm::ArrayRef<uint8_t>> data =
-      GetStream(MinidumpStreamType::ThreadList);
+llvm::Optional<std::string> MinidumpParser::GetMinidumpString(uint32_t rva) {
+  auto arr_ref = m_data_sp->GetData();
+  if (rva > arr_ref.size())
+    return llvm::None;
+  arr_ref = arr_ref.drop_front(rva);
+  return parseMinidumpString(arr_ref);
+}
 
-  if (!data)
+llvm::ArrayRef<MinidumpThread> MinidumpParser::GetThreads() {
+  llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ThreadList);
+
+  if (data.size() == 0)
     return llvm::None;
 
-  return MinidumpThread::ParseThreadList(data.getValue());
+  return MinidumpThread::ParseThreadList(data);
 }
 
 const MinidumpSystemInfo *MinidumpParser::GetSystemInfo() {
-  llvm::Optional<llvm::ArrayRef<uint8_t>> data =
-      GetStream(MinidumpStreamType::SystemInfo);
+  llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::SystemInfo);
 
-  if (!data)
+  if (data.size() == 0)
     return nullptr;
 
-  return MinidumpSystemInfo::Parse(data.getValue());
+  return MinidumpSystemInfo::Parse(data);
 }
 
 ArchSpec MinidumpParser::GetArchitecture() {
   ArchSpec arch_spec;
-  arch_spec.GetTriple().setOS(llvm::Triple::OSType::UnknownOS);
-  arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::UnknownVendor);
-  arch_spec.GetTriple().setArch(llvm::Triple::ArchType::UnknownArch);
-
-  // TODO should we add the OS type here, or somewhere else ?
-
   const MinidumpSystemInfo *system_info = GetSystemInfo();
 
   if (!system_info)
@@ -122,35 +118,110 @@
   // TODO what to do about big endiand flavors of arm ?
   // TODO set the arm subarch stuff if the minidump has info about it
 
+  llvm::Triple triple;
+  triple.setVendor(llvm::Triple::VendorType::UnknownVendor);
+
   const MinidumpCPUArchitecture arch =
       static_cast<const MinidumpCPUArchitecture>(
           static_cast<const uint32_t>(system_info->processor_arch));
+
   switch (arch) {
   case MinidumpCPUArchitecture::X86:
-    arch_spec.GetTriple().setArch(llvm::Triple::ArchType::x86);
+    triple.setArch(llvm::Triple::ArchType::x86);
     break;
   case MinidumpCPUArchitecture::AMD64:
-    arch_spec.GetTriple().setArch(llvm::Triple::ArchType::x86_64);
+    triple.setArch(llvm::Triple::ArchType::x86_64);
     break;
   case MinidumpCPUArchitecture::ARM:
-    arch_spec.GetTriple().setArch(llvm::Triple::ArchType::arm);
+    triple.setArch(llvm::Triple::ArchType::arm);
     break;
   case MinidumpCPUArchitecture::ARM64:
-    arch_spec.GetTriple().setArch(llvm::Triple::ArchType::aarch64);
+    triple.setArch(llvm::Triple::ArchType::aarch64);
+    break;
+  default:
+    triple.setArch(llvm::Triple::ArchType::UnknownArch);
     break;
   default:
     break;
   }
 
+  const MinidumpOSPlatform os = static_cast<const MinidumpOSPlatform>(
+      static_cast<const uint32_t>(system_info->platform_id));
+
+  // TODO add all of the OSes that Minidump/breakpad distinguishes?
+  switch (os) {
+  case MinidumpOSPlatform::Win32S:
+  case MinidumpOSPlatform::Win32Windows:
+  case MinidumpOSPlatform::Win32NT:
+  case MinidumpOSPlatform::Win32CE:
+    triple.setOS(llvm::Triple::OSType::Win32);
+    break;
+  case MinidumpOSPlatform::Linux:
+    triple.setOS(llvm::Triple::OSType::Linux);
+    break;
+  case MinidumpOSPlatform::MacOSX:
+    triple.setOS(llvm::Triple::OSType::MacOSX);
+    break;
+  case MinidumpOSPlatform::Android:
+    triple.setOS(llvm::Triple::OSType::Linux);
+    triple.setEnvironment(llvm::Triple::EnvironmentType::Android);
+    break;
+  default:
+    triple.setOS(llvm::Triple::OSType::UnknownOS);
+    break;
+  }
+
+  arch_spec.SetTriple(triple);
+
   return arch_spec;
 }
 
 const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() {
-  llvm::Optional<llvm::ArrayRef<uint8_t>> data =
-      GetStream(MinidumpStreamType::MiscInfo);
+  llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MiscInfo);
 
-  if (!data)
+  if (data.size() == 0)
     return nullptr;
 
-  return MinidumpMiscInfo::Parse(data.getValue());
+  return MinidumpMiscInfo::Parse(data);
+}
+
+llvm::Optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() {
+  llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::LinuxProcStatus);
+
+  if (data.size() == 0)
+    return llvm::None;
+
+  return LinuxProcStatus::Parse(data);
+}
+
+llvm::Optional<lldb::pid_t> MinidumpParser::GetPid() {
+  const MinidumpMiscInfo *misc_info = GetMiscInfo();
+  if (misc_info != nullptr) {
+    return misc_info->GetPid();
+  }
+
+  llvm::Optional<LinuxProcStatus> proc_status = GetLinuxProcStatus();
+  if (proc_status.hasValue()) {
+    return proc_status->GetPid();
+  }
+
+  return llvm::None;
+}
+
+llvm::ArrayRef<MinidumpModule> MinidumpParser::GetModuleList() {
+  llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ModuleList);
+
+  if (data.size() == 0)
+    return {};
+
+  return MinidumpModule::ParseModuleList(data);
+}
+
+const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() {
+  llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::Exception);
+
+  if (data.size() == 0)
+    return nullptr;
+
+  return MinidumpExceptionStream::Parse(data);
 }