Backed out changeset f85447f6f56d (bug 1891145) for causing mochitest failures @...
[gecko.git] / ipc / mscom / ProfilerMarkers.cpp
blob29d033723f4eec576bd5ac13e8d3258c91aaac26
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"
16 #include "nsCOMPtr.h"
17 #include "nsIObserver.h"
18 #include "nsIObserverService.h"
19 #include "nsISupportsImpl.h"
20 #include "nsString.h"
21 #include "nsXULAppAPI.h"
23 #include <objbase.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}};
30 namespace {
32 class ProfilerMarkerChannelHook final : public IChannelHook {
33 ~ProfilerMarkerChannelHook() = default;
35 public:
36 ProfilerMarkerChannelHook() : mRefCnt(0) {}
38 // IUnknown
39 STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) override;
40 STDMETHODIMP_(ULONG) AddRef() override;
41 STDMETHODIMP_(ULONG) Release() override;
43 /**
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
59 * logging the end.
61 * Finally, our implementation responds to any request matching our extension
62 * ID, however we only care about main thread COM calls.
65 // IChannelHook
66 STDMETHODIMP_(void)
67 ClientGetSize(REFGUID aExtensionId, REFIID aIid,
68 ULONG* aOutDataSize) override;
70 // No-op (see the large comment above)
71 STDMETHODIMP_(void)
72 ClientFillBuffer(REFGUID aExtensionId, REFIID aIid, ULONG* aDataSize,
73 void* aDataBuf) override {}
75 STDMETHODIMP_(void)
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.
80 STDMETHODIMP_(void)
81 ServerNotify(REFGUID aExtensionId, REFIID aIid, ULONG aDataSize,
82 void* aDataBuf, DWORD aDataRep) override {}
84 STDMETHODIMP_(void)
85 ServerGetSize(REFGUID aExtensionId, REFIID aIid, HRESULT aFault,
86 ULONG* aOutDataSize) override {}
88 STDMETHODIMP_(void)
89 ServerFillBuffer(REFGUID aExtensionId, REFIID aIid, ULONG* aDataSize,
90 void* aDataBuf, HRESULT aFault) override {}
92 private:
93 void BuildMarkerName(REFIID aIid, nsACString& aOutMarkerName);
95 private:
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);
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::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(),
137 Tracing, "MSCOM");
140 if (aOutDataSize) {
141 // We don't add any payload data to the channel
142 *aOutDataSize = 0UL;
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(),
154 Tracing, "MSCOM");
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));
167 namespace {
169 class ProfilerStartupObserver final : public nsIObserver {
170 ~ProfilerStartupObserver() = default;
172 public:
173 NS_DECL_ISUPPORTS
174 NS_DECL_NSIOBSERVER
177 NS_IMPL_ISUPPORTS(ProfilerStartupObserver, nsIObserver)
179 NS_IMETHODIMP ProfilerStartupObserver::Observe(nsISupports* aSubject,
180 const char* aTopic,
181 const char16_t* aData) {
182 if (strcmp(aTopic, "profiler-started")) {
183 return NS_OK;
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);
192 if (!obsServ) {
193 return NS_OK;
196 obsServ->RemoveObserver(this, "profiler-started");
197 return NS_OK;
200 } // anonymous namespace
202 namespace mozilla {
203 namespace mscom {
205 void InitProfilerMarkers() {
206 if (!XRE_IsParentProcess()) {
207 return;
210 MOZ_ASSERT(NS_IsMainThread());
211 if (!NS_IsMainThread()) {
212 return;
215 if (profiler_is_active()) {
216 // If the profiler is already running, we'll immediately register our
217 // channel hook.
218 RegisterChannelHook();
219 return;
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);
227 if (!obsServ) {
228 return;
231 nsCOMPtr<nsIObserver> obs(new ProfilerStartupObserver());
232 obsServ->AddObserver(obs, "profiler-started", false);
235 } // namespace mscom
236 } // namespace mozilla