Support using atomic-append logging for multiple file outputs

* The primary user is the main logfile itself, but it's useful for debugging
  purposes to be able to log to a file and still open it externally in a viewer
  without having the logging code take exclusive access to it.
This commit is contained in:
baldurk
2019-10-29 18:08:33 +00:00
parent 99934e799a
commit 253194c9a1
4 changed files with 66 additions and 52 deletions
+10 -9
View File
@@ -293,7 +293,7 @@ uint64_t Log2Floor(uint64_t value)
#endif
static std::string logfile;
static bool logfileOpened = false;
static FileIO::LogFileHandle *logfileHandle = NULL;
const char *rdclog_getfilename()
{
@@ -308,21 +308,22 @@ void rdclog_filename(const char *filename)
if(filename && filename[0])
logfile = filename;
FileIO::logfile_close(NULL);
FileIO::logfile_close(logfileHandle, NULL);
logfileOpened = false;
logfileHandle = NULL;
if(!logfile.empty())
{
logfileOpened = FileIO::logfile_open(logfile.c_str());
logfileHandle = FileIO::logfile_open(logfile.c_str());
if(logfileOpened && previous.c_str())
if(logfileHandle && previous.c_str())
{
std::vector<unsigned char> previousContents;
FileIO::slurp(previous.c_str(), previousContents);
if(!previousContents.empty())
FileIO::logfile_append((const char *)&previousContents[0], previousContents.size());
FileIO::logfile_append(logfileHandle, (const char *)&previousContents[0],
previousContents.size());
FileIO::Delete(previous.c_str());
}
@@ -339,7 +340,7 @@ void rdclog_enableoutput()
void rdclog_closelog(const char *filename)
{
log_output_enabled = false;
FileIO::logfile_close(filename);
FileIO::logfile_close(logfileHandle, filename);
}
void rdclog_flush()
@@ -366,10 +367,10 @@ void rdclogprint_int(LogType type, const char *fullMsg, const char *msg)
OSUtility::WriteOutput(OSUtility::Output_StdErr, msg);
#endif
#if ENABLED(OUTPUT_LOG_TO_DISK)
if(logfileOpened)
if(logfileHandle)
{
// strlen used as byte length - str is UTF-8 so this is NOT number of characters
FileIO::logfile_append(fullMsg, strlen(fullMsg));
FileIO::logfile_append(logfileHandle, fullMsg, strlen(fullMsg));
}
#endif
}
+4 -3
View File
@@ -311,9 +311,10 @@ int fclose(FILE *f);
// functions for atomically appending to a log that may be in use in multiple
// processes
bool logfile_open(const char *filename);
void logfile_append(const char *msg, size_t length);
void logfile_close(const char *filename);
struct LogFileHandle;
LogFileHandle *logfile_open(const char *filename);
void logfile_append(LogFileHandle *logHandle, const char *msg, size_t length);
void logfile_close(LogFileHandle *logHandle, const char *deleteFilename);
// read the whole logfile into memory. This may race with processes writing, but it will read the
// whole of the file at some point. Useful since normal file reading may fail on the shared logfile
+42 -26
View File
@@ -471,15 +471,15 @@ bool exists(const char *filename)
return (res == 0);
}
static int logfileFD = -1;
rdcarray<int> logfiles;
// this is used in posix_process.cpp, so that we can close the handle any time that we fork()
void ReleaseFDAfterFork()
{
// we do NOT release the shared lock here, since the file descriptor is shared so we'd be
// releasing the parent process's lock. Just close our file descriptor
if(logfileFD >= 0)
close(logfileFD);
for(int log : logfiles)
close(log);
}
std::string logfile_readall(const char *filename)
@@ -504,35 +504,49 @@ std::string logfile_readall(const char *filename)
return ret;
}
bool logfile_open(const char *filename)
LogFileHandle *logfile_open(const char *filename)
{
logfileFD = open(filename, O_APPEND | O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int fd = open(filename, O_APPEND | O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(fd < 0)
{
RDCWARN("Couldn't open logfile '%s': %d", filename, (int)errno);
return NULL;
}
logfiles.push_back(fd);
// acquire a shared lock. Every process acquires a shared lock to the common logfile. Each time a
// process shuts down and wants to close the logfile, it releases its shared lock and tries to
// acquire an exclusive lock, to see if it can delete the file. See logfile_close.
int err = flock(logfileFD, LOCK_SH | LOCK_NB);
int err = flock(fd, LOCK_SH | LOCK_NB);
if(err < 0)
RDCWARN("Couldn't acquire shared lock to %s: %d", filename, (int)errno);
RDCWARN("Couldn't acquire shared lock to '%s': %d", filename, (int)errno);
return logfileFD >= 0;
return (LogFileHandle *)(uintptr_t)fd;
}
void logfile_append(const char *msg, size_t length)
void logfile_append(LogFileHandle *logHandle, const char *msg, size_t length)
{
if(logfileFD >= 0)
write(logfileFD, msg, (unsigned int)length);
}
void logfile_close(const char *filename)
{
if(logfileFD >= 0)
if(logHandle)
{
// release our shared lock
int err = flock(logfileFD, LOCK_UN | LOCK_NB);
int fd = int(uintptr_t(logHandle) & 0xffffffff);
if(err == 0 && filename)
write(fd, msg, (unsigned int)length);
}
}
void logfile_close(LogFileHandle *logHandle, const char *deleteFilename)
{
if(logHandle)
{
int fd = int(uintptr_t(logHandle) & 0xffffffff);
// release our shared lock
int err = flock(fd, LOCK_UN | LOCK_NB);
if(err == 0 && deleteFilename)
{
// now try to acquire an exclusive lock. If this succeeds, no other processes are using the
// file (since no other shared locks exist), so we can delete it. If it fails, some other
@@ -540,20 +554,20 @@ void logfile_close(const char *filename)
// NOTE: there is a race here between acquiring the exclusive lock and unlinking, but we
// aren't interested in this kind of race - we're interested in whether an application is
// still running when the UI closes, or vice versa, or similar cases.
err = flock(logfileFD, LOCK_EX | LOCK_NB);
err = flock(fd, LOCK_EX | LOCK_NB);
if(err == 0)
{
// we got the exclusive lock. Now release it, close fd, and unlink the file
err = flock(logfileFD, LOCK_UN | LOCK_NB);
err = flock(fd, LOCK_UN | LOCK_NB);
// can't really error handle here apart from retrying
if(err != 0)
RDCWARN("Couldn't release exclusive lock to %s: %d", filename, (int)errno);
RDCWARN("Couldn't release exclusive lock to '%s': %d", deleteFilename, (int)errno);
close(logfileFD);
close(fd);
unlink(filename);
unlink(deleteFilename);
// return immediately so we don't close again below.
return;
@@ -561,12 +575,14 @@ void logfile_close(const char *filename)
}
else
{
RDCWARN("Couldn't release shared lock to %s: %d", filename, (int)errno);
RDCWARN("Couldn't release shared lock to '%s': %d", deleteFilename, (int)errno);
// nothing to do, we won't try again, just exit. The log might lie around, but that's
// relatively harmless.
}
close(logfileFD);
logfiles.removeOne(fd);
close(fd);
}
}
};
+10 -14
View File
@@ -662,15 +662,12 @@ int fclose(FILE *f)
return ::fclose(f);
}
static HANDLE logHandle = NULL;
bool logfile_open(const char *filename)
LogFileHandle *logfile_open(const char *filename)
{
std::wstring wfn = StringFormat::UTF82Wide(std::string(filename));
logHandle = CreateFileW(wfn.c_str(), FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
return logHandle != NULL;
return (LogFileHandle *)CreateFileW(wfn.c_str(), FILE_APPEND_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
}
static std::string logfile_readall_fallback(const wchar_t *filename)
@@ -747,25 +744,24 @@ std::string logfile_readall(const char *filename)
return ret;
}
void logfile_append(const char *msg, size_t length)
void logfile_append(LogFileHandle *logHandle, const char *msg, size_t length)
{
if(logHandle)
{
DWORD bytesWritten = 0;
WriteFile(logHandle, msg, (DWORD)length, &bytesWritten, NULL);
WriteFile((HANDLE)logHandle, msg, (DWORD)length, &bytesWritten, NULL);
}
}
void logfile_close(const char *filename)
void logfile_close(LogFileHandle *logHandle, const char *deleteFilename)
{
CloseHandle(logHandle);
logHandle = NULL;
CloseHandle((HANDLE)logHandle);
if(filename)
if(deleteFilename)
{
// we can just try to delete the file. If it's open elsewhere in another process, the delete
// will fail.
std::wstring wpath = StringFormat::UTF82Wide(std::string(filename));
std::wstring wpath = StringFormat::UTF82Wide(std::string(deleteFilename));
::DeleteFileW(wpath.c_str());
}
}