Skip to content

Commit

Permalink
Rewritten file I / O for portable C API
Browse files Browse the repository at this point in the history
Besides portability, it also provides more guarantees (for example, the
write system call does not guarantee that the entire buffer will be
written, unlike fwrite, which tries to write the remainder).
  • Loading branch information
Roman-Koshelev committed Dec 2, 2020
1 parent bea4aaf commit 22a348d
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 68 deletions.
2 changes: 1 addition & 1 deletion runtime/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

namespace NanoLogConfig {
// Controls in what mode the compressed log file will be opened
static const int FILE_PARAMS = O_APPEND|O_RDWR|O_CREAT|O_NOATIME|O_DSYNC;
static const char FILE_PARAMS[] = "a+";

// Location of the initial log file
static const char DEFAULT_LOG_FILE[] = "./compressedLog";
Expand Down
23 changes: 5 additions & 18 deletions runtime/DoubleBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,11 @@

class DoubleBuffer
{
struct Deleter {
void operator()(char *__ptr) const { free(__ptr); }
};

using MemPtrT = std::unique_ptr<char, Deleter>;
using MemPtrT = std::unique_ptr<char[]>;

static MemPtrT allocateMemory()
{
char *tmp;
int err = posix_memalign(reinterpret_cast<void **>(&tmp), 512,
NanoLogConfig::OUTPUT_BUFFER_SIZE);
if (err) {
perror(
"The NanoLog system was not able to allocate enough memory "
"to support its operations. Quitting...\r\n");
std::exit(-1);
}
return MemPtrT{tmp};
return MemPtrT{new char[NanoLogConfig::OUTPUT_BUFFER_SIZE]};
};

static char *accessOnce(const MemPtrT &ptr)
Expand Down Expand Up @@ -80,7 +67,7 @@ class DoubleBuffer
}
}

void writeToFile(int file) noexcept
void writeToFile(FILE* file) noexcept
{
unsigned tmp_size = 0;
MemPtrT tmp_ptr = nullptr;
Expand All @@ -94,8 +81,8 @@ class DoubleBuffer

int res = 0;
if (tmp_size != 0) {
res = write(file, tmp_ptr.get(), tmp_size);
res = (res < 0) ? errno : 0;
size_t wsize = fwrite(tmp_ptr.get(), 1u, tmp_size, file);
res = (wsize != tmp_size) ? errno : 0;
}

while (accessOnce(freeBuffer) != nullptr) {}
Expand Down
27 changes: 27 additions & 0 deletions runtime/FSyncPortable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#ifndef _WIN32

#include <unistd.h>

#else

#include <io.h>
#include <Windows.h>

inline int fsync(int fd) {
HANDLE h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE) {
return -1;
}
if (!FlushFileBuffers(h)) {
return -1;
}
return 0;
}

inline int fdatasync(int fd) {
return fsync(fd);
}

#endif
72 changes: 28 additions & 44 deletions runtime/RuntimeLogger.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ RuntimeLogger::RuntimeLogger()
, condMutex()
, workAdded()
, hintSyncCompleted()
, outputFd(-1)
, outputFile(nullptr)
, buffer()
, currentLogLevel(NOTICE)
, cycleAtThreadStart(0)
Expand All @@ -60,7 +60,6 @@ RuntimeLogger::RuntimeLogger()
, cyclesDiskIO_upperBound(0)
, totalBytesRead(0)
, totalBytesWritten(0)
, padBytesWritten(0)
, logsProcessed(0)
, numAioWritesCompleted(0)
, coreId(-1)
Expand All @@ -72,14 +71,15 @@ RuntimeLogger::RuntimeLogger()
stagingBufferPeekDist[i] = 0;

const char *filename = NanoLogConfig::DEFAULT_LOG_FILE;
outputFd = open(filename, NanoLogConfig::FILE_PARAMS, 0666);
if (outputFd < 0) {
outputFile = fopen(filename, NanoLogConfig::FILE_PARAMS);
if (outputFile == nullptr) {
fprintf(stderr, "NanoLog could not open the default file location "
"for the log file (\"%s\").\r\n Please check the permissions "
"or use NanoLog::setLogFile(const char* filename) to "
"specify a different log file.\r\n", filename);
std::exit(-1);
}
setvbuf(outputFile, nullptr, _IONBF, 0);

#ifndef BENCHMARK_DISCARD_ENTRIES_AT_STAGINGBUFFER
compressionThread = std::thread(&RuntimeLogger::compressionThreadMain, this);
Expand Down Expand Up @@ -109,10 +109,10 @@ RuntimeLogger::~RuntimeLogger() {
if (nanoLogSingleton.writerThread.joinable())
nanoLogSingleton.writerThread.join();

if (outputFd > 0)
close(outputFd);
if (outputFile != nullptr)
fclose(outputFile);

outputFd = 0;
outputFile = nullptr;
}

// Documentation in NanoLog.h
Expand All @@ -122,7 +122,7 @@ RuntimeLogger::getStats() {
char buffer[1024];
// Leaks abstraction, but basically flush so we get all the time
uint64_t start = PerfUtils::Cycles::rdtsc();
fdatasync(nanoLogSingleton.outputFd);
fdatasync(fileno(nanoLogSingleton.outputFile));
uint64_t stop = PerfUtils::Cycles::rdtsc();
nanoLogSingleton.cyclesDiskIO_upperBound += (stop - start);

Expand All @@ -136,8 +136,6 @@ RuntimeLogger::getStats() {
nanoLogSingleton.totalBytesWritten);
double totalBytesReadDouble = static_cast<double>(
nanoLogSingleton.totalBytesRead);
double padBytesWrittenDouble = static_cast<double>(
nanoLogSingleton.padBytesWritten);
double numEventsProcessedDouble = static_cast<double>(
nanoLogSingleton.logsProcessed);

Expand Down Expand Up @@ -197,14 +195,11 @@ RuntimeLogger::getStats() {
compressTime * 1.0e9 / numEventsProcessedDouble);
out << buffer;

snprintf(buffer, 1024, "The compression ratio was %0.2lf-%0.2lfx "
"(%lu bytes in, %lu bytes out, %lu pad bytes)\n",
1.0 * totalBytesReadDouble / (totalBytesWrittenDouble
+ padBytesWrittenDouble),
snprintf(buffer, 1024, "The compression ratio was %0.2lf "
"(%lu bytes in, %lu bytes out)\n",
1.0 * totalBytesReadDouble / totalBytesWrittenDouble,
nanoLogSingleton.totalBytesRead,
nanoLogSingleton.totalBytesWritten,
nanoLogSingleton.padBytesWritten);
nanoLogSingleton.totalBytesWritten);
out << buffer;

return out.str();
Expand Down Expand Up @@ -525,17 +520,6 @@ RuntimeLogger::compressionThreadMain() {
if (bytesToWrite == 0)
continue;

// Pad the output if necessary
if (NanoLogConfig::FILE_PARAMS & O_DIRECT) {
ssize_t bytesOver = bytesToWrite % 512;

if (bytesOver != 0) {
memset(buffer.getCompressingBuffer(), 0, 512 - bytesOver);
bytesToWrite = bytesToWrite + 512 - bytesOver;
padBytesWritten += (512 - bytesOver);
}
}

totalBytesWritten += bytesToWrite;

uint64_t tmp = PerfUtils::Cycles::rdtsc();
Expand Down Expand Up @@ -569,29 +553,29 @@ RuntimeLogger::compressionThreadMain() {
void
RuntimeLogger::writerThreadMain() {
while (!writerThreadShouldExit) {
buffer.writeToFile(outputFd);
buffer.writeToFile(outputFile);
}
}

// Documentation in NanoLog.h
void
RuntimeLogger::setLogFile_internal(const char *filename) {
// Check if it exists and is readable/writeable
if (access(filename, F_OK) == 0 && access(filename, R_OK | W_OK) != 0) {
std::string err = "Unable to read/write from new log file: ";
err.append(filename);
throw std::ios_base::failure(err);
}

// Try to open the file
int newFd = open(filename, NanoLogConfig::FILE_PARAMS, 0666);
if (newFd < 0) {
std::string err = "Unable to open file new log file: '";
err.append(filename);
err.append("': ");
err.append(strerror(errno));
FILE* newFile = fopen(filename, NanoLogConfig::FILE_PARAMS);
if (newFile == nullptr) {
std::string err;
if (errno == EACCES) {
err = "Unable to read/write from new log file: ";
err.append(filename);
} else {
err = "Unable to open file new log file: '";
err.append(filename);
err.append("': ");
err.append(strerror(errno));
}
throw std::ios_base::failure(err);
}
setvbuf(outputFile, nullptr, _IONBF, 0);

// Everything seems okay, stop the background thread and change files
sync();
Expand All @@ -614,9 +598,9 @@ RuntimeLogger::setLogFile_internal(const char *filename) {
if (writerThread.joinable())
writerThread.join();

if (outputFd > 0)
close(outputFd);
outputFd = newFd;
if (outputFile != nullptr)
fclose(outputFile);
outputFile = newFile;

// Relaunch thread
nextInvocationIndexToBePersisted = 0; // Reset the dictionary
Expand Down
7 changes: 2 additions & 5 deletions runtime/RuntimeLogger.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#define RUNTIME_NANOLOG_H

#include <cassert>
#include <cstdio>

#include <condition_variable>
#include <mutex>
Expand Down Expand Up @@ -235,7 +236,7 @@ using namespace NanoLog;

// File handle for the output file; should only be opened once at the
// construction of the LogCompressor
int outputFd;
FILE* outputFile;

// Buffer to stage compressed log message
DoubleBuffer buffer;
Expand Down Expand Up @@ -276,10 +277,6 @@ using namespace NanoLog;
// Metric: Number of bytes written to the output file (includes padding)
uint64_t totalBytesWritten;

// Metric: Number of pad bytes written to round the file to the nearest
// 512B
uint64_t padBytesWritten;

// Metric: Number of log statements compressed and outputted.
uint64_t logsProcessed;

Expand Down

0 comments on commit 22a348d

Please sign in to comment.