Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <string>
8 : #include <sstream>
9 : #include "GeckoProfiler.h"
10 : #include "nsIFileStreams.h"
11 : #include "nsProfiler.h"
12 : #include "nsProfilerStartParams.h"
13 : #include "nsMemory.h"
14 : #include "nsString.h"
15 : #include "mozilla/Services.h"
16 : #include "nsIObserverService.h"
17 : #include "nsIInterfaceRequestor.h"
18 : #include "nsILoadContext.h"
19 : #include "nsIWebNavigation.h"
20 : #include "nsIInterfaceRequestorUtils.h"
21 : #include "shared-libraries.h"
22 : #include "js/Value.h"
23 : #include "mozilla/ErrorResult.h"
24 : #include "mozilla/dom/Promise.h"
25 : #include "mozilla/dom/TypedArray.h"
26 : #include "nsLocalFile.h"
27 : #include "nsThreadUtils.h"
28 : #include "ProfilerParent.h"
29 : #include "platform.h"
30 :
31 : using namespace mozilla;
32 :
33 : using dom::AutoJSAPI;
34 : using dom::Promise;
35 : using std::string;
36 :
37 0 : NS_IMPL_ISUPPORTS(nsProfiler, nsIProfiler)
38 :
39 0 : nsProfiler::nsProfiler()
40 : : mLockedForPrivateBrowsing(false)
41 : , mPendingProfiles(0)
42 0 : , mGathering(false)
43 : {
44 0 : }
45 :
46 0 : nsProfiler::~nsProfiler()
47 : {
48 0 : nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
49 0 : if (observerService) {
50 0 : observerService->RemoveObserver(this, "chrome-document-global-created");
51 0 : observerService->RemoveObserver(this, "last-pb-context-exited");
52 : }
53 0 : }
54 :
55 : nsresult
56 0 : nsProfiler::Init() {
57 0 : nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
58 0 : if (observerService) {
59 0 : observerService->AddObserver(this, "chrome-document-global-created", false);
60 0 : observerService->AddObserver(this, "last-pb-context-exited", false);
61 : }
62 0 : return NS_OK;
63 : }
64 :
65 : NS_IMETHODIMP
66 0 : nsProfiler::Observe(nsISupports *aSubject,
67 : const char *aTopic,
68 : const char16_t *aData)
69 : {
70 : // The profiler's handling of private browsing is as simple as possible: it
71 : // is stopped when the first PB window opens, and left stopped when the last
72 : // PB window closes.
73 0 : if (strcmp(aTopic, "chrome-document-global-created") == 0) {
74 0 : nsCOMPtr<nsIInterfaceRequestor> requestor = do_QueryInterface(aSubject);
75 0 : nsCOMPtr<nsIWebNavigation> parentWebNav = do_GetInterface(requestor);
76 0 : nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(parentWebNav);
77 0 : if (loadContext && loadContext->UsePrivateBrowsing() && !mLockedForPrivateBrowsing) {
78 0 : mLockedForPrivateBrowsing = true;
79 0 : profiler_stop();
80 : }
81 0 : } else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
82 0 : mLockedForPrivateBrowsing = false;
83 : }
84 0 : return NS_OK;
85 : }
86 :
87 : NS_IMETHODIMP
88 0 : nsProfiler::CanProfile(bool *aCanProfile)
89 : {
90 0 : *aCanProfile = !mLockedForPrivateBrowsing;
91 0 : return NS_OK;
92 : }
93 :
94 : static bool
95 0 : HasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature)
96 : {
97 0 : for (size_t i = 0; i < aFeatureCount; i++) {
98 0 : if (strcmp(aFeatures[i], aFeature) == 0) {
99 0 : return true;
100 : }
101 : }
102 0 : return false;
103 : }
104 :
105 : NS_IMETHODIMP
106 0 : nsProfiler::StartProfiler(uint32_t aEntries, double aInterval,
107 : const char** aFeatures, uint32_t aFeatureCount,
108 : const char** aFilters, uint32_t aFilterCount)
109 : {
110 0 : if (mLockedForPrivateBrowsing) {
111 0 : return NS_ERROR_NOT_AVAILABLE;
112 : }
113 :
114 : #define ADD_FEATURE_BIT(n_, str_, Name_) \
115 : if (HasFeature(aFeatures, aFeatureCount, str_)) { \
116 : features |= ProfilerFeature::Name_; \
117 : }
118 :
119 : // Convert the array of strings to a bitfield.
120 0 : uint32_t features = 0;
121 0 : PROFILER_FOR_EACH_FEATURE(ADD_FEATURE_BIT)
122 :
123 : #undef ADD_FEATURE_BIT
124 :
125 0 : ResetGathering();
126 :
127 0 : profiler_start(aEntries, aInterval, features, aFilters, aFilterCount);
128 :
129 0 : return NS_OK;
130 : }
131 :
132 : NS_IMETHODIMP
133 0 : nsProfiler::StopProfiler()
134 : {
135 : // If we have a Promise in flight, we should reject it.
136 0 : if (mPromiseHolder.isSome()) {
137 0 : mPromiseHolder->RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
138 : }
139 0 : mExitProfiles.Clear();
140 0 : ResetGathering();
141 :
142 0 : profiler_stop();
143 :
144 0 : return NS_OK;
145 : }
146 :
147 : NS_IMETHODIMP
148 0 : nsProfiler::IsPaused(bool *aIsPaused)
149 : {
150 0 : *aIsPaused = profiler_is_paused();
151 0 : return NS_OK;
152 : }
153 :
154 : NS_IMETHODIMP
155 0 : nsProfiler::PauseSampling()
156 : {
157 0 : profiler_pause();
158 0 : return NS_OK;
159 : }
160 :
161 : NS_IMETHODIMP
162 0 : nsProfiler::ResumeSampling()
163 : {
164 0 : profiler_resume();
165 0 : return NS_OK;
166 : }
167 :
168 : NS_IMETHODIMP
169 0 : nsProfiler::AddMarker(const char *aMarker)
170 : {
171 0 : profiler_add_marker(aMarker);
172 0 : return NS_OK;
173 : }
174 :
175 : NS_IMETHODIMP
176 0 : nsProfiler::GetProfile(double aSinceTime, char** aProfile)
177 : {
178 0 : mozilla::UniquePtr<char[]> profile = profiler_get_profile(aSinceTime);
179 0 : if (profile) {
180 0 : size_t len = strlen(profile.get());
181 : char *profileStr = static_cast<char *>
182 0 : (nsMemory::Clone(profile.get(), (len + 1) * sizeof(char)));
183 0 : profileStr[len] = '\0';
184 0 : *aProfile = profileStr;
185 : }
186 0 : return NS_OK;
187 : }
188 :
189 : namespace {
190 0 : struct StringWriteFunc : public JSONWriteFunc
191 : {
192 : nsAString& mBuffer; // This struct must not outlive this buffer
193 0 : explicit StringWriteFunc(nsAString& buffer) : mBuffer(buffer) {}
194 :
195 0 : void Write(const char* aStr)
196 : {
197 0 : mBuffer.Append(NS_ConvertUTF8toUTF16(aStr));
198 0 : }
199 : };
200 : }
201 :
202 : NS_IMETHODIMP
203 0 : nsProfiler::GetSharedLibraries(JSContext* aCx,
204 : JS::MutableHandle<JS::Value> aResult)
205 : {
206 0 : JS::RootedValue val(aCx);
207 : {
208 0 : nsString buffer;
209 0 : JSONWriter w(MakeUnique<StringWriteFunc>(buffer));
210 0 : w.StartArrayElement();
211 0 : AppendSharedLibraries(w);
212 0 : w.EndArray();
213 0 : MOZ_ALWAYS_TRUE(JS_ParseJSON(aCx, static_cast<const char16_t*>(buffer.get()),
214 : buffer.Length(), &val));
215 : }
216 0 : JS::RootedObject obj(aCx, &val.toObject());
217 0 : if (!obj) {
218 0 : return NS_ERROR_FAILURE;
219 : }
220 0 : aResult.setObject(*obj);
221 0 : return NS_OK;
222 : }
223 :
224 : NS_IMETHODIMP
225 0 : nsProfiler::DumpProfileToFile(const char* aFilename)
226 : {
227 0 : profiler_save_profile_to_file(aFilename);
228 0 : return NS_OK;
229 : }
230 :
231 : NS_IMETHODIMP
232 0 : nsProfiler::GetProfileData(double aSinceTime, JSContext* aCx,
233 : JS::MutableHandle<JS::Value> aResult)
234 : {
235 0 : mozilla::UniquePtr<char[]> profile = profiler_get_profile(aSinceTime);
236 0 : if (!profile) {
237 0 : return NS_ERROR_FAILURE;
238 : }
239 :
240 0 : NS_ConvertUTF8toUTF16 js_string(nsDependentCString(profile.get()));
241 0 : auto profile16 = static_cast<const char16_t*>(js_string.get());
242 :
243 0 : JS::RootedValue val(aCx);
244 0 : MOZ_ALWAYS_TRUE(JS_ParseJSON(aCx, profile16, js_string.Length(), &val));
245 :
246 0 : aResult.set(val);
247 0 : return NS_OK;
248 : }
249 :
250 : NS_IMETHODIMP
251 0 : nsProfiler::GetProfileDataAsync(double aSinceTime, JSContext* aCx,
252 : nsISupports** aPromise)
253 : {
254 0 : MOZ_ASSERT(NS_IsMainThread());
255 :
256 0 : if (!profiler_is_active()) {
257 0 : return NS_ERROR_FAILURE;
258 : }
259 :
260 0 : if (NS_WARN_IF(!aCx)) {
261 0 : return NS_ERROR_FAILURE;
262 : }
263 :
264 : nsIGlobalObject* globalObject =
265 0 : xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
266 :
267 0 : if (NS_WARN_IF(!globalObject)) {
268 0 : return NS_ERROR_FAILURE;
269 : }
270 :
271 0 : ErrorResult result;
272 0 : RefPtr<Promise> promise = Promise::Create(globalObject, result);
273 0 : if (NS_WARN_IF(result.Failed())) {
274 0 : return result.StealNSResult();
275 : }
276 :
277 0 : StartGathering(aSinceTime)->Then(
278 : GetMainThreadSerialEventTarget(), __func__,
279 0 : [promise](nsCString aResult) {
280 0 : AutoJSAPI jsapi;
281 0 : if (NS_WARN_IF(!jsapi.Init(promise->GlobalJSObject()))) {
282 : // We're really hosed if we can't get a JS context for some reason.
283 0 : promise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
284 0 : return;
285 : }
286 :
287 0 : JSContext* cx = jsapi.cx();
288 :
289 : // Now parse the JSON so that we resolve with a JS Object.
290 0 : JS::RootedValue val(cx);
291 : {
292 0 : NS_ConvertUTF8toUTF16 js_string(aResult);
293 0 : if (!JS_ParseJSON(cx, static_cast<const char16_t*>(js_string.get()),
294 : js_string.Length(), &val)) {
295 0 : if (!jsapi.HasException()) {
296 0 : promise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
297 : } else {
298 0 : JS::RootedValue exn(cx);
299 0 : DebugOnly<bool> gotException = jsapi.StealException(&exn);
300 0 : MOZ_ASSERT(gotException);
301 :
302 0 : jsapi.ClearException();
303 0 : promise->MaybeReject(cx, exn);
304 : }
305 : } else {
306 0 : promise->MaybeResolve(val);
307 : }
308 : }
309 : },
310 0 : [promise](nsresult aRv) {
311 0 : promise->MaybeReject(aRv);
312 0 : });
313 :
314 0 : promise.forget(aPromise);
315 0 : return NS_OK;
316 : }
317 :
318 : NS_IMETHODIMP
319 0 : nsProfiler::GetProfileDataAsArrayBuffer(double aSinceTime, JSContext* aCx,
320 : nsISupports** aPromise)
321 : {
322 0 : MOZ_ASSERT(NS_IsMainThread());
323 :
324 0 : if (!profiler_is_active()) {
325 0 : return NS_ERROR_FAILURE;
326 : }
327 :
328 0 : if (NS_WARN_IF(!aCx)) {
329 0 : return NS_ERROR_FAILURE;
330 : }
331 :
332 : nsIGlobalObject* globalObject =
333 0 : xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
334 :
335 0 : if (NS_WARN_IF(!globalObject)) {
336 0 : return NS_ERROR_FAILURE;
337 : }
338 :
339 0 : ErrorResult result;
340 0 : RefPtr<Promise> promise = Promise::Create(globalObject, result);
341 0 : if (NS_WARN_IF(result.Failed())) {
342 0 : return result.StealNSResult();
343 : }
344 :
345 0 : StartGathering(aSinceTime)->Then(
346 : GetMainThreadSerialEventTarget(), __func__,
347 0 : [promise](nsCString aResult) {
348 0 : AutoJSAPI jsapi;
349 0 : if (NS_WARN_IF(!jsapi.Init(promise->GlobalJSObject()))) {
350 : // We're really hosed if we can't get a JS context for some reason.
351 0 : promise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
352 0 : return;
353 : }
354 :
355 0 : JSContext* cx = jsapi.cx();
356 : JSObject* typedArray =
357 0 : dom::ArrayBuffer::Create(cx, aResult.Length(),
358 0 : reinterpret_cast<const uint8_t*>(aResult.Data()));
359 0 : if (typedArray) {
360 0 : JS::RootedValue val(cx, JS::ObjectValue(*typedArray));
361 0 : promise->MaybeResolve(val);
362 : } else {
363 0 : promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
364 : }
365 : },
366 0 : [promise](nsresult aRv) {
367 0 : promise->MaybeReject(aRv);
368 0 : });
369 :
370 0 : promise.forget(aPromise);
371 0 : return NS_OK;
372 : }
373 :
374 : NS_IMETHODIMP
375 0 : nsProfiler::DumpProfileToFileAsync(const nsACString& aFilename,
376 : double aSinceTime)
377 : {
378 0 : MOZ_ASSERT(NS_IsMainThread());
379 :
380 0 : if (!profiler_is_active()) {
381 0 : return NS_ERROR_FAILURE;
382 : }
383 :
384 0 : nsCString filename(aFilename);
385 :
386 0 : StartGathering(aSinceTime)->Then(
387 : GetMainThreadSerialEventTarget(), __func__,
388 0 : [filename](const nsCString& aResult) {
389 0 : nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
390 0 : nsresult rv = file->InitWithNativePath(filename);
391 0 : if (NS_FAILED(rv)) {
392 0 : MOZ_CRASH();
393 : }
394 : nsCOMPtr<nsIFileOutputStream> of =
395 0 : do_CreateInstance("@mozilla.org/network/file-output-stream;1");
396 0 : of->Init(file, -1, -1, 0);
397 : uint32_t sz;
398 0 : of->Write(aResult.get(), aResult.Length(), &sz);
399 0 : of->Close();
400 0 : },
401 0 : [](nsresult aRv) { });
402 :
403 0 : return NS_OK;
404 : }
405 :
406 :
407 : NS_IMETHODIMP
408 0 : nsProfiler::GetElapsedTime(double* aElapsedTime)
409 : {
410 0 : *aElapsedTime = profiler_time();
411 0 : return NS_OK;
412 : }
413 :
414 : NS_IMETHODIMP
415 0 : nsProfiler::IsActive(bool *aIsActive)
416 : {
417 0 : *aIsActive = profiler_is_active();
418 0 : return NS_OK;
419 : }
420 :
421 : static void
422 0 : GetArrayOfStringsForFeatures(uint32_t aFeatures,
423 : uint32_t* aCount, char*** aFeatureList)
424 : {
425 : #define COUNT_IF_SET(n_, str_, Name_) \
426 : if (ProfilerFeature::Has##Name_(aFeatures)) { \
427 : len++; \
428 : }
429 :
430 : // Count the number of features in use.
431 0 : uint32_t len = 0;
432 0 : PROFILER_FOR_EACH_FEATURE(COUNT_IF_SET)
433 :
434 : #undef COUNT_IF_SET
435 :
436 0 : auto featureList = static_cast<char**>(moz_xmalloc(len * sizeof(char*)));
437 :
438 : #define DUP_IF_SET(n_, str_, Name_) \
439 : if (ProfilerFeature::Has##Name_(aFeatures)) { \
440 : size_t strLen = strlen(str_); \
441 : featureList[i] = static_cast<char*>( \
442 : nsMemory::Clone(str_, (strLen + 1) * sizeof(char))); \
443 : i++; \
444 : }
445 :
446 : // Insert the strings for the features in use.
447 0 : size_t i = 0;
448 0 : PROFILER_FOR_EACH_FEATURE(DUP_IF_SET)
449 :
450 : #undef DUP_IF_SET
451 :
452 0 : *aFeatureList = featureList;
453 0 : *aCount = len;
454 0 : }
455 :
456 : NS_IMETHODIMP
457 0 : nsProfiler::GetFeatures(uint32_t* aCount, char*** aFeatureList)
458 : {
459 0 : uint32_t features = profiler_get_available_features();
460 0 : GetArrayOfStringsForFeatures(features, aCount, aFeatureList);
461 0 : return NS_OK;
462 : }
463 :
464 : NS_IMETHODIMP
465 0 : nsProfiler::GetAllFeatures(uint32_t* aCount, char*** aFeatureList)
466 : {
467 0 : GetArrayOfStringsForFeatures((uint32_t)-1, aCount, aFeatureList);
468 0 : return NS_OK;
469 : }
470 :
471 : NS_IMETHODIMP
472 0 : nsProfiler::GetStartParams(nsIProfilerStartParams** aRetVal)
473 : {
474 0 : if (!profiler_is_active()) {
475 0 : *aRetVal = nullptr;
476 : } else {
477 0 : int entries = 0;
478 0 : double interval = 0;
479 0 : uint32_t features = 0;
480 0 : mozilla::Vector<const char*> filters;
481 0 : profiler_get_start_params(&entries, &interval, &features, &filters);
482 :
483 0 : nsTArray<nsCString> filtersArray;
484 0 : for (uint32_t i = 0; i < filters.length(); ++i) {
485 0 : filtersArray.AppendElement(filters[i]);
486 : }
487 :
488 : nsCOMPtr<nsIProfilerStartParams> startParams =
489 0 : new nsProfilerStartParams(entries, interval, features, filtersArray);
490 :
491 0 : startParams.forget(aRetVal);
492 : }
493 0 : return NS_OK;
494 : }
495 :
496 : NS_IMETHODIMP
497 0 : nsProfiler::GetBufferInfo(uint32_t* aCurrentPosition, uint32_t* aTotalSize,
498 : uint32_t* aGeneration)
499 : {
500 0 : MOZ_ASSERT(aCurrentPosition);
501 0 : MOZ_ASSERT(aTotalSize);
502 0 : MOZ_ASSERT(aGeneration);
503 0 : profiler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration);
504 0 : return NS_OK;
505 : }
506 :
507 : void
508 0 : nsProfiler::GatheredOOPProfile(const nsACString& aProfile)
509 : {
510 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
511 :
512 0 : if (!profiler_is_active()) {
513 0 : return;
514 : }
515 :
516 0 : if (!mGathering) {
517 : // If we're not actively gathering, then we don't actually care that we
518 : // gathered a profile here. This can happen for processes that exit while
519 : // profiling.
520 0 : return;
521 : }
522 :
523 0 : MOZ_RELEASE_ASSERT(mWriter.isSome(),
524 : "Should always have a writer if mGathering is true");
525 :
526 0 : if (!aProfile.IsEmpty()) {
527 0 : mWriter->Splice(PromiseFlatCString(aProfile).get());
528 : }
529 :
530 0 : mPendingProfiles--;
531 :
532 0 : if (mPendingProfiles == 0) {
533 : // We've got all of the async profiles now. Let's
534 : // finish off the profile and resolve the Promise.
535 0 : FinishGathering();
536 : }
537 : }
538 :
539 : // When a subprocess exits before we've gathered profiles, we'll store profiles
540 : // for those processes until gathering starts. We'll only store up to
541 : // MAX_SUBPROCESS_EXIT_PROFILES. The buffer is circular, so as soon as we
542 : // receive another exit profile, we'll bump the oldest one out of the buffer.
543 : static const uint32_t MAX_SUBPROCESS_EXIT_PROFILES = 5;
544 :
545 : void
546 0 : nsProfiler::ReceiveShutdownProfile(const nsCString& aProfile)
547 : {
548 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
549 :
550 0 : if (!profiler_is_active()) {
551 0 : return;
552 : }
553 :
554 : // Append the exit profile to mExitProfiles so that it can be picked up the
555 : // next time a profile is requested. If we're currently gathering a profile,
556 : // do not add this exit profile to it; chances are that we already have a
557 : // profile from the exiting process and we don't want another one.
558 : // We only keep around at most MAX_SUBPROCESS_EXIT_PROFILES exit profiles.
559 0 : if (mExitProfiles.Length() >= MAX_SUBPROCESS_EXIT_PROFILES) {
560 0 : mExitProfiles.RemoveElementAt(0);
561 : }
562 0 : mExitProfiles.AppendElement(aProfile);
563 : }
564 :
565 : RefPtr<nsProfiler::GatheringPromise>
566 0 : nsProfiler::StartGathering(double aSinceTime)
567 : {
568 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
569 :
570 0 : if (mGathering) {
571 : // If we're already gathering, return a rejected promise - this isn't
572 : // going to end well.
573 0 : return GatheringPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
574 : }
575 :
576 0 : mGathering = true;
577 :
578 : // Request profiles from the other processes. This will trigger asynchronous
579 : // calls to ProfileGatherer::GatheredOOPProfile as the profiles arrive.
580 : //
581 : // Do this before the call to profiler_stream_json_for_this_process() because
582 : // that call is slow and we want to let the other processes grab their
583 : // profiles as soon as possible.
584 : nsTArray<RefPtr<ProfilerParent::SingleProcessProfilePromise>> profiles =
585 0 : ProfilerParent::GatherProfiles();
586 :
587 0 : mWriter.emplace();
588 :
589 : // Start building up the JSON result and grab the profile from this process.
590 0 : mWriter->Start(SpliceableJSONWriter::SingleLineStyle);
591 0 : if (!profiler_stream_json_for_this_process(*mWriter, aSinceTime)) {
592 : // The profiler is inactive. This either means that it was inactive even
593 : // at the time that ProfileGatherer::Start() was called, or that it was
594 : // stopped on a different thread since that call. Either way, we need to
595 : // reject the promise and stop gathering.
596 0 : return GatheringPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
597 : }
598 :
599 0 : mWriter->StartArrayProperty("processes");
600 :
601 : // If we have any process exit profiles, add them immediately, and clear
602 : // mExitProfiles.
603 0 : for (size_t i = 0; i < mExitProfiles.Length(); ++i) {
604 0 : if (!mExitProfiles[i].IsEmpty()) {
605 0 : mWriter->Splice(mExitProfiles[i].get());
606 : }
607 : }
608 0 : mExitProfiles.Clear();
609 :
610 0 : mPromiseHolder.emplace();
611 0 : RefPtr<GatheringPromise> promise = mPromiseHolder->Ensure(__func__);
612 :
613 : // Keep the array property "processes" and the root object in mWriter open
614 : // until FinishGathering() is called. As profiles from the other processes
615 : // come in, they will be inserted and end up in the right spot.
616 : // FinishGathering() will close the array and the root object.
617 :
618 0 : mPendingProfiles = profiles.Length();
619 0 : RefPtr<nsProfiler> self = this;
620 0 : for (auto profile : profiles) {
621 : profile->Then(GetMainThreadSerialEventTarget(), __func__,
622 0 : [self](const nsCString& aResult) {
623 0 : self->GatheredOOPProfile(aResult);
624 0 : },
625 0 : [self](ipc::PromiseRejectReason aReason) {
626 0 : self->GatheredOOPProfile(NS_LITERAL_CSTRING(""));
627 0 : });
628 : }
629 0 : if (!mPendingProfiles) {
630 0 : FinishGathering();
631 : }
632 :
633 0 : return promise;
634 : }
635 :
636 : void
637 0 : nsProfiler::FinishGathering()
638 : {
639 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
640 0 : MOZ_RELEASE_ASSERT(mWriter.isSome());
641 0 : MOZ_RELEASE_ASSERT(mPromiseHolder.isSome());
642 :
643 : // Close the "processes" array property.
644 0 : mWriter->EndArray();
645 :
646 : // Close the root object of the generated JSON.
647 0 : mWriter->End();
648 :
649 0 : UniquePtr<char[]> buf = mWriter->WriteFunc()->CopyData();
650 0 : nsCString result(buf.get());
651 0 : mPromiseHolder->Resolve(result, __func__);
652 :
653 0 : ResetGathering();
654 0 : }
655 :
656 : void
657 0 : nsProfiler::ResetGathering()
658 : {
659 0 : mPromiseHolder.reset();
660 0 : mPendingProfiles = 0;
661 0 : mGathering = false;
662 0 : mWriter.reset();
663 0 : }
664 :
|