diff options
author | georgesak <georgesak@chromium.org> | 2015-04-13 18:36:17 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-14 01:37:57 +0000 |
commit | 1cc86c4f686869f32dfede093a07828c73563892 (patch) | |
tree | 256f51e5a02376d70d06014f173df09ece8594c7 /base/trace_event | |
parent | 3455a3e0cbbd6b1d812518251589f0646b0a1518 (diff) | |
download | chromium_src-1cc86c4f686869f32dfede093a07828c73563892.zip chromium_src-1cc86c4f686869f32dfede093a07828c73563892.tar.gz chromium_src-1cc86c4f686869f32dfede093a07828c73563892.tar.bz2 |
Add option to export tracing events to ETW.
- Exporting of ETW events can be turned on using --trace-export-events-to-etw.
BUG=
Review URL: https://codereview.chromium.org/1038453002
Cr-Commit-Position: refs/heads/master@{#324982}
Diffstat (limited to 'base/trace_event')
-rw-r--r-- | base/trace_event/BUILD.gn | 6 | ||||
-rw-r--r-- | base/trace_event/etw_manifest/BUILD.gn | 37 | ||||
-rw-r--r-- | base/trace_event/etw_manifest/BUILD/message_compiler.py | 16 | ||||
-rw-r--r-- | base/trace_event/etw_manifest/chrome_events_win.man | 84 | ||||
-rw-r--r-- | base/trace_event/etw_manifest/etw_manifest.gyp | 39 | ||||
-rw-r--r-- | base/trace_event/trace_event.gypi | 2 | ||||
-rw-r--r-- | base/trace_event/trace_event.h | 7 | ||||
-rw-r--r-- | base/trace_event/trace_event_etw_export_win.cc | 239 | ||||
-rw-r--r-- | base/trace_event/trace_event_etw_export_win.h | 66 | ||||
-rw-r--r-- | base/trace_event/trace_event_impl.cc | 15 | ||||
-rw-r--r-- | base/trace_event/trace_event_impl.h | 3 |
11 files changed, 511 insertions, 3 deletions
diff --git a/base/trace_event/BUILD.gn b/base/trace_event/BUILD.gn index c0c6a05..1ddaaea 100644 --- a/base/trace_event/BUILD.gn +++ b/base/trace_event/BUILD.gn @@ -26,6 +26,8 @@ source_set("trace_event") { "trace_event_android.cc", "trace_event_argument.cc", "trace_event_argument.h", + "trace_event_etw_export_win.cc", + "trace_event_etw_export_win.h", "trace_event_impl.cc", "trace_event_impl.h", "trace_event_impl_constants.cc", @@ -56,6 +58,10 @@ source_set("trace_event") { "//base/third_party/dynamic_annotations", ] + if (is_win) { + deps += [ "//base/trace_event/etw_manifest:chrome_events_win" ] + } + allow_circular_includes_from = [ "//base/debug", "//base/memory", diff --git a/base/trace_event/etw_manifest/BUILD.gn b/base/trace_event/etw_manifest/BUILD.gn new file mode 100644 index 0000000..07cf80e --- /dev/null +++ b/base/trace_event/etw_manifest/BUILD.gn @@ -0,0 +1,37 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +assert(is_win, "This only runs on Windows.") + +# Makes the .h/.rc files from the .man file. +action("chrome_events_win") { + visibility = [ + "//base/trace_event/*", + "//chrome:main_dll", + ] + script = "build/message_compiler.py" + + sources = [ + "chrome_events_win.man", + ] + + outputs = [ + "$target_gen_dir/chrome_events_win.h", + "$target_gen_dir/chrome_events_win.rc", + ] + + args = [ + # Where to put the header. + "-h", + rebase_path("$target_gen_dir", root_build_dir), + + # Where to put the .rc file. + "-r", + rebase_path("$target_gen_dir", root_build_dir), + + # Generate the user-mode code. + "-um", + rebase_path("chrome_events_win.man", root_build_dir), + ] +} diff --git a/base/trace_event/etw_manifest/BUILD/message_compiler.py b/base/trace_event/etw_manifest/BUILD/message_compiler.py new file mode 100644 index 0000000..60b0eaa --- /dev/null +++ b/base/trace_event/etw_manifest/BUILD/message_compiler.py @@ -0,0 +1,16 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Runs the Microsoft Message Compiler (mc.exe). This Python adapter is for the +# GYP build, which can only run Python and not native binaries. + +import subprocess +import sys + +# mc writes to stderr, so this explicily redirects to stdout and eats it. +try: + subprocess.check_output(["mc.exe"] + sys.argv[1:], stderr=subprocess.STDOUT) +except subprocess.CalledProcessError as e: + print e.output + sys.exit(e.returncode) diff --git a/base/trace_event/etw_manifest/chrome_events_win.man b/base/trace_event/etw_manifest/chrome_events_win.man new file mode 100644 index 0000000..10a8ddf --- /dev/null +++ b/base/trace_event/etw_manifest/chrome_events_win.man @@ -0,0 +1,84 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes'?> +<instrumentationManifest + xmlns="http://schemas.microsoft.com/win/2004/08/events" + xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://schemas.microsoft.com/win/2004/08/events eventman.xsd" + > + <instrumentation> + <events> + <provider + guid="{D2D578D9-2936-45B6-A09f-30E32715F42D}" + messageFileName="chrome.dll" + name="Chrome" + resourceFileName="chrome.dll" + symbol="CHROME" + > + <channels> + <importChannel + chid="SYSTEM" + name="System" + /> + </channels> + <templates> + <template tid="tid_chrome_event"> + <data + inType="win:AnsiString" + name="Name" + /> + <data + inType="win:AnsiString" + name="Phase" + /> + <data + inType="win:AnsiString" + name="Arg Name 1" + /> + <data + inType="win:AnsiString" + name="Arg Value 1" + /> + <data + inType="win:AnsiString" + name="Arg Name 2" + /> + <data + inType="win:AnsiString" + name="Arg Value 2" + /> + <data + inType="win:AnsiString" + name="Arg Name 3" + /> + <data + inType="win:AnsiString" + name="Arg Value 3" + /> + </template> + </templates> + <events> + <event + channel="SYSTEM" + level="win:Informational" + message="$(string.ChromeEvent.EventMessage)" + opcode="win:Info" + symbol="ChromeEvent" + template="tid_chrome_event" + value="1" + /> + </events> + </provider> + </events> + </instrumentation> + <localization xmlns="http://schemas.microsoft.com/win/2004/08/events"> + <resources culture="en-US"> + <stringTable> + <string + id="ChromeEvent.EventMessage" + value="Chrome Event: %1 (%2)" + /> + </stringTable> + </resources> + </localization> +</instrumentationManifest> diff --git a/base/trace_event/etw_manifest/etw_manifest.gyp b/base/trace_event/etw_manifest/etw_manifest.gyp new file mode 100644 index 0000000..b0a8712 --- /dev/null +++ b/base/trace_event/etw_manifest/etw_manifest.gyp @@ -0,0 +1,39 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + # GN version: //base/trace_event/etw_manifest/BUILD.gn + 'target_name': 'etw_manifest', + 'type': 'static_library', + 'conditions': [ + ['OS=="win"', { + 'sources': [ + 'chrome_events_win.man', + ], + 'variables': { + 'man_output_dir': '<(SHARED_INTERMEDIATE_DIR)/base/trace_event/etw_manifest', + }, + 'rules': [{ + # Rule to run the message compiler. + 'rule_name': 'message_compiler', + 'extension': 'man', + 'outputs': [ + '<(man_output_dir)/chrome_events_win.h', + '<(man_output_dir)/chrome_events_win.rc', + ], + 'action': [ + 'mc.exe', + '-h', '<(man_output_dir)', + '-r', '<(man_output_dir)/.', + '-um', + '<(RULE_INPUT_PATH)', + ], + 'message': 'Running message compiler on <(RULE_INPUT_PATH)', + }], + }], + ], + } + ] +} diff --git a/base/trace_event/trace_event.gypi b/base/trace_event/trace_event.gypi index 36e3ab3..b586f64 100644 --- a/base/trace_event/trace_event.gypi +++ b/base/trace_event/trace_event.gypi @@ -26,6 +26,8 @@ 'trace_event/trace_event_android.cc', 'trace_event/trace_event_argument.cc', 'trace_event/trace_event_argument.h', + 'trace_event/trace_event_etw_export_win.cc', + 'trace_event/trace_event_etw_export_win.h', 'trace_event/trace_event_impl.cc', 'trace_event/trace_event_impl.h', 'trace_event/trace_event_impl_constants.cc', diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h index 1bf9429..7c4d3ab 100644 --- a/base/trace_event/trace_event.h +++ b/base/trace_event/trace_event.h @@ -828,9 +828,10 @@ category_group, name, TRACE_ID_DONT_MANGLE(id), TRACE_EVENT_FLAG_NONE) #define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE() \ - UNLIKELY(*INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \ - (base::trace_event::TraceLog::ENABLED_FOR_RECORDING | \ - base::trace_event::TraceLog::ENABLED_FOR_EVENT_CALLBACK)) + UNLIKELY(*INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \ + (base::trace_event::TraceLog::ENABLED_FOR_RECORDING | \ + base::trace_event::TraceLog::ENABLED_FOR_EVENT_CALLBACK | \ + base::trace_event::TraceLog::ENABLED_FOR_ETW_EXPORT)) // Macro to efficiently determine if a given category group is enabled. #define TRACE_EVENT_CATEGORY_GROUP_ENABLED(category_group, ret) \ diff --git a/base/trace_event/trace_event_etw_export_win.cc b/base/trace_event/trace_event_etw_export_win.cc new file mode 100644 index 0000000..e12782c --- /dev/null +++ b/base/trace_event/trace_event_etw_export_win.cc @@ -0,0 +1,239 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/trace_event/trace_event_etw_export_win.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "base/strings/utf_string_conversions.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_impl.h" + +// The GetProcAddress technique is borrowed from +// https://github.com/randomascii/main/tree/master/xperf/ETWProviders +// +// EVNTAPI is used in evntprov.h which is included by chrome_events_win.h. +// We define EVNTAPI without the DECLSPEC_IMPORT specifier so that we can +// implement these functions locally instead of using the import library, and +// can therefore still run on Windows XP. +#define EVNTAPI __stdcall +// Include the event register/write/unregister macros compiled from the manifest +// file. Note that this includes evntprov.h which requires a Vista+ Windows SDK. +// +// In SHARED_INTERMEDIATE_DIR. +#include "base/trace_event/etw_manifest/chrome_events_win.h" // NOLINT + +namespace { +// Typedefs for use with GetProcAddress +typedef ULONG(__stdcall* tEventRegister)(LPCGUID ProviderId, + PENABLECALLBACK EnableCallback, + PVOID CallbackContext, + PREGHANDLE RegHandle); +typedef ULONG(__stdcall* tEventWrite)(REGHANDLE RegHandle, + PCEVENT_DESCRIPTOR EventDescriptor, + ULONG UserDataCount, + PEVENT_DATA_DESCRIPTOR UserData); +typedef ULONG(__stdcall* tEventUnregister)(REGHANDLE RegHandle); + +tEventRegister EventRegisterProc = nullptr; +tEventWrite EventWriteProc = nullptr; +tEventUnregister EventUnregisterProc = nullptr; +} // namespace + +// Redirector function for EventRegister. Called by macros in +// chrome_events_win.h +ULONG EVNTAPI EventRegister(LPCGUID ProviderId, + PENABLECALLBACK EnableCallback, + PVOID CallbackContext, + PREGHANDLE RegHandle) { + if (EventRegisterProc) + return EventRegisterProc(ProviderId, EnableCallback, CallbackContext, + RegHandle); + return 0; +} + +// Redirector function for EventWrite. Called by macros in +// chrome_events_win.h +ULONG EVNTAPI EventWrite(REGHANDLE RegHandle, + PCEVENT_DESCRIPTOR EventDescriptor, + ULONG UserDataCount, + PEVENT_DATA_DESCRIPTOR UserData) { + if (EventWriteProc) + return EventWriteProc(RegHandle, EventDescriptor, UserDataCount, UserData); + return 0; +} + +// Redirector function for EventUnregister. Called by macros in +// chrome_events_win.h +ULONG EVNTAPI EventUnregister(REGHANDLE RegHandle) { + if (EventUnregisterProc) + return EventUnregisterProc(RegHandle); + return 0; +} + +namespace base { +namespace trace_event { + +TraceEventETWExport::TraceEventETWExport() : ETWExportEnabled_(false) { + // Find Advapi32.dll. This should always succeed. + HMODULE AdvapiDLL = ::LoadLibraryW(L"Advapi32.dll"); + if (AdvapiDLL) { + // Try to find the ETW functions. This will fail on XP. + EventRegisterProc = reinterpret_cast<tEventRegister>( + ::GetProcAddress(AdvapiDLL, "EventRegister")); + EventWriteProc = reinterpret_cast<tEventWrite>( + ::GetProcAddress(AdvapiDLL, "EventWrite")); + EventUnregisterProc = reinterpret_cast<tEventUnregister>( + ::GetProcAddress(AdvapiDLL, "EventUnregister")); + + // Register the ETW provider. If registration fails then the event logging + // calls will fail (on XP this call will do nothing). + EventRegisterChrome(); + } +} + +TraceEventETWExport::~TraceEventETWExport() { + EventUnregisterChrome(); +} + +// static +TraceEventETWExport* TraceEventETWExport::GetInstance() { + return Singleton<TraceEventETWExport, + StaticMemorySingletonTraits<TraceEventETWExport>>::get(); +} + +// static +void TraceEventETWExport::EnableETWExport() { + GetInstance()->ETWExportEnabled_ = true; +} + +// static +void TraceEventETWExport::DisableETWExport() { + GetInstance()->ETWExportEnabled_ = false; +} + +// static +void TraceEventETWExport::AddEvent( + char phase, + const unsigned char* category_group_enabled, + const char* name, + unsigned long long id, + int num_args, + const char** arg_names, + const unsigned char* arg_types, + const unsigned long long* arg_values, + const scoped_refptr<ConvertableToTraceFormat>* convertable_values) { + // We bail early in case exporting is disabled or no consumer is listening. + if (!GetInstance()->ETWExportEnabled_ || !EventEnabledChromeEvent()) + return; + + std::string phase_string; + switch (phase) { + case TRACE_EVENT_PHASE_BEGIN: + phase_string = "Begin"; + break; + case TRACE_EVENT_PHASE_END: + phase_string = "End"; + break; + case TRACE_EVENT_PHASE_COMPLETE: + phase_string = "Complete"; + break; + case TRACE_EVENT_PHASE_INSTANT: + phase_string = "Instant"; + break; + case TRACE_EVENT_PHASE_ASYNC_BEGIN: + phase_string = "Async Begin"; + break; + case TRACE_EVENT_PHASE_ASYNC_STEP_INTO: + phase_string = "Async Step Into"; + break; + case TRACE_EVENT_PHASE_ASYNC_STEP_PAST: + phase_string = "Async Step Past"; + break; + case TRACE_EVENT_PHASE_ASYNC_END: + phase_string = "Async End"; + break; + case TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN: + phase_string = "Nestable Async Begin"; + break; + case TRACE_EVENT_PHASE_NESTABLE_ASYNC_END: + phase_string = "Nestable Async End"; + break; + case TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT: + phase_string = "Nestable Async Instant"; + break; + case TRACE_EVENT_PHASE_FLOW_BEGIN: + phase_string = "Phase Flow Begin"; + break; + case TRACE_EVENT_PHASE_FLOW_STEP: + phase_string = "Phase Flow Step"; + break; + case TRACE_EVENT_PHASE_FLOW_END: + phase_string = "Phase Flow End"; + break; + case TRACE_EVENT_PHASE_METADATA: + phase_string = "Phase Metadata"; + break; + case TRACE_EVENT_PHASE_COUNTER: + phase_string = "Phase Counter"; + break; + case TRACE_EVENT_PHASE_SAMPLE: + phase_string = "Phase Sample"; + break; + case TRACE_EVENT_PHASE_CREATE_OBJECT: + phase_string = "Phase Create Object"; + break; + case TRACE_EVENT_PHASE_SNAPSHOT_OBJECT: + phase_string = "Phase Snapshot Object"; + break; + case TRACE_EVENT_PHASE_DELETE_OBJECT: + phase_string = "Phase Delete Object"; + break; + default: + phase_string.push_back(phase); + break; + } + + std::string arg_values_string[3]; + for (int i = 0; i < num_args; i++) { + if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) { + convertable_values[i]->AppendAsTraceFormat(arg_values_string + i); + } else { + TraceEvent::TraceValue trace_event; + trace_event.as_uint = arg_values[i]; + TraceEvent::AppendValueAsJSON(arg_types[i], trace_event, + arg_values_string + i); + } + } + + EventWriteChromeEvent( + name, phase_string.c_str(), num_args > 0 ? arg_names[0] : "", + arg_values_string[0].c_str(), num_args > 1 ? arg_names[1] : "", + arg_values_string[1].c_str(), num_args > 2 ? arg_names[2] : "", + arg_values_string[2].c_str()); +} + +// static +void TraceEventETWExport::AddCustomEvent(const char* name, + char const* phase, + const char* arg_name_1, + const char* arg_value_1, + const char* arg_name_2, + const char* arg_value_2, + const char* arg_name_3, + const char* arg_value_3) { + if (!GetInstance()->ETWExportEnabled_ || !EventEnabledChromeEvent()) + return; + + EventWriteChromeEvent(name, phase, arg_name_1, arg_value_1, arg_name_2, + arg_value_2, arg_name_3, arg_value_3); +} + +void TraceEventETWExport::Resurrect() { + StaticMemorySingletonTraits<TraceEventETWExport>::Resurrect(); +} + +} // namespace trace_event +} // namespace base diff --git a/base/trace_event/trace_event_etw_export_win.h b/base/trace_event/trace_event_etw_export_win.h new file mode 100644 index 0000000..73a9d89 --- /dev/null +++ b/base/trace_event/trace_event_etw_export_win.h @@ -0,0 +1,66 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains the Windows-specific exporting to ETW. +#ifndef BASE_TRACE_EVENT_TRACE_ETW_EXPORT_H_ +#define BASE_TRACE_EVENT_TRACE_ETW_EXPORT_H_ + +#include "base/base_export.h" +#include "base/trace_event/trace_event_impl.h" + +// Fwd. +template <typename Type> +struct StaticMemorySingletonTraits; + +namespace base { +namespace trace_event { + +class BASE_EXPORT TraceEventETWExport { + public: + ~TraceEventETWExport(); + + // Retrieves the singleton. + // Note that this may return NULL post-AtExit processing. + static TraceEventETWExport* GetInstance(); + + static void EnableETWExport(); + static void DisableETWExport(); + + static bool isETWExportEnabled() { return GetInstance()->ETWExportEnabled_; } + + static void AddEvent( + char phase, + const unsigned char* category_group_enabled, + const char* name, + unsigned long long id, + int num_args, + const char** arg_names, + const unsigned char* arg_types, + const unsigned long long* arg_values, + const scoped_refptr<ConvertableToTraceFormat>* convertable_values); + + static void AddCustomEvent(const char* name, + char const* phase, + const char* arg_name_1, + const char* arg_value_1, + const char* arg_name_2, + const char* arg_value_2, + const char* arg_name_3, + const char* arg_value_3); + + void Resurrect(); + + private: + bool ETWExportEnabled_; + // Ensure only the provider can construct us. + friend struct StaticMemorySingletonTraits<TraceEventETWExport>; + TraceEventETWExport(); + + DISALLOW_COPY_AND_ASSIGN(TraceEventETWExport); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_TRACE_ETW_EXPORT_H_ diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc index 834f826e..3cf4f07 100644 --- a/base/trace_event/trace_event_impl.cc +++ b/base/trace_event/trace_event_impl.cc @@ -36,6 +36,7 @@ #include "base/trace_event/trace_event_synthetic_delay.h" #if defined(OS_WIN) +#include "base/trace_event/trace_event_etw_export_win.h" #include "base/trace_event/trace_event_win.h" #endif @@ -1292,6 +1293,11 @@ void TraceLog::UpdateCategoryGroupEnabledFlag(size_t category_index) { if (event_callback_ && event_callback_category_filter_.IsCategoryGroupEnabled(category_group)) enabled_flag |= ENABLED_FOR_EVENT_CALLBACK; +#if defined(OS_WIN) + if (base::trace_event::TraceEventETWExport::isETWExportEnabled()) + enabled_flag |= ENABLED_FOR_ETW_EXPORT; +#endif + g_category_group_enabled[category_index] = enabled_flag; } @@ -1984,6 +1990,15 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp( } } +#if defined(OS_WIN) + // This is done sooner rather than later, to avoid creating the event and + // acquiring the lock, which is not needed for ETW as it's already threadsafe. + if (*category_group_enabled & ENABLED_FOR_ETW_EXPORT) + TraceEventETWExport::AddEvent(phase, category_group_enabled, name, id, + num_args, arg_names, arg_types, arg_values, + convertable_values); +#endif // OS_WIN + std::string console_message; if (*category_group_enabled & (ENABLED_FOR_RECORDING | ENABLED_FOR_MONITORING)) { diff --git a/base/trace_event/trace_event_impl.h b/base/trace_event/trace_event_impl.h index c3da0b5e..890e65a 100644 --- a/base/trace_event/trace_event_impl.h +++ b/base/trace_event/trace_event_impl.h @@ -26,6 +26,7 @@ // Older style trace macros with explicit id and extra data // Only these macros result in publishing data to ETW as currently implemented. +// TODO(georgesak): Update/replace these with new ETW macros. #define TRACE_EVENT_BEGIN_ETW(name, id, extra) \ base::trace_event::TraceLog::AddTraceEventEtw( \ TRACE_EVENT_PHASE_BEGIN, \ @@ -446,6 +447,8 @@ class BASE_EXPORT TraceLog { ENABLED_FOR_MONITORING = 1 << 1, // Category group enabled by SetEventCallbackEnabled(). ENABLED_FOR_EVENT_CALLBACK = 1 << 2, + // Category group enabled to export events to ETW. + ENABLED_FOR_ETW_EXPORT = 1 << 3 }; static TraceLog* GetInstance(); |