diff options
Diffstat (limited to 'chromium/base/trace_event')
26 files changed, 1751 insertions, 241 deletions
diff --git a/chromium/base/trace_event/base_tracing.h b/chromium/base/trace_event/base_tracing.h new file mode 100644 index 00000000000..c5831f237d2 --- /dev/null +++ b/chromium/base/trace_event/base_tracing.h @@ -0,0 +1,28 @@ +// Copyright 2020 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. + +#ifndef BASE_TRACE_EVENT_BASE_TRACING_H_ +#define BASE_TRACE_EVENT_BASE_TRACING_H_ + +// Proxy header that provides tracing instrumentation for //base code. When +// tracing support is disabled via the gn flag enable_base_tracing, this header +// provides a mock implementation of the relevant trace macros instead, which +// causes the instrumentation in //base to be compiled into no-ops. + +#include "base/tracing_buildflags.h" + +#if BUILDFLAG(ENABLE_BASE_TRACING) +// Update the check in //base/PRESUBMIT.py when adding new headers here. +// TODO(crbug/1006541): Switch to perfetto for trace event implementation. +#include "base/trace_event/blame_context.h" +#include "base/trace_event/memory_allocator_dump_guid.h" +#include "base/trace_event/memory_dump_provider.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/traced_value.h" +#include "base/trace_event/typed_macros.h" +#else // BUILDFLAG(ENABLE_BASE_TRACING) +#include "base/trace_event/trace_event_stub.h" +#endif // BUILDFLAG(ENABLE_BASE_TRACING) + +#endif // BASE_TRACE_EVENT_BASE_TRACING_H_ diff --git a/chromium/base/trace_event/builtin_categories.h b/chromium/base/trace_event/builtin_categories.h index 7ce21d4711b..31f563dd4d5 100644 --- a/chromium/base/trace_event/builtin_categories.h +++ b/chromium/base/trace_event/builtin_categories.h @@ -83,6 +83,7 @@ X("GAMEPAD") \ X("gpu") \ X("gpu.capture") \ + X("gpu.memory") \ X("headless") \ X("hwoverlays") \ X("identity") \ @@ -148,9 +149,11 @@ X("test_gpu") \ X("test_tracing") \ X("toplevel") \ + X("toplevel.flow") \ X("ui") \ X("v8") \ X("v8.execute") \ + X("v8.wasm") \ X("ValueStoreFrontend::Backend") \ X("views") \ X("views.frame") \ @@ -214,6 +217,7 @@ X(TRACE_DISABLED_BY_DEFAULT("power")) \ X(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler")) \ X(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug")) \ + X(TRACE_DISABLED_BY_DEFAULT("sandbox")) \ X(TRACE_DISABLED_BY_DEFAULT("sequence_manager")) \ X(TRACE_DISABLED_BY_DEFAULT("sequence_manager.debug")) \ X(TRACE_DISABLED_BY_DEFAULT("sequence_manager.verbose_snapshots")) \ @@ -223,12 +227,10 @@ X(TRACE_DISABLED_BY_DEFAULT("SyncFileSystem")) \ X(TRACE_DISABLED_BY_DEFAULT("system_stats")) \ X(TRACE_DISABLED_BY_DEFAULT("thread_pool_diagnostics")) \ - X(TRACE_DISABLED_BY_DEFAULT("toplevel.flow")) \ X(TRACE_DISABLED_BY_DEFAULT("toplevel.ipc")) \ X(TRACE_DISABLED_BY_DEFAULT("user_action_samples")) \ X(TRACE_DISABLED_BY_DEFAULT("v8.compile")) \ X(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler")) \ - X(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler.hires")) \ X(TRACE_DISABLED_BY_DEFAULT("v8.gc")) \ X(TRACE_DISABLED_BY_DEFAULT("v8.gc_stats")) \ X(TRACE_DISABLED_BY_DEFAULT("v8.ic_stats")) \ @@ -236,7 +238,7 @@ X(TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats")) \ X(TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats_sampling")) \ X(TRACE_DISABLED_BY_DEFAULT("v8.turbofan")) \ - X(TRACE_DISABLED_BY_DEFAULT("v8.wasm")) \ + X(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed")) \ X(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture")) \ X(TRACE_DISABLED_BY_DEFAULT("viz.debug.overlay_planes")) \ X(TRACE_DISABLED_BY_DEFAULT("viz.hit_testing_flow")) \ @@ -246,6 +248,7 @@ X(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime")) \ X(TRACE_DISABLED_BY_DEFAULT("viz.triangles")) \ X(TRACE_DISABLED_BY_DEFAULT("webaudio.audionode")) \ + X(TRACE_DISABLED_BY_DEFAULT("webrtc")) \ X(TRACE_DISABLED_BY_DEFAULT("worker.scheduler")) #define INTERNAL_TRACE_INIT_CATEGORY_NAME(name) name, diff --git a/chromium/base/trace_event/category_registry.cc b/chromium/base/trace_event/category_registry.cc index 691336f8707..27c77740358 100644 --- a/chromium/base/trace_event/category_registry.cc +++ b/chromium/base/trace_event/category_registry.cc @@ -6,6 +6,7 @@ #include <string.h> +#include <ostream> #include <type_traits> #include "base/check.h" diff --git a/chromium/base/trace_event/category_registry.h b/chromium/base/trace_event/category_registry.h index a6439d94595..cd95ba8f547 100644 --- a/chromium/base/trace_event/category_registry.h +++ b/chromium/base/trace_event/category_registry.h @@ -10,7 +10,7 @@ #include "base/atomicops.h" #include "base/base_export.h" -#include "base/logging.h" +#include "base/check_op.h" #include "base/stl_util.h" #include "base/trace_event/builtin_categories.h" #include "base/trace_event/common/trace_event_common.h" diff --git a/chromium/base/trace_event/etw_manifest/BUILD.gn b/chromium/base/trace_event/etw_manifest/BUILD.gn deleted file mode 100644 index a66fef9e3c8..00000000000 --- a/chromium/base/trace_event/etw_manifest/BUILD.gn +++ /dev/null @@ -1,27 +0,0 @@ -# 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. - -import("//build/win/message_compiler.gni") - -assert(is_win, "This only runs on Windows.") - -message_compiler("chrome_events_win") { - visibility = [ - "//base/*", - "//chrome:chrome_dll", - ] - - sources = [ "chrome_events_win.man" ] - - user_mode_logging = true - - # The only code generated from chrome_events_win.man is a header file that - # is included by trace_event_etw_export_win.cc, so there is no need to - # compile any generated code. The other thing which compile_generated_code - # controls in this context is linking in the .res file generated from the - # manifest. However this is only needed for ETW provider registration which - # is done by UIforETW (https://github.com/google/UIforETW) and therefore the - # manifest resource can be skipped in Chrome. - compile_generated_code = false -} diff --git a/chromium/base/trace_event/etw_manifest/chrome_events_win.man b/chromium/base/trace_event/etw_manifest/chrome_events_win.man deleted file mode 100644 index 489d16720aa..00000000000 --- a/chromium/base/trace_event/etw_manifest/chrome_events_win.man +++ /dev/null @@ -1,95 +0,0 @@ -<?xml version='1.0' encoding='utf-8' standalone='yes'?> -<assembly - xmlns="urn:schemas-microsoft-com:asm.v3" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - manifestVersion="1.0" - > - <assemblyIdentity - buildType="$(build.buildType)" - language="neutral" - name="Chrome.ETW" - processorArchitecture="$(build.arch)" - publicKeyToken="$(Build.WindowsPublicKeyToken)" - version="$(build.version)" - versionScope="nonSxS" - /> - <instrumentation - xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events" - buildFilter="not build.isWow" - > - <events xmlns="http://schemas.microsoft.com/win/2004/08/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> - <resources culture="en-US"> - <stringTable> - <string - id="ChromeEvent.EventMessage" - value="Chrome Event: %1 (%2)" - /> - </stringTable> - </resources> - </localization> -</assembly> diff --git a/chromium/base/trace_event/features.gni b/chromium/base/trace_event/features.gni deleted file mode 100644 index 7d6bb2a8509..00000000000 --- a/chromium/base/trace_event/features.gni +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2020 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. - -# Features used by //base/trace_event and //services/tracing. -declare_args() { - # Switches the TRACE_EVENT instrumentation from base's TraceLog implementation - # to //third_party/perfetto's client library. Not implemented yet, currently a - # no-op to set up trybot infrastructure. - # TODO(eseckler): Implement. - use_perfetto_client_library = false -} diff --git a/chromium/base/trace_event/memory_allocator_dump.h b/chromium/base/trace_event/memory_allocator_dump.h index 4999e85f560..b11e239b2a1 100644 --- a/chromium/base/trace_event/memory_allocator_dump.h +++ b/chromium/base/trace_event/memory_allocator_dump.h @@ -13,7 +13,6 @@ #include "base/base_export.h" #include "base/gtest_prod_util.h" -#include "base/logging.h" #include "base/macros.h" #include "base/optional.h" #include "base/trace_event/memory_allocator_dump_guid.h" diff --git a/chromium/base/trace_event/memory_dump_manager.cc b/chromium/base/trace_event/memory_dump_manager.cc index 0490e364a46..240b7af3bb0 100644 --- a/chromium/base/trace_event/memory_dump_manager.cc +++ b/chromium/base/trace_event/memory_dump_manager.cc @@ -16,6 +16,7 @@ #include "base/command_line.h" #include "base/debug/alias.h" #include "base/debug/stack_trace.h" +#include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/sequenced_task_runner.h" #include "base/strings/string_util.h" diff --git a/chromium/base/trace_event/memory_infra_background_allowlist.cc b/chromium/base/trace_event/memory_infra_background_allowlist.cc index 7d1e5744c15..dfaf6271f1f 100644 --- a/chromium/base/trace_event/memory_infra_background_allowlist.cc +++ b/chromium/base/trace_event/memory_infra_background_allowlist.cc @@ -26,6 +26,7 @@ const char* const kDumpProviderAllowlist[] = { "BlinkGC", "BlinkObjectCounters", "BlobStorageContext", + "Canvas", "ClientDiscardableSharedMemoryManager", "DevTools", "DiscardableSharedMemoryManager", @@ -95,6 +96,8 @@ const char* const kAllocatorDumpNameAllowlist[] = { "blink_objects/WorkerGlobalScope", "blink_objects/UACSSResource", "blink_objects/ResourceFetcher", + "canvas/ResourceProvider/SkSurface", + "canvas/ResourceProvider/SkSurface/0x?", "components/download/controller_0x?", "devtools/file_watcher_0x?", "discardable", diff --git a/chromium/base/trace_event/process_memory_dump.cc b/chromium/base/trace_event/process_memory_dump.cc index 021e9862e21..2b095661230 100644 --- a/chromium/base/trace_event/process_memory_dump.cc +++ b/chromium/base/trace_event/process_memory_dump.cc @@ -8,6 +8,7 @@ #include <vector> +#include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/shared_memory_tracker.h" #include "base/process/process_metrics.h" diff --git a/chromium/base/trace_event/trace_config.cc b/chromium/base/trace_event/trace_config.cc index 5b4493f1bd6..14a670647fa 100644 --- a/chromium/base/trace_event/trace_config.cc +++ b/chromium/base/trace_event/trace_config.cc @@ -11,6 +11,7 @@ #include "base/json/json_reader.h" #include "base/json/json_writer.h" +#include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/strings/string_split.h" #include "base/trace_event/memory_dump_manager.h" diff --git a/chromium/base/trace_event/trace_event_etw_export_win.cc b/chromium/base/trace_event/trace_event_etw_export_win.cc index 680cdbdc027..cf4383fe031 100644 --- a/chromium/base/trace_event/trace_event_etw_export_win.cc +++ b/chromium/base/trace_event/trace_event_etw_export_win.cc @@ -4,44 +4,23 @@ #include "base/trace_event/trace_event_etw_export_win.h" +#include <evntrace.h> +#include <guiddef.h> #include <stddef.h> +#include <stdlib.h> +#include <windows.h> #include "base/at_exit.h" #include "base/check_op.h" #include "base/command_line.h" +#include "base/logging.h" #include "base/memory/singleton.h" #include "base/strings/string_tokenizer.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/platform_thread.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_impl.h" - -#include <windows.h> - -// The GetProcAddress technique is borrowed from -// https://github.com/google/UIforETW/tree/master/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. - -// Headers generated by mc.exe have a ';' at the end of extern "C" {} blocks. -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wextra-semi" -#endif - -#include "base/trace_event/etw_manifest/chrome_events_win.h" // NOLINT - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif +#include "base/trace_event/trace_logging_minimal_win.h" namespace { @@ -51,28 +30,62 @@ namespace { // one bit per category. We can therefore enable a particular category by // setting its corresponding bit in the keyword. For events that are not present // in |kFilteredEventGroupNames|, we have two bits that control their -// behaviour. When bit 61 is enabled, any event that is not disabled by default +// behaviour. When bit 46 is enabled, any event that is not disabled by default // (ie. doesn't start with disabled-by-default-) will be exported. Likewise, -// when bit 62 is enabled, any event that is disabled by default will be +// when bit 47 is enabled, any event that is disabled by default will be // exported. // -// Note that bit 63 (MSB) must always be set, otherwise tracing will be disabled -// by ETW. Therefore, the keyword will always be greater than -// 0x8000000000000000. -// // Examples of passing keywords to the provider using xperf: // # This exports "benchmark" and "cc" events -// xperf -start chrome -on Chrome:0x8000000000000009 +// xperf -start chrome -on Chrome:0x9 // // # This exports "gpu", "netlog" and all other events that are not disabled by // # default -// xperf -start chrome -on Chrome:0xA0000000000000A0 +// xperf -start chrome -on Chrome:0x4000000000A0 // // More info about starting a trace and keyword can be obtained by using the // help section of xperf (xperf -help start). Note that xperf documentation // refers to keywords as flags and there are two ways to enable them, using // group names or the hex representation. We only support the latter. Also, we // ignore the level. +// +// To avoid continually having to bump MSEdge values to next higher bits, we +// are putting MSEdge values at the high end of the bit range and will grow +// 'down' to lower bits for future MSEdge entries. +// +// As the writing of this comment, we have 4 values: +// "navigation", // 0x40000000000 +// "ServiceWorker", // 0x80000000000 +// "edge_webview", // 0x100000000000 +// "diagnostic_event", // 0x200000000000 +// +// This means the next value added should be: +// "the_next_value", // 0x20000000000 +// "navigation", // 0x40000000000 +// "ServiceWorker", // 0x80000000000 +// "edge_webview", // 0x100000000000 +// "diagnostic_event", // 0x200000000000 +// +// The addition of the "unused_bit_nn" entries keeps the existing code execution +// routines working (ex. TraceEventETWExport::UpdateEnabledCategories()) and +// enables others to see which bits are available. +// +// Example: For some new category group... +// "latency", // 0x8000 +// "blink.user_timing", // 0x10000 +// "unused_bit_18", // 0x20000 +// "unused_bit_19", // 0x40000 +// "unused_bit_20", // 0x80000 +// ... +// becomes: +// "latency", // 0x8000 +// "blink.user_timing", // 0x10000 +// "new_upstream_value", // 0x20000 +// "unused_bit_19", // 0x40000 +// "unused_bit_20", // 0x80000 +// +// The high 16 bits of the keyword have special semantics and should not be +// set for enabling individual categories as they are reserved by winmeta.xml. const char* const kFilteredEventGroupNames[] = { "benchmark", // 0x1 "blink", // 0x2 @@ -93,13 +106,44 @@ const char* const kFilteredEventGroupNames[] = { "blink.user_timing", // 0x10000 "media", // 0x20000 "loading", // 0x40000 + "unused_bit_19", // 0x80000 + "unused_bit_20", // 0x100000 + "unused_bit_21", // 0x200000 + "unused_bit_22", // 0x400000 + "unused_bit_23", // 0x800000 + "unused_bit_24", // 0x1000000 + "unused_bit_25", // 0x2000000 + "unused_bit_26", // 0x4000000 + "unused_bit_27", // 0x8000000 + "unused_bit_28", // 0x10000000 + "unused_bit_29", // 0x20000000 + "unused_bit_30", // 0x40000000 + "unused_bit_31", // 0x80000000 + "unused_bit_32", // 0x100000000 + "unused_bit_33", // 0x200000000 + "unused_bit_34", // 0x400000000 + "unused_bit_35", // 0x800000000 + "unused_bit_36", // 0x1000000000 + "unused_bit_37", // 0x2000000000 + "unused_bit_38", // 0x4000000000 + "unused_bit_39", // 0x8000000000 + "unused_bit_40", // 0x10000000000 + "unused_bit_41", // 0x20000000000 + "navigation", // 0x40000000000 + "ServiceWorker", // 0x80000000000 + "edge_webview", // 0x100000000000 + "diagnostic_event", // 0x200000000000 + "__OTHER_EVENTS", // 0x400000000000 See below + "__DISABLED_OTHER_EVENTS", // 0x800000000000 See below }; -const char kOtherEventsGroupName[] = "__OTHER_EVENTS"; // 0x2000000000000000 -const char kDisabledOtherEventsGroupName[] = - "__DISABLED_OTHER_EVENTS"; // 0x4000000000000000 -const uint64_t kOtherEventsKeywordBit = 1ULL << 61; -const uint64_t kDisabledOtherEventsKeywordBit = 1ULL << 62; -const size_t kNumberOfCategories = ARRAYSIZE(kFilteredEventGroupNames) + 2U; + +// These must be kept as the last two entries in the above array. +constexpr uint8_t kOtherEventsGroupNameIndex = 46; +constexpr uint8_t kDisabledOtherEventsGroupNameIndex = 47; + +// Max number of available keyword bits. +constexpr size_t kMaxNumberOfGroupNames = 48; +uint64_t g_callback_match_any_keyword = 0; static void __stdcall EtwEnableCallback(LPCGUID SourceId, ULONG ControlCode, @@ -108,10 +152,19 @@ static void __stdcall EtwEnableCallback(LPCGUID SourceId, ULONGLONG MatchAllKeyword, PEVENT_FILTER_DESCRIPTOR FilterData, PVOID CallbackContext) { - // Invoke the default callback, which updates the information inside - // CHROME_Context. - McGenControlCallbackV2(SourceId, ControlCode, Level, MatchAnyKeyword, - MatchAllKeyword, FilterData, CallbackContext); + // This callback is called in the context of an ETW OS thread to + // inform the process of the global state of the level and keyword + // across all sessions for this provider. We need to update the + // local keywords so we log the corresponding events. Protect the + // upper 16 bits reserved by winmeta.xml as they should not be used + // but older logging code and tools incorrectly used them. + g_callback_match_any_keyword = MatchAnyKeyword; + g_callback_match_any_keyword &= ~0xFFFF000000000000; + + DVLOG(1) << "ETW Keyword" + << " Bits enabled in global context: " << std::hex << MatchAnyKeyword + << " Bits enabled in our code: " << std::hex + << g_callback_match_any_keyword; base::trace_event::TraceEventETWExport::OnETWEnableUpdate(); } @@ -123,31 +176,41 @@ namespace trace_event { bool TraceEventETWExport::is_registration_complete_ = false; -TraceEventETWExport::TraceEventETWExport() : etw_match_any_keyword_(0ULL) { - // Register the ETW provider. If registration fails then the event logging - // calls will fail. We're essentially doing the same operation as - // EventRegisterChrome (which was auto generated for our provider by the - // ETW manifest compiler), but instead we're passing our own callback. +TraceEventETWExport::TraceEventETWExport() { + // Construct the ETW provider. If construction fails then the event logging + // calls will fail. We're passing a callback function as part of registration. // This allows us to detect changes to enable/disable/keyword changes. - // ChromeHandle and the other parameters to EventRegister are all generated - // globals from chrome_events_win.h - DCHECK(!ChromeHandle); - EventRegister(&CHROME, &EtwEnableCallback, &CHROME_Context, &ChromeHandle); + + // This GUID is the used to identify the Chrome provider and is used whenever + // ETW is enabled via tracing tools and cannot change without updating tools + // that collect Chrome ETW data. + static const GUID Chrome_GUID = { + 0xD2D578D9, + 0x2936, + 0x45B6, + {0xA0, 0x9F, 0x30, 0xE3, 0x27, 0x15, 0xF4, 0x2D}}; + + etw_provider_ = std::make_unique<TlmProvider>("Google.Chrome", Chrome_GUID, + &EtwEnableCallback); TraceEventETWExport::is_registration_complete_ = true; // Make sure to initialize the map with all the group names. Subsequent // modifications will be made by the background thread and only affect the // values of the keys (no key addition/deletion). Therefore, the map does not // require a lock for access. - for (size_t i = 0; i < ARRAYSIZE(kFilteredEventGroupNames); i++) + // Also set up the map from category name to keyword. + for (size_t i = 0; i < ARRAYSIZE(kFilteredEventGroupNames); i++) { + uint64_t keyword = 1ULL << i; categories_status_[kFilteredEventGroupNames[i]] = false; - categories_status_[kOtherEventsGroupName] = false; - categories_status_[kDisabledOtherEventsGroupName] = false; - DCHECK_EQ(kNumberOfCategories, categories_status_.size()); + categories_keyword_[kFilteredEventGroupNames[i]] = keyword; + } + // Make sure we stay at 48 entries, the maximum number of bits available + // for keyword use. + static_assert(ARRAYSIZE(kFilteredEventGroupNames) <= kMaxNumberOfGroupNames, + "Exceeded max ETW keyword bits"); } TraceEventETWExport::~TraceEventETWExport() { - EventUnregisterChrome(); is_registration_complete_ = false; } @@ -163,6 +226,52 @@ void TraceEventETWExport::EnableETWExport() { } // static +uint64_t TraceEventETWExport::CategoryGroupToKeyword( + const uint8_t* category_state) { + uint64_t keyword = 0; + + // To enable multiple sessions with this provider enabled we need to log the + // level and keyword with the event so that if the sessions differ in the + // level or keywords enabled we log the right events and allow ETW to + // route the data to the appropriate session. + // TODO(joel@microsoft.com) Explore better methods in future integration + // with perfetto. + + auto* instance = GetInstance(); + if (!instance) + return keyword; + + // Add in the keyword for the special bits if they are set. + if (instance->categories_status_ + [kFilteredEventGroupNames[kOtherEventsGroupNameIndex]]) { + keyword |= instance->categories_keyword_ + [kFilteredEventGroupNames[kOtherEventsGroupNameIndex]]; + } + if (instance->categories_status_ + [kFilteredEventGroupNames[kDisabledOtherEventsGroupNameIndex]]) { + keyword |= + instance->categories_keyword_ + [kFilteredEventGroupNames[kDisabledOtherEventsGroupNameIndex]]; + } + // Add in the keyword for the categories specified at the logging site. + const TraceCategory* category = TraceCategory::FromStatePtr(category_state); + StringPiece category_group_name = category->name(); + + CStringTokenizer category_group_tokens(category_group_name.begin(), + category_group_name.end(), ","); + while (category_group_tokens.GetNext()) { + StringPiece category_group_token = category_group_tokens.token_piece(); + + // Lookup the keyword for this part of the category_group_name + // and or in the keyword. + auto it = instance->categories_keyword_.find(category_group_token); + if (it != instance->categories_keyword_.end()) + keyword |= it->second; + } + return keyword; +} + +// static void TraceEventETWExport::AddEvent(char phase, const unsigned char* category_group_enabled, const char* name, @@ -170,10 +279,14 @@ void TraceEventETWExport::AddEvent(char phase, const TraceArguments* args) { // We bail early in case exporting is disabled or no consumer is listening. auto* instance = GetInstance(); - if (!instance || !EventEnabledChromeEvent()) + uint64_t keyword = CategoryGroupToKeyword(category_group_enabled); + if (!instance || + !instance->etw_provider_->IsEnabled(TRACE_LEVEL_NONE, keyword)) { return; + } const char* phase_string = nullptr; + // Space to store the phase identifier and null-terminator, when needed. char phase_buffer[2]; switch (phase) { @@ -257,30 +370,54 @@ void TraceEventETWExport::AddEvent(char phase, } } - EventWriteChromeEvent( - name, phase_string, num_args > 0 ? args->names()[0] : "", - arg_values_string[0].c_str(), num_args > 1 ? args->names()[1] : "", - arg_values_string[1].c_str(), "", ""); + // Log the event and include the info needed to decode it via TraceLogging + if (num_args == 0) { + instance->etw_provider_->WriteEvent( + name, TlmEventDescriptor(0, keyword), + TlmMbcsStringField("Phase", phase_string)); + } else if (num_args == 1) { + instance->etw_provider_->WriteEvent( + name, TlmEventDescriptor(0, keyword), + TlmMbcsStringField("Phase", phase_string), + TlmMbcsStringField((args->names()[0]), (arg_values_string[0].c_str()))); + } else if (num_args == 2) { + instance->etw_provider_->WriteEvent( + name, TlmEventDescriptor(0, keyword), + TlmMbcsStringField("Phase", phase_string), + TlmMbcsStringField((args->names()[0]), (arg_values_string[0].c_str())), + TlmMbcsStringField((args->names()[1]), (arg_values_string[1].c_str()))); + } else { + NOTREACHED(); + } } // static -void TraceEventETWExport::AddCompleteEndEvent(const char* name) { +void TraceEventETWExport::AddCompleteEndEvent( + const unsigned char* category_group_enabled, + const char* name) { auto* instance = GetInstance(); - if (!instance || !EventEnabledChromeEvent()) + uint64_t keyword = CategoryGroupToKeyword(category_group_enabled); + if (!instance || + !instance->etw_provider_->IsEnabled(TRACE_LEVEL_NONE, keyword)) { return; + } - EventWriteChromeEvent(name, "Complete End", "", "", "", "", "", ""); + // Log the event and include the info needed to decode it via TraceLogging + instance->etw_provider_->WriteEvent( + name, TlmEventDescriptor(0, keyword), + TlmMbcsStringField("Phase", "Complete End")); } // static bool TraceEventETWExport::IsCategoryGroupEnabled( StringPiece category_group_name) { DCHECK(!category_group_name.empty()); + auto* instance = GetInstanceIfExists(); if (instance == nullptr) return false; - if (!EventEnabledChromeEvent()) + if (!instance->etw_provider_->IsEnabled()) return false; CStringTokenizer category_group_tokens(category_group_name.begin(), @@ -295,14 +432,15 @@ bool TraceEventETWExport::IsCategoryGroupEnabled( } bool TraceEventETWExport::UpdateEnabledCategories() { - if (etw_match_any_keyword_ == CHROME_Context.MatchAnyKeyword) + if (etw_match_any_keyword_ == g_callback_match_any_keyword) return false; - // If the keyword has changed, update each category. - // Chrome_Context.MatchAnyKeyword is set by UIforETW (or other ETW trace - // recording tools) using the ETW infrastructure. This value will be set in - // all Chrome processes that have registered their ETW provider. - etw_match_any_keyword_ = CHROME_Context.MatchAnyKeyword; + // If the global keyword has changed, update each category. The global + // context is set by UIforETW (or other ETW trace recording tools) + // using the ETW infrastructure. When the global context changes the + // callback will be called to set the updated keyword bits in each + // browser process that has registered their ETW provider. + etw_match_any_keyword_ = g_callback_match_any_keyword; for (size_t i = 0; i < ARRAYSIZE(kFilteredEventGroupNames); i++) { if (etw_match_any_keyword_ & (1ULL << i)) { categories_status_[kFilteredEventGroupNames[i]] = true; @@ -311,20 +449,6 @@ bool TraceEventETWExport::UpdateEnabledCategories() { } } - // Also update the two default categories. - if (etw_match_any_keyword_ & kOtherEventsKeywordBit) { - categories_status_[kOtherEventsGroupName] = true; - } else { - categories_status_[kOtherEventsGroupName] = false; - } - if (etw_match_any_keyword_ & kDisabledOtherEventsKeywordBit) { - categories_status_[kDisabledOtherEventsGroupName] = true; - } else { - categories_status_[kDisabledOtherEventsGroupName] = false; - } - - DCHECK_EQ(kNumberOfCategories, categories_status_.size()); - // Update the categories in TraceLog. TraceLog::GetInstance()->UpdateETWCategoryGroupEnabledFlags(); @@ -332,7 +456,6 @@ bool TraceEventETWExport::UpdateEnabledCategories() { } bool TraceEventETWExport::IsCategoryEnabled(StringPiece category_name) const { - DCHECK_EQ(kNumberOfCategories, categories_status_.size()); // Try to find the category and return its status if found auto it = categories_status_.find(category_name); if (it != categories_status_.end()) @@ -341,13 +464,19 @@ bool TraceEventETWExport::IsCategoryEnabled(StringPiece category_name) const { // Otherwise return the corresponding default status by first checking if the // category is disabled by default. if (category_name.starts_with("disabled-by-default")) { - DCHECK(categories_status_.find(kDisabledOtherEventsGroupName) != + DCHECK(categories_status_.find( + kFilteredEventGroupNames[kDisabledOtherEventsGroupNameIndex]) != categories_status_.end()); - return categories_status_.find(kDisabledOtherEventsGroupName)->second; + return categories_status_ + .find(kFilteredEventGroupNames[kDisabledOtherEventsGroupNameIndex]) + ->second; } else { - DCHECK(categories_status_.find(kOtherEventsGroupName) != + DCHECK(categories_status_.find( + kFilteredEventGroupNames[kOtherEventsGroupNameIndex]) != categories_status_.end()); - return categories_status_.find(kOtherEventsGroupName)->second; + return categories_status_ + .find(kFilteredEventGroupNames[kOtherEventsGroupNameIndex]) + ->second; } } diff --git a/chromium/base/trace_event/trace_event_etw_export_win.h b/chromium/base/trace_event/trace_event_etw_export_win.h index 0f853533d3a..8f5c8849860 100644 --- a/chromium/base/trace_event/trace_event_etw_export_win.h +++ b/chromium/base/trace_event/trace_event_etw_export_win.h @@ -7,13 +7,16 @@ #define BASE_TRACE_EVENT_TRACE_EVENT_ETW_EXPORT_WIN_H_ #include <stdint.h> +#include <windows.h> #include <map> +#include <memory> #include "base/base_export.h" #include "base/macros.h" #include "base/strings/string_piece.h" #include "base/trace_event/trace_event_impl.h" +#include "base/trace_event/trace_logging_minimal_win.h" namespace base { @@ -49,7 +52,8 @@ class BASE_EXPORT TraceEventETWExport { const TraceArguments* args); // Exports an ETW event that marks the end of a complete event. - static void AddCompleteEndEvent(const char* name); + static void AddCompleteEndEvent(const unsigned char* category_group_enabled, + const char* name); // Returns true if any category in the group is enabled. static bool IsCategoryGroupEnabled(StringPiece category_group_name); @@ -61,23 +65,31 @@ class BASE_EXPORT TraceEventETWExport { private: // Ensure only the provider can construct us. friend struct StaticMemorySingletonTraits<TraceEventETWExport>; - TraceEventETWExport(); // Updates the list of enabled categories by consulting the ETW keyword. // Returns true if there was a change, false otherwise. bool UpdateEnabledCategories(); + static uint64_t CategoryGroupToKeyword(const uint8_t* category_state); + // Returns true if the category is enabled. bool IsCategoryEnabled(StringPiece category_name) const; static bool is_registration_complete_; + // The keywords that were enabled last time the callback was made. + uint64_t etw_match_any_keyword_ = 0; + + // The provider is set based on channel for MSEdge, in other Chromium + // based browsers all channels use the same GUID/provider. + std::unique_ptr<TlmProvider> etw_provider_; + // Maps category names to their status (enabled/disabled). std::map<StringPiece, bool> categories_status_; - // Local copy of the ETW keyword. - uint64_t etw_match_any_keyword_; + // Maps category names to their keyword. + std::map<StringPiece, uint64_t> categories_keyword_; DISALLOW_COPY_AND_ASSIGN(TraceEventETWExport); }; diff --git a/chromium/base/trace_event/trace_event_impl.cc b/chromium/base/trace_event/trace_event_impl.cc index c74d71c5dd1..dd902bd85d3 100644 --- a/chromium/base/trace_event/trace_event_impl.cc +++ b/chromium/base/trace_event/trace_event_impl.cc @@ -6,6 +6,8 @@ #include <stddef.h> +#include <sstream> + #include "base/format_macros.h" #include "base/json/string_escape.h" #include "base/memory/ptr_util.h" diff --git a/chromium/base/trace_event/trace_event_stub.cc b/chromium/base/trace_event/trace_event_stub.cc new file mode 100644 index 00000000000..2b49d9c3ac4 --- /dev/null +++ b/chromium/base/trace_event/trace_event_stub.cc @@ -0,0 +1,21 @@ +// Copyright 2020 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 <stddef.h> + +#include <string> + +#include "base/trace_event/trace_event_stub.h" + +namespace base { +namespace trace_event { + +ConvertableToTraceFormat::~ConvertableToTraceFormat() = default; + +void TracedValue::AppendAsTraceFormat(std::string* out) const {} + +MemoryDumpProvider::~MemoryDumpProvider() = default; + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/trace_event_stub.h b/chromium/base/trace_event/trace_event_stub.h new file mode 100644 index 00000000000..b10e9498e61 --- /dev/null +++ b/chromium/base/trace_event/trace_event_stub.h @@ -0,0 +1,176 @@ +// Copyright 2020 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. + +#ifndef BASE_TRACE_EVENT_TRACE_EVENT_STUB_H_ +#define BASE_TRACE_EVENT_TRACE_EVENT_STUB_H_ + +#include <stddef.h> + +#include <string> + +#include "base/base_export.h" +#include "base/macros.h" +#include "base/strings/string_piece.h" +#include "base/trace_event/common/trace_event_common.h" +#include "base/trace_event/memory_allocator_dump_guid.h" +#include "base/values.h" + +#define TRACE_STR_COPY(str) str +#define TRACE_ID_WITH_SCOPE(scope, ...) 0 +#define TRACE_ID_GLOBAL(id) 0 +#define TRACE_ID_LOCAL(id) 0 + +namespace trace_event_internal { + +const unsigned long long kNoId = 0; + +template <typename... Args> +void Ignore(Args&&... args) {} + +struct IgnoredValue { + template <typename... Args> + IgnoredValue(Args&&... args) {} +}; + +} // namespace trace_event_internal + +#define INTERNAL_TRACE_IGNORE(...) \ + (false ? trace_event_internal::Ignore(__VA_ARGS__) : (void)0) + +#define INTERNAL_TRACE_EVENT_ADD(...) INTERNAL_TRACE_IGNORE(__VA_ARGS__) +#define INTERNAL_TRACE_EVENT_ADD_SCOPED(...) INTERNAL_TRACE_IGNORE(__VA_ARGS__) +#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(...) INTERNAL_TRACE_IGNORE(__VA_ARGS__) +#define INTERNAL_TRACE_TASK_EXECUTION(...) INTERNAL_TRACE_IGNORE(__VA_ARGS__) +#define INTERNAL_TRACE_LOG_MESSAGE(...) INTERNAL_TRACE_IGNORE(__VA_ARGS__) +#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(...) \ + INTERNAL_TRACE_IGNORE(__VA_ARGS__) +#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(...) \ + INTERNAL_TRACE_IGNORE(__VA_ARGS__) +#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMPS(...) \ + INTERNAL_TRACE_IGNORE(__VA_ARGS__) + +#define TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION \ + trace_event_internal::IgnoredValue + +#define TRACE_ID_MANGLE(val) (val) + +#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(cat) INTERNAL_TRACE_IGNORE(cat); +#define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE() false + +#define TRACE_EVENT_API_CURRENT_THREAD_ID 0 + +// Typed macros. For these, we have to erase the extra args entirely, as they +// may include a lambda that refers to protozero message types (which aren't +// available in the stub). This may trigger "unused variable" errors at the +// callsite, which have to be addressed at the callsite (e.g. via +// ignore_result()). +#define TRACE_EVENT_BEGIN(category, name, ...) \ + INTERNAL_TRACE_IGNORE(category, name) +#define TRACE_EVENT_END(category, ...) INTERNAL_TRACE_IGNORE(category) +#define TRACE_EVENT(category, name, ...) INTERNAL_TRACE_IGNORE(category, name) +#define TRACE_EVENT_INSTANT(category, name, scope, ...) \ + INTERNAL_TRACE_IGNORE(category, name, scope) + +namespace base { +namespace trace_event { + +class BASE_EXPORT ConvertableToTraceFormat { + public: + ConvertableToTraceFormat() = default; + virtual ~ConvertableToTraceFormat(); + + // Append the class info to the provided |out| string. The appended + // data must be a valid JSON object. Strings must be properly quoted, and + // escaped. There is no processing applied to the content after it is + // appended. + virtual void AppendAsTraceFormat(std::string* out) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ConvertableToTraceFormat); +}; + +class BASE_EXPORT TracedValue : public ConvertableToTraceFormat { + public: + explicit TracedValue(size_t capacity = 0) {} + + void EndDictionary() {} + void EndArray() {} + + void SetInteger(const char* name, int value) {} + void SetDouble(const char* name, double value) {} + void SetBoolean(const char* name, bool value) {} + void SetString(const char* name, base::StringPiece value) {} + void SetValue(const char* name, TracedValue* value) {} + void BeginDictionary(const char* name) {} + void BeginArray(const char* name) {} + + void SetIntegerWithCopiedName(base::StringPiece name, int value) {} + void SetDoubleWithCopiedName(base::StringPiece name, double value) {} + void SetBooleanWithCopiedName(base::StringPiece name, bool value) {} + void SetStringWithCopiedName(base::StringPiece name, + base::StringPiece value) {} + void SetValueWithCopiedName(base::StringPiece name, TracedValue* value) {} + void BeginDictionaryWithCopiedName(base::StringPiece name) {} + void BeginArrayWithCopiedName(base::StringPiece name) {} + + void AppendInteger(int) {} + void AppendDouble(double) {} + void AppendBoolean(bool) {} + void AppendString(base::StringPiece) {} + void BeginArray() {} + void BeginDictionary() {} + + void AppendAsTraceFormat(std::string* out) const override; +}; + +class BASE_EXPORT TracedValueJSON : public TracedValue { + public: + explicit TracedValueJSON(size_t capacity = 0) : TracedValue(capacity) {} + + std::unique_ptr<base::Value> ToBaseValue() const { return nullptr; } + std::string ToJSON() const { return ""; } + std::string ToFormattedJSON() const { return ""; } +}; + +class BASE_EXPORT BlameContext { + public: + BlameContext(const char* category, + const char* name, + const char* type, + const char* scope, + int64_t id, + const BlameContext* parent_context) {} + + void Initialize() {} + void Enter() {} + void Leave() {} + void TakeSnapshot() {} + + const char* category() const { return nullptr; } + const char* name() const { return nullptr; } + const char* type() const { return nullptr; } + const char* scope() const { return nullptr; } + int64_t id() const { return 0; } +}; + +struct MemoryDumpArgs; +class ProcessMemoryDump; + +class BASE_EXPORT MemoryDumpProvider { + public: + virtual ~MemoryDumpProvider(); + + virtual bool OnMemoryDump(const MemoryDumpArgs& args, + ProcessMemoryDump* pmd) = 0; + + protected: + MemoryDumpProvider() = default; + + DISALLOW_COPY_AND_ASSIGN(MemoryDumpProvider); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_TRACE_EVENT_STUB_H_ diff --git a/chromium/base/trace_event/trace_event_unittest.cc b/chromium/base/trace_event/trace_event_unittest.cc index 7cdfe504b8c..70f1e1fa260 100644 --- a/chromium/base/trace_event/trace_event_unittest.cc +++ b/chromium/base/trace_event/trace_event_unittest.cc @@ -17,6 +17,7 @@ #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/location.h" +#include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted_memory.h" @@ -2317,7 +2318,7 @@ bool IsArgNameWhitelisted(const char* arg_name) { return base::MatchPattern(arg_name, "granular_arg_whitelisted"); } -bool IsTraceEventArgsWhitelisted(const char* category_group_name, +bool IsTraceEventArgsAllowlisted(const char* category_group_name, const char* event_name, ArgumentNameFilterPredicate* arg_filter) { if (base::MatchPattern(category_group_name, "toplevel") && @@ -2338,7 +2339,7 @@ bool IsTraceEventArgsWhitelisted(const char* category_group_name, TEST_F(TraceEventTestFixture, ArgsWhitelisting) { TraceLog::GetInstance()->SetArgumentFilterPredicate( - base::BindRepeating(&IsTraceEventArgsWhitelisted)); + base::BindRepeating(&IsTraceEventArgsAllowlisted)); TraceLog::GetInstance()->SetEnabled( TraceConfig(kRecordAllCategoryFilter, "enable-argument-filter"), diff --git a/chromium/base/trace_event/trace_log.cc b/chromium/base/trace_event/trace_log.cc index c0d85ea90f1..ac2bc911840 100644 --- a/chromium/base/trace_event/trace_log.cc +++ b/chromium/base/trace_event/trace_log.cc @@ -16,6 +16,7 @@ #include "base/command_line.h" #include "base/debug/leak_annotations.h" #include "base/location.h" +#include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted_memory.h" @@ -172,7 +173,7 @@ void ForEachCategoryFilter(const unsigned char* category_group_enabled, } // The fallback arguments filtering function will filter away every argument. -bool DefaultIsTraceEventArgsWhitelisted( +bool DefaultIsTraceEventArgsAllowlisted( const char* category_group_name, const char* event_name, base::trace_event::ArgumentNameFilterPredicate* arg_name_filter) { @@ -993,7 +994,7 @@ void TraceLog::FinishFlush(int generation, bool discard_events) { // use the safe default filtering predicate. if (argument_filter_predicate_.is_null()) { argument_filter_predicate = - base::BindRepeating(&DefaultIsTraceEventArgsWhitelisted); + base::BindRepeating(&DefaultIsTraceEventArgsAllowlisted); } else { argument_filter_predicate = argument_filter_predicate_; } @@ -1488,7 +1489,7 @@ void TraceLog::UpdateTraceEventDurationExplicit( #if defined(OS_WIN) // Generate an ETW event that marks the end of a complete event. if (category_group_enabled_local & TraceCategory::ENABLED_FOR_ETW_EXPORT) - TraceEventETWExport::AddCompleteEndEvent(name); + TraceEventETWExport::AddCompleteEndEvent(category_group_enabled, name); #endif // OS_WIN if (category_group_enabled_local & TraceCategory::ENABLED_FOR_RECORDING) { diff --git a/chromium/base/trace_event/trace_logging_minimal_win.cc b/chromium/base/trace_event/trace_logging_minimal_win.cc new file mode 100644 index 00000000000..6a346a0e281 --- /dev/null +++ b/chromium/base/trace_event/trace_logging_minimal_win.cc @@ -0,0 +1,351 @@ +// Copyright 2020 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_logging_minimal_win.h" + +#include <evntrace.h> + +#include "base/check_op.h" + +/* +EventSetInformation configuration macros: + +TraceLogging works best if the EventSetInformation API can be used to notify +ETW that the provider uses TraceLogging event encoding. + +The EventSetInformation API is available on Windows 8 and later. (It is also +available on fully-patched Windows 7, but not on Windows 7 RTM). + +The TLM_HAVE_EVENT_SET_INFORMATION and TLM_EVENT_SET_INFORMATION macros can +be set before compiling this file to control how the TlmProvider class deals +with the EventSetInformation API. + +If these macros are not set, the default behavior is to check the WINVER +macro at compile time: + +- If WINVER is set to Windows 7 or before, TlmProvider will use GetProcAddress + to locate EventSetInformation, and then invoke it if present. This is less + efficient, but works on older versions of Windows. +- If WINVER is set to Windows 8 or later, TlmProvider will directly invoke + EventSetInformation. This is more efficient, but the resulting application + will only work correctly on newer versions of Windows. + +If you need to run on Windows 7 RTM, but for some reason need to set WINVER to +Windows 8 or higher, you can override the default behavior by defining +TLM_HAVE_EVENT_SET_INFORMATION=2 when compiling this file. + +Details: +- The TLM_EVENT_SET_INFORMATION macro can be set the name of a replacement + function that TlmProvider should use instead of EventSetInformation. +- The TLM_HAVE_EVENT_SET_INFORMATION macro can be set to 0 (disable the use of + EventSetInformation), 1 (directly invoke EventSetInformation), or 2 (try to + locate EventSetInformation via GetProcAddress, and invoke if found). +*/ + +// This code needs to run on Windows 7 and this is magic which +// removes static linking to EventSetInformation +#define TLM_HAVE_EVENT_SET_INFORMATION 2 + +#ifndef TLM_EVENT_SET_INFORMATION +#define TLM_EVENT_SET_INFORMATION EventSetInformation +#ifndef TLM_HAVE_EVENT_SET_INFORMATION +#if WINVER < 0x0602 || !defined(EVENT_FILTER_TYPE_SCHEMATIZED) +// Find "EventSetInformation" via GetModuleHandleExW+GetProcAddress +#define TLM_HAVE_EVENT_SET_INFORMATION 2 +#else +// Directly invoke TLM_EVENT_SET_INFORMATION(...) +#define TLM_HAVE_EVENT_SET_INFORMATION 1 +#endif +#endif +#elif !defined(TLM_HAVE_EVENT_SET_INFORMATION) +// Directly invoke TLM_EVENT_SET_INFORMATION(...) +#define TLM_HAVE_EVENT_SET_INFORMATION 1 +#endif + +TlmProvider::~TlmProvider() { + Unregister(); +} + +TlmProvider::TlmProvider(const char* provider_name, + const GUID& provider_guid, + PENABLECALLBACK enable_callback, + void* enable_callback_context) noexcept { + int32_t status = Register(provider_name, provider_guid, enable_callback, + enable_callback_context); + DCHECK_EQ(status, ERROR_SUCCESS); +} + +// Appends a nul-terminated string to a metadata block. +// Returns new meta_data_index value, or -1 for overflow. +uint16_t TlmProvider::AppendNameToMetadata(char* metadata, + uint16_t metadata_size, + uint16_t metadata_index, + const char* name) const noexcept { + uint16_t index = metadata_index; + DCHECK_LE(index, metadata_size); + + const size_t cch = strlen(name) + 1; + if (cch > static_cast<unsigned>(metadata_size - index)) { + index = -1; + } else { + memcpy(metadata + index, name, cch); + index += static_cast<uint16_t>(cch); + } + + return index; +} + +void TlmProvider::Unregister() noexcept { + if (reg_handle_ == 0) + return; + + int32_t status = EventUnregister(reg_handle_); + DCHECK_EQ(status, ERROR_SUCCESS); + reg_handle_ = 0; + level_plus1_ = 0; +} + +int32_t TlmProvider::Register(const char* provider_name, + const GUID& provider_guid, + PENABLECALLBACK enable_callback, + void* enable_callback_context) noexcept { + // Calling Register when already registered is a fatal error. + CHECK_EQ(reg_handle_, 0ULL); + + // provider_metadata_ for tracelogging has the following format: + // UINT16 metadata_size; + // char NullTerminatedUtf8ProviderName[]; + // ( + optional extension data, not used here) + + // Append the provider name starting at offset 2 (skip MetadataSize). + provider_metadata_size_ = AppendNameToMetadata( + provider_metadata_, kMaxProviderMetadataSize, 2, provider_name); + if (provider_metadata_size_ > kMaxProviderMetadataSize) { + DCHECK_GT(provider_metadata_size_, kMaxProviderMetadataSize); + return ERROR_BUFFER_OVERFLOW; + } + + // Fill in MetadataSize field at offset 0. + *reinterpret_cast<uint16_t*>(provider_metadata_) = provider_metadata_size_; + + enable_callback_ = enable_callback; + enable_callback_context_ = enable_callback_context; + int32_t status = + EventRegister(&provider_guid, StaticEnableCallback, this, ®_handle_); + if (status != ERROR_SUCCESS) + return status; + +#if TLM_HAVE_EVENT_SET_INFORMATION == 1 + + // Best-effort, ignore failure. + status = + TLM_EVENT_SET_INFORMATION(reg_handle_, EventProviderSetTraits, + provider_metadata_, provider_metadata_size_); + +#elif TLM_HAVE_EVENT_SET_INFORMATION == 2 + + HMODULE eventing_lib; + if (GetModuleHandleExW(0, L"api-ms-win-eventing-provider-l1-1-0.dll", + &eventing_lib) || + GetModuleHandleExW(0, L"advapi32.dll", &eventing_lib)) { + typedef ULONG(WINAPI * PFEventSetInformation)( + REGHANDLE reg_handle, EVENT_INFO_CLASS information_class, + PVOID event_information, ULONG information_length); + PFEventSetInformation event_set_information_ptr = + reinterpret_cast<decltype(&::EventSetInformation)>( + GetProcAddress(eventing_lib, "EventSetInformation")); + if (event_set_information_ptr) { + // Best-effort, ignore failure. + status = event_set_information_ptr(reg_handle_, EventProviderSetTraits, + provider_metadata_, + provider_metadata_size_); + DCHECK_EQ(status, ERROR_SUCCESS); + } + + FreeLibrary(eventing_lib); + } + +#else // TLM_HAVE_EVENT_SET_INFORMATION == 0 + + // Make no attempt to invoke EventSetInformation. + +#endif // TLM_HAVE_EVENT_SET_INFORMATION + + return status; +} + +bool TlmProvider::IsEnabled() const noexcept { + return 0 < level_plus1_; +} + +bool TlmProvider::IsEnabled(uint8_t level) const noexcept { + return level < level_plus1_; +} + +bool TlmProvider::IsEnabled(uint8_t level, uint64_t keyword) const noexcept { + return level < level_plus1_ && KeywordEnabled(keyword); +} + +bool TlmProvider::IsEnabled( + const EVENT_DESCRIPTOR& event_descriptor) const noexcept { + return event_descriptor.Level < level_plus1_ && + KeywordEnabled(event_descriptor.Keyword); +} + +void TlmProvider::StaticEnableCallback(const GUID* source_id, + ULONG is_enabled, + UCHAR level, + ULONGLONG match_any_keyword, + ULONGLONG match_all_keyword, + PEVENT_FILTER_DESCRIPTOR filter_data, + PVOID callback_context) { + if (!callback_context) + return; + + TlmProvider* pProvider = static_cast<TlmProvider*>(callback_context); + switch (is_enabled) { + case EVENT_CONTROL_CODE_DISABLE_PROVIDER: + pProvider->level_plus1_ = 0; + break; + case EVENT_CONTROL_CODE_ENABLE_PROVIDER: + pProvider->level_plus1_ = + level != 0 ? static_cast<unsigned>(level) + 1u : 256u; + pProvider->keyword_any_ = match_any_keyword; + pProvider->keyword_all_ = match_all_keyword; + break; + } + + if (pProvider->enable_callback_) { + pProvider->enable_callback_(source_id, is_enabled, level, match_any_keyword, + match_all_keyword, filter_data, + pProvider->enable_callback_context_); + } +} + +uint16_t TlmProvider::EventBegin(char* metadata, + const char* event_name) const noexcept { + // EventMetadata for tracelogging has the following format + // UINT16 MetadataSize; + // BYTE SpecialFlags[]; // Not used, so always size 1. + // char NullTerminatedUtf8EventName[]; + // ( + field definitions) + + uint16_t index = 2; // Skip MetadataSize field. + + metadata[index] = 0; // Set SpecialFlags[0] = 0. + index++; // sizeof(SpecialFlags) == 1. + + index = + AppendNameToMetadata(metadata, kMaxEventMetadataSize, index, event_name); + return index; +} + +char TlmProvider::EventAddField(char* metadata, + uint16_t* metadata_index, + uint8_t in_type, + uint8_t out_type, + const char* field_name) const noexcept { + DCHECK_LT(in_type, 0x80); + DCHECK_LT(out_type, 0x80); + + // FieldDefinition = + // char NullTerminatedUtf8FieldName[]; + // BYTE InType; + // BYTE OutType; // Only present if high bit set in InType. + // ( + optional extension data not used here) + + if (*metadata_index >= kMaxEventMetadataSize) + return 0; + + *metadata_index = AppendNameToMetadata(metadata, kMaxEventMetadataSize, + *metadata_index, field_name); + if (*metadata_index >= kMaxEventMetadataSize) + return 0; + + if (out_type == 0) { + // 1-byte encoding: inType + TlgOutNULL. + if (1 > kMaxEventMetadataSize - *metadata_index) { + *metadata_index = -1; + return 0; + } + + metadata[*metadata_index] = in_type; + *metadata_index += 1; + return 0; + } + // 2-byte encoding: in_type + out_type. + if (kMaxEventMetadataSize - *metadata_index < 2) { + *metadata_index = -1; + return 0; + } + + // Set high bit to indicate presence of OutType. + metadata[*metadata_index] = in_type | 0x80; + *metadata_index += 1; + metadata[*metadata_index] = out_type; + *metadata_index += 1; + return 0; +} + +int32_t TlmProvider::EventEnd( + char* metadata, + uint16_t meta_data_index, + EVENT_DATA_DESCRIPTOR* descriptors, + uint32_t descriptors_index, + const EVENT_DESCRIPTOR& event_descriptor) const noexcept { + if (meta_data_index > kMaxEventMetadataSize) { + return ERROR_BUFFER_OVERFLOW; + } + + // Fill in EventMetadata's MetadataSize field. + *reinterpret_cast<uint16_t*>(metadata) = meta_data_index; + + descriptors[0].Ptr = reinterpret_cast<ULONG_PTR>(provider_metadata_); + descriptors[0].Size = provider_metadata_size_; + descriptors[0].Reserved = EVENT_DATA_DESCRIPTOR_TYPE_PROVIDER_METADATA; + + descriptors[1].Ptr = reinterpret_cast<ULONG_PTR>(metadata); + descriptors[1].Size = meta_data_index; + descriptors[1].Reserved = EVENT_DATA_DESCRIPTOR_TYPE_EVENT_METADATA; + + return EventWrite(reg_handle_, &event_descriptor, descriptors_index, + descriptors); +} + +bool TlmProvider::KeywordEnabled(uint64_t keyword) const noexcept { + return keyword == 0 || + ((keyword & keyword_any_) && (keyword & keyword_all_) == keyword_all_); +} + +TlmMbcsStringField::TlmMbcsStringField(const char* name, + const char* value) noexcept + : TlmFieldBase(name), value_(value) { + DCHECK_NE(Name(), nullptr); + DCHECK_NE(value_, nullptr); +} + +const char* TlmMbcsStringField::Value() const noexcept { + return value_; +} + +void TlmMbcsStringField::FillEventDescriptor( + EVENT_DATA_DESCRIPTOR* descriptors) const noexcept { + EventDataDescCreate(&descriptors[0], value_, strlen(value_) + 1); +} + +TlmUtf8StringField::TlmUtf8StringField(const char* name, + const char* value) noexcept + : TlmFieldBase(name), value_(value) { + DCHECK_NE(Name(), nullptr); + DCHECK_NE(value_, nullptr); +} + +const char* TlmUtf8StringField::Value() const noexcept { + return value_; +} + +void TlmUtf8StringField::FillEventDescriptor( + EVENT_DATA_DESCRIPTOR* descriptors) const noexcept { + EventDataDescCreate(&descriptors[0], value_, strlen(value_) + 1); +} diff --git a/chromium/base/trace_event/trace_logging_minimal_win.h b/chromium/base/trace_event/trace_logging_minimal_win.h new file mode 100644 index 00000000000..e7b640e9f21 --- /dev/null +++ b/chromium/base/trace_event/trace_logging_minimal_win.h @@ -0,0 +1,393 @@ +// Copyright 2020 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. + +#ifndef BASE_TRACE_EVENT_TRACE_LOGGING_MINIMAL_WIN_H_ +#define BASE_TRACE_EVENT_TRACE_LOGGING_MINIMAL_WIN_H_ + +/* + * TraceLogging minimal dynamic provider + * + * TlmProvider is a simple class that implements an Event Tracing for Windows + * (ETW) provider that generates TraceLogging events with string fields. Unlike + * the Windows SDK's TraceLoggingProvider.h, this provider class supports + * runtime-variable settings for event name, level, keyword, and field name. + * + * Note that this approach is not recommended for general use. Support for + * runtime-variable settings is not normally needed, and it requires extra + * buffering as compared to the approach used by TraceLoggingProvider.h. It is + * needed in this case because we're trying to feed data from the existing call + * sites (which use a runtime-variable function-call syntax) into ETW. If this + * were new code, it would be better to update each call site to use a syntax + * compatible with compile-time event settings compatible with structured + * logging like TraceLoggingProvider.h. + */ + +#include <stdint.h> +#include <windows.h> +// Evntprov.h must come after windows.h. +#include <evntprov.h> +// TODO(joel@microsoft.com) Update headers and use defined constants instead +// of magic numbers after crbug.com/1089996 is resolved. + +/* + * An instance of TlmProvider represents a logger through which data can be + * sent to Event Tracing for Windows (ETW). This logger generates + * TraceLogging-encoded events (compatible with the events generated by the + * Windows SDK's TraceLoggingProvider.h header). In most cases, a developer + * would prefer using TraceLoggingProvider.h over TlmProvider + * (TraceLoggingProvider.h is more efficient and more full-featured), but + * TlmProvider allows for configuring the event parameters (event name, + * level, keyword, field names) at runtime (TraceLoggingProvider.h requires + * these to be set at compile time). + * + * Note that the Register/Unregister operations are relatively expensive, so + * the TlmProvider instance should be a long-lived variable (i.e. global + * variable, static variable, or field of a long-lived object), not a local + * variable andnot a field of a short-lived object. + * + * Note that provider name and provider GUID are a tightly-bound pair, i.e. + * they should each uniquely map to each other. Once a provider name and + * provider GUID have been used together, no other GUID should be used with + * that name and no other name should be used with that GUID. Normally this + * goal is achieved by using a hashing algorithm to generate the GUID from + * a hash of the name. + * + * Note that each event should use a non-zero level and a non-zero keyword. + * Predefined level constants are defined in <evntrace.h>: 0=Always, + * 1=Critical, 2=Error, 3=Warning, 4=Info, 5=Verbose (other level values can + * be used but are not well-defined and are not generally useful). A keyword + * is a bitmask of "category" bits, where each bit indicates whether or not + * the event belongs in a particular category of event. The low 48 bits are + * user-defined and the upper 16 bits are Microsoft-defined (in <winmeta.h>). + * + * General usage: + * + * // During component initialization (main or DllMain), call Register(). + * // Note that there is an overload of the TlmProvider constructor that + * // calls Register(), but it's often convenient to do this manually + * // (i.e. to control the timing of the call to Register). + * my_provider.Register( + * "MyCompany.MyComponentName", + * MyComponentGuid); + * + * // To log an event with minimal code: + * my_provider.WriteEvent("MyEventName", + * TlmEventDescriptor( + * TRACE_LEVEL_VERBOSE, // Level defined in <evntrace.h> + * 0x20), // Keyword bits are user-defined. + * // Value must not be null for the string fields. + * TlmUtf8StringField("MyUtf8Field", GetValue1()), + * TlmMbcsStringField("MyAsciiField", GetValue2())); + * + * // Note that the minimal-code example has a bit of overhead, as it + * // will make the calls to GetValue1(), GetValue2(), and WriteEvent() + * // even if nobody is listening to the event. WriteEvent() will return + // immediately if nobody is listening, but there is still some + * // overhead. To minimize the overhead when nobody is listening, + * // add an extra IF condition: + * static const auto MyEventDescriptor = TlmEventDescriptor( + * TRACE_LEVEL_VERBOSE, // Level defined in <evntrace.h> + * 0x20); // Keyword bits are user-defined. + * if (my_provider.IsEnabled(MyEventDescriptor)) + * { + * // The IF condition is primarily to prevent unnecessary + * // calls to GetValue1() and GetValue2(). + * my_provider.WriteEvent("MyEventName", + * MyEventDescriptor, + * // Value must not be null for the string fields. + * TlmUtf8StringField("MyUtf8Field", GetValue1()), + * TlmMbcsStringField("MyAsciiField", GetValue2())); + * } + * + * // During component shutdown (main or DllMain), call Unregister(). + * // Note that the TlmProvider destructor will also call + * // Unregister(), butit's often convenient to do this manually + * // (i.e. to control the timingof the call to Unregister). + * my_provider.Unregister(); + */ + +class TlmProvider { + public: + // Initialize a provider in the unregistered state. + // Note that WriteEvent and Unregister operations on an unregistered + // provider are safe no-ops. + constexpr TlmProvider() noexcept = default; + + // Initializes a provider and attempts to register it. + // If there is an error, provider will be left unregistered. + // Note that WriteEvent and Unregister operations on an unregistered + // provider are safe no-ops. + TlmProvider(const char* provider_name, + const GUID& provider_guid, + PENABLECALLBACK enable_callback = nullptr, + void* enable_callback_context = nullptr) noexcept; + + // If provider is registered, unregisters provider. + ~TlmProvider(); + + // Disable copy operations. + TlmProvider(const TlmProvider&) = delete; + TlmProvider& operator=(const TlmProvider&) = delete; + + // Unregisters this provider. + // Calling Unregister on an unregistered provider is a safe no-op. + // Not thread safe - caller must ensure serialization between calls to + // Register() and calls to Unregister(). + void Unregister() noexcept; + + // Registers this provider. Returns Win32 error code or 0 for success. + // Error code is primarily for debugging and should generally be ignored + // in production (failure to register means Unregister and WriteEvent are + // safe no-ops.) + // Calling Register on an already-registered provider is a fatal error. + // Not thread safe - caller must ensure serialization between calls to + // Register() and calls to Unregister(). + int32_t Register(const char* provider_name, + const GUID& provider_guid, + PENABLECALLBACK enable_callback = nullptr, + void* enable_callback_context = nullptr) noexcept; + + // Returns true if any active trace listeners are interested in any events + // from this provider. + // Equivalent to IsEnabled(0, 0). + bool IsEnabled() const noexcept; + + // Returns true if any active trace listeners are interested in events + // from this provider with the specified level. + // Equivalent to IsEnabled(level, 0). + bool IsEnabled(uint8_t level) const noexcept; + + // Returns true if any active trace listeners are interested in events + // from this provider with the specified level and keyword. + bool IsEnabled(uint8_t level, uint64_t keyword) const noexcept; + + // Returns true if any active trace listeners are interested in events + // from this provider with the specified level and keyword. + // Equivalent to IsEnabled(event_descriptor.level, event_descriptor.keyword). + bool IsEnabled(const EVENT_DESCRIPTOR& event_descriptor) const noexcept; + + // If any active trace listeners are interested in events from this provider + // with the specified level and keyword, packs the data into an event and + // sends it to ETW. Returns Win32 error code or 0 for success. + template <class... FieldTys> + int32_t WriteEvent(const char* event_name, + const EVENT_DESCRIPTOR& event_descriptor, + const FieldTys&... event_fields) const noexcept { + if (!IsEnabled(event_descriptor)) { + // If nobody is listening, report success. + return 0; + } + // Pack the event metadata. + char metadata[kMaxEventMetadataSize]; + uint16_t metadata_index; + metadata_index = EventBegin(metadata, event_name); + { // scope for dummy array (simulates a C++17 comma-fold expression) + bool dummy[sizeof...(FieldTys) == 0 ? 1 : sizeof...(FieldTys)] = { + EventAddField(metadata, &metadata_index, event_fields.in_type_, + event_fields.out_type_, event_fields.Name())...}; + DCHECK(dummy); + } + + // Pack the event data. + constexpr uint8_t kDescriptorsCount = + 2 + DataDescCountSum<FieldTys...>::value; + EVENT_DATA_DESCRIPTOR descriptors[kDescriptorsCount]; + uint8_t descriptors_index = 2; + { // scope for dummy array (simulates a C++17 comma-fold expression) + bool dummy[sizeof...(FieldTys) == 0 ? 1 : sizeof...(FieldTys)] = { + EventDescriptorFill(descriptors, &descriptors_index, + event_fields)...}; + DCHECK(dummy); + } + + // Finalize event and call EventWrite. + return EventEnd(metadata, metadata_index, descriptors, descriptors_index, + event_descriptor); + } + + private: + // Size of the buffer used for provider metadata (field within the + // TlmProvider object). Provider metadata consists of the nul-terminated + // provider name plus a few sizes and flags, so this buffer needs to be + // just a few bytes larger than the largest expected provider name. + static constexpr uint16_t kMaxProviderMetadataSize = 128; + + // Size of the buffer used for event metadata (stack-allocated in the + // WriteEvent method). Event metadata consists of nul-terminated event + // name, nul-terminated field names, field types (1 or 2 bytes per field), + // and a few bytes for sizes and flags. + static constexpr uint16_t kMaxEventMetadataSize = 256; + + template <class... FieldTys> + struct DataDescCountSum; // undefined + + template <> + struct DataDescCountSum<> { + static constexpr uint8_t value = 0; + }; + + template <class FieldTy1, class... FieldTyRest> + struct DataDescCountSum<FieldTy1, FieldTyRest...> { + static constexpr uint8_t value = + FieldTy1::data_desc_count_ + DataDescCountSum<FieldTyRest...>::value; + }; + + template <class FieldTy> + static char EventDescriptorFill(EVENT_DATA_DESCRIPTOR* descriptors, + uint8_t* pdescriptors_index, + const FieldTy& event_field) noexcept { + event_field.FillEventDescriptor(&descriptors[*pdescriptors_index]); + *pdescriptors_index += FieldTy::data_desc_count_; + return 0; + } + + // This is called from the OS, so use the required call type. + static void __stdcall StaticEnableCallback( + const GUID* source_id, + ULONG is_enabled, + UCHAR level, + ULONGLONG match_any_keyword, + ULONGLONG match_all_keyword, + PEVENT_FILTER_DESCRIPTOR FilterData, + PVOID callback_context); + + // Returns initial value of metadata_index. + uint16_t EventBegin(char* metadata, const char* event_name) const noexcept; + + char EventAddField(char* metadata, + uint16_t* metadata_index, + uint8_t in_type, + uint8_t out_type, + const char* field_name) const noexcept; + + // Returns Win32 error code, or 0 for success. + int32_t EventEnd(char* metadata, + uint16_t metadata_index, + EVENT_DATA_DESCRIPTOR* descriptors, + uint32_t descriptors_index, + const EVENT_DESCRIPTOR& event_descriptor) const noexcept; + + bool KeywordEnabled(uint64_t keyword) const noexcept; + + uint16_t AppendNameToMetadata(char* metadata, + uint16_t metadata_size, + uint16_t metadata_index, + const char* name) const noexcept; + + uint32_t level_plus1_ = 0; + uint32_t provider_metadata_size_ = 0; + uint64_t keyword_any_ = 0; + uint64_t keyword_all_ = 0; + uint64_t reg_handle_ = 0; + PENABLECALLBACK enable_callback_ = nullptr; + void* enable_callback_context_ = nullptr; + char provider_metadata_[kMaxProviderMetadataSize] = {}; +}; + +// Base class for field types. +template <uint8_t data_desc_count, + uint8_t in_type, + uint8_t out_type = 0> // Default out_type is TlgOutNULL +class TlmFieldBase { + public: + constexpr const char* Name() const noexcept { return name_; } + + protected: + explicit constexpr TlmFieldBase(const char* name) noexcept : name_(name) {} + + private: + friend class TlmProvider; + + static constexpr uint8_t data_desc_count_ = data_desc_count; + static constexpr uint8_t in_type_ = in_type; + static constexpr uint8_t out_type_ = out_type; + + const char* name_; +}; + +// Class that represents an event field containing nul-terminated MBCS data. +class TlmMbcsStringField + : public TlmFieldBase<1, 2> // 1 data descriptor, Type = TlgInANSISTRING +{ + public: + // name is a utf-8 nul-terminated string. + // value is MBCS nul-terminated string (assumed to be in system's default code + // page). + TlmMbcsStringField(const char* name, const char* value) noexcept; + + const char* Value() const noexcept; + + void FillEventDescriptor(EVENT_DATA_DESCRIPTOR* descriptors) const noexcept; + + private: + const char* value_; +}; + +// Class that represents an event field containing nul-terminated UTF-8 data. +class TlmUtf8StringField + : public TlmFieldBase<1, 2, 35> // 1 data descriptor, Type = + // TlgInANSISTRING + TlgOutUTF8 +{ + public: + // name and value are utf-8 nul-terminated strings. + TlmUtf8StringField(const char* name, const char* value) noexcept; + + const char* Value() const noexcept; + + void FillEventDescriptor(EVENT_DATA_DESCRIPTOR* descriptors) const noexcept; + + private: + const char* value_; +}; + +// Helper for creating event descriptors for use with WriteEvent. +constexpr EVENT_DESCRIPTOR TlmEventDescriptor(uint8_t level, + uint64_t keyword) noexcept { + return { + + // Id + // TraceLogging generally uses the event's Name instead of Id+Version, + // so Id is normally set to 0 for TraceLogging events. + 0, + + // Version + // TraceLogging generally uses the event's Name instead of Id+Version, + // so Version is normally set to 0 for TraceLogging events. + 0, + + // Channel (WINEVENT_CHANNEL_*) + // Setting Channel = 11 allows TraceLogging events to be decoded + // correctly even if they were collected on older operating systems. + // If a TraceLogging event sets channel to a value other than 11, the + // event will only decode correctly if it was collected on an + // operating system that has built-in TraceLogging support, i.e. + // Windows 7sp1 + patch, Windows 8.1 + patch, or Windows 10+. + 11, // = WINEVENT_CHANNEL_TRACELOGGING + + // Level (WINEVENT_LEVEL_*) + // 0=always, 1=fatal, 2=error, 3=warning, 4=info, 5=verbose. + // Levels higher than 5 are for user-defined debug levels. + level, + + // Opcode (WINEVENT_OPCODE_*) + // Set Opcode for special semantics such as starting/ending an + // activity. + 0, // = WINEVENT_OPCODE_INFO + + // Task + // Set Task for user-defined semantics. + 0, // = WINEVENT_TASK_NONE + + // Keyword + // A keyword is a 64-bit value used for filtering events. Each bit of + // the keyword indicates whether the event belongs to a particular + // category of events. The top 16 bits of keyword have + // Microsoft-defined semantics and should be set to 0. The low 48 bits + // of keyword have user-defined semantics. All events should use a + // nonzero keyword to support effective event filtering (events with + // keyword set to 0 always pass keyword filtering). + keyword}; +} + +#endif // BASE_TRACE_EVENT_TRACE_LOGGING_MINIMAL_WIN_H_
\ No newline at end of file diff --git a/chromium/base/trace_event/typed_macros.h b/chromium/base/trace_event/typed_macros.h new file mode 100644 index 00000000000..8b792c4d52b --- /dev/null +++ b/chromium/base/trace_event/typed_macros.h @@ -0,0 +1,74 @@ +// Copyright 2019 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. + +#ifndef BASE_TRACE_EVENT_TYPED_MACROS_H_ +#define BASE_TRACE_EVENT_TYPED_MACROS_H_ + +#include "base/base_export.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/typed_macros_internal.h" +#include "build/build_config.h" + +// Needed not for this file, but for every user of the TRACE_EVENT macros for +// the lambda definition. So included here for convenience. +#include "third_party/perfetto/include/perfetto/tracing/event_context.h" +#include "third_party/perfetto/protos/perfetto/trace/track_event/track_event.pbzero.h" + +#if defined(TRACE_EVENT_BEGIN) +#error "Another copy of perfetto tracing macros have been included" +#endif + +// This file implements typed event macros [1] that will be provided by the +// Perfetto client library in the future, as a stop-gap to support typed trace +// events in Chrome until we are ready to switch to the client library's +// implementation of trace macros. +// [1] https://perfetto.dev/docs/instrumentation/track-events +// TODO(crbug/1006541): Replace this file with the Perfetto client library. + +// Begin a thread-scoped slice under |category| with the title |name|. Both +// strings must be static constants. The track event is only recorded if +// |category| is enabled for a tracing session. +// +// Rest of parameters can contain: a perfetto::Track object for asynchronous +// events and a lambda used to fill typed event. Should be passed in that exact +// order when both are used. +// +// When lambda is passed as an argument, it is executed synchronously. +// +// TODO(nuskos): Give a simple example once we have a typed event that doesn't +// need interning. +// TRACE_EVENT_BEGIN("log", "LogMessage", +// [](perfetto::EventContext ctx) { +// auto* event = ctx.event(); +// // Fill in some field in track_event. +// }); +#define TRACE_EVENT_BEGIN(category, name, ...) \ + TRACING_INTERNAL_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_BEGIN, category, name, \ + TRACE_EVENT_FLAG_NONE, ##__VA_ARGS__) + +// End a thread-scoped slice under |category|. +#define TRACE_EVENT_END(category, ...) \ + TRACING_INTERNAL_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_END, category, \ + trace_event_internal::kTraceEventEndName, \ + TRACE_EVENT_FLAG_NONE, ##__VA_ARGS__) + +// Begin a thread-scoped slice which gets automatically closed when going out +// of scope. +// +// BEWARE: similarly to TRACE_EVENT_BEGIN, this macro does accept a track, but +// it does not work properly and should not be used. +// TODO(b/154583431): figure out how to fix or disallow that and update the +// comment. +// +// Similarly to TRACE_EVENT_BEGIN, when lambda is passed as an argument, it is +// executed synchronously. +#define TRACE_EVENT(category, name, ...) \ + TRACING_INTERNAL_SCOPED_ADD_TRACE_EVENT(category, name, ##__VA_ARGS__) + +// Emit a single event called "name" immediately, with zero duration. +#define TRACE_EVENT_INSTANT(category, name, scope, ...) \ + TRACING_INTERNAL_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_INSTANT, category, name, \ + scope, ##__VA_ARGS__) + +#endif // BASE_TRACE_EVENT_TYPED_MACROS_H_ diff --git a/chromium/base/trace_event/typed_macros_embedder_support.h b/chromium/base/trace_event/typed_macros_embedder_support.h new file mode 100644 index 00000000000..d06c5b8a359 --- /dev/null +++ b/chromium/base/trace_event/typed_macros_embedder_support.h @@ -0,0 +1,65 @@ +// Copyright 2020 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. + +#ifndef BASE_TRACE_EVENT_TYPED_MACROS_EMBEDDER_SUPPORT_H_ +#define BASE_TRACE_EVENT_TYPED_MACROS_EMBEDDER_SUPPORT_H_ + +#include "base/base_export.h" +#include "base/trace_event/trace_event.h" +#include "third_party/perfetto/protos/perfetto/trace/track_event/track_event.pbzero.h" + +namespace base { +namespace trace_event { + +class BASE_EXPORT TrackEventHandle { + public: + using TrackEvent = perfetto::protos::pbzero::TrackEvent; + + class BASE_EXPORT CompletionListener { + public: + // Implemented in typed_macros_internal.h. + virtual ~CompletionListener(); + virtual void OnTrackEventCompleted() = 0; + }; + + // Creates a handle to |event| which notifies |listener| on the handle's + // destruction, i.e. after the event lambda has emitted any typed arguments + // into the event. Note that |listener| must outlive the TRACE_EVENT call, + // i.e. cannot be destroyed until OnTrackEventCompleted() is called. Ownership + // of both TrackEvent and the listener remains with the caller. + TrackEventHandle(TrackEvent* event, CompletionListener* listener) + : event_(event), listener_(listener) {} + + // Creates an invalid handle. + TrackEventHandle() : TrackEventHandle(nullptr, nullptr) {} + + ~TrackEventHandle() { + if (listener_) + listener_->OnTrackEventCompleted(); + } + + explicit operator bool() const { return event_; } + TrackEvent& operator*() const { return *event_; } + TrackEvent* operator->() const { return event_; } + TrackEvent* get() const { return event_; } + + private: + TrackEvent* event_; + CompletionListener* listener_; +}; + +using PrepareTrackEventFunction = TrackEventHandle (*)(TraceEvent*); + +// Embedder should call this (only once) to set the callback invoked when a +// typed event should be emitted. The callback function may be executed on any +// thread. Implemented in typed_macros_internal.h. +BASE_EXPORT void EnableTypedTraceEvents( + PrepareTrackEventFunction typed_event_callback); + +BASE_EXPORT void ResetTypedTraceEventsForTesting(); + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_TYPED_MACROS_EMBEDDER_SUPPORT_H_ diff --git a/chromium/base/trace_event/typed_macros_internal.cc b/chromium/base/trace_event/typed_macros_internal.cc new file mode 100644 index 00000000000..89825fde5cb --- /dev/null +++ b/chromium/base/trace_event/typed_macros_internal.cc @@ -0,0 +1,95 @@ +// Copyright 2019 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/typed_macros_internal.h" + +#include "base/optional.h" +#include "base/time/time.h" +#include "base/trace_event/thread_instruction_count.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/typed_macros.h" + +namespace { + +base::ThreadTicks ThreadNow() { + return base::ThreadTicks::IsSupported() + ? base::subtle::ThreadTicksNowIgnoringOverride() + : base::ThreadTicks(); +} + +base::trace_event::ThreadInstructionCount ThreadInstructionNow() { + return base::trace_event::ThreadInstructionCount::IsSupported() + ? base::trace_event::ThreadInstructionCount::Now() + : base::trace_event::ThreadInstructionCount(); +} + +base::trace_event::PrepareTrackEventFunction g_typed_event_callback = nullptr; + +} // namespace + +namespace base { +namespace trace_event { + +void EnableTypedTraceEvents(PrepareTrackEventFunction typed_event_callback) { + g_typed_event_callback = typed_event_callback; +} + +void ResetTypedTraceEventsForTesting() { + g_typed_event_callback = nullptr; +} + +TrackEventHandle::CompletionListener::~CompletionListener() = default; + +} // namespace trace_event +} // namespace base + +namespace trace_event_internal { + +base::trace_event::TrackEventHandle CreateTrackEvent( + char phase, + const unsigned char* category_group_enabled, + const char* name, + unsigned int flags, + base::TimeTicks ts, + bool explicit_track) { + DCHECK(phase == TRACE_EVENT_PHASE_BEGIN || phase == TRACE_EVENT_PHASE_END || + phase == TRACE_EVENT_PHASE_INSTANT); + DCHECK(category_group_enabled); + + if (!g_typed_event_callback) + return base::trace_event::TrackEventHandle(); + + const int thread_id = static_cast<int>(base::PlatformThread::CurrentId()); + auto* trace_log = base::trace_event::TraceLog::GetInstance(); + DCHECK(trace_log); + if (!trace_log->ShouldAddAfterUpdatingState(phase, category_group_enabled, + name, trace_event_internal::kNoId, + thread_id, nullptr)) { + return base::trace_event::TrackEventHandle(); + } + + if (ts.is_null()) { + ts = TRACE_TIME_TICKS_NOW(); + } else { + flags |= TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP; + } + + // Only emit thread time / instruction count for events on the default track + // without explicit timestamp. + base::ThreadTicks thread_now; + base::trace_event::ThreadInstructionCount thread_instruction_now; + if ((flags & TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP) == 0 && !explicit_track) { + thread_now = ThreadNow(); + thread_instruction_now = ThreadInstructionNow(); + } + + base::trace_event::TraceEvent event( + thread_id, ts, thread_now, thread_instruction_now, phase, + category_group_enabled, name, trace_event_internal::kGlobalScope, + trace_event_internal::kNoId, trace_event_internal::kNoId, nullptr, flags); + + return g_typed_event_callback(&event); +} + +} // namespace trace_event_internal diff --git a/chromium/base/trace_event/typed_macros_internal.h b/chromium/base/trace_event/typed_macros_internal.h new file mode 100644 index 00000000000..51e91b7e56e --- /dev/null +++ b/chromium/base/trace_event/typed_macros_internal.h @@ -0,0 +1,176 @@ +// Copyright 2019 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. + +#ifndef BASE_TRACE_EVENT_TYPED_MACROS_INTERNAL_H_ +#define BASE_TRACE_EVENT_TYPED_MACROS_INTERNAL_H_ + +#include "base/base_export.h" +#include "base/optional.h" +#include "base/time/time.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/typed_macros_embedder_support.h" +#include "build/build_config.h" +#include "third_party/perfetto/include/perfetto/protozero/message_handle.h" +#include "third_party/perfetto/include/perfetto/tracing/event_context.h" +#include "third_party/perfetto/include/perfetto/tracing/track.h" +#include "third_party/perfetto/protos/perfetto/trace/track_event/track_event.pbzero.h" + +// These macros should not be called directly. They are intended to be used by +// macros in //base/trace_event/typed_macros.h only. + +#define TRACING_INTERNAL_CONCAT2(a, b) a##b +#define TRACING_INTERNAL_CONCAT(a, b) TRACING_INTERNAL_CONCAT2(a, b) +#define TRACING_INTERNAL_UID(prefix) TRACING_INTERNAL_CONCAT(prefix, __LINE__) + +#define TRACING_INTERNAL_ADD_TRACE_EVENT(phase, category, name, flags, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED()) { \ + trace_event_internal::AddTraceEvent( \ + phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + flags, ##__VA_ARGS__); \ + } \ + } while (false) + +#define TRACING_INTERNAL_SCOPED_ADD_TRACE_EVENT(category, name, ...) \ + struct { \ + struct ScopedTraceEvent { \ + /* The parameter is an implementation detail. It allows the */ \ + /* anonymous struct to use aggregate initialization to invoke the */ \ + /* lambda to emit the begin event with the proper reference capture */ \ + /* for any TrackEventArgumentFunction in |__VA_ARGS__|. This is */ \ + /* required so that the scoped event is exactly ONE line and can't */ \ + /* escape the scope if used in a single line if statement. */ \ + ScopedTraceEvent(...) {} \ + ~ScopedTraceEvent() { \ + /* TODO(nuskos): Remove the empty string passed as the |name| */ \ + /* field. As described in macros.h we shouldn't need it in our */ \ + /* end state. */ \ + TRACING_INTERNAL_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_END, category, "", \ + TRACE_EVENT_FLAG_NONE, \ + [](perfetto::EventContext) {}); \ + } \ + } event; \ + } TRACING_INTERNAL_UID(scoped_event){[&]() { \ + TRACING_INTERNAL_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_BEGIN, category, name, \ + TRACE_EVENT_FLAG_NONE, ##__VA_ARGS__); \ + return 0; \ + }()}; + +namespace trace_event_internal { + +// Copy of function with the same name from Perfetto client library. +template <typename T> +static constexpr bool IsValidTraceLambdaImpl( + typename std::enable_if<static_cast<bool>( + sizeof(std::declval<T>()(std::declval<perfetto::EventContext>()), + 0))>::type* = nullptr) { + return true; +} + +template <typename T> +static constexpr bool IsValidTraceLambdaImpl(...) { + return false; +} + +template <typename T> +static constexpr bool IsValidTraceLambda() { + return IsValidTraceLambdaImpl<T>(nullptr); +} + +// The perfetto client library does not use event names for +// TRACE_EVENT_PHASE_END. However, TraceLog expects all TraceEvents to have +// event names. So, until we move over to the client library, we will use this +// empty name for all TRACE_EVENT_PHASE_END typed events. +constexpr char kTraceEventEndName[] = ""; + +base::trace_event::TrackEventHandle BASE_EXPORT +CreateTrackEvent(char phase, + const unsigned char* category_group_enabled, + const char* name, + unsigned int flags, + base::TimeTicks timestamp, + bool explicit_track); + +template < + typename TrackEventArgumentFunction = void (*)(perfetto::EventContext), + typename ArgumentFunctionCheck = typename std::enable_if< + IsValidTraceLambda<TrackEventArgumentFunction>()>::type> +static inline void AddTraceEvent(char phase, + const unsigned char* category_group_enabled, + const char* name, + unsigned int flags, + const perfetto::Track& track, + base::TimeTicks timestamp, + TrackEventArgumentFunction argument_func) { + base::trace_event::TrackEventHandle track_event = + CreateTrackEvent(phase, category_group_enabled, name, flags, timestamp, + track.uuid != perfetto::Track().uuid); + if (!track_event) + return; + + if (track) + track_event->set_track_uuid(track.uuid); + + argument_func(perfetto::EventContext(track_event.get())); +} + +template < + typename TrackEventArgumentFunction = void (*)(perfetto::EventContext), + typename ArgumentFunctionCheck = typename std::enable_if< + IsValidTraceLambda<TrackEventArgumentFunction>()>::type, + typename TrackType, + typename TrackTypeCheck = typename std::enable_if< + std::is_convertible<TrackType, perfetto::Track>::value>::type> +static inline void AddTraceEvent(char phase, + const unsigned char* category_group_enabled, + const char* name, + unsigned int flags, + const TrackType& track, + TrackEventArgumentFunction argument_func) { + AddTraceEvent(phase, category_group_enabled, name, flags, track, + base::TimeTicks(), argument_func); +} + +template < + typename TrackEventArgumentFunction = void (*)(perfetto::EventContext), + typename ArgumentFunctionCheck = typename std::enable_if< + IsValidTraceLambda<TrackEventArgumentFunction>()>::type> +static inline void AddTraceEvent(char phase, + const unsigned char* category_group_enabled, + const char* name, + unsigned int flags, + TrackEventArgumentFunction argument_func) { + AddTraceEvent(phase, category_group_enabled, name, flags, perfetto::Track(), + base::TimeTicks(), argument_func); +} + +template <typename TrackType, + typename TrackTypeCheck = typename std::enable_if< + std::is_convertible<TrackType, perfetto::Track>::value>::type> +inline void AddTraceEvent(char phase, + const unsigned char* category_group_enabled, + const char* name, + unsigned int flags, + const TrackType& track) { + AddTraceEvent(phase, category_group_enabled, name, flags, track, + base::TimeTicks(), [](perfetto::EventContext ctx) {}); +} + +template <typename TrackType, + typename TrackTypeCheck = typename std::enable_if< + std::is_convertible<TrackType, perfetto::Track>::value>::type> +inline void AddTraceEvent(char phase, + const unsigned char* category_group_enabled, + const char* name, + unsigned int flags, + const TrackType& track, + base::TimeTicks timestamp) { + AddTraceEvent(phase, category_group_enabled, name, flags, track, timestamp, + [](perfetto::EventContext ctx) {}); +} + +} // namespace trace_event_internal + +#endif // BASE_TRACE_EVENT_TYPED_MACROS_INTERNAL_H_ diff --git a/chromium/base/trace_event/typed_macros_unittest.cc b/chromium/base/trace_event/typed_macros_unittest.cc new file mode 100644 index 00000000000..741b43fba3f --- /dev/null +++ b/chromium/base/trace_event/typed_macros_unittest.cc @@ -0,0 +1,111 @@ +// Copyright (c) 2020 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/typed_macros.h" + +#include "base/synchronization/waitable_event.h" +#include "base/trace_event/trace_log.h" +#include "base/trace_event/typed_macros_embedder_support.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/perfetto/include/perfetto/protozero/scattered_heap_buffer.h" + +#include "third_party/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.h" + +namespace base { +namespace trace_event { + +namespace { + +constexpr const char kRecordAllCategoryFilter[] = "*"; + +void CancelTraceAsync(WaitableEvent* flush_complete_event) { + TraceLog::GetInstance()->CancelTracing(base::BindRepeating( + [](WaitableEvent* complete_event, + const scoped_refptr<base::RefCountedString>&, bool has_more_events) { + if (!has_more_events) + complete_event->Signal(); + }, + base::Unretained(flush_complete_event))); +} + +void CancelTrace() { + WaitableEvent flush_complete_event(WaitableEvent::ResetPolicy::AUTOMATIC, + WaitableEvent::InitialState::NOT_SIGNALED); + CancelTraceAsync(&flush_complete_event); + flush_complete_event.Wait(); +} + +struct TestTrackEvent; +TestTrackEvent* g_test_track_event; + +struct TestTrackEvent : public TrackEventHandle::CompletionListener { + public: + TestTrackEvent() { + CHECK_EQ(g_test_track_event, nullptr) + << "Another instance of TestTrackEvent is already active"; + g_test_track_event = this; + } + + ~TestTrackEvent() override { g_test_track_event = nullptr; } + + void OnTrackEventCompleted() override { + EXPECT_FALSE(event_completed); + event_completed = true; + } + + protozero::HeapBuffered<perfetto::protos::pbzero::TrackEvent> event; + bool prepare_called = false; + bool event_completed = false; +}; + +TrackEventHandle PrepareTrackEvent(TraceEvent*) { + CHECK_NE(g_test_track_event, nullptr) << "TestTrackEvent not set yet"; + g_test_track_event->prepare_called = true; + return TrackEventHandle(g_test_track_event->event.get(), g_test_track_event); +} + +class TypedTraceEventTest : public testing::Test { + public: + TypedTraceEventTest() { EnableTypedTraceEvents(&PrepareTrackEvent); } + + ~TypedTraceEventTest() override { ResetTypedTraceEventsForTesting(); } + + protected: + TestTrackEvent event_; +}; + +} // namespace + +TEST_F(TypedTraceEventTest, CallbackExecutedWhenTracingEnabled) { + TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), + TraceLog::RECORDING_MODE); + + TRACE_EVENT("cat", "Name", [this](perfetto::EventContext ctx) { + EXPECT_EQ(ctx.event(), event_.event.get()); + perfetto::protos::pbzero::LogMessage* log = ctx.event()->set_log_message(); + log->set_body_iid(1); + }); + + EXPECT_TRUE(event_.prepare_called); + EXPECT_FALSE(event_.event.empty()); + EXPECT_TRUE(event_.event_completed); + + CancelTrace(); +} + +TEST_F(TypedTraceEventTest, CallbackNotExecutedWhenTracingDisabled) { + TRACE_EVENT("cat", "Name", [this](perfetto::EventContext ctx) { + EXPECT_EQ(ctx.event(), event_.event.get()); + perfetto::protos::pbzero::LogMessage* log = ctx.event()->set_log_message(); + log->set_body_iid(1); + }); + + EXPECT_FALSE(event_.prepare_called); + EXPECT_TRUE(event_.event.empty()); + EXPECT_FALSE(event_.event_completed); +} + +} // namespace trace_event +} // namespace base |