tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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