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