1 //===-- MinidumpParser.cpp ---------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // Project includes
11 #include "MinidumpParser.h"
12 
13 // Other libraries and framework includes
14 // C includes
15 // C++ includes
16 
17 using namespace lldb_private;
18 using namespace minidump;
19 
20 llvm::Optional<MinidumpParser>
21 MinidumpParser::Create(const lldb::DataBufferSP &data_buf_sp) {
22   if (data_buf_sp->GetByteSize() < sizeof(MinidumpHeader)) {
23     return llvm::None;
24   }
25 
26   llvm::ArrayRef<uint8_t> header_data(data_buf_sp->GetBytes(),
27                                       sizeof(MinidumpHeader));
28   const MinidumpHeader *header = MinidumpHeader::Parse(header_data);
29 
30   if (header == nullptr) {
31     return llvm::None;
32   }
33 
34   lldb::offset_t directory_list_offset = header->stream_directory_rva;
35   // check if there is enough data for the parsing of the directory list
36   if ((directory_list_offset +
37        sizeof(MinidumpDirectory) * header->streams_count) >
38       data_buf_sp->GetByteSize()) {
39     return llvm::None;
40   }
41 
42   const MinidumpDirectory *directory = nullptr;
43   Error error;
44   llvm::ArrayRef<uint8_t> directory_data(
45       data_buf_sp->GetBytes() + directory_list_offset,
46       sizeof(MinidumpDirectory) * header->streams_count);
47   llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> directory_map;
48 
49   for (uint32_t i = 0; i < header->streams_count; ++i) {
50     error = consumeObject(directory_data, directory);
51     if (error.Fail()) {
52       return llvm::None;
53     }
54     directory_map[static_cast<const uint32_t>(directory->stream_type)] =
55         directory->location;
56   }
57 
58   return MinidumpParser(data_buf_sp, header, std::move(directory_map));
59 }
60 
61 MinidumpParser::MinidumpParser(
62     const lldb::DataBufferSP &data_buf_sp, const MinidumpHeader *header,
63     llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> &&directory_map)
64     : m_data_sp(data_buf_sp), m_header(header), m_directory_map(directory_map) {
65 }
66 
67 llvm::ArrayRef<uint8_t> MinidumpParser::GetData() {
68   return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes(),
69                                  m_data_sp->GetByteSize());
70 }
71 
72 llvm::ArrayRef<uint8_t>
73 MinidumpParser::GetStream(MinidumpStreamType stream_type) {
74   auto iter = m_directory_map.find(static_cast<uint32_t>(stream_type));
75   if (iter == m_directory_map.end())
76     return {};
77 
78   // check if there is enough data
79   if (iter->second.rva + iter->second.data_size > m_data_sp->GetByteSize())
80     return {};
81 
82   return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes() + iter->second.rva,
83                                  iter->second.data_size);
84 }
85 
86 llvm::Optional<std::string> MinidumpParser::GetMinidumpString(uint32_t rva) {
87   auto arr_ref = m_data_sp->GetData();
88   if (rva > arr_ref.size())
89     return llvm::None;
90   arr_ref = arr_ref.drop_front(rva);
91   return parseMinidumpString(arr_ref);
92 }
93 
94 llvm::ArrayRef<MinidumpThread> MinidumpParser::GetThreads() {
95   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ThreadList);
96 
97   if (data.size() == 0)
98     return llvm::None;
99 
100   return MinidumpThread::ParseThreadList(data);
101 }
102 
103 const MinidumpSystemInfo *MinidumpParser::GetSystemInfo() {
104   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::SystemInfo);
105 
106   if (data.size() == 0)
107     return nullptr;
108 
109   return MinidumpSystemInfo::Parse(data);
110 }
111 
112 ArchSpec MinidumpParser::GetArchitecture() {
113   ArchSpec arch_spec;
114   const MinidumpSystemInfo *system_info = GetSystemInfo();
115 
116   if (!system_info)
117     return arch_spec;
118 
119   // TODO what to do about big endiand flavors of arm ?
120   // TODO set the arm subarch stuff if the minidump has info about it
121 
122   llvm::Triple triple;
123   triple.setVendor(llvm::Triple::VendorType::UnknownVendor);
124 
125   const MinidumpCPUArchitecture arch =
126       static_cast<const MinidumpCPUArchitecture>(
127           static_cast<const uint32_t>(system_info->processor_arch));
128 
129   switch (arch) {
130   case MinidumpCPUArchitecture::X86:
131     triple.setArch(llvm::Triple::ArchType::x86);
132     break;
133   case MinidumpCPUArchitecture::AMD64:
134     triple.setArch(llvm::Triple::ArchType::x86_64);
135     break;
136   case MinidumpCPUArchitecture::ARM:
137     triple.setArch(llvm::Triple::ArchType::arm);
138     break;
139   case MinidumpCPUArchitecture::ARM64:
140     triple.setArch(llvm::Triple::ArchType::aarch64);
141     break;
142   default:
143     triple.setArch(llvm::Triple::ArchType::UnknownArch);
144     break;
145   }
146 
147   const MinidumpOSPlatform os = static_cast<const MinidumpOSPlatform>(
148       static_cast<const uint32_t>(system_info->platform_id));
149 
150   // TODO add all of the OSes that Minidump/breakpad distinguishes?
151   switch (os) {
152   case MinidumpOSPlatform::Win32S:
153   case MinidumpOSPlatform::Win32Windows:
154   case MinidumpOSPlatform::Win32NT:
155   case MinidumpOSPlatform::Win32CE:
156     triple.setOS(llvm::Triple::OSType::Win32);
157     break;
158   case MinidumpOSPlatform::Linux:
159     triple.setOS(llvm::Triple::OSType::Linux);
160     break;
161   case MinidumpOSPlatform::MacOSX:
162     triple.setOS(llvm::Triple::OSType::MacOSX);
163     break;
164   case MinidumpOSPlatform::Android:
165     triple.setOS(llvm::Triple::OSType::Linux);
166     triple.setEnvironment(llvm::Triple::EnvironmentType::Android);
167     break;
168   default:
169     triple.setOS(llvm::Triple::OSType::UnknownOS);
170     break;
171   }
172 
173   arch_spec.SetTriple(triple);
174 
175   return arch_spec;
176 }
177 
178 const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() {
179   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MiscInfo);
180 
181   if (data.size() == 0)
182     return nullptr;
183 
184   return MinidumpMiscInfo::Parse(data);
185 }
186 
187 llvm::Optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() {
188   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::LinuxProcStatus);
189 
190   if (data.size() == 0)
191     return llvm::None;
192 
193   return LinuxProcStatus::Parse(data);
194 }
195 
196 llvm::Optional<lldb::pid_t> MinidumpParser::GetPid() {
197   const MinidumpMiscInfo *misc_info = GetMiscInfo();
198   if (misc_info != nullptr) {
199     return misc_info->GetPid();
200   }
201 
202   llvm::Optional<LinuxProcStatus> proc_status = GetLinuxProcStatus();
203   if (proc_status.hasValue()) {
204     return proc_status->GetPid();
205   }
206 
207   return llvm::None;
208 }
209 
210 llvm::ArrayRef<MinidumpModule> MinidumpParser::GetModuleList() {
211   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ModuleList);
212 
213   if (data.size() == 0)
214     return {};
215 
216   return MinidumpModule::ParseModuleList(data);
217 }
218 
219 const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() {
220   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::Exception);
221 
222   if (data.size() == 0)
223     return nullptr;
224 
225   return MinidumpExceptionStream::Parse(data);
226 }
227