https://github.com/qiongsiwu updated https://github.com/llvm/llvm-project/pull/211966
>From b1d0246f45ebfa2e9aea29530dd1acceb7fafed5 Mon Sep 17 00:00:00 2001 From: Qiongsi Wu <[email protected]> Date: Fri, 24 Jul 2026 16:13:14 -0700 Subject: [PATCH 1/5] Add a clang driver option to enable dependency scanning logging. --- clang/include/clang/Basic/AtomicLineLogger.h | 9 ++- .../DependencyScanningWorker.h | 2 + clang/include/clang/Options/Options.td | 7 ++ clang/lib/Basic/AtomicLineLogger.cpp | 76 ++++++++++++++----- clang/lib/Tooling/DependencyScanningTool.cpp | 20 +++-- .../test/ClangScanDeps/logging-driver-flag.c | 47 ++++++++++++ 6 files changed, 132 insertions(+), 29 deletions(-) create mode 100644 clang/test/ClangScanDeps/logging-driver-flag.c diff --git a/clang/include/clang/Basic/AtomicLineLogger.h b/clang/include/clang/Basic/AtomicLineLogger.h index 3d06ddfce0262..ddcd5993b060e 100644 --- a/clang/include/clang/Basic/AtomicLineLogger.h +++ b/clang/include/clang/Basic/AtomicLineLogger.h @@ -19,6 +19,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" #include <atomic> +#include <mutex> #include <optional> #include <string> @@ -60,9 +61,11 @@ class LogLine { }; class AtomicLineLogger { - int FD = -1; + std::atomic<int> FD{-1}; std::string LogPath; std::atomic<uint64_t> DroppedLines{0}; + std::mutex EnableMtx; + bool WarnedConflict = false; public: AtomicLineLogger() {} @@ -75,6 +78,10 @@ class AtomicLineLogger { ~AtomicLineLogger(); + // Enables the logger if it is not already enabled. Thread safe. + // If the logger is already enabled, call to enable is a no-op. + void enable(StringRef LogFilePath); + LogLine log(); }; diff --git a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h index 638c317d9ce64..2c5c7fbb8f9a0 100644 --- a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h @@ -90,6 +90,8 @@ class DependencyScanningWorker { return TracingFS.get(); } + DependencyScanningService &getService() const { return Service; } + // MaxNumOfByNameQueries is the upper limit of the number of names the by-name // scanning API (computeDependenciesByName) can drain per call. At the time of // this commit, the estimated number of total unique importable names is diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index cf66ee3e52f2d..f8651a7bc42a5 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -3781,6 +3781,13 @@ def fno_modules_driver : Group<f_Group>, Visibility<[ClangOption]>, HelpText<"Disable support for driver managed module builds (experimental)">; +def fdepscan_log_path : Joined<["-"], "fdepscan-log-path=">, + Group<f_Group>, + Visibility<[ClangOption]>, + Flags<[NoArgumentUnused]>, + MetaVarName<"<file>">, + HelpText<"Log the timing of dependency scanning actions to <file>. Only " + "takes effect while running the dependency scanner.">; def fincremental_extensions : Flag<["-"], "fincremental-extensions">, diff --git a/clang/lib/Basic/AtomicLineLogger.cpp b/clang/lib/Basic/AtomicLineLogger.cpp index dc0f2bf5adc6c..4bf26f0cb756d 100644 --- a/clang/lib/Basic/AtomicLineLogger.cpp +++ b/clang/lib/Basic/AtomicLineLogger.cpp @@ -41,20 +41,43 @@ static uint64_t getTimestampMillis() { #endif } +#ifdef _WIN32 +// Write to files opened with OF_Append may not be guaranteed to be atomic +// on Windows. Both openLogFile and writeLineToFD are noops on Windows. +static int openLogFile(StringRef Path) { + (void)Path; + return -1; +} + +static bool writeLineToFD(int FD, const char *Data, size_t Size) { + (void)FD, (void)Data, (void)Size; + return false; +} + +#else + +static int openLogFile(StringRef Path) { + int FD = -1; + std::error_code EC = llvm::sys::fs::openFileForWrite( + Path, FD, llvm::sys::fs::CD_OpenAlways, llvm::sys::fs::OF_Append); + if (EC) { + llvm::errs() << "warning: unable to open log file '" << Path + << "': " << EC.message() << "\n"; + return -1; + } + return FD; +} + // Writes the whole line into an FD that is opened with OF_Append. // This function only does one write (up to retry due to interrupts), and the // single write is blocking and atomic on POSIX systems. static bool writeLineToFD(int FD, const char *Data, size_t Size) { -#ifndef _WIN32 ssize_t Written = llvm::sys::RetryAfterSignal(-1, write, FD, Data, Size); return Written >= 0 && (static_cast<size_t>(Written) == Size); -#else - (void)FD, (void)Data, (void)Size; - llvm_unreachable("Logging not supported on Windows!"); - return false; -#endif } +#endif + LogLine::LogLine(int FD, std::atomic<uint64_t> *DroppedLines) : FormattingOS(Buffer), FD(FD), DroppedLines(DroppedLines) { auto Millis = getTimestampMillis(); @@ -83,33 +106,44 @@ LogLine::~LogLine() { DroppedLines->fetch_add(1, std::memory_order_relaxed); } -AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath) - : LogPath(LogFilePath.str()) { -#ifndef _WIN32 +AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath) { if (LogFilePath.empty()) return; + LogPath = LogFilePath.str(); + FD.store(openLogFile(LogFilePath), std::memory_order_release); +} - std::error_code EC = llvm::sys::fs::openFileForWrite( - LogFilePath, FD, llvm::sys::fs::CD_OpenAlways, llvm::sys::fs::OF_Append); - if (EC) { - llvm::errs() << "warning: unable to open log file '" << LogFilePath - << "': " << EC.message() << "\n"; - FD = -1; +void AtomicLineLogger::enable(StringRef LogFilePath) { + if (LogFilePath.empty()) + return; + std::lock_guard<std::mutex> Lock(EnableMtx); + if (FD.load(std::memory_order_relaxed) != -1) { + if (LogFilePath != LogPath && !WarnedConflict) { + llvm::errs() << "warning: dependency scanning log path '" << LogFilePath + << "' ignored; already logging to '" << LogPath << "'\n"; + WarnedConflict = true; + } return; } -#endif - // Write to files opened with OF_Append may not be guaranteed to be atomic - // on Windows, so we do not enable logging on Windows. + + int NewFD = openLogFile(LogFilePath); + if (NewFD == -1) + return; + LogPath = LogFilePath.str(); + FD.store(NewFD, std::memory_order_relaxed); + return; } LogLine AtomicLineLogger::log() { - if (FD != -1) - return LogLine(FD, &DroppedLines); + int CurFD = FD.load(std::memory_order_relaxed); + if (CurFD != -1) + return LogLine(CurFD, &DroppedLines); return LogLine(); } AtomicLineLogger::~AtomicLineLogger() { - if (FD == -1) + int CurFD = FD.load(std::memory_order_relaxed); + if (CurFD == -1) return; if (uint64_t Dropped = DroppedLines.load(std::memory_order_relaxed)) llvm::errs() << "warning: log '" << LogPath diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index b3332d9d49cd8..d0e01bd788ce2 100644 --- a/clang/lib/Tooling/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanningTool.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/DependencyScanningTool.h" +#include "clang/Basic/AtomicLineLogger.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticFrontend.h" #include "clang/DependencyScanning/DependencyScanningWorker.h" @@ -15,6 +16,7 @@ #include "clang/Driver/Tool.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/Utils.h" +#include "clang/Options/Options.h" #include "llvm/ADT/SmallVectorExtras.h" #include "llvm/ADT/iterator.h" #include "llvm/TargetParser/Host.h" @@ -90,7 +92,7 @@ static std::pair<std::unique_ptr<driver::Driver>, std::unique_ptr<driver::Compilation>> buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - llvm::BumpPtrAllocator &Alloc) { + llvm::BumpPtrAllocator &Alloc, AtomicLineLogger &Logger) { SmallVector<const char *, 256> Argv; Argv.reserve(ArgStrs.size()); for (const std::string &Arg : ArgStrs) @@ -125,6 +127,9 @@ buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, return std::make_pair(nullptr, nullptr); } + Logger.enable( + Compilation->getArgs().getLastArgValue(options::OPT_fdepscan_log_path)); + return std::make_pair(std::move(Driver), std::move(Compilation)); } @@ -155,8 +160,8 @@ static bool computeDependenciesForDriverCommandLine( auto DiagEngine = CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DiagConsumer, /*ShouldOwnClient=*/false); - const auto [Driver, Compilation] = - buildCompilation(CommandLine, *DiagEngine, FS, Alloc); + const auto [Driver, Compilation] = buildCompilation( + CommandLine, *DiagEngine, FS, Alloc, Worker.getService().getLogger()); if (!Compilation) return false; @@ -326,13 +331,14 @@ DependencyScanningTool::getTranslationUnitDependencies( static std::optional<SmallVector<std::string, 0>> getFirstCC1CommandLine(ArrayRef<std::string> CommandLine, DiagnosticsEngine &Diags, - llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) { + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, + AtomicLineLogger &Logger) { // Compilation holds a non-owning a reference to the Driver, hence we need to // keep the Driver alive when we use Compilation. Arguments to commands may be // owned by Alloc when expanded from response files. llvm::BumpPtrAllocator Alloc; const auto [Driver, Compilation] = - buildCompilation(CommandLine, Diags, std::move(FS), Alloc); + buildCompilation(CommandLine, Diags, std::move(FS), Alloc, Logger); if (!Compilation) return std::nullopt; @@ -362,8 +368,8 @@ bool DependencyScanningTool::getByNameDependencies( auto DiagEngine = CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DiagConsumer, /*ShouldOwnClient=*/false); - auto MaybeFirstCC1 = - getFirstCC1CommandLine(ModifiedCommandLine, *DiagEngine, FS); + auto MaybeFirstCC1 = getFirstCC1CommandLine( + ModifiedCommandLine, *DiagEngine, FS, Worker.getService().getLogger()); if (!MaybeFirstCC1) return false; CC1CommandLine.assign(MaybeFirstCC1->begin(), MaybeFirstCC1->end()); diff --git a/clang/test/ClangScanDeps/logging-driver-flag.c b/clang/test/ClangScanDeps/logging-driver-flag.c new file mode 100644 index 0000000000000..b2d99a98e5f56 --- /dev/null +++ b/clang/test/ClangScanDeps/logging-driver-flag.c @@ -0,0 +1,47 @@ +// UNSUPPORTED: system-windows +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: sed -e "s|DIR|%/t|g" %t/cdb-by-name.json.template > %t/cdb-by-name.json + +// Translation-unit scanning: logging enabled via the driver command line in +// computeDependenciesForDriverCommandLine -> buildCompilation. +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -format experimental-full -j 1 -o %t/deps.json +// RUN: FileCheck %s --check-prefix=TU --input-file %t/tu.log + +// TU: [{{[0-9]+\.[0-9]+}}] [[#PID:]] [[#TID:]]: starting scanning command:{{.*}}tu.c +// TU: [{{[0-9]+\.[0-9]+}}] {{.*}}: pcm_write: {{.*}}.pcm +// TU: [{{[0-9]+\.[0-9]+}}] {{.*}}: finished scanning command:{{.*}}tu.c + +// By-name scanning: logging enabled via getFirstCC1CommandLine -> +// buildCompilation. +// RUN: clang-scan-deps -compilation-database %t/cdb-by-name.json \ +// RUN: -format experimental-full -j 1 -module-names=A +// RUN: FileCheck %s --check-prefix=BY-NAME --input-file %t/by-name.log + +// BY-NAME: [{{[0-9]+\.[0-9]+}}] {{.*}}: start scan_by_name: A +// BY-NAME: [{{[0-9]+\.[0-9]+}}] {{.*}}: finish scan_by_name: A + +//--- cdb.json.template +[{ + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.c -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -fbuild-session-timestamp=1 +-fmodules-validate-once-per-build-session -fdepscan-log-path=DIR/tu.log", + "file": "DIR/tu.c" +}] + +//--- cdb-by-name.json.template +[{ + "directory": "DIR", + "command": "clang -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -I DIR -x c -fdepscan-log-path=DIR/by-name.log", + "file": "" +}] + +//--- module.modulemap +module A { header "A.h" } +//--- A.h +void A_func(void); +//--- tu.c +#include "A.h" +void foo(void) { A_func(); } >From d44bb662a5ea9f91b2b492220bf47623ab6bb371 Mon Sep 17 00:00:00 2001 From: Qiongsi Wu <[email protected]> Date: Tue, 11 Aug 2026 16:08:09 -0700 Subject: [PATCH 2/5] Adding logging_start and logging_end events. --- clang/include/clang/Basic/AtomicLineLogger.h | 2 + clang/lib/Basic/AtomicLineLogger.cpp | 13 ++-- .../ClangScanDeps/logging-simple-by-name.c | 4 +- clang/test/ClangScanDeps/logging-simple.c | 4 +- .../unittests/Basic/AtomicLineLoggerTest.cpp | 61 ++++++++++++++----- 5 files changed, 64 insertions(+), 20 deletions(-) diff --git a/clang/include/clang/Basic/AtomicLineLogger.h b/clang/include/clang/Basic/AtomicLineLogger.h index ddcd5993b060e..a2b1f9abdaa6d 100644 --- a/clang/include/clang/Basic/AtomicLineLogger.h +++ b/clang/include/clang/Basic/AtomicLineLogger.h @@ -67,6 +67,8 @@ class AtomicLineLogger { std::mutex EnableMtx; bool WarnedConflict = false; + void setFD(StringRef Path); + public: AtomicLineLogger() {} AtomicLineLogger(StringRef LogFilePath); diff --git a/clang/lib/Basic/AtomicLineLogger.cpp b/clang/lib/Basic/AtomicLineLogger.cpp index 4bf26f0cb756d..c32f3551dcc0e 100644 --- a/clang/lib/Basic/AtomicLineLogger.cpp +++ b/clang/lib/Basic/AtomicLineLogger.cpp @@ -106,11 +106,16 @@ LogLine::~LogLine() { DroppedLines->fetch_add(1, std::memory_order_relaxed); } +void AtomicLineLogger::setFD(StringRef Path) { + LogPath = Path.str(); + FD.store(openLogFile(Path), std::memory_order_release); + log() << "logging_start"; +} + AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath) { if (LogFilePath.empty()) return; - LogPath = LogFilePath.str(); - FD.store(openLogFile(LogFilePath), std::memory_order_release); + setFD(LogFilePath); } void AtomicLineLogger::enable(StringRef LogFilePath) { @@ -129,8 +134,7 @@ void AtomicLineLogger::enable(StringRef LogFilePath) { int NewFD = openLogFile(LogFilePath); if (NewFD == -1) return; - LogPath = LogFilePath.str(); - FD.store(NewFD, std::memory_order_relaxed); + setFD(LogFilePath); return; } @@ -145,6 +149,7 @@ AtomicLineLogger::~AtomicLineLogger() { int CurFD = FD.load(std::memory_order_relaxed); if (CurFD == -1) return; + log() << "logging_end"; if (uint64_t Dropped = DroppedLines.load(std::memory_order_relaxed)) llvm::errs() << "warning: log '" << LogPath << "' is incomplete: " << Dropped diff --git a/clang/test/ClangScanDeps/logging-simple-by-name.c b/clang/test/ClangScanDeps/logging-simple-by-name.c index 954d25acdd1f7..b02fad5117ea9 100644 --- a/clang/test/ClangScanDeps/logging-simple-by-name.c +++ b/clang/test/ClangScanDeps/logging-simple-by-name.c @@ -17,7 +17,8 @@ // RUN: -module-names=M,N -o %t/deps.json // RUN: FileCheck %s < %t/scan.log -// CHECK: [{{[0-9]+\.[0-9]+}}] [[#PID:]] [[#TID1:]]: init_compiler_instance_with_context:{{.*}} +// CHECK: [{{[0-9]+\.[0-9]+}}] [[#PID:]] [[#TID1:]]: logging_start +// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: init_compiler_instance_with_context:{{.*}} // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: start scan_by_name: M // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: timestamp_read: {{.*}}[[MPCMFILE:.*\.pcm]] // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: pcm_read_cached: {{.*}}[[MPCMFILE]] @@ -46,6 +47,7 @@ // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: timestamp_read: {{.*}}[[NPCMFILE]] // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: pcm_finalized: {{.*}}[[NPCMFILE]] // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: finish scan_by_name: N +// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: logging_end //--- cdb.json.template [{ diff --git a/clang/test/ClangScanDeps/logging-simple.c b/clang/test/ClangScanDeps/logging-simple.c index 0022fb97cbc73..444fdf1ab6e20 100644 --- a/clang/test/ClangScanDeps/logging-simple.c +++ b/clang/test/ClangScanDeps/logging-simple.c @@ -13,7 +13,8 @@ // build a single scanning pcm. We should only log these events, no more and no // less, strictly in this order. Changes to this list should be intentional. -// CHECK: [{{[0-9]+\.[0-9]+}}] [[#PID:]] [[#TID:]]: starting scanning command:{{.*}}tu.c +// CHECK: [{{[0-9]+\.[0-9]+}}] [[#PID:]] [[#TID:]]: logging_start +// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: starting scanning command:{{.*}}tu.c // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: init_compiler_instance_with_context:{{.*}} // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: timestamp_read: {{.*}}[[PCMFILE:.*\.pcm]] // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: pcm_read_cached: {{.*}}[[PCMFILE]] @@ -26,6 +27,7 @@ // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: pcm_read_cached: {{.*}}[[PCMFILE]] // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: pcm_finalized: {{.*}}[[PCMFILE]] // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: finished scanning command:{{.*}}tu.c +// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: logging_end //--- cdb.json.template [{ diff --git a/clang/unittests/Basic/AtomicLineLoggerTest.cpp b/clang/unittests/Basic/AtomicLineLoggerTest.cpp index b1dfb616d08c2..654ebf1149668 100644 --- a/clang/unittests/Basic/AtomicLineLoggerTest.cpp +++ b/clang/unittests/Basic/AtomicLineLoggerTest.cpp @@ -16,6 +16,21 @@ using namespace clang; +static StringRef logBody(StringRef Line) { return Line.split(": ").second; } + +static SmallVector<StringRef> logBodyLines(StringRef Content) { + SmallVector<StringRef> Lines; + Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + if (Lines.size() < 2) { + ADD_FAILURE() << "log not framed by logging_start/logging_end; got " + << Lines.size() << " line(s)"; + return {}; + } + EXPECT_EQ(logBody(Lines.front()), "logging_start"); + EXPECT_EQ(logBody(Lines.back()), "logging_end"); + return SmallVector<StringRef>(ArrayRef(Lines).drop_front().drop_back()); +} + TEST(AtomicLineLoggerTest, DisabledLoggerDoesNotCrash) { AtomicLineLogger Logger; Logger.log() << "this goes nowhere"; @@ -41,9 +56,10 @@ TEST(AtomicLineLoggerTest, LogLineMoveConstructor) { ASSERT_TRUE(BufOrErr) << "Failed to read log file"; StringRef Content = (*BufOrErr)->getBuffer(); - // Only one line should be written (from Moved, not from Original). - EXPECT_EQ(Content.count('\n'), 1u); - EXPECT_TRUE(Content.contains("after_move")); + // Only one log body line should be written (from Moved, not from Original). + auto Body = logBodyLines(Content); + ASSERT_EQ(Body.size(), 1u); + EXPECT_EQ(logBody(Body.front()), "after_move"); } TEST(AtomicLineLoggerTest, LogLinePIDTIDMsg) { @@ -60,17 +76,18 @@ TEST(AtomicLineLoggerTest, LogLinePIDTIDMsg) { ASSERT_TRUE(BufOrErr) << "Failed to read log file"; StringRef Content = (*BufOrErr)->getBuffer(); - // Ends with message + newline. - EXPECT_TRUE(Content.ends_with("test_event: some_file.pcm\n")); + auto Body = logBodyLines(Content); + ASSERT_EQ(Body.size(), 1u); + EXPECT_EQ(logBody(Body.front()), "test_event: some_file.pcm"); // Prefix has the form: "<timestamp> <pid> <tid>: " // Verify PID matches this process. std::string ExpectedPID = std::to_string(llvm::sys::Process::getProcessId()); - EXPECT_TRUE(Content.contains(ExpectedPID)); + EXPECT_TRUE(Body.front().contains(ExpectedPID)); // Verify TID is present. std::string ExpectedTID = std::to_string(llvm::get_threadid()); - EXPECT_TRUE(Content.contains(ExpectedTID)); + EXPECT_TRUE(Body.front().contains(ExpectedTID)); } TEST(AtomicLineLoggerTest, LogLineLogArray) { @@ -149,11 +166,9 @@ TEST(AtomicLineLoggerTest, SingleLineWrittenToFile) { StringRef Content = (*BufOrErr)->getBuffer(); // Verify the message is present and the line ends with a newline. - EXPECT_TRUE(Content.contains("pcm_write: module.pcm")); - EXPECT_TRUE(Content.ends_with("\n")); - - // Verify there is exactly one line. - EXPECT_EQ(Content.count('\n'), 1u); + auto Body = logBodyLines(Content); + ASSERT_EQ(Body.size(), 1u); + EXPECT_EQ(logBody(Body.front()), "pcm_write: module.pcm"); } TEST(AtomicLineLoggerTest, ConcurrentWritesProduceCompleteLines) { @@ -167,6 +182,8 @@ TEST(AtomicLineLoggerTest, ConcurrentWritesProduceCompleteLines) { constexpr unsigned NumThreads = 8; constexpr unsigned LinesPerThread = 100; constexpr unsigned MessageLen = 32; + // One LoggerEven and one LoggerOdd. + constexpr unsigned NumLoggers = 2; { // Creating two loggers based on the same file to make sure @@ -204,9 +221,25 @@ TEST(AtomicLineLoggerTest, ConcurrentWritesProduceCompleteLines) { SmallVector<StringRef> Lines; Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - EXPECT_EQ(Lines.size(), (size_t)(NumThreads * LinesPerThread)); + SmallVector<StringRef> MessageLines; + unsigned Starts = 0, Ends = 0; + for (StringRef Line : Lines) { + StringRef Body = logBody(Line); + if (Body == "logging_start") { + ++Starts; + continue; + } + if (Body == "logging_end") { + ++Ends; + continue; + } + MessageLines.push_back(Line); + } + EXPECT_EQ(Starts, NumLoggers); + EXPECT_EQ(Ends, NumLoggers); + EXPECT_EQ(MessageLines.size(), (size_t)(NumThreads * LinesPerThread)); - for (const auto &Line : Lines) { + for (const auto &Line : MessageLines) { // For each line, we check the separator, message length, message start and // the prefix format to make sure no lines are interleved. >From ddbbd74d750e7b27d2872a07779547e1df7fa2e7 Mon Sep 17 00:00:00 2001 From: Qiongsi Wu <[email protected]> Date: Wed, 19 Aug 2026 13:04:18 -0700 Subject: [PATCH 3/5] Address review comments. --- clang/include/clang/Basic/AtomicLineLogger.h | 2 +- clang/lib/Basic/AtomicLineLogger.cpp | 44 +++++++++---------- .../unittests/Basic/AtomicLineLoggerTest.cpp | 44 ++++++++++++++++++- 3 files changed, 65 insertions(+), 25 deletions(-) diff --git a/clang/include/clang/Basic/AtomicLineLogger.h b/clang/include/clang/Basic/AtomicLineLogger.h index a2b1f9abdaa6d..1e8593578054d 100644 --- a/clang/include/clang/Basic/AtomicLineLogger.h +++ b/clang/include/clang/Basic/AtomicLineLogger.h @@ -67,7 +67,7 @@ class AtomicLineLogger { std::mutex EnableMtx; bool WarnedConflict = false; - void setFD(StringRef Path); + void initialize(StringRef LogFilePath); public: AtomicLineLogger() {} diff --git a/clang/lib/Basic/AtomicLineLogger.cpp b/clang/lib/Basic/AtomicLineLogger.cpp index c32f3551dcc0e..08bc4ef9b397c 100644 --- a/clang/lib/Basic/AtomicLineLogger.cpp +++ b/clang/lib/Basic/AtomicLineLogger.cpp @@ -14,6 +14,7 @@ #include "clang/Basic/AtomicLineLogger.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Errno.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/Process.h" @@ -41,22 +42,16 @@ static uint64_t getTimestampMillis() { #endif } -#ifdef _WIN32 -// Write to files opened with OF_Append may not be guaranteed to be atomic -// on Windows. Both openLogFile and writeLineToFD are noops on Windows. static int openLogFile(StringRef Path) { +#ifdef _WIN32 + // Logging is always disabled on Windows. openLogFile implements this policy + // by never returning a valid FD, so the logger and the LogLines it creates + // stay dormant (FD == -1). The reason is that writes to files opened with + // OF_Append are not guaranteed atomic on Windows. If a use case arises we'll + // need a different strategy to write LogLines atomically. (void)Path; return -1; -} - -static bool writeLineToFD(int FD, const char *Data, size_t Size) { - (void)FD, (void)Data, (void)Size; - return false; -} - #else - -static int openLogFile(StringRef Path) { int FD = -1; std::error_code EC = llvm::sys::fs::openFileForWrite( Path, FD, llvm::sys::fs::CD_OpenAlways, llvm::sys::fs::OF_Append); @@ -66,17 +61,21 @@ static int openLogFile(StringRef Path) { return -1; } return FD; +#endif } // Writes the whole line into an FD that is opened with OF_Append. // This function only does one write (up to retry due to interrupts), and the // single write is blocking and atomic on POSIX systems. static bool writeLineToFD(int FD, const char *Data, size_t Size) { +#ifdef _WIN32 + (void)FD, (void)Data, (void)Size; + llvm_unreachable("dependency scanning logging is unsupported on Windows"); +#else ssize_t Written = llvm::sys::RetryAfterSignal(-1, write, FD, Data, Size); return Written >= 0 && (static_cast<size_t>(Written) == Size); -} - #endif +} LogLine::LogLine(int FD, std::atomic<uint64_t> *DroppedLines) : FormattingOS(Buffer), FD(FD), DroppedLines(DroppedLines) { @@ -106,16 +105,19 @@ LogLine::~LogLine() { DroppedLines->fetch_add(1, std::memory_order_relaxed); } -void AtomicLineLogger::setFD(StringRef Path) { - LogPath = Path.str(); - FD.store(openLogFile(Path), std::memory_order_release); +void AtomicLineLogger::initialize(StringRef LogFilePath) { + int NewFD = openLogFile(LogFilePath); + if (NewFD == -1) + return; + LogPath = LogFilePath.str(); + FD.store(NewFD, std::memory_order_relaxed); log() << "logging_start"; } AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath) { if (LogFilePath.empty()) return; - setFD(LogFilePath); + initialize(LogFilePath); } void AtomicLineLogger::enable(StringRef LogFilePath) { @@ -131,11 +133,7 @@ void AtomicLineLogger::enable(StringRef LogFilePath) { return; } - int NewFD = openLogFile(LogFilePath); - if (NewFD == -1) - return; - setFD(LogFilePath); - return; + initialize(LogFilePath); } LogLine AtomicLineLogger::log() { diff --git a/clang/unittests/Basic/AtomicLineLoggerTest.cpp b/clang/unittests/Basic/AtomicLineLoggerTest.cpp index 654ebf1149668..5e0b4c9a88cd0 100644 --- a/clang/unittests/Basic/AtomicLineLoggerTest.cpp +++ b/clang/unittests/Basic/AtomicLineLoggerTest.cpp @@ -16,6 +16,7 @@ using namespace clang; +#ifndef _WIN32 static StringRef logBody(StringRef Line) { return Line.split(": ").second; } static SmallVector<StringRef> logBodyLines(StringRef Content) { @@ -39,7 +40,6 @@ TEST(AtomicLineLoggerTest, DisabledLoggerDoesNotCrash) { EXPECT_TRUE(true); } -#ifndef _WIN32 TEST(AtomicLineLoggerTest, LogLineMoveConstructor) { llvm::unittest::TempDir Dir("atomic-logger-test", /*Unique=*/true); SmallString<128> LogPath(Dir.path()); @@ -171,6 +171,48 @@ TEST(AtomicLineLoggerTest, SingleLineWrittenToFile) { EXPECT_EQ(logBody(Body.front()), "pcm_write: module.pcm"); } +TEST(AtomicLineLoggerTest, EnableWithConflictingPathIsIgnoredAndWarnsOnce) { + llvm::unittest::TempDir Dir("atomic-logger-test", /*Unique=*/true); + SmallString<128> PathA(Dir.path()); + llvm::sys::path::append(PathA, "a.log"); + SmallString<128> PathB(Dir.path()); + llvm::sys::path::append(PathB, "b.log"); + + std::string Err; + { + AtomicLineLogger Logger; + Logger.enable(PathA); + + testing::internal::CaptureStderr(); + // Enable PathB twice to check if only one warning shows up. + Logger.enable(PathB); + Logger.enable(PathB); + // Enabling the path already in effect is a no-op. + Logger.enable(PathA); + Err = testing::internal::GetCapturedStderr(); + + StringRef ErrRef(Err); + EXPECT_EQ(ErrRef.count("already logging to"), 1u); + EXPECT_TRUE(ErrRef.contains(PathB)); + EXPECT_TRUE(ErrRef.contains(PathA)); + + Logger.log() << "still_on_a"; + } + // Logger destroyed here. Log file is written to disk. + + EXPECT_EQ(Err, + ("warning: dependency scanning log path '" + Twine(PathB.str()) + + "' ignored; already logging to '" + Twine(PathA.str()) + "'\n") + .str()); + + auto ABuf = llvm::MemoryBuffer::getFile(PathA); + ASSERT_TRUE(ABuf) << "Failed to read log file A"; + EXPECT_TRUE((*ABuf)->getBuffer().contains("still_on_a")); + + EXPECT_FALSE(llvm::sys::fs::exists(PathB)) + << "Conflicting path B should never have been opened"; +} + TEST(AtomicLineLoggerTest, ConcurrentWritesProduceCompleteLines) { llvm::unittest::TempDir Dir("atomic-logger-concurrent", /*Unique=*/true); SmallString<128> LogPath(Dir.path()); >From cb2353acc3eec5509a46d48c03acf56730a94118 Mon Sep 17 00:00:00 2001 From: Qiongsi Wu <[email protected]> Date: Wed, 19 Aug 2026 16:09:35 -0700 Subject: [PATCH 4/5] Hooking up the logger option for modules driver. --- clang/lib/Driver/ModulesDriver.cpp | 10 +++++++--- .../test/Driver/modules-driver-depscan-log.cpp | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 clang/test/Driver/modules-driver-depscan-log.cpp diff --git a/clang/lib/Driver/ModulesDriver.cpp b/clang/lib/Driver/ModulesDriver.cpp index f0f312769fab1..607705014c42d 100644 --- a/clang/lib/Driver/ModulesDriver.cpp +++ b/clang/lib/Driver/ModulesDriver.cpp @@ -607,7 +607,7 @@ static std::optional<DependencyScanResult> scanDependencies( ArrayRef<std::unique_ptr<Command>> Jobs, llvm::DenseMap<StringRef, const StdModuleManifest::Module *> ManifestLookup, StringRef ModuleCachePath, StringRef WorkingDirectory, - DiagnosticsEngine &Diags) { + StringRef DepScanLogPath, DiagnosticsEngine &Diags) { llvm::PrettyStackTraceString CrashInfo("Performing module dependency scan."); // Classify the jobs based on scan eligibility. @@ -645,6 +645,7 @@ static std::optional<DependencyScanResult> scanDependencies( const bool HasStdlibModuleInputs = !StdlibModuleScanIndexByID.empty(); deps::DependencyScanningServiceOptions Opts; + Opts.LogPath = DepScanLogPath.str(); deps::DependencyScanningService ScanningService(std::move(Opts)); std::unique_ptr<llvm::ThreadPoolInterface> ThreadPool; @@ -1655,8 +1656,11 @@ void driver::modules::runModulesDriver( auto MaybeCWD = C.getDriver().getVFS().getCurrentWorkingDirectory(); const auto CWD = MaybeCWD ? std::move(*MaybeCWD) : "."; - auto MaybeScanResults = scanDependencies(Jobs, ManifestEntryBySource, - *MaybeModuleCachePath, CWD, Diags); + StringRef DepScanLogPath = + C.getArgs().getLastArgValue(options::OPT_fdepscan_log_path); + auto MaybeScanResults = + scanDependencies(Jobs, ManifestEntryBySource, *MaybeModuleCachePath, CWD, + DepScanLogPath, Diags); if (!MaybeScanResults) { Diags.Report(diag::err_dependency_scan_failed); return; diff --git a/clang/test/Driver/modules-driver-depscan-log.cpp b/clang/test/Driver/modules-driver-depscan-log.cpp new file mode 100644 index 0000000000000..62f0ae859d56a --- /dev/null +++ b/clang/test/Driver/modules-driver-depscan-log.cpp @@ -0,0 +1,17 @@ +// Check that -fdepscan-log-path enables dependency scanning logging. + +// UNSUPPORTED: system-windows +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang -c -std=c++23 -fmodules-driver -fdepscan-log-path=%t/scan.log \ +// RUN: %t/A.cppm +// RUN: FileCheck %s --input-file %t/scan.log + +// CHECK: logging_start +// CHECK: starting scanning command: +// CHECK: logging_end + +//--- A.cppm +export module A; +export int a() { return 0; } >From 85c7164c93dd5e6bc868e0ac2e8170c41affa5d3 Mon Sep 17 00:00:00 2001 From: Qiongsi Wu <[email protected]> Date: Wed, 2 Sep 2026 15:35:21 -0700 Subject: [PATCH 5/5] Adding error checks against enabling the logger inconsistently through driver commands. --- clang/include/clang/Basic/AtomicLineLogger.h | 11 +++- .../clang/Basic/DiagnosticDriverKinds.td | 6 ++ clang/lib/Basic/AtomicLineLogger.cpp | 25 ++++---- clang/lib/Driver/ModulesDriver.cpp | 9 ++- clang/lib/Tooling/DependencyScanningTool.cpp | 19 +++++- clang/test/ClangScanDeps/depscan-log-path.c | 64 +++++++++++++++++++ .../unittests/Basic/AtomicLineLoggerTest.cpp | 42 ------------ 7 files changed, 115 insertions(+), 61 deletions(-) create mode 100644 clang/test/ClangScanDeps/depscan-log-path.c diff --git a/clang/include/clang/Basic/AtomicLineLogger.h b/clang/include/clang/Basic/AtomicLineLogger.h index 1e8593578054d..b3a48d847868f 100644 --- a/clang/include/clang/Basic/AtomicLineLogger.h +++ b/clang/include/clang/Basic/AtomicLineLogger.h @@ -65,7 +65,8 @@ class AtomicLineLogger { std::string LogPath; std::atomic<uint64_t> DroppedLines{0}; std::mutex EnableMtx; - bool WarnedConflict = false; + bool EnabledAtConstruction = false; + bool CommittedByFlag = false; void initialize(StringRef LogFilePath); @@ -81,8 +82,12 @@ class AtomicLineLogger { ~AtomicLineLogger(); // Enables the logger if it is not already enabled. Thread safe. - // If the logger is already enabled, call to enable is a no-op. - void enable(StringRef LogFilePath); + // Returns false only when the requested path conflicts with the + // already-committed path. In other words, returns false if + // the logger is not enabled consistently during its lifetime. + bool enable(StringRef RequestedLogPath); + + StringRef getLogPath() const { return LogPath; } LogLine log(); }; diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index ae3eecc60fc78..bb2fa5c0fcec9 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -659,6 +659,12 @@ def remark_printing_module_graph : Remark< def err_module_defined_outside_of_module_source : Error< "module '%0' is defined in file '%1', but module declarations are only " "allowed in C++ module inputs; use the '.cppm' extension or '-x c++module'">; +def err_drv_depscan_log_path_empty : Error< + "'-fdepscan-log-path=' requires a non-empty file path">; +def err_drv_depscan_log_path_inconsistent : Error< + "'-fdepscan-log-path' set inconsistently within a dependency scan: " + "%select{no log path|log path '%1'}0 conflicts with " + "%select{no log path|log path '%3'}2 requested earlier">; def warn_drv_delayed_template_parsing_after_cxx20 : Warning< "-fdelayed-template-parsing is deprecated after C++20">, diff --git a/clang/lib/Basic/AtomicLineLogger.cpp b/clang/lib/Basic/AtomicLineLogger.cpp index 08bc4ef9b397c..75b8914fd61ae 100644 --- a/clang/lib/Basic/AtomicLineLogger.cpp +++ b/clang/lib/Basic/AtomicLineLogger.cpp @@ -106,10 +106,10 @@ LogLine::~LogLine() { } void AtomicLineLogger::initialize(StringRef LogFilePath) { + LogPath = LogFilePath.str(); int NewFD = openLogFile(LogFilePath); if (NewFD == -1) return; - LogPath = LogFilePath.str(); FD.store(NewFD, std::memory_order_relaxed); log() << "logging_start"; } @@ -118,22 +118,21 @@ AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath) { if (LogFilePath.empty()) return; initialize(LogFilePath); + EnabledAtConstruction = true; } -void AtomicLineLogger::enable(StringRef LogFilePath) { - if (LogFilePath.empty()) - return; +bool AtomicLineLogger::enable(StringRef RequestedLogPath) { std::lock_guard<std::mutex> Lock(EnableMtx); - if (FD.load(std::memory_order_relaxed) != -1) { - if (LogFilePath != LogPath && !WarnedConflict) { - llvm::errs() << "warning: dependency scanning log path '" << LogFilePath - << "' ignored; already logging to '" << LogPath << "'\n"; - WarnedConflict = true; - } - return; - } + if (EnabledAtConstruction) + return RequestedLogPath.empty() || RequestedLogPath == LogPath; - initialize(LogFilePath); + if (!CommittedByFlag) { + CommittedByFlag = true; + if (!RequestedLogPath.empty()) + initialize(RequestedLogPath); + return true; + } + return RequestedLogPath == LogPath; } LogLine AtomicLineLogger::log() { diff --git a/clang/lib/Driver/ModulesDriver.cpp b/clang/lib/Driver/ModulesDriver.cpp index 607705014c42d..9e6984fa8ef6b 100644 --- a/clang/lib/Driver/ModulesDriver.cpp +++ b/clang/lib/Driver/ModulesDriver.cpp @@ -1656,8 +1656,15 @@ void driver::modules::runModulesDriver( auto MaybeCWD = C.getDriver().getVFS().getCurrentWorkingDirectory(); const auto CWD = MaybeCWD ? std::move(*MaybeCWD) : "."; + const llvm::opt::Arg *LogPathArg = + C.getArgs().getLastArg(options::OPT_fdepscan_log_path); StringRef DepScanLogPath = - C.getArgs().getLastArgValue(options::OPT_fdepscan_log_path); + LogPathArg ? StringRef(LogPathArg->getValue()).trim() : StringRef(); + if (LogPathArg && DepScanLogPath.empty()) { + Diags.Report(diag::err_drv_depscan_log_path_empty); + return; + } + auto MaybeScanResults = scanDependencies(Jobs, ManifestEntryBySource, *MaybeModuleCachePath, CWD, DepScanLogPath, Diags); diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index d0e01bd788ce2..a937879706522 100644 --- a/clang/lib/Tooling/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanningTool.cpp @@ -127,8 +127,23 @@ buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, return std::make_pair(nullptr, nullptr); } - Logger.enable( - Compilation->getArgs().getLastArgValue(options::OPT_fdepscan_log_path)); + const llvm::opt::Arg *LogPathArg = + Compilation->getArgs().getLastArg(options::OPT_fdepscan_log_path); + StringRef LogPath = + LogPathArg ? StringRef(LogPathArg->getValue()).trim() : StringRef(); + + // Forbid -fdepscan-log-path="". + if (LogPathArg && LogPath.empty()) { + Diags.Report(diag::err_drv_depscan_log_path_empty); + return std::make_pair(nullptr, nullptr); + } + + if (!Logger.enable(LogPath)) { + Diags.Report(diag::err_drv_depscan_log_path_inconsistent) + << unsigned(!LogPath.empty()) << LogPath + << unsigned(!Logger.getLogPath().empty()) << Logger.getLogPath(); + return std::make_pair(nullptr, nullptr); + } return std::make_pair(std::move(Driver), std::move(Compilation)); } diff --git a/clang/test/ClangScanDeps/depscan-log-path.c b/clang/test/ClangScanDeps/depscan-log-path.c new file mode 100644 index 0000000000000..ea3993c80c8fa --- /dev/null +++ b/clang/test/ClangScanDeps/depscan-log-path.c @@ -0,0 +1,64 @@ +// Diagnostics for the -fdepscan-log-path driver flag during dependency scanning: +// an empty value is rejected, an inconsistent value across commands in one scan +// is rejected, and a consistent value is accepted (and aggregated). + +// UNSUPPORTED: system-windows +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/empty.json.template > %t/empty.json +// RUN: sed -e "s|DIR|%/t|g" %t/inconsistent.json.template > %t/inconsistent.json +// RUN: sed -e "s|DIR|%/t|g" %t/consistent.json.template > %t/consistent.json + +// An explicitly empty value is rejected. +// RUN: not clang-scan-deps -compilation-database %t/empty.json \ +// RUN: -format experimental-full -j 1 2>&1 | FileCheck %s --check-prefix=EMPTY +// EMPTY: error: '-fdepscan-log-path=' requires a non-empty file path + +// Different log paths across commands in one scan are rejected. +// RUN: not clang-scan-deps -compilation-database %t/inconsistent.json \ +// RUN: -format experimental-full -j 1 2>&1 | FileCheck %s --check-prefix=CONFLICT +// CONFLICT: error: '-fdepscan-log-path' set inconsistently within a dependency scan + +// The same log path across commands is fine; both are aggregated into one log. +// RUN: clang-scan-deps -compilation-database %t/consistent.json \ +// RUN: -format experimental-full -j 1 -o %t/deps.json +// RUN: FileCheck %s --check-prefix=OK --input-file %t/scan.log +// OK: logging_start +// OK: starting scanning command:{{.*}}tu.c +// OK: starting scanning command:{{.*}}tu2.c +// OK: logging_end + +// One command with a valid flag, the other does not have a flag in effect. +// RUN: not clang-scan-deps -compilation-database %t/inconsistent.json \ +// RUN: -format experimental-full -j 1 2>&1 | FileCheck %s --check-prefix=CONFLICT2 +// CONFLICT2: error: '-fdepscan-log-path' set inconsistently within a dependency scan + +//--- empty.json.template +[{ + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.c -fdepscan-log-path=", + "file": "DIR/tu.c" +}] + +//--- inconsistent.json.template +[ +{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu.c -fdepscan-log-path=DIR/a.log", "file": "DIR/tu.c" }, +{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu2.c -fdepscan-log-path=DIR/b.log", "file": "DIR/tu2.c" } +] + +//--- consistent.json.template +[ +{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu.c -fdepscan-log-path=DIR/scan.log", "file": "DIR/tu.c" }, +{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu2.c -fdepscan-log-path=DIR/scan.log", "file": "DIR/tu2.c" } +] + +//--- inconsistent2.json.template +[ +{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu.c", "file": "DIR/tu.c" }, +{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu2.c -fdepscan-log-path=DIR/b.log", "file": "DIR/tu2.c" } +] + +//--- tu.c +void foo(void) {} +//--- tu2.c +void bar(void) {} diff --git a/clang/unittests/Basic/AtomicLineLoggerTest.cpp b/clang/unittests/Basic/AtomicLineLoggerTest.cpp index 5e0b4c9a88cd0..97e600a1a17eb 100644 --- a/clang/unittests/Basic/AtomicLineLoggerTest.cpp +++ b/clang/unittests/Basic/AtomicLineLoggerTest.cpp @@ -171,48 +171,6 @@ TEST(AtomicLineLoggerTest, SingleLineWrittenToFile) { EXPECT_EQ(logBody(Body.front()), "pcm_write: module.pcm"); } -TEST(AtomicLineLoggerTest, EnableWithConflictingPathIsIgnoredAndWarnsOnce) { - llvm::unittest::TempDir Dir("atomic-logger-test", /*Unique=*/true); - SmallString<128> PathA(Dir.path()); - llvm::sys::path::append(PathA, "a.log"); - SmallString<128> PathB(Dir.path()); - llvm::sys::path::append(PathB, "b.log"); - - std::string Err; - { - AtomicLineLogger Logger; - Logger.enable(PathA); - - testing::internal::CaptureStderr(); - // Enable PathB twice to check if only one warning shows up. - Logger.enable(PathB); - Logger.enable(PathB); - // Enabling the path already in effect is a no-op. - Logger.enable(PathA); - Err = testing::internal::GetCapturedStderr(); - - StringRef ErrRef(Err); - EXPECT_EQ(ErrRef.count("already logging to"), 1u); - EXPECT_TRUE(ErrRef.contains(PathB)); - EXPECT_TRUE(ErrRef.contains(PathA)); - - Logger.log() << "still_on_a"; - } - // Logger destroyed here. Log file is written to disk. - - EXPECT_EQ(Err, - ("warning: dependency scanning log path '" + Twine(PathB.str()) + - "' ignored; already logging to '" + Twine(PathA.str()) + "'\n") - .str()); - - auto ABuf = llvm::MemoryBuffer::getFile(PathA); - ASSERT_TRUE(ABuf) << "Failed to read log file A"; - EXPECT_TRUE((*ABuf)->getBuffer().contains("still_on_a")); - - EXPECT_FALSE(llvm::sys::fs::exists(PathB)) - << "Conflicting path B should never have been opened"; -} - TEST(AtomicLineLoggerTest, ConcurrentWritesProduceCompleteLines) { llvm::unittest::TempDir Dir("atomic-logger-concurrent", /*Unique=*/true); SmallString<128> LogPath(Dir.path()); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
