Coverage Report

Created: 2018-09-25 14:53

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