X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=common%2Ftrace_writer_local.cpp;h=c5a5c4447b2b4213bea2603f6fd905f554c67bca;hb=754120775a7e038d194ca2ef2a227c965ccd5ff7;hp=e560e498cb7a80dd92378db097fa6b7f8a7d6748;hpb=91392daed9dc6b4c94859dc38c729d91bd9efb40;p=apitrace diff --git a/common/trace_writer_local.cpp b/common/trace_writer_local.cpp index e560e49..c5a5c44 100644 --- a/common/trace_writer_local.cpp +++ b/common/trace_writer_local.cpp @@ -31,12 +31,15 @@ #include #include "os.hpp" +#include "os_thread.hpp" +#include "os_string.hpp" #include "trace_file.hpp" -#include "trace_writer.hpp" +#include "trace_writer_local.hpp" #include "trace_format.hpp" +#include "trace_backtrace.hpp" -namespace Trace { +namespace trace { static const char *memcpy_args[3] = {"dest", "src", "n"}; @@ -61,44 +64,52 @@ static void exceptionCallback(void) LocalWriter::LocalWriter() : acquired(0) { + os::log("apitrace: loaded\n"); + // Install the signal handlers as early as possible, to prevent // interfering with the application's signal handling. - OS::SetExceptionCallback(exceptionCallback); + os::setExceptionCallback(exceptionCallback); } LocalWriter::~LocalWriter() { - OS::ResetExceptionCallback(); + os::resetExceptionCallback(); + checkProcessId(); } void LocalWriter::open(void) { + os::String szFileName; - static unsigned dwCounter = 0; - - const char *szExtension = "trace"; - char szFileName[PATH_MAX]; const char *lpFileName; lpFileName = getenv("TRACE_FILE"); - if (lpFileName) { - strncpy(szFileName, lpFileName, PATH_MAX); - } - else { - char szProcessName[PATH_MAX]; - char szCurrentDir[PATH_MAX]; - OS::GetProcessName(szProcessName, PATH_MAX); - OS::GetCurrentDir(szCurrentDir, PATH_MAX); + if (!lpFileName) { + static unsigned dwCounter = 0; + + os::String process = os::getProcessName(); +#ifdef _WIN32 + process.trimExtension(); +#endif + process.trimDirectory(); + +#ifdef ANDROID + os::String prefix = "/data"; +#else + os::String prefix = os::getCurrentDir(); +#endif + prefix.join(process); for (;;) { FILE *file; if (dwCounter) - snprintf(szFileName, PATH_MAX, "%s%c%s.%u.%s", szCurrentDir, PATH_SEP, szProcessName, dwCounter, szExtension); + szFileName = os::String::format("%s.%u.trace", prefix.str(), dwCounter); else - snprintf(szFileName, PATH_MAX, "%s%c%s.%s", szCurrentDir, PATH_SEP, szProcessName, szExtension); + szFileName = os::String::format("%s.trace", prefix.str()); - file = fopen(szFileName, "rb"); + lpFileName = szFileName; + file = fopen(lpFileName, "rb"); if (file == NULL) break; @@ -108,9 +119,14 @@ LocalWriter::open(void) { } } - OS::DebugMessage("apitrace: tracing to %s\n", szFileName); + os::log("apitrace: tracing to %s\n", lpFileName); + + if (!Writer::open(lpFileName)) { + os::log("apitrace: error: failed to open %s\n", lpFileName); + os::abort(); + } - Writer::open(szFileName); + pid = os::getCurrentProcessId(); #if 0 // For debugging the exception handler @@ -118,25 +134,64 @@ LocalWriter::open(void) { #endif } -unsigned LocalWriter::beginEnter(const FunctionSig *sig) { - OS::AcquireMutex(); +static uintptr_t next_thread_num = 1; + +static OS_THREAD_SPECIFIC_PTR(void) +thread_num; + +void LocalWriter::checkProcessId(void) { + if (m_file->isOpened() && + os::getCurrentProcessId() != pid) { + // We are a forked child process that inherited the trace file, so + // create a new file. We can't call any method of the current + // file, as it may cause it to flush and corrupt the parent's + // trace, so we effectively leak the old file object. + m_file = File::createSnappy(); + // Don't want to open the same file again + os::unsetEnvironment("TRACE_FILE"); + open(); + } +} + +unsigned LocalWriter::beginEnter(const FunctionSig *sig, bool fake) { + mutex.lock(); ++acquired; + checkProcessId(); if (!m_file->isOpened()) { open(); } - return Writer::beginEnter(sig); + // Although thread_num is a void *, we actually use it as a uintptr_t + uintptr_t this_thread_num = + reinterpret_cast(static_cast(thread_num)); + if (!this_thread_num) { + this_thread_num = next_thread_num++; + thread_num = reinterpret_cast(this_thread_num); + } + + assert(this_thread_num); + unsigned thread_id = this_thread_num - 1; + unsigned call_no = Writer::beginEnter(sig, thread_id); + if (!fake && backtrace_is_needed(sig->name)) { + std::vector backtrace = get_backtrace(); + beginBacktrace(backtrace.size()); + for (unsigned i = 0; i < backtrace.size(); ++i) { + writeStackFrame(&backtrace[i]); + } + endBacktrace(); + } + return call_no; } void LocalWriter::endEnter(void) { Writer::endEnter(); --acquired; - OS::ReleaseMutex(); + mutex.unlock(); } void LocalWriter::beginLeave(unsigned call) { - OS::AcquireMutex(); + mutex.lock(); ++acquired; Writer::beginLeave(call); } @@ -144,28 +199,37 @@ void LocalWriter::beginLeave(unsigned call) { void LocalWriter::endLeave(void) { Writer::endLeave(); --acquired; - OS::ReleaseMutex(); + mutex.unlock(); } void LocalWriter::flush(void) { /* * Do nothing if the mutex is already acquired (e.g., if a segfault happen - * while writing the file) to prevent dead-lock. + * while writing the file) as state could be inconsistent, therefore yield + * inconsistent trace files and/or repeated segfaults till infinity. */ - if (!acquired) { - OS::AcquireMutex(); + mutex.lock(); + if (acquired) { + os::log("apitrace: ignoring exception while tracing\n"); + } else { + ++acquired; if (m_file->isOpened()) { - OS::DebugMessage("apitrace: flushing trace due to an exception\n"); - m_file->flush(); + if (os::getCurrentProcessId() != pid) { + os::log("apitrace: ignoring exception in child process\n"); + } else { + os::log("apitrace: flushing trace due to an exception\n"); + m_file->flush(); + } } - OS::ReleaseMutex(); + --acquired; } + mutex.unlock(); } LocalWriter localWriter; -} /* namespace Trace */ +} /* namespace trace */