From ed7dcced1945864c94dcfd358a64e23c812fd0dc Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 25 Oct 2016 02:36:38 -0400 Subject: [PATCH] minidump-2-core: rewrite argument processing This uses the same general framework as other minidump tools by using getopt to parse command line options, and then passing the parsed state around as a struct rather than via globals. This does change the --sobasedir flag to -S because we don't support getopt_long anywhere in the tree. Unfortunate, but better to match all the other breakpad tools which only accept short options. BUG=chromium:598947 Change-Id: I473081a29a8e3ef07a370848343f1a9e6681fd4e Reviewed-on: https://chromium-review.googlesource.com/402908 Reviewed-by: Mark Mentovai --- src/tools/linux/md2core/minidump-2-core.cc | 219 +++++++++++++-------- 1 file changed, 134 insertions(+), 85 deletions(-) diff --git a/src/tools/linux/md2core/minidump-2-core.cc b/src/tools/linux/md2core/minidump-2-core.cc index 6f637845..ca236063 100644 --- a/src/tools/linux/md2core/minidump-2-core.cc +++ b/src/tools/linux/md2core/minidump-2-core.cc @@ -30,8 +30,6 @@ // Converts a minidump file to a core file which gdb can read. // Large parts lifted from the userspace core dumper: // http://code.google.com/p/google-coredumper/ -// -// Usage: minidump-2-core [-v] 1234.dmp > core #include #include @@ -97,12 +95,64 @@ typedef MDTypeHelper::MDRawDebug MDRawDebug; typedef MDTypeHelper::MDRawLinkMap MDRawLinkMap; static const MDRVA kInvalidMDRVA = static_cast(-1); -static bool verbose; -static string g_custom_so_basedir; -static int usage(const char* argv0) { - fprintf(stderr, "Usage: %s [-v] \n", argv0); - return 1; +struct Options { + string minidump_path; + bool verbose; + string so_basedir; +}; + +static void +Usage(int argc, const char* argv[]) { + fprintf(stderr, + "Usage: %s [options] \n" + "\n" + "Convert a minidump file into a core file (often for use by gdb).\n" + "\n" + "Options:\n" + " -v Enable verbose output\n" + " -S Set soname base directory. This will force all debug/symbol\n" + " lookups to be done in this directory rather than the filesystem\n" + " layout as it exists in the crashing image. This path should end\n" + " with a slash if it's a directory.\n" + "", basename(argv[0])); +} + +static void +SetupOptions(int argc, const char* argv[], Options* options) { + extern int optind; + int ch; + + // Initialize the options struct as needed. + options->verbose = false; + + while ((ch = getopt(argc, (char * const *)argv, "hS:v")) != -1) { + switch (ch) { + case 'h': + Usage(argc, argv); + exit(0); + break; + case '?': + Usage(argc, argv); + exit(1); + break; + + case 'S': + options->so_basedir = optarg; + break; + case 'v': + options->verbose = true; + break; + } + } + + if ((argc - optind) != 1) { + fprintf(stderr, "%s: Missing minidump file\n", argv[0]); + Usage(argc, argv); + exit(1); + } + + options->minidump_path = argv[optind]; } // Write all of the given buffer, handling short writes and EINTR. Return true @@ -429,10 +479,11 @@ ParseThreadRegisters(CrashedProcess::Thread* thread, #endif static void -ParseThreadList(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, +ParseThreadList(const Options& options, CrashedProcess* crashinfo, + const MinidumpMemoryRange& range, const MinidumpMemoryRange& full_file) { const uint32_t num_threads = *range.GetData(0); - if (verbose) { + if (options.verbose) { fprintf(stderr, "MD_THREAD_LIST_STREAM:\n" "Found %d threads\n" @@ -459,12 +510,13 @@ ParseThreadList(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, } static void -ParseSystemInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, +ParseSystemInfo(const Options& options, CrashedProcess* crashinfo, + const MinidumpMemoryRange& range, const MinidumpMemoryRange& full_file) { const MDRawSystemInfo* sysinfo = range.GetData(0); if (!sysinfo) { fprintf(stderr, "Failed to access MD_SYSTEM_INFO_STREAM\n"); - _exit(1); + exit(1); } #if defined(__i386__) if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_X86) { @@ -472,7 +524,7 @@ ParseSystemInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, "This version of minidump-2-core only supports x86 (32bit)%s.\n", sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64 ? ",\nbut the minidump file is from a 64bit machine" : ""); - _exit(1); + exit(1); } #elif defined(__x86_64__) if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_AMD64) { @@ -480,32 +532,32 @@ ParseSystemInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, "This version of minidump-2-core only supports x86 (64bit)%s.\n", sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 ? ",\nbut the minidump file is from a 32bit machine" : ""); - _exit(1); + exit(1); } #elif defined(__arm__) if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM) { fprintf(stderr, "This version of minidump-2-core only supports ARM (32bit).\n"); - _exit(1); + exit(1); } #elif defined(__aarch64__) if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM64) { fprintf(stderr, "This version of minidump-2-core only supports ARM (64bit).\n"); - _exit(1); + exit(1); } #elif defined(__mips__) # if _MIPS_SIM == _ABIO32 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS) { fprintf(stderr, "This version of minidump-2-core only supports mips o32 (32bit).\n"); - _exit(1); + exit(1); } # elif _MIPS_SIM == _ABI64 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS64) { fprintf(stderr, "This version of minidump-2-core only supports mips n64 (64bit).\n"); - _exit(1); + exit(1); } # else # error "This mips ABI is currently not supported (n32)" @@ -517,10 +569,10 @@ ParseSystemInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, "Linux") && sysinfo->platform_id != MD_OS_NACL) { fprintf(stderr, "This minidump was not generated by Linux or NaCl.\n"); - _exit(1); + exit(1); } - if (verbose) { + if (options.verbose) { fprintf(stderr, "MD_SYSTEM_INFO_STREAM:\n" "Architecture: %s\n" @@ -561,8 +613,9 @@ ParseSystemInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, } static void -ParseCPUInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { - if (verbose) { +ParseCPUInfo(const Options& options, CrashedProcess* crashinfo, + const MinidumpMemoryRange& range) { + if (options.verbose) { fputs("MD_LINUX_CPU_INFO:\n", stderr); fwrite(range.data(), range.length(), 1, stderr); fputs("\n\n\n", stderr); @@ -570,9 +623,9 @@ ParseCPUInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { } static void -ParseProcessStatus(CrashedProcess* crashinfo, +ParseProcessStatus(const Options& options, CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { - if (verbose) { + if (options.verbose) { fputs("MD_LINUX_PROC_STATUS:\n", stderr); fwrite(range.data(), range.length(), 1, stderr); fputs("\n\n", stderr); @@ -580,8 +633,9 @@ ParseProcessStatus(CrashedProcess* crashinfo, } static void -ParseLSBRelease(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { - if (verbose) { +ParseLSBRelease(const Options& options, CrashedProcess* crashinfo, + const MinidumpMemoryRange& range) { + if (options.verbose) { fputs("MD_LINUX_LSB_RELEASE:\n", stderr); fwrite(range.data(), range.length(), 1, stderr); fputs("\n\n", stderr); @@ -589,8 +643,9 @@ ParseLSBRelease(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { } static void -ParseMaps(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { - if (verbose) { +ParseMaps(const Options& options, CrashedProcess* crashinfo, + const MinidumpMemoryRange& range) { + if (options.verbose) { fputs("MD_LINUX_MAPS:\n", stderr); fwrite(range.data(), range.length(), 1, stderr); } @@ -629,14 +684,15 @@ ParseMaps(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { free(permissions); free(filename); } - if (verbose) { + if (options.verbose) { fputs("\n\n\n", stderr); } } static void -ParseEnvironment(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { - if (verbose) { +ParseEnvironment(const Options& options, CrashedProcess* crashinfo, + const MinidumpMemoryRange& range) { + if (options.verbose) { fputs("MD_LINUX_ENVIRON:\n", stderr); char* env = new char[range.length()]; memcpy(env, range.data(), range.length()); @@ -673,7 +729,8 @@ ParseEnvironment(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { } static void -ParseAuxVector(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { +ParseAuxVector(const Options& options, CrashedProcess* crashinfo, + const MinidumpMemoryRange& range) { // Some versions of Chrome erroneously used the MD_LINUX_AUXV stream value // when dumping /proc/$x/maps if (range.length() > 17) { @@ -684,7 +741,7 @@ ParseAuxVector(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { memcpy(addresses, range.data(), 17); addresses[17] = '\000'; if (strspn(addresses, "0123456789abcdef-") == 17) { - ParseMaps(crashinfo, range); + ParseMaps(options, crashinfo, range); return; } } @@ -694,12 +751,13 @@ ParseAuxVector(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { } static void -ParseCmdLine(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { +ParseCmdLine(const Options& options, CrashedProcess* crashinfo, + const MinidumpMemoryRange& range) { // The command line is supposed to use NUL bytes to separate arguments. // As Chrome rewrites its own command line and (incorrectly) substitutes // spaces, this is often not the case in our minidump files. const char* cmdline = (const char*) range.data(); - if (verbose) { + if (options.verbose) { fputs("MD_LINUX_CMD_LINE:\n", stderr); unsigned i = 0; for (; i < range.length() && cmdline[i] && cmdline[i] != ' '; ++i) { } @@ -742,13 +800,14 @@ ParseCmdLine(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { } static void -ParseDSODebugInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, +ParseDSODebugInfo(const Options& options, CrashedProcess* crashinfo, + const MinidumpMemoryRange& range, const MinidumpMemoryRange& full_file) { const MDRawDebug* debug = range.GetData(0); if (!debug) { return; } - if (verbose) { + if (options.verbose) { fprintf(stderr, "MD_LINUX_DSO_DEBUG:\n" "Version: %d\n" @@ -773,7 +832,7 @@ ParseDSODebugInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, const MDRawLinkMap* link_map = full_file.GetArrayElement(debug->map, i); if (link_map) { - if (verbose) { + if (options.verbose) { fprintf(stderr, "#%03d: %" PRIx64 ", %" PRIx64 ", \"%s\"\n", i, static_cast(link_map->addr), @@ -784,13 +843,13 @@ ParseDSODebugInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, } } } - if (verbose) { + if (options.verbose) { fputs("\n\n", stderr); } } static void -ParseExceptionStream(CrashedProcess* crashinfo, +ParseExceptionStream(const Options& options, CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { const MDRawExceptionStream* exp = range.GetData(0); crashinfo->crashing_tid = exp->thread_id; @@ -846,9 +905,10 @@ WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) { } static void -ParseModuleStream(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, +ParseModuleStream(const Options& options, CrashedProcess* crashinfo, + const MinidumpMemoryRange& range, const MinidumpMemoryRange& full_file) { - if (verbose) { + if (options.verbose) { fputs("MD_MODULE_LIST_STREAM:\n", stderr); } const uint32_t num_mappings = *range.GetData(0); @@ -883,15 +943,15 @@ ParseModuleStream(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, filename : filename.substr(slash + 1); if (strcmp(guid, "00000000-0000-0000-0000-000000000000")) { string prefix; - if (!g_custom_so_basedir.empty()) - prefix = g_custom_so_basedir; + if (!options.so_basedir.empty()) + prefix = options.so_basedir; else prefix = string("/var/lib/breakpad/") + guid + "-" + basename; crashinfo->signatures[rawmodule->base_of_image] = prefix + basename; } - if (verbose) { + if (options.verbose) { fprintf(stderr, "0x%08llX-0x%08llX, ChkSum: 0x%08X, GUID: %s, \"%s\"\n", (unsigned long long)rawmodule->base_of_image, (unsigned long long)rawmodule->base_of_image + @@ -899,7 +959,7 @@ ParseModuleStream(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, rawmodule->checksum, guid, filename.c_str()); } } - if (verbose) { + if (options.verbose) { fputs("\n\n", stderr); } } @@ -954,7 +1014,7 @@ AddDataToMapping(CrashedProcess* crashinfo, const string& data, } static void -AugmentMappings(CrashedProcess* crashinfo, +AugmentMappings(const Options& options, CrashedProcess* crashinfo, const MinidumpMemoryRange& full_file) { // For each thread, find the memory mapping that matches the thread's stack. // Then adjust the mapping to include the stack dump. @@ -1019,7 +1079,7 @@ AugmentMappings(CrashedProcess* crashinfo, ElfW(Dyn) dyn; if ((i+1)*sizeof(dyn) > crashinfo->dynamic_data.length()) { no_dt_debug: - if (verbose) { + if (options.verbose) { fprintf(stderr, "No DT_DEBUG entry found\n"); } return; @@ -1042,31 +1102,14 @@ AugmentMappings(CrashedProcess* crashinfo, } int -main(int argc, char** argv) { - int argi = 1; - while (argi < argc && argv[argi][0] == '-') { - if (!strcmp(argv[argi], "-v")) { - verbose = true; - } else if (!strcmp(argv[argi], "--sobasedir")) { - argi++; - if (argi >= argc) { - fprintf(stderr, "--sobasedir expects an argument."); - return usage(argv[0]); - } +main(int argc, const char* argv[]) { + Options options; + SetupOptions(argc, argv, &options); - g_custom_so_basedir = argv[argi]; - } else { - return usage(argv[0]); - } - argi++; - } - - if (argc != argi + 1) - return usage(argv[0]); - - MemoryMappedFile mapped_file(argv[argi], 0); + MemoryMappedFile mapped_file(options.minidump_path.c_str(), 0); if (!mapped_file.data()) { - fprintf(stderr, "Failed to mmap dump file\n"); + fprintf(stderr, "Failed to mmap dump file: %s: %s\n", + options.minidump_path.c_str(), strerror(errno)); return 1; } @@ -1084,7 +1127,8 @@ main(int argc, char** argv) { dump.GetArrayElement(header->stream_directory_rva, i); switch (dirent->stream_type) { case MD_SYSTEM_INFO_STREAM: - ParseSystemInfo(&crashinfo, dump.Subrange(dirent->location), dump); + ParseSystemInfo(options, &crashinfo, dump.Subrange(dirent->location), + dump); ok = true; break; default: @@ -1093,7 +1137,7 @@ main(int argc, char** argv) { } if (!ok) { fprintf(stderr, "Cannot determine input file format.\n"); - _exit(1); + exit(1); } for (unsigned i = 0; i < header->stream_count; ++i) { @@ -1101,45 +1145,50 @@ main(int argc, char** argv) { dump.GetArrayElement(header->stream_directory_rva, i); switch (dirent->stream_type) { case MD_THREAD_LIST_STREAM: - ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump); + ParseThreadList(options, &crashinfo, dump.Subrange(dirent->location), + dump); break; case MD_LINUX_CPU_INFO: - ParseCPUInfo(&crashinfo, dump.Subrange(dirent->location)); + ParseCPUInfo(options, &crashinfo, dump.Subrange(dirent->location)); break; case MD_LINUX_PROC_STATUS: - ParseProcessStatus(&crashinfo, dump.Subrange(dirent->location)); + ParseProcessStatus(options, &crashinfo, + dump.Subrange(dirent->location)); break; case MD_LINUX_LSB_RELEASE: - ParseLSBRelease(&crashinfo, dump.Subrange(dirent->location)); + ParseLSBRelease(options, &crashinfo, dump.Subrange(dirent->location)); break; case MD_LINUX_ENVIRON: - ParseEnvironment(&crashinfo, dump.Subrange(dirent->location)); + ParseEnvironment(options, &crashinfo, dump.Subrange(dirent->location)); break; case MD_LINUX_MAPS: - ParseMaps(&crashinfo, dump.Subrange(dirent->location)); + ParseMaps(options, &crashinfo, dump.Subrange(dirent->location)); break; case MD_LINUX_AUXV: - ParseAuxVector(&crashinfo, dump.Subrange(dirent->location)); + ParseAuxVector(options, &crashinfo, dump.Subrange(dirent->location)); break; case MD_LINUX_CMD_LINE: - ParseCmdLine(&crashinfo, dump.Subrange(dirent->location)); + ParseCmdLine(options, &crashinfo, dump.Subrange(dirent->location)); break; case MD_LINUX_DSO_DEBUG: - ParseDSODebugInfo(&crashinfo, dump.Subrange(dirent->location), dump); + ParseDSODebugInfo(options, &crashinfo, dump.Subrange(dirent->location), + dump); break; case MD_EXCEPTION_STREAM: - ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location)); + ParseExceptionStream(options, &crashinfo, + dump.Subrange(dirent->location)); break; case MD_MODULE_LIST_STREAM: - ParseModuleStream(&crashinfo, dump.Subrange(dirent->location), dump); + ParseModuleStream(options, &crashinfo, dump.Subrange(dirent->location), + dump); break; default: - if (verbose) + if (options.verbose) fprintf(stderr, "Skipping %x\n", dirent->stream_type); } } - AugmentMappings(&crashinfo, dump); + AugmentMappings(options, &crashinfo, dump); // Write the ELF header. The file will look like: // ELF header