Minidump parsing

Summary:
This is a Minidump parsing code.
There are still some more structures/data streams that need to be added.
The aim ot this is to be used in the implementation of
a minidump debugging plugin that works on all platforms/architectures.
Currently we have a windows-only plugin that uses the WinAPI to parse
the dump files.
Also added unittests for the current functionality.

Reviewers: labath, amccarth

Subscribers: tberghammer, danalbert, srhines, lldb-commits, dschuff

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

llvm-svn: 280356
diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
new file mode 100644
index 0000000..18b8a3f
--- /dev/null
+++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
@@ -0,0 +1,160 @@
+//===-- MinidumpParser.cpp ---------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// Project includes
+#include "MinidumpParser.h"
+
+// Other libraries and framework includes
+// C includes
+// C++ includes
+
+using namespace lldb_private;
+using namespace minidump;
+
+llvm::Optional<MinidumpParser>
+MinidumpParser::Create(const lldb::DataBufferSP &data_buf_sp)
+{
+    if (data_buf_sp->GetByteSize() < sizeof(MinidumpHeader))
+    {
+        return llvm::None;
+    }
+
+    llvm::ArrayRef<uint8_t> header_data(data_buf_sp->GetBytes(), sizeof(MinidumpHeader));
+    const MinidumpHeader *header = MinidumpHeader::Parse(header_data);
+
+    if (header == nullptr)
+    {
+        return llvm::None;
+    }
+
+    lldb::offset_t directory_list_offset = header->stream_directory_rva;
+    // check if there is enough data for the parsing of the directory list
+    if ((directory_list_offset + sizeof(MinidumpDirectory) * header->streams_count) > data_buf_sp->GetByteSize())
+    {
+        return llvm::None;
+    }
+
+    const MinidumpDirectory *directory = nullptr;
+    Error error;
+    llvm::ArrayRef<uint8_t> directory_data(data_buf_sp->GetBytes() + directory_list_offset,
+                                           sizeof(MinidumpDirectory) * header->streams_count);
+    llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> directory_map;
+
+    for (uint32_t i = 0; i < header->streams_count; ++i)
+    {
+        error = consumeObject(directory_data, directory);
+        if (error.Fail())
+        {
+            return llvm::None;
+        }
+        directory_map[static_cast<const uint32_t>(directory->stream_type)] = directory->location;
+    }
+
+    MinidumpParser parser(data_buf_sp, header, directory_map);
+    return llvm::Optional<MinidumpParser>(parser);
+}
+
+MinidumpParser::MinidumpParser(const lldb::DataBufferSP &data_buf_sp, const MinidumpHeader *header,
+                               const llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> &directory_map)
+    : m_data_sp(data_buf_sp), m_header(header), m_directory_map(directory_map)
+{
+}
+
+lldb::offset_t
+MinidumpParser::GetByteSize()
+{
+    return m_data_sp->GetByteSize();
+}
+
+llvm::Optional<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;
+
+    // check if there is enough data
+    if (iter->second.rva + iter->second.data_size > m_data_sp->GetByteSize())
+        return llvm::None;
+
+    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);
+}
+
+llvm::Optional<std::vector<const MinidumpThread *>>
+MinidumpParser::GetThreads()
+{
+    llvm::Optional<llvm::ArrayRef<uint8_t>> data = GetStream(MinidumpStreamType::ThreadList);
+
+    if (!data)
+        return llvm::None;
+
+    return MinidumpThread::ParseThreadList(data.getValue());
+}
+
+const MinidumpSystemInfo *
+MinidumpParser::GetSystemInfo()
+{
+    llvm::Optional<llvm::ArrayRef<uint8_t>> data = GetStream(MinidumpStreamType::SystemInfo);
+
+    if (!data)
+        return nullptr;
+
+    return MinidumpSystemInfo::Parse(data.getValue());
+}
+
+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)
+        return arch_spec;
+
+    // TODO what to do about big endiand flavors of arm ?
+    // TODO set the arm subarch stuff if the minidump has info about it
+
+    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);
+            break;
+        case MinidumpCPUArchitecture::AMD64:
+            arch_spec.GetTriple().setArch(llvm::Triple::ArchType::x86_64);
+            break;
+        case MinidumpCPUArchitecture::ARM:
+            arch_spec.GetTriple().setArch(llvm::Triple::ArchType::arm);
+            break;
+        case MinidumpCPUArchitecture::ARM64:
+            arch_spec.GetTriple().setArch(llvm::Triple::ArchType::aarch64);
+            break;
+    }
+
+    return arch_spec;
+}
+
+const MinidumpMiscInfo *
+MinidumpParser::GetMiscInfo()
+{
+    llvm::Optional<llvm::ArrayRef<uint8_t>> data = GetStream(MinidumpStreamType::MiscInfo);
+
+    if (!data)
+        return nullptr;
+
+    return MinidumpMiscInfo::Parse(data.getValue());
+}