Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; 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 "ThreadInfo.h"
8 :
9 : #include "mozilla/DebugOnly.h"
10 :
11 : #if defined(GP_OS_darwin)
12 : #include <pthread.h>
13 : #endif
14 :
15 : #ifdef XP_WIN
16 : #include <process.h>
17 : #define getpid _getpid
18 : #else
19 : #include <unistd.h> // for getpid()
20 : #endif
21 :
22 75 : ThreadInfo::ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread,
23 75 : void* aStackTop)
24 : : mName(strdup(aName))
25 : , mThreadId(aThreadId)
26 : , mIsMainThread(aIsMainThread)
27 75 : , mRacyInfo(mozilla::WrapNotNull(new RacyThreadInfo()))
28 : , mPlatformData(AllocPlatformData(aThreadId))
29 : , mStackTop(aStackTop)
30 : , mIsBeingProfiled(false)
31 : , mContext(nullptr)
32 : , mJSSampling(INACTIVE)
33 150 : , mLastSample()
34 : {
35 75 : MOZ_COUNT_CTOR(ThreadInfo);
36 :
37 : // We don't have to guess on mac
38 : #if defined(GP_OS_darwin)
39 : pthread_t self = pthread_self();
40 : mStackTop = pthread_get_stackaddr_np(self);
41 : #endif
42 :
43 : // I don't know if we can assert this. But we should warn.
44 75 : MOZ_ASSERT(aThreadId >= 0, "native thread ID is < 0");
45 : MOZ_ASSERT(aThreadId <= INT32_MAX, "native thread ID is > INT32_MAX");
46 75 : }
47 :
48 2 : ThreadInfo::~ThreadInfo()
49 : {
50 1 : MOZ_COUNT_DTOR(ThreadInfo);
51 :
52 1 : delete mRacyInfo;
53 1 : }
54 :
55 : void
56 0 : ThreadInfo::StartProfiling()
57 : {
58 0 : mIsBeingProfiled = true;
59 0 : mRacyInfo->ReinitializeOnResume();
60 0 : if (mIsMainThread) {
61 0 : mResponsiveness.emplace();
62 : }
63 0 : }
64 :
65 : void
66 0 : ThreadInfo::StopProfiling()
67 : {
68 0 : mResponsiveness.reset();
69 0 : mIsBeingProfiled = false;
70 0 : }
71 :
72 : void
73 0 : ThreadInfo::StreamJSON(const ProfileBuffer& aBuffer,
74 : SpliceableJSONWriter& aWriter,
75 : const TimeStamp& aProcessStartTime, double aSinceTime)
76 : {
77 : // mUniqueStacks may already be emplaced from FlushSamplesAndMarkers.
78 0 : if (!mUniqueStacks.isSome()) {
79 0 : mUniqueStacks.emplace(mContext);
80 : }
81 :
82 0 : aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
83 : {
84 0 : StreamSamplesAndMarkers(Name(), ThreadId(), aBuffer, aWriter,
85 : aProcessStartTime, aSinceTime, mContext,
86 : mSavedStreamedSamples.get(),
87 0 : mSavedStreamedMarkers.get(), *mUniqueStacks);
88 0 : mSavedStreamedSamples.reset();
89 0 : mSavedStreamedMarkers.reset();
90 :
91 0 : aWriter.StartObjectProperty("stackTable");
92 : {
93 : {
94 0 : JSONSchemaWriter schema(aWriter);
95 0 : schema.WriteField("prefix");
96 0 : schema.WriteField("frame");
97 : }
98 :
99 0 : aWriter.StartArrayProperty("data");
100 : {
101 0 : mUniqueStacks->SpliceStackTableElements(aWriter);
102 : }
103 0 : aWriter.EndArray();
104 : }
105 0 : aWriter.EndObject();
106 :
107 0 : aWriter.StartObjectProperty("frameTable");
108 : {
109 : {
110 0 : JSONSchemaWriter schema(aWriter);
111 0 : schema.WriteField("location");
112 0 : schema.WriteField("implementation");
113 0 : schema.WriteField("optimizations");
114 0 : schema.WriteField("line");
115 0 : schema.WriteField("category");
116 : }
117 :
118 0 : aWriter.StartArrayProperty("data");
119 : {
120 0 : mUniqueStacks->SpliceFrameTableElements(aWriter);
121 : }
122 0 : aWriter.EndArray();
123 : }
124 0 : aWriter.EndObject();
125 :
126 0 : aWriter.StartArrayProperty("stringTable");
127 : {
128 0 : mUniqueStacks->mUniqueStrings.SpliceStringTableElements(aWriter);
129 : }
130 0 : aWriter.EndArray();
131 : }
132 0 : aWriter.End();
133 :
134 0 : mUniqueStacks.reset();
135 0 : }
136 :
137 : void
138 0 : StreamSamplesAndMarkers(const char* aName,
139 : int aThreadId,
140 : const ProfileBuffer& aBuffer,
141 : SpliceableJSONWriter& aWriter,
142 : const TimeStamp& aProcessStartTime,
143 : double aSinceTime,
144 : JSContext* aContext,
145 : char* aSavedStreamedSamples,
146 : char* aSavedStreamedMarkers,
147 : UniqueStacks& aUniqueStacks)
148 : {
149 0 : aWriter.StringProperty("processType",
150 0 : XRE_ChildProcessTypeToString(XRE_GetProcessType()));
151 :
152 0 : aWriter.StringProperty("name", aName);
153 0 : aWriter.IntProperty("tid", static_cast<int64_t>(aThreadId));
154 0 : aWriter.IntProperty("pid", static_cast<int64_t>(getpid()));
155 :
156 0 : aWriter.StartObjectProperty("samples");
157 : {
158 : {
159 0 : JSONSchemaWriter schema(aWriter);
160 0 : schema.WriteField("stack");
161 0 : schema.WriteField("time");
162 0 : schema.WriteField("responsiveness");
163 0 : schema.WriteField("rss");
164 0 : schema.WriteField("uss");
165 : }
166 :
167 0 : aWriter.StartArrayProperty("data");
168 : {
169 0 : if (aSavedStreamedSamples) {
170 : // We would only have saved streamed samples during shutdown
171 : // streaming, which cares about dumping the entire buffer, and thus
172 : // should have passed in 0 for aSinceTime.
173 0 : MOZ_ASSERT(aSinceTime == 0);
174 0 : aWriter.Splice(aSavedStreamedSamples);
175 : }
176 : aBuffer.StreamSamplesToJSON(aWriter, aThreadId, aSinceTime, aContext,
177 0 : aUniqueStacks);
178 : }
179 0 : aWriter.EndArray();
180 : }
181 0 : aWriter.EndObject();
182 :
183 0 : aWriter.StartObjectProperty("markers");
184 : {
185 : {
186 0 : JSONSchemaWriter schema(aWriter);
187 0 : schema.WriteField("name");
188 0 : schema.WriteField("time");
189 0 : schema.WriteField("data");
190 : }
191 :
192 0 : aWriter.StartArrayProperty("data");
193 : {
194 0 : if (aSavedStreamedMarkers) {
195 0 : MOZ_ASSERT(aSinceTime == 0);
196 0 : aWriter.Splice(aSavedStreamedMarkers);
197 : }
198 : aBuffer.StreamMarkersToJSON(aWriter, aThreadId, aProcessStartTime,
199 0 : aSinceTime, aUniqueStacks);
200 : }
201 0 : aWriter.EndArray();
202 : }
203 0 : aWriter.EndObject();
204 0 : }
205 :
206 : void
207 0 : ThreadInfo::FlushSamplesAndMarkers(const TimeStamp& aProcessStartTime,
208 : ProfileBuffer& aBuffer)
209 : {
210 : // This function is used to serialize the current buffer just before
211 : // JSContext destruction.
212 0 : MOZ_ASSERT(mContext);
213 :
214 : // Unlike StreamJSObject, do not surround the samples in brackets by calling
215 : // aWriter.{Start,End}BareList. The result string will be a comma-separated
216 : // list of JSON object literals that will prepended by StreamJSObject into
217 : // an existing array.
218 : //
219 : // Note that the UniqueStacks instance is persisted so that the frame-index
220 : // mapping is stable across JS shutdown.
221 0 : mUniqueStacks.emplace(mContext);
222 :
223 : {
224 0 : SpliceableChunkedJSONWriter b;
225 0 : b.StartBareList();
226 : {
227 0 : aBuffer.StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
228 0 : mContext, *mUniqueStacks);
229 : }
230 0 : b.EndBareList();
231 0 : mSavedStreamedSamples = b.WriteFunc()->CopyData();
232 : }
233 :
234 : {
235 0 : SpliceableChunkedJSONWriter b;
236 0 : b.StartBareList();
237 : {
238 0 : aBuffer.StreamMarkersToJSON(b, mThreadId, aProcessStartTime,
239 0 : /* aSinceTime = */ 0, *mUniqueStacks);
240 : }
241 0 : b.EndBareList();
242 0 : mSavedStreamedMarkers = b.WriteFunc()->CopyData();
243 : }
244 :
245 : // Reset the buffer. Attempting to symbolicate JS samples after mContext has
246 : // gone away will crash.
247 0 : aBuffer.Reset();
248 0 : }
249 :
250 : size_t
251 0 : ThreadInfo::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
252 : {
253 0 : size_t n = aMallocSizeOf(this);
254 0 : n += aMallocSizeOf(mName.get());
255 0 : n += mRacyInfo->SizeOfIncludingThis(aMallocSizeOf);
256 :
257 : // Measurement of the following members may be added later if DMD finds it
258 : // is worthwhile:
259 : // - mPlatformData
260 : // - mSavedStreamedSamples
261 : // - mSavedStreamedMarkers
262 : // - mUniqueStacks
263 : //
264 : // The following members are not measured:
265 : // - mThread: because it is non-owning
266 :
267 0 : return n;
268 : }
|