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 @@ -94,6 +94,7 @@ #include "llvm/Support/StringSaver.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" +#include #include #include #include @@ -4658,6 +4659,91 @@ llvm_unreachable("invalid phase in ConstructPhaseAction"); } +// Infer data storing path of the options `-ftime-trace`, `-ftime-trace=` +void InferTimeTracePath(Compilation &C) { + bool TimeTrace = C.getArgs().getLastArg(options::OPT_ftime_trace) != nullptr; + bool TimeTraceFile = + C.getArgs().getLastArg(options::OPT_ftime_trace_EQ) != nullptr; + // if `-ftime-trace` or `-ftime-trace=` are specified + if (TimeTrace || TimeTraceFile) { + SmallString<128> TracePath(""); + // get the linking executable file's parent path, default is "." + // 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 && + !J.getOutputFilenames().empty()) { + if (llvm::sys::path::has_parent_path( + SmallString<128>(J.getOutputFilenames()[0].c_str()))) { + TracePath = llvm::sys::path::parent_path( + SmallString<128>(J.getOutputFilenames()[0].c_str())); + } else { + TracePath = SmallString<128>("."); + } + break; + } + } + // Add or replace -ftime-trace` to the correct one to all clang jobs + for (auto &J : C.getJobs()) { + if (J.getSource().getKind() == Action::AssembleJobClass || + J.getSource().getKind() == Action::BackendJobClass) { + SmallString<128> OutputPath(J.getOutputFilenames()[0].c_str()); + std::string arg = std::string("-ftime-trace="); + if (!TimeTraceFile) { + if (TracePath.empty()) { + // /xxx/yyy.o => /xxx/yyy.json + llvm::sys::path::replace_extension(OutputPath, "json"); + arg += std::string(OutputPath.c_str()); + } else { + // /xxx/yyy.o => /executable_file_parent_path/yyy.json + llvm::sys::path::append(TracePath, + llvm::sys::path::filename(OutputPath)); + llvm::sys::path::replace_extension(TracePath, "json"); + arg += std::string(TracePath.c_str()); + } + } else { + // /full_file_path_specified or /path_specified/yyy.json + TracePath = SmallString<128>( + C.getArgs().getLastArg(options::OPT_ftime_trace_EQ)->getValue()); + if (llvm::sys::fs::is_directory(TracePath)) + llvm::sys::path::append(TracePath, + llvm::sys::path::filename(OutputPath)); + llvm::sys::path::replace_extension(TracePath, "json"); + arg += std::string(TracePath.c_str()); + } + + assert(arg.size() > strlen("-ftime-trace") && + arg.find("-ftime-trace=") == 0 && arg[arg.size() - 1] != '=' && + "invalid `-ftime-trace=`"); + + const std::string::size_type size = arg.size(); + char *buffer = new char[size + 1]; + memcpy(buffer, arg.c_str(), size + 1); + + // replace `-ftime-trace=` + auto &JArgs = J.getArguments(); + bool exist = false; + for (unsigned I = 0; I < JArgs.size(); ++I) { + if (StringRef(JArgs[I]).startswith("-ftime-trace=")) { + ArgStringList NewArgs(JArgs.begin(), JArgs.begin() + I); + NewArgs.push_back(buffer); + NewArgs.append(JArgs.begin() + I + 1, JArgs.end()); + J.replaceArguments(NewArgs); + exist = true; + break; + } + } + // `-ftime-trace=` is not found, add it directly + if (!exist) { + ArgStringList NewArgs(JArgs.begin(), JArgs.end()); + NewArgs.push_back(buffer); + J.replaceArguments(NewArgs); + } + } + } + } +} + void Driver::BuildJobs(Compilation &C) const { llvm::PrettyStackTraceString CrashInfo("Building compilation jobs"); @@ -4736,6 +4822,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,9 @@ +// RUN: rm -rf %T/exe && mkdir %T/exe +// RUN: %clangxx -ftime-trace -ftime-trace-granularity=0 -o %T/exe/check-time-trace %s -### +// RUN: %clangxx -ftime-trace -ftime-trace-granularity=0 -o %T/exe/check-time-trace %s +// RUN: cat %T/exe/check-time-trace*.json \ +// RUN: | %python -c 'import json, sys; json.dump(json.loads(sys.stdin.read()), sys.stdout, sort_keys=True, indent=2)' \ +// RUN: | FileCheck %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)' \ 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();