diff --git a/clang/include/clang/Driver/Compilation.h b/clang/include/clang/Driver/Compilation.h --- a/clang/include/clang/Driver/Compilation.h +++ b/clang/include/clang/Driver/Compilation.h @@ -129,6 +129,10 @@ /// Whether to keep temporary files regardless of -save-temps. bool ForceKeepTempFiles = false; + StringRef TimeTracePath; + + llvm::DenseMap TimeTraceFiles; + public: Compilation(const Driver &D, const ToolChain &DefaultToolChain, llvm::opt::InputArgList *Args, @@ -269,6 +273,21 @@ return Name; } + void setTimeTraceFileForJob(const JobAction *JA, + const char *TimeTraceFileName) { + TimeTraceFiles[JA] = TimeTraceFileName; + } + + const char *getTimeTraceFileForJob(const JobAction *JA) const { + return TimeTraceFiles.lookup(JA); + } + + void setTimeTracePath(llvm::StringRef _TimeTracePath) { + TimeTracePath = _TimeTracePath; + } + + llvm::StringRef getTimeTracePath() const { return TimeTracePath; } + /// CleanupFile - Delete a given file. /// /// \param IssueErrors - Report failures as errors. diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -721,6 +721,12 @@ &CachedResults, Action::OffloadKind TargetDeviceOffloadKind) const; + const char *getTimeTraceOutputPath(Compilation &C, const JobAction &JA, + StringRef OrigFileName, + StringRef OrigBoundArch, bool AtTopLevel, + bool MultipleArchs, + StringRef OffloadingPrefix) const; + public: /// GetReleaseVersion - Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and /// return the grouped values as integers. Numbers which are not diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2872,8 +2872,7 @@ Turn on time profiler. Generates JSON file based on output filename. Results can be analyzed with chrome://tracing or `Speedscope App `_ for flamegraph visualization.}]>, - Flags<[CC1Option, CoreOption]>, - MarshallingInfoFlag>; + Flags<[CoreOption]>; def ftime_trace_granularity_EQ : Joined<["-"], "ftime-trace-granularity=">, Group, HelpText<"Minimum time granularity (in microseconds) traced by time profiler">, Flags<[CC1Option, CoreOption]>, diff --git a/clang/include/clang/Driver/Tool.h b/clang/include/clang/Driver/Tool.h --- a/clang/include/clang/Driver/Tool.h +++ b/clang/include/clang/Driver/Tool.h @@ -62,6 +62,8 @@ /// driver add an additional "command failed" diagnostic on failures. virtual bool hasGoodDiagnostics() const { return false; } + virtual bool supportsTimeTrace() const { return false; } + /// ConstructJob - Construct jobs to perform the action \p JA, /// writing to \p Output and with \p Inputs, and add the jobs to /// \p C. diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -283,9 +283,6 @@ /// print the supported cpus for the current target unsigned PrintSupportedCPUs : 1; - /// Output time trace profile. - unsigned TimeTrace : 1; - /// Show the -version text. unsigned ShowVersion : 1; @@ -508,16 +505,15 @@ public: FrontendOptions() : DisableFree(false), RelocatablePCH(false), ShowHelp(false), - ShowStats(false), TimeTrace(false), ShowVersion(false), - FixWhatYouCan(false), FixOnlyWarnings(false), FixAndRecompile(false), - FixToTemporaries(false), ARCMTMigrateEmitARCErrors(false), - SkipFunctionBodies(false), UseGlobalModuleIndex(true), - GenerateGlobalModuleIndex(true), ASTDumpDecls(false), - ASTDumpLookups(false), BuildingImplicitModule(false), - BuildingImplicitModuleUsesLock(true), ModulesEmbedAllFiles(false), - IncludeTimestamps(true), UseTemporary(true), - AllowPCMWithCompilerErrors(false), ModulesShareFileManager(true), - TimeTraceGranularity(500) {} + ShowStats(false), ShowVersion(false), FixWhatYouCan(false), + FixOnlyWarnings(false), FixAndRecompile(false), FixToTemporaries(false), + ARCMTMigrateEmitARCErrors(false), SkipFunctionBodies(false), + UseGlobalModuleIndex(true), GenerateGlobalModuleIndex(true), + ASTDumpDecls(false), ASTDumpLookups(false), + BuildingImplicitModule(false), BuildingImplicitModuleUsesLock(true), + ModulesEmbedAllFiles(false), IncludeTimestamps(true), + UseTemporary(true), AllowPCMWithCompilerErrors(false), + ModulesShareFileManager(true), TimeTraceGranularity(500) {} /// getInputKindForExtension - Return the appropriate input kind for a file /// extension. For example, "c" would return Language::C. diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -4510,6 +4510,82 @@ llvm_unreachable("invalid phase in ConstructPhaseAction"); } +// Infer data storing path of the options `-ftime-trace`, `-ftime-trace=` +static void inferTimeTracePath(Compilation &C, const Arg *FinalOutput) { + const ArgList &Args = C.getArgs(); + const bool HasTimeTrace = Args.hasArgNoClaim(options::OPT_ftime_trace); + const bool HasTimeTraceFile = Args.hasArgNoClaim(options::OPT_ftime_trace_EQ); + + // Whether `-ftime-trace` or `-ftime-trace=` are specified + if (!HasTimeTrace && !HasTimeTraceFile) + return; + + if (HasTimeTraceFile) { + StringRef TracePath = + Args.getLastArgNoClaim(options::OPT_ftime_trace_EQ)->getValue(); + if (!llvm::sys::fs::is_directory(TracePath)) + TracePath = llvm::sys::path::parent_path(TracePath); + C.setTimeTracePath(TracePath); + return; + } + + if (FinalOutput != nullptr) { + C.setTimeTracePath(llvm::sys::path::parent_path(FinalOutput->getValue())); + return; + } + + // Empty trace path prefix, use the current working directory + C.setTimeTracePath(StringRef()); +} + +const char *Driver::getTimeTraceOutputPath(Compilation &C, const JobAction &JA, + StringRef OrigFileName, + StringRef OrigBoundArch, + bool AtTopLevel, bool MultipleArchs, + StringRef OffloadingPrefix) const { + StringRef FileName = OrigFileName; + if (AtTopLevel) { + // Output to a user requested destination? + if (const Arg *TimeTraceFile = + C.getArgs().getLastArg(options::OPT_ftime_trace_EQ)) { + if (!llvm::sys::fs::is_directory(TimeTraceFile->getValue())) + return TimeTraceFile->getValue(); + assert(TimeTraceFile->getValue() == C.getTimeTracePath() && + "C.getTimeTracePath() should match the value of -ftime-trace="); + } + + // "-o output.o" -> "output.json" + if (const Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o)) + FileName = FinalOutput->getValue(); + } + const StringRef Stem = llvm::sys::path::stem(FileName); + + llvm::SmallString<64> BoundArch(OrigBoundArch); + if (is_style_windows(llvm::sys::path::Style::native)) { + // BoundArch may contains ':', which is invalid in file names on Windows, + // therefore replace it with '%'. + std::replace(BoundArch.begin(), BoundArch.end(), ':', '@'); + } + + SmallString<128> TraceFileName(C.getTimeTracePath()); + llvm::sys::path::append(TraceFileName, Stem); + TraceFileName += OffloadingPrefix; + if (MultipleArchs && !BoundArch.empty()) { + TraceFileName += "-"; + TraceFileName.append(BoundArch); + } + if (isSaveTempsEnabled()) { + const char *Suffix = types::getTypeTempSuffix(JA.getType(), IsCLMode()); + assert(Suffix && "All types used for output should have a suffix."); + TraceFileName += "."; + TraceFileName += Suffix; + } + + TraceFileName += ".json"; + + return C.addResultFile(C.getArgs().MakeArgString(TraceFileName.c_str()), &JA); +} + void Driver::BuildJobs(Compilation &C) const { llvm::PrettyStackTraceString CrashInfo("Building compilation jobs"); @@ -4572,6 +4648,8 @@ if (A->getOption().matches(options::OPT_arch)) ArchNames.insert(A->getValue()); + inferTimeTracePath(C, FinalOutput); + // Set of (Action, canonical ToolChain triple) pairs we've built jobs for. std::map, InputInfoList> CachedResults; for (Action *A : C.getActions()) { @@ -5325,6 +5403,22 @@ BaseInput); } + // Only claim the options if the tool supports '-ftime-trace' + if (T->supportsTimeTrace() && + Args.hasArg(options::OPT_ftime_trace, options::OPT_ftime_trace_EQ)) { + // We only have to generate a prefix for the host if this is not a top-level + // action. + std::string OffloadingPrefix = Action::GetOffloadingFileNamePrefix( + A->getOffloadingDeviceKind(), TC->getTriple().normalize(), + /*CreatePrefixForHost=*/ + !(A->getOffloadingHostActiveKinds() == Action::OFK_None || AtTopLevel)); + + const char *TimeTraceOutputPath = + getTimeTraceOutputPath(C, *JA, BaseInput, BoundArch, AtTopLevel, + MultipleArchs, OffloadingPrefix); + C.setTimeTraceFileForJob(JA, TimeTraceOutputPath); + } + if (CCCPrintBindings && !CCGenDiagnostics) { llvm::errs() << "# \"" << T->getToolChain().getTripleString() << '"' << " - \"" << T->getName() << "\", inputs: ["; diff --git a/clang/lib/Driver/ToolChains/Clang.h b/clang/lib/Driver/ToolChains/Clang.h --- a/clang/lib/Driver/ToolChains/Clang.h +++ b/clang/lib/Driver/ToolChains/Clang.h @@ -113,6 +113,7 @@ bool hasIntegratedBackend() const override { return HasBackend; } bool hasIntegratedCPP() const override { return true; } bool canEmitIR() const override { return true; } + bool supportsTimeTrace() const override { return true; } void ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6230,9 +6230,11 @@ Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_parseable_fixits); Args.AddLastArg(CmdArgs, options::OPT_ftime_report); Args.AddLastArg(CmdArgs, options::OPT_ftime_report_EQ); - Args.AddLastArg(CmdArgs, options::OPT_ftime_trace); Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_granularity_EQ); - Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_EQ); + + if (const char *Name = C.getTimeTraceFileForJob(&JA)) + CmdArgs.push_back(Args.MakeArgString("-ftime-trace=" + Twine(Name))); + Args.AddLastArg(CmdArgs, options::OPT_ftrapv); Args.AddLastArg(CmdArgs, options::OPT_malign_double); Args.AddLastArg(CmdArgs, options::OPT_fno_temp_file); diff --git a/clang/test/Driver/check-time-trace.cpp b/clang/test/Driver/check-time-trace.cpp --- a/clang/test/Driver/check-time-trace.cpp +++ b/clang/test/Driver/check-time-trace.cpp @@ -31,6 +31,42 @@ // CHECK: "name": "process_name" // CHECK: "name": "thread_name" +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %clangxx -ftime-trace=path/to/trace.json -c -x hip -nogpulib -nogpuinc --offload-arch=gfx803 --offload-arch=gfx900 -### -- %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=COMMON,FILE %s +// RUN: %clangxx -ftime-trace=%t -c -x hip -nogpulib -nogpuinc --offload-arch=gfx803 --offload-arch=gfx900 -### -- %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=COMMON,FOLDER %s +// RUN: %clangxx -ftime-trace -c -o path/to/output.o -x hip -nogpulib -nogpuinc --offload-arch=gfx803 --offload-arch=gfx900 -### -- %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=COMMON,OUTPUT %s +// RUN: %clangxx -ftime-trace -x hip -nogpulib -nogpuinc --offload-arch=gfx803 --offload-arch=gfx900 -### -- %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=COMMON,NO_OUTPUT %s + +// COMMON: "-target-cpu" "gfx803" +// FILE: "-ftime-trace=path/to/check-time-trace-{{.*}}-gfx803.json" +// FOLDER: "-ftime-trace=[[BASE:.*]]/check-time-trace-{{.*}}-gfx803.json" +// OUTPUT: "-ftime-trace=path/to/check-time-trace-{{.*}}-gfx803.json" +// NO_OUTPUT: "-ftime-trace=check-time-trace-{{.*}}-gfx803.json" + +// COMMON: "-target-cpu" "gfx900" +// FILE: "-ftime-trace=path/to/check-time-trace-{{.*}}-gfx900.json" +// FOLDER: "-ftime-trace=[[BASE]]/check-time-trace-{{.*}}-gfx900.json" +// OUTPUT: "-ftime-trace=path/to/check-time-trace-{{.*}}-gfx900.json" +// NO_OUTPUT: "-ftime-trace=check-time-trace-{{.*}}-gfx900.json" + +// COMMON: "-cc1" +// COMMON-NOT: "-target-cpu" "gfx +// FILE: "-ftime-trace=path/to/trace.json" +// FOLDER: "-ftime-trace=[[BASE]]/check-time-trace.json" +// OUTPUT: "-ftime-trace=path/to/output.json" +// NO_OUTPUT: "-ftime-trace=check-time-trace-host-{{.*}}.json" + +// RUN %clangxx -ftime-trace -save-temps -S -### -- %s 2>&1 | FileCheck --check-prefix=SAVE_TEMPS +// SAVE_TEMPS: "-E" +// SAVE_TEMPS: "-ftime-trace=check-time-trace.ii.json" +// SAVE_TEMPS: "-S" +// SAVE_TEMPS: "-ftime-trace=check-time-trace.s.json" + template struct Struct { T Num; diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp --- a/clang/tools/driver/cc1_main.cpp +++ b/clang/tools/driver/cc1_main.cpp @@ -212,9 +212,7 @@ bool Success = CompilerInvocation::CreateFromArgs(Clang->getInvocation(), Argv, Diags, Argv0); - if (Clang->getFrontendOpts().TimeTrace || - !Clang->getFrontendOpts().TimeTracePath.empty()) { - Clang->getFrontendOpts().TimeTrace = 1; + if (!Clang->getFrontendOpts().TimeTracePath.empty()) { llvm::timeTraceProfilerInitialize( Clang->getFrontendOpts().TimeTraceGranularity, Argv0); } @@ -256,17 +254,10 @@ llvm::TimerGroup::clearAll(); if (llvm::timeTraceProfilerEnabled()) { - SmallString<128> Path(Clang->getFrontendOpts().OutputFile); - llvm::sys::path::replace_extension(Path, "json"); - if (!Clang->getFrontendOpts().TimeTracePath.empty()) { - // replace the suffix to '.json' directly - SmallString<128> TracePath(Clang->getFrontendOpts().TimeTracePath); - if (llvm::sys::fs::is_directory(TracePath)) - llvm::sys::path::append(TracePath, llvm::sys::path::filename(Path)); - Path.assign(TracePath); - } + const StringRef TracePath = Clang->getFrontendOpts().TimeTracePath; + assert(!TracePath.empty() && "`-ftime-trace=` is empty"); if (auto profilerOutput = Clang->createOutputFile( - Path.str(), /*Binary=*/false, /*RemoveFileOnSignal=*/false, + TracePath, /*Binary=*/false, /*RemoveFileOnSignal=*/false, /*useTemporary=*/false)) { llvm::timeTraceProfilerWrite(*profilerOutput); profilerOutput.reset();