[clang] [llvm] [TimeProfiler][Clang] Add zstd-compressed time-trace output support (PR #228627)
Chandler Carruth via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 18 02:37:14 PDT 2026
https://github.com/chandlerc updated https://github.com/llvm/llvm-project/pull/228627
>From 2765dc14d832440ecb0ede141a64d3a1460ec088 Mon Sep 17 00:00:00 2001
From: Chandler Carruth <chandlerc at gmail.com>
Date: Fri, 2 Oct 2026 18:06:45 +0000
Subject: [PATCH 1/4] [TimeProfiler][Clang] Add zstd-compressed time-trace
output support
Time trace JSON files can become very large (tens of megabytes) when profiling
with fine granularity (-ftime-trace-granularity=0) or verbose output
(-ftime-trace-verbose), while compressing by ~20x with zstd.
Add support for writing zstd-compressed time-trace output directly using LLVM's
existing llvm::compression facilities:
- Extend llvm::timeTraceProfilerInitialize and llvm::timeTraceProfilerWrite to
support DebugCompressionType and infer zstd compression from .zst/.zstd file
extensions via llvm::inferTimeTraceCompressionFromPath.
- Add -ftime-trace-compress and -ftime-trace-compress=<none|zstd> driver and
CC1 options to explicitly control compression regardless of output filename.
- Update default output filename inference when -ftime-trace is used without an
explicit filename so compressed traces use a .json.zst extension.
Assisted-by: Antigravity with Gemini
---
.../clang/Basic/DiagnosticDriverKinds.td | 2 +
.../include/clang/Frontend/FrontendOptions.h | 5 ++
clang/include/clang/Options/Options.td | 11 +++
clang/lib/Driver/Driver.cpp | 18 ++++-
clang/lib/Driver/ToolChains/Clang.cpp | 26 +++++++
clang/lib/Frontend/CompilerInvocation.cpp | 5 ++
clang/test/Driver/compress-zstd.c | 4 +
clang/test/Driver/ftime-trace.cpp | 11 ++-
clang/tools/driver/cc1_main.cpp | 7 +-
llvm/include/llvm/Support/TimeProfiler.h | 12 ++-
llvm/lib/Support/TimeProfiler.cpp | 74 +++++++++++++++++--
llvm/unittests/Support/TimeProfilerTest.cpp | 28 +++++++
12 files changed, 185 insertions(+), 18 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index df6ff0c2cf399f..369fbef8d0c858 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -564,6 +564,8 @@ def warn_incompatible_sysroot : Warning<"using sysroot for '%0' but targeting '%
InGroup<DiagGroup<"incompatible-sysroot">>;
def warn_debug_compression_unavailable : Warning<"cannot compress debug sections (%0 not enabled)">,
InGroup<DiagGroup<"debug-compression-unavailable">>;
+def err_drv_time_trace_compression_unavailable
+ : Error<"cannot compress time trace (%0 not enabled)">;
def warn_drv_disabling_vptr_no_rtti_default : Warning<
"implicitly disabling vptr sanitizer because rtti wasn't enabled">,
InGroup<AutoDisableVptrSanitizer>;
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index b85cf4c0078628..701c8d75fe61f8 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -16,6 +16,7 @@
#include "clang/Serialization/ModuleFileExtension.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Compression.h"
#include "llvm/Support/MemoryBuffer.h"
#include <cassert>
#include <map>
@@ -543,6 +544,10 @@ class FrontendOptions {
LLVM_PREFERRED_TYPE(bool)
unsigned TimeTraceVerbose : 1;
+ /// Compression format for -ftime-trace output.
+ llvm::DebugCompressionType TimeTraceCompress =
+ llvm::DebugCompressionType::None;
+
/// Path which stores the output files for -ftime-trace
std::string TimeTracePath;
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 60ae84cb0af5c7..1d867223b9b68b 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -4822,6 +4822,17 @@ def ftime_trace_verbose : Joined<["-"], "ftime-trace-verbose">, Group<f_Group>,
HelpText<"Make time trace capture verbose event details (e.g. source filenames). This can increase the size of the output by 2-3 times">,
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
MarshallingInfoFlag<FrontendOpts<"TimeTraceVerbose">>;
+def ftime_trace_compress_EQ : Joined<["-"], "ftime-trace-compress=">, Group<f_Group>,
+ HelpText<"Compress -ftime-trace output with the specified algorithm">,
+ Values<"none,zstd">,
+ NormalizedValuesScope<"llvm::DebugCompressionType">,
+ NormalizedValues<["None", "Zstd"]>,
+ Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
+ MarshallingInfoEnum<FrontendOpts<"TimeTraceCompress">, "None">;
+def ftime_trace_compress : Flag<["-"], "ftime-trace-compress">, Group<f_Group>,
+ Alias<ftime_trace_compress_EQ>, AliasArgs<["zstd"]>,
+ HelpText<"Alias for -ftime-trace-compress=zstd">,
+ Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>;
def ftime_trace_EQ : Joined<["-"], "ftime-trace=">, Group<f_Group>,
HelpText<"Similar to -ftime-trace. Specify the JSON file or a directory which will contain the JSON file">,
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 7649941a68b1cc..79ad73d45825c5 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -5173,6 +5173,16 @@ static void handleTimeTrace(Compilation &C, const ArgList &Args,
/*CreatePrefixForHost=*/true);
}
+ StringRef DefaultExt = ".json";
+ StringRef DefaultExtNoDot = "json";
+ if (const Arg *CompressArg =
+ Args.getLastArgNoClaim(options::OPT_ftime_trace_compress_EQ)) {
+ if (StringRef(CompressArg->getValue()) == "zstd") {
+ DefaultExt = ".json.zst";
+ DefaultExtNoDot = "json.zst";
+ }
+ }
+
SmallString<128> Path;
if (A->getOption().matches(options::OPT_ftime_trace_EQ)) {
Path = A->getValue();
@@ -5181,7 +5191,7 @@ static void handleTimeTrace(Compilation &C, const ArgList &Args,
? llvm::sys::path::stem(Result.getFilename())
: llvm::sys::path::stem(BaseInput));
Tmp += OffloadingPrefix;
- Tmp += ".json";
+ Tmp += DefaultExt;
llvm::sys::path::append(Path, Tmp);
}
} else {
@@ -5191,7 +5201,7 @@ static void handleTimeTrace(Compilation &C, const ArgList &Args,
Path = DumpDir->getValue();
Path += llvm::sys::path::stem(BaseInput);
Path += OffloadingPrefix;
- Path += ".json";
+ Path += DefaultExt;
} else if (!OffloadingPrefix.empty()) {
// For offloading, derive path from -o output directory combined with
// the input filename and offload prefix.
@@ -5200,10 +5210,10 @@ static void handleTimeTrace(Compilation &C, const ArgList &Args,
if (Arg *FinalOutput = Args.getLastArg(options::OPT_o))
Path = llvm::sys::path::parent_path(FinalOutput->getValue());
llvm::sys::path::append(Path, TraceName);
- Path += ".json";
+ Path += DefaultExt;
} else {
Path = Result.getFilename();
- llvm::sys::path::replace_extension(Path, "json");
+ llvm::sys::path::replace_extension(Path, DefaultExtNoDot);
}
}
const char *ResultFile = C.getArgs().MakeArgString(Path);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index f0d04bf4b6a838..eda506494829a8 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -46,11 +46,13 @@
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Compression.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
+#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/TargetParser/AArch64TargetParser.h"
#include "llvm/TargetParser/ARMTargetParserCommon.h"
@@ -7344,6 +7346,28 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(Args.MakeArgString("-ftime-trace=" + Twine(Name)));
Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_granularity_EQ);
Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_verbose);
+ if (const Arg *CompressArg =
+ Args.getLastArg(options::OPT_ftime_trace_compress_EQ)) {
+ StringRef Value = CompressArg->getValue();
+ if (Value == "none") {
+ // Explicit -ftime-trace-compress=none disables compression even if the
+ // filename has a .zst/.zstd extension.
+ } else if (Value == "zstd") {
+ if (llvm::compression::zstd::isAvailable())
+ CmdArgs.push_back("-ftime-trace-compress=zstd");
+ else
+ D.Diag(diag::err_drv_time_trace_compression_unavailable) << "zstd";
+ } else {
+ D.Diag(diag::err_drv_unsupported_option_argument)
+ << CompressArg->getSpelling() << Value;
+ }
+ } else if (llvm::inferTimeTraceCompressionFromPath(Name) ==
+ llvm::DebugCompressionType::Zstd) {
+ if (llvm::compression::zstd::isAvailable())
+ CmdArgs.push_back("-ftime-trace-compress=zstd");
+ else
+ D.Diag(diag::err_drv_time_trace_compression_unavailable) << "zstd";
+ }
}
if (Arg *A = Args.getLastArg(options::OPT_ftrapv_handler_EQ)) {
@@ -9760,6 +9784,8 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
OPT_ftime_trace_EQ,
OPT_ftime_trace_granularity_EQ,
OPT_ftime_trace_verbose,
+ OPT_ftime_trace_compress,
+ OPT_ftime_trace_compress_EQ,
OPT_opt_record_file,
OPT_opt_record_format,
OPT_opt_record_passes,
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index b794a99c3d0c08..ed30d93b6026b4 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -5239,6 +5239,11 @@ bool CompilerInvocation::CreateFromArgsImpl(
llvm::driver::ProfileInstrKind::ProfileNone)
Diags.Report(diag::err_drv_profile_instrument_use_path_with_no_kind);
+ if (Res.getFrontendOpts().TimeTraceCompress ==
+ llvm::DebugCompressionType::Zstd &&
+ !llvm::compression::zstd::isAvailable())
+ Diags.Report(diag::err_drv_time_trace_compression_unavailable) << "zstd";
+
FixupInvocation(Res, Diags, Args, DashX);
return Diags.getNumErrors() == NumErrorsBefore;
diff --git a/clang/test/Driver/compress-zstd.c b/clang/test/Driver/compress-zstd.c
index 84b26354589e95..a8e07631eede48 100644
--- a/clang/test/Driver/compress-zstd.c
+++ b/clang/test/Driver/compress-zstd.c
@@ -5,3 +5,7 @@
// CHECK: {{"-cc1(as)?".* "--compress-debug-sections=zstd"}}
// CHECK: "--compress-debug-sections=zstd"
+
+// RUN: %clang -### -c -ftime-trace -ftime-trace-compress %s -o a.o 2>&1 | FileCheck %s --check-prefix=TIME-TRACE-ZSTD
+// RUN: %clang -### -c -ftime-trace=a.json.zst %s -o a.o 2>&1 | FileCheck %s --check-prefix=TIME-TRACE-ZSTD
+// TIME-TRACE-ZSTD: "-cc1"{{.*}} "-ftime-trace=a.json.zst" "-ftime-trace-compress=zstd"
diff --git a/clang/test/Driver/ftime-trace.cpp b/clang/test/Driver/ftime-trace.cpp
index 60c5885704b58e..77c46ae43bb534 100644
--- a/clang/test/Driver/ftime-trace.cpp
+++ b/clang/test/Driver/ftime-trace.cpp
@@ -55,14 +55,23 @@
// LINK3: -cc1{{.*}} "-ftime-trace=e{{/|\\\\}}a-{{[^.]*}}.json" "-ftime-trace-granularity=0" "-ftime-trace-verbose"
// LINK3: -cc1{{.*}} "-ftime-trace=e{{/|\\\\}}b-{{[^.]*}}.json" "-ftime-trace-granularity=0" "-ftime-trace-verbose"
-// RUN: %clang -### -ftime-trace -ftime-trace=e -ftime-trace-granularity=1 -ftime-trace-verbose -xassembler d/a.cpp 2>&1 | \
+// RUN: %clang -### -ftime-trace -ftime-trace=e -ftime-trace-granularity=1 -ftime-trace-verbose -ftime-trace-compress -ftime-trace-compress=none -xassembler d/a.cpp 2>&1 | \
// RUN: FileCheck %s --check-prefix=UNUSED
// UNUSED: warning: argument unused during compilation: '-ftime-trace'
// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace=e'
// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace-granularity=1'
// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace-verbose'
+// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace-compress'
+// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace-compress=none'
// UNUSED-NOT: warning:
+// RUN: %clang -### -c -ftime-trace=foo.json.zst -ftime-trace-compress=none -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-NONE
+// COMPRESS-NONE: -cc1{{.*}} "-ftime-trace=foo.json.zst"
+// COMPRESS-NONE-NOT: "-ftime-trace-compress
+
+// RUN: not %clang -### -c -ftime-trace -ftime-trace-compress=invalid d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-INVALID
+// COMPRESS-INVALID: error: unsupported argument 'invalid' to option '-ftime-trace-compress='
+
template <typename T>
struct Struct {
T Num;
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 89b0a340e6672e..1c3fef0630ea45 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -253,7 +253,8 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
if (!Clang->getFrontendOpts().TimeTracePath.empty()) {
llvm::timeTraceProfilerInitialize(
Clang->getFrontendOpts().TimeTraceGranularity, Argv0,
- Clang->getFrontendOpts().TimeTraceVerbose);
+ Clang->getFrontendOpts().TimeTraceVerbose,
+ Clang->getFrontendOpts().TimeTraceCompress);
}
// --print-supported-cpus takes priority over the actual compilation.
if (Clang->getFrontendOpts().PrintSupportedCPUs)
@@ -312,8 +313,10 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
}
if (llvm::timeTraceProfilerEnabled()) {
+ bool Binary = Clang->getFrontendOpts().TimeTraceCompress !=
+ llvm::DebugCompressionType::None;
if (auto profilerOutput = Clang->createOutputFile(
- Clang->getFrontendOpts().TimeTracePath, /*Binary=*/false,
+ Clang->getFrontendOpts().TimeTracePath, Binary,
/*RemoveFileOnSignal=*/false,
/*useTemporary=*/false)) {
llvm::timeTraceProfilerWrite(*profilerOutput);
diff --git a/llvm/include/llvm/Support/TimeProfiler.h b/llvm/include/llvm/Support/TimeProfiler.h
index 2eb2c09d1becd3..014379de62f0d2 100644
--- a/llvm/include/llvm/Support/TimeProfiler.h
+++ b/llvm/include/llvm/Support/TimeProfiler.h
@@ -78,6 +78,7 @@
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Compression.h"
#include "llvm/Support/Error.h"
namespace llvm {
@@ -113,12 +114,17 @@ LLVM_ABI bool isTimeTraceVerbose();
struct TimeTraceProfilerEntry;
+/// Infer the time trace compression format from \p Path based on its file
+/// extension (.zst/.zstd -> Zstd, otherwise None).
+LLVM_ABI DebugCompressionType inferTimeTraceCompressionFromPath(StringRef Path);
+
/// Initialize the time trace profiler.
/// This sets up the global \p TimeTraceProfilerInstance
/// variable to be the profiler instance.
-LLVM_ABI void timeTraceProfilerInitialize(unsigned TimeTraceGranularity,
- StringRef ProcName,
- bool TimeTraceVerbose = false);
+LLVM_ABI void timeTraceProfilerInitialize(
+ unsigned TimeTraceGranularity, StringRef ProcName,
+ bool TimeTraceVerbose = false,
+ DebugCompressionType TimeTraceCompress = DebugCompressionType::None);
/// Cleanup the time trace profiler, if it was initialized.
LLVM_ABI void timeTraceProfilerCleanup();
diff --git a/llvm/lib/Support/TimeProfiler.cpp b/llvm/lib/Support/TimeProfiler.cpp
index 002529f20d661b..c2816e98cd30a8 100644
--- a/llvm/lib/Support/TimeProfiler.cpp
+++ b/llvm/lib/Support/TimeProfiler.cpp
@@ -127,12 +127,15 @@ struct InProgressEntry {
};
struct llvm::TimeTraceProfiler {
- TimeTraceProfiler(unsigned TimeTraceGranularity = 0, StringRef ProcName = "",
- bool TimeTraceVerbose = false)
+ TimeTraceProfiler(
+ unsigned TimeTraceGranularity = 0, StringRef ProcName = "",
+ bool TimeTraceVerbose = false,
+ DebugCompressionType TimeTraceCompress = DebugCompressionType::None)
: BeginningOfTime(system_clock::now()), StartTime(ClockType::now()),
ProcName(ProcName), Pid(sys::Process::getProcessId()),
Tid(llvm::get_threadid()), TimeTraceGranularity(TimeTraceGranularity),
- TimeTraceVerbose(TimeTraceVerbose) {
+ TimeTraceVerbose(TimeTraceVerbose),
+ TimeTraceCompress(TimeTraceCompress) {
llvm::get_thread_name(ThreadName);
}
@@ -212,7 +215,7 @@ struct llvm::TimeTraceProfiler {
// Write events from this TimeTraceProfilerInstance and
// ThreadTimeTraceProfilerInstances.
- void write(raw_pwrite_stream &OS) {
+ void write(raw_ostream &OS) {
// Acquire Mutex as reading ThreadTimeTraceProfilerInstances.
auto &Instances = getTimeTraceProfilerInstances();
std::lock_guard<std::mutex> Lock(Instances.Lock);
@@ -383,6 +386,9 @@ struct llvm::TimeTraceProfiler {
// Make time trace capture verbose event details (e.g. source filenames). This
// can increase the size of the output by 2-3 times.
const bool TimeTraceVerbose;
+
+ // Optional compression format for output.
+ DebugCompressionType TimeTraceCompress;
};
bool llvm::isTimeTraceVerbose() {
@@ -390,14 +396,22 @@ bool llvm::isTimeTraceVerbose() {
getTimeTraceProfilerInstance()->TimeTraceVerbose;
}
+DebugCompressionType llvm::inferTimeTraceCompressionFromPath(StringRef Path) {
+ StringRef Ext = llvm::sys::path::extension(Path);
+ if (Ext.equals_insensitive(".zst") || Ext.equals_insensitive(".zstd"))
+ return DebugCompressionType::Zstd;
+ return DebugCompressionType::None;
+}
+
void llvm::timeTraceProfilerInitialize(unsigned TimeTraceGranularity,
StringRef ProcName,
- bool TimeTraceVerbose) {
+ bool TimeTraceVerbose,
+ DebugCompressionType TimeTraceCompress) {
assert(TimeTraceProfilerInstance == nullptr &&
"Profiler should not be initialized");
TimeTraceProfilerInstance = new TimeTraceProfiler(
TimeTraceGranularity, llvm::sys::path::filename(ProcName),
- TimeTraceVerbose);
+ TimeTraceVerbose, TimeTraceCompress);
}
// Removes all TimeTraceProfilerInstances.
@@ -425,7 +439,30 @@ void llvm::timeTraceProfilerFinishThread() {
void llvm::timeTraceProfilerWrite(raw_pwrite_stream &OS) {
assert(TimeTraceProfilerInstance != nullptr &&
"Profiler object can't be null");
- TimeTraceProfilerInstance->write(OS);
+ DebugCompressionType CompressType =
+ TimeTraceProfilerInstance->TimeTraceCompress;
+ if (CompressType == DebugCompressionType::None) {
+ TimeTraceProfilerInstance->write(OS);
+ return;
+ }
+
+ compression::Format F = compression::formatFor(CompressType);
+ if (const char *Reason = compression::getReasonIfUnsupported(F))
+ report_fatal_error(Reason);
+
+ SmallVector<char, 0> Uncompressed;
+ {
+ raw_svector_ostream UncompressedOS(Uncompressed);
+ TimeTraceProfilerInstance->write(UncompressedOS);
+ }
+ SmallVector<uint8_t, 0> Compressed;
+ compression::compress(
+ compression::Params(F),
+ ArrayRef(reinterpret_cast<const uint8_t *>(Uncompressed.data()),
+ Uncompressed.size()),
+ Compressed);
+ OS.write(reinterpret_cast<const char *>(Compressed.data()),
+ Compressed.size());
}
Error llvm::timeTraceProfilerWrite(StringRef PreferredFileName,
@@ -437,14 +474,35 @@ Error llvm::timeTraceProfilerWrite(StringRef PreferredFileName,
if (Path.empty()) {
Path = FallbackFileName == "-" ? "out" : FallbackFileName.str();
Path += TimeTraceFileExtension;
+ if (TimeTraceProfilerInstance->TimeTraceCompress ==
+ DebugCompressionType::Zstd)
+ Path += ".zst";
+ }
+
+ DebugCompressionType CompressType =
+ TimeTraceProfilerInstance->TimeTraceCompress;
+ if (CompressType == DebugCompressionType::None)
+ CompressType = inferTimeTraceCompressionFromPath(Path);
+
+ if (CompressType != DebugCompressionType::None) {
+ if (const char *Reason = compression::getReasonIfUnsupported(
+ compression::formatFor(CompressType)))
+ return createStringError(inconvertibleErrorCode(), Reason);
}
std::error_code EC;
- raw_fd_ostream OS(Path, EC, sys::fs::OF_TextWithCRLF);
+ sys::fs::OpenFlags Flags = CompressType == DebugCompressionType::None
+ ? sys::fs::OF_TextWithCRLF
+ : sys::fs::OF_None;
+ raw_fd_ostream OS(Path, EC, Flags);
if (EC)
return createStringError(EC, "Could not open " + Path);
+ DebugCompressionType SavedCompress =
+ TimeTraceProfilerInstance->TimeTraceCompress;
+ TimeTraceProfilerInstance->TimeTraceCompress = CompressType;
timeTraceProfilerWrite(OS);
+ TimeTraceProfilerInstance->TimeTraceCompress = SavedCompress;
return Error::success();
}
diff --git a/llvm/unittests/Support/TimeProfilerTest.cpp b/llvm/unittests/Support/TimeProfilerTest.cpp
index aa1185bae2961f..38d25b35991aab 100644
--- a/llvm/unittests/Support/TimeProfilerTest.cpp
+++ b/llvm/unittests/Support/TimeProfilerTest.cpp
@@ -43,6 +43,34 @@ TEST(TimeProfiler, Scope_Smoke) {
ASSERT_TRUE(json.find(R"("detail":"detail")") != std::string::npos);
}
+TEST(TimeProfiler, Compression) {
+ EXPECT_EQ(inferTimeTraceCompressionFromPath("foo.json"),
+ DebugCompressionType::None);
+ EXPECT_EQ(inferTimeTraceCompressionFromPath("foo.json.zst"),
+ DebugCompressionType::Zstd);
+ EXPECT_EQ(inferTimeTraceCompressionFromPath("foo.ZSTD"),
+ DebugCompressionType::Zstd);
+
+ if (!compression::zstd::isAvailable())
+ return;
+
+ timeTraceProfilerInitialize(/*TimeTraceGranularity=*/0, "test",
+ /*TimeTraceVerbose=*/false,
+ DebugCompressionType::Zstd);
+ {
+ TimeTraceScope Scope("compressed_event", "compressed_detail");
+ }
+
+ SmallVector<char, 0> CompressedChars;
+ raw_svector_ostream OS(CompressedChars);
+ timeTraceProfilerWrite(OS);
+ timeTraceProfilerCleanup();
+
+ ASSERT_FALSE(CompressedChars.empty());
+ // Compressed output must not start with '{'.
+ EXPECT_NE(CompressedChars.front(), '{');
+}
+
TEST(TimeProfiler, Begin_End_Smoke) {
setupProfiler();
>From 52e61698f81748e1009883b8bf59b689edfba190 Mon Sep 17 00:00:00 2001
From: Chandler Carruth <chandlerc at gmail.com>
Date: Mon, 5 Oct 2026 08:07:28 +0000
Subject: [PATCH 2/4] [TimeProfiler][Clang] Address review comments on #228627
- Simplify Path and CompressType initialization in timeTraceProfilerWrite so
inferTimeTraceCompressionFromPath is only consulted when an explicit
PreferredFileName is provided.
- Clarify the comment on -ftime-trace-compress=none in the Clang driver and add
a driver test case for overriding an earlier -ftime-trace-compress flag.
Assisted-by: Antigravity with Gemini
---
clang/lib/Driver/ToolChains/Clang.cpp | 5 +++--
clang/test/Driver/ftime-trace.cpp | 4 ++++
llvm/lib/Support/TimeProfiler.cpp | 12 +++++-------
3 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index eda506494829a8..a49bf1256e54c4 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7350,8 +7350,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.getLastArg(options::OPT_ftime_trace_compress_EQ)) {
StringRef Value = CompressArg->getValue();
if (Value == "none") {
- // Explicit -ftime-trace-compress=none disables compression even if the
- // filename has a .zst/.zstd extension.
+ // Explicit -ftime-trace-compress=none disables compression (for
+ // example, overriding an earlier -ftime-trace-compress flag or a
+ // .zst/.zstd filename extension); -cc1 defaults to uncompressed output.
} else if (Value == "zstd") {
if (llvm::compression::zstd::isAvailable())
CmdArgs.push_back("-ftime-trace-compress=zstd");
diff --git a/clang/test/Driver/ftime-trace.cpp b/clang/test/Driver/ftime-trace.cpp
index 77c46ae43bb534..4a3a644165444e 100644
--- a/clang/test/Driver/ftime-trace.cpp
+++ b/clang/test/Driver/ftime-trace.cpp
@@ -65,6 +65,10 @@
// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace-compress=none'
// UNUSED-NOT: warning:
+// RUN: %clang -### -c -ftime-trace -ftime-trace-compress -ftime-trace-compress=none -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-OVERRIDE-NONE
+// COMPRESS-OVERRIDE-NONE: -cc1{{.*}} "-ftime-trace=e/a.json"
+// COMPRESS-OVERRIDE-NONE-NOT: "-ftime-trace-compress
+
// RUN: %clang -### -c -ftime-trace=foo.json.zst -ftime-trace-compress=none -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-NONE
// COMPRESS-NONE: -cc1{{.*}} "-ftime-trace=foo.json.zst"
// COMPRESS-NONE-NOT: "-ftime-trace-compress
diff --git a/llvm/lib/Support/TimeProfiler.cpp b/llvm/lib/Support/TimeProfiler.cpp
index c2816e98cd30a8..5edcc6212c594a 100644
--- a/llvm/lib/Support/TimeProfiler.cpp
+++ b/llvm/lib/Support/TimeProfiler.cpp
@@ -470,19 +470,17 @@ Error llvm::timeTraceProfilerWrite(StringRef PreferredFileName,
assert(TimeTraceProfilerInstance != nullptr &&
"Profiler object can't be null");
+ DebugCompressionType CompressType =
+ TimeTraceProfilerInstance->TimeTraceCompress;
std::string Path = PreferredFileName.str();
if (Path.empty()) {
Path = FallbackFileName == "-" ? "out" : FallbackFileName.str();
Path += TimeTraceFileExtension;
- if (TimeTraceProfilerInstance->TimeTraceCompress ==
- DebugCompressionType::Zstd)
+ if (CompressType == DebugCompressionType::Zstd)
Path += ".zst";
- }
-
- DebugCompressionType CompressType =
- TimeTraceProfilerInstance->TimeTraceCompress;
- if (CompressType == DebugCompressionType::None)
+ } else if (CompressType == DebugCompressionType::None) {
CompressType = inferTimeTraceCompressionFromPath(Path);
+ }
if (CompressType != DebugCompressionType::None) {
if (const char *Reason = compression::getReasonIfUnsupported(
>From ecfda00fe1db581dcc0a18bad5958df8b386d400 Mon Sep 17 00:00:00 2001
From: Chandler Carruth <chandlerc at gmail.com>
Date: Tue, 6 Oct 2026 02:41:08 +0000
Subject: [PATCH 3/4] [TimeProfiler][Clang] Use tri-state TimeTraceCompression
and remove bare flag
- Introduce llvm::TimeTraceCompression ({Infer, None, Zstd}) in TimeProfiler.h
so TimeTraceCompression::Infer infers compression from the output path while
TimeTraceCompression::None explicitly disables compression even for
.zst/.zstd filenames.
- Remove the bare -ftime-trace-compress alias in favor of explicit
-ftime-trace-compress=<none|zstd|infer>.
Assisted-by: Antigravity with Gemini
---
.../include/clang/Frontend/FrontendOptions.h | 8 ++--
clang/include/clang/Options/Options.td | 12 ++----
clang/lib/Driver/ToolChains/Clang.cpp | 30 ++++++-------
clang/lib/Frontend/CompilerInvocation.cpp | 9 +++-
clang/test/Driver/compress-zstd.c | 3 +-
clang/test/Driver/ftime-trace.cpp | 9 ++--
clang/tools/driver/cc1_main.cpp | 16 +++++--
llvm/include/llvm/Support/TimeProfiler.h | 12 ++++--
llvm/lib/Support/TimeProfiler.cpp | 41 +++++++++---------
llvm/unittests/Support/TimeProfilerTest.cpp | 42 +++++++++++++++++--
10 files changed, 117 insertions(+), 65 deletions(-)
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 701c8d75fe61f8..5624934cd8659b 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -16,8 +16,8 @@
#include "clang/Serialization/ModuleFileExtension.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
-#include "llvm/Support/Compression.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/TimeProfiler.h"
#include <cassert>
#include <map>
#include <memory>
@@ -544,9 +544,9 @@ class FrontendOptions {
LLVM_PREFERRED_TYPE(bool)
unsigned TimeTraceVerbose : 1;
- /// Compression format for -ftime-trace output.
- llvm::DebugCompressionType TimeTraceCompress =
- llvm::DebugCompressionType::None;
+ /// Compression mode for -ftime-trace output.
+ llvm::TimeTraceCompression TimeTraceCompress =
+ llvm::TimeTraceCompression::Infer;
/// Path which stores the output files for -ftime-trace
std::string TimeTracePath;
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 1d867223b9b68b..c73a811a1a4ecf 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -4824,15 +4824,11 @@ def ftime_trace_verbose : Joined<["-"], "ftime-trace-verbose">, Group<f_Group>,
MarshallingInfoFlag<FrontendOpts<"TimeTraceVerbose">>;
def ftime_trace_compress_EQ : Joined<["-"], "ftime-trace-compress=">, Group<f_Group>,
HelpText<"Compress -ftime-trace output with the specified algorithm">,
- Values<"none,zstd">,
- NormalizedValuesScope<"llvm::DebugCompressionType">,
- NormalizedValues<["None", "Zstd"]>,
+ Values<"none,zstd,infer">,
+ NormalizedValuesScope<"llvm::TimeTraceCompression">,
+ NormalizedValues<["None", "Zstd", "Infer"]>,
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
- MarshallingInfoEnum<FrontendOpts<"TimeTraceCompress">, "None">;
-def ftime_trace_compress : Flag<["-"], "ftime-trace-compress">, Group<f_Group>,
- Alias<ftime_trace_compress_EQ>, AliasArgs<["zstd"]>,
- HelpText<"Alias for -ftime-trace-compress=zstd">,
- Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>;
+ MarshallingInfoEnum<FrontendOpts<"TimeTraceCompress">, "Infer">;
def ftime_trace_EQ : Joined<["-"], "ftime-trace=">, Group<f_Group>,
HelpText<"Similar to -ftime-trace. Specify the JSON file or a directory which will contain the JSON file">,
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index a49bf1256e54c4..1858c3b35f2747 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7346,28 +7346,31 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(Args.MakeArgString("-ftime-trace=" + Twine(Name)));
Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_granularity_EQ);
Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_verbose);
+ llvm::TimeTraceCompression Compress = llvm::TimeTraceCompression::Infer;
if (const Arg *CompressArg =
Args.getLastArg(options::OPT_ftime_trace_compress_EQ)) {
StringRef Value = CompressArg->getValue();
- if (Value == "none") {
- // Explicit -ftime-trace-compress=none disables compression (for
- // example, overriding an earlier -ftime-trace-compress flag or a
- // .zst/.zstd filename extension); -cc1 defaults to uncompressed output.
- } else if (Value == "zstd") {
- if (llvm::compression::zstd::isAvailable())
- CmdArgs.push_back("-ftime-trace-compress=zstd");
- else
- D.Diag(diag::err_drv_time_trace_compression_unavailable) << "zstd";
- } else {
+ if (Value == "none")
+ Compress = llvm::TimeTraceCompression::None;
+ else if (Value == "zstd")
+ Compress = llvm::TimeTraceCompression::Zstd;
+ else if (Value == "infer")
+ Compress = llvm::TimeTraceCompression::Infer;
+ else
D.Diag(diag::err_drv_unsupported_option_argument)
<< CompressArg->getSpelling() << Value;
- }
- } else if (llvm::inferTimeTraceCompressionFromPath(Name) ==
- llvm::DebugCompressionType::Zstd) {
+ }
+ if (Compress == llvm::TimeTraceCompression::Infer)
+ Compress = llvm::inferTimeTraceCompressionFromPath(Name);
+ if (Compress == llvm::TimeTraceCompression::Zstd) {
if (llvm::compression::zstd::isAvailable())
CmdArgs.push_back("-ftime-trace-compress=zstd");
else
D.Diag(diag::err_drv_time_trace_compression_unavailable) << "zstd";
+ } else if (Compress == llvm::TimeTraceCompression::None &&
+ llvm::inferTimeTraceCompressionFromPath(Name) !=
+ llvm::TimeTraceCompression::None) {
+ CmdArgs.push_back("-ftime-trace-compress=none");
}
}
@@ -9785,7 +9788,6 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
OPT_ftime_trace_EQ,
OPT_ftime_trace_granularity_EQ,
OPT_ftime_trace_verbose,
- OPT_ftime_trace_compress,
OPT_ftime_trace_compress_EQ,
OPT_opt_record_file,
OPT_opt_record_format,
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index ed30d93b6026b4..2d8eaea5782a70 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -5239,8 +5239,13 @@ bool CompilerInvocation::CreateFromArgsImpl(
llvm::driver::ProfileInstrKind::ProfileNone)
Diags.Report(diag::err_drv_profile_instrument_use_path_with_no_kind);
- if (Res.getFrontendOpts().TimeTraceCompress ==
- llvm::DebugCompressionType::Zstd &&
+ llvm::TimeTraceCompression TimeTraceCompress =
+ Res.getFrontendOpts().TimeTraceCompress;
+ if (TimeTraceCompress == llvm::TimeTraceCompression::Infer &&
+ !Res.getFrontendOpts().TimeTracePath.empty())
+ TimeTraceCompress = llvm::inferTimeTraceCompressionFromPath(
+ Res.getFrontendOpts().TimeTracePath);
+ if (TimeTraceCompress == llvm::TimeTraceCompression::Zstd &&
!llvm::compression::zstd::isAvailable())
Diags.Report(diag::err_drv_time_trace_compression_unavailable) << "zstd";
diff --git a/clang/test/Driver/compress-zstd.c b/clang/test/Driver/compress-zstd.c
index a8e07631eede48..22f9ca53e3be8d 100644
--- a/clang/test/Driver/compress-zstd.c
+++ b/clang/test/Driver/compress-zstd.c
@@ -6,6 +6,7 @@
// CHECK: {{"-cc1(as)?".* "--compress-debug-sections=zstd"}}
// CHECK: "--compress-debug-sections=zstd"
-// RUN: %clang -### -c -ftime-trace -ftime-trace-compress %s -o a.o 2>&1 | FileCheck %s --check-prefix=TIME-TRACE-ZSTD
+// RUN: %clang -### -c -ftime-trace -ftime-trace-compress=zstd %s -o a.o 2>&1 | FileCheck %s --check-prefix=TIME-TRACE-ZSTD
// RUN: %clang -### -c -ftime-trace=a.json.zst %s -o a.o 2>&1 | FileCheck %s --check-prefix=TIME-TRACE-ZSTD
+// RUN: %clang -### -c -ftime-trace=a.json.zst -ftime-trace-compress=none -ftime-trace-compress=infer %s -o a.o 2>&1 | FileCheck %s --check-prefix=TIME-TRACE-ZSTD
// TIME-TRACE-ZSTD: "-cc1"{{.*}} "-ftime-trace=a.json.zst" "-ftime-trace-compress=zstd"
diff --git a/clang/test/Driver/ftime-trace.cpp b/clang/test/Driver/ftime-trace.cpp
index 4a3a644165444e..032cbab5e40257 100644
--- a/clang/test/Driver/ftime-trace.cpp
+++ b/clang/test/Driver/ftime-trace.cpp
@@ -55,23 +55,22 @@
// LINK3: -cc1{{.*}} "-ftime-trace=e{{/|\\\\}}a-{{[^.]*}}.json" "-ftime-trace-granularity=0" "-ftime-trace-verbose"
// LINK3: -cc1{{.*}} "-ftime-trace=e{{/|\\\\}}b-{{[^.]*}}.json" "-ftime-trace-granularity=0" "-ftime-trace-verbose"
-// RUN: %clang -### -ftime-trace -ftime-trace=e -ftime-trace-granularity=1 -ftime-trace-verbose -ftime-trace-compress -ftime-trace-compress=none -xassembler d/a.cpp 2>&1 | \
+// RUN: %clang -### -ftime-trace -ftime-trace=e -ftime-trace-granularity=1 -ftime-trace-verbose -ftime-trace-compress=none -xassembler d/a.cpp 2>&1 | \
// RUN: FileCheck %s --check-prefix=UNUSED
// UNUSED: warning: argument unused during compilation: '-ftime-trace'
// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace=e'
// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace-granularity=1'
// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace-verbose'
-// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace-compress'
// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace-compress=none'
// UNUSED-NOT: warning:
-// RUN: %clang -### -c -ftime-trace -ftime-trace-compress -ftime-trace-compress=none -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-OVERRIDE-NONE
+// RUN: %clang -### -c -ftime-trace -ftime-trace-compress=zstd -ftime-trace-compress=none -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-OVERRIDE-NONE
+// RUN: %clang -### -c -ftime-trace -ftime-trace-compress=zstd -ftime-trace-compress=infer -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-OVERRIDE-NONE
// COMPRESS-OVERRIDE-NONE: -cc1{{.*}} "-ftime-trace=e/a.json"
// COMPRESS-OVERRIDE-NONE-NOT: "-ftime-trace-compress
// RUN: %clang -### -c -ftime-trace=foo.json.zst -ftime-trace-compress=none -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-NONE
-// COMPRESS-NONE: -cc1{{.*}} "-ftime-trace=foo.json.zst"
-// COMPRESS-NONE-NOT: "-ftime-trace-compress
+// COMPRESS-NONE: -cc1{{.*}} "-ftime-trace=foo.json.zst" "-ftime-trace-compress=none"
// RUN: not %clang -### -c -ftime-trace -ftime-trace-compress=invalid d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-INVALID
// COMPRESS-INVALID: error: unsupported argument 'invalid' to option '-ftime-trace-compress='
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 1c3fef0630ea45..5ed0c372717e1d 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -251,10 +251,14 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
std::move(PCHOps));
if (!Clang->getFrontendOpts().TimeTracePath.empty()) {
+ llvm::TimeTraceCompression Compress =
+ Clang->getFrontendOpts().TimeTraceCompress;
+ if (Compress == llvm::TimeTraceCompression::Infer)
+ Compress = llvm::inferTimeTraceCompressionFromPath(
+ Clang->getFrontendOpts().TimeTracePath);
llvm::timeTraceProfilerInitialize(
Clang->getFrontendOpts().TimeTraceGranularity, Argv0,
- Clang->getFrontendOpts().TimeTraceVerbose,
- Clang->getFrontendOpts().TimeTraceCompress);
+ Clang->getFrontendOpts().TimeTraceVerbose, Compress);
}
// --print-supported-cpus takes priority over the actual compilation.
if (Clang->getFrontendOpts().PrintSupportedCPUs)
@@ -313,8 +317,12 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
}
if (llvm::timeTraceProfilerEnabled()) {
- bool Binary = Clang->getFrontendOpts().TimeTraceCompress !=
- llvm::DebugCompressionType::None;
+ llvm::TimeTraceCompression Compress =
+ Clang->getFrontendOpts().TimeTraceCompress;
+ if (Compress == llvm::TimeTraceCompression::Infer)
+ Compress = llvm::inferTimeTraceCompressionFromPath(
+ Clang->getFrontendOpts().TimeTracePath);
+ bool Binary = Compress == llvm::TimeTraceCompression::Zstd;
if (auto profilerOutput = Clang->createOutputFile(
Clang->getFrontendOpts().TimeTracePath, Binary,
/*RemoveFileOnSignal=*/false,
diff --git a/llvm/include/llvm/Support/TimeProfiler.h b/llvm/include/llvm/Support/TimeProfiler.h
index 014379de62f0d2..66b543a63e86b2 100644
--- a/llvm/include/llvm/Support/TimeProfiler.h
+++ b/llvm/include/llvm/Support/TimeProfiler.h
@@ -78,7 +78,6 @@
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/Support/Compiler.h"
-#include "llvm/Support/Compression.h"
#include "llvm/Support/Error.h"
namespace llvm {
@@ -98,6 +97,13 @@ enum class TimeTraceEventType {
AsyncEvent
};
+/// Compression mode for time trace output.
+enum class TimeTraceCompression {
+ Infer,
+ None,
+ Zstd,
+};
+
struct TimeTraceMetadata {
std::string Detail;
// Source file and line number information for the event.
@@ -116,7 +122,7 @@ struct TimeTraceProfilerEntry;
/// Infer the time trace compression format from \p Path based on its file
/// extension (.zst/.zstd -> Zstd, otherwise None).
-LLVM_ABI DebugCompressionType inferTimeTraceCompressionFromPath(StringRef Path);
+LLVM_ABI TimeTraceCompression inferTimeTraceCompressionFromPath(StringRef Path);
/// Initialize the time trace profiler.
/// This sets up the global \p TimeTraceProfilerInstance
@@ -124,7 +130,7 @@ LLVM_ABI DebugCompressionType inferTimeTraceCompressionFromPath(StringRef Path);
LLVM_ABI void timeTraceProfilerInitialize(
unsigned TimeTraceGranularity, StringRef ProcName,
bool TimeTraceVerbose = false,
- DebugCompressionType TimeTraceCompress = DebugCompressionType::None);
+ TimeTraceCompression TimeTraceCompress = TimeTraceCompression::Infer);
/// Cleanup the time trace profiler, if it was initialized.
LLVM_ABI void timeTraceProfilerCleanup();
diff --git a/llvm/lib/Support/TimeProfiler.cpp b/llvm/lib/Support/TimeProfiler.cpp
index 5edcc6212c594a..05df72e30ad1a3 100644
--- a/llvm/lib/Support/TimeProfiler.cpp
+++ b/llvm/lib/Support/TimeProfiler.cpp
@@ -15,6 +15,7 @@
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Compression.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
@@ -130,7 +131,7 @@ struct llvm::TimeTraceProfiler {
TimeTraceProfiler(
unsigned TimeTraceGranularity = 0, StringRef ProcName = "",
bool TimeTraceVerbose = false,
- DebugCompressionType TimeTraceCompress = DebugCompressionType::None)
+ TimeTraceCompression TimeTraceCompress = TimeTraceCompression::Infer)
: BeginningOfTime(system_clock::now()), StartTime(ClockType::now()),
ProcName(ProcName), Pid(sys::Process::getProcessId()),
Tid(llvm::get_threadid()), TimeTraceGranularity(TimeTraceGranularity),
@@ -387,8 +388,8 @@ struct llvm::TimeTraceProfiler {
// can increase the size of the output by 2-3 times.
const bool TimeTraceVerbose;
- // Optional compression format for output.
- DebugCompressionType TimeTraceCompress;
+ // Compression mode for output.
+ TimeTraceCompression TimeTraceCompress;
};
bool llvm::isTimeTraceVerbose() {
@@ -396,17 +397,17 @@ bool llvm::isTimeTraceVerbose() {
getTimeTraceProfilerInstance()->TimeTraceVerbose;
}
-DebugCompressionType llvm::inferTimeTraceCompressionFromPath(StringRef Path) {
+TimeTraceCompression llvm::inferTimeTraceCompressionFromPath(StringRef Path) {
StringRef Ext = llvm::sys::path::extension(Path);
if (Ext.equals_insensitive(".zst") || Ext.equals_insensitive(".zstd"))
- return DebugCompressionType::Zstd;
- return DebugCompressionType::None;
+ return TimeTraceCompression::Zstd;
+ return TimeTraceCompression::None;
}
void llvm::timeTraceProfilerInitialize(unsigned TimeTraceGranularity,
StringRef ProcName,
bool TimeTraceVerbose,
- DebugCompressionType TimeTraceCompress) {
+ TimeTraceCompression TimeTraceCompress) {
assert(TimeTraceProfilerInstance == nullptr &&
"Profiler should not be initialized");
TimeTraceProfilerInstance = new TimeTraceProfiler(
@@ -439,14 +440,14 @@ void llvm::timeTraceProfilerFinishThread() {
void llvm::timeTraceProfilerWrite(raw_pwrite_stream &OS) {
assert(TimeTraceProfilerInstance != nullptr &&
"Profiler object can't be null");
- DebugCompressionType CompressType =
+ TimeTraceCompression CompressType =
TimeTraceProfilerInstance->TimeTraceCompress;
- if (CompressType == DebugCompressionType::None) {
+ if (CompressType != TimeTraceCompression::Zstd) {
TimeTraceProfilerInstance->write(OS);
return;
}
- compression::Format F = compression::formatFor(CompressType);
+ compression::Format F = compression::Format::Zstd;
if (const char *Reason = compression::getReasonIfUnsupported(F))
report_fatal_error(Reason);
@@ -470,33 +471,33 @@ Error llvm::timeTraceProfilerWrite(StringRef PreferredFileName,
assert(TimeTraceProfilerInstance != nullptr &&
"Profiler object can't be null");
- DebugCompressionType CompressType =
+ TimeTraceCompression CompressType =
TimeTraceProfilerInstance->TimeTraceCompress;
std::string Path = PreferredFileName.str();
if (Path.empty()) {
Path = FallbackFileName == "-" ? "out" : FallbackFileName.str();
Path += TimeTraceFileExtension;
- if (CompressType == DebugCompressionType::Zstd)
+ if (CompressType == TimeTraceCompression::Zstd)
Path += ".zst";
- } else if (CompressType == DebugCompressionType::None) {
+ } else if (CompressType == TimeTraceCompression::Infer) {
CompressType = inferTimeTraceCompressionFromPath(Path);
}
- if (CompressType != DebugCompressionType::None) {
- if (const char *Reason = compression::getReasonIfUnsupported(
- compression::formatFor(CompressType)))
+ if (CompressType == TimeTraceCompression::Zstd) {
+ if (const char *Reason =
+ compression::getReasonIfUnsupported(compression::Format::Zstd))
return createStringError(inconvertibleErrorCode(), Reason);
}
std::error_code EC;
- sys::fs::OpenFlags Flags = CompressType == DebugCompressionType::None
- ? sys::fs::OF_TextWithCRLF
- : sys::fs::OF_None;
+ sys::fs::OpenFlags Flags = CompressType == TimeTraceCompression::Zstd
+ ? sys::fs::OF_None
+ : sys::fs::OF_TextWithCRLF;
raw_fd_ostream OS(Path, EC, Flags);
if (EC)
return createStringError(EC, "Could not open " + Path);
- DebugCompressionType SavedCompress =
+ TimeTraceCompression SavedCompress =
TimeTraceProfilerInstance->TimeTraceCompress;
TimeTraceProfilerInstance->TimeTraceCompress = CompressType;
timeTraceProfilerWrite(OS);
diff --git a/llvm/unittests/Support/TimeProfilerTest.cpp b/llvm/unittests/Support/TimeProfilerTest.cpp
index 38d25b35991aab..d10b4bce2c5fca 100644
--- a/llvm/unittests/Support/TimeProfilerTest.cpp
+++ b/llvm/unittests/Support/TimeProfilerTest.cpp
@@ -15,6 +15,10 @@
//===----------------------------------------------------------------------===//
#include "llvm/Support/TimeProfiler.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/Support/Compression.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
#include "gtest/gtest.h"
using namespace llvm;
@@ -45,18 +49,18 @@ TEST(TimeProfiler, Scope_Smoke) {
TEST(TimeProfiler, Compression) {
EXPECT_EQ(inferTimeTraceCompressionFromPath("foo.json"),
- DebugCompressionType::None);
+ TimeTraceCompression::None);
EXPECT_EQ(inferTimeTraceCompressionFromPath("foo.json.zst"),
- DebugCompressionType::Zstd);
+ TimeTraceCompression::Zstd);
EXPECT_EQ(inferTimeTraceCompressionFromPath("foo.ZSTD"),
- DebugCompressionType::Zstd);
+ TimeTraceCompression::Zstd);
if (!compression::zstd::isAvailable())
return;
timeTraceProfilerInitialize(/*TimeTraceGranularity=*/0, "test",
/*TimeTraceVerbose=*/false,
- DebugCompressionType::Zstd);
+ TimeTraceCompression::Zstd);
{
TimeTraceScope Scope("compressed_event", "compressed_detail");
}
@@ -69,6 +73,36 @@ TEST(TimeProfiler, Compression) {
ASSERT_FALSE(CompressedChars.empty());
// Compressed output must not start with '{'.
EXPECT_NE(CompressedChars.front(), '{');
+
+ SmallString<128> TempPath;
+ sys::fs::createUniquePath("time-trace-%%%%%%%.json.zst", TempPath, true);
+ llvm::scope_exit CleanupFile([&]() { sys::fs::remove(TempPath); });
+
+ // Default (TimeTraceCompression::Infer) infers Zstd from the .zst extension.
+ timeTraceProfilerInitialize(/*TimeTraceGranularity=*/0, "test");
+ {
+ TimeTraceScope Scope("inferred_event", "inferred_detail");
+ }
+ ASSERT_FALSE(errorToBool(timeTraceProfilerWrite(TempPath, "fallback")));
+ timeTraceProfilerCleanup();
+ auto BufOrErr = MemoryBuffer::getFile(TempPath);
+ ASSERT_TRUE(static_cast<bool>(BufOrErr));
+ ASSERT_FALSE((*BufOrErr)->getBuffer().empty());
+ EXPECT_NE((*BufOrErr)->getBuffer().front(), '{');
+
+ // Explicit TimeTraceCompression::None overrides the .zst extension.
+ timeTraceProfilerInitialize(/*TimeTraceGranularity=*/0, "test",
+ /*TimeTraceVerbose=*/false,
+ TimeTraceCompression::None);
+ {
+ TimeTraceScope Scope("uncompressed_event", "uncompressed_detail");
+ }
+ ASSERT_FALSE(errorToBool(timeTraceProfilerWrite(TempPath, "fallback")));
+ timeTraceProfilerCleanup();
+ BufOrErr = MemoryBuffer::getFile(TempPath);
+ ASSERT_TRUE(static_cast<bool>(BufOrErr));
+ ASSERT_FALSE((*BufOrErr)->getBuffer().empty());
+ EXPECT_EQ((*BufOrErr)->getBuffer().front(), '{');
}
TEST(TimeProfiler, Begin_End_Smoke) {
>From 63f49e758768cfefbc452dbf749f952a1b75ad36 Mon Sep 17 00:00:00 2001
From: Chandler Carruth <chandlerc at gmail.com>
Date: Sun, 18 Oct 2026 09:27:54 +0000
Subject: [PATCH 4/4] [Clang] Forward -ftime-trace-compress= directly to cc1
Leave option validation, path-based compression inference, and zstd
availability checking to cc1 rather than duplicating them in the driver.
Assisted-by: Antigravity with Gemini
---
clang/lib/Driver/ToolChains/Clang.cpp | 29 +--------------------------
clang/test/Driver/compress-zstd.c | 5 -----
clang/test/Driver/ftime-trace.cpp | 14 ++++++++-----
3 files changed, 10 insertions(+), 38 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 1858c3b35f2747..4ff6128b4a4c61 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -46,13 +46,11 @@
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Compiler.h"
-#include "llvm/Support/Compression.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
-#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/TargetParser/AArch64TargetParser.h"
#include "llvm/TargetParser/ARMTargetParserCommon.h"
@@ -7346,32 +7344,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(Args.MakeArgString("-ftime-trace=" + Twine(Name)));
Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_granularity_EQ);
Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_verbose);
- llvm::TimeTraceCompression Compress = llvm::TimeTraceCompression::Infer;
- if (const Arg *CompressArg =
- Args.getLastArg(options::OPT_ftime_trace_compress_EQ)) {
- StringRef Value = CompressArg->getValue();
- if (Value == "none")
- Compress = llvm::TimeTraceCompression::None;
- else if (Value == "zstd")
- Compress = llvm::TimeTraceCompression::Zstd;
- else if (Value == "infer")
- Compress = llvm::TimeTraceCompression::Infer;
- else
- D.Diag(diag::err_drv_unsupported_option_argument)
- << CompressArg->getSpelling() << Value;
- }
- if (Compress == llvm::TimeTraceCompression::Infer)
- Compress = llvm::inferTimeTraceCompressionFromPath(Name);
- if (Compress == llvm::TimeTraceCompression::Zstd) {
- if (llvm::compression::zstd::isAvailable())
- CmdArgs.push_back("-ftime-trace-compress=zstd");
- else
- D.Diag(diag::err_drv_time_trace_compression_unavailable) << "zstd";
- } else if (Compress == llvm::TimeTraceCompression::None &&
- llvm::inferTimeTraceCompressionFromPath(Name) !=
- llvm::TimeTraceCompression::None) {
- CmdArgs.push_back("-ftime-trace-compress=none");
- }
+ Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_compress_EQ);
}
if (Arg *A = Args.getLastArg(options::OPT_ftrapv_handler_EQ)) {
diff --git a/clang/test/Driver/compress-zstd.c b/clang/test/Driver/compress-zstd.c
index 22f9ca53e3be8d..84b26354589e95 100644
--- a/clang/test/Driver/compress-zstd.c
+++ b/clang/test/Driver/compress-zstd.c
@@ -5,8 +5,3 @@
// CHECK: {{"-cc1(as)?".* "--compress-debug-sections=zstd"}}
// CHECK: "--compress-debug-sections=zstd"
-
-// RUN: %clang -### -c -ftime-trace -ftime-trace-compress=zstd %s -o a.o 2>&1 | FileCheck %s --check-prefix=TIME-TRACE-ZSTD
-// RUN: %clang -### -c -ftime-trace=a.json.zst %s -o a.o 2>&1 | FileCheck %s --check-prefix=TIME-TRACE-ZSTD
-// RUN: %clang -### -c -ftime-trace=a.json.zst -ftime-trace-compress=none -ftime-trace-compress=infer %s -o a.o 2>&1 | FileCheck %s --check-prefix=TIME-TRACE-ZSTD
-// TIME-TRACE-ZSTD: "-cc1"{{.*}} "-ftime-trace=a.json.zst" "-ftime-trace-compress=zstd"
diff --git a/clang/test/Driver/ftime-trace.cpp b/clang/test/Driver/ftime-trace.cpp
index 032cbab5e40257..4170e00fd408ef 100644
--- a/clang/test/Driver/ftime-trace.cpp
+++ b/clang/test/Driver/ftime-trace.cpp
@@ -64,16 +64,20 @@
// UNUSED-NEXT: warning: argument unused during compilation: '-ftime-trace-compress=none'
// UNUSED-NOT: warning:
+// RUN: %clang -### -c -ftime-trace -ftime-trace-compress=zstd -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-ZSTD
+// COMPRESS-ZSTD: -cc1{{.*}} "-ftime-trace=e/a.json.zst" "-ftime-trace-compress=zstd"
+
// RUN: %clang -### -c -ftime-trace -ftime-trace-compress=zstd -ftime-trace-compress=none -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-OVERRIDE-NONE
-// RUN: %clang -### -c -ftime-trace -ftime-trace-compress=zstd -ftime-trace-compress=infer -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-OVERRIDE-NONE
-// COMPRESS-OVERRIDE-NONE: -cc1{{.*}} "-ftime-trace=e/a.json"
-// COMPRESS-OVERRIDE-NONE-NOT: "-ftime-trace-compress
+// COMPRESS-OVERRIDE-NONE: -cc1{{.*}} "-ftime-trace=e/a.json" "-ftime-trace-compress=none"
+
+// RUN: %clang -### -c -ftime-trace -ftime-trace-compress=zstd -ftime-trace-compress=infer -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-OVERRIDE-INFER
+// COMPRESS-OVERRIDE-INFER: -cc1{{.*}} "-ftime-trace=e/a.json" "-ftime-trace-compress=infer"
// RUN: %clang -### -c -ftime-trace=foo.json.zst -ftime-trace-compress=none -fintegrated-as d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-NONE
// COMPRESS-NONE: -cc1{{.*}} "-ftime-trace=foo.json.zst" "-ftime-trace-compress=none"
-// RUN: not %clang -### -c -ftime-trace -ftime-trace-compress=invalid d/a.cpp -o e/a.o 2>&1 | FileCheck %s --check-prefix=COMPRESS-INVALID
-// COMPRESS-INVALID: error: unsupported argument 'invalid' to option '-ftime-trace-compress='
+// RUN: not %clangxx -S -no-canonical-prefixes -ftime-trace -ftime-trace-compress=invalid -o out %s 2>&1 | FileCheck %s --check-prefix=COMPRESS-INVALID
+// COMPRESS-INVALID: error: invalid value 'invalid' in '-ftime-trace-compress=invalid'
template <typename T>
struct Struct {
More information about the cfe-commits
mailing list