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