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 "MainThreadUtils.h"
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Atomics.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/mscom/Utils.h"
14 #include "mozilla/ProfilerMarkers.h"
15 #include "mozilla/Services.h"
17 #include "nsIObserver.h"
18 #include "nsIObserverService.h"
19 #include "nsISupportsImpl.h"
21 #include "nsXULAppAPI.h"
24 #include <objidlbase.h>
26 // {9DBE6B28-E5E7-4FDE-AF00-9404604E74DC}
27 static const GUID GUID_MozProfilerMarkerExtension
= {
28 0x9dbe6b28, 0xe5e7, 0x4fde, {0xaf, 0x0, 0x94, 0x4, 0x60, 0x4e, 0x74, 0xdc}};
32 class ProfilerMarkerChannelHook final
: public IChannelHook
{
33 ~ProfilerMarkerChannelHook() = default;
36 ProfilerMarkerChannelHook() : mRefCnt(0) {}
39 STDMETHODIMP
QueryInterface(REFIID aIid
, void** aOutInterface
) override
;
40 STDMETHODIMP_(ULONG
) AddRef() override
;
41 STDMETHODIMP_(ULONG
) Release() override
;
44 * IChannelHook exposes six methods: The Client* methods are called when
45 * a client is sending an IPC request, whereas the Server* methods are called
46 * when a server is receiving an IPC request.
48 * For our purposes, we only care about the client-side methods. The COM
49 * runtime invokes the methods in the following order:
50 * 1. ClientGetSize, where the hook specifies the size of its payload;
51 * 2. ClientFillBuffer, where the hook fills the channel's buffer with its
52 * payload information. NOTE: This method is only called when ClientGetSize
53 * specifies a non-zero payload size. For our purposes, since we are not
54 * sending a payload, this method will never be called!
55 * 3. ClientNotify, when the response has been received from the server.
57 * Since we want to use these hooks to record the beginning and end of a COM
58 * IPC call, we use ClientGetSize for logging the start, and ClientNotify for
61 * Finally, our implementation responds to any request matching our extension
62 * ID, however we only care about main thread COM calls.
67 ClientGetSize(REFGUID aExtensionId
, REFIID aIid
,
68 ULONG
* aOutDataSize
) override
;
70 // No-op (see the large comment above)
72 ClientFillBuffer(REFGUID aExtensionId
, REFIID aIid
, ULONG
* aDataSize
,
73 void* aDataBuf
) override
{}
76 ClientNotify(REFGUID aExtensionId
, REFIID aIid
, ULONG aDataSize
,
77 void* aDataBuffer
, DWORD aDataRep
, HRESULT aFault
) override
;
79 // We don't care about the server-side notifications, so leave as no-ops.
81 ServerNotify(REFGUID aExtensionId
, REFIID aIid
, ULONG aDataSize
,
82 void* aDataBuf
, DWORD aDataRep
) override
{}
85 ServerGetSize(REFGUID aExtensionId
, REFIID aIid
, HRESULT aFault
,
86 ULONG
* aOutDataSize
) override
{}
89 ServerFillBuffer(REFGUID aExtensionId
, REFIID aIid
, ULONG
* aDataSize
,
90 void* aDataBuf
, HRESULT aFault
) override
{}
93 void BuildMarkerName(REFIID aIid
, nsACString
& aOutMarkerName
);
96 mozilla::Atomic
<ULONG
> mRefCnt
;
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::BuildMarkerName(REFIID aIid
,
122 nsACString
& aOutMarkerName
) {
123 aOutMarkerName
.AssignLiteral("ORPC Call for ");
125 nsAutoCString iidStr
;
126 mozilla::mscom::DiagnosticNameForIID(aIid
, iidStr
);
127 aOutMarkerName
.Append(iidStr
);
130 void ProfilerMarkerChannelHook::ClientGetSize(REFGUID aExtensionId
, REFIID aIid
,
131 ULONG
* aOutDataSize
) {
132 if (aExtensionId
== GUID_MozProfilerMarkerExtension
) {
133 if (NS_IsMainThread()) {
134 nsAutoCString markerName
;
135 BuildMarkerName(aIid
, markerName
);
136 PROFILER_MARKER(markerName
, IPC
, mozilla::MarkerTiming::IntervalStart(),
141 // We don't add any payload data to the channel
147 void ProfilerMarkerChannelHook::ClientNotify(REFGUID aExtensionId
, REFIID aIid
,
148 ULONG aDataSize
, void* aDataBuffer
,
149 DWORD aDataRep
, HRESULT aFault
) {
150 if (NS_IsMainThread() && aExtensionId
== GUID_MozProfilerMarkerExtension
) {
151 nsAutoCString markerName
;
152 BuildMarkerName(aIid
, markerName
);
153 PROFILER_MARKER(markerName
, IPC
, mozilla::MarkerTiming::IntervalEnd(),
158 } // anonymous namespace
160 static void RegisterChannelHook() {
161 RefPtr
<ProfilerMarkerChannelHook
> hook(new ProfilerMarkerChannelHook());
162 mozilla::DebugOnly
<HRESULT
> hr
=
163 ::CoRegisterChannelHook(GUID_MozProfilerMarkerExtension
, hook
);
164 MOZ_ASSERT(SUCCEEDED(hr
));
169 class ProfilerStartupObserver final
: public nsIObserver
{
170 ~ProfilerStartupObserver() = default;
177 NS_IMPL_ISUPPORTS(ProfilerStartupObserver
, nsIObserver
)
179 NS_IMETHODIMP
ProfilerStartupObserver::Observe(nsISupports
* aSubject
,
181 const char16_t
* aData
) {
182 if (strcmp(aTopic
, "profiler-started")) {
186 RegisterChannelHook();
188 // Once we've set the channel hook, we don't care about this notification
189 // anymore; our channel hook will remain set for the lifetime of the process.
190 nsCOMPtr
<nsIObserverService
> obsServ(mozilla::services::GetObserverService());
191 MOZ_ASSERT(!!obsServ
);
196 obsServ
->RemoveObserver(this, "profiler-started");
200 } // anonymous namespace
205 void InitProfilerMarkers() {
206 if (!XRE_IsParentProcess()) {
210 MOZ_ASSERT(NS_IsMainThread());
211 if (!NS_IsMainThread()) {
215 if (profiler_is_active()) {
216 // If the profiler is already running, we'll immediately register our
218 RegisterChannelHook();
222 // The profiler is not running yet. To avoid unnecessary invocations of the
223 // channel hook, we won't bother with installing it until the profiler starts.
224 // Set up an observer to watch for this.
225 nsCOMPtr
<nsIObserverService
> obsServ(mozilla::services::GetObserverService());
226 MOZ_ASSERT(!!obsServ
);
231 nsCOMPtr
<nsIObserver
> obs(new ProfilerStartupObserver());
232 obsServ
->AddObserver(obs
, "profiler-started", false);
236 } // namespace mozilla