1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/build/MainThreadIOLogger.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,223 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "MainThreadIOLogger.h" 1.11 + 1.12 +#include "GeckoProfiler.h" 1.13 +#include "IOInterposerPrivate.h" 1.14 +#include "mozilla/IOInterposer.h" 1.15 +#include "mozilla/StaticPtr.h" 1.16 +#include "mozilla/TimeStamp.h" 1.17 +#include "nsAutoPtr.h" 1.18 + 1.19 +/** 1.20 + * This code uses NSPR stuff and STL containers because it must be detached 1.21 + * from leak checking code; this observer runs until the process terminates. 1.22 + */ 1.23 + 1.24 +#include <prenv.h> 1.25 +#include <prprf.h> 1.26 +#include <prthread.h> 1.27 +#include <vector> 1.28 + 1.29 +namespace { 1.30 + 1.31 +struct ObservationWithStack 1.32 +{ 1.33 + ObservationWithStack(mozilla::IOInterposeObserver::Observation& aObs, 1.34 + ProfilerBacktrace *aStack) 1.35 + : mObservation(aObs) 1.36 + , mStack(aStack) 1.37 + { 1.38 + const char16_t* filename = aObs.Filename(); 1.39 + if (filename) { 1.40 + mFilename = filename; 1.41 + } 1.42 + } 1.43 + 1.44 + mozilla::IOInterposeObserver::Observation mObservation; 1.45 + ProfilerBacktrace* mStack; 1.46 + nsString mFilename; 1.47 +}; 1.48 + 1.49 +} // anonymous namespace 1.50 + 1.51 +namespace mozilla { 1.52 + 1.53 +class MainThreadIOLoggerImpl MOZ_FINAL : public IOInterposeObserver 1.54 +{ 1.55 +public: 1.56 + MainThreadIOLoggerImpl(); 1.57 + ~MainThreadIOLoggerImpl(); 1.58 + 1.59 + bool Init(); 1.60 + 1.61 + void Observe(Observation& aObservation); 1.62 + 1.63 +private: 1.64 + static void sIOThreadFunc(void* aArg); 1.65 + void IOThreadFunc(); 1.66 + 1.67 + TimeStamp mLogStartTime; 1.68 + const char* mFileName; 1.69 + PRThread* mIOThread; 1.70 + IOInterposer::Monitor mMonitor; 1.71 + bool mShutdownRequired; 1.72 + std::vector<ObservationWithStack> mObservations; 1.73 +}; 1.74 + 1.75 +static StaticAutoPtr<MainThreadIOLoggerImpl> sImpl; 1.76 + 1.77 +MainThreadIOLoggerImpl::MainThreadIOLoggerImpl() 1.78 + : mFileName(nullptr) 1.79 + , mIOThread(nullptr) 1.80 + , mShutdownRequired(false) 1.81 +{ 1.82 +} 1.83 + 1.84 +MainThreadIOLoggerImpl::~MainThreadIOLoggerImpl() 1.85 +{ 1.86 + if (!mIOThread) { 1.87 + return; 1.88 + } 1.89 + { // Scope for lock 1.90 + IOInterposer::MonitorAutoLock lock(mMonitor); 1.91 + mShutdownRequired = true; 1.92 + lock.Notify(); 1.93 + } 1.94 + PR_JoinThread(mIOThread); 1.95 + mIOThread = nullptr; 1.96 +} 1.97 + 1.98 +bool 1.99 +MainThreadIOLoggerImpl::Init() 1.100 +{ 1.101 + if (mFileName) { 1.102 + // Already initialized 1.103 + return true; 1.104 + } 1.105 + mFileName = PR_GetEnv("MOZ_MAIN_THREAD_IO_LOG"); 1.106 + if (!mFileName) { 1.107 + // Can't start 1.108 + return false; 1.109 + } 1.110 + mIOThread = PR_CreateThread(PR_USER_THREAD, &sIOThreadFunc, this, 1.111 + PR_PRIORITY_LOW, PR_GLOBAL_THREAD, 1.112 + PR_JOINABLE_THREAD, 0); 1.113 + if (!mIOThread) { 1.114 + return false; 1.115 + } 1.116 + return true; 1.117 +} 1.118 + 1.119 +/* static */ void 1.120 +MainThreadIOLoggerImpl::sIOThreadFunc(void* aArg) 1.121 +{ 1.122 + PR_SetCurrentThreadName("MainThreadIOLogger"); 1.123 + MainThreadIOLoggerImpl* obj = static_cast<MainThreadIOLoggerImpl*>(aArg); 1.124 + obj->IOThreadFunc(); 1.125 +} 1.126 + 1.127 +void 1.128 +MainThreadIOLoggerImpl::IOThreadFunc() 1.129 +{ 1.130 + PRFileDesc* fd = PR_Open(mFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 1.131 + PR_IRUSR | PR_IWUSR | PR_IRGRP); 1.132 + if (!fd) { 1.133 + IOInterposer::MonitorAutoLock lock(mMonitor); 1.134 + mShutdownRequired = true; 1.135 + std::vector<ObservationWithStack>().swap(mObservations); 1.136 + return; 1.137 + } 1.138 + mLogStartTime = TimeStamp::Now(); 1.139 + { // Scope for lock 1.140 + IOInterposer::MonitorAutoLock lock(mMonitor); 1.141 + while (true) { 1.142 + while (!mShutdownRequired && mObservations.empty()) { 1.143 + lock.Wait(); 1.144 + } 1.145 + if (mShutdownRequired) { 1.146 + break; 1.147 + } 1.148 + // Pull events off the shared array onto a local one 1.149 + std::vector<ObservationWithStack> observationsToWrite; 1.150 + observationsToWrite.swap(mObservations); 1.151 + 1.152 + // Release the lock so that we're not holding anybody up during I/O 1.153 + IOInterposer::MonitorAutoUnlock unlock(mMonitor); 1.154 + 1.155 + // Now write the events. 1.156 + for (std::vector<ObservationWithStack>::iterator 1.157 + i = observationsToWrite.begin(), e = observationsToWrite.end(); 1.158 + i != e; ++i) { 1.159 + if (i->mObservation.ObservedOperation() == OpNextStage) { 1.160 + PR_fprintf(fd, "%f,NEXT-STAGE\n", 1.161 + (TimeStamp::Now() - mLogStartTime).ToMilliseconds()); 1.162 + continue; 1.163 + } 1.164 + double durationMs = i->mObservation.Duration().ToMilliseconds(); 1.165 + nsAutoCString nativeFilename; 1.166 + nativeFilename.AssignLiteral("(not available)"); 1.167 + if (!i->mFilename.IsEmpty()) { 1.168 + if (NS_FAILED(NS_CopyUnicodeToNative(i->mFilename, nativeFilename))) { 1.169 + nativeFilename.AssignLiteral("(conversion failed)"); 1.170 + } 1.171 + } 1.172 + /** 1.173 + * Format: 1.174 + * Start Timestamp (Milliseconds), Operation, Duration (Milliseconds), Event Source, Filename 1.175 + */ 1.176 + if (PR_fprintf(fd, "%f,%s,%f,%s,%s\n", 1.177 + (i->mObservation.Start() - mLogStartTime).ToMilliseconds(), 1.178 + i->mObservation.ObservedOperationString(), durationMs, 1.179 + i->mObservation.Reference(), nativeFilename.get()) > 0) { 1.180 + ProfilerBacktrace* stack = i->mStack; 1.181 + if (stack) { 1.182 + // TODO: Write out the callstack 1.183 + // (This will be added in a later bug) 1.184 + profiler_free_backtrace(stack); 1.185 + } 1.186 + } 1.187 + } 1.188 + } 1.189 + } 1.190 + PR_Close(fd); 1.191 +} 1.192 + 1.193 +void 1.194 +MainThreadIOLoggerImpl::Observe(Observation& aObservation) 1.195 +{ 1.196 + if (!mFileName || !IsMainThread()) { 1.197 + return; 1.198 + } 1.199 + IOInterposer::MonitorAutoLock lock(mMonitor); 1.200 + if (mShutdownRequired) { 1.201 + // The writer thread isn't running. Don't enqueue any more data. 1.202 + return; 1.203 + } 1.204 + // Passing nullptr as aStack parameter for now 1.205 + mObservations.push_back(ObservationWithStack(aObservation, nullptr)); 1.206 + lock.Notify(); 1.207 +} 1.208 + 1.209 +namespace MainThreadIOLogger { 1.210 + 1.211 +bool 1.212 +Init() 1.213 +{ 1.214 + nsAutoPtr<MainThreadIOLoggerImpl> impl(new MainThreadIOLoggerImpl()); 1.215 + if (!impl->Init()) { 1.216 + return false; 1.217 + } 1.218 + sImpl = impl.forget(); 1.219 + IOInterposer::Register(IOInterposeObserver::OpAllWithStaging, sImpl); 1.220 + return true; 1.221 +} 1.222 + 1.223 +} // namespace MainThreadIOLogger 1.224 + 1.225 +} // namespace mozilla 1.226 +