Author: Qiongsi Wu Date: 2026-09-04T21:13:46-07:00 New Revision: a638ce4ea02fe59d0ffba4a81408ddb86cfa0ecc
URL: https://github.com/llvm/llvm-project/commit/a638ce4ea02fe59d0ffba4a81408ddb86cfa0ecc DIFF: https://github.com/llvm/llvm-project/commit/a638ce4ea02fe59d0ffba4a81408ddb86cfa0ecc.diff LOG: [clang][DependencyScanning] Add a Clang Driver Option to Enable Dependency Scanning Logging (#211966) This PR adds a clang driver option to enable dependency scanning logging. The important implementation detail is that the `DependencyScanningService` holds a single instance of the logger, and we need to route the option to the logger per scan. An `enable` method is added to the logger's API to enable it with thread safety. A second detail worth calling out is that the option is a driver option, so if a `cc1` command is passed directly to the scanner, the option will not be processed. Added: clang/test/ClangScanDeps/depscan-log-path.c clang/test/ClangScanDeps/logging-driver-flag.c clang/test/Driver/modules-driver-depscan-log.cpp Modified: clang/include/clang/Basic/AtomicLineLogger.h clang/include/clang/Basic/DiagnosticDriverKinds.td clang/include/clang/DependencyScanning/DependencyScanningWorker.h clang/include/clang/Options/Options.td clang/lib/Basic/AtomicLineLogger.cpp clang/lib/Driver/ModulesDriver.cpp clang/lib/Tooling/DependencyScanningTool.cpp clang/test/ClangScanDeps/logging-simple-by-name.c clang/test/ClangScanDeps/logging-simple.c clang/unittests/Basic/AtomicLineLoggerTest.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Basic/AtomicLineLogger.h b/clang/include/clang/Basic/AtomicLineLogger.h index 3d06ddfce0262..f1489214a9c56 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,18 @@ class LogLine { }; class AtomicLineLogger { - int FD = -1; + std::atomic<int> FD{-1}; std::string LogPath; std::atomic<uint64_t> DroppedLines{0}; + std::mutex EnableMtx; + enum class LogPathSource { + None, + Constructor, // The path is from the constructor. + EnableMethod // The path is set through calling the enable() method. + }; + LogPathSource PathSource = LogPathSource::None; + + void initialize(StringRef LogFilePath); public: AtomicLineLogger() {} @@ -75,6 +85,14 @@ class AtomicLineLogger { ~AtomicLineLogger(); + /// Enables the logger if it is not already enabled. Thread safe. + /// + /// \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 5e8ca1d822a22..df6ff0c2cf399 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -667,6 +667,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/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 348739c0c15ed..37e5c3199a003 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -3822,6 +3822,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..592e1cd63594c 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,17 +42,38 @@ static uint64_t getTimestampMillis() { #endif } +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 diff erent strategy to write LogLines atomically. + (void)Path; + return -1; +#else + 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; +#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) { -#ifndef _WIN32 +#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); -#else - (void)FD, (void)Data, (void)Size; - llvm_unreachable("Logging not supported on Windows!"); - return false; #endif } @@ -83,34 +105,51 @@ LogLine::~LogLine() { DroppedLines->fetch_add(1, std::memory_order_relaxed); } -AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath) - : LogPath(LogFilePath.str()) { -#ifndef _WIN32 - if (LogFilePath.empty()) +void AtomicLineLogger::initialize(StringRef LogFilePath) { + LogPath = LogFilePath.str(); + int NewFD = openLogFile(LogFilePath); + if (NewFD == -1) return; + FD.store(NewFD, std::memory_order_relaxed); + log() << "logging_start"; +} - 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; +AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath) { + if (LogFilePath.empty()) return; + initialize(LogFilePath); + PathSource = LogPathSource::Constructor; +} + +bool AtomicLineLogger::enable(StringRef RequestedLogPath) { + std::lock_guard<std::mutex> Lock(EnableMtx); + switch (PathSource) { + case LogPathSource::None: + PathSource = LogPathSource::EnableMethod; + if (!RequestedLogPath.empty()) + initialize(RequestedLogPath); + return true; + case LogPathSource::Constructor: + return RequestedLogPath.empty() || RequestedLogPath == LogPath; + case LogPathSource::EnableMethod: + return RequestedLogPath == LogPath; } -#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. + + llvm_unreachable("unhandled LogPathSource"); } 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; + 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/lib/Driver/ModulesDriver.cpp b/clang/lib/Driver/ModulesDriver.cpp index f0f312769fab1..9e6984fa8ef6b 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,18 @@ 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); + const llvm::opt::Arg *LogPathArg = + C.getArgs().getLastArg(options::OPT_fdepscan_log_path); + StringRef DepScanLogPath = + 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); if (!MaybeScanResults) { Diags.Report(diag::err_dependency_scan_failed); return; diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index b3332d9d49cd8..a937879706522 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,24 @@ buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, return std::make_pair(nullptr, nullptr); } + 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)); } @@ -155,8 +175,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 +346,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 +383,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/depscan-log-path.c b/clang/test/ClangScanDeps/depscan-log-path.c new file mode 100644 index 0000000000000..4a543c511eb67 --- /dev/null +++ b/clang/test/ClangScanDeps/depscan-log-path.c @@ -0,0 +1,65 @@ +// 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/inconsistent2.json.template > %t/inconsistent2.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 + +// One command with a valid flag, the other does not have a flag in effect. +// RUN: not clang-scan-deps -compilation-database %t/inconsistent2.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 + + +//--- 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/test/ClangScanDeps/logging-driver-flag.c b/clang/test/ClangScanDeps/logging-driver-flag.c new file mode 100644 index 0000000000000..144099ee5dce0 --- /dev/null +++ b/clang/test/ClangScanDeps/logging-driver-flag.c @@ -0,0 +1,43 @@ +// 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 + +// 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 + +// 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(); } 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/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; } diff --git a/clang/unittests/Basic/AtomicLineLoggerTest.cpp b/clang/unittests/Basic/AtomicLineLoggerTest.cpp index b1dfb616d08c2..97e600a1a17eb 100644 --- a/clang/unittests/Basic/AtomicLineLoggerTest.cpp +++ b/clang/unittests/Basic/AtomicLineLoggerTest.cpp @@ -16,6 +16,22 @@ using namespace clang; +#ifndef _WIN32 +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"; @@ -24,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()); @@ -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. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
