Line data Source code
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/. */
6 :
7 : #include "MainThreadIOLogger.h"
8 :
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 "nsAutoPtr.h"
15 : #include "nsNativeCharsetUtils.h"
16 : #include "nsThreadUtils.h"
17 :
18 : /**
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.
21 : */
22 :
23 : #include <prenv.h>
24 : #include <prprf.h>
25 : #include <prthread.h>
26 : #include <vector>
27 :
28 : namespace {
29 :
30 0 : struct ObservationWithStack
31 : {
32 0 : ObservationWithStack(mozilla::IOInterposeObserver::Observation& aObs,
33 : ProfilerBacktrace* aStack)
34 0 : : mObservation(aObs)
35 0 : , mStack(aStack)
36 : {
37 0 : const char16_t* filename = aObs.Filename();
38 0 : if (filename) {
39 0 : mFilename = filename;
40 : }
41 0 : }
42 :
43 : mozilla::IOInterposeObserver::Observation mObservation;
44 : ProfilerBacktrace* mStack;
45 : nsString mFilename;
46 : };
47 :
48 : class MainThreadIOLoggerImpl final : public mozilla::IOInterposeObserver
49 : {
50 : public:
51 : MainThreadIOLoggerImpl();
52 : ~MainThreadIOLoggerImpl();
53 :
54 : bool Init();
55 :
56 : void Observe(Observation& aObservation);
57 :
58 : private:
59 : static void sIOThreadFunc(void* aArg);
60 : void IOThreadFunc();
61 :
62 : TimeStamp mLogStartTime;
63 : const char* mFileName;
64 : PRThread* mIOThread;
65 : IOInterposer::Monitor mMonitor;
66 : bool mShutdownRequired;
67 : std::vector<ObservationWithStack> mObservations;
68 : };
69 :
70 3 : static StaticAutoPtr<MainThreadIOLoggerImpl> sImpl;
71 :
72 1 : MainThreadIOLoggerImpl::MainThreadIOLoggerImpl()
73 : : mFileName(nullptr)
74 : , mIOThread(nullptr)
75 1 : , mShutdownRequired(false)
76 : {
77 1 : }
78 :
79 3 : MainThreadIOLoggerImpl::~MainThreadIOLoggerImpl()
80 : {
81 1 : if (!mIOThread) {
82 1 : return;
83 : }
84 : {
85 : // Scope for lock
86 0 : IOInterposer::MonitorAutoLock lock(mMonitor);
87 0 : mShutdownRequired = true;
88 0 : lock.Notify();
89 : }
90 0 : PR_JoinThread(mIOThread);
91 0 : mIOThread = nullptr;
92 3 : }
93 :
94 : bool
95 1 : MainThreadIOLoggerImpl::Init()
96 : {
97 1 : if (mFileName) {
98 : // Already initialized
99 0 : return true;
100 : }
101 1 : mFileName = PR_GetEnv("MOZ_MAIN_THREAD_IO_LOG");
102 1 : if (!mFileName) {
103 : // Can't start
104 1 : return false;
105 : }
106 0 : mIOThread = PR_CreateThread(PR_USER_THREAD, &sIOThreadFunc, this,
107 : PR_PRIORITY_LOW, PR_GLOBAL_THREAD,
108 : PR_JOINABLE_THREAD, 0);
109 0 : if (!mIOThread) {
110 0 : return false;
111 : }
112 0 : return true;
113 : }
114 :
115 : /* static */ void
116 0 : MainThreadIOLoggerImpl::sIOThreadFunc(void* aArg)
117 : {
118 0 : AutoProfilerRegisterThread registerThread("MainThreadIOLogger");
119 0 : NS_SetCurrentThreadName("MainThreadIOLogger");
120 0 : MainThreadIOLoggerImpl* obj = static_cast<MainThreadIOLoggerImpl*>(aArg);
121 0 : obj->IOThreadFunc();
122 0 : }
123 :
124 : void
125 0 : MainThreadIOLoggerImpl::IOThreadFunc()
126 : {
127 0 : PRFileDesc* fd = PR_Open(mFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
128 0 : PR_IRUSR | PR_IWUSR | PR_IRGRP);
129 0 : if (!fd) {
130 0 : IOInterposer::MonitorAutoLock lock(mMonitor);
131 0 : mShutdownRequired = true;
132 0 : std::vector<ObservationWithStack>().swap(mObservations);
133 0 : return;
134 : }
135 0 : mLogStartTime = TimeStamp::Now();
136 : {
137 : // Scope for lock
138 0 : IOInterposer::MonitorAutoLock lock(mMonitor);
139 : while (true) {
140 0 : while (!mShutdownRequired && mObservations.empty()) {
141 0 : lock.Wait();
142 : }
143 0 : if (mShutdownRequired) {
144 0 : break;
145 : }
146 : // Pull events off the shared array onto a local one
147 0 : std::vector<ObservationWithStack> observationsToWrite;
148 0 : observationsToWrite.swap(mObservations);
149 :
150 : // Release the lock so that we're not holding anybody up during I/O
151 0 : IOInterposer::MonitorAutoUnlock unlock(mMonitor);
152 :
153 : // Now write the events.
154 0 : for (auto i = observationsToWrite.begin(), e = observationsToWrite.end();
155 : i != e; ++i) {
156 0 : if (i->mObservation.ObservedOperation() == OpNextStage) {
157 0 : PR_fprintf(fd, "%f,NEXT-STAGE\n",
158 0 : (TimeStamp::Now() - mLogStartTime).ToMilliseconds());
159 0 : continue;
160 : }
161 0 : double durationMs = i->mObservation.Duration().ToMilliseconds();
162 0 : nsAutoCString nativeFilename;
163 0 : nativeFilename.AssignLiteral("(not available)");
164 0 : if (!i->mFilename.IsEmpty()) {
165 0 : if (NS_FAILED(NS_CopyUnicodeToNative(i->mFilename, nativeFilename))) {
166 0 : nativeFilename.AssignLiteral("(conversion failed)");
167 : }
168 : }
169 : /**
170 : * Format:
171 : * Start Timestamp (Milliseconds), Operation, Duration (Milliseconds), Event Source, Filename
172 : */
173 0 : if (PR_fprintf(fd, "%f,%s,%f,%s,%s\n",
174 0 : (i->mObservation.Start() - mLogStartTime).ToMilliseconds(),
175 0 : i->mObservation.ObservedOperationString(), durationMs,
176 0 : i->mObservation.Reference(), nativeFilename.get()) > 0) {
177 : // TODO: Write out the callstack
178 0 : i->mStack = nullptr;
179 : }
180 : }
181 0 : }
182 : }
183 0 : PR_Close(fd);
184 : }
185 :
186 : void
187 0 : MainThreadIOLoggerImpl::Observe(Observation& aObservation)
188 : {
189 0 : if (!mFileName || !IsMainThread()) {
190 0 : return;
191 : }
192 0 : IOInterposer::MonitorAutoLock lock(mMonitor);
193 0 : if (mShutdownRequired) {
194 : // The writer thread isn't running. Don't enqueue any more data.
195 0 : return;
196 : }
197 : // Passing nullptr as aStack parameter for now
198 0 : mObservations.push_back(ObservationWithStack(aObservation, nullptr));
199 0 : lock.Notify();
200 : }
201 :
202 : } // namespace
203 :
204 : namespace mozilla {
205 :
206 : namespace MainThreadIOLogger {
207 :
208 : bool
209 1 : Init()
210 : {
211 2 : nsAutoPtr<MainThreadIOLoggerImpl> impl(new MainThreadIOLoggerImpl());
212 1 : if (!impl->Init()) {
213 1 : return false;
214 : }
215 0 : sImpl = impl.forget();
216 0 : IOInterposer::Register(IOInterposeObserver::OpAllWithStaging, sImpl);
217 0 : return true;
218 : }
219 :
220 : } // namespace MainThreadIOLogger
221 :
222 : } // namespace mozilla
223 :
|