1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
7 #include "ProfilerMarkers.h"
9 #include "GeckoProfiler.h"
10 #include "MainThreadUtils.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/Atomics.h"
13 #include "mozilla/DebugOnly.h"
14 #include "mozilla/Services.h"
16 #include "nsIObserver.h"
17 #include "nsIObserverService.h"
18 #include "nsISupportsImpl.h"
19 #include "nsXULAppAPI.h"
22 #include <objidlbase.h>
24 // {9DBE6B28-E5E7-4FDE-AF00-9404604E74DC}
25 static const GUID GUID_MozProfilerMarkerExtension
= {
26 0x9dbe6b28, 0xe5e7, 0x4fde, {0xaf, 0x0, 0x94, 0x4, 0x60, 0x4e, 0x74, 0xdc}};
30 class ProfilerMarkerChannelHook final
: public IChannelHook
{
31 ~ProfilerMarkerChannelHook() = default;
34 ProfilerMarkerChannelHook() : mRefCnt(0), mMainThreadORPCDepth(0) {}
37 STDMETHODIMP
QueryInterface(REFIID aIid
, void** aOutInterface
) override
;
38 STDMETHODIMP_(ULONG
) AddRef() override
;
39 STDMETHODIMP_(ULONG
) Release() override
;
42 * IChannelHook exposes six methods: The Client* methods are called when
43 * a client is sending an IPC request, whereas the Server* methods are called
44 * when a server is receiving an IPC request.
46 * For our purposes, we only care about the client-side methods. The COM
47 * runtime invokes the methods in the following order:
48 * 1. ClientGetSize, where the hook specifies the size of its payload;
49 * 2. ClientFillBuffer, where the hook fills the channel's buffer with its
50 * payload information. NOTE: This method is only called when ClientGetSize
51 * specifies a non-zero payload size. For our purposes, since we are not
52 * sending a payload, this method will never be called!
53 * 3. ClientNotify, when the response has been received from the server.
55 * Since we want to use these hooks to record the beginning and end of a COM
56 * IPC call, we use ClientGetSize for logging the start, and ClientNotify for
59 * Finally, our implementation responds to any request matching our extension
60 * ID, however we only care about main thread COM calls.
62 * Further note that COM allows re-entrancy, however for our purposes we only
63 * care about the outermost IPC call on the main thread, so we use the
64 * mMainThreadORPCDepth variable to track that.
69 ClientGetSize(REFGUID aExtensionId
, REFIID aIid
,
70 ULONG
* aOutDataSize
) override
;
72 // No-op (see the large comment above)
74 ClientFillBuffer(REFGUID aExtensionId
, REFIID aIid
, ULONG
* aDataSize
,
75 void* aDataBuf
) override
{}
78 ClientNotify(REFGUID aExtensionId
, REFIID aIid
, ULONG aDataSize
,
79 void* aDataBuffer
, DWORD aDataRep
, HRESULT aFault
) override
;
81 // We don't care about the server-side notifications, so leave as no-ops.
83 ServerNotify(REFGUID aExtensionId
, REFIID aIid
, ULONG aDataSize
,
84 void* aDataBuf
, DWORD aDataRep
) override
{}
87 ServerGetSize(REFGUID aExtensionId
, REFIID aIid
, HRESULT aFault
,
88 ULONG
* aOutDataSize
) override
{}
91 ServerFillBuffer(REFGUID aExtensionId
, REFIID aIid
, ULONG
* aDataSize
,
92 void* aDataBuf
, HRESULT aFault
) override
{}
95 mozilla::Atomic
<ULONG
> mRefCnt
;
96 ULONG mMainThreadORPCDepth
;
99 HRESULT
ProfilerMarkerChannelHook::QueryInterface(REFIID aIid
,
100 void** aOutInterface
) {
101 if (aIid
== IID_IChannelHook
|| aIid
== IID_IUnknown
) {
102 RefPtr
<IChannelHook
> ptr(this);
103 ptr
.forget(aOutInterface
);
107 return E_NOINTERFACE
;
110 ULONG
ProfilerMarkerChannelHook::AddRef() { return ++mRefCnt
; }
112 ULONG
ProfilerMarkerChannelHook::Release() {
113 ULONG result
= --mRefCnt
;
121 void ProfilerMarkerChannelHook::ClientGetSize(REFGUID aExtensionId
, REFIID aIid
,
122 ULONG
* aOutDataSize
) {
123 if (aExtensionId
== GUID_MozProfilerMarkerExtension
) {
124 if (NS_IsMainThread()) {
125 if (!mMainThreadORPCDepth
) {
126 PROFILER_TRACING_MARKER("MSCOM", "ORPC Call", IPC
,
127 TRACING_INTERVAL_START
);
130 ++mMainThreadORPCDepth
;
134 // We don't add any payload data to the channel
140 void ProfilerMarkerChannelHook::ClientNotify(REFGUID aExtensionId
, REFIID aIid
,
141 ULONG aDataSize
, void* aDataBuffer
,
142 DWORD aDataRep
, HRESULT aFault
) {
143 if (aExtensionId
== GUID_MozProfilerMarkerExtension
&& NS_IsMainThread()) {
144 MOZ_ASSERT(mMainThreadORPCDepth
> 0);
145 --mMainThreadORPCDepth
;
146 if (!mMainThreadORPCDepth
) {
147 PROFILER_TRACING_MARKER("MSCOM", "ORPC Call", IPC
, TRACING_INTERVAL_END
);
152 } // anonymous namespace
154 static void RegisterChannelHook() {
155 RefPtr
<ProfilerMarkerChannelHook
> hook(new ProfilerMarkerChannelHook());
156 mozilla::DebugOnly
<HRESULT
> hr
=
157 ::CoRegisterChannelHook(GUID_MozProfilerMarkerExtension
, hook
);
158 MOZ_ASSERT(SUCCEEDED(hr
));
163 class ProfilerStartupObserver final
: public nsIObserver
{
164 ~ProfilerStartupObserver() = default;
171 NS_IMPL_ISUPPORTS(ProfilerStartupObserver
, nsIObserver
)
173 NS_IMETHODIMP
ProfilerStartupObserver::Observe(nsISupports
* aSubject
,
175 const char16_t
* aData
) {
176 if (strcmp(aTopic
, "profiler-started")) {
180 RegisterChannelHook();
182 // Once we've set the channel hook, we don't care about this notification
183 // anymore; our channel hook will remain set for the lifetime of the process.
184 nsCOMPtr
<nsIObserverService
> obsServ(mozilla::services::GetObserverService());
185 MOZ_ASSERT(!!obsServ
);
190 obsServ
->RemoveObserver(this, "profiler-started");
194 } // anonymous namespace
199 void InitProfilerMarkers() {
200 if (!XRE_IsParentProcess()) {
204 MOZ_ASSERT(NS_IsMainThread());
205 if (!NS_IsMainThread()) {
209 if (profiler_is_active()) {
210 // If the profiler is already running, we'll immediately register our
212 RegisterChannelHook();
216 // The profiler is not running yet. To avoid unnecessary invocations of the
217 // channel hook, we won't bother with installing it until the profiler starts.
218 // Set up an observer to watch for this.
219 nsCOMPtr
<nsIObserverService
> obsServ(mozilla::services::GetObserverService());
220 MOZ_ASSERT(!!obsServ
);
225 nsCOMPtr
<nsIObserver
> obs(new ProfilerStartupObserver());
226 obsServ
->AddObserver(obs
, "profiler-started", false);
230 } // namespace mozilla