mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-06 01:50:38 +00:00
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:
+39
-20
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user