[XRay] Encapsulate all FD log related logic into a class

This abstracts away the file descriptor related logic which makes it
easier to port XRay to platform that don't use file descriptors or
file system for writing the log data, such as Fuchsia.

Differential Revision: https://reviews.llvm.org/D52161

llvm-svn: 344578
This commit is contained in:
Petr Hosek 2018-10-16 01:24:46 +00:00
parent 5df333b2ec
commit 6a8cedef25
5 changed files with 86 additions and 107 deletions

View File

@ -60,7 +60,7 @@ struct alignas(64) ThreadLocalData {
void *ShadowStack = nullptr;
size_t StackSize = 0;
size_t StackEntries = 0;
int Fd = -1;
LogWriter *LogWriter = nullptr;
};
struct BasicLoggingOptions {
@ -83,10 +83,10 @@ static atomic_uint64_t ThresholdTicks{0};
static atomic_uint64_t TicksPerSec{0};
static atomic_uint64_t CycleFrequency{NanosecondsPerSecond};
static int openLogFile() XRAY_NEVER_INSTRUMENT {
int F = getLogFD();
if (F == -1)
return -1;
static LogWriter *getLog() XRAY_NEVER_INSTRUMENT {
LogWriter* LW = LogWriter::Open();
if (LW == nullptr)
return LW;
static pthread_once_t DetectOnce = PTHREAD_ONCE_INIT;
pthread_once(&DetectOnce, +[] {
@ -108,16 +108,16 @@ static int openLogFile() XRAY_NEVER_INSTRUMENT {
// before setting the values in the header.
Header.ConstantTSC = 1;
Header.NonstopTSC = 1;
retryingWriteAll(F, reinterpret_cast<char *>(&Header),
reinterpret_cast<char *>(&Header) + sizeof(Header));
return F;
LW->WriteAll(reinterpret_cast<char *>(&Header),
reinterpret_cast<char *>(&Header) + sizeof(Header));
return LW;
}
static int getGlobalFd() XRAY_NEVER_INSTRUMENT {
static LogWriter *getGlobalLog() XRAY_NEVER_INSTRUMENT {
static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
static int Fd = 0;
pthread_once(&OnceInit, +[] { Fd = openLogFile(); });
return Fd;
static LogWriter *LW = nullptr;
pthread_once(&OnceInit, +[] { LW = getLog(); });
return LW;
}
static ThreadLocalData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
@ -129,7 +129,7 @@ static ThreadLocalData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
return false;
}
pthread_setspecific(PThreadKey, &TLD);
TLD.Fd = getGlobalFd();
TLD.LogWriter = getGlobalLog();
TLD.InMemoryBuffer = reinterpret_cast<XRayRecord *>(
InternalAlloc(sizeof(XRayRecord) * GlobalOptions.ThreadBufferSize,
nullptr, alignof(XRayRecord)));
@ -157,8 +157,8 @@ template <class RDTSC>
void InMemoryRawLog(int32_t FuncId, XRayEntryType Type,
RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT {
auto &TLD = getThreadLocalData();
int Fd = getGlobalFd();
if (Fd == -1)
LogWriter *LW = getGlobalLog();
if (LW == nullptr)
return;
// Use a simple recursion guard, to handle cases where we're already logging
@ -242,9 +242,9 @@ void InMemoryRawLog(int32_t FuncId, XRayEntryType Type,
auto FirstEntry = reinterpret_cast<XRayRecord *>(TLD.InMemoryBuffer);
internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
if (++TLD.BufferOffset == TLD.BufferSize) {
SpinMutexLock L(&LogMutex);
retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
SpinMutexLock Lock(&LogMutex);
LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
TLD.BufferOffset = 0;
TLD.StackEntries = 0;
}
@ -257,17 +257,17 @@ void InMemoryRawLogWithArg(int32_t FuncId, XRayEntryType Type, uint64_t Arg1,
auto FirstEntry =
reinterpret_cast<XRayArgPayload *>(TLD.InMemoryBuffer);
const auto &BuffLen = TLD.BufferSize;
int Fd = getGlobalFd();
if (Fd == -1)
LogWriter *LW = getGlobalLog();
if (LW == nullptr)
return;
// First we check whether there's enough space to write the data consecutively
// in the thread-local buffer. If not, we first flush the buffer before
// attempting to write the two records that must be consecutive.
if (TLD.BufferOffset + 2 > BuffLen) {
SpinMutexLock L(&LogMutex);
retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
SpinMutexLock Lock(&LogMutex);
LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
TLD.BufferOffset = 0;
TLD.StackEntries = 0;
}
@ -288,9 +288,9 @@ void InMemoryRawLogWithArg(int32_t FuncId, XRayEntryType Type, uint64_t Arg1,
R.Arg = Arg1;
internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
if (++TLD.BufferOffset == BuffLen) {
SpinMutexLock L(&LogMutex);
retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
SpinMutexLock Lock(&LogMutex);
LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
TLD.BufferOffset = 0;
TLD.StackEntries = 0;
}
@ -347,25 +347,25 @@ static void TLDDestructor(void *P) XRAY_NEVER_INSTRUMENT {
Report("Cleaned up log for TID: %d\n", GetTid());
});
if (TLD.Fd == -1 || TLD.BufferOffset == 0) {
if (TLD.LogWriter == nullptr || TLD.BufferOffset == 0) {
if (Verbosity())
Report("Skipping buffer for TID: %d; Fd = %d; Offset = %llu\n", GetTid(),
TLD.Fd, TLD.BufferOffset);
Report("Skipping buffer for TID: %d; Offset = %llu\n", GetTid(),
TLD.BufferOffset);
return;
}
{
SpinMutexLock L(&LogMutex);
retryingWriteAll(TLD.Fd, reinterpret_cast<char *>(TLD.InMemoryBuffer),
reinterpret_cast<char *>(TLD.InMemoryBuffer) +
(sizeof(XRayRecord) * TLD.BufferOffset));
TLD.LogWriter->WriteAll(reinterpret_cast<char *>(TLD.InMemoryBuffer),
reinterpret_cast<char *>(TLD.InMemoryBuffer) +
(sizeof(XRayRecord) * TLD.BufferOffset));
}
// Because this thread's exit could be the last one trying to write to
// the file and that we're not able to close out the file properly, we
// sync instead and hope that the pending writes are flushed as the
// thread exits.
fsync(TLD.Fd);
TLD.LogWriter->Flush();
}
XRayLogInitStatus basicLoggingInit(UNUSED size_t BufferSize,

View File

@ -838,8 +838,8 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
// (fixed-sized) and let the tools reading the buffers deal with the data
// afterwards.
//
int Fd = getLogFD();
if (Fd == -1) {
LogWriter* LW = LogWriter::Open();
if (LW == nullptr) {
auto Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
atomic_store(&LogFlushStatus, Result, memory_order_release);
return Result;
@ -847,8 +847,8 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
XRayFileHeader Header = fdrCommonHeaderInfo();
Header.FdrData = FdrAdditionalHeaderData{BQ->ConfiguredBufferSize()};
retryingWriteAll(Fd, reinterpret_cast<char *>(&Header),
reinterpret_cast<char *>(&Header) + sizeof(Header));
LW->WriteAll(reinterpret_cast<char *>(&Header),
reinterpret_cast<char *>(&Header) + sizeof(Header));
// Release the current thread's buffer before we attempt to write out all the
// buffers. This ensures that in case we had only a single thread going, that
@ -871,11 +871,11 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
uint8_t(MetadataRecord::RecordKinds::BufferExtents);
internal_memcpy(ExtentsRecord.Data, &BufferExtents, sizeof(BufferExtents));
if (BufferExtents > 0) {
retryingWriteAll(Fd, reinterpret_cast<char *>(&ExtentsRecord),
reinterpret_cast<char *>(&ExtentsRecord) +
sizeof(MetadataRecord));
retryingWriteAll(Fd, reinterpret_cast<char *>(B.Data),
reinterpret_cast<char *>(B.Data) + BufferExtents);
LW->WriteAll(reinterpret_cast<char *>(&ExtentsRecord),
reinterpret_cast<char *>(&ExtentsRecord) +
sizeof(MetadataRecord));
LW->WriteAll(reinterpret_cast<char *>(B.Data),
reinterpret_cast<char *>(B.Data) + BufferExtents);
}
});

View File

@ -133,21 +133,20 @@ XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
if (Verbosity())
Report("profiling: No data to flush.\n");
} else {
int Fd = getLogFD();
if (Fd == -1) {
LogWriter* LW = LogWriter::Open();
if (LW == nullptr) {
if (Verbosity())
Report("profiling: Failed to flush to file, dropping data.\n");
} else {
// Now for each of the buffers, write out the profile data as we would
// see it in memory, verbatim.
while (B.Data != nullptr && B.Size != 0) {
retryingWriteAll(Fd, reinterpret_cast<const char *>(B.Data),
reinterpret_cast<const char *>(B.Data) + B.Size);
LW->WriteAll(reinterpret_cast<const char *>(B.Data),
reinterpret_cast<const char *>(B.Data) + B.Size);
B = profileCollectorService::nextBuffer(B);
}
// Then we close out the file.
internal_close(Fd);
}
LogWriter::Close(LW);
}
}

View File

@ -12,7 +12,9 @@
//===----------------------------------------------------------------------===//
#include "xray_utils.h"
#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_common.h"
#include "xray_allocator.h"
#include "xray_defs.h"
#include "xray_flags.h"
#include <cstdio>
@ -31,7 +33,11 @@ void printToStdErr(const char *Buffer) XRAY_NEVER_INSTRUMENT {
fprintf(stderr, "%s", Buffer);
}
void retryingWriteAll(int Fd, const char *Begin, const char *End) XRAY_NEVER_INSTRUMENT {
LogWriter::~LogWriter() {
internal_close(Fd);
}
void LogWriter::WriteAll(const char *Begin, const char *End) XRAY_NEVER_INSTRUMENT {
if (Begin == End)
return;
auto TotalBytes = std::distance(Begin, End);
@ -49,50 +55,11 @@ void retryingWriteAll(int Fd, const char *Begin, const char *End) XRAY_NEVER_INS
}
}
std::pair<ssize_t, bool> retryingReadSome(int Fd, char *Begin,
char *End) XRAY_NEVER_INSTRUMENT {
auto BytesToRead = std::distance(Begin, End);
ssize_t BytesRead;
ssize_t TotalBytesRead = 0;
while (BytesToRead && (BytesRead = read(Fd, Begin, BytesToRead))) {
if (BytesRead == -1) {
if (errno == EINTR)
continue;
Report("Read error; errno = %d\n", errno);
return std::make_pair(TotalBytesRead, false);
}
TotalBytesRead += BytesRead;
BytesToRead -= BytesRead;
Begin += BytesRead;
}
return std::make_pair(TotalBytesRead, true);
void LogWriter::Flush() XRAY_NEVER_INSTRUMENT {
fsync(Fd);
}
bool readValueFromFile(const char *Filename,
long long *Value) XRAY_NEVER_INSTRUMENT {
int Fd = open(Filename, O_RDONLY | O_CLOEXEC);
if (Fd == -1)
return false;
static constexpr size_t BufSize = 256;
char Line[BufSize] = {};
ssize_t BytesRead;
bool Success;
std::tie(BytesRead, Success) = retryingReadSome(Fd, Line, Line + BufSize);
if (!Success)
return false;
close(Fd);
const char *End = nullptr;
long long Tmp = internal_simple_strtoll(Line, &End, 10);
bool Result = false;
if (Line[0] != '\0' && (*End == '\n' || *End == '\0')) {
*Value = Tmp;
Result = true;
}
return Result;
}
int getLogFD() XRAY_NEVER_INSTRUMENT {
LogWriter *LogWriter::Open() XRAY_NEVER_INSTRUMENT {
// Open a temporary file once for the log.
char TmpFilename[256] = {};
char TmpWildcardPattern[] = "XXXXXX";
@ -108,18 +75,25 @@ int getLogFD() XRAY_NEVER_INSTRUMENT {
flags()->xray_logfile_base, Progname, TmpWildcardPattern);
if (NeededLength > int(sizeof(TmpFilename))) {
Report("XRay log file name too long (%d): %s\n", NeededLength, TmpFilename);
return -1;
return nullptr;
}
int Fd = mkstemp(TmpFilename);
if (Fd == -1) {
Report("XRay: Failed opening temporary file '%s'; not logging events.\n",
TmpFilename);
return -1;
return nullptr;
}
if (Verbosity())
Report("XRay: Log file in '%s'\n", TmpFilename);
return Fd;
LogWriter *LW = allocate<LogWriter>();
new (LW) LogWriter(Fd);
return LW;
}
void LogWriter::Close(LogWriter *LW) {
LW->~LogWriter();
deallocate(LW);
}
} // namespace __xray

View File

@ -22,22 +22,28 @@
namespace __xray {
class LogWriter {
public:
explicit LogWriter(int Fd) : Fd(Fd) {}
~LogWriter();
// Write a character range into a log.
void WriteAll(const char *Begin, const char *End);
void Flush();
// Returns a new log instance initialized using the flag-provided values.
static LogWriter *Open();
// Closes and deallocates the log instance.
static void Close(LogWriter *LogWriter);
private:
int Fd = -1;
};
// Default implementation of the reporting interface for sanitizer errors.
void printToStdErr(const char *Buffer);
// EINTR-safe write routine, provided a file descriptor and a character range.
void retryingWriteAll(int Fd, const char *Begin, const char *End);
// Reads a long long value from a provided file.
bool readValueFromFile(const char *Filename, long long *Value);
// EINTR-safe read routine, providing a file descriptor and a character range.
std::pair<ssize_t, bool> retryingReadSome(int Fd, char *Begin, char *End);
// EINTR-safe open routine, uses flag-provided values for initialising a log
// file.
int getLogFD();
constexpr size_t gcd(size_t a, size_t b) {
return (b == 0) ? a : gcd(b, a % b);
}