#include <string.h>
#include "os.hpp"
-#include "os_path.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 {
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);
LocalWriter::~LocalWriter()
{
os::resetExceptionCallback();
+ checkProcessId();
}
void
LocalWriter::open(void) {
- os::Path szFileName;
+ os::String szFileName;
const char *lpFileName;
if (!lpFileName) {
static unsigned dwCounter = 0;
- os::Path process = os::getProcessName();
+ os::String process = os::getProcessName();
#ifdef _WIN32
process.trimExtension();
#endif
process.trimDirectory();
- os::Path prefix = os::getCurrentDir();
+#ifdef ANDROID
+ os::String prefix = "/data";
+#else
+ os::String prefix = os::getCurrentDir();
+#endif
prefix.join(process);
for (;;) {
FILE *file;
if (dwCounter)
- szFileName = os::Path::format("%s.%u.trace", prefix.str(), dwCounter);
+ szFileName = os::String::format("%s.%u.trace", prefix.str(), dwCounter);
else
- szFileName = os::Path::format("%s.trace", prefix.str());
+ szFileName = os::String::format("%s.trace", prefix.str());
lpFileName = szFileName;
file = fopen(lpFileName, "rb");
os::log("apitrace: tracing to %s\n", lpFileName);
- Writer::open(lpFileName);
+ if (!Writer::open(lpFileName)) {
+ os::log("apitrace: error: failed to open %s\n", lpFileName);
+ os::abort();
+ }
+
+ pid = os::getCurrentProcessId();
#if 0
// For debugging the exception handler
#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<uintptr_t>(static_cast<void *>(thread_num));
+ if (!this_thread_num) {
+ this_thread_num = next_thread_num++;
+ thread_num = reinterpret_cast<void *>(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<RawStackFrame> 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);
}
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::log("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();
}