Bug 1651162 [wpt PR 24490] - Origin isolation: add WPTs for different ports, a=testonly
[gecko.git] / ipc / mscom / ProfilerMarkers.cpp
blob2a9353334b592075fa93ed963171e2eb03c337b3
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"
15 #include "nsCOMPtr.h"
16 #include "nsIObserver.h"
17 #include "nsIObserverService.h"
18 #include "nsISupportsImpl.h"
19 #include "nsXULAppAPI.h"
21 #include <objbase.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}};
28 namespace {
30 class ProfilerMarkerChannelHook final : public IChannelHook {
31 ~ProfilerMarkerChannelHook() = default;
33 public:
34 ProfilerMarkerChannelHook() : mRefCnt(0), mMainThreadORPCDepth(0) {}
36 // IUnknown
37 STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) override;
38 STDMETHODIMP_(ULONG) AddRef() override;
39 STDMETHODIMP_(ULONG) Release() override;
41 /**
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
57 * logging the end.
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.
67 // IChannelHook
68 STDMETHODIMP_(void)
69 ClientGetSize(REFGUID aExtensionId, REFIID aIid,
70 ULONG* aOutDataSize) override;
72 // No-op (see the large comment above)
73 STDMETHODIMP_(void)
74 ClientFillBuffer(REFGUID aExtensionId, REFIID aIid, ULONG* aDataSize,
75 void* aDataBuf) override {}
77 STDMETHODIMP_(void)
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.
82 STDMETHODIMP_(void)
83 ServerNotify(REFGUID aExtensionId, REFIID aIid, ULONG aDataSize,
84 void* aDataBuf, DWORD aDataRep) override {}
86 STDMETHODIMP_(void)
87 ServerGetSize(REFGUID aExtensionId, REFIID aIid, HRESULT aFault,
88 ULONG* aOutDataSize) override {}
90 STDMETHODIMP_(void)
91 ServerFillBuffer(REFGUID aExtensionId, REFIID aIid, ULONG* aDataSize,
92 void* aDataBuf, HRESULT aFault) override {}
94 private:
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);
104 return S_OK;
107 return E_NOINTERFACE;
110 ULONG ProfilerMarkerChannelHook::AddRef() { return ++mRefCnt; }
112 ULONG ProfilerMarkerChannelHook::Release() {
113 ULONG result = --mRefCnt;
114 if (!result) {
115 delete this;
118 return result;
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;
133 if (aOutDataSize) {
134 // We don't add any payload data to the channel
135 *aOutDataSize = 0UL;
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));
161 namespace {
163 class ProfilerStartupObserver final : public nsIObserver {
164 ~ProfilerStartupObserver() = default;
166 public:
167 NS_DECL_ISUPPORTS
168 NS_DECL_NSIOBSERVER
171 NS_IMPL_ISUPPORTS(ProfilerStartupObserver, nsIObserver)
173 NS_IMETHODIMP ProfilerStartupObserver::Observe(nsISupports* aSubject,
174 const char* aTopic,
175 const char16_t* aData) {
176 if (strcmp(aTopic, "profiler-started")) {
177 return NS_OK;
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);
186 if (!obsServ) {
187 return NS_OK;
190 obsServ->RemoveObserver(this, "profiler-started");
191 return NS_OK;
194 } // anonymous namespace
196 namespace mozilla {
197 namespace mscom {
199 void InitProfilerMarkers() {
200 if (!XRE_IsParentProcess()) {
201 return;
204 MOZ_ASSERT(NS_IsMainThread());
205 if (!NS_IsMainThread()) {
206 return;
209 if (profiler_is_active()) {
210 // If the profiler is already running, we'll immediately register our
211 // channel hook.
212 RegisterChannelHook();
213 return;
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);
221 if (!obsServ) {
222 return;
225 nsCOMPtr<nsIObserver> obs(new ProfilerStartupObserver());
226 obsServ->AddObserver(obs, "profiler-started", false);
229 } // namespace mscom
230 } // namespace mozilla