diff options
Diffstat (limited to 'xpcom/build/perfprobe.cpp')
-rw-r--r-- | xpcom/build/perfprobe.cpp | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/xpcom/build/perfprobe.cpp b/xpcom/build/perfprobe.cpp new file mode 100644 index 0000000000..d5a82be59d --- /dev/null +++ b/xpcom/build/perfprobe.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/***************************** + Windows implementation of probes, using xperf + *****************************/ +#include <windows.h> +#include <wmistr.h> +#include <evntrace.h> + +#include "perfprobe.h" + +namespace mozilla { +namespace probes { + +#if defined(MOZ_LOGGING) +static LazyLogModule sProbeLog("SysProbe"); +# define LOG(x) MOZ_LOG(sProbeLog, mozilla::LogLevel::Debug, x) +#else +# define LOG(x) +#endif + +// Utility function +GUID CID_to_GUID(const nsCID& aCID) { + GUID result; + result.Data1 = aCID.m0; + result.Data2 = aCID.m1; + result.Data3 = aCID.m2; + for (int i = 0; i < 8; ++i) { + result.Data4[i] = aCID.m3[i]; + } + return result; +} + +// Implementation of Probe + +Probe::Probe(const nsCID& aGUID, const nsACString& aName, + ProbeManager* aManager) + : mGUID(CID_to_GUID(aGUID)), mName(aName), mManager(aManager) {} + +nsresult Probe::Trigger() { + if (!(mManager->mIsActive)) { + // Do not trigger if there is no session + return NS_OK; + } + + _EVENT_TRACE_HEADER event; + ZeroMemory(&event, sizeof(event)); + event.Size = sizeof(event); + event.Flags = WNODE_FLAG_TRACED_GUID; + event.Guid = (const GUID)mGUID; + event.Class.Type = 1; + event.Class.Version = 0; + event.Class.Level = TRACE_LEVEL_INFORMATION; + + ULONG result = TraceEvent(mManager->mSessionHandle, &event); + + LOG(("Probes: Triggered %s, %s, %ld", mName.Data(), + result == ERROR_SUCCESS ? "success" : "failure", result)); + + nsresult rv; + switch (result) { + case ERROR_SUCCESS: + rv = NS_OK; + break; + case ERROR_INVALID_FLAG_NUMBER: + case ERROR_MORE_DATA: + case ERROR_INVALID_PARAMETER: + rv = NS_ERROR_INVALID_ARG; + break; + case ERROR_INVALID_HANDLE: + rv = NS_ERROR_FAILURE; + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + rv = NS_ERROR_OUT_OF_MEMORY; + break; + default: + rv = NS_ERROR_UNEXPECTED; + } + return rv; +} + +// Implementation of ProbeManager + +ProbeManager::~ProbeManager() { + // If the manager goes out of scope, stop the session. + if (mIsActive && mRegistrationHandle) { + StopSession(); + } +} + +ProbeManager::ProbeManager(const nsCID& aApplicationUID, + const nsACString& aApplicationName) + : mIsActive(false), + mApplicationUID(aApplicationUID), + mApplicationName(aApplicationName), + mSessionHandle(0), + mRegistrationHandle(0), + mInitialized(false) { +#if defined(MOZ_LOGGING) + char cidStr[NSID_LENGTH]; + aApplicationUID.ToProvidedString(cidStr); + LOG(("ProbeManager::Init for application %s, %s", aApplicationName.Data(), + cidStr)); +#endif +} + +// Note: The Windows API is just a little bit scary there. +// The only way to obtain the session handle is to +//- ignore the session handle obtained from RegisterTraceGuids +//- pass a callback +//- in that callback, request the session handle through +// GetTraceLoggerHandle and some opaque value received by the callback + +ULONG WINAPI ControlCallback(WMIDPREQUESTCODE aRequestCode, PVOID aContext, + ULONG* aReserved, PVOID aBuffer) { + ProbeManager* context = (ProbeManager*)aContext; + switch (aRequestCode) { + case WMI_ENABLE_EVENTS: { + context->mIsActive = true; + TRACEHANDLE sessionHandle = GetTraceLoggerHandle(aBuffer); + // Note: We only accept one handle + if ((HANDLE)sessionHandle == INVALID_HANDLE_VALUE) { + ULONG result = GetLastError(); + LOG(("Probes: ControlCallback failed, %lu", result)); + return result; + } else if (context->mIsActive && context->mSessionHandle && + context->mSessionHandle != sessionHandle) { + LOG( + ("Probes: Can only handle one context at a time, " + "ignoring activation")); + return ERROR_SUCCESS; + } else { + context->mSessionHandle = sessionHandle; + LOG(("Probes: ControlCallback activated")); + return ERROR_SUCCESS; + } + } + + case WMI_DISABLE_EVENTS: + context->mIsActive = false; + context->mSessionHandle = 0; + LOG(("Probes: ControlCallback deactivated")); + return ERROR_SUCCESS; + + default: + LOG(("Probes: ControlCallback does not know what to do with %d", + aRequestCode)); + return ERROR_INVALID_PARAMETER; + } +} + +already_AddRefed<Probe> ProbeManager::GetProbe(const nsCID& aEventUID, + const nsACString& aEventName) { + RefPtr<Probe> result(new Probe(aEventUID, aEventName, this)); + mAllProbes.AppendElement(result); + return result.forget(); +} + +nsresult ProbeManager::StartSession() { return StartSession(mAllProbes); } + +nsresult ProbeManager::StartSession(nsTArray<RefPtr<Probe>>& aProbes) { + const size_t probesCount = aProbes.Length(); + _TRACE_GUID_REGISTRATION* probes = new _TRACE_GUID_REGISTRATION[probesCount]; + for (unsigned int i = 0; i < probesCount; ++i) { + const Probe* probe = aProbes[i]; + const Probe* probeX = static_cast<const Probe*>(probe); + probes[i].Guid = (LPCGUID)&probeX->mGUID; + } + ULONG result = + RegisterTraceGuids(&ControlCallback + /*RequestAddress: Sets mSessions appropriately.*/, + this + /*RequestContext: Passed to ControlCallback*/, + (LPGUID)&mApplicationUID + /*ControlGuid: Tracing GUID + the cast comes from MSDN examples*/ + , + probesCount + /*GuidCount: Number of probes*/, + probes + /*TraceGuidReg: Probes registration*/, + nullptr + /*MofImagePath: Must be nullptr, says MSDN*/, + nullptr + /*MofResourceName:Must be nullptr, says MSDN*/, + &mRegistrationHandle + /*RegistrationHandle: Handler. + used only for unregistration*/ + ); + delete[] probes; + if (NS_WARN_IF(result != ERROR_SUCCESS)) { + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +nsresult ProbeManager::StopSession() { + LOG(("Probes: Stopping measures")); + if (mSessionHandle != 0) { + ULONG result = UnregisterTraceGuids(mSessionHandle); + mSessionHandle = 0; + if (result != ERROR_SUCCESS) { + return NS_ERROR_INVALID_ARG; + } + } + return NS_OK; +} + +} // namespace probes +} // namespace mozilla |