LCOV - code coverage report
Current view: top level - tools/profiler/gecko - nsProfiler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 293 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 60 0.0 %
Legend: Lines: hit not hit

          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             : 

Generated by: LCOV version 1.13