clang 24.0.0git
AtomicLineLogger.cpp
Go to the documentation of this file.
1//===- AtomicLineLogger.cpp -----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines the implementation of an AtomicLineLogger and the relevant
10// supporting classes.
11//
12//===----------------------------------------------------------------------===//
13
15#include "llvm/ADT/StringRef.h"
16#include "llvm/Support/Errno.h"
17#include "llvm/Support/ErrorHandling.h"
18#include "llvm/Support/FileSystem.h"
19#include "llvm/Support/Format.h"
20#include "llvm/Support/Process.h"
21#include "llvm/Support/Threading.h"
22#ifndef _WIN32
23#include <unistd.h>
24#endif
25#ifdef __APPLE__
26#include <sys/time.h>
27#endif
28
29using namespace clang;
30
31static uint64_t getTimestampMillis() {
32#ifdef __APPLE__
33 // Using chrono is roughly 50% slower.
34 struct timeval T;
35 gettimeofday(&T, 0);
36 return T.tv_sec * 1000 + T.tv_usec / 1000;
37#else
38 auto Time = std::chrono::system_clock::now();
39 auto Millis = std::chrono::duration_cast<std::chrono::milliseconds>(
40 Time.time_since_epoch());
41 return Millis.count();
42#endif
43}
44
45static int openLogFile(StringRef Path) {
46#ifdef _WIN32
47 // Logging is always disabled on Windows. openLogFile implements this policy
48 // by never returning a valid FD, so the logger and the LogLines it creates
49 // stay dormant (FD == -1). The reason is that writes to files opened with
50 // OF_Append are not guaranteed atomic on Windows. If a use case arises we'll
51 // need a different strategy to write LogLines atomically.
52 (void)Path;
53 return -1;
54#else
55 int FD = -1;
56 std::error_code EC = llvm::sys::fs::openFileForWrite(
57 Path, FD, llvm::sys::fs::CD_OpenAlways, llvm::sys::fs::OF_Append);
58 if (EC) {
59 llvm::errs() << "warning: unable to open log file '" << Path
60 << "': " << EC.message() << "\n";
61 return -1;
62 }
63 return FD;
64#endif
65}
66
67// Writes the whole line into an FD that is opened with OF_Append.
68// This function only does one write (up to retry due to interrupts), and the
69// single write is blocking and atomic on POSIX systems.
70static bool writeLineToFD(int FD, const char *Data, size_t Size) {
71#ifdef _WIN32
72 (void)FD, (void)Data, (void)Size;
73 llvm_unreachable("dependency scanning logging is unsupported on Windows");
74#else
75 ssize_t Written = llvm::sys::RetryAfterSignal(-1, write, FD, Data, Size);
76 return Written >= 0 && (static_cast<size_t>(Written) == Size);
77#endif
78}
79
80LogLine::LogLine(int FD, std::atomic<uint64_t> *DroppedLines)
81 : FormattingOS(Buffer), FD(FD), DroppedLines(DroppedLines) {
82 auto Millis = getTimestampMillis();
83 *FormattingOS << llvm::format("[%lld.%0.3lld]", Millis / 1000, Millis % 1000);
84 *FormattingOS << ' ' << llvm::sys::Process::getProcessId() << ' '
85 << llvm::get_threadid() << ": ";
86}
87
89 : Buffer(std::move(Other.Buffer)), FD(Other.FD),
90 DroppedLines(Other.DroppedLines) {
91 if (Other.FormattingOS)
92 FormattingOS.emplace(Buffer);
93
94 // Destroy the info in Other so its destructor does not write out the line.
95 Other.FormattingOS.reset();
96 Other.FD = -1;
97 Other.DroppedLines = nullptr;
98}
99
101 if (!FormattingOS)
102 return;
103 *FormattingOS << "\n";
104 if (!writeLineToFD(FD, Buffer.data(), Buffer.size()))
105 DroppedLines->fetch_add(1, std::memory_order_relaxed);
106}
107
108void AtomicLineLogger::initialize(StringRef LogFilePath) {
109 LogPath = LogFilePath.str();
110 int NewFD = openLogFile(LogFilePath);
111 if (NewFD == -1)
112 return;
113 FD.store(NewFD, std::memory_order_relaxed);
114 log() << "logging_start";
115}
116
118 if (LogFilePath.empty())
119 return;
120 initialize(LogFilePath);
121 PathSource = LogPathSource::Constructor;
122}
123
124bool AtomicLineLogger::enable(StringRef RequestedLogPath) {
125 std::lock_guard<std::mutex> Lock(EnableMtx);
126 switch (PathSource) {
127 case LogPathSource::None:
128 PathSource = LogPathSource::EnableMethod;
129 if (!RequestedLogPath.empty())
130 initialize(RequestedLogPath);
131 return true;
132 case LogPathSource::Constructor:
133 return RequestedLogPath.empty() || RequestedLogPath == LogPath;
134 case LogPathSource::EnableMethod:
135 return RequestedLogPath == LogPath;
136 }
137
138 llvm_unreachable("unhandled LogPathSource");
139}
140
142 int CurFD = FD.load(std::memory_order_relaxed);
143 if (CurFD != -1)
144 return LogLine(CurFD, &DroppedLines);
145 return LogLine();
146}
147
149 int CurFD = FD.load(std::memory_order_relaxed);
150 if (CurFD == -1)
151 return;
152 log() << "logging_end";
153 if (uint64_t Dropped = DroppedLines.load(std::memory_order_relaxed))
154 llvm::errs() << "warning: log '" << LogPath
155 << "' is incomplete: " << Dropped
156 << " line(s) dropped due to write errors\n";
157 llvm::sys::Process::SafelyCloseFileDescriptor(FD);
158}
static int openLogFile(StringRef Path)
static bool writeLineToFD(int FD, const char *Data, size_t Size)
static uint64_t getTimestampMillis()
Defines a logger where each line is written atomically to the file.
bool enable(StringRef RequestedLogPath)
Enables the logger if it is not already enabled.
Top level wrappers for InstallAPI frontend operations.
const FunctionProtoType * T
@ Other
Other implicit parameter.
Definition Decl.h:1775