From c91b41a64ccce6d1097d85c8c7354ea63a5566a0 Mon Sep 17 00:00:00 2001 From: Chad Rosier Date: Wed, 31 Oct 2012 18:31:33 +0000 Subject: [PATCH] [driver] Completely rework how superfluous options are stripped out of the crash diagnostics script. This addresses the FIXME pertaining to quoted arguments. We also delineate between those flags that have an argument (e.g., -D macro, -MF file) and those that do not (e.g., -M, -MM, -MG). Finally, we add the -dwarf-debug-flags to the list of flags to be removed. rdar://12329974 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@167152 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Driver/Compilation.h | 8 +++ lib/Driver/Compilation.cpp | 100 +++++++++++++++++++++++++++++ lib/Driver/Driver.cpp | 55 +--------------- test/Driver/crash-report.c | 3 +- 4 files changed, 112 insertions(+), 54 deletions(-) diff --git a/include/clang/Driver/Compilation.h b/include/clang/Driver/Compilation.h index daa4dea410..5f63aa7688 100644 --- a/include/clang/Driver/Compilation.h +++ b/include/clang/Driver/Compilation.h @@ -141,6 +141,14 @@ public: void PrintJob(raw_ostream &OS, const Job &J, const char *Terminator, bool Quote) const; + /// PrintDiagnosticJob - Print one job in -### format, but with the + /// superfluous options removed, which are not necessary for + /// reproducing the crash. + /// + /// \param OS - The stream to print on. + /// \param J - The job to print. + void PrintDiagnosticJob(raw_ostream &OS, const Job &J) const; + /// ExecuteCommand - Execute an actual command. /// /// \param FailingCommand - For non-zero results, this will be set to the diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index c962fca0bf..e804a54383 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -17,6 +17,7 @@ #include "clang/Driver/ToolChain.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Program.h" #include @@ -101,6 +102,105 @@ void Compilation::PrintJob(raw_ostream &OS, const Job &J, } } +static bool skipArg(const char *Flag, bool &SkipNextArg) { + StringRef FlagRef(Flag); + + // Assume we're going to see -Flag . + SkipNextArg = true; + + // These flags are all of the form -Flag and are treated as two + // arguments. Therefore, we need to skip the flag and the next argument. + bool Res = llvm::StringSwitch(Flag) + .Cases("-I", "-MF", "-MT", "-MQ", true) + .Cases("-o", "-coverage-file", "-dependency-file", true) + .Cases("-fdebug-compilation-dir", "-fmodule-cache-path", "-idirafter", true) + .Cases("-include", "-include-pch", "-internal-isystem", true) + .Cases("-internal-externc-isystem ", "-iprefix ", "-iwithprefix", true) + .Cases("-iwithprefixbefore", "-isysroot", "-isystem", "-iquote", true) + .Cases("-resource-dir", "-serialize-diagnostic-file", true) + .Case("-dwarf-debug-flags", true) + .Default(false); + + // Match found. + if (Res) + return Res; + + // The remaining flags are treated as a single argument. + SkipNextArg = false; + + // These flags are all of the form -Flag and have no second argument. + Res = llvm::StringSwitch(Flag) + .Cases("-M", "-MM", "-MG", "-MP", "-MD", true) + .Case("-MMD", true) + .Default(false); + + // Match found. + if (Res) + return Res; + + // These flags are treated as a single argument (e.g., -F). + if (FlagRef.startswith("-F") || FlagRef.startswith("-I")) + return true; + + return false; +} + +static bool quoteNextArg(const char *flag) { + return llvm::StringSwitch(flag) + .Case("-D", true) + .Default(false); +} + +void Compilation::PrintDiagnosticJob(raw_ostream &OS, const Job &J) const { + if (const Command *C = dyn_cast(&J)) { + OS << C->getExecutable(); + unsigned QuoteNextArg = 0; + for (ArgStringList::const_iterator it = C->getArguments().begin(), + ie = C->getArguments().end(); it != ie; ++it) { + + bool SkipNext; + if (skipArg(*it, SkipNext)) { + if (SkipNext) ++it; + continue; + } + + if (!QuoteNextArg) + QuoteNextArg = quoteNextArg(*it) ? 2 : 0; + + OS << ' '; + + if (QuoteNextArg == 1) + OS << '"'; + + if (!std::strpbrk(*it, " \"\\$")) { + OS << *it; + } else { + // Quote the argument and escape shell special characters; this isn't + // really complete but is good enough. + OS << '"'; + for (const char *s = *it; *s; ++s) { + if (*s == '"' || *s == '\\' || *s == '$') + OS << '\\'; + OS << *s; + } + OS << '"'; + } + + if (QuoteNextArg) { + if (QuoteNextArg == 1) + OS << '"'; + --QuoteNextArg; + } + } + OS << '\n'; + } else { + const JobList *Jobs = cast(&J); + for (JobList::const_iterator + it = Jobs->begin(), ie = Jobs->end(); it != ie; ++it) + PrintDiagnosticJob(OS, **it); + } +} + bool Compilation::CleanupFileList(const ArgStringList &Files, bool IssueErrors) const { bool Success = true; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 5c20c23e56..ac1e112f71 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -362,11 +362,11 @@ void Driver::generateCompilationDiagnostics(Compilation &C, std::string Cmd; llvm::raw_string_ostream OS(Cmd); if (FailingCommand) - C.PrintJob(OS, *FailingCommand, "\n", false); + C.PrintDiagnosticJob(OS, *FailingCommand); else // Crash triggered by FORCE_CLANG_DIAGNOSTICS_CRASH, which doesn't have an // associated FailingCommand, so just pass all jobs. - C.PrintJob(OS, C.getJobs(), "\n", false); + C.PrintDiagnosticJob(OS, C.getJobs()); OS.flush(); // Clear stale state and suppress tool output. @@ -464,57 +464,6 @@ void Driver::generateCompilationDiagnostics(Compilation &C, Diag(clang::diag::note_drv_command_failed_diag_msg) << "Error generating run script: " + Script + " " + Err; } else { - // Strip away options not necessary to reproduce the crash. - // FIXME: This doesn't work with quotes (e.g., -D "foo bar"). - SmallVector Flag; - Flag.push_back("-D "); - Flag.push_back("-F"); - Flag.push_back("-I "); - Flag.push_back("-M "); - Flag.push_back("-MD "); - Flag.push_back("-MF "); - Flag.push_back("-MG "); - Flag.push_back("-MM "); - Flag.push_back("-MMD "); - Flag.push_back("-MP "); - Flag.push_back("-MQ "); - Flag.push_back("-MT "); - Flag.push_back("-o "); - Flag.push_back("-coverage-file "); - Flag.push_back("-dependency-file "); - Flag.push_back("-fdebug-compilation-dir "); - Flag.push_back("-fmodule-cache-path "); - Flag.push_back("-idirafter "); - Flag.push_back("-include "); - Flag.push_back("-include-pch "); - Flag.push_back("-internal-isystem "); - Flag.push_back("-internal-externc-isystem "); - Flag.push_back("-iprefix "); - Flag.push_back("-iwithprefix "); - Flag.push_back("-iwithprefixbefore "); - Flag.push_back("-isysroot "); - Flag.push_back("-isystem "); - Flag.push_back("-iquote "); - Flag.push_back("-resource-dir "); - Flag.push_back("-serialize-diagnostic-file "); - for (unsigned i = 0, e = Flag.size(); i < e; ++i) { - size_t I = 0, E = 0; - do { - I = Cmd.find(Flag[i], I); - if (I == std::string::npos) break; - - E = Cmd.find(" ", I + Flag[i].length()); - if (E == std::string::npos) break; - // The -D option is not removed. Instead, the argument is quoted. - if (Flag[i] != "-D ") { - Cmd.erase(I, E - I + 1); - } else { - Cmd.insert(I+3, "\""); - Cmd.insert(++E, "\""); - I = E; - } - } while(1); - } // Append the new filename with correct preprocessed suffix. size_t I, E; I = Cmd.find("-main-file-name "); diff --git a/test/Driver/crash-report.c b/test/Driver/crash-report.c index 7adaf42a2c..bfcd5732b3 100644 --- a/test/Driver/crash-report.c +++ b/test/Driver/crash-report.c @@ -1,6 +1,6 @@ // RUN: rm -rf %t // RUN: mkdir %t -// RUN: env TMPDIR=%t TEMP=%t TMP=%t %clang -fsyntax-only %s \ +// RUN: env TMPDIR=%t TEMP=%t TMP=%t RC_DEBUG_OPTIONS=1 %clang -fsyntax-only %s \ // RUN: -F/tmp/ -I /tmp/ -idirafter /tmp/ -iquote /tmp/ -isystem /tmp/ \ // RUN: -iprefix /the/prefix -iwithprefix /tmp -iwithprefixbefore /tmp/ \ // RUN: -internal-isystem /tmp/ -internal-externc-isystem /tmp/ \ @@ -25,3 +25,4 @@ FOO // CHECKSH-NOT: -iwithprefixbefore /tmp/ // CHECKSH-NOT: -internal-isystem /tmp/ // CHECKSH-NOT: -internal-externc-isystem /tmp/ +// CHECKSH-NOT: -dwarf-debug-flags -- 2.40.0