Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
39644 views
1
//===-- ProcessMinidump.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 "ProcessMinidump.h"
10
11
#include "ThreadMinidump.h"
12
13
#include "lldb/Core/DumpDataExtractor.h"
14
#include "lldb/Core/Module.h"
15
#include "lldb/Core/ModuleSpec.h"
16
#include "lldb/Core/PluginManager.h"
17
#include "lldb/Core/Section.h"
18
#include "lldb/Interpreter/CommandInterpreter.h"
19
#include "lldb/Interpreter/CommandObject.h"
20
#include "lldb/Interpreter/CommandObjectMultiword.h"
21
#include "lldb/Interpreter/CommandReturnObject.h"
22
#include "lldb/Interpreter/OptionArgParser.h"
23
#include "lldb/Interpreter/OptionGroupBoolean.h"
24
#include "lldb/Target/JITLoaderList.h"
25
#include "lldb/Target/MemoryRegionInfo.h"
26
#include "lldb/Target/SectionLoadList.h"
27
#include "lldb/Target/Target.h"
28
#include "lldb/Target/UnixSignals.h"
29
#include "lldb/Utility/LLDBAssert.h"
30
#include "lldb/Utility/LLDBLog.h"
31
#include "lldb/Utility/Log.h"
32
#include "lldb/Utility/State.h"
33
#include "llvm/BinaryFormat/Magic.h"
34
#include "llvm/Support/MemoryBuffer.h"
35
#include "llvm/Support/Threading.h"
36
37
#include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h"
38
#include "Plugins/Process/Utility/StopInfoMachException.h"
39
40
#include <memory>
41
#include <optional>
42
43
using namespace lldb;
44
using namespace lldb_private;
45
using namespace minidump;
46
47
LLDB_PLUGIN_DEFINE(ProcessMinidump)
48
49
namespace {
50
51
/// Duplicate the HashElfTextSection() from the breakpad sources.
52
///
53
/// Breakpad, a Google crash log reporting tool suite, creates minidump files
54
/// for many different architectures. When using Breakpad to create ELF
55
/// minidumps, it will check for a GNU build ID when creating a minidump file
56
/// and if one doesn't exist in the file, it will say the UUID of the file is a
57
/// checksum of up to the first 4096 bytes of the .text section. Facebook also
58
/// uses breakpad and modified this hash to avoid collisions so we can
59
/// calculate and check for this as well.
60
///
61
/// The breakpad code might end up hashing up to 15 bytes that immediately
62
/// follow the .text section in the file, so this code must do exactly what it
63
/// does so we can get an exact match for the UUID.
64
///
65
/// \param[in] module_sp The module to grab the .text section from.
66
///
67
/// \param[in,out] breakpad_uuid A vector that will receive the calculated
68
/// breakpad .text hash.
69
///
70
/// \param[in,out] facebook_uuid A vector that will receive the calculated
71
/// facebook .text hash.
72
///
73
void HashElfTextSection(ModuleSP module_sp, std::vector<uint8_t> &breakpad_uuid,
74
std::vector<uint8_t> &facebook_uuid) {
75
SectionList *sect_list = module_sp->GetSectionList();
76
if (sect_list == nullptr)
77
return;
78
SectionSP sect_sp = sect_list->FindSectionByName(ConstString(".text"));
79
if (!sect_sp)
80
return;
81
constexpr size_t kMDGUIDSize = 16;
82
constexpr size_t kBreakpadPageSize = 4096;
83
// The breakpad code has a bug where it might access beyond the end of a
84
// .text section by up to 15 bytes, so we must ensure we round up to the
85
// next kMDGUIDSize byte boundary.
86
DataExtractor data;
87
const size_t text_size = sect_sp->GetFileSize();
88
const size_t read_size = std::min<size_t>(
89
llvm::alignTo(text_size, kMDGUIDSize), kBreakpadPageSize);
90
sect_sp->GetObjectFile()->GetData(sect_sp->GetFileOffset(), read_size, data);
91
92
breakpad_uuid.assign(kMDGUIDSize, 0);
93
facebook_uuid.assign(kMDGUIDSize, 0);
94
95
// The only difference between the breakpad hash and the facebook hash is the
96
// hashing of the text section size into the hash prior to hashing the .text
97
// contents.
98
for (size_t i = 0; i < kMDGUIDSize; i++)
99
facebook_uuid[i] ^= text_size % 255;
100
101
// This code carefully duplicates how the hash was created in Breakpad
102
// sources, including the error where it might has an extra 15 bytes past the
103
// end of the .text section if the .text section is less than a page size in
104
// length.
105
const uint8_t *ptr = data.GetDataStart();
106
const uint8_t *ptr_end = data.GetDataEnd();
107
while (ptr < ptr_end) {
108
for (unsigned i = 0; i < kMDGUIDSize; i++) {
109
breakpad_uuid[i] ^= ptr[i];
110
facebook_uuid[i] ^= ptr[i];
111
}
112
ptr += kMDGUIDSize;
113
}
114
}
115
116
} // namespace
117
118
llvm::StringRef ProcessMinidump::GetPluginDescriptionStatic() {
119
return "Minidump plug-in.";
120
}
121
122
lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp,
123
lldb::ListenerSP listener_sp,
124
const FileSpec *crash_file,
125
bool can_connect) {
126
if (!crash_file || can_connect)
127
return nullptr;
128
129
lldb::ProcessSP process_sp;
130
// Read enough data for the Minidump header
131
constexpr size_t header_size = sizeof(Header);
132
auto DataPtr = FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(),
133
header_size, 0);
134
if (!DataPtr)
135
return nullptr;
136
137
lldbassert(DataPtr->GetByteSize() == header_size);
138
if (identify_magic(toStringRef(DataPtr->GetData())) != llvm::file_magic::minidump)
139
return nullptr;
140
141
auto AllData =
142
FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), -1, 0);
143
if (!AllData)
144
return nullptr;
145
146
return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file,
147
std::move(AllData));
148
}
149
150
bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp,
151
bool plugin_specified_by_name) {
152
return true;
153
}
154
155
ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp,
156
lldb::ListenerSP listener_sp,
157
const FileSpec &core_file,
158
DataBufferSP core_data)
159
: PostMortemProcess(target_sp, listener_sp, core_file),
160
m_core_data(std::move(core_data)), m_active_exception(nullptr),
161
m_is_wow64(false) {}
162
163
ProcessMinidump::~ProcessMinidump() {
164
Clear();
165
// We need to call finalize on the process before destroying ourselves to
166
// make sure all of the broadcaster cleanup goes as planned. If we destruct
167
// this class, then Process::~Process() might have problems trying to fully
168
// destroy the broadcaster.
169
Finalize(true /* destructing */);
170
}
171
172
void ProcessMinidump::Initialize() {
173
static llvm::once_flag g_once_flag;
174
175
llvm::call_once(g_once_flag, []() {
176
PluginManager::RegisterPlugin(GetPluginNameStatic(),
177
GetPluginDescriptionStatic(),
178
ProcessMinidump::CreateInstance);
179
});
180
}
181
182
void ProcessMinidump::Terminate() {
183
PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance);
184
}
185
186
Status ProcessMinidump::DoLoadCore() {
187
auto expected_parser = MinidumpParser::Create(m_core_data);
188
if (!expected_parser)
189
return Status(expected_parser.takeError());
190
m_minidump_parser = std::move(*expected_parser);
191
192
Status error;
193
194
// Do we support the minidump's architecture?
195
ArchSpec arch = GetArchitecture();
196
switch (arch.GetMachine()) {
197
case llvm::Triple::x86:
198
case llvm::Triple::x86_64:
199
case llvm::Triple::arm:
200
case llvm::Triple::aarch64:
201
// Any supported architectures must be listed here and also supported in
202
// ThreadMinidump::CreateRegisterContextForFrame().
203
break;
204
default:
205
error.SetErrorStringWithFormat("unsupported minidump architecture: %s",
206
arch.GetArchitectureName());
207
return error;
208
}
209
GetTarget().SetArchitecture(arch, true /*set_platform*/);
210
211
m_thread_list = m_minidump_parser->GetThreads();
212
m_active_exception = m_minidump_parser->GetExceptionStream();
213
214
SetUnixSignals(UnixSignals::Create(GetArchitecture()));
215
216
ReadModuleList();
217
if (ModuleSP module = GetTarget().GetExecutableModule())
218
GetTarget().MergeArchitecture(module->GetArchitecture());
219
std::optional<lldb::pid_t> pid = m_minidump_parser->GetPid();
220
if (!pid) {
221
Debugger::ReportWarning("unable to retrieve process ID from minidump file, "
222
"setting process ID to 1",
223
GetTarget().GetDebugger().GetID());
224
pid = 1;
225
}
226
SetID(*pid);
227
228
return error;
229
}
230
231
Status ProcessMinidump::DoDestroy() { return Status(); }
232
233
void ProcessMinidump::RefreshStateAfterStop() {
234
235
if (!m_active_exception)
236
return;
237
238
constexpr uint32_t BreakpadDumpRequested = 0xFFFFFFFF;
239
if (m_active_exception->ExceptionRecord.ExceptionCode ==
240
BreakpadDumpRequested) {
241
// This "ExceptionCode" value is a sentinel that is sometimes used
242
// when generating a dump for a process that hasn't crashed.
243
244
// TODO: The definition and use of this "dump requested" constant
245
// in Breakpad are actually Linux-specific, and for similar use
246
// cases on Mac/Windows it defines different constants, referring
247
// to them as "simulated" exceptions; consider moving this check
248
// down to the OS-specific paths and checking each OS for its own
249
// constant.
250
return;
251
}
252
253
lldb::StopInfoSP stop_info;
254
lldb::ThreadSP stop_thread;
255
256
Process::m_thread_list.SetSelectedThreadByID(m_active_exception->ThreadId);
257
stop_thread = Process::m_thread_list.GetSelectedThread();
258
ArchSpec arch = GetArchitecture();
259
260
if (arch.GetTriple().getOS() == llvm::Triple::Linux) {
261
uint32_t signo = m_active_exception->ExceptionRecord.ExceptionCode;
262
263
if (signo == 0) {
264
// No stop.
265
return;
266
}
267
268
stop_info = StopInfo::CreateStopReasonWithSignal(
269
*stop_thread, signo);
270
} else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) {
271
stop_info = StopInfoMachException::CreateStopReasonWithMachException(
272
*stop_thread, m_active_exception->ExceptionRecord.ExceptionCode, 2,
273
m_active_exception->ExceptionRecord.ExceptionFlags,
274
m_active_exception->ExceptionRecord.ExceptionAddress, 0);
275
} else {
276
std::string desc;
277
llvm::raw_string_ostream desc_stream(desc);
278
desc_stream << "Exception "
279
<< llvm::format_hex(
280
m_active_exception->ExceptionRecord.ExceptionCode, 8)
281
<< " encountered at address "
282
<< llvm::format_hex(
283
m_active_exception->ExceptionRecord.ExceptionAddress, 8);
284
stop_info = StopInfo::CreateStopReasonWithException(
285
*stop_thread, desc_stream.str().c_str());
286
}
287
288
stop_thread->SetStopInfo(stop_info);
289
}
290
291
bool ProcessMinidump::IsAlive() { return true; }
292
293
bool ProcessMinidump::WarnBeforeDetach() const { return false; }
294
295
size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
296
Status &error) {
297
// Don't allow the caching that lldb_private::Process::ReadMemory does since
298
// we have it all cached in our dump file anyway.
299
return DoReadMemory(addr, buf, size, error);
300
}
301
302
size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
303
Status &error) {
304
305
llvm::ArrayRef<uint8_t> mem = m_minidump_parser->GetMemory(addr, size);
306
if (mem.empty()) {
307
error.SetErrorString("could not parse memory info");
308
return 0;
309
}
310
311
std::memcpy(buf, mem.data(), mem.size());
312
return mem.size();
313
}
314
315
ArchSpec ProcessMinidump::GetArchitecture() {
316
if (!m_is_wow64) {
317
return m_minidump_parser->GetArchitecture();
318
}
319
320
llvm::Triple triple;
321
triple.setVendor(llvm::Triple::VendorType::UnknownVendor);
322
triple.setArch(llvm::Triple::ArchType::x86);
323
triple.setOS(llvm::Triple::OSType::Win32);
324
return ArchSpec(triple);
325
}
326
327
void ProcessMinidump::BuildMemoryRegions() {
328
if (m_memory_regions)
329
return;
330
m_memory_regions.emplace();
331
bool is_complete;
332
std::tie(*m_memory_regions, is_complete) =
333
m_minidump_parser->BuildMemoryRegions();
334
335
if (is_complete)
336
return;
337
338
MemoryRegionInfos to_add;
339
ModuleList &modules = GetTarget().GetImages();
340
SectionLoadList &load_list = GetTarget().GetSectionLoadList();
341
modules.ForEach([&](const ModuleSP &module_sp) {
342
SectionList *sections = module_sp->GetSectionList();
343
for (size_t i = 0; i < sections->GetSize(); ++i) {
344
SectionSP section_sp = sections->GetSectionAtIndex(i);
345
addr_t load_addr = load_list.GetSectionLoadAddress(section_sp);
346
if (load_addr == LLDB_INVALID_ADDRESS)
347
continue;
348
MemoryRegionInfo::RangeType section_range(load_addr,
349
section_sp->GetByteSize());
350
MemoryRegionInfo region =
351
MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr);
352
if (region.GetMapped() != MemoryRegionInfo::eYes &&
353
region.GetRange().GetRangeBase() <= section_range.GetRangeBase() &&
354
section_range.GetRangeEnd() <= region.GetRange().GetRangeEnd()) {
355
to_add.emplace_back();
356
to_add.back().GetRange() = section_range;
357
to_add.back().SetLLDBPermissions(section_sp->GetPermissions());
358
to_add.back().SetMapped(MemoryRegionInfo::eYes);
359
to_add.back().SetName(module_sp->GetFileSpec().GetPath().c_str());
360
}
361
}
362
return true;
363
});
364
m_memory_regions->insert(m_memory_regions->end(), to_add.begin(),
365
to_add.end());
366
llvm::sort(*m_memory_regions);
367
}
368
369
Status ProcessMinidump::DoGetMemoryRegionInfo(lldb::addr_t load_addr,
370
MemoryRegionInfo &region) {
371
BuildMemoryRegions();
372
region = MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr);
373
return Status();
374
}
375
376
Status ProcessMinidump::GetMemoryRegions(MemoryRegionInfos &region_list) {
377
BuildMemoryRegions();
378
region_list = *m_memory_regions;
379
return Status();
380
}
381
382
void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); }
383
384
bool ProcessMinidump::DoUpdateThreadList(ThreadList &old_thread_list,
385
ThreadList &new_thread_list) {
386
for (const minidump::Thread &thread : m_thread_list) {
387
LocationDescriptor context_location = thread.Context;
388
389
// If the minidump contains an exception context, use it
390
if (m_active_exception != nullptr &&
391
m_active_exception->ThreadId == thread.ThreadId) {
392
context_location = m_active_exception->ThreadContext;
393
}
394
395
llvm::ArrayRef<uint8_t> context;
396
if (!m_is_wow64)
397
context = m_minidump_parser->GetThreadContext(context_location);
398
else
399
context = m_minidump_parser->GetThreadContextWow64(thread);
400
401
lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context));
402
new_thread_list.AddThread(thread_sp);
403
}
404
return new_thread_list.GetSize(false) > 0;
405
}
406
407
ModuleSP ProcessMinidump::GetOrCreateModule(UUID minidump_uuid,
408
llvm::StringRef name,
409
ModuleSpec module_spec) {
410
Log *log = GetLog(LLDBLog::DynamicLoader);
411
Status error;
412
413
ModuleSP module_sp =
414
GetTarget().GetOrCreateModule(module_spec, true /* notify */, &error);
415
if (!module_sp)
416
return module_sp;
417
// We consider the module to be a match if the minidump UUID is a
418
// prefix of the actual UUID, or if either of the UUIDs are empty.
419
const auto dmp_bytes = minidump_uuid.GetBytes();
420
const auto mod_bytes = module_sp->GetUUID().GetBytes();
421
const bool match = dmp_bytes.empty() || mod_bytes.empty() ||
422
mod_bytes.take_front(dmp_bytes.size()) == dmp_bytes;
423
if (match) {
424
LLDB_LOG(log, "Partial uuid match for {0}.", name);
425
return module_sp;
426
}
427
428
// Breakpad generates minindump files, and if there is no GNU build
429
// ID in the binary, it will calculate a UUID by hashing first 4096
430
// bytes of the .text section and using that as the UUID for a module
431
// in the minidump. Facebook uses a modified breakpad client that
432
// uses a slightly modified this hash to avoid collisions. Check for
433
// UUIDs from the minindump that match these cases and accept the
434
// module we find if they do match.
435
std::vector<uint8_t> breakpad_uuid;
436
std::vector<uint8_t> facebook_uuid;
437
HashElfTextSection(module_sp, breakpad_uuid, facebook_uuid);
438
if (dmp_bytes == llvm::ArrayRef<uint8_t>(breakpad_uuid)) {
439
LLDB_LOG(log, "Breakpad .text hash match for {0}.", name);
440
return module_sp;
441
}
442
if (dmp_bytes == llvm::ArrayRef<uint8_t>(facebook_uuid)) {
443
LLDB_LOG(log, "Facebook .text hash match for {0}.", name);
444
return module_sp;
445
}
446
// The UUID wasn't a partial match and didn't match the .text hash
447
// so remove the module from the target, we will need to create a
448
// placeholder object file.
449
GetTarget().GetImages().Remove(module_sp);
450
module_sp.reset();
451
return module_sp;
452
}
453
454
void ProcessMinidump::ReadModuleList() {
455
std::vector<const minidump::Module *> filtered_modules =
456
m_minidump_parser->GetFilteredModuleList();
457
458
Log *log = GetLog(LLDBLog::DynamicLoader);
459
460
for (auto module : filtered_modules) {
461
std::string name = cantFail(m_minidump_parser->GetMinidumpFile().getString(
462
module->ModuleNameRVA));
463
const uint64_t load_addr = module->BaseOfImage;
464
const uint64_t load_size = module->SizeOfImage;
465
LLDB_LOG(log, "found module: name: {0} {1:x10}-{2:x10} size: {3}", name,
466
load_addr, load_addr + load_size, load_size);
467
468
// check if the process is wow64 - a 32 bit windows process running on a
469
// 64 bit windows
470
if (llvm::StringRef(name).ends_with_insensitive("wow64.dll")) {
471
m_is_wow64 = true;
472
}
473
474
const auto uuid = m_minidump_parser->GetModuleUUID(module);
475
auto file_spec = FileSpec(name, GetArchitecture().GetTriple());
476
ModuleSpec module_spec(file_spec, uuid);
477
module_spec.GetArchitecture() = GetArchitecture();
478
Status error;
479
// Try and find a module with a full UUID that matches. This function will
480
// add the module to the target if it finds one.
481
lldb::ModuleSP module_sp = GetTarget().GetOrCreateModule(module_spec,
482
true /* notify */, &error);
483
if (module_sp) {
484
LLDB_LOG(log, "Full uuid match for {0}.", name);
485
} else {
486
// We couldn't find a module with an exactly-matching UUID. Sometimes
487
// a minidump UUID is only a partial match or is a hash. So try again
488
// without specifying the UUID, then again without specifying the
489
// directory if that fails. This will allow us to find modules with
490
// partial matches or hash UUIDs in user-provided sysroots or search
491
// directories (target.exec-search-paths).
492
ModuleSpec partial_module_spec = module_spec;
493
partial_module_spec.GetUUID().Clear();
494
module_sp = GetOrCreateModule(uuid, name, partial_module_spec);
495
if (!module_sp) {
496
partial_module_spec.GetFileSpec().ClearDirectory();
497
module_sp = GetOrCreateModule(uuid, name, partial_module_spec);
498
}
499
}
500
if (module_sp) {
501
// Watch out for place holder modules that have different paths, but the
502
// same UUID. If the base address is different, create a new module. If
503
// we don't then we will end up setting the load address of a different
504
// ObjectFilePlaceholder and an assertion will fire.
505
auto *objfile = module_sp->GetObjectFile();
506
if (objfile &&
507
objfile->GetPluginName() ==
508
ObjectFilePlaceholder::GetPluginNameStatic()) {
509
if (((ObjectFilePlaceholder *)objfile)->GetBaseImageAddress() !=
510
load_addr)
511
module_sp.reset();
512
}
513
}
514
if (!module_sp) {
515
// We failed to locate a matching local object file. Fortunately, the
516
// minidump format encodes enough information about each module's memory
517
// range to allow us to create placeholder modules.
518
//
519
// This enables most LLDB functionality involving address-to-module
520
// translations (ex. identifing the module for a stack frame PC) and
521
// modules/sections commands (ex. target modules list, ...)
522
LLDB_LOG(log,
523
"Unable to locate the matching object file, creating a "
524
"placeholder module for: {0}",
525
name);
526
527
module_sp = Module::CreateModuleFromObjectFile<ObjectFilePlaceholder>(
528
module_spec, load_addr, load_size);
529
GetTarget().GetImages().Append(module_sp, true /* notify */);
530
}
531
532
bool load_addr_changed = false;
533
module_sp->SetLoadAddress(GetTarget(), load_addr, false,
534
load_addr_changed);
535
}
536
}
537
538
bool ProcessMinidump::GetProcessInfo(ProcessInstanceInfo &info) {
539
info.Clear();
540
info.SetProcessID(GetID());
541
info.SetArchitecture(GetArchitecture());
542
lldb::ModuleSP module_sp = GetTarget().GetExecutableModule();
543
if (module_sp) {
544
const bool add_exe_file_as_first_arg = false;
545
info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),
546
add_exe_file_as_first_arg);
547
}
548
return true;
549
}
550
551
// For minidumps there's no runtime generated code so we don't need JITLoader(s)
552
// Avoiding them will also speed up minidump loading since JITLoaders normally
553
// try to set up symbolic breakpoints, which in turn may force loading more
554
// debug information than needed.
555
JITLoaderList &ProcessMinidump::GetJITLoaders() {
556
if (!m_jit_loaders_up) {
557
m_jit_loaders_up = std::make_unique<JITLoaderList>();
558
}
559
return *m_jit_loaders_up;
560
}
561
562
#define INIT_BOOL(VAR, LONG, SHORT, DESC) \
563
VAR(LLDB_OPT_SET_1, false, LONG, SHORT, DESC, false, true)
564
#define APPEND_OPT(VAR) \
565
m_option_group.Append(&VAR, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1)
566
567
class CommandObjectProcessMinidumpDump : public CommandObjectParsed {
568
private:
569
OptionGroupOptions m_option_group;
570
OptionGroupBoolean m_dump_all;
571
OptionGroupBoolean m_dump_directory;
572
OptionGroupBoolean m_dump_linux_cpuinfo;
573
OptionGroupBoolean m_dump_linux_proc_status;
574
OptionGroupBoolean m_dump_linux_lsb_release;
575
OptionGroupBoolean m_dump_linux_cmdline;
576
OptionGroupBoolean m_dump_linux_environ;
577
OptionGroupBoolean m_dump_linux_auxv;
578
OptionGroupBoolean m_dump_linux_maps;
579
OptionGroupBoolean m_dump_linux_proc_stat;
580
OptionGroupBoolean m_dump_linux_proc_uptime;
581
OptionGroupBoolean m_dump_linux_proc_fd;
582
OptionGroupBoolean m_dump_linux_all;
583
OptionGroupBoolean m_fb_app_data;
584
OptionGroupBoolean m_fb_build_id;
585
OptionGroupBoolean m_fb_version;
586
OptionGroupBoolean m_fb_java_stack;
587
OptionGroupBoolean m_fb_dalvik;
588
OptionGroupBoolean m_fb_unwind;
589
OptionGroupBoolean m_fb_error_log;
590
OptionGroupBoolean m_fb_app_state;
591
OptionGroupBoolean m_fb_abort;
592
OptionGroupBoolean m_fb_thread;
593
OptionGroupBoolean m_fb_logcat;
594
OptionGroupBoolean m_fb_all;
595
596
void SetDefaultOptionsIfNoneAreSet() {
597
if (m_dump_all.GetOptionValue().GetCurrentValue() ||
598
m_dump_linux_all.GetOptionValue().GetCurrentValue() ||
599
m_fb_all.GetOptionValue().GetCurrentValue() ||
600
m_dump_directory.GetOptionValue().GetCurrentValue() ||
601
m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue() ||
602
m_dump_linux_proc_status.GetOptionValue().GetCurrentValue() ||
603
m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue() ||
604
m_dump_linux_cmdline.GetOptionValue().GetCurrentValue() ||
605
m_dump_linux_environ.GetOptionValue().GetCurrentValue() ||
606
m_dump_linux_auxv.GetOptionValue().GetCurrentValue() ||
607
m_dump_linux_maps.GetOptionValue().GetCurrentValue() ||
608
m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue() ||
609
m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue() ||
610
m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue() ||
611
m_fb_app_data.GetOptionValue().GetCurrentValue() ||
612
m_fb_build_id.GetOptionValue().GetCurrentValue() ||
613
m_fb_version.GetOptionValue().GetCurrentValue() ||
614
m_fb_java_stack.GetOptionValue().GetCurrentValue() ||
615
m_fb_dalvik.GetOptionValue().GetCurrentValue() ||
616
m_fb_unwind.GetOptionValue().GetCurrentValue() ||
617
m_fb_error_log.GetOptionValue().GetCurrentValue() ||
618
m_fb_app_state.GetOptionValue().GetCurrentValue() ||
619
m_fb_abort.GetOptionValue().GetCurrentValue() ||
620
m_fb_thread.GetOptionValue().GetCurrentValue() ||
621
m_fb_logcat.GetOptionValue().GetCurrentValue())
622
return;
623
// If no options were set, then dump everything
624
m_dump_all.GetOptionValue().SetCurrentValue(true);
625
}
626
bool DumpAll() const {
627
return m_dump_all.GetOptionValue().GetCurrentValue();
628
}
629
bool DumpDirectory() const {
630
return DumpAll() ||
631
m_dump_directory.GetOptionValue().GetCurrentValue();
632
}
633
bool DumpLinux() const {
634
return DumpAll() || m_dump_linux_all.GetOptionValue().GetCurrentValue();
635
}
636
bool DumpLinuxCPUInfo() const {
637
return DumpLinux() ||
638
m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue();
639
}
640
bool DumpLinuxProcStatus() const {
641
return DumpLinux() ||
642
m_dump_linux_proc_status.GetOptionValue().GetCurrentValue();
643
}
644
bool DumpLinuxProcStat() const {
645
return DumpLinux() ||
646
m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue();
647
}
648
bool DumpLinuxLSBRelease() const {
649
return DumpLinux() ||
650
m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue();
651
}
652
bool DumpLinuxCMDLine() const {
653
return DumpLinux() ||
654
m_dump_linux_cmdline.GetOptionValue().GetCurrentValue();
655
}
656
bool DumpLinuxEnviron() const {
657
return DumpLinux() ||
658
m_dump_linux_environ.GetOptionValue().GetCurrentValue();
659
}
660
bool DumpLinuxAuxv() const {
661
return DumpLinux() ||
662
m_dump_linux_auxv.GetOptionValue().GetCurrentValue();
663
}
664
bool DumpLinuxMaps() const {
665
return DumpLinux() ||
666
m_dump_linux_maps.GetOptionValue().GetCurrentValue();
667
}
668
bool DumpLinuxProcUptime() const {
669
return DumpLinux() ||
670
m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue();
671
}
672
bool DumpLinuxProcFD() const {
673
return DumpLinux() ||
674
m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue();
675
}
676
bool DumpFacebook() const {
677
return DumpAll() || m_fb_all.GetOptionValue().GetCurrentValue();
678
}
679
bool DumpFacebookAppData() const {
680
return DumpFacebook() || m_fb_app_data.GetOptionValue().GetCurrentValue();
681
}
682
bool DumpFacebookBuildID() const {
683
return DumpFacebook() || m_fb_build_id.GetOptionValue().GetCurrentValue();
684
}
685
bool DumpFacebookVersionName() const {
686
return DumpFacebook() || m_fb_version.GetOptionValue().GetCurrentValue();
687
}
688
bool DumpFacebookJavaStack() const {
689
return DumpFacebook() || m_fb_java_stack.GetOptionValue().GetCurrentValue();
690
}
691
bool DumpFacebookDalvikInfo() const {
692
return DumpFacebook() || m_fb_dalvik.GetOptionValue().GetCurrentValue();
693
}
694
bool DumpFacebookUnwindSymbols() const {
695
return DumpFacebook() || m_fb_unwind.GetOptionValue().GetCurrentValue();
696
}
697
bool DumpFacebookErrorLog() const {
698
return DumpFacebook() || m_fb_error_log.GetOptionValue().GetCurrentValue();
699
}
700
bool DumpFacebookAppStateLog() const {
701
return DumpFacebook() || m_fb_app_state.GetOptionValue().GetCurrentValue();
702
}
703
bool DumpFacebookAbortReason() const {
704
return DumpFacebook() || m_fb_abort.GetOptionValue().GetCurrentValue();
705
}
706
bool DumpFacebookThreadName() const {
707
return DumpFacebook() || m_fb_thread.GetOptionValue().GetCurrentValue();
708
}
709
bool DumpFacebookLogcat() const {
710
return DumpFacebook() || m_fb_logcat.GetOptionValue().GetCurrentValue();
711
}
712
public:
713
CommandObjectProcessMinidumpDump(CommandInterpreter &interpreter)
714
: CommandObjectParsed(interpreter, "process plugin dump",
715
"Dump information from the minidump file.", nullptr),
716
m_option_group(),
717
INIT_BOOL(m_dump_all, "all", 'a',
718
"Dump the everything in the minidump."),
719
INIT_BOOL(m_dump_directory, "directory", 'd',
720
"Dump the minidump directory map."),
721
INIT_BOOL(m_dump_linux_cpuinfo, "cpuinfo", 'C',
722
"Dump linux /proc/cpuinfo."),
723
INIT_BOOL(m_dump_linux_proc_status, "status", 's',
724
"Dump linux /proc/<pid>/status."),
725
INIT_BOOL(m_dump_linux_lsb_release, "lsb-release", 'r',
726
"Dump linux /etc/lsb-release."),
727
INIT_BOOL(m_dump_linux_cmdline, "cmdline", 'c',
728
"Dump linux /proc/<pid>/cmdline."),
729
INIT_BOOL(m_dump_linux_environ, "environ", 'e',
730
"Dump linux /proc/<pid>/environ."),
731
INIT_BOOL(m_dump_linux_auxv, "auxv", 'x',
732
"Dump linux /proc/<pid>/auxv."),
733
INIT_BOOL(m_dump_linux_maps, "maps", 'm',
734
"Dump linux /proc/<pid>/maps."),
735
INIT_BOOL(m_dump_linux_proc_stat, "stat", 'S',
736
"Dump linux /proc/<pid>/stat."),
737
INIT_BOOL(m_dump_linux_proc_uptime, "uptime", 'u',
738
"Dump linux process uptime."),
739
INIT_BOOL(m_dump_linux_proc_fd, "fd", 'f',
740
"Dump linux /proc/<pid>/fd."),
741
INIT_BOOL(m_dump_linux_all, "linux", 'l',
742
"Dump all linux streams."),
743
INIT_BOOL(m_fb_app_data, "fb-app-data", 1,
744
"Dump Facebook application custom data."),
745
INIT_BOOL(m_fb_build_id, "fb-build-id", 2,
746
"Dump the Facebook build ID."),
747
INIT_BOOL(m_fb_version, "fb-version", 3,
748
"Dump Facebook application version string."),
749
INIT_BOOL(m_fb_java_stack, "fb-java-stack", 4,
750
"Dump Facebook java stack."),
751
INIT_BOOL(m_fb_dalvik, "fb-dalvik-info", 5,
752
"Dump Facebook Dalvik info."),
753
INIT_BOOL(m_fb_unwind, "fb-unwind-symbols", 6,
754
"Dump Facebook unwind symbols."),
755
INIT_BOOL(m_fb_error_log, "fb-error-log", 7,
756
"Dump Facebook error log."),
757
INIT_BOOL(m_fb_app_state, "fb-app-state-log", 8,
758
"Dump Facebook java stack."),
759
INIT_BOOL(m_fb_abort, "fb-abort-reason", 9,
760
"Dump Facebook abort reason."),
761
INIT_BOOL(m_fb_thread, "fb-thread-name", 10,
762
"Dump Facebook thread name."),
763
INIT_BOOL(m_fb_logcat, "fb-logcat", 11,
764
"Dump Facebook logcat."),
765
INIT_BOOL(m_fb_all, "facebook", 12, "Dump all Facebook streams.") {
766
APPEND_OPT(m_dump_all);
767
APPEND_OPT(m_dump_directory);
768
APPEND_OPT(m_dump_linux_cpuinfo);
769
APPEND_OPT(m_dump_linux_proc_status);
770
APPEND_OPT(m_dump_linux_lsb_release);
771
APPEND_OPT(m_dump_linux_cmdline);
772
APPEND_OPT(m_dump_linux_environ);
773
APPEND_OPT(m_dump_linux_auxv);
774
APPEND_OPT(m_dump_linux_maps);
775
APPEND_OPT(m_dump_linux_proc_stat);
776
APPEND_OPT(m_dump_linux_proc_uptime);
777
APPEND_OPT(m_dump_linux_proc_fd);
778
APPEND_OPT(m_dump_linux_all);
779
APPEND_OPT(m_fb_app_data);
780
APPEND_OPT(m_fb_build_id);
781
APPEND_OPT(m_fb_version);
782
APPEND_OPT(m_fb_java_stack);
783
APPEND_OPT(m_fb_dalvik);
784
APPEND_OPT(m_fb_unwind);
785
APPEND_OPT(m_fb_error_log);
786
APPEND_OPT(m_fb_app_state);
787
APPEND_OPT(m_fb_abort);
788
APPEND_OPT(m_fb_thread);
789
APPEND_OPT(m_fb_logcat);
790
APPEND_OPT(m_fb_all);
791
m_option_group.Finalize();
792
}
793
794
~CommandObjectProcessMinidumpDump() override = default;
795
796
Options *GetOptions() override { return &m_option_group; }
797
798
void DoExecute(Args &command, CommandReturnObject &result) override {
799
const size_t argc = command.GetArgumentCount();
800
if (argc > 0) {
801
result.AppendErrorWithFormat("'%s' take no arguments, only options",
802
m_cmd_name.c_str());
803
return;
804
}
805
SetDefaultOptionsIfNoneAreSet();
806
807
ProcessMinidump *process = static_cast<ProcessMinidump *>(
808
m_interpreter.GetExecutionContext().GetProcessPtr());
809
result.SetStatus(eReturnStatusSuccessFinishResult);
810
Stream &s = result.GetOutputStream();
811
MinidumpParser &minidump = *process->m_minidump_parser;
812
if (DumpDirectory()) {
813
s.Printf("RVA SIZE TYPE StreamType\n");
814
s.Printf("---------- ---------- ---------- --------------------------\n");
815
for (const auto &stream_desc : minidump.GetMinidumpFile().streams())
816
s.Printf(
817
"0x%8.8x 0x%8.8x 0x%8.8x %s\n", (uint32_t)stream_desc.Location.RVA,
818
(uint32_t)stream_desc.Location.DataSize,
819
(unsigned)(StreamType)stream_desc.Type,
820
MinidumpParser::GetStreamTypeAsString(stream_desc.Type).data());
821
s.Printf("\n");
822
}
823
auto DumpTextStream = [&](StreamType stream_type,
824
llvm::StringRef label) -> void {
825
auto bytes = minidump.GetStream(stream_type);
826
if (!bytes.empty()) {
827
if (label.empty())
828
label = MinidumpParser::GetStreamTypeAsString(stream_type);
829
s.Printf("%s:\n%s\n\n", label.data(), bytes.data());
830
}
831
};
832
auto DumpBinaryStream = [&](StreamType stream_type,
833
llvm::StringRef label) -> void {
834
auto bytes = minidump.GetStream(stream_type);
835
if (!bytes.empty()) {
836
if (label.empty())
837
label = MinidumpParser::GetStreamTypeAsString(stream_type);
838
s.Printf("%s:\n", label.data());
839
DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,
840
process->GetAddressByteSize());
841
DumpDataExtractor(data, &s, 0, lldb::eFormatBytesWithASCII, 1,
842
bytes.size(), 16, 0, 0, 0);
843
s.Printf("\n\n");
844
}
845
};
846
847
if (DumpLinuxCPUInfo())
848
DumpTextStream(StreamType::LinuxCPUInfo, "/proc/cpuinfo");
849
if (DumpLinuxProcStatus())
850
DumpTextStream(StreamType::LinuxProcStatus, "/proc/PID/status");
851
if (DumpLinuxLSBRelease())
852
DumpTextStream(StreamType::LinuxLSBRelease, "/etc/lsb-release");
853
if (DumpLinuxCMDLine())
854
DumpTextStream(StreamType::LinuxCMDLine, "/proc/PID/cmdline");
855
if (DumpLinuxEnviron())
856
DumpTextStream(StreamType::LinuxEnviron, "/proc/PID/environ");
857
if (DumpLinuxAuxv())
858
DumpBinaryStream(StreamType::LinuxAuxv, "/proc/PID/auxv");
859
if (DumpLinuxMaps())
860
DumpTextStream(StreamType::LinuxMaps, "/proc/PID/maps");
861
if (DumpLinuxProcStat())
862
DumpTextStream(StreamType::LinuxProcStat, "/proc/PID/stat");
863
if (DumpLinuxProcUptime())
864
DumpTextStream(StreamType::LinuxProcUptime, "uptime");
865
if (DumpLinuxProcFD())
866
DumpTextStream(StreamType::LinuxProcFD, "/proc/PID/fd");
867
if (DumpFacebookAppData())
868
DumpTextStream(StreamType::FacebookAppCustomData,
869
"Facebook App Data");
870
if (DumpFacebookBuildID()) {
871
auto bytes = minidump.GetStream(StreamType::FacebookBuildID);
872
if (bytes.size() >= 4) {
873
DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,
874
process->GetAddressByteSize());
875
lldb::offset_t offset = 0;
876
uint32_t build_id = data.GetU32(&offset);
877
s.Printf("Facebook Build ID:\n");
878
s.Printf("%u\n", build_id);
879
s.Printf("\n");
880
}
881
}
882
if (DumpFacebookVersionName())
883
DumpTextStream(StreamType::FacebookAppVersionName,
884
"Facebook Version String");
885
if (DumpFacebookJavaStack())
886
DumpTextStream(StreamType::FacebookJavaStack,
887
"Facebook Java Stack");
888
if (DumpFacebookDalvikInfo())
889
DumpTextStream(StreamType::FacebookDalvikInfo,
890
"Facebook Dalvik Info");
891
if (DumpFacebookUnwindSymbols())
892
DumpBinaryStream(StreamType::FacebookUnwindSymbols,
893
"Facebook Unwind Symbols Bytes");
894
if (DumpFacebookErrorLog())
895
DumpTextStream(StreamType::FacebookDumpErrorLog,
896
"Facebook Error Log");
897
if (DumpFacebookAppStateLog())
898
DumpTextStream(StreamType::FacebookAppStateLog,
899
"Faceook Application State Log");
900
if (DumpFacebookAbortReason())
901
DumpTextStream(StreamType::FacebookAbortReason,
902
"Facebook Abort Reason");
903
if (DumpFacebookThreadName())
904
DumpTextStream(StreamType::FacebookThreadName,
905
"Facebook Thread Name");
906
if (DumpFacebookLogcat())
907
DumpTextStream(StreamType::FacebookLogcat, "Facebook Logcat");
908
}
909
};
910
911
class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword {
912
public:
913
CommandObjectMultiwordProcessMinidump(CommandInterpreter &interpreter)
914
: CommandObjectMultiword(interpreter, "process plugin",
915
"Commands for operating on a ProcessMinidump process.",
916
"process plugin <subcommand> [<subcommand-options>]") {
917
LoadSubCommand("dump",
918
CommandObjectSP(new CommandObjectProcessMinidumpDump(interpreter)));
919
}
920
921
~CommandObjectMultiwordProcessMinidump() override = default;
922
};
923
924
CommandObject *ProcessMinidump::GetPluginCommandObject() {
925
if (!m_command_sp)
926
m_command_sp = std::make_shared<CommandObjectMultiwordProcessMinidump>(
927
GetTarget().GetDebugger().GetCommandInterpreter());
928
return m_command_sp.get();
929
}
930
931