ProfilerMarkers.cpp (7519B)
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/. */ 6 7 #include "ProfilerMarkers.h" 8 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" 22 23 #include <objbase.h> 24 #include <objidlbase.h> 25 26 // {9DBE6B28-E5E7-4FDE-AF00-9404604E74DC} 27 static const GUID GUID_MozProfilerMarkerExtension = { 28 0x9dbe6b28, 0xe5e7, 0x4fde, {0xaf, 0x0, 0x94, 0x4, 0x60, 0x4e, 0x74, 0xdc}}; 29 30 namespace { 31 32 class ProfilerMarkerChannelHook final : public IChannelHook { 33 ~ProfilerMarkerChannelHook() = default; 34 35 public: 36 ProfilerMarkerChannelHook() : mRefCnt(0) {} 37 38 // IUnknown 39 STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) override; 40 STDMETHODIMP_(ULONG) AddRef() override; 41 STDMETHODIMP_(ULONG) Release() override; 42 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. 47 * 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. 56 * 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. 60 * 61 * Finally, our implementation responds to any request matching our extension 62 * ID, however we only care about main thread COM calls. 63 */ 64 65 // IChannelHook 66 STDMETHODIMP_(void) 67 ClientGetSize(REFGUID aExtensionId, REFIID aIid, 68 ULONG* aOutDataSize) override; 69 70 // No-op (see the large comment above) 71 STDMETHODIMP_(void) 72 ClientFillBuffer(REFGUID aExtensionId, REFIID aIid, ULONG* aDataSize, 73 void* aDataBuf) override {} 74 75 STDMETHODIMP_(void) 76 ClientNotify(REFGUID aExtensionId, REFIID aIid, ULONG aDataSize, 77 void* aDataBuffer, DWORD aDataRep, HRESULT aFault) override; 78 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 {} 83 84 STDMETHODIMP_(void) 85 ServerGetSize(REFGUID aExtensionId, REFIID aIid, HRESULT aFault, 86 ULONG* aOutDataSize) override {} 87 88 STDMETHODIMP_(void) 89 ServerFillBuffer(REFGUID aExtensionId, REFIID aIid, ULONG* aDataSize, 90 void* aDataBuf, HRESULT aFault) override {} 91 92 private: 93 void BuildMarkerName(REFIID aIid, nsACString& aOutMarkerName); 94 95 private: 96 mozilla::Atomic<ULONG> mRefCnt; 97 }; 98 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; 105 } 106 107 return E_NOINTERFACE; 108 } 109 110 ULONG ProfilerMarkerChannelHook::AddRef() { return ++mRefCnt; } 111 112 ULONG ProfilerMarkerChannelHook::Release() { 113 ULONG result = --mRefCnt; 114 if (!result) { 115 delete this; 116 } 117 118 return result; 119 } 120 121 void ProfilerMarkerChannelHook::BuildMarkerName(REFIID aIid, 122 nsACString& aOutMarkerName) { 123 aOutMarkerName.AssignLiteral("ORPC Call for "); 124 125 nsAutoCString iidStr; 126 mozilla::mscom::DiagnosticNameForIID(aIid, iidStr); 127 aOutMarkerName.Append(iidStr); 128 } 129 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"); 138 } 139 140 if (aOutDataSize) { 141 // We don't add any payload data to the channel 142 *aOutDataSize = 0UL; 143 } 144 } 145 } 146 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"); 155 } 156 } 157 158 } // anonymous namespace 159 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)); 165 } 166 167 namespace { 168 169 class ProfilerStartupObserver final : public nsIObserver { 170 ~ProfilerStartupObserver() = default; 171 172 public: 173 NS_DECL_ISUPPORTS 174 NS_DECL_NSIOBSERVER 175 }; 176 177 NS_IMPL_ISUPPORTS(ProfilerStartupObserver, nsIObserver) 178 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; 184 } 185 186 RegisterChannelHook(); 187 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; 194 } 195 196 obsServ->RemoveObserver(this, "profiler-started"); 197 return NS_OK; 198 } 199 200 } // anonymous namespace 201 202 namespace mozilla { 203 namespace mscom { 204 205 void InitProfilerMarkers() { 206 if (!XRE_IsParentProcess()) { 207 return; 208 } 209 210 MOZ_ASSERT(NS_IsMainThread()); 211 if (!NS_IsMainThread()) { 212 return; 213 } 214 215 if (profiler_is_active()) { 216 // If the profiler is already running, we'll immediately register our 217 // channel hook. 218 RegisterChannelHook(); 219 return; 220 } 221 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; 229 } 230 231 nsCOMPtr<nsIObserver> obs(new ProfilerStartupObserver()); 232 obsServ->AddObserver(obs, "profiler-started", false); 233 } 234 235 } // namespace mscom 236 } // namespace mozilla