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 
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 
40 using namespace lldb;
41 using namespace lldb_private;
42 using namespace minidump;
43 
44 //------------------------------------------------------------------
45 /// A placeholder module used for minidumps, where the original
46 /// object files may not be available (so we can't parse the object
47 /// files to extract the set of sections/segments)
48 ///
49 /// This placeholder module has a single synthetic section (.module_image)
50 /// which represents the module memory range covering the whole module.
51 //------------------------------------------------------------------
52 class PlaceholderModule : public Module {
53 public:
54   PlaceholderModule(const ModuleSpec &module_spec) :
55     Module(module_spec.GetFileSpec(), module_spec.GetArchitecture()) {
56     if (module_spec.GetUUID().IsValid())
57       SetUUID(module_spec.GetUUID());
58   }
59 
60   // Creates a synthetic module section covering the whole module image (and
61   // sets the section load address as well)
62   void CreateImageSection(const MinidumpModule *module, Target& target) {
63     const ConstString section_name(".module_image");
64     lldb::SectionSP section_sp(new Section(
65         shared_from_this(),     // Module to which this section belongs.
66         nullptr,                // ObjectFile
67         0,                      // Section ID.
68         section_name,           // Section name.
69         eSectionTypeContainer,  // Section type.
70         module->base_of_image,  // VM address.
71         module->size_of_image,  // VM size in bytes of this section.
72         0,                      // Offset of this section in the file.
73         module->size_of_image,  // Size of the section as found in the file.
74         12,                     // Alignment of the section (log2)
75         0,                      // Flags for this section.
76         1));                    // Number of host bytes per target byte
77     section_sp->SetPermissions(ePermissionsExecutable | ePermissionsReadable);
78     GetSectionList()->AddSection(section_sp);
79     target.GetSectionLoadList().SetSectionLoadAddress(
80         section_sp, module->base_of_image);
81   }
82 
83 ObjectFile *GetObjectFile() override { return nullptr; }
84 
85   SectionList *GetSectionList() override {
86     return Module::GetUnifiedSectionList();
87   }
88 };
89 
90 ConstString ProcessMinidump::GetPluginNameStatic() {
91   static ConstString g_name("minidump");
92   return g_name;
93 }
94 
95 const char *ProcessMinidump::GetPluginDescriptionStatic() {
96   return "Minidump plug-in.";
97 }
98 
99 lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp,
100                                                 lldb::ListenerSP listener_sp,
101                                                 const FileSpec *crash_file) {
102   if (!crash_file)
103     return nullptr;
104 
105   lldb::ProcessSP process_sp;
106   // Read enough data for the Minidump header
107   constexpr size_t header_size = sizeof(MinidumpHeader);
108   auto DataPtr = FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(),
109                                                          header_size, 0);
110   if (!DataPtr)
111     return nullptr;
112 
113   lldbassert(DataPtr->GetByteSize() == header_size);
114 
115   // first, only try to parse the header, beacuse we need to be fast
116   llvm::ArrayRef<uint8_t> HeaderBytes = DataPtr->GetData();
117   const MinidumpHeader *header = MinidumpHeader::Parse(HeaderBytes);
118   if (header == nullptr)
119     return nullptr;
120 
121   auto AllData =
122       FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), -1, 0);
123   if (!AllData)
124     return nullptr;
125 
126   return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file,
127                                            std::move(AllData));
128 }
129 
130 bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp,
131                                bool plugin_specified_by_name) {
132   return true;
133 }
134 
135 ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp,
136                                  lldb::ListenerSP listener_sp,
137                                  const FileSpec &core_file,
138                                  DataBufferSP core_data)
139     : Process(target_sp, listener_sp), m_core_file(core_file),
140       m_core_data(std::move(core_data)), m_is_wow64(false) {}
141 
142 ProcessMinidump::~ProcessMinidump() {
143   Clear();
144   // We need to call finalize on the process before destroying ourselves to
145   // make sure all of the broadcaster cleanup goes as planned. If we destruct
146   // this class, then Process::~Process() might have problems trying to fully
147   // destroy the broadcaster.
148   Finalize();
149 }
150 
151 void ProcessMinidump::Initialize() {
152   static llvm::once_flag g_once_flag;
153 
154   llvm::call_once(g_once_flag, []() {
155     PluginManager::RegisterPlugin(GetPluginNameStatic(),
156                                   GetPluginDescriptionStatic(),
157                                   ProcessMinidump::CreateInstance);
158   });
159 }
160 
161 void ProcessMinidump::Terminate() {
162   PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance);
163 }
164 
165 Status ProcessMinidump::DoLoadCore() {
166   auto expected_parser = MinidumpParser::Create(m_core_data);
167   if (!expected_parser)
168     return Status(expected_parser.takeError());
169   m_minidump_parser = std::move(*expected_parser);
170 
171   Status error;
172 
173   // Do we support the minidump's architecture?
174   ArchSpec arch = GetArchitecture();
175   switch (arch.GetMachine()) {
176   case llvm::Triple::x86:
177   case llvm::Triple::x86_64:
178   case llvm::Triple::arm:
179   case llvm::Triple::aarch64:
180     // Any supported architectures must be listed here and also supported in
181     // ThreadMinidump::CreateRegisterContextForFrame().
182     break;
183   default:
184     error.SetErrorStringWithFormat("unsupported minidump architecture: %s",
185                                    arch.GetArchitectureName());
186     return error;
187   }
188   GetTarget().SetArchitecture(arch, true /*set_platform*/);
189 
190   m_thread_list = m_minidump_parser->GetThreads();
191   m_active_exception = m_minidump_parser->GetExceptionStream();
192   ReadModuleList();
193 
194   llvm::Optional<lldb::pid_t> pid = m_minidump_parser->GetPid();
195   if (!pid) {
196     error.SetErrorString("failed to parse PID");
197     return error;
198   }
199   SetID(pid.getValue());
200 
201   return error;
202 }
203 
204 ConstString ProcessMinidump::GetPluginName() { return GetPluginNameStatic(); }
205 
206 uint32_t ProcessMinidump::GetPluginVersion() { return 1; }
207 
208 Status ProcessMinidump::DoDestroy() { return Status(); }
209 
210 void ProcessMinidump::RefreshStateAfterStop() {
211   if (!m_active_exception)
212     return;
213 
214   if (m_active_exception->exception_record.exception_code ==
215       MinidumpException::DumpRequested) {
216     return;
217   }
218 
219   lldb::StopInfoSP stop_info;
220   lldb::ThreadSP stop_thread;
221 
222   Process::m_thread_list.SetSelectedThreadByID(m_active_exception->thread_id);
223   stop_thread = Process::m_thread_list.GetSelectedThread();
224   ArchSpec arch = GetArchitecture();
225 
226   if (arch.GetTriple().getOS() == llvm::Triple::Linux) {
227     stop_info = StopInfo::CreateStopReasonWithSignal(
228         *stop_thread, m_active_exception->exception_record.exception_code);
229   } else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) {
230     stop_info = StopInfoMachException::CreateStopReasonWithMachException(
231         *stop_thread, m_active_exception->exception_record.exception_code, 2,
232         m_active_exception->exception_record.exception_flags,
233         m_active_exception->exception_record.exception_address, 0);
234   } else {
235     std::string desc;
236     llvm::raw_string_ostream desc_stream(desc);
237     desc_stream << "Exception "
238                 << llvm::format_hex(
239                        m_active_exception->exception_record.exception_code, 8)
240                 << " encountered at address "
241                 << llvm::format_hex(
242                        m_active_exception->exception_record.exception_address,
243                        8);
244     stop_info = StopInfo::CreateStopReasonWithException(
245         *stop_thread, desc_stream.str().c_str());
246   }
247 
248   stop_thread->SetStopInfo(stop_info);
249 }
250 
251 bool ProcessMinidump::IsAlive() { return true; }
252 
253 bool ProcessMinidump::WarnBeforeDetach() const { return false; }
254 
255 size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
256                                    Status &error) {
257   // Don't allow the caching that lldb_private::Process::ReadMemory does since
258   // we have it all cached in our dump file anyway.
259   return DoReadMemory(addr, buf, size, error);
260 }
261 
262 size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
263                                      Status &error) {
264 
265   llvm::ArrayRef<uint8_t> mem = m_minidump_parser->GetMemory(addr, size);
266   if (mem.empty()) {
267     error.SetErrorString("could not parse memory info");
268     return 0;
269   }
270 
271   std::memcpy(buf, mem.data(), mem.size());
272   return mem.size();
273 }
274 
275 ArchSpec ProcessMinidump::GetArchitecture() {
276   if (!m_is_wow64) {
277     return m_minidump_parser->GetArchitecture();
278   }
279 
280   llvm::Triple triple;
281   triple.setVendor(llvm::Triple::VendorType::UnknownVendor);
282   triple.setArch(llvm::Triple::ArchType::x86);
283   triple.setOS(llvm::Triple::OSType::Win32);
284   return ArchSpec(triple);
285 }
286 
287 Status ProcessMinidump::GetMemoryRegionInfo(lldb::addr_t load_addr,
288                                             MemoryRegionInfo &range_info) {
289   range_info = m_minidump_parser->GetMemoryRegionInfo(load_addr);
290   return Status();
291 }
292 
293 Status ProcessMinidump::GetMemoryRegions(
294     lldb_private::MemoryRegionInfos &region_list) {
295   region_list = m_minidump_parser->GetMemoryRegions();
296   return Status();
297 }
298 
299 void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); }
300 
301 bool ProcessMinidump::UpdateThreadList(ThreadList &old_thread_list,
302                                        ThreadList &new_thread_list) {
303   for (const MinidumpThread& thread : m_thread_list) {
304     MinidumpLocationDescriptor context_location = thread.thread_context;
305 
306     // If the minidump contains an exception context, use it
307     if (m_active_exception != nullptr &&
308         m_active_exception->thread_id == thread.thread_id) {
309       context_location = m_active_exception->thread_context;
310     }
311 
312     llvm::ArrayRef<uint8_t> context;
313     if (!m_is_wow64)
314       context = m_minidump_parser->GetThreadContext(context_location);
315     else
316       context = m_minidump_parser->GetThreadContextWow64(thread);
317 
318     lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context));
319     new_thread_list.AddThread(thread_sp);
320   }
321   return new_thread_list.GetSize(false) > 0;
322 }
323 
324 void ProcessMinidump::ReadModuleList() {
325   std::vector<const MinidumpModule *> filtered_modules =
326       m_minidump_parser->GetFilteredModuleList();
327 
328   for (auto module : filtered_modules) {
329     llvm::Optional<std::string> name =
330         m_minidump_parser->GetMinidumpString(module->module_name_rva);
331 
332     if (!name)
333       continue;
334 
335     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
336     if (log) {
337       log->Printf("ProcessMinidump::%s found module: name: %s %#010" PRIx64
338                   "-%#010" PRIx64 " size: %" PRIu32,
339                   __FUNCTION__, name.getValue().c_str(),
340                   uint64_t(module->base_of_image),
341                   module->base_of_image + module->size_of_image,
342                   uint32_t(module->size_of_image));
343     }
344 
345     // check if the process is wow64 - a 32 bit windows process running on a
346     // 64 bit windows
347     if (llvm::StringRef(name.getValue()).endswith_lower("wow64.dll")) {
348       m_is_wow64 = true;
349     }
350 
351     const auto uuid = m_minidump_parser->GetModuleUUID(module);
352     auto file_spec = FileSpec(name.getValue(), GetArchitecture().GetTriple());
353     FileSystem::Instance().Resolve(file_spec);
354     ModuleSpec module_spec(file_spec, uuid);
355     Status error;
356     lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec, &error);
357     if (!module_sp || error.Fail()) {
358       // We failed to locate a matching local object file. Fortunately, the
359       // minidump format encodes enough information about each module's memory
360       // range to allow us to create placeholder modules.
361       //
362       // This enables most LLDB functionality involving address-to-module
363       // translations (ex. identifing the module for a stack frame PC) and
364       // modules/sections commands (ex. target modules list, ...)
365       if (log) {
366         log->Printf("Unable to locate the matching object file, creating a "
367                     "placeholder module for: %s",
368                     name.getValue().c_str());
369       }
370 
371       auto placeholder_module =
372           std::make_shared<PlaceholderModule>(module_spec);
373       placeholder_module->CreateImageSection(module, GetTarget());
374       module_sp = placeholder_module;
375       GetTarget().GetImages().Append(module_sp);
376     }
377 
378     if (log) {
379       log->Printf("ProcessMinidump::%s load module: name: %s", __FUNCTION__,
380                   name.getValue().c_str());
381     }
382 
383     bool load_addr_changed = false;
384     module_sp->SetLoadAddress(GetTarget(), module->base_of_image, false,
385                               load_addr_changed);
386   }
387 }
388 
389 bool ProcessMinidump::GetProcessInfo(ProcessInstanceInfo &info) {
390   info.Clear();
391   info.SetProcessID(GetID());
392   info.SetArchitecture(GetArchitecture());
393   lldb::ModuleSP module_sp = GetTarget().GetExecutableModule();
394   if (module_sp) {
395     const bool add_exe_file_as_first_arg = false;
396     info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),
397                            add_exe_file_as_first_arg);
398   }
399   return true;
400 }
401 
402 // For minidumps there's no runtime generated code so we don't need JITLoader(s)
403 // Avoiding them will also speed up minidump loading since JITLoaders normally
404 // try to set up symbolic breakpoints, which in turn may force loading more
405 // debug information than needed.
406 JITLoaderList &ProcessMinidump::GetJITLoaders() {
407   if (!m_jit_loaders_up) {
408     m_jit_loaders_up = llvm::make_unique<JITLoaderList>();
409   }
410   return *m_jit_loaders_up;
411 }
412 
413 #define INIT_BOOL(VAR, LONG, SHORT, DESC) \
414     VAR(LLDB_OPT_SET_1, false, LONG, SHORT, DESC, false, true)
415 #define APPEND_OPT(VAR) \
416     m_option_group.Append(&VAR, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1)
417 
418 class CommandObjectProcessMinidumpDump : public CommandObjectParsed {
419 private:
420   OptionGroupOptions m_option_group;
421   OptionGroupBoolean m_dump_all;
422   OptionGroupBoolean m_dump_directory;
423   OptionGroupBoolean m_dump_linux_cpuinfo;
424   OptionGroupBoolean m_dump_linux_proc_status;
425   OptionGroupBoolean m_dump_linux_lsb_release;
426   OptionGroupBoolean m_dump_linux_cmdline;
427   OptionGroupBoolean m_dump_linux_environ;
428   OptionGroupBoolean m_dump_linux_auxv;
429   OptionGroupBoolean m_dump_linux_maps;
430   OptionGroupBoolean m_dump_linux_proc_stat;
431   OptionGroupBoolean m_dump_linux_proc_uptime;
432   OptionGroupBoolean m_dump_linux_proc_fd;
433   OptionGroupBoolean m_dump_linux_all;
434   OptionGroupBoolean m_fb_app_data;
435   OptionGroupBoolean m_fb_build_id;
436   OptionGroupBoolean m_fb_version;
437   OptionGroupBoolean m_fb_java_stack;
438   OptionGroupBoolean m_fb_dalvik;
439   OptionGroupBoolean m_fb_unwind;
440   OptionGroupBoolean m_fb_error_log;
441   OptionGroupBoolean m_fb_app_state;
442   OptionGroupBoolean m_fb_abort;
443   OptionGroupBoolean m_fb_thread;
444   OptionGroupBoolean m_fb_logcat;
445   OptionGroupBoolean m_fb_all;
446 
447   void SetDefaultOptionsIfNoneAreSet() {
448     if (m_dump_all.GetOptionValue().GetCurrentValue() ||
449         m_dump_linux_all.GetOptionValue().GetCurrentValue() ||
450         m_fb_all.GetOptionValue().GetCurrentValue() ||
451         m_dump_directory.GetOptionValue().GetCurrentValue() ||
452         m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue() ||
453         m_dump_linux_proc_status.GetOptionValue().GetCurrentValue() ||
454         m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue() ||
455         m_dump_linux_cmdline.GetOptionValue().GetCurrentValue() ||
456         m_dump_linux_environ.GetOptionValue().GetCurrentValue() ||
457         m_dump_linux_auxv.GetOptionValue().GetCurrentValue() ||
458         m_dump_linux_maps.GetOptionValue().GetCurrentValue() ||
459         m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue() ||
460         m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue() ||
461         m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue() ||
462         m_fb_app_data.GetOptionValue().GetCurrentValue() ||
463         m_fb_build_id.GetOptionValue().GetCurrentValue() ||
464         m_fb_version.GetOptionValue().GetCurrentValue() ||
465         m_fb_java_stack.GetOptionValue().GetCurrentValue() ||
466         m_fb_dalvik.GetOptionValue().GetCurrentValue() ||
467         m_fb_unwind.GetOptionValue().GetCurrentValue() ||
468         m_fb_error_log.GetOptionValue().GetCurrentValue() ||
469         m_fb_app_state.GetOptionValue().GetCurrentValue() ||
470         m_fb_abort.GetOptionValue().GetCurrentValue() ||
471         m_fb_thread.GetOptionValue().GetCurrentValue() ||
472         m_fb_logcat.GetOptionValue().GetCurrentValue())
473       return;
474     // If no options were set, then dump everything
475     m_dump_all.GetOptionValue().SetCurrentValue(true);
476   }
477   bool DumpAll() const {
478     return m_dump_all.GetOptionValue().GetCurrentValue();
479   }
480   bool DumpDirectory() const {
481     return DumpAll() ||
482         m_dump_directory.GetOptionValue().GetCurrentValue();
483   }
484   bool DumpLinux() const {
485     return DumpAll() || m_dump_linux_all.GetOptionValue().GetCurrentValue();
486   }
487   bool DumpLinuxCPUInfo() const {
488     return DumpLinux() ||
489         m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue();
490   }
491   bool DumpLinuxProcStatus() const {
492     return DumpLinux() ||
493         m_dump_linux_proc_status.GetOptionValue().GetCurrentValue();
494   }
495   bool DumpLinuxProcStat() const {
496     return DumpLinux() ||
497         m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue();
498   }
499   bool DumpLinuxLSBRelease() const {
500     return DumpLinux() ||
501         m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue();
502   }
503   bool DumpLinuxCMDLine() const {
504     return DumpLinux() ||
505         m_dump_linux_cmdline.GetOptionValue().GetCurrentValue();
506   }
507   bool DumpLinuxEnviron() const {
508     return DumpLinux() ||
509         m_dump_linux_environ.GetOptionValue().GetCurrentValue();
510   }
511   bool DumpLinuxAuxv() const {
512     return DumpLinux() ||
513         m_dump_linux_auxv.GetOptionValue().GetCurrentValue();
514   }
515   bool DumpLinuxMaps() const {
516     return DumpLinux() ||
517         m_dump_linux_maps.GetOptionValue().GetCurrentValue();
518   }
519   bool DumpLinuxProcUptime() const {
520     return DumpLinux() ||
521         m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue();
522   }
523   bool DumpLinuxProcFD() const {
524     return DumpLinux() ||
525         m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue();
526   }
527   bool DumpFacebook() const {
528     return DumpAll() || m_fb_all.GetOptionValue().GetCurrentValue();
529   }
530   bool DumpFacebookAppData() const {
531     return DumpFacebook() || m_fb_app_data.GetOptionValue().GetCurrentValue();
532   }
533   bool DumpFacebookBuildID() const {
534     return DumpFacebook() || m_fb_build_id.GetOptionValue().GetCurrentValue();
535   }
536   bool DumpFacebookVersionName() const {
537     return DumpFacebook() || m_fb_version.GetOptionValue().GetCurrentValue();
538   }
539   bool DumpFacebookJavaStack() const {
540     return DumpFacebook() || m_fb_java_stack.GetOptionValue().GetCurrentValue();
541   }
542   bool DumpFacebookDalvikInfo() const {
543     return DumpFacebook() || m_fb_dalvik.GetOptionValue().GetCurrentValue();
544   }
545   bool DumpFacebookUnwindSymbols() const {
546     return DumpFacebook() || m_fb_unwind.GetOptionValue().GetCurrentValue();
547   }
548   bool DumpFacebookErrorLog() const {
549     return DumpFacebook() || m_fb_error_log.GetOptionValue().GetCurrentValue();
550   }
551   bool DumpFacebookAppStateLog() const {
552     return DumpFacebook() || m_fb_app_state.GetOptionValue().GetCurrentValue();
553   }
554   bool DumpFacebookAbortReason() const {
555     return DumpFacebook() || m_fb_abort.GetOptionValue().GetCurrentValue();
556   }
557   bool DumpFacebookThreadName() const {
558     return DumpFacebook() || m_fb_thread.GetOptionValue().GetCurrentValue();
559   }
560   bool DumpFacebookLogcat() const {
561     return DumpFacebook() || m_fb_logcat.GetOptionValue().GetCurrentValue();
562   }
563 public:
564 
565   CommandObjectProcessMinidumpDump(CommandInterpreter &interpreter)
566   : CommandObjectParsed(interpreter, "process plugin dump",
567       "Dump information from the minidump file.", NULL),
568     m_option_group(),
569     INIT_BOOL(m_dump_all, "all", 'a',
570               "Dump the everything in the minidump."),
571     INIT_BOOL(m_dump_directory, "directory", 'd',
572               "Dump the minidump directory map."),
573     INIT_BOOL(m_dump_linux_cpuinfo, "cpuinfo", 'C',
574               "Dump linux /proc/cpuinfo."),
575     INIT_BOOL(m_dump_linux_proc_status, "status", 's',
576               "Dump linux /proc/<pid>/status."),
577     INIT_BOOL(m_dump_linux_lsb_release, "lsb-release", 'r',
578               "Dump linux /etc/lsb-release."),
579     INIT_BOOL(m_dump_linux_cmdline, "cmdline", 'c',
580               "Dump linux /proc/<pid>/cmdline."),
581     INIT_BOOL(m_dump_linux_environ, "environ", 'e',
582               "Dump linux /proc/<pid>/environ."),
583     INIT_BOOL(m_dump_linux_auxv, "auxv", 'x',
584               "Dump linux /proc/<pid>/auxv."),
585     INIT_BOOL(m_dump_linux_maps, "maps", 'm',
586               "Dump linux /proc/<pid>/maps."),
587     INIT_BOOL(m_dump_linux_proc_stat, "stat", 'S',
588               "Dump linux /proc/<pid>/stat."),
589     INIT_BOOL(m_dump_linux_proc_uptime, "uptime", 'u',
590               "Dump linux process uptime."),
591     INIT_BOOL(m_dump_linux_proc_fd, "fd", 'f',
592               "Dump linux /proc/<pid>/fd."),
593     INIT_BOOL(m_dump_linux_all, "linux", 'l',
594               "Dump all linux streams."),
595     INIT_BOOL(m_fb_app_data, "fb-app-data", 1,
596               "Dump Facebook application custom data."),
597     INIT_BOOL(m_fb_build_id, "fb-build-id", 2,
598               "Dump the Facebook build ID."),
599     INIT_BOOL(m_fb_version, "fb-version", 3,
600               "Dump Facebook application version string."),
601     INIT_BOOL(m_fb_java_stack, "fb-java-stack", 4,
602               "Dump Facebook java stack."),
603     INIT_BOOL(m_fb_dalvik, "fb-dalvik-info", 5,
604               "Dump Facebook Dalvik info."),
605     INIT_BOOL(m_fb_unwind, "fb-unwind-symbols", 6,
606               "Dump Facebook unwind symbols."),
607     INIT_BOOL(m_fb_error_log, "fb-error-log", 7,
608               "Dump Facebook error log."),
609     INIT_BOOL(m_fb_app_state, "fb-app-state-log", 8,
610               "Dump Facebook java stack."),
611     INIT_BOOL(m_fb_abort, "fb-abort-reason", 9,
612               "Dump Facebook abort reason."),
613     INIT_BOOL(m_fb_thread, "fb-thread-name", 10,
614               "Dump Facebook thread name."),
615     INIT_BOOL(m_fb_logcat, "fb-logcat", 11,
616               "Dump Facebook logcat."),
617     INIT_BOOL(m_fb_all, "facebook", 12, "Dump all Facebook streams.") {
618     APPEND_OPT(m_dump_all);
619     APPEND_OPT(m_dump_directory);
620     APPEND_OPT(m_dump_linux_cpuinfo);
621     APPEND_OPT(m_dump_linux_proc_status);
622     APPEND_OPT(m_dump_linux_lsb_release);
623     APPEND_OPT(m_dump_linux_cmdline);
624     APPEND_OPT(m_dump_linux_environ);
625     APPEND_OPT(m_dump_linux_auxv);
626     APPEND_OPT(m_dump_linux_maps);
627     APPEND_OPT(m_dump_linux_proc_stat);
628     APPEND_OPT(m_dump_linux_proc_uptime);
629     APPEND_OPT(m_dump_linux_proc_fd);
630     APPEND_OPT(m_dump_linux_all);
631     APPEND_OPT(m_fb_app_data);
632     APPEND_OPT(m_fb_build_id);
633     APPEND_OPT(m_fb_version);
634     APPEND_OPT(m_fb_java_stack);
635     APPEND_OPT(m_fb_dalvik);
636     APPEND_OPT(m_fb_unwind);
637     APPEND_OPT(m_fb_error_log);
638     APPEND_OPT(m_fb_app_state);
639     APPEND_OPT(m_fb_abort);
640     APPEND_OPT(m_fb_thread);
641     APPEND_OPT(m_fb_logcat);
642     APPEND_OPT(m_fb_all);
643     m_option_group.Finalize();
644   }
645 
646   ~CommandObjectProcessMinidumpDump() {}
647 
648   Options *GetOptions() override { return &m_option_group; }
649 
650   bool DoExecute(Args &command, CommandReturnObject &result) override {
651     const size_t argc = command.GetArgumentCount();
652     if (argc > 0) {
653       result.AppendErrorWithFormat("'%s' take no arguments, only options",
654                                    m_cmd_name.c_str());
655       result.SetStatus(eReturnStatusFailed);
656       return false;
657     }
658     SetDefaultOptionsIfNoneAreSet();
659 
660     ProcessMinidump *process = static_cast<ProcessMinidump *>(
661         m_interpreter.GetExecutionContext().GetProcessPtr());
662     result.SetStatus(eReturnStatusSuccessFinishResult);
663     Stream &s = result.GetOutputStream();
664     MinidumpParser &minidump = *process->m_minidump_parser;
665     if (DumpDirectory()) {
666       s.Printf("RVA        SIZE       TYPE       MinidumpStreamType\n");
667       s.Printf("---------- ---------- ---------- --------------------------\n");
668       for (const auto &pair: minidump.GetDirectoryMap())
669         s.Printf("0x%8.8x 0x%8.8x 0x%8.8x %s\n", (uint32_t)pair.second.rva,
670                  (uint32_t)pair.second.data_size, pair.first,
671                  MinidumpParser::GetStreamTypeAsString(pair.first).data());
672       s.Printf("\n");
673     }
674     auto DumpTextStream = [&](MinidumpStreamType stream_type,
675                               llvm::StringRef label) -> void {
676       auto bytes = minidump.GetStream(stream_type);
677       if (!bytes.empty()) {
678         if (label.empty())
679           label = MinidumpParser::GetStreamTypeAsString((uint32_t)stream_type);
680         s.Printf("%s:\n%s\n\n", label.data(), bytes.data());
681       }
682     };
683     auto DumpBinaryStream = [&](MinidumpStreamType stream_type,
684                                 llvm::StringRef label) -> void {
685       auto bytes = minidump.GetStream(stream_type);
686       if (!bytes.empty()) {
687         if (label.empty())
688           label = MinidumpParser::GetStreamTypeAsString((uint32_t)stream_type);
689         s.Printf("%s:\n", label.data());
690         DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,
691                            process->GetAddressByteSize());
692         DumpDataExtractor(data, &s, 0, lldb::eFormatBytesWithASCII, 1,
693                           bytes.size(), 16, 0, 0, 0);
694         s.Printf("\n\n");
695       }
696     };
697 
698     if (DumpLinuxCPUInfo())
699       DumpTextStream(MinidumpStreamType::LinuxCPUInfo, "/proc/cpuinfo");
700     if (DumpLinuxProcStatus())
701       DumpTextStream(MinidumpStreamType::LinuxProcStatus, "/proc/PID/status");
702     if (DumpLinuxLSBRelease())
703       DumpTextStream(MinidumpStreamType::LinuxLSBRelease, "/etc/lsb-release");
704     if (DumpLinuxCMDLine())
705       DumpTextStream(MinidumpStreamType::LinuxCMDLine, "/proc/PID/cmdline");
706     if (DumpLinuxEnviron())
707       DumpTextStream(MinidumpStreamType::LinuxEnviron, "/proc/PID/environ");
708     if (DumpLinuxAuxv())
709       DumpBinaryStream(MinidumpStreamType::LinuxAuxv, "/proc/PID/auxv");
710     if (DumpLinuxMaps())
711       DumpTextStream(MinidumpStreamType::LinuxMaps, "/proc/PID/maps");
712     if (DumpLinuxProcStat())
713       DumpTextStream(MinidumpStreamType::LinuxProcStat, "/proc/PID/stat");
714     if (DumpLinuxProcUptime())
715       DumpTextStream(MinidumpStreamType::LinuxProcUptime, "uptime");
716     if (DumpLinuxProcFD())
717       DumpTextStream(MinidumpStreamType::LinuxProcFD, "/proc/PID/fd");
718     if (DumpFacebookAppData())
719       DumpTextStream(MinidumpStreamType::FacebookAppCustomData,
720                      "Facebook App Data");
721     if (DumpFacebookBuildID()) {
722       auto bytes = minidump.GetStream(MinidumpStreamType::FacebookBuildID);
723       if (bytes.size() >= 4) {
724         DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,
725                            process->GetAddressByteSize());
726         lldb::offset_t offset = 0;
727         uint32_t build_id = data.GetU32(&offset);
728         s.Printf("Facebook Build ID:\n");
729         s.Printf("%u\n", build_id);
730         s.Printf("\n");
731       }
732     }
733     if (DumpFacebookVersionName())
734       DumpTextStream(MinidumpStreamType::FacebookAppVersionName,
735                      "Facebook Version String");
736     if (DumpFacebookJavaStack())
737       DumpTextStream(MinidumpStreamType::FacebookJavaStack,
738                      "Facebook Java Stack");
739     if (DumpFacebookDalvikInfo())
740       DumpTextStream(MinidumpStreamType::FacebookDalvikInfo,
741                      "Facebook Dalvik Info");
742     if (DumpFacebookUnwindSymbols())
743       DumpBinaryStream(MinidumpStreamType::FacebookUnwindSymbols,
744                        "Facebook Unwind Symbols Bytes");
745     if (DumpFacebookErrorLog())
746       DumpTextStream(MinidumpStreamType::FacebookDumpErrorLog,
747                      "Facebook Error Log");
748     if (DumpFacebookAppStateLog())
749       DumpTextStream(MinidumpStreamType::FacebookAppStateLog,
750                      "Faceook Application State Log");
751     if (DumpFacebookAbortReason())
752       DumpTextStream(MinidumpStreamType::FacebookAbortReason,
753                      "Facebook Abort Reason");
754     if (DumpFacebookThreadName())
755       DumpTextStream(MinidumpStreamType::FacebookThreadName,
756                      "Facebook Thread Name");
757     if (DumpFacebookLogcat())
758       DumpTextStream(MinidumpStreamType::FacebookLogcat,
759                      "Facebook Logcat");
760     return true;
761   }
762 };
763 
764 class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword {
765 public:
766   CommandObjectMultiwordProcessMinidump(CommandInterpreter &interpreter)
767     : CommandObjectMultiword(interpreter, "process plugin",
768           "Commands for operating on a ProcessMinidump process.",
769           "process plugin <subcommand> [<subcommand-options>]") {
770     LoadSubCommand("dump",
771         CommandObjectSP(new CommandObjectProcessMinidumpDump(interpreter)));
772   }
773 
774   ~CommandObjectMultiwordProcessMinidump() {}
775 };
776 
777 CommandObject *ProcessMinidump::GetPluginCommandObject() {
778   if (!m_command_sp)
779     m_command_sp = std::make_shared<CommandObjectMultiwordProcessMinidump>(
780         GetTarget().GetDebugger().GetCommandInterpreter());
781   return m_command_sp.get();
782 }
783