Bug 1321617 - Call profiler_stream_json_for_this_process in ProfileGatherer::Finish() and get rid of the 'profile-subprocess' notification indirection. r?njn
MozReview-Commit-ID: CnE0SJBsfDN
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -23,17 +23,16 @@
#include "mozilla/ThreadLocal.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StaticPtr.h"
#include "PseudoStack.h"
#include "ThreadInfo.h"
#include "nsIHttpProtocolHandler.h"
#include "nsIObserverService.h"
-#include "nsIProfileSaveEvent.h"
#include "nsIXULAppInfo.h"
#include "nsIXULRuntime.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsMemoryReporterManager.h"
#include "nsXULAppAPI.h"
#include "nsProfilerStartParams.h"
#include "mozilla/Services.h"
@@ -1097,41 +1096,16 @@ Tick(PS::LockRef aLock, ProfileBuffer* a
}
// END tick/unwinding code
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// BEGIN saving/streaming code
-class ProfileSaveEvent final : public nsIProfileSaveEvent
-{
-public:
- typedef void (*AddSubProfileFunc)(const char* aProfile, void* aClosure);
- NS_DECL_ISUPPORTS
-
- ProfileSaveEvent(AddSubProfileFunc aFunc, void* aClosure)
- : mFunc(aFunc)
- , mClosure(aClosure)
- {}
-
- NS_IMETHOD AddSubProfile(const char* aProfile) override {
- mFunc(aProfile, mClosure);
- return NS_OK;
- }
-
-private:
- ~ProfileSaveEvent() {}
-
- AddSubProfileFunc mFunc;
- void* mClosure;
-};
-
-NS_IMPL_ISUPPORTS(ProfileSaveEvent, nsIProfileSaveEvent)
-
const static uint64_t kJS_MAX_SAFE_UINTEGER = +9007199254740991ULL;
static int64_t
SafeJSInteger(uint64_t aValue) {
return aValue <= kJS_MAX_SAFE_UINTEGER ? int64_t(aValue) : -1;
}
static void
@@ -1272,36 +1246,16 @@ StreamMetaJSCustomObject(PS::LockRef aLo
if (appInfo) {
nsAutoCString string;
res = appInfo->GetName(string);
if (!NS_FAILED(res))
aWriter.StringProperty("product", string.Data());
}
}
-struct SubprocessClosure
-{
- explicit SubprocessClosure(SpliceableJSONWriter* aWriter)
- : mWriter(aWriter)
- {}
-
- SpliceableJSONWriter* mWriter;
-};
-
-static void
-SubProcessCallback(const char* aProfile, void* aClosure)
-{
- // Called by the observer to get their profile data included as a sub profile.
- SubprocessClosure* closure = (SubprocessClosure*)aClosure;
-
- // Add the subprocess profile into the profile, as an element in the
- // "processes" array.
- closure->mWriter->Splice(aProfile);
-}
-
#if defined(PROFILE_JAVA)
static void
BuildJavaThreadJSObject(SpliceableJSONWriter& aWriter)
{
aWriter.StringProperty("name", "Java Main Thread");
aWriter.StartArrayProperty("samples");
{
@@ -1415,17 +1369,17 @@ locked_profiler_stream_json_for_this_pro
}
#endif
gPS->SetIsPaused(aLock, false);
}
aWriter.EndArray();
}
-static bool
+bool
profiler_stream_json_for_this_process(SpliceableJSONWriter& aWriter, double aSinceTime)
{
LOG("locked_profiler_stream_json_for_this_process");
MOZ_RELEASE_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(gPS);
PS::AutoLock lock(gPSMutex);
@@ -2118,29 +2072,19 @@ profiler_get_profile(double aSinceTime)
SpliceableChunkedJSONWriter b;
b.Start(SpliceableJSONWriter::SingleLineStyle);
{
if (!profiler_stream_json_for_this_process(b, aSinceTime)) {
return nullptr;
}
+ // Don't include profiles from other processes because this is a
+ // synchronous function.
b.StartArrayProperty("processes");
- if (CanNotifyObservers()) {
- // Send a event asking any subprocesses (plugins) to
- // give us their information
- SubprocessClosure closure(&b);
- nsCOMPtr<nsIObserverService> os =
- mozilla::services::GetObserverService();
- if (os) {
- RefPtr<ProfileSaveEvent> pse =
- new ProfileSaveEvent(SubProcessCallback, &closure);
- os->NotifyObservers(pse, "profiler-subprocess", nullptr);
- }
- }
b.EndArray();
}
b.End();
return b.WriteFunc()->CopyData();
}
void
--- a/tools/profiler/gecko/ProfileGatherer.cpp
+++ b/tools/profiler/gecko/ProfileGatherer.cpp
@@ -3,35 +3,35 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ProfileGatherer.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
-#include "nsIProfileSaveEvent.h"
#include "nsLocalFile.h"
#include "nsIFileStreams.h"
+#include "ProfileJSONWriter.h"
using mozilla::dom::AutoJSAPI;
using mozilla::dom::Promise;
namespace mozilla {
/**
* When a subprocess exits before we've gathered profiles, we'll
* store profiles for those processes until gathering starts. We'll
* only store up to MAX_SUBPROCESS_EXIT_PROFILES. The buffer is
* circular, so as soon as we receive another exit profile, we'll
* bump the oldest one out of the buffer.
*/
static const uint32_t MAX_SUBPROCESS_EXIT_PROFILES = 5;
-NS_IMPL_ISUPPORTS(ProfileGatherer, nsIObserver)
+NS_IMPL_ISUPPORTS0(ProfileGatherer)
ProfileGatherer::ProfileGatherer()
: mSinceTime(0)
, mPendingProfiles(0)
, mGathering(false)
{
}
@@ -122,69 +122,80 @@ ProfileGatherer::Start2(double aSinceTim
mSinceTime = aSinceTime;
mGathering = true;
mPendingProfiles = 0;
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
DebugOnly<nsresult> rv =
- os->AddObserver(this, "profiler-subprocess", false);
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "AddObserver failed");
-
- rv = os->NotifyObservers(this, "profiler-subprocess-gather", nullptr);
+ os->NotifyObservers(this, "profiler-subprocess-gather", nullptr);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NotifyObservers failed");
}
if (!mPendingProfiles) {
Finish();
}
}
void
ProfileGatherer::Finish()
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
- // It's unlikely but possible that profiler_stop() could be called while the
- // profile gathering is in flight, but not via nsProfiler::StopProfiler().
- // This check will detect that case.
- //
- // XXX: However, it won't detect the case where profiler_stop() *and*
- // profiler_start() have both been called. (If that does happen, we'll end up
- // with a franken-profile that includes a mix of data from the old and new
- // profile activations.) We could include the activity generation to detect
- // that, but it's not worth it for what should be an extremely unlikely case.
- // It would be better if this class was rearranged so that
- // profiler_get_profile() was called for the parent process in Start()
- // instead of in Finish(). Then we wouldn't have to worry about cancelling.
- if (!profiler_is_active()) {
- Cancel();
- return;
+ SpliceableChunkedJSONWriter b;
+ b.Start(SpliceableJSONWriter::SingleLineStyle);
+ {
+ if (!profiler_stream_json_for_this_process(b, mSinceTime)) {
+ // It's unlikely but possible that profiler_stop() could be called while the
+ // profile gathering is in flight, but not via nsProfiler::StopProfiler().
+ // This check will detect that case.
+ //
+ // XXX: However, it won't detect the case where profiler_stop() *and*
+ // profiler_start() have both been called. (If that does happen, we'll end up
+ // with a franken-profile that includes a mix of data from the old and new
+ // profile activations.) We could include the activity generation to detect
+ // that, but it's not worth it for what should be an extremely unlikely case.
+ // It would be better if this class was rearranged so that
+ // profiler_get_profile() was called for the parent process in Start()
+ // instead of in Finish(). Then we wouldn't have to worry about cancelling.
+ Cancel();
+ return;
+ }
+
+ b.StartArrayProperty("processes");
+ for (size_t i = 0; i < mExitProfiles.Length(); ++i) {
+ if (!mExitProfiles[i].IsEmpty()) {
+ b.Splice(mExitProfiles[i].get());
+ }
+ }
+ mExitProfiles.Clear();
+ for (size_t i = 0; i < mResponseProfiles.Length(); ++i) {
+ if (!mResponseProfiles[i].IsEmpty()) {
+ b.Splice(mResponseProfiles[i].get());
+ }
+ }
+ mResponseProfiles.Clear();
+ b.EndArray();
}
+ b.End();
- UniquePtr<char[]> buf = profiler_get_profile(mSinceTime);
+ UniquePtr<char[]> buf = b.WriteFunc()->CopyData();
if (mFile) {
nsCOMPtr<nsIFileOutputStream> of =
do_CreateInstance("@mozilla.org/network/file-output-stream;1");
of->Init(mFile, -1, -1, 0);
uint32_t sz;
of->Write(buf.get(), strlen(buf.get()), &sz);
of->Close();
Reset();
return;
}
- nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
- if (os) {
- DebugOnly<nsresult> rv = os->RemoveObserver(this, "profiler-subprocess");
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RemoveObserver failed");
- }
-
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(mPromise->GlobalJSObject()))) {
// We're really hosed if we can't get a JS context for some reason.
Reset();
return;
}
JSContext* cx = jsapi.cx();
@@ -239,34 +250,9 @@ void
ProfileGatherer::OOPExitProfile(const nsACString& aProfile)
{
if (mExitProfiles.Length() >= MAX_SUBPROCESS_EXIT_PROFILES) {
mExitProfiles.RemoveElementAt(0);
}
mExitProfiles.AppendElement(aProfile);
}
-NS_IMETHODIMP
-ProfileGatherer::Observe(nsISupports* aSubject,
- const char* aTopic,
- const char16_t *someData)
-{
- if (!strcmp(aTopic, "profiler-subprocess")) {
- nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
- if (pse) {
- for (size_t i = 0; i < mExitProfiles.Length(); ++i) {
- if (!mExitProfiles[i].IsEmpty()) {
- pse->AddSubProfile(mExitProfiles[i].get());
- }
- }
- mExitProfiles.Clear();
- for (size_t i = 0; i < mResponseProfiles.Length(); ++i) {
- if (!mResponseProfiles[i].IsEmpty()) {
- pse->AddSubProfile(mResponseProfiles[i].get());
- }
- }
- mResponseProfiles.Clear();
- }
- }
- return NS_OK;
-}
-
} // namespace mozilla
--- a/tools/profiler/gecko/ProfileGatherer.h
+++ b/tools/profiler/gecko/ProfileGatherer.h
@@ -6,21 +6,20 @@
#define MOZ_PROFILE_GATHERER_H
#include "mozilla/dom/Promise.h"
#include "nsIFile.h"
namespace mozilla {
// This class holds the state for an async profile-gathering request.
-class ProfileGatherer final : public nsIObserver
+class ProfileGatherer final : public nsISupports
{
public:
NS_DECL_ISUPPORTS
- NS_DECL_NSIOBSERVER
explicit ProfileGatherer();
void WillGatherOOPProfile();
void GatheredOOPProfile(const nsACString& aProfile);
void Start(double aSinceTime, mozilla::dom::Promise* aPromise);
void Start(double aSinceTime, const nsACString& aFileName);
void Cancel();
void OOPExitProfile(const nsACString& aProfile);
deleted file mode 100644
--- a/tools/profiler/gecko/nsIProfileSaveEvent.idl
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-[uuid(f5ad0830-e178-41f9-b253-db9b4fae4cb3)]
-interface nsIProfileSaveEvent : nsISupports
-{
- /**
- * Call this method when observing this event to include
- * a sub profile origining from an external source such
- * as a non native thread or another process.
- */
- void AddSubProfile(in string aMarker);
-};
-
-
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -3,17 +3,16 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
if CONFIG['MOZ_GECKO_PROFILER']:
XPIDL_MODULE = 'profiler'
XPIDL_SOURCES += [
'gecko/nsIProfiler.idl',
- 'gecko/nsIProfileSaveEvent.idl',
]
EXPORTS += [
'public/CrossProcessProfilerController.h',
'public/ProfilerMarkers.h',
'public/PseudoStack.h',
'public/shared-libraries.h',
]
EXTRA_JS_MODULES += [
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -26,16 +26,18 @@
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "js/TypeDecls.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Vector.h"
#include "nsString.h"
+class SpliceableJSONWriter;
+
namespace mozilla {
class TimeStamp;
namespace dom {
class Promise;
} // namespace dom
} // namespace mozilla
@@ -210,16 +212,22 @@ PROFILER_FUNC(bool profiler_feature_acti
// active or not.
PROFILER_FUNC_VOID(profiler_set_frame_number(int frameNumber))
// Get the profile encoded as a JSON string. A no-op (returning nullptr) if the
// profiler is inactive.
PROFILER_FUNC(mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0),
nullptr)
+// Write the profile for this process (excluding subprocesses) into aWriter.
+// Returns false if the profiler is inactive.
+PROFILER_FUNC(bool profiler_stream_json_for_this_process(SpliceableJSONWriter& aWriter,
+ double aSinceTime = 0),
+ false)
+
// Get the params used to start the profiler. Returns 0 and empty vectors (via
// outparams) if the profile is inactive.
PROFILER_FUNC_VOID(profiler_get_start_params(int* aEntrySize,
double* aInterval,
mozilla::Vector<const char*>* aFeatures,
mozilla::Vector<const char*>* aFilters))
// Get the profile and write it into a file. A no-op if the profile is