Set all processes under a given UI instance to log to the same file.

* Using OS-specific append writing functions instead of simple fopen/
  fwrite/fclose we can guarantee that multiple processes writing to the
  same log file won't trash or interleave their log output.
* To make the logs readable the PID is now included along with the
  project/timestamp.
* After that, we make sure that when we launch a program we set the
  output log to the same file as the parent UI. This keeps all the logs
  together and removes user confusion over which logfile is needed.
* We also move all of RenderDoc's temporary files under a RenderDoc
  folder in the temp folder, instead of scattering the files in the root
This commit is contained in:
baldurk
2016-11-01 16:12:29 +01:00
parent aa4e4750ed
commit 8f4a55a3b5
11 changed files with 140 additions and 55 deletions
+39 -20
View File
@@ -252,24 +252,40 @@ uint64_t Log2Floor(uint64_t value)
}
#endif
static string &logfile()
{
// deliberately leak this so that it doesn't get destructed while we're still logging in process
// teardown.
static string *fn = new string;
return *fn;
}
static string logfile;
static void *logfileHandle = NULL;
const char *rdclog_getfilename()
{
return logfile().c_str();
return logfile.c_str();
}
void rdclog_filename(const char *filename)
{
logfile() = "";
string previous = logfile;
logfile = "";
if(filename && filename[0])
logfile() = filename;
logfile = filename;
FileIO::logfile_close(logfileHandle);
if(!logfile.empty())
{
logfileHandle = FileIO::logfile_open(filename);
if(logfileHandle && previous.c_str())
{
vector<unsigned char> previousContents;
FileIO::slurp(previous.c_str(), previousContents);
if(!previousContents.empty())
FileIO::logfile_append(logfileHandle, (const char *)&previousContents[0],
previousContents.size());
FileIO::Delete(previous.c_str());
}
}
}
static bool log_output_enabled = false;
@@ -279,6 +295,13 @@ void rdclog_enableoutput()
log_output_enabled = true;
}
void rdclog_closelog()
{
log_output_enabled = false;
if(logfileHandle)
FileIO::logfile_close(logfileHandle);
}
void rdclog_flush()
{
}
@@ -303,15 +326,10 @@ void rdclogprint_int(LogType type, const char *fullMsg, const char *msg)
OSUtility::WriteOutput(OSUtility::Output_StdErr, msg);
#endif
#if defined(OUTPUT_LOG_TO_DISK)
if(!logfile().empty())
if(logfileHandle)
{
FILE *f = FileIO::fopen(logfile().c_str(), "a");
if(f)
{
// strlen used as byte length - str is UTF-8 so this is NOT number of characters
FileIO::fwrite(fullMsg, 1, strlen(fullMsg), f);
FileIO::fclose(f);
}
// strlen used as byte length - str is UTF-8 so this is NOT number of characters
FileIO::logfile_append(logfileHandle, fullMsg, strlen(fullMsg));
}
#endif
}
@@ -356,8 +374,9 @@ void rdclog_int(LogType type, const char *project, const char *file, unsigned in
char *output = rdclog_outputBuffer;
size_t available = rdclog_outBufSize;
int numWritten = StringFormat::snprintf(output, available, "% 4s: %s%s%s - ", project, timestamp,
location, typestr[type]);
int numWritten =
StringFormat::snprintf(output, available, "% 4s %06u: %s%s%s - ", project,
Process::GetCurrentPID(), timestamp, location, typestr[type]);
if(numWritten < 0)
{
+2
View File
@@ -266,11 +266,13 @@ void rdclog_int(LogType type, const char *project, const char *file, unsigned in
const char *rdclog_getfilename();
void rdclog_filename(const char *filename);
void rdclog_enableoutput();
void rdclog_closelog();
#define RDCLOGFILE(fn) rdclog_filename(fn)
#define RDCGETLOGFILE() rdclog_getfilename()
#define RDCLOGOUTPUT() rdclog_enableoutput()
#define RDCSTOPLOGGING() rdclog_closelog()
#if(!defined(RELEASE) || defined(FORCE_DEBUG_LOGS)) && !defined(STRIP_DEBUG_LOGS)
#define RDCDEBUG(...) rdclog(RDCLog_Debug, __VA_ARGS__)
+3 -3
View File
@@ -292,15 +292,13 @@ void RenderDoc::Initialise()
const char *base = "RenderDoc_app";
if(IsReplayApp())
base = "RenderDoc_replay";
base = "RenderDoc";
FileIO::GetDefaultFiles(base, capture_filename, m_LoggingFilename, m_Target);
if(m_LogFile.empty())
SetLogFile(capture_filename.c_str());
string existingLog = RDCGETLOGFILE();
FileIO::Copy(existingLog.c_str(), m_LoggingFilename.c_str(), true);
RDCLOGFILE(m_LoggingFilename.c_str());
}
@@ -359,6 +357,8 @@ RenderDoc::~RenderDoc()
}
}
RDCSTOPLOGGING();
FileIO::Delete(m_LoggingFilename.c_str());
if(m_RemoteThread)
+1 -1
View File
@@ -51,7 +51,7 @@ public:
GetTempPathW(MAX_PATH - 1, tempPath);
wstring dumpFolder = tempPath;
dumpFolder += L"RenderDocDumps";
dumpFolder += L"RenderDoc/dumps";
CreateDirectoryW(dumpFolder.c_str(), NULL);
+2 -8
View File
@@ -42,16 +42,10 @@ void LibraryHooks::CreateHooks()
HOOKS_BEGIN();
for(auto it = m_Hooks.begin(); it != m_Hooks.end(); ++it)
{
RDCDEBUG("Attempting to hook %s", it->first);
RDCDEBUG("Hooking %s", it->first);
if(it->second->CreateHooks(it->first))
{
RDCLOG("Loaded and hooked into %s, PID %d", it->first, Process::GetCurrentPID());
}
else
{
if(!it->second->CreateHooks(it->first))
RDCWARN("Couldn't hook into %s", it->first);
}
}
HOOKS_END();
}
+6
View File
@@ -307,6 +307,12 @@ bool feof(FILE *f);
int fclose(FILE *f);
// functions for atomically appending to a log that may be in use in multiple
// processes
void *logfile_open(const char *filename);
void logfile_append(void *handle, const char *msg, size_t length);
void logfile_close(void *handle);
// utility functions
inline bool dump(const char *filename, const void *buffer, size_t size)
{
+2
View File
@@ -421,6 +421,8 @@ uint32_t Process::LaunchAndInjectIntoProcess(const char *app, const char *workin
EnvironmentModification(eEnvModification_Replace, "RENDERDOC_LOGFILE", logfile));
modifications.push_back(
EnvironmentModification(eEnvModification_Replace, "RENDERDOC_CAPTUREOPTS", optstr.c_str()));
modifications.push_back(EnvironmentModification(eEnvModification_Replace,
"RENDERDOC_DEBUG_LOG_FILE", RDCGETLOGFILE()));
for(size_t i = 0; i < modifications.size(); i++)
{
+35 -5
View File
@@ -25,6 +25,7 @@
#include <dirent.h>
#include <dlfcn.h> // for dladdr
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
@@ -168,17 +169,22 @@ void GetDefaultFiles(const char *logBaseName, string &capture_filename, string &
char temp_filename[2048] = {0};
snprintf(temp_filename, sizeof(temp_filename) - 1, "%s/%s_%04d.%02d.%02d_%02d.%02d.rdc",
snprintf(temp_filename, sizeof(temp_filename) - 1, "%s/RenderDoc/%s_%04d.%02d.%02d_%02d.%02d.rdc",
temp_folder, mod, 1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour,
now.tm_min);
capture_filename = string(temp_filename);
snprintf(temp_filename, sizeof(temp_filename) - 1, "%s/%s_%04d.%02d.%02d_%02d.%02d.%02d.log",
temp_folder, logBaseName, 1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour,
now.tm_min, now.tm_sec);
snprintf(temp_filename, sizeof(temp_filename) - 1,
"%s/RenderDoc/%s_%04d.%02d.%02d_%02d.%02d.%02d.log", temp_folder, logBaseName,
1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec);
logging_filename = string(temp_filename);
// set by UI when launching programs so all logging goes to the same file
char *logfile_override = getenv("RENDERDOC_DEBUG_LOG_FILE");
if(logfile_override)
logging_filename = string(logfile_override);
else
logging_filename = string(temp_filename);
}
uint64_t GetModifiedTimestamp(const string &filename)
@@ -361,6 +367,30 @@ int fclose(FILE *f)
{
return ::fclose(f);
}
void *logfile_open(const char *filename)
{
int fd = open(filename, O_APPEND | O_WRONLY | O_CREAT, S_IWRITE);
return (void *)(intptr_t)fd;
}
void logfile_append(void *handle, const char *msg, size_t length)
{
if(handle)
{
int fd = ((intptr_t)handle & 0xffffffff);
write(fd, msg, (unsigned int)length);
}
}
void logfile_close(void *handle)
{
if(handle)
{
int fd = ((intptr_t)handle & 0xffffffff);
close(fd);
}
}
};
namespace StringFormat
+26 -15
View File
@@ -154,39 +154,45 @@ void Process::ApplyEnvironmentModification()
}
// helpers for various shims and dlls etc, not part of the public API
extern "C" __declspec(dllexport) void __cdecl RENDERDOC_GetTargetControlIdent(uint32_t *ident)
extern "C" __declspec(dllexport) void __cdecl INTERNAL_GetTargetControlIdent(uint32_t *ident)
{
if(ident)
*ident = RenderDoc::Inst().GetTargetControlIdent();
}
extern "C" __declspec(dllexport) void __cdecl RENDERDOC_SetCaptureOptions(CaptureOptions *opts)
extern "C" __declspec(dllexport) void __cdecl INTERNAL_SetCaptureOptions(CaptureOptions *opts)
{
if(opts)
RenderDoc::Inst().SetCaptureOptions(*opts);
}
extern "C" __declspec(dllexport) void __cdecl RENDERDOC_SetLogFile(const char *log)
extern "C" __declspec(dllexport) void __cdecl INTERNAL_SetLogFile(const char *log)
{
if(log)
RenderDoc::Inst().SetLogFile(log);
}
extern "C" __declspec(dllexport) void __cdecl INTERNAL_SetDebugLogFile(const char *log)
{
if(log)
RDCLOGFILE(log);
}
static Process::EnvironmentModification tempEnvMod;
extern "C" __declspec(dllexport) void __cdecl RENDERDOC_EnvModName(const char *name)
extern "C" __declspec(dllexport) void __cdecl INTERNAL_EnvModName(const char *name)
{
if(name)
tempEnvMod.name = name;
}
extern "C" __declspec(dllexport) void __cdecl RENDERDOC_EnvModValue(const char *value)
extern "C" __declspec(dllexport) void __cdecl INTERNAL_EnvModValue(const char *value)
{
if(value)
tempEnvMod.value = value;
}
extern "C" __declspec(dllexport) void __cdecl RENDERDOC_EnvMod(Process::ModificationType *type)
extern "C" __declspec(dllexport) void __cdecl INTERNAL_EnvMod(Process::ModificationType *type)
{
if(type)
{
@@ -195,7 +201,7 @@ extern "C" __declspec(dllexport) void __cdecl RENDERDOC_EnvMod(Process::Modifica
}
}
extern "C" __declspec(dllexport) void __cdecl RENDERDOC_ApplyEnvMods(void *ignored)
extern "C" __declspec(dllexport) void __cdecl INTERNAL_ApplyEnvMods(void *ignored)
{
Process::ApplyEnvironmentModification();
}
@@ -701,13 +707,18 @@ uint32_t Process::InjectIntoProcess(uint32_t pid, EnvironmentModification *env,
// safe to cast away the const as we know these functions don't modify the parameters
if(logfile != NULL)
InjectFunctionCall(hProcess, loc, "RENDERDOC_SetLogFile", (void *)logfile, strlen(logfile) + 1);
InjectFunctionCall(hProcess, loc, "INTERNAL_SetLogFile", (void *)logfile, strlen(logfile) + 1);
std::string debugLogfile = RDCGETLOGFILE();
InjectFunctionCall(hProcess, loc, "INTERNAL_SetDebugLogFile", (void *)debugLogfile.c_str(),
debugLogfile.size() + 1);
if(opts != NULL)
InjectFunctionCall(hProcess, loc, "RENDERDOC_SetCaptureOptions", (CaptureOptions *)opts,
InjectFunctionCall(hProcess, loc, "INTERNAL_SetCaptureOptions", (CaptureOptions *)opts,
sizeof(CaptureOptions));
InjectFunctionCall(hProcess, loc, "RENDERDOC_GetTargetControlIdent", &controlident,
InjectFunctionCall(hProcess, loc, "INTERNAL_GetTargetControlIdent", &controlident,
sizeof(controlident));
if(env)
@@ -721,17 +732,17 @@ uint32_t Process::InjectIntoProcess(uint32_t pid, EnvironmentModification *env,
if(name == "")
break;
InjectFunctionCall(hProcess, loc, "RENDERDOC_EnvModName", (void *)name.c_str(),
InjectFunctionCall(hProcess, loc, "INTERNAL_EnvModName", (void *)name.c_str(),
name.size() + 1);
InjectFunctionCall(hProcess, loc, "RENDERDOC_EnvModValue", (void *)value.c_str(),
InjectFunctionCall(hProcess, loc, "INTERNAL_EnvModValue", (void *)value.c_str(),
value.size() + 1);
InjectFunctionCall(hProcess, loc, "RENDERDOC_EnvMod", &type, sizeof(type));
InjectFunctionCall(hProcess, loc, "INTERNAL_EnvMod", &type, sizeof(type));
env++;
}
// parameter is unused
InjectFunctionCall(hProcess, loc, "RENDERDOC_ApplyEnvMods", env,
InjectFunctionCall(hProcess, loc, "INTERNAL_ApplyEnvMods", env,
sizeof(EnvironmentModification));
}
}
@@ -802,7 +813,7 @@ uint32_t Process::LaunchAndInjectIntoProcess(const char *app, const char *workin
bool waitForExit)
{
void *func =
GetProcAddress(GetModuleHandleA(STRINGIZE(RDOC_DLL_FILE) ".dll"), "RENDERDOC_SetLogFile");
GetProcAddress(GetModuleHandleA(STRINGIZE(RDOC_DLL_FILE) ".dll"), "INTERNAL_SetLogFile");
if(func == NULL)
{
+23 -2
View File
@@ -270,7 +270,7 @@ void GetDefaultFiles(const char *logBaseName, string &capture_filename, string &
wchar_t *filename_start = temp_filename + wcslen(temp_filename);
wsprintf(filename_start, L"%ls_%04d.%02d.%02d_%02d.%02d.rdc", mod, 1900 + now.tm_year,
wsprintf(filename_start, L"RenderDoc\\%ls_%04d.%02d.%02d_%02d.%02d.rdc", mod, 1900 + now.tm_year,
now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min);
capture_filename = StringFormat::Wide2UTF8(wstring(temp_filename));
@@ -279,7 +279,7 @@ void GetDefaultFiles(const char *logBaseName, string &capture_filename, string &
wstring wbase = StringFormat::UTF82Wide(string(logBaseName));
wsprintf(filename_start, L"%ls_%04d.%02d.%02d_%02d.%02d.%02d.log", wbase.c_str(),
wsprintf(filename_start, L"RenderDoc\\%ls_%04d.%02d.%02d_%02d.%02d.%02d.log", wbase.c_str(),
1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec);
logging_filename = StringFormat::Wide2UTF8(wstring(temp_filename));
@@ -500,6 +500,27 @@ int fclose(FILE *f)
{
return ::fclose(f);
}
void *logfile_open(const char *filename)
{
wstring wfn = StringFormat::UTF82Wide(string(filename));
return (void *)CreateFileW(wfn.c_str(), FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}
void logfile_append(void *handle, const char *msg, size_t length)
{
if(handle)
{
DWORD bytesWritten = 0;
WriteFile((HANDLE)handle, msg, (DWORD)length, &bytesWritten, NULL);
}
}
void logfile_close(void *handle)
{
CloseHandle((HANDLE)handle);
}
};
namespace StringFormat
+1 -1
View File
@@ -506,7 +506,7 @@ struct CrashHandlerCommand : public Command
Sleep(100);
wstring dumpFolder = tempPath;
dumpFolder += L"RenderDocDumps";
dumpFolder += L"RenderDoc/dumps";
CreateDirectoryW(dumpFolder.c_str(), NULL);