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,95 @@ llvm_unreachable("invalid phase in ConstructPhaseAction"); } +// Infer data storing path of the options `-ftime-trace`, `-ftime-trace=` +static void inferTimeTracePath(Compilation &C) { + bool HasTimeTrace = C.getArgs().hasArg(options::OPT_ftime_trace); + bool HasTimeTraceFile = C.getArgs().hasArg(options::OPT_ftime_trace_EQ); + // Whether `-ftime-trace` or `-ftime-trace=` are specified + if (!HasTimeTrace && !HasTimeTraceFile) + return; + + // If `-ftime-trace=` is specified, TracePath is the . + // Else if there is a linking job, TracePath is the parent path of .exe, + // then the OutputFile's name may be appended to it. + // Else, TracePath is "", + // then the full OutputFile's path may be appended to it. + SmallString<128> TracePath(""); + if (HasTimeTraceFile) { + TracePath = SmallString<128>( + C.getArgs().getLastArg(options::OPT_ftime_trace_EQ)->getValue()); + } else { + // Get linking executable file's parent path as TracePath's parent path, + // default is ".". Filename may be determined and added into TracePath then. + // + // e.g. executable file's path: /usr/local/a.out + // its parent's path: /usr/local + for (auto &J : C.getJobs()) { + if (J.getSource().getKind() == Action::LinkJobClass) { + assert(!J.getOutputFilenames().empty() && + "linking output filename is empty"); + auto OutputFilePath = + SmallString<128>(J.getOutputFilenames()[0].c_str()); + if (llvm::sys::path::has_parent_path(OutputFilePath)) { + TracePath = llvm::sys::path::parent_path(OutputFilePath); + } else { + TracePath = SmallString<128>("."); + } + break; + } + } + } + + // Add or replace the modified -ftime-trace=` to all clang jobs + for (auto &J : C.getJobs()) { + if (J.getSource().getKind() == Action::AssembleJobClass || + J.getSource().getKind() == Action::BackendJobClass || + J.getSource().getKind() == Action::CompileJobClass) { + SmallString<128> TracePathReal = TracePath; + SmallString<128> OutputPath(J.getOutputFilenames()[0].c_str()); + SmallString<128> arg("-ftime-trace="); + if (!HasTimeTraceFile) { + if (TracePathReal.empty()) { + // /xxx/yyy.o => /xxx/yyy.json + llvm::sys::path::replace_extension(OutputPath, "json"); + arg += OutputPath; + } else { + // /xxx/yyy.o => /executable_file_parent_path/yyy.json + llvm::sys::path::append(TracePathReal, + llvm::sys::path::filename(OutputPath)); + llvm::sys::path::replace_extension(TracePathReal, "json"); + arg += TracePathReal; + } + } else { + // /full_file_path_specified or /path_specified/yyy.json + if (llvm::sys::fs::is_directory(TracePathReal)) + llvm::sys::path::append(TracePathReal, + llvm::sys::path::filename(OutputPath)); + llvm::sys::path::replace_extension(TracePathReal, "json"); + arg += TracePathReal; + } + + assert(arg.size() > strlen("-ftime-trace") && + arg.find("-ftime-trace=") == 0 && arg[arg.size() - 1] != '=' && + "invalid `-ftime-trace=`"); + + // Replace `-ftime-trace` or `-ftime-trace=` with the modified + // `-ftime-trace=`. + auto &JArgs = J.getArguments(); + for (size_t I = 0, S = JArgs.size(); I < S; ++I) { + if (StringRef(JArgs[I]).startswith("-ftime-trace=") || + (StringRef(JArgs[I]) == "-ftime-trace" && !HasTimeTraceFile)) { + ArgStringList NewArgs(JArgs.begin(), JArgs.begin() + I); + NewArgs.push_back(C.getArgs().MakeArgStringRef(arg)); + NewArgs.append(JArgs.begin() + I + 1, JArgs.end()); + J.replaceArguments(NewArgs); + break; + } + } + } + } +} + void Driver::BuildJobs(Compilation &C) const { llvm::PrettyStackTraceString CrashInfo("Building compilation jobs"); @@ -4597,6 +4686,9 @@ /*TargetDeviceOffloadKind*/ Action::OFK_None); } + // set data storing path of the options `-ftime-trace`, `-ftime-trace=` + inferTimeTracePath(C); + // If we have more than one job, then disable integrated-cc1 for now. Do this // also when we need to report process execution statistics. if (C.getJobs().size() > 1 || CCPrintProcessStats) 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 @@ -1,3 +1,6 @@ +// RUN: rm -rf %T/exe && mkdir %T/exe +// RUN: %clangxx -### -ftime-trace -ftime-trace-granularity=0 -o %T/exe/check-time-trace %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-OPT %s // RUN: %clangxx -S -ftime-trace -ftime-trace-granularity=0 -o %T/check-time-trace %s // RUN: cat %T/check-time-trace.json \ // RUN: | %python -c 'import json, sys; json.dump(json.loads(sys.stdin.read()), sys.stdout, sort_keys=True, indent=2)' \ @@ -17,6 +20,7 @@ // RUN: | %python -c 'import json, sys; json.dump(json.loads(sys.stdin.read()), sys.stdout, sort_keys=True, indent=2)' \ // RUN: | FileCheck %s +// CHECK-OPT: "-ftime-trace={{.*}}{{/|\\\\}}exe{{/|\\\\}}check-time-trace{{.*}}.json" // CHECK: "beginningOfTime": {{[0-9]{16},}} // CHECK-NEXT: "traceEvents": [ // CHECK: "args": 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 @@ -256,17 +256,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); - } + SmallString<128> TracePath(Clang->getFrontendOpts().TimeTracePath); + assert(!TracePath.empty() && "`-ftime-trace=` is empty"); if (auto profilerOutput = Clang->createOutputFile( - Path.str(), /*Binary=*/false, /*RemoveFileOnSignal=*/false, + TracePath.str(), /*Binary=*/false, /*RemoveFileOnSignal=*/false, /*useTemporary=*/false)) { llvm::timeTraceProfilerWrite(*profilerOutput); profilerOutput.reset();