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