1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "MainThreadIOLogger.h"
9 #include "GeckoProfiler.h"
10 #include "IOInterposerPrivate.h"
11 #include "mozilla/IOInterposer.h"
12 #include "mozilla/StaticPtr.h"
13 #include "mozilla/TimeStamp.h"
14 #include "mozilla/UniquePtr.h"
15 #include "nsNativeCharsetUtils.h"
16 #include "nsThreadUtils.h"
19 * This code uses NSPR stuff and STL containers because it must be detached
20 * from leak checking code; this observer runs until the process terminates.
30 struct ObservationWithStack
{
31 explicit ObservationWithStack(mozilla::IOInterposeObserver::Observation
& aObs
,
32 ProfilerBacktrace
* aStack
)
33 : mObservation(aObs
), mStack(aStack
) {
34 aObs
.Filename(mFilename
);
37 mozilla::IOInterposeObserver::Observation mObservation
;
38 ProfilerBacktrace
* mStack
;
42 class MainThreadIOLoggerImpl final
: public mozilla::IOInterposeObserver
{
44 MainThreadIOLoggerImpl();
45 ~MainThreadIOLoggerImpl();
49 void Observe(Observation
& aObservation
) override
;
52 static void sIOThreadFunc(void* aArg
);
55 mozilla::TimeStamp mLogStartTime
;
56 const char* mFileName
;
58 mozilla::IOInterposer::Monitor mMonitor
;
59 bool mShutdownRequired
;
60 std::vector
<ObservationWithStack
> mObservations
;
63 static mozilla::StaticAutoPtr
<MainThreadIOLoggerImpl
> sImpl
;
65 MainThreadIOLoggerImpl::MainThreadIOLoggerImpl()
66 : mFileName(nullptr), mIOThread(nullptr), mShutdownRequired(false) {}
68 MainThreadIOLoggerImpl::~MainThreadIOLoggerImpl() {
74 mozilla::IOInterposer::MonitorAutoLock
lock(mMonitor
);
75 mShutdownRequired
= true;
78 PR_JoinThread(mIOThread
);
82 bool MainThreadIOLoggerImpl::Init() {
84 // Already initialized
87 mFileName
= PR_GetEnv("MOZ_MAIN_THREAD_IO_LOG");
93 PR_CreateThread(PR_USER_THREAD
, &sIOThreadFunc
, this, PR_PRIORITY_LOW
,
94 PR_GLOBAL_THREAD
, PR_JOINABLE_THREAD
, 0);
102 void MainThreadIOLoggerImpl::sIOThreadFunc(void* aArg
) {
103 AUTO_PROFILER_REGISTER_THREAD("MainThreadIOLogger");
105 NS_SetCurrentThreadName("MainThreadIOLogger");
106 MainThreadIOLoggerImpl
* obj
= static_cast<MainThreadIOLoggerImpl
*>(aArg
);
110 void MainThreadIOLoggerImpl::IOThreadFunc() {
111 PRFileDesc
* fd
= PR_Open(mFileName
, PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
,
112 PR_IRUSR
| PR_IWUSR
| PR_IRGRP
);
114 mozilla::IOInterposer::MonitorAutoLock
lock(mMonitor
);
115 mShutdownRequired
= true;
116 std::vector
<ObservationWithStack
>().swap(mObservations
);
119 mLogStartTime
= mozilla::TimeStamp::Now();
122 mozilla::IOInterposer::MonitorAutoLock
lock(mMonitor
);
124 while (!mShutdownRequired
&& mObservations
.empty()) {
127 if (mShutdownRequired
) {
130 // Pull events off the shared array onto a local one
131 std::vector
<ObservationWithStack
> observationsToWrite
;
132 observationsToWrite
.swap(mObservations
);
134 // Release the lock so that we're not holding anybody up during I/O
135 mozilla::IOInterposer::MonitorAutoUnlock
unlock(mMonitor
);
137 // Now write the events.
138 for (auto i
= observationsToWrite
.begin(), e
= observationsToWrite
.end();
140 if (i
->mObservation
.ObservedOperation() == OpNextStage
) {
142 fd
, "%f,NEXT-STAGE\n",
143 (mozilla::TimeStamp::Now() - mLogStartTime
).ToMilliseconds());
146 double durationMs
= i
->mObservation
.Duration().ToMilliseconds();
147 nsAutoCString nativeFilename
;
148 nativeFilename
.AssignLiteral("(not available)");
149 if (!i
->mFilename
.IsEmpty()) {
150 if (NS_FAILED(NS_CopyUnicodeToNative(i
->mFilename
, nativeFilename
))) {
151 nativeFilename
.AssignLiteral("(conversion failed)");
157 * Start Timestamp (Milliseconds), Operation, Duration (Milliseconds), Event Source, Filename
161 fd
, "%f,%s,%f,%s,%s\n",
162 (i
->mObservation
.Start() - mLogStartTime
).ToMilliseconds(),
163 i
->mObservation
.ObservedOperationString(), durationMs
,
164 i
->mObservation
.Reference(), nativeFilename
.get()) > 0) {
165 // TODO: Write out the callstack
174 void MainThreadIOLoggerImpl::Observe(Observation
& aObservation
) {
175 if (!mFileName
|| !IsMainThread()) {
178 mozilla::IOInterposer::MonitorAutoLock
lock(mMonitor
);
179 if (mShutdownRequired
) {
180 // The writer thread isn't running. Don't enqueue any more data.
183 // Passing nullptr as aStack parameter for now
184 mObservations
.push_back(ObservationWithStack(aObservation
, nullptr));
192 namespace MainThreadIOLogger
{
195 auto impl
= MakeUnique
<MainThreadIOLoggerImpl
>();
199 sImpl
= impl
.release();
200 IOInterposer::Register(IOInterposeObserver::OpAllWithStaging
, sImpl
);
204 } // namespace MainThreadIOLogger
206 } // namespace mozilla