[clang] 33632ff - [TimeProfiler][Clang] Add zstd-compressed time-trace output support (#228627)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 18 03:46:20 PDT 2026
Author: Chandler Carruth
Date: 2026-10-18T10:46:14Z
New Revision: 33632ffa14254f6acea12afc368e99e49ab695e2
URL: https://github.com/llvm/llvm-project/commit/33632ffa14254f6acea12afc368e99e49ab695e2
DIFF: https://github.com/llvm/llvm-project/commit/33632ffa14254f6acea12afc368e99e49ab695e2.diff
LOG: [TimeProfiler][Clang] Add zstd-compressed time-trace output support (#228627)
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:
- Introduce llvm::TimeTraceCompression ({Infer, None, Zstd}) in
TimeProfiler.h and extend llvm::timeTraceProfilerInitialize and
llvm::timeTraceProfilerWrite to infer zstd compression from .zst/.zstd
file extensions via llvm::inferTimeTraceCompressionFromPath when set to
Infer.
- Add -ftime-trace-compress=<none|zstd|infer> driver and CC1 option to
explicitly control compression or infer it from the 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
Added:
Modified:
clang/include/clang/Basic/DiagnosticDriverKinds.td
clang/include/clang/Frontend/FrontendOptions.h
clang/include/clang/Options/Options.td
clang/lib/Driver/Driver.cpp
clang/lib/Driver/ToolChains/Clang.cpp
clang/lib/Frontend/CompilerInvocation.cpp
clang/test/Driver/ftime-trace.cpp
clang/tools/driver/cc1_main.cpp
llvm/include/llvm/Support/TimeProfiler.h
llvm/lib/Support/TimeProfiler.cpp
llvm/unittests/Support/TimeProfilerTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index b5b1ac744b49de..79859e724a8179 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -563,6 +563,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 7e2b0936525662..087a96a13af3f0 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -17,6 +17,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/TimeProfiler.h"
#include <cassert>
#include <map>
#include <memory>
@@ -549,6 +550,10 @@ class FrontendOptions {
LLVM_PREFERRED_TYPE(bool)
unsigned TimeTraceVerbose : 1;
+ /// 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 41afddd0ffe82e..38d285b8268e15 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -4848,6 +4848,13 @@ 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,infer">,
+ NormalizedValuesScope<"llvm::TimeTraceCompression">,
+ NormalizedValues<["None", "Zstd", "Infer"]>,
+ 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/Driver.cpp b/clang/lib/Driver/Driver.cpp
index b2dbc32f1b89fd..b9c9fee18e9c3e 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -5181,6 +5181,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();
@@ -5189,7 +5199,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 {
@@ -5199,7 +5209,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.
@@ -5208,10 +5218,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 45aede75681103..608d7bb68a4e6d 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7390,6 +7390,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);
+ Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_compress_EQ);
}
if (Arg *A = Args.getLastArg(options::OPT_ftrapv_handler_EQ)) {
@@ -9812,6 +9813,7 @@ 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_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 c9281c2a6d6b41..225962359129b3 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -5246,6 +5246,16 @@ bool CompilerInvocation::CreateFromArgsImpl(
llvm::driver::ProfileInstrKind::ProfileNone)
Diags.Report(diag::err_drv_profile_instrument_use_path_with_no_kind);
+ 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";
+
FixupInvocation(Res, Diags, Args, DashX);
return Diags.getNumErrors() == NumErrorsBefore;
diff --git a/clang/test/Driver/ftime-trace.cpp b/clang/test/Driver/ftime-trace.cpp
index 60c5885704b58e..4170e00fd408ef 100644
--- a/clang/test/Driver/ftime-trace.cpp
+++ b/clang/test/Driver/ftime-trace.cpp
@@ -55,14 +55,30 @@
// 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=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=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
+// 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 %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 {
T Num;
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 89b0a340e6672e..5ed0c372717e1d 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -251,9 +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().TimeTraceVerbose, Compress);
}
// --print-supported-cpus takes priority over the actual compilation.
if (Clang->getFrontendOpts().PrintSupportedCPUs)
@@ -312,8 +317,14 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
}
if (llvm::timeTraceProfilerEnabled()) {
+ 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=*/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 e80ace27a23ca8..3119784a1e1afd 100644
--- a/llvm/include/llvm/Support/TimeProfiler.h
+++ b/llvm/include/llvm/Support/TimeProfiler.h
@@ -97,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.
@@ -113,12 +120,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 TimeTraceCompression 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,
+ 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 002529f20d661b..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"
@@ -127,12 +128,15 @@ struct InProgressEntry {
};
struct llvm::TimeTraceProfiler {
- TimeTraceProfiler(unsigned TimeTraceGranularity = 0, StringRef ProcName = "",
- bool TimeTraceVerbose = false)
+ TimeTraceProfiler(
+ unsigned TimeTraceGranularity = 0, StringRef ProcName = "",
+ bool TimeTraceVerbose = false,
+ TimeTraceCompression TimeTraceCompress = TimeTraceCompression::Infer)
: 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 +216,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 +387,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;
+
+ // Compression mode for output.
+ TimeTraceCompression TimeTraceCompress;
};
bool llvm::isTimeTraceVerbose() {
@@ -390,14 +397,22 @@ bool llvm::isTimeTraceVerbose() {
getTimeTraceProfilerInstance()->TimeTraceVerbose;
}
+TimeTraceCompression llvm::inferTimeTraceCompressionFromPath(StringRef Path) {
+ StringRef Ext = llvm::sys::path::extension(Path);
+ if (Ext.equals_insensitive(".zst") || Ext.equals_insensitive(".zstd"))
+ return TimeTraceCompression::Zstd;
+ return TimeTraceCompression::None;
+}
+
void llvm::timeTraceProfilerInitialize(unsigned TimeTraceGranularity,
StringRef ProcName,
- bool TimeTraceVerbose) {
+ bool TimeTraceVerbose,
+ TimeTraceCompression 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 +440,30 @@ void llvm::timeTraceProfilerFinishThread() {
void llvm::timeTraceProfilerWrite(raw_pwrite_stream &OS) {
assert(TimeTraceProfilerInstance != nullptr &&
"Profiler object can't be null");
- TimeTraceProfilerInstance->write(OS);
+ TimeTraceCompression CompressType =
+ TimeTraceProfilerInstance->TimeTraceCompress;
+ if (CompressType != TimeTraceCompression::Zstd) {
+ TimeTraceProfilerInstance->write(OS);
+ return;
+ }
+
+ compression::Format F = compression::Format::Zstd;
+ 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,
@@ -433,18 +471,37 @@ Error llvm::timeTraceProfilerWrite(StringRef PreferredFileName,
assert(TimeTraceProfilerInstance != nullptr &&
"Profiler object can't be null");
+ TimeTraceCompression CompressType =
+ TimeTraceProfilerInstance->TimeTraceCompress;
std::string Path = PreferredFileName.str();
if (Path.empty()) {
Path = FallbackFileName == "-" ? "out" : FallbackFileName.str();
Path += TimeTraceFileExtension;
+ if (CompressType == TimeTraceCompression::Zstd)
+ Path += ".zst";
+ } else if (CompressType == TimeTraceCompression::Infer) {
+ CompressType = inferTimeTraceCompressionFromPath(Path);
+ }
+
+ if (CompressType == TimeTraceCompression::Zstd) {
+ if (const char *Reason =
+ compression::getReasonIfUnsupported(compression::Format::Zstd))
+ return createStringError(inconvertibleErrorCode(), Reason);
}
std::error_code EC;
- raw_fd_ostream OS(Path, EC, sys::fs::OF_TextWithCRLF);
+ 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);
+ TimeTraceCompression 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..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;
@@ -43,6 +47,64 @@ TEST(TimeProfiler, Scope_Smoke) {
ASSERT_TRUE(json.find(R"("detail":"detail")") != std::string::npos);
}
+TEST(TimeProfiler, Compression) {
+ EXPECT_EQ(inferTimeTraceCompressionFromPath("foo.json"),
+ TimeTraceCompression::None);
+ EXPECT_EQ(inferTimeTraceCompressionFromPath("foo.json.zst"),
+ TimeTraceCompression::Zstd);
+ EXPECT_EQ(inferTimeTraceCompressionFromPath("foo.ZSTD"),
+ TimeTraceCompression::Zstd);
+
+ if (!compression::zstd::isAvailable())
+ return;
+
+ timeTraceProfilerInitialize(/*TimeTraceGranularity=*/0, "test",
+ /*TimeTraceVerbose=*/false,
+ TimeTraceCompression::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(), '{');
+
+ 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) {
setupProfiler();
More information about the cfe-commits
mailing list