| // Copyright 2013 The Flutter 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 "flutter/shell/common/skia_event_tracer_impl.h" |
| |
| #define TRACE_EVENT_HIDE_MACROS |
| #include <map> |
| #include <set> |
| #include <vector> |
| |
| #include "flutter/fml/logging.h" |
| #include "flutter/fml/posix_wrappers.h" |
| #include "flutter/fml/trace_event.h" |
| #include "third_party/dart/runtime/include/dart_tools_api.h" |
| #include "third_party/skia/include/utils/SkEventTracer.h" |
| #include "third_party/skia/include/utils/SkTraceEventPhase.h" |
| |
| #if defined(OS_FUCHSIA) |
| |
| #include <algorithm> |
| #include <cstring> |
| |
| #include <lib/trace-engine/context.h> |
| #include <lib/trace-engine/instrumentation.h> |
| |
| // Skia's copy of these flags are defined in a private header, so, as is |
| // commonly done with "trace_event_common.h" values, copy them inline here (see |
| // https://cs.chromium.org/chromium/src/base/trace_event/common/trace_event_common.h?l=1102-1110&rcl=239b85aeb3a6c07b33b5f162cd0ae8128eabf44d). |
| // |
| // Type values for identifying types in the TraceValue union. |
| #define TRACE_VALUE_TYPE_BOOL (static_cast<unsigned char>(1)) |
| #define TRACE_VALUE_TYPE_UINT (static_cast<unsigned char>(2)) |
| #define TRACE_VALUE_TYPE_INT (static_cast<unsigned char>(3)) |
| #define TRACE_VALUE_TYPE_DOUBLE (static_cast<unsigned char>(4)) |
| #define TRACE_VALUE_TYPE_POINTER (static_cast<unsigned char>(5)) |
| #define TRACE_VALUE_TYPE_STRING (static_cast<unsigned char>(6)) |
| #define TRACE_VALUE_TYPE_COPY_STRING (static_cast<unsigned char>(7)) |
| #define TRACE_VALUE_TYPE_CONVERTABLE (static_cast<unsigned char>(8)) |
| |
| #endif // defined(OS_FUCHSIA) |
| |
| namespace flutter { |
| |
| namespace { |
| |
| // Skia prepends this string to the category names of its trace events. |
| // Defined in Skia's src/core/SkTraceEvent.h. |
| constexpr std::string_view kTraceCategoryPrefix = "disabled-by-default-"; |
| |
| // Category name used for shader compilation events. |
| constexpr std::string_view kShaderCategoryName = |
| "disabled-by-default-skia.shaders"; |
| |
| #if !defined(OS_FUCHSIA) |
| // Argument name of the tag used by DevTools. |
| constexpr char kDevtoolsTagArg[] = "devtoolsTag"; |
| |
| // DevtoolsTag value for shader events. |
| constexpr char kShadersDevtoolsTag[] = "shaders"; |
| #endif // !defined(OS_FUCHSIA) |
| |
| #if defined(OS_FUCHSIA) |
| template <class T, class U> |
| inline T BitCast(const U& u) { |
| static_assert(sizeof(T) == sizeof(U)); |
| |
| T t; |
| memcpy(&t, &u, sizeof(t)); |
| return t; |
| } |
| #endif // defined(OS_FUCHSIA) |
| |
| } // namespace |
| |
| class FlutterEventTracer : public SkEventTracer { |
| public: |
| static constexpr const char* kSkiaTag = "skia"; |
| static constexpr uint8_t kYes = 1; |
| static constexpr uint8_t kNo = 0; |
| |
| FlutterEventTracer(bool enabled, |
| const std::optional<std::vector<std::string>>& allowlist) |
| : enabled_(enabled ? kYes : kNo) { |
| if (allowlist.has_value()) { |
| allowlist_.emplace(); |
| for (const std::string& category : *allowlist) { |
| allowlist_->insert(std::string(kTraceCategoryPrefix) + category); |
| } |
| } |
| }; |
| |
| SkEventTracer::Handle addTraceEvent(char phase, |
| const uint8_t* category_enabled_flag, |
| const char* name, |
| uint64_t id, |
| int num_args, |
| const char** p_arg_names, |
| const uint8_t* p_arg_types, |
| const uint64_t* p_arg_values, |
| uint8_t flags) override { |
| #if defined(OS_FUCHSIA) |
| static trace_site_t trace_site; |
| trace_string_ref_t category_ref; |
| trace_context_t* trace_context = trace_acquire_context_for_category_cached( |
| kSkiaTag, &trace_site, &category_ref); |
| |
| if (likely(!trace_context)) { |
| return 0; |
| } |
| |
| trace_ticks_t ticks = zx_ticks_get(); |
| |
| trace_thread_ref_t thread_ref; |
| trace_context_register_current_thread(trace_context, &thread_ref); |
| trace_string_ref_t name_ref; |
| trace_context_register_string_literal(trace_context, name, &name_ref); |
| |
| constexpr int kMaxArgs = 2; |
| trace_arg_t trace_args[kMaxArgs] = {}; |
| FML_DCHECK(num_args >= 0); |
| int num_trace_args = std::min(kMaxArgs, num_args); |
| |
| for (int i = 0; i < num_trace_args; i++) { |
| const char* arg_name = p_arg_names[i]; |
| const uint8_t arg_type = p_arg_types[i]; |
| const uint64_t arg_value = p_arg_values[i]; |
| |
| trace_string_ref_t arg_name_string_ref = |
| trace_context_make_registered_string_literal(trace_context, arg_name); |
| |
| trace_arg_value_t trace_arg_value; |
| switch (arg_type) { |
| case TRACE_VALUE_TYPE_BOOL: { |
| trace_arg_value = trace_make_bool_arg_value(!!arg_value); |
| break; |
| } |
| case TRACE_VALUE_TYPE_UINT: |
| trace_arg_value = trace_make_uint64_arg_value(arg_value); |
| break; |
| case TRACE_VALUE_TYPE_INT: |
| trace_arg_value = |
| trace_make_int64_arg_value(BitCast<int64_t>(arg_value)); |
| break; |
| case TRACE_VALUE_TYPE_DOUBLE: |
| trace_arg_value = |
| trace_make_double_arg_value(BitCast<double>(arg_value)); |
| break; |
| case TRACE_VALUE_TYPE_POINTER: |
| trace_arg_value = |
| trace_make_pointer_arg_value(BitCast<uintptr_t>(arg_value)); |
| break; |
| case TRACE_VALUE_TYPE_STRING: { |
| trace_string_ref_t arg_value_string_ref = |
| trace_context_make_registered_string_literal( |
| trace_context, reinterpret_cast<const char*>(arg_value)); |
| trace_arg_value = trace_make_string_arg_value(arg_value_string_ref); |
| break; |
| } |
| case TRACE_VALUE_TYPE_COPY_STRING: { |
| const char* arg_value_as_cstring = |
| reinterpret_cast<const char*>(arg_value); |
| trace_string_ref_t arg_value_string_ref = |
| trace_context_make_registered_string_copy( |
| trace_context, arg_value_as_cstring, |
| strlen(arg_value_as_cstring)); |
| trace_arg_value = trace_make_string_arg_value(arg_value_string_ref); |
| break; |
| } |
| case TRACE_VALUE_TYPE_CONVERTABLE: |
| trace_arg_value = trace_make_null_arg_value(); |
| break; |
| default: |
| trace_arg_value = trace_make_null_arg_value(); |
| } |
| |
| trace_args[i] = trace_make_arg(arg_name_string_ref, trace_arg_value); |
| } |
| |
| switch (phase) { |
| case TRACE_EVENT_PHASE_BEGIN: |
| case TRACE_EVENT_PHASE_COMPLETE: |
| trace_context_write_duration_begin_event_record( |
| trace_context, ticks, &thread_ref, &category_ref, &name_ref, |
| trace_args, num_trace_args); |
| break; |
| case TRACE_EVENT_PHASE_END: |
| trace_context_write_duration_end_event_record( |
| trace_context, ticks, &thread_ref, &category_ref, &name_ref, |
| trace_args, num_trace_args); |
| break; |
| case TRACE_EVENT_PHASE_INSTANT: |
| trace_context_write_instant_event_record( |
| trace_context, ticks, &thread_ref, &category_ref, &name_ref, |
| TRACE_SCOPE_THREAD, trace_args, num_trace_args); |
| break; |
| case TRACE_EVENT_PHASE_ASYNC_BEGIN: |
| trace_context_write_async_begin_event_record( |
| trace_context, ticks, &thread_ref, &category_ref, &name_ref, id, |
| trace_args, num_trace_args); |
| break; |
| case TRACE_EVENT_PHASE_ASYNC_END: |
| trace_context_write_async_end_event_record( |
| trace_context, ticks, &thread_ref, &category_ref, &name_ref, id, |
| trace_args, num_trace_args); |
| break; |
| default: |
| break; |
| } |
| |
| trace_release_context(trace_context); |
| |
| #else // defined(OS_FUCHSIA) |
| const char* devtoolsTag = nullptr; |
| if (shaders_category_flag_ && |
| category_enabled_flag == shaders_category_flag_) { |
| devtoolsTag = kShadersDevtoolsTag; |
| } |
| switch (phase) { |
| case TRACE_EVENT_PHASE_BEGIN: |
| case TRACE_EVENT_PHASE_COMPLETE: |
| if (devtoolsTag) { |
| fml::tracing::TraceEvent1(kSkiaTag, name, /*flow_id_count=*/0, |
| /*flow_ids=*/nullptr, kDevtoolsTagArg, |
| devtoolsTag); |
| } else { |
| fml::tracing::TraceEvent0(kSkiaTag, name, /*flow_id_count=*/0, |
| /*flow_ids=*/nullptr); |
| } |
| break; |
| case TRACE_EVENT_PHASE_END: |
| fml::tracing::TraceEventEnd(name); |
| break; |
| case TRACE_EVENT_PHASE_INSTANT: |
| if (devtoolsTag) { |
| fml::tracing::TraceEventInstant1(kSkiaTag, name, |
| /*flow_id_count=*/0, |
| /*flow_ids=*/nullptr, |
| kDevtoolsTagArg, devtoolsTag); |
| } else { |
| fml::tracing::TraceEventInstant0(kSkiaTag, name, |
| /*flow_id_count=*/0, |
| /*flow_ids=*/nullptr); |
| } |
| break; |
| case TRACE_EVENT_PHASE_ASYNC_BEGIN: |
| if (devtoolsTag) { |
| fml::tracing::TraceEventAsyncBegin1(kSkiaTag, name, id, |
| /*flow_id_count=*/0, |
| /*flow_ids=*/nullptr, |
| kDevtoolsTagArg, devtoolsTag); |
| } else { |
| fml::tracing::TraceEventAsyncBegin0(kSkiaTag, name, id, |
| /*flow_id_count=*/0, |
| /*flow_ids=*/nullptr); |
| } |
| break; |
| case TRACE_EVENT_PHASE_ASYNC_END: |
| if (devtoolsTag) { |
| fml::tracing::TraceEventAsyncEnd1(kSkiaTag, name, id, kDevtoolsTagArg, |
| devtoolsTag); |
| } else { |
| fml::tracing::TraceEventAsyncEnd0(kSkiaTag, name, id); |
| } |
| break; |
| default: |
| break; |
| } |
| #endif // defined(OS_FUCHSIA) |
| return 0; |
| } |
| |
| void updateTraceEventDuration(const uint8_t* category_enabled_flag, |
| const char* name, |
| SkEventTracer::Handle handle) override { |
| // This is only ever called from a scoped trace event so we will just end |
| // the section. |
| #if defined(OS_FUCHSIA) |
| TRACE_DURATION_END(kSkiaTag, name); |
| #else |
| fml::tracing::TraceEventEnd(name); |
| #endif |
| } |
| |
| const uint8_t* getCategoryGroupEnabled(const char* name) override { |
| // Skia will only use long-lived string literals as event names. |
| std::lock_guard<std::mutex> lock(flag_map_mutex_); |
| auto flag_it = category_flag_map_.find(name); |
| if (flag_it == category_flag_map_.end()) { |
| bool allowed; |
| if (enabled_) { |
| allowed = !allowlist_.has_value() || |
| allowlist_->find(name) != allowlist_->end(); |
| } else { |
| allowed = false; |
| } |
| flag_it = category_flag_map_.insert(std::make_pair(name, allowed)).first; |
| const uint8_t* flag = &flag_it->second; |
| reverse_flag_map_.insert(std::make_pair(flag, name)); |
| if (kShaderCategoryName == name) { |
| shaders_category_flag_ = flag; |
| } |
| } |
| return &flag_it->second; |
| } |
| |
| const char* getCategoryGroupName( |
| const uint8_t* category_enabled_flag) override { |
| std::lock_guard<std::mutex> lock(flag_map_mutex_); |
| auto reverse_it = reverse_flag_map_.find(category_enabled_flag); |
| if (reverse_it != reverse_flag_map_.end()) { |
| return reverse_it->second; |
| } else { |
| return kSkiaTag; |
| } |
| } |
| |
| private: |
| uint8_t enabled_; |
| std::optional<std::set<std::string>> allowlist_; |
| std::mutex flag_map_mutex_; |
| std::map<const char*, uint8_t> category_flag_map_; |
| std::map<const uint8_t*, const char*> reverse_flag_map_; |
| const uint8_t* shaders_category_flag_ = nullptr; |
| FML_DISALLOW_COPY_AND_ASSIGN(FlutterEventTracer); |
| }; |
| |
| void InitSkiaEventTracer( |
| bool enabled, |
| const std::optional<std::vector<std::string>>& allowlist) { |
| auto tracer = new FlutterEventTracer(enabled, allowlist); |
| // Initialize the binding to Skia's tracing events. Skia will |
| // take ownership of and clean up the memory allocated here. |
| SkEventTracer::SetInstance(tracer); |
| } |
| |
| } // namespace flutter |