diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index ff6a45fea3ec..31b32a10c693 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -74,10 +74,6 @@ #include "imgLoader.h" #include "GMPServiceChild.h" -#ifdef MOZ_GECKO_PROFILER -#include "ChildProfilerController.h" -#endif - #if defined(MOZ_CONTENT_SANDBOX) #if defined(XP_WIN) #define TARGET_SANDBOX_EXPORTS @@ -1054,11 +1050,6 @@ ContentChild::InitXPCOM(const XPCOMInitData& aXPCOMInit, nsCOMPtr ucsURL = DeserializeURI(aXPCOMInit.userContentSheetURL()); nsLayoutStylesheetCache::SetUserContentCSSURL(ucsURL); -#ifdef MOZ_GECKO_PROFILER - mProfilerController = new ChildProfilerController(); - Unused << SendInitProfiler(mProfilerController->SetUpEndpoints(OtherPid())); -#endif - // This will register cross-process observer. mozilla::dom::time::InitializeDateCacheCleaner(); @@ -2660,6 +2651,60 @@ ContentChild::DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* actor) return true; } +mozilla::ipc::IPCResult +ContentChild::RecvStartProfiler(const ProfilerInitParams& params) +{ + nsTArray filterArray; + for (size_t i = 0; i < params.filters().Length(); ++i) { + filterArray.AppendElement(params.filters()[i].get()); + } + + profiler_start(params.entries(), params.interval(), params.features(), + filterArray.Elements(), filterArray.Length()); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +ContentChild::RecvStopProfiler() +{ + profiler_stop(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +ContentChild::RecvPauseProfiler(const bool& aPause) +{ + if (aPause) { + profiler_pause(); + } else { + profiler_resume(); + } + + return IPC_OK(); +} + +void +ContentChild::GatherProfile(bool aIsExitProfile) +{ + nsCString profileCString; + UniquePtr profile = profiler_get_profile(); + if (profile) { + profileCString = nsCString(profile.get(), strlen(profile.get())); + } else { + profileCString = EmptyCString(); + } + + Unused << SendProfile(profileCString, aIsExitProfile); +} + +mozilla::ipc::IPCResult +ContentChild::RecvGatherProfile() +{ + GatherProfile(false); + return IPC_OK(); +} + mozilla::ipc::IPCResult ContentChild::RecvLoadPluginResult(const uint32_t& aPluginId, const bool& aResult) @@ -2826,12 +2871,11 @@ ContentChild::RecvShutdown() GetIPCChannel()->SetAbortOnError(false); #ifdef MOZ_GECKO_PROFILER - if (mProfilerController) { - nsCString shutdownProfile = mProfilerController->GrabShutdownProfileAndShutdown(); - mProfilerController = nullptr; - // Send the shutdown profile to the parent process through our own - // message channel, which we know will survive for long enough. - Unused << SendShutdownProfile(shutdownProfile); + if (profiler_is_active()) { + // We're shutting down while we were profiling. Send the + // profile up to the parent so that we don't lose this + // information. + GatherProfile(true); } #endif diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 3e2689f6b242..8dbf5319cfdf 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -35,7 +35,6 @@ struct LookAndFeelInt; namespace mozilla { class RemoteSpellcheckEngineChild; -class ChildProfilerController; using mozilla::loader::PScriptCacheChild; @@ -453,6 +452,14 @@ public: virtual mozilla::ipc::IPCResult RecvUpdateWindow(const uintptr_t& aChildId) override; + virtual mozilla::ipc::IPCResult RecvStartProfiler(const ProfilerInitParams& params) override; + + virtual mozilla::ipc::IPCResult RecvPauseProfiler(const bool& aPause) override; + + virtual mozilla::ipc::IPCResult RecvStopProfiler() override; + + virtual mozilla::ipc::IPCResult RecvGatherProfile() override; + virtual mozilla::ipc::IPCResult RecvDomainSetChanged(const uint32_t& aSetType, const uint32_t& aChangeType, const OptionalURIParams& aDomain) override; @@ -685,6 +692,8 @@ private: virtual already_AddRefed GetConstructedEventTarget(const Message& aMsg) override; + void GatherProfile(bool aIsExitProfile); + InfallibleTArray > mAlertObservers; RefPtr mConsoleListener; @@ -729,10 +738,6 @@ private: nsCOMPtr mPolicy; nsCOMPtr mForceKillTimer; -#ifdef MOZ_GECKO_PROFILER - RefPtr mProfilerController; -#endif - #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) nsCOMPtr mProfileDir; #endif diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index d06da7358cba..ab2fde5f1a3d 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -24,6 +24,9 @@ #include "chrome/common/process_watcher.h" #include "mozilla/a11y/PDocAccessible.h" +#ifdef MOZ_GECKO_PROFILER +#include "CrossProcessProfilerController.h" +#endif #include "GeckoProfiler.h" #include "GMPServiceParent.h" #include "HandlerServiceParent.h" @@ -185,7 +188,6 @@ #include "nsHostObjectProtocolHandler.h" #include "nsICaptivePortalService.h" #include "nsIObjectLoadingContent.h" -#include "ProfilerParent.h" #include "nsIBidiKeyboard.h" @@ -253,10 +255,6 @@ #include "nsAccessibilityService.h" #endif -#ifdef MOZ_GECKO_PROFILER -#include "nsIProfiler.h" -#endif - // For VP9Benchmark::sBenchmarkFpsPref #include "Benchmark.h" @@ -1069,6 +1067,38 @@ ContentParent::RecvRemovePermission(const IPC::Principal& aPrincipal, return IPC_OK(); } +void +ContentParent::SendStartProfiler(const ProfilerInitParams& aParams) +{ + if (mSubprocess && mIsAlive) { + Unused << PContentParent::SendStartProfiler(aParams); + } +} + +void +ContentParent::SendStopProfiler() +{ + if (mSubprocess && mIsAlive) { + Unused << PContentParent::SendStopProfiler(); + } +} + +void +ContentParent::SendPauseProfiler(const bool& aPause) +{ + if (mSubprocess && mIsAlive) { + Unused << PContentParent::SendPauseProfiler(aPause); + } +} + +void +ContentParent::SendGatherProfile() +{ + if (mSubprocess && mIsAlive) { + Unused << PContentParent::SendGatherProfile(); + } +} + mozilla::ipc::IPCResult ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv, @@ -1322,6 +1352,10 @@ ContentParent::Init() } #endif +#ifdef MOZ_GECKO_PROFILER + mProfilerController = MakeUnique(this); +#endif + // Ensure that the default set of permissions are avaliable in the content // process before we try to load any URIs in it. EnsurePermissionsByKey(EmptyCString()); @@ -1716,6 +1750,10 @@ ContentParent::ActorDestroy(ActorDestroyReason why) mConsoleService = nullptr; +#ifdef MOZ_GECKO_PROFILER + mProfilerController = nullptr; +#endif + if (obs) { RefPtr props = new nsHashPropertyBag(); @@ -2754,15 +2792,6 @@ ContentParent::RecvInitBackground(Endpoint&& aEndpoint) return IPC_OK(); } -mozilla::ipc::IPCResult -ContentParent::RecvInitProfiler(Endpoint&& aEndpoint) -{ - if (!ProfilerParent::Alloc(Move(aEndpoint))) { - NS_WARNING("ProfilerParent::Alloc failed"); - } - return IPC_OK(); -} - mozilla::jsipc::PJavaScriptParent * ContentParent::AllocPJavaScriptParent() { @@ -4669,11 +4698,12 @@ ContentParent::RecvCreateWindowInDifferentProcess( } mozilla::ipc::IPCResult -ContentParent::RecvShutdownProfile(const nsCString& aProfile) +ContentParent::RecvProfile(const nsCString& aProfile, const bool& aIsExitProfile) { #ifdef MOZ_GECKO_PROFILER - nsCOMPtr profiler(do_GetService("@mozilla.org/tools/profiler;1")); - profiler->ReceiveShutdownProfile(aProfile); + if (mProfilerController) { + mProfilerController->RecvProfile(aProfile, aIsExitProfile); + } #endif return IPC_OK(); } diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index aa6467e031f7..87be8f5e19f1 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -30,6 +30,7 @@ #include "nsIDOMGeoPositionErrorCallback.h" #include "nsRefPtrHashtable.h" #include "PermissionMessageUtils.h" +#include "ProfilerControllingProcess.h" #include "DriverCrashGuard.h" #define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown") @@ -55,6 +56,9 @@ class nsIWidget; namespace mozilla { class PRemoteSpellcheckEngineParent; +#ifdef MOZ_GECKO_PROFILER +class CrossProcessProfilerController; +#endif #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) class SandboxBroker; @@ -110,6 +114,7 @@ class ContentParent final : public PContentParent , public mozilla::LinkedListElement , public gfx::GPUProcessListener , public mozilla::MemoryReportingProcess + , public mozilla::ProfilerControllingProcess { typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost; typedef mozilla::ipc::OptionalURIParams OptionalURIParams; @@ -307,6 +312,11 @@ public: const nsCString& aPermissionType, nsresult* aRv) override; + void SendStartProfiler(const ProfilerInitParams& aParams) override; + void SendStopProfiler() override; + void SendPauseProfiler(const bool& aPause) override; + void SendGatherProfile() override; + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver) NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -815,7 +825,6 @@ private: static nsTArray& GetOrCreatePool(const nsAString& aContentProcessType); virtual mozilla::ipc::IPCResult RecvInitBackground(Endpoint&& aEndpoint) override; - virtual mozilla::ipc::IPCResult RecvInitProfiler(Endpoint&& aEndpoint) override; mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override; mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override; @@ -1122,7 +1131,8 @@ private: virtual mozilla::ipc::IPCResult RecvUpdateDropEffect(const uint32_t& aDragAction, const uint32_t& aDropEffect) override; - virtual mozilla::ipc::IPCResult RecvShutdownProfile(const nsCString& aProfile) override; + virtual mozilla::ipc::IPCResult RecvProfile(const nsCString& aProfile, + const bool& aIsExitProfile) override; virtual mozilla::ipc::IPCResult RecvGetGraphicsDeviceInitData(ContentDeviceData* aOut) override; @@ -1239,6 +1249,10 @@ private: PProcessHangMonitorParent* mHangMonitorActor; +#ifdef MOZ_GECKO_PROFILER + UniquePtr mProfilerController; +#endif + UniquePtr mDriverCrashGuard; UniquePtr mMemoryReportRequest; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 5b1a615e8ae5..565eb744cf46 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -45,7 +45,6 @@ include protocol PURLClassifierLocal; include protocol PVRManager; include protocol PVideoDecoderManager; include protocol PFlyWebPublishedServer; -include protocol PProfiler; include protocol PScriptCache; include DOMTypes; include JavaScriptTypes; @@ -59,6 +58,7 @@ include PBackgroundSharedTypes; include PContentPermission; include ServiceWorkerConfiguration; include GraphicsMessages; +include ProfilerTypes; include MemoryReportTypes; // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp @@ -511,6 +511,15 @@ child: */ async LoadPluginResult(uint32_t aPluginId, bool aResult); + /** + * Control the Gecko Profiler in the child process. + */ + async StartProfiler(ProfilerInitParams params); + async StopProfiler(); + async PauseProfiler(bool aPause); + + async GatherProfile(); + async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action); async EndDragSession(bool aDoneDrag, bool aUserCancelled, @@ -620,7 +629,6 @@ child: parent: async InitBackground(Endpoint aEndpoint); - async InitProfiler(Endpoint aEndpoint); sync CreateChildProcess(IPCTabContext context, ProcessPriority priority, @@ -987,7 +995,7 @@ parent: async PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal, TabId tabId); - async ShutdownProfile(nsCString aProfile); + async Profile(nsCString aProfile, bool aIsExitProfile); /** * Request graphics initialization information from the parent. diff --git a/dom/plugins/ipc/PPluginModule.ipdl b/dom/plugins/ipc/PPluginModule.ipdl index 3671539bf346..6e8288017258 100644 --- a/dom/plugins/ipc/PPluginModule.ipdl +++ b/dom/plugins/ipc/PPluginModule.ipdl @@ -6,7 +6,7 @@ include protocol PPluginInstance; include protocol PPluginScriptableObject; include protocol PContent; -include protocol PProfiler; +include ProfilerTypes; using NPError from "npapi.h"; using NPNVariable from "npapi.h"; @@ -95,6 +95,14 @@ child: intr InitCrashReporter(Shmem shmem) returns (NativeThreadId tid); + /** + * Control the Gecko Profiler in the plugin process. + */ + async StartProfiler(ProfilerInitParams params); + async StopProfiler(); + async PauseProfiler(bool aPause); + async GatherProfile(); + async SettingChanged(PluginSettings settings); async NPP_SetValue_NPNVaudioDeviceChangeDetails(NPAudioDeviceChangeDetailsIPC changeDetails); @@ -104,8 +112,6 @@ child: parent: async NP_InitializeResult(NPError aError); - async InitProfiler(Endpoint aEndPoint); - /** * This message is only used on X11 platforms. * @@ -147,6 +153,8 @@ parent: // down the plugin process in response. async NotifyContentModuleDestroyed(); + async Profile(nsCString aProfile, bool aIsExitProfile); + // Answers to request about site data async ReturnClearSiteData(NPError aRv, uint64_t aCallbackId); diff --git a/dom/plugins/ipc/PluginModuleChild.cpp b/dom/plugins/ipc/PluginModuleChild.cpp index e8756a5f55fd..aeecd8aeff88 100644 --- a/dom/plugins/ipc/PluginModuleChild.cpp +++ b/dom/plugins/ipc/PluginModuleChild.cpp @@ -53,9 +53,7 @@ #include "mozilla/ipc/CrashReporterClient.h" #endif -#ifdef MOZ_GECKO_PROFILER -#include "ChildProfilerController.h" -#endif +#include "GeckoProfiler.h" using namespace mozilla; using namespace mozilla::ipc; @@ -286,11 +284,6 @@ PluginModuleChild::InitForChrome(const std::string& aPluginFilename, GetIPCChannel()->SetAbortOnError(true); -#ifdef MOZ_GECKO_PROFILER - mProfilerController = new ChildProfilerController(); - Unused << SendInitProfiler(mProfilerController->SetUpEndpoints(OtherPid())); -#endif - // TODO: use PluginPRLibrary here #if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS) @@ -753,13 +746,6 @@ PluginModuleChild::AnswerInitCrashReporter(Shmem&& aShmem, mozilla::dom::NativeT void PluginModuleChild::ActorDestroy(ActorDestroyReason why) { -#ifdef MOZ_GECKO_PROFILER - if (mProfilerController) { - mProfilerController->Shutdown(); - mProfilerController = nullptr; - } -#endif - if (!mIsChrome) { PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome(); if (chromeInstance) { @@ -2679,6 +2665,54 @@ PluginModuleChild::ProcessNativeEvents() { } #endif +mozilla::ipc::IPCResult +PluginModuleChild::RecvStartProfiler(const ProfilerInitParams& params) +{ + nsTArray filterArray; + for (size_t i = 0; i < params.filters().Length(); ++i) { + filterArray.AppendElement(params.filters()[i].get()); + } + + profiler_start(params.entries(), params.interval(), params.features(), + filterArray.Elements(), filterArray.Length()); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +PluginModuleChild::RecvStopProfiler() +{ + profiler_stop(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +PluginModuleChild::RecvPauseProfiler(const bool& aPause) +{ + if (aPause) { + profiler_pause(); + } else { + profiler_resume(); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +PluginModuleChild::RecvGatherProfile() +{ + nsCString profileCString; + UniquePtr profile = profiler_get_profile(); + if (profile != nullptr) { + profileCString = nsCString(profile.get(), strlen(profile.get())); + } else { + profileCString = nsCString("", 0); + } + + Unused << SendProfile(profileCString, false); + return IPC_OK(); +} + NPError PluginModuleChild::PluginRequiresAudioDeviceChanges( PluginInstanceChild* aInstance, diff --git a/dom/plugins/ipc/PluginModuleChild.h b/dom/plugins/ipc/PluginModuleChild.h index 7c6b203b2c46..bbaec559cf47 100644 --- a/dom/plugins/ipc/PluginModuleChild.h +++ b/dom/plugins/ipc/PluginModuleChild.h @@ -46,9 +46,6 @@ typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINUNIXINIT) (const NPNetscapeFun typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINSHUTDOWN) (void); namespace mozilla { - -class ChildProfilerController; - namespace plugins { class PluginInstanceChild; @@ -128,6 +125,11 @@ protected: virtual mozilla::ipc::IPCResult RecvProcessNativeEventsInInterruptCall() override; + virtual mozilla::ipc::IPCResult RecvStartProfiler(const ProfilerInitParams& params) override; + virtual mozilla::ipc::IPCResult RecvStopProfiler() override; + virtual mozilla::ipc::IPCResult RecvPauseProfiler(const bool& aPause) override; + virtual mozilla::ipc::IPCResult RecvGatherProfile() override; + virtual mozilla::ipc::IPCResult AnswerModuleSupportsAsyncRender(bool* aResult) override; public: @@ -253,10 +255,6 @@ private: bool mIsChrome; bool mHasShutdown; // true if NP_Shutdown has run -#ifdef MOZ_GECKO_PROFILER - RefPtr mProfilerController; -#endif - // we get this from the plugin NP_PLUGINSHUTDOWN mShutdownFunc; #if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS) diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index a9ace742b82a..985f32640aad 100644 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -33,8 +33,10 @@ #include "prclist.h" #include "PluginQuirks.h" #include "gfxPlatform.h" +#ifdef MOZ_GECKO_PROFILER +#include "CrossProcessProfilerController.h" +#endif #include "GeckoProfiler.h" -#include "ProfilerParent.h" #include "nsPluginTags.h" #include "nsUnicharUtils.h" #include "mozilla/layers/TextureClientRecycleAllocator.h" @@ -56,6 +58,9 @@ using base::KillProcess; using mozilla::PluginLibrary; +#ifdef MOZ_GECKO_PROFILER +using mozilla::CrossProcessProfilerController; +#endif using mozilla::ipc::MessageChannel; using mozilla::ipc::GeckoChildProcessHost; @@ -631,6 +636,10 @@ PluginModuleChromeParent::OnProcessLaunched(const bool aSucceeded) } #endif } + +#ifdef MOZ_GECKO_PROFILER + mProfilerController = MakeUnique(this); +#endif } bool @@ -770,6 +779,10 @@ PluginModuleChromeParent::~PluginModuleChromeParent() MOZ_CRASH("unsafe destruction"); } +#ifdef MOZ_GECKO_PROFILER + mProfilerController = nullptr; +#endif + #ifdef XP_WIN // If we registered for audio notifications, stop. mozilla::plugins::PluginUtilsWin::RegisterForAudioDeviceChanges(this, @@ -3120,15 +3133,6 @@ PluginModuleParent::RecvReturnSitesWithData(nsTArray&& aSites, return IPC_OK(); } -mozilla::ipc::IPCResult -PluginModuleParent::RecvInitProfiler(Endpoint&& aEndpoint) -{ - if (!ProfilerParent::Alloc(Move(aEndpoint))) { - NS_WARNING("ProfilerParent::Alloc failed"); - } - return IPC_OK(); -} - layers::TextureClientRecycleAllocator* PluginModuleParent::EnsureTextureAllocatorForDirectBitmap() { @@ -3259,6 +3263,18 @@ PluginModuleChromeParent::OnCrash(DWORD processID) #endif // MOZ_CRASHREPORTER_INJECTOR +mozilla::ipc::IPCResult +PluginModuleChromeParent::RecvProfile(const nsCString& aProfile, + const bool& aIsExitProfile) +{ +#ifdef MOZ_GECKO_PROFILER + if (mProfilerController) { + mProfilerController->RecvProfile(aProfile, aIsExitProfile); + } +#endif + return IPC_OK(); +} + mozilla::ipc::IPCResult PluginModuleParent::AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) { diff --git a/dom/plugins/ipc/PluginModuleParent.h b/dom/plugins/ipc/PluginModuleParent.h index 8059744356bd..b633338da3c0 100644 --- a/dom/plugins/ipc/PluginModuleParent.h +++ b/dom/plugins/ipc/PluginModuleParent.h @@ -29,6 +29,7 @@ #include "sandboxPermissions.h" #endif #endif +#include "ProfilerControllingProcess.h" #ifdef MOZ_CRASHREPORTER #include "nsExceptionHandler.h" @@ -38,6 +39,11 @@ class nsPluginTag; namespace mozilla { +#ifdef MOZ_GECKO_PROFILER +class CrossProcessProfilerController; +#endif + + namespace ipc { class CrashReporterHost; } // namespace ipc @@ -79,6 +85,7 @@ class FinishInjectorInitTask; class PluginModuleParent : public PPluginModuleParent , public PluginLibrary + , public mozilla::ProfilerControllingProcess #ifdef MOZ_CRASHREPORTER_INJECTOR , public CrashReporter::InjectorCrashCallback #endif @@ -140,6 +147,23 @@ public: int GetQuirks() { return mQuirks; } + void SendStartProfiler(const ProfilerInitParams& aParams) override + { + Unused << PPluginModuleParent::SendStartProfiler(aParams); + } + void SendStopProfiler() override + { + Unused << PPluginModuleParent::SendStopProfiler(); + } + void SendPauseProfiler(const bool& aPause) override + { + Unused << PPluginModuleParent::SendPauseProfiler(aPause); + } + void SendGatherProfile() override + { + Unused << PPluginModuleParent::SendGatherProfile(); + } + protected: virtual mozilla::ipc::RacyInterruptPolicy MediateInterruptRace(const MessageInfo& parent, @@ -209,6 +233,8 @@ protected: virtual mozilla::ipc::IPCResult RecvNotifyContentModuleDestroyed() override { return IPC_OK(); } + virtual mozilla::ipc::IPCResult RecvProfile(const nsCString& aProfile, const bool& aIsExitProfile) override { return IPC_OK(); } + virtual mozilla::ipc::IPCResult AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override; virtual mozilla::ipc::IPCResult RecvReturnClearSiteData(const NPError& aRv, @@ -217,8 +243,6 @@ protected: virtual mozilla::ipc::IPCResult RecvReturnSitesWithData(nsTArray&& aSites, const uint64_t& aCallbackId) override; - virtual mozilla::ipc::IPCResult RecvInitProfiler(Endpoint&& aEndpoint) override; - void SetPluginFuncs(NPPluginFuncs* aFuncs); nsresult NPP_NewInternal(NPMIMEType pluginType, NPP instance, @@ -502,6 +526,9 @@ class PluginModuleChromeParent void CachedSettingChanged(); + virtual mozilla::ipc::IPCResult + RecvProfile(const nsCString& aProfile, const bool& aIsExitProfile) override; + virtual mozilla::ipc::IPCResult AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override; @@ -653,6 +680,9 @@ private: // processes in existence! dom::ContentParent* mContentParent; nsCOMPtr mPluginOfflineObserver; +#ifdef MOZ_GECKO_PROFILER + UniquePtr mProfilerController; +#endif bool mIsBlocklisted; static bool sInstantiated; #if defined(XP_WIN) && defined(MOZ_SANDBOX) diff --git a/gfx/ipc/GPUChild.cpp b/gfx/ipc/GPUChild.cpp index a8094c117cde..d1d52da6ebaf 100644 --- a/gfx/ipc/GPUChild.cpp +++ b/gfx/ipc/GPUChild.cpp @@ -20,7 +20,9 @@ #include "mozilla/ipc/CrashReporterHost.h" #include "mozilla/layers/LayerTreeOwnerTracker.h" #include "mozilla/Unused.h" -#include "ProfilerParent.h" +#ifdef MOZ_GECKO_PROFILER +#include "CrossProcessProfilerController.h" +#endif namespace mozilla { namespace gfx { @@ -74,6 +76,10 @@ GPUChild::Init() SendInit(prefs, updates, devicePrefs, mappings); gfxVars::AddReceiver(this); + +#ifdef MOZ_GECKO_PROFILER + mProfilerController = MakeUnique(this); +#endif } void @@ -199,6 +205,17 @@ GPUChild::RecvNotifyDeviceReset(const GPUDeviceData& aData) return IPC_OK(); } +mozilla::ipc::IPCResult +GPUChild::RecvProfile(const nsCString& aProfile, const bool& aIsExitProfile) +{ +#ifdef MOZ_GECKO_PROFILER + if (mProfilerController) { + mProfilerController->RecvProfile(aProfile, aIsExitProfile); + } +#endif + return IPC_OK(); +} + bool GPUChild::SendRequestMemoryReport(const uint32_t& aGeneration, const bool& aAnonymize, @@ -214,16 +231,6 @@ GPUChild::SendRequestMemoryReport(const uint32_t& aGeneration, return true; } -mozilla::ipc::IPCResult -GPUChild::RecvInitProfiler(Endpoint&& aEndpoint) -{ - if (!ProfilerParent::Alloc(Move(aEndpoint))) { - return IPC_FAIL(this, "ProfilerParent::Alloc failed"); - } - - return IPC_OK(); -} - mozilla::ipc::IPCResult GPUChild::RecvAddMemoryReport(const MemoryReport& aReport) { @@ -243,6 +250,30 @@ GPUChild::RecvFinishMemoryReport(const uint32_t& aGeneration) return IPC_OK(); } +void +GPUChild::SendStartProfiler(const ProfilerInitParams& aParams) +{ + Unused << PGPUChild::SendStartProfiler(aParams); +} + +void +GPUChild::SendStopProfiler() +{ + Unused << PGPUChild::SendStopProfiler(); +} + +void +GPUChild::SendPauseProfiler(const bool& aPause) +{ + Unused << PGPUChild::SendPauseProfiler(aPause); +} + +void +GPUChild::SendGatherProfile() +{ + Unused << PGPUChild::SendGatherProfile(); +} + void GPUChild::ActorDestroy(ActorDestroyReason aWhy) { @@ -264,6 +295,10 @@ GPUChild::ActorDestroy(ActorDestroyReason aWhy) } +#ifdef MOZ_GECKO_PROFILER + mProfilerController = nullptr; +#endif + gfxVars::RemoveReceiver(this); mHost->OnChannelClosed(); } diff --git a/gfx/ipc/GPUChild.h b/gfx/ipc/GPUChild.h index 7399e9b961ab..550cb76f541d 100644 --- a/gfx/ipc/GPUChild.h +++ b/gfx/ipc/GPUChild.h @@ -10,9 +10,14 @@ #include "mozilla/UniquePtr.h" #include "mozilla/gfx/PGPUChild.h" #include "mozilla/gfx/gfxVarReceiver.h" +#include "ProfilerControllingProcess.h" namespace mozilla { +#ifdef MOZ_GECKO_PROFILER +class CrossProcessProfilerController; +#endif + namespace ipc { class CrashReporterHost; } // namespace ipc @@ -26,6 +31,7 @@ class GPUProcessHost; class GPUChild final : public PGPUChild , public gfxVarReceiver + , public ProfilerControllingProcess { typedef mozilla::dom::MemoryReportRequestHost MemoryReportRequestHost; @@ -55,10 +61,15 @@ public: mozilla::ipc::IPCResult RecvGraphicsError(const nsCString& aError) override; mozilla::ipc::IPCResult RecvNotifyUiObservers(const nsCString& aTopic) override; mozilla::ipc::IPCResult RecvNotifyDeviceReset(const GPUDeviceData& aData) override; - mozilla::ipc::IPCResult RecvInitProfiler(Endpoint&& aEndpoint) override; + mozilla::ipc::IPCResult RecvProfile(const nsCString& aProfile, const bool& aIsExitProfile) override; mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override; mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override; + void SendStartProfiler(const ProfilerInitParams& aParams) override; + void SendStopProfiler() override; + void SendPauseProfiler(const bool& aPause) override; + void SendGatherProfile() override; + bool SendRequestMemoryReport(const uint32_t& aGeneration, const bool& aAnonymize, const bool& aMinimizeMemoryUsage, @@ -71,6 +82,9 @@ private: UniquePtr mCrashReporter; UniquePtr mMemoryReportRequest; bool mGPUReady; +#ifdef MOZ_GECKO_PROFILER + UniquePtr mProfilerController; +#endif }; } // namespace gfx diff --git a/gfx/ipc/GPUParent.cpp b/gfx/ipc/GPUParent.cpp index ea2867060c58..80311e74c6cd 100644 --- a/gfx/ipc/GPUParent.cpp +++ b/gfx/ipc/GPUParent.cpp @@ -7,6 +7,7 @@ #include "WMF.h" #endif #include "GPUParent.h" +#include "GeckoProfiler.h" #include "gfxConfig.h" #include "gfxPlatform.h" #include "gfxPrefs.h" @@ -44,9 +45,6 @@ #ifdef MOZ_WIDGET_GTK # include #endif -#ifdef MOZ_GECKO_PROFILER -#include "ChildProfilerController.h" -#endif namespace mozilla { namespace gfx { @@ -118,10 +116,6 @@ GPUParent::Init(base::ProcessId aParentPid, mozilla::ipc::SetThisProcessName("GPU Process"); #ifdef XP_WIN wmf::MFStartup(); -#endif -#ifdef MOZ_GECKO_PROFILER - mProfilerController = new ChildProfilerController(); - Unused << SendInitProfiler(mProfilerController->SetUpEndpoints(OtherPid())); #endif return true; } @@ -388,6 +382,51 @@ GPUParent::RecvNotifyGpuObservers(const nsCString& aTopic) return IPC_OK(); } +mozilla::ipc::IPCResult +GPUParent::RecvStartProfiler(const ProfilerInitParams& params) +{ + nsTArray filterArray; + for (size_t i = 0; i < params.filters().Length(); ++i) { + filterArray.AppendElement(params.filters()[i].get()); + } + profiler_start(params.entries(), params.interval(), params.features(), + filterArray.Elements(), filterArray.Length()); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +GPUParent::RecvStopProfiler() +{ + profiler_stop(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +GPUParent::RecvPauseProfiler(const bool& aPause) +{ + if (aPause) { + profiler_pause(); + } else { + profiler_resume(); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +GPUParent::RecvGatherProfile() +{ + nsCString profileCString; + UniquePtr profile = profiler_get_profile(); + if (profile) { + profileCString = nsDependentCString(profile.get()); + } + + Unused << SendProfile(profileCString, false /* aIsExitProfile */); + return IPC_OK(); +} + mozilla::ipc::IPCResult GPUParent::RecvRequestMemoryReport(const uint32_t& aGeneration, const bool& aAnonymize, @@ -419,13 +458,6 @@ GPUParent::ActorDestroy(ActorDestroyReason aWhy) ProcessChild::QuickExit(); #endif -#ifdef MOZ_GECKO_PROFILER - if (mProfilerController) { - mProfilerController->Shutdown(); - mProfilerController = nullptr; - } -#endif - if (mVsyncBridge) { mVsyncBridge->Shutdown(); mVsyncBridge = nullptr; diff --git a/gfx/ipc/GPUParent.h b/gfx/ipc/GPUParent.h index 833d3fc97e72..b5c7d8968792 100644 --- a/gfx/ipc/GPUParent.h +++ b/gfx/ipc/GPUParent.h @@ -12,7 +12,6 @@ namespace mozilla { class TimeStamp; -class ChildProfilerController; namespace gfx { @@ -56,6 +55,10 @@ public: mozilla::ipc::IPCResult RecvAddLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override; mozilla::ipc::IPCResult RecvRemoveLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override; mozilla::ipc::IPCResult RecvNotifyGpuObservers(const nsCString& aTopic) override; + mozilla::ipc::IPCResult RecvStartProfiler(const ProfilerInitParams& params) override; + mozilla::ipc::IPCResult RecvPauseProfiler(const bool& aPause) override; + mozilla::ipc::IPCResult RecvStopProfiler() override; + mozilla::ipc::IPCResult RecvGatherProfile() override; mozilla::ipc::IPCResult RecvRequestMemoryReport( const uint32_t& generation, const bool& anonymize, @@ -67,9 +70,6 @@ public: private: const TimeStamp mLaunchTime; RefPtr mVsyncBridge; -#ifdef MOZ_GECKO_PROFILER - RefPtr mProfilerController; -#endif }; } // namespace gfx diff --git a/gfx/ipc/PGPU.ipdl b/gfx/ipc/PGPU.ipdl index 66a52e833dae..c314382292b9 100644 --- a/gfx/ipc/PGPU.ipdl +++ b/gfx/ipc/PGPU.ipdl @@ -4,10 +4,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ include GraphicsMessages; +include ProfilerTypes; include MemoryReportTypes; include protocol PCompositorBridge; include protocol PImageBridge; -include protocol PProfiler; include protocol PVRManager; include protocol PVsyncBridge; include protocol PUiCompositorController; @@ -90,6 +90,12 @@ parent: // observer service. async NotifyGpuObservers(nsCString aTopic); + // Control the Gecko Profiler in the GPU process. + async StartProfiler(ProfilerInitParams params); + async StopProfiler(); + async PauseProfiler(bool aPause); + async GatherProfile(); + async RequestMemoryReport(uint32_t generation, bool anonymize, bool minimizeMemoryUsage, @@ -100,9 +106,6 @@ child: // Init(). async InitComplete(GPUDeviceData data); - // Sent to the UI process to register the GPU process's profiler. - async InitProfiler(Endpoint endpoint); - // Sent when APZ detects checkerboarding and apz checkerboard reporting is enabled. async ReportCheckerboard(uint32_t severity, nsCString log); @@ -124,6 +127,8 @@ child: async NotifyDeviceReset(GPUDeviceData status); + // Called in response to GatherProfile. + async Profile(nsCString aProfile, bool aIsExitProfile); async AddMemoryReport(MemoryReport aReport); async FinishMemoryReport(uint32_t aGeneration); }; diff --git a/tools/profiler/core/platform-linux-android.cpp b/tools/profiler/core/platform-linux-android.cpp index f6eda665e95e..2d607b2af5e5 100644 --- a/tools/profiler/core/platform-linux-android.cpp +++ b/tools/profiler/core/platform-linux-android.cpp @@ -265,6 +265,8 @@ SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration, // mSamplerTid to a dummy value and fill it in for real in ThreadEntry(). , mSamplerTid(-1) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + #if defined(USE_EHABI_STACKWALK) mozilla::EHABIStackWalkInit(); #elif defined(USE_LUL_STACKWALK) @@ -319,6 +321,8 @@ SamplerThread::~SamplerThread() void SamplerThread::Stop(PSLockRef aLock) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + // Restore old signal handler. This is global state so it's important that // we do it now, while gPSMutex is locked. It's safe to do this now even // though this SamplerThread is still alive, because the next time the main diff --git a/tools/profiler/core/platform-macos.cpp b/tools/profiler/core/platform-macos.cpp index 1c33bea7e6d1..cdbf3627be8c 100644 --- a/tools/profiler/core/platform-macos.cpp +++ b/tools/profiler/core/platform-macos.cpp @@ -78,6 +78,8 @@ SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration, , mIntervalMicroseconds( std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5)))) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + pthread_attr_t* attr_ptr = nullptr; if (pthread_create(&mThread, attr_ptr, ThreadEntry, this) != 0) { MOZ_CRASH("pthread_create failed"); @@ -92,6 +94,7 @@ SamplerThread::~SamplerThread() void SamplerThread::Stop(PSLockRef aLock) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); } void diff --git a/tools/profiler/core/platform-win32.cpp b/tools/profiler/core/platform-win32.cpp index cd8bd4d14e2d..aa108cd9aeaa 100644 --- a/tools/profiler/core/platform-win32.cpp +++ b/tools/profiler/core/platform-win32.cpp @@ -95,6 +95,8 @@ SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration, , mIntervalMicroseconds( std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5)))) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + // By default we'll not adjust the timer resolution which tends to be // around 16ms. However, if the requested interval is sufficiently low // we'll try to adjust the resolution to match. @@ -130,6 +132,8 @@ SamplerThread::~SamplerThread() void SamplerThread::Stop(PSLockRef aLock) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + // Disable any timer resolution changes we've made. Do it now while // gPSMutex is locked, i.e. before any other SamplerThread can be created // and call ::timeBeginPeriod(). diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp index f24bdb89cf9c..300b383c1dc2 100644 --- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -35,7 +35,6 @@ #include "nsMemoryReporterManager.h" #include "nsXULAppAPI.h" #include "nsProfilerStartParams.h" -#include "ProfilerParent.h" #include "mozilla/Services.h" #include "nsThreadUtils.h" #include "ProfilerMarkerPayload.h" @@ -299,32 +298,16 @@ private: } if (mInterposeObserver) { - // We need to register the observer on the main thread, because we want - // to observe IO that happens on the main thread. - if (NS_IsMainThread()) { - IOInterposer::Register(IOInterposeObserver::OpAll, mInterposeObserver); - } else { - RefPtr observer = mInterposeObserver; - NS_DispatchToMainThread(NS_NewRunnableFunction([=]() { - IOInterposer::Register(IOInterposeObserver::OpAll, observer); - })); - } + mozilla::IOInterposer::Register(mozilla::IOInterposeObserver::OpAll, + mInterposeObserver.get()); } } ~ActivePS() { if (mInterposeObserver) { - // We need to unregister the observer on the main thread, because that's - // where we've registered it. - if (NS_IsMainThread()) { - IOInterposer::Unregister(IOInterposeObserver::OpAll, mInterposeObserver); - } else { - RefPtr observer = mInterposeObserver; - NS_DispatchToMainThread(NS_NewRunnableFunction([=]() { - IOInterposer::Unregister(IOInterposeObserver::OpAll, observer); - })); - } + mozilla::IOInterposer::Unregister(mozilla::IOInterposeObserver::OpAll, + mInterposeObserver.get()); } } @@ -467,7 +450,7 @@ private: SamplerThread* const mSamplerThread; // The interposer that records main thread I/O. - const RefPtr mInterposeObserver; + const UniquePtr mInterposeObserver; // Is the profiler paused? bool mIsPaused; @@ -549,6 +532,19 @@ MOZ_THREAD_LOCAL(PseudoStack*) sPseudoStack; // The name of the main thread. static const char* const kMainThreadName = "GeckoMain"; +static bool +CanNotifyObservers() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + +#if defined(GP_OS_android) + // Android ANR reporter uses the profiler off the main thread. + return NS_IsMainThread(); +#else + return true; +#endif +} + //////////////////////////////////////////////////////////////////////// // BEGIN tick/unwinding code @@ -1384,6 +1380,7 @@ static void StreamTaskTracer(PSLockRef aLock, SpliceableJSONWriter& aWriter) { #ifdef MOZ_TASK_TRACER + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock)); aWriter.StartArrayProperty("data"); @@ -1420,28 +1417,10 @@ StreamTaskTracer(PSLockRef aLock, SpliceableJSONWriter& aWriter) static void StreamMetaJSCustomObject(PSLockRef aLock, SpliceableJSONWriter& aWriter) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock)); aWriter.IntProperty("version", 6); - - // The "startTime" field holds the number of milliseconds since midnight - // January 1, 1970 GMT. This grotty code computes (Now - (Now - - // ProcessStartTime)) to convert CorePS::ProcessStartTime() into that form. - mozilla::TimeDuration delta = - mozilla::TimeStamp::Now() - CorePS::ProcessStartTime(aLock); - aWriter.DoubleProperty( - "startTime", static_cast(PR_Now()/1000.0 - delta.ToMilliseconds())); - - if (!NS_IsMainThread()) { - // Leave the rest of the properties out if we're not on the main thread. - // At the moment, the only case in which this function is called on a - // background thread is if we're in a content process and are going to - // send this profile to the parent process. In that case, the parent - // process profile's "meta" object already has all this information, and - // the parent process profile is dumped on its main thread. - return; - } - aWriter.DoubleProperty("interval", ActivePS::Interval(aLock)); aWriter.IntProperty("stackwalk", ActivePS::FeatureStackWalk(aLock)); @@ -1456,6 +1435,14 @@ StreamMetaJSCustomObject(PSLockRef aLock, SpliceableJSONWriter& aWriter) bool asyncStacks = Preferences::GetBool("javascript.options.asyncstack"); aWriter.IntProperty("asyncstack", asyncStacks); + // The "startTime" field holds the number of milliseconds since midnight + // January 1, 1970 GMT. This grotty code computes (Now - (Now - + // ProcessStartTime)) to convert CorePS::ProcessStartTime() into that form. + mozilla::TimeDuration delta = + mozilla::TimeStamp::Now() - CorePS::ProcessStartTime(aLock); + aWriter.DoubleProperty( + "startTime", static_cast(PR_Now()/1000.0 - delta.ToMilliseconds())); + aWriter.IntProperty("processType", XRE_GetProcessType()); nsresult res; @@ -1567,6 +1554,7 @@ locked_profiler_stream_json_for_this_process(PSLockRef aLock, { LOG("locked_profiler_stream_json_for_this_process"); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock)); // Put shared library info @@ -1631,6 +1619,7 @@ profiler_stream_json_for_this_process(SpliceableJSONWriter& aWriter, double aSin { LOG("profiler_stream_json_for_this_process"); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists()); PSAutoLock lock(gPSMutex); @@ -2002,24 +1991,19 @@ locked_register_thread(PSLockRef aLock, const char* aName, void* stackTop) CorePS::LiveThreads(aLock).push_back(info); } -static void -NotifyObservers(const char* aTopic, nsISupports* aSubject = nullptr) -{ - if (!NS_IsMainThread()) { - nsCOMPtr subject = aSubject; - NS_DispatchToMainThread(NS_NewRunnableFunction([=] { NotifyObservers(aTopic, subject); })); - return; - } - - if (nsCOMPtr os = services::GetObserverService()) { - os->NotifyObservers(aSubject, aTopic, nullptr); - } -} - static void NotifyProfilerStarted(const int aEntries, double aInterval, uint32_t aFeatures, const char** aFilters, uint32_t aFilterCount) { + if (!CanNotifyObservers()) { + return; + } + + nsCOMPtr os = mozilla::services::GetObserverService(); + if (!os) { + return; + } + nsTArray filtersArray; for (size_t i = 0; i < aFilterCount; ++i) { filtersArray.AppendElement(aFilters[i]); @@ -2028,8 +2012,22 @@ NotifyProfilerStarted(const int aEntries, double aInterval, uint32_t aFeatures, nsCOMPtr params = new nsProfilerStartParams(aEntries, aInterval, aFeatures, filtersArray); - ProfilerParent::ProfilerStarted(params); - NotifyObservers("profiler-started", params); + os->NotifyObservers(params, "profiler-started", nullptr); +} + +static void +NotifyObservers(const char* aTopic) +{ + if (!CanNotifyObservers()) { + return; + } + + nsCOMPtr os = mozilla::services::GetObserverService(); + if (!os) { + return; + } + + os->NotifyObservers(nullptr, aTopic, nullptr); } static void @@ -2174,7 +2172,6 @@ profiler_shutdown() // We do these operations with gPSMutex unlocked. The comments in // profiler_stop() explain why. if (samplerThread) { - ProfilerParent::ProfilerStopped(); NotifyObservers("profiler-stopped"); delete samplerThread; } @@ -2185,6 +2182,7 @@ profiler_get_profile(double aSinceTime) { LOG("profiler_get_profile"); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists()); SpliceableChunkedJSONWriter b; @@ -2208,6 +2206,7 @@ void profiler_get_start_params(int* aEntries, double* aInterval, uint32_t* aFeatures, mozilla::Vector* aFilters) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists()); if (NS_WARN_IF(!aEntries) || NS_WARN_IF(!aInterval) || @@ -2283,6 +2282,7 @@ profiler_save_profile_to_file(const char* aFilename) uint32_t profiler_get_available_features() { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists()); uint32_t features = 0; @@ -2354,6 +2354,7 @@ locked_profiler_start(PSLockRef aLock, int aEntries, double aInterval, } } + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists() && !ActivePS::Exists(aLock)); // Fall back to the default values if the passed-in values are unreasonable. @@ -2410,6 +2411,7 @@ profiler_start(int aEntries, double aInterval, uint32_t aFeatures, { LOG("profiler_start"); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); SamplerThread* samplerThread = nullptr; { @@ -2432,7 +2434,6 @@ profiler_start(int aEntries, double aInterval, uint32_t aFeatures, // We do these operations with gPSMutex unlocked. The comments in // profiler_stop() explain why. if (samplerThread) { - ProfilerParent::ProfilerStopped(); NotifyObservers("profiler-stopped"); delete samplerThread; } @@ -2445,6 +2446,7 @@ locked_profiler_stop(PSLockRef aLock) { LOG("locked_profiler_stop"); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock)); #ifdef MOZ_TASK_TRACER @@ -2492,6 +2494,7 @@ profiler_stop() { LOG("profiler_stop"); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists()); SamplerThread* samplerThread; @@ -2508,7 +2511,6 @@ profiler_stop() // We notify observers with gPSMutex unlocked. Otherwise we might get a // deadlock, if code run by the observer calls a profiler function that locks // gPSMutex. (This has been seen in practise in bug 1346356.) - ProfilerParent::ProfilerStopped(); NotifyObservers("profiler-stopped"); // We delete with gPSMutex unlocked. Otherwise we would get a deadlock: we @@ -2526,6 +2528,7 @@ profiler_stop() bool profiler_is_paused() { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists()); PSAutoLock lock(gPSMutex); @@ -2542,6 +2545,7 @@ profiler_pause() { LOG("profiler_pause"); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists()); { @@ -2555,7 +2559,6 @@ profiler_pause() } // gPSMutex must be unlocked when we notify, to avoid potential deadlocks. - ProfilerParent::ProfilerPaused(); NotifyObservers("profiler-paused"); } @@ -2564,6 +2567,7 @@ profiler_resume() { LOG("profiler_resume"); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(CorePS::Exists()); { @@ -2577,7 +2581,6 @@ profiler_resume() } // gPSMutex must be unlocked when we notify, to avoid potential deadlocks. - ProfilerParent::ProfilerResumed(); NotifyObservers("profiler-resumed"); } diff --git a/tools/profiler/gecko/ChildProfilerController.cpp b/tools/profiler/gecko/ChildProfilerController.cpp deleted file mode 100644 index ac99b597cd45..000000000000 --- a/tools/profiler/gecko/ChildProfilerController.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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 "ChildProfilerController.h" -#include "nsThreadUtils.h" -#include "ProfilerChild.h" - -using namespace mozilla::ipc; - -namespace mozilla { - -ChildProfilerController::ChildProfilerController() -{ - MOZ_COUNT_CTOR(ChildProfilerController); - MOZ_RELEASE_ASSERT(NS_IsMainThread()); -} - -Endpoint -ChildProfilerController::SetUpEndpoints(base::ProcessId aOtherPid) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - Endpoint parent; - Endpoint child; - nsresult rv = PProfiler::CreateEndpoints(aOtherPid, - base::GetCurrentProcId(), - &parent, &child); - - if (NS_FAILED(rv)) { - MOZ_CRASH("Failed to create top level actor for PProfiler!"); - } - - if (NS_SUCCEEDED(NS_NewNamedThread("ProfilerChild", getter_AddRefs(mThread)))) { - // Now that mThread has been set, run SetupProfilerChild on the thread. - mThread->Dispatch(NewRunnableMethod&&>( - this, &ChildProfilerController::SetupProfilerChild, Move(child)), - NS_DISPATCH_NORMAL); - } - - return parent; -} - -nsCString -ChildProfilerController::GrabShutdownProfileAndShutdown() -{ - nsCString shutdownProfile; - ShutdownAndMaybeGrabShutdownProfileFirst(&shutdownProfile); - return shutdownProfile; -} - -void -ChildProfilerController::Shutdown() -{ - ShutdownAndMaybeGrabShutdownProfileFirst(nullptr); -} - -void -ChildProfilerController::ShutdownAndMaybeGrabShutdownProfileFirst(nsCString* aOutShutdownProfile) -{ - if (mThread) { - mThread->Dispatch(NewRunnableMethod( - this, &ChildProfilerController::ShutdownProfilerChild, aOutShutdownProfile), - NS_DISPATCH_NORMAL); - // Shut down the thread. This call will spin until all runnables (including - // the ShutdownProfilerChild runnable) have been processed. - mThread->Shutdown(); - mThread = nullptr; - } -} - -ChildProfilerController::~ChildProfilerController() -{ - MOZ_COUNT_DTOR(ChildProfilerController); - - MOZ_ASSERT(!mThread, "Please call Shutdown before destroying ChildProfilerController"); - MOZ_ASSERT(!mProfilerChild); -} - -void -ChildProfilerController::SetupProfilerChild(Endpoint&& aEndpoint) -{ - MOZ_RELEASE_ASSERT(mThread == NS_GetCurrentThread()); - MOZ_ASSERT(aEndpoint.IsValid()); - - mProfilerChild = new ProfilerChild(); - Endpoint endpoint = Move(aEndpoint); - - if (!endpoint.Bind(mProfilerChild)) { - MOZ_CRASH("Failed to bind ProfilerChild!"); - } -} - -void -ChildProfilerController::ShutdownProfilerChild(nsCString* aOutShutdownProfile) -{ - MOZ_RELEASE_ASSERT(mThread == NS_GetCurrentThread()); - - if (aOutShutdownProfile) { - *aOutShutdownProfile = mProfilerChild->GrabShutdownProfile(); - } - mProfilerChild->Destroy(); - mProfilerChild = nullptr; -} - -} // namespace mozilla diff --git a/tools/profiler/gecko/CrossProcessProfilerController.cpp b/tools/profiler/gecko/CrossProcessProfilerController.cpp new file mode 100644 index 000000000000..7f06fa886eb2 --- /dev/null +++ b/tools/profiler/gecko/CrossProcessProfilerController.cpp @@ -0,0 +1,157 @@ +/* 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 "CrossProcessProfilerController.h" + +#include "mozilla/Move.h" +#include "mozilla/ProfilerTypes.h" +#include "nsIProfiler.h" +#include "nsISupports.h" +#include "nsIObserver.h" +#include "nsProfiler.h" +#include "ProfilerControllingProcess.h" + +namespace mozilla { + +static const char* sObserverTopics[] = { + "profiler-started", + "profiler-stopped", + "profiler-paused", + "profiler-resumed", + "profiler-subprocess-gather", +}; + +// ProfilerObserver is a refcounted class that gets registered with the +// observer service and just forwards Observe() calls to mController. +// This indirection makes the CrossProcessProfilerController API nicer because +// it doesn't require a separate Init() method to register with the observer +// service, and because not being refcounted allows CPPC to be managed with a +// UniquePtr. +// The life time of ProfilerObserver is bounded by the life time of CPPC: CPPC +// unregisters the ProfilerObserver from the observer service in its +// destructor, and then it drops its reference to the ProfilerObserver, which +// destroys the ProfilerObserver. +class ProfilerObserver final : public nsIObserver +{ +public: + explicit ProfilerObserver(CrossProcessProfilerController& aController) + : mController(aController) + {} + + NS_DECL_ISUPPORTS + + NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) override + { + mController.Observe(aSubject, aTopic); + return NS_OK; + } + +private: + ~ProfilerObserver() {} + + CrossProcessProfilerController& mController; +}; + +NS_IMPL_ISUPPORTS(ProfilerObserver, nsIObserver) + +CrossProcessProfilerController::CrossProcessProfilerController( + ProfilerControllingProcess* aProcess) + : mProcess(aProcess) + , mObserver(new ProfilerObserver(*this)) +{ + if (profiler_is_active()) { + // If the profiler is already running in this process, start it in the + // child process immediately. + nsCOMPtr currentProfilerParams; + nsCOMPtr profiler(do_GetService("@mozilla.org/tools/profiler;1")); + DebugOnly rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + StartProfiler(currentProfilerParams); + } + + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + size_t length = ArrayLength(sObserverTopics); + for (size_t i = 0; i < length; ++i) { + obs->AddObserver(mObserver, sObserverTopics[i], false); + } + } +} + +CrossProcessProfilerController::~CrossProcessProfilerController() +{ + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + size_t length = ArrayLength(sObserverTopics); + for (size_t i = 0; i < length; ++i) { + obs->RemoveObserver(mObserver, sObserverTopics[i]); + } + } +} + +void +CrossProcessProfilerController::StartProfiler(nsIProfilerStartParams* aParams) +{ + if (NS_WARN_IF(!aParams)) { + return; + } + + ProfilerInitParams ipcParams; + + ipcParams.enabled() = true; + aParams->GetEntries(&ipcParams.entries()); + aParams->GetInterval(&ipcParams.interval()); + aParams->GetFeatures(&ipcParams.features()); + ipcParams.filters() = aParams->GetFilters(); + + mProcess->SendStartProfiler(ipcParams); +} + +void +CrossProcessProfilerController::Observe(nsISupports* aSubject, + const char* aTopic) +{ + if (!strcmp(aTopic, "profiler-subprocess-gather")) { + // profiler-subprocess-gather is the request to capture the profile. We + // need to tell the other process that we're interested in its profile, + // and we tell the gatherer that we've forwarded the request, so that it + // can keep track of the number of pending profiles. + nsProfiler::GetOrCreate()->WillGatherOOPProfile(); + mProcess->SendGatherProfile(); + } + // These four notifications are sent by the profiler when its corresponding + // methods are called inside this process. These state changes just need to + // be forwarded to the other process. + else if (!strcmp(aTopic, "profiler-started")) { + nsCOMPtr params(do_QueryInterface(aSubject)); + StartProfiler(params); + } + else if (!strcmp(aTopic, "profiler-stopped")) { + mProcess->SendStopProfiler(); + } + else if (!strcmp(aTopic, "profiler-paused")) { + mProcess->SendPauseProfiler(true); + } + else if (!strcmp(aTopic, "profiler-resumed")) { + mProcess->SendPauseProfiler(false); + } +} + +// This is called in response to a SendGatherProfile request, or when the +// other process exits while the profiler is running. +void +CrossProcessProfilerController::RecvProfile(const nsCString& aProfile, + bool aIsExitProfile) +{ + // Pass our process's profile along to nsProfiler. + if (aIsExitProfile) { + nsProfiler::GetOrCreate()->OOPExitProfile(aProfile); + } else { + nsProfiler::GetOrCreate()->GatheredOOPProfile(aProfile); + } +} + +} // namespace mozilla diff --git a/tools/profiler/gecko/PProfiler.ipdl b/tools/profiler/gecko/PProfiler.ipdl deleted file mode 100644 index 1adb2ef9b6cb..000000000000 --- a/tools/profiler/gecko/PProfiler.ipdl +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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 ProfilerTypes; - -namespace mozilla { - -// PProfiler is a top-level protocol. It is used to let the main process -// control the Gecko Profiler in other processes, and request profiles from -// those processes. -// It is a top-level protocol so that its child endpoint can be on a -// background thread, so that profiles can be gathered even if the main thread -// is unresponsive. -async protocol PProfiler -{ -child: - async Start(ProfilerInitParams params); - async Stop(); - async Pause(); - async Resume(); - - async GatherProfile(); - -parent: - async Profile(nsCString aProfile); -}; - -} // namespace mozilla - diff --git a/tools/profiler/gecko/ProfileGatherer.cpp b/tools/profiler/gecko/ProfileGatherer.cpp index 75a68a0d996f..724738be8b95 100644 --- a/tools/profiler/gecko/ProfileGatherer.cpp +++ b/tools/profiler/gecko/ProfileGatherer.cpp @@ -60,6 +60,12 @@ ProfileGatherer::GatheredOOPProfile(const nsACString& aProfile) } } +void +ProfileGatherer::WillGatherOOPProfile() +{ + mPendingProfiles++; +} + RefPtr ProfileGatherer::Start(double aSinceTime) { @@ -72,14 +78,20 @@ ProfileGatherer::Start(double aSinceTime) } mGathering = true; + mPendingProfiles = 0; - // Request profiles from the other processes. This will trigger - // asynchronous calls to ProfileGatherer::GatheredOOPProfile as the - // profiles arrive. + // Send a notification to request profiles from other processes. The + // observers of this notification will call WillGatherOOPProfile() which + // increments mPendingProfiles. // Do this before the call to profiler_stream_json_for_this_process because // that call is slow and we want to let the other processes grab their // profiles as soon as possible. - mPendingProfiles = ProfilerParent::GatherProfiles(); + nsCOMPtr os = mozilla::services::GetObserverService(); + if (os) { + DebugOnly rv = + os->NotifyObservers(this, "profiler-subprocess-gather", nullptr); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NotifyObservers failed"); + } mWriter.emplace(); diff --git a/tools/profiler/gecko/ProfileGatherer.h b/tools/profiler/gecko/ProfileGatherer.h index 27ce09d2d259..2c474de70782 100644 --- a/tools/profiler/gecko/ProfileGatherer.h +++ b/tools/profiler/gecko/ProfileGatherer.h @@ -20,6 +20,7 @@ public: typedef MozPromise ProfileGatherPromise; explicit ProfileGatherer(); + void WillGatherOOPProfile(); void GatheredOOPProfile(const nsACString& aProfile); RefPtr Start(double aSinceTime); void OOPExitProfile(const nsACString& aProfile); diff --git a/tools/profiler/gecko/ProfilerChild.cpp b/tools/profiler/gecko/ProfilerChild.cpp deleted file mode 100644 index 76d3b83bb74e..000000000000 --- a/tools/profiler/gecko/ProfilerChild.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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 "GeckoProfiler.h" -#include "ProfilerChild.h" -#include "nsThreadUtils.h" - -namespace mozilla { - -ProfilerChild::ProfilerChild() - : mThread(NS_GetCurrentThread()) - , mDestroyed(false) -{ - MOZ_COUNT_CTOR(ProfilerChild); -} - -ProfilerChild::~ProfilerChild() -{ - MOZ_COUNT_DTOR(ProfilerChild); -} - -mozilla::ipc::IPCResult -ProfilerChild::RecvStart(const ProfilerInitParams& params) -{ - nsTArray filterArray; - for (size_t i = 0; i < params.filters().Length(); ++i) { - filterArray.AppendElement(params.filters()[i].get()); - } - - profiler_start(params.entries(), params.interval(), - params.features(), - filterArray.Elements(), - filterArray.Length()); - - return IPC_OK(); -} - -mozilla::ipc::IPCResult -ProfilerChild::RecvStop() -{ - profiler_stop(); - return IPC_OK(); -} - -mozilla::ipc::IPCResult -ProfilerChild::RecvPause() -{ - profiler_pause(); - return IPC_OK(); -} - -mozilla::ipc::IPCResult -ProfilerChild::RecvResume() -{ - profiler_resume(); - return IPC_OK(); -} - -static nsCString -CollectProfileOrEmptyString() -{ - nsCString profileCString; - UniquePtr profile = profiler_get_profile(); - if (profile) { - profileCString = nsCString(profile.get(), strlen(profile.get())); - } else { - profileCString = EmptyCString(); - } - return profileCString; -} - -mozilla::ipc::IPCResult -ProfilerChild::RecvGatherProfile() -{ - if (!mDestroyed) { - Unused << SendProfile(CollectProfileOrEmptyString()); - } - return IPC_OK(); -} - -void -ProfilerChild::ActorDestroy(ActorDestroyReason aActorDestroyReason) -{ - mDestroyed = true; -} - -void -ProfilerChild::Destroy() -{ - if (!mDestroyed) { - Close(); - } -} - -nsCString -ProfilerChild::GrabShutdownProfile() -{ - return CollectProfileOrEmptyString(); -} - -} // namespace mozilla diff --git a/tools/profiler/gecko/ProfilerIOInterposeObserver.h b/tools/profiler/gecko/ProfilerIOInterposeObserver.h index bb378622a89c..720c617f34b9 100644 --- a/tools/profiler/gecko/ProfilerIOInterposeObserver.h +++ b/tools/profiler/gecko/ProfilerIOInterposeObserver.h @@ -8,7 +8,6 @@ #ifdef MOZ_GECKO_PROFILER #include "mozilla/IOInterposer.h" -#include "nsISupportsImpl.h" namespace mozilla { @@ -18,13 +17,8 @@ namespace mozilla { */ class ProfilerIOInterposeObserver final : public IOInterposeObserver { - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProfilerIOInterposeObserver) - public: virtual void Observe(Observation& aObservation); - -protected: - virtual ~ProfilerIOInterposeObserver() {} }; } // namespace mozilla diff --git a/tools/profiler/gecko/ProfilerParent.cpp b/tools/profiler/gecko/ProfilerParent.cpp deleted file mode 100644 index 998b73b7c81b..000000000000 --- a/tools/profiler/gecko/ProfilerParent.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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 "ProfilerParent.h" - -#include "mozilla/ClearOnShutdown.h" -#include "mozilla/RefPtr.h" -#include "mozilla/Unused.h" - -#include "nsProfiler.h" -#include "nsTArray.h" -#include "nsThreadUtils.h" - -namespace mozilla { - -using namespace ipc; - -class ProfilerParentTracker final { -public: - static void StartTracking(ProfilerParent* aParent); - static void StopTracking(ProfilerParent* aParent); - - template - static void Enumerate(FuncType aIterFunc); - - ProfilerParentTracker() {} - ~ProfilerParentTracker(); - -private: - nsTArray mProfilerParents; - static UniquePtr sInstance; -}; - -UniquePtr ProfilerParentTracker::sInstance; - -/* static */ void -ProfilerParentTracker::StartTracking(ProfilerParent* aProfilerParent) -{ - if (!sInstance) { - sInstance = MakeUnique(); - ClearOnShutdown(&sInstance, ShutdownPhase::Shutdown); - } - sInstance->mProfilerParents.AppendElement(aProfilerParent); -} - -/* static */ void -ProfilerParentTracker::StopTracking(ProfilerParent* aParent) -{ - if (sInstance) { - sInstance->mProfilerParents.RemoveElement(aParent); - } -} - -template -/* static */ void -ProfilerParentTracker::Enumerate(FuncType aIterFunc) -{ - if (sInstance) { - for (ProfilerParent* profilerParent : sInstance->mProfilerParents) { - if (!profilerParent->mDestroyed) { - aIterFunc(profilerParent); - } - } - } -} - -ProfilerParentTracker::~ProfilerParentTracker() -{ - nsTArray parents; - parents = mProfilerParents; - // Close the channels of any profiler parents that haven't been destroyed. - for (ProfilerParent* profilerParent : parents) { - if (!profilerParent->mDestroyed) { - // Keep the object alive until the call to Close() has completed. - // Close() will trigger a call to DeallocPProfilerParent. - RefPtr actor = profilerParent; - actor->Close(); - } - } -} - -/* static */ bool -ProfilerParent::Alloc(Endpoint&& aEndpoint) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - Endpoint endpoint = Move(aEndpoint); - RefPtr actor = new ProfilerParent(); - - if (!endpoint.Bind(actor)) { - return false; - } - - // mSelfRef will be cleared in DeallocPProfilerParent. - actor->mSelfRef = actor; - actor->Init(); - - return true; -} - -ProfilerParent::ProfilerParent() - : mPendingRequestedProfilesCount(0) - , mDestroyed(false) -{ - MOZ_COUNT_CTOR(ProfilerParent); - - MOZ_RELEASE_ASSERT(NS_IsMainThread()); -} - -void -ProfilerParent::Init() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - ProfilerParentTracker::StartTracking(this); - - if (profiler_is_active()) { - // If the profiler is already running in this process, start it in the - // child process immediately. - int entries = 0; - double interval = 0; - mozilla::Vector filters; - uint32_t features; - profiler_get_start_params(&entries, &interval, &features, &filters); - - ProfilerInitParams ipcParams; - ipcParams.enabled() = true; - ipcParams.entries() = entries; - ipcParams.interval() = interval; - ipcParams.features() = features; - - for (uint32_t i = 0; i < filters.length(); ++i) { - ipcParams.filters().AppendElement(filters[i]); - } - - Unused << SendStart(ipcParams); - } -} - -ProfilerParent::~ProfilerParent() -{ - MOZ_COUNT_DTOR(ProfilerParent); - - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - ProfilerParentTracker::StopTracking(this); -} - -/* static */ uint32_t -ProfilerParent::GatherProfiles() -{ - if (!NS_IsMainThread()) { - return 0; - } - - uint32_t count = 0; - ProfilerParentTracker::Enumerate([&](ProfilerParent* profilerParent) { - profilerParent->mPendingRequestedProfilesCount++; - Unused << profilerParent->SendGatherProfile(); - count++; - }); - return count; -} - -/* static */ void -ProfilerParent::ProfilerStarted(nsIProfilerStartParams* aParams) -{ - if (!NS_IsMainThread()) { - return; - } - - ProfilerInitParams ipcParams; - ipcParams.enabled() = true; - aParams->GetEntries(&ipcParams.entries()); - aParams->GetInterval(&ipcParams.interval()); - aParams->GetFeatures(&ipcParams.features()); - ipcParams.filters() = aParams->GetFilters(); - - ProfilerParentTracker::Enumerate([&](ProfilerParent* profilerParent) { - Unused << profilerParent->SendStart(ipcParams); - }); -} - -/* static */ void -ProfilerParent::ProfilerStopped() -{ - if (!NS_IsMainThread()) { - return; - } - - ProfilerParentTracker::Enumerate([](ProfilerParent* profilerParent) { - Unused << profilerParent->SendStop(); - }); -} - -/* static */ void -ProfilerParent::ProfilerPaused() -{ - if (!NS_IsMainThread()) { - return; - } - - ProfilerParentTracker::Enumerate([](ProfilerParent* profilerParent) { - Unused << profilerParent->SendPause(); - }); -} - -/* static */ void -ProfilerParent::ProfilerResumed() -{ - if (!NS_IsMainThread()) { - return; - } - - ProfilerParentTracker::Enumerate([](ProfilerParent* profilerParent) { - Unused << profilerParent->SendResume(); - }); -} - -mozilla::ipc::IPCResult -ProfilerParent::RecvProfile(const nsCString& aProfile) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - MOZ_RELEASE_ASSERT(mPendingRequestedProfilesCount > 0); - mPendingRequestedProfilesCount--; - nsProfiler::GetOrCreate()->GatheredOOPProfile(aProfile); - return IPC_OK(); -} - -void -ProfilerParent::ActorDestroy(ActorDestroyReason aActorDestroyReason) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - RefPtr profiler = nsProfiler::GetOrCreate(); - while (mPendingRequestedProfilesCount > 0) { - profiler->GatheredOOPProfile(NS_LITERAL_CSTRING("")); - mPendingRequestedProfilesCount--; - } - mDestroyed = true; -} - -void -ProfilerParent::DeallocPProfilerParent() -{ - mSelfRef = nullptr; -} - -} // namespace mozilla diff --git a/tools/profiler/gecko/nsIProfiler.idl b/tools/profiler/gecko/nsIProfiler.idl index edd52dc59dfc..e305ac7b84fa 100644 --- a/tools/profiler/gecko/nsIProfiler.idl +++ b/tools/profiler/gecko/nsIProfiler.idl @@ -10,7 +10,6 @@ class nsCString; %} -[ref] native nsCString(const nsCString); [ref] native StringArrayRef(const nsTArray); /** @@ -111,7 +110,4 @@ interface nsIProfiler : nsISupports * Dump the collected profile to a file. */ void dumpProfileToFile(in string aFilename); - - [noscript, notxpcom, nostdcall] - void receiveShutdownProfile(in nsCString aProfile); }; diff --git a/tools/profiler/gecko/nsProfiler.cpp b/tools/profiler/gecko/nsProfiler.cpp index 8176e050275e..5c07e2169f09 100644 --- a/tools/profiler/gecko/nsProfiler.cpp +++ b/tools/profiler/gecko/nsProfiler.cpp @@ -480,6 +480,29 @@ nsProfiler::GetStartParams(nsIProfilerStartParams** aRetVal) return NS_OK; } +NS_IMETHODIMP +nsProfiler::GetBufferInfo(uint32_t* aCurrentPosition, uint32_t* aTotalSize, + uint32_t* aGeneration) +{ + MOZ_ASSERT(aCurrentPosition); + MOZ_ASSERT(aTotalSize); + MOZ_ASSERT(aGeneration); + profiler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration); + return NS_OK; +} + +void +nsProfiler::WillGatherOOPProfile() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + if (!mGatherer) { + return; + } + + mGatherer->WillGatherOOPProfile(); +} + void nsProfiler::GatheredOOPProfile(const nsACString& aProfile) { @@ -493,7 +516,7 @@ nsProfiler::GatheredOOPProfile(const nsACString& aProfile) } void -nsProfiler::ReceiveShutdownProfile(const nsCString& aProfile) +nsProfiler::OOPExitProfile(const nsACString& aProfile) { MOZ_RELEASE_ASSERT(NS_IsMainThread()); @@ -504,13 +527,3 @@ nsProfiler::ReceiveShutdownProfile(const nsCString& aProfile) mGatherer->OOPExitProfile(aProfile); } -NS_IMETHODIMP -nsProfiler::GetBufferInfo(uint32_t* aCurrentPosition, uint32_t* aTotalSize, - uint32_t* aGeneration) -{ - MOZ_ASSERT(aCurrentPosition); - MOZ_ASSERT(aTotalSize); - MOZ_ASSERT(aGeneration); - profiler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration); - return NS_OK; -} diff --git a/tools/profiler/gecko/nsProfiler.h b/tools/profiler/gecko/nsProfiler.h index 4d526afcbff2..f6b840680d60 100644 --- a/tools/profiler/gecko/nsProfiler.h +++ b/tools/profiler/gecko/nsProfiler.h @@ -33,7 +33,9 @@ public: return static_cast(iprofiler.get()); } + void WillGatherOOPProfile(); void GatheredOOPProfile(const nsACString& aProfile); + void OOPExitProfile(const nsACString& aProfile); private: ~nsProfiler(); diff --git a/tools/profiler/gecko/nsProfilerStartParams.h b/tools/profiler/gecko/nsProfilerStartParams.h index c9212a169c3c..d5e68f570892 100644 --- a/tools/profiler/gecko/nsProfilerStartParams.h +++ b/tools/profiler/gecko/nsProfilerStartParams.h @@ -13,7 +13,7 @@ class nsProfilerStartParams : public nsIProfilerStartParams { public: - NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_ISUPPORTS NS_DECL_NSIPROFILERSTARTPARAMS nsProfilerStartParams(uint32_t aEntries, diff --git a/tools/profiler/moz.build b/tools/profiler/moz.build index 2f8643433573..0969a5b1f883 100644 --- a/tools/profiler/moz.build +++ b/tools/profiler/moz.build @@ -10,7 +10,7 @@ if CONFIG['MOZ_GECKO_PROFILER']: 'gecko/nsIProfiler.idl', ] EXPORTS += [ - 'public/ChildProfilerController.h', + 'public/CrossProcessProfilerController.h', 'public/ProfilerMarkerPayload.h', 'public/PseudoStack.h', 'public/shared-libraries.h', @@ -24,7 +24,7 @@ if CONFIG['MOZ_GECKO_PROFILER']: 'core/ProfilerMarkerPayload.cpp', 'core/StackTop.cpp', 'core/ThreadInfo.cpp', - 'gecko/ChildProfilerController.cpp', + 'gecko/CrossProcessProfilerController.cpp', 'gecko/nsProfilerFactory.cpp', 'gecko/nsProfilerStartParams.cpp', 'gecko/ProfileGatherer.cpp', @@ -109,7 +109,6 @@ if CONFIG['MOZ_GECKO_PROFILER']: FINAL_LIBRARY = 'xul' IPDL_SOURCES += [ - 'gecko/PProfiler.ipdl', 'gecko/ProfilerTypes.ipdlh', ] @@ -118,13 +117,7 @@ include('/ipc/chromium/chromium-config.mozbuild') EXPORTS += [ 'public/GeckoProfiler.h', 'public/GeckoProfilerReporter.h', - 'public/ProfilerChild.h', - 'public/ProfilerParent.h', -] - -UNIFIED_SOURCES += [ - 'gecko/ProfilerChild.cpp', - 'gecko/ProfilerParent.cpp', + 'public/ProfilerControllingProcess.h', ] if CONFIG['MOZ_TASK_TRACER']: diff --git a/tools/profiler/public/ChildProfilerController.h b/tools/profiler/public/ChildProfilerController.h deleted file mode 100644 index e63f9eff9a5c..000000000000 --- a/tools/profiler/public/ChildProfilerController.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- Mode: C++; 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/. */ - -#ifndef ChildProfilerController_h -#define ChildProfilerController_h - -#include "mozilla/Attributes.h" -#include "mozilla/RefPtr.h" -#include "base/process.h" - -class nsCString; - -namespace mozilla { - -class ProfilerChild; -class PProfilerChild; -class PProfilerParent; - -// ChildProfilerController manages the setup and teardown of ProfilerChild. -// It's used on the main thread. -// It manages a background thread that ProfilerChild runs on. -class ChildProfilerController final -{ -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChildProfilerController) - - ChildProfilerController(); - mozilla::ipc::Endpoint SetUpEndpoints(base::ProcessId aOtherPid); - MOZ_MUST_USE nsCString GrabShutdownProfileAndShutdown(); - void Shutdown(); - -private: - virtual ~ChildProfilerController(); - void ShutdownAndMaybeGrabShutdownProfileFirst(nsCString* aOutShutdownProfile); - - // Called on mThread: - void SetupProfilerChild(mozilla::ipc::Endpoint&& aEndpoint); - void ShutdownProfilerChild(nsCString* aOutShutdownProfile); - - RefPtr mProfilerChild; // only accessed on mThread - RefPtr mThread; -}; - -} // namespace mozilla - -#endif // ChildProfilerController_h diff --git a/tools/profiler/public/CrossProcessProfilerController.h b/tools/profiler/public/CrossProcessProfilerController.h new file mode 100644 index 000000000000..cc089a3ca86b --- /dev/null +++ b/tools/profiler/public/CrossProcessProfilerController.h @@ -0,0 +1,42 @@ +/* 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/. */ + +#ifndef CrossProcessProfilerController_h +#define CrossProcessProfilerController_h + +#include "nsString.h" + +class nsIProfilerStartParams; + +namespace mozilla { + +class ProfilerObserver; +class ProfilerControllingProcess; + +// A class that calls methods on aProcess to coordinate the profiler in other +// processes. aProcess needs to implement a number of calls that trigger calls +// to the relevant profiler_* functions in the other process. +// RecvProfile needs to be called when the other process replies with its +// profile. +class CrossProcessProfilerController final +{ +public: + // aProcess is expected to outlast this CrossProcessProfilerController object. + explicit CrossProcessProfilerController(ProfilerControllingProcess* aProcess); + ~CrossProcessProfilerController(); + void RecvProfile(const nsCString& aProfile, bool aIsExitProfile); + +private: + void StartProfiler(nsIProfilerStartParams* aParams); + void Observe(nsISupports* aSubject, const char* aTopic); + + friend class ProfilerObserver; + + ProfilerControllingProcess* mProcess; + RefPtr mObserver; +}; + +} // namespace mozilla + +#endif diff --git a/tools/profiler/public/ProfilerChild.h b/tools/profiler/public/ProfilerChild.h deleted file mode 100644 index bd732f9ffd9b..000000000000 --- a/tools/profiler/public/ProfilerChild.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -#ifndef ProfilerChild_h -#define ProfilerChild_h - -#include "mozilla/PProfilerChild.h" -#include "mozilla/RefPtr.h" - -class nsIThread; - -namespace mozilla { - -// The ProfilerChild actor is created in all processes except for the main -// process. The corresponding ProfilerParent actor will be created in the main -// process, and it will notify us about profiler state changes and request -// profiles from us. -class ProfilerChild final : public PProfilerChild -{ - NS_INLINE_DECL_REFCOUNTING(ProfilerChild) - - ProfilerChild(); - - // Collects and returns a profile. - // This method can be used to grab a profile just before PProfiler is torn - // down. The collected profile should then be sent through a different - // message channel that is guaranteed to stay open long enough. - nsCString GrabShutdownProfile(); - - void Destroy(); - -private: - virtual ~ProfilerChild(); - - mozilla::ipc::IPCResult RecvStart(const ProfilerInitParams& params) override; - mozilla::ipc::IPCResult RecvStop() override; - mozilla::ipc::IPCResult RecvPause() override; - mozilla::ipc::IPCResult RecvResume() override; - mozilla::ipc::IPCResult RecvGatherProfile() override; - - void ActorDestroy(ActorDestroyReason aActorDestroyReason) override; - - nsCOMPtr mThread; - bool mDestroyed; -}; - -} // namespace mozilla - -#endif // ProfilerChild_h diff --git a/tools/profiler/public/ProfilerControllingProcess.h b/tools/profiler/public/ProfilerControllingProcess.h new file mode 100644 index 000000000000..93cb3f727f5f --- /dev/null +++ b/tools/profiler/public/ProfilerControllingProcess.h @@ -0,0 +1,26 @@ +/* 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/. */ + +#ifndef ProfilerControllingProcess_h +#define ProfilerControllingProcess_h + +namespace mozilla { + +class ProfilerInitParams; + +// Top-level process actors should implement this to integrate with +// CrossProcessProfilerController. +class ProfilerControllingProcess { +public: + virtual void SendStartProfiler(const ProfilerInitParams& aParams) = 0; + virtual void SendPauseProfiler(const bool& aPause) = 0; + virtual void SendStopProfiler() = 0; + virtual void SendGatherProfile() = 0; + + virtual ~ProfilerControllingProcess() {} +}; + +} // namespace mozilla + +#endif diff --git a/tools/profiler/public/ProfilerParent.h b/tools/profiler/public/ProfilerParent.h deleted file mode 100644 index e813742db844..000000000000 --- a/tools/profiler/public/ProfilerParent.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -#ifndef ProfilerParent_h -#define ProfilerParent_h - -#include "mozilla/RefPtr.h" -#include "mozilla/PProfilerParent.h" - -namespace mozilla { - -class ProfilerParentTracker; - -// This is the main process side of the PProfiler protocol. -// ProfilerParent instances only exist on the main thread of the main process. -// The other side (ProfilerChild) lives on a background thread in the other -// process. -// The creation of PProfiler actors is initiated from the other process. -// ProfilerParent instances are destroyed once the message channel closes, -// which can be triggered by either process, depending on which one shuts down -// first. -// All ProfilerParent instances are registered with a manager class called -// ProfilerParentTracker, which has the list of living ProfilerParent instances -// and handles shutdown. -class ProfilerParent final : public PProfilerParent -{ -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProfilerParent) - - static bool Alloc(mozilla::ipc::Endpoint&& aEndpoint); - - // The following static methods can be called on any thread, but they are - // no-ops on anything other than the main thread. - // If called on the main thread, the call will be broadcast to all - // registered processes (all processes for which we have a ProfilerParent - // object). - // At the moment, the main process always calls these methods on the main - // thread, and that's the only process in which we need to forward these - // calls to other processes. The other processes will call these methods on - // the ProfilerChild background thread, but those processes don't need to - // forward these calls any further. - - // Returns the number of profiles to expect. The gathered profiles will be - // provided asynchronously with a call to ProfileGatherer::ReceiveGatheredProfile. - static uint32_t GatherProfiles(); - - static void ProfilerStarted(nsIProfilerStartParams* aParams); - static void ProfilerStopped(); - static void ProfilerPaused(); - static void ProfilerResumed(); - -private: - friend class ProfilerParentTracker; - - ProfilerParent(); - virtual ~ProfilerParent(); - - void Init(); - void ActorDestroy(ActorDestroyReason aActorDestroyReason) override; - void DeallocPProfilerParent() override; - - mozilla::ipc::IPCResult RecvProfile(const nsCString& aProfile) override; - - RefPtr mSelfRef; - uint32_t mPendingRequestedProfilesCount; - bool mDestroyed; -}; - -} // namespace mozilla - -#endif // ProfilerParent_h