/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "src/trace_processor/trace_processor_impl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "perfetto/base/logging.h" #include "perfetto/base/status.h" #include "perfetto/base/time.h" #include "perfetto/ext/base/flat_hash_map.h" #include "perfetto/ext/base/small_vector.h" #include "perfetto/ext/base/status_or.h" #include "perfetto/ext/base/string_splitter.h" #include "perfetto/ext/base/string_utils.h" #include "perfetto/protozero/scattered_heap_buffer.h" #include "perfetto/public/compiler.h" #include "perfetto/trace_processor/basic_types.h" #include "perfetto/trace_processor/iterator.h" #include "perfetto/trace_processor/trace_blob_view.h" #include "perfetto/trace_processor/trace_processor.h" #include "src/trace_processor/importers/android_bugreport/android_bugreport_parser.h" #include "src/trace_processor/importers/common/clock_tracker.h" #include "src/trace_processor/importers/common/metadata_tracker.h" #include "src/trace_processor/importers/common/trace_parser.h" #include "src/trace_processor/importers/fuchsia/fuchsia_trace_parser.h" #include "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h" #include "src/trace_processor/importers/gzip/gzip_trace_parser.h" #include "src/trace_processor/importers/json/json_trace_parser_impl.h" #include "src/trace_processor/importers/json/json_trace_tokenizer.h" #include "src/trace_processor/importers/json/json_utils.h" #include "src/trace_processor/importers/ninja/ninja_log_parser.h" #include "src/trace_processor/importers/perf/perf_data_tokenizer.h" #include "src/trace_processor/importers/perf/record_parser.h" #include "src/trace_processor/importers/proto/additional_modules.h" #include "src/trace_processor/importers/proto/content_analyzer.h" #include "src/trace_processor/importers/systrace/systrace_trace_parser.h" #include "src/trace_processor/importers/zip/zip_trace_reader.h" #include "src/trace_processor/iterator_impl.h" #include "src/trace_processor/metrics/all_chrome_metrics.descriptor.h" #include "src/trace_processor/metrics/all_webview_metrics.descriptor.h" #include "src/trace_processor/metrics/metrics.descriptor.h" #include "src/trace_processor/metrics/metrics.h" #include "src/trace_processor/metrics/sql/amalgamated_sql_metrics.h" #include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h" #include "src/trace_processor/perfetto_sql/engine/table_pointer_module.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/base64.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/clock_functions.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/create_function.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/create_view_function.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/dfs.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/dominator_tree.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/import.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/layout_functions.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/math.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/pprof_functions.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/sqlite3_str_split.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/stack_functions.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/structural_tree_partition.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/to_ftrace.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/utils.h" #include "src/trace_processor/perfetto_sql/intrinsics/functions/window_functions.h" #include "src/trace_processor/perfetto_sql/intrinsics/operators/counter_mipmap_operator.h" #include "src/trace_processor/perfetto_sql/intrinsics/operators/interval_intersect_operator.h" #include "src/trace_processor/perfetto_sql/intrinsics/operators/slice_mipmap_operator.h" #include "src/trace_processor/perfetto_sql/intrinsics/operators/span_join_operator.h" #include "src/trace_processor/perfetto_sql/intrinsics/operators/window_operator.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/ancestor.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/connected_flow.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/descendant.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/dfs_weight_bounded.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_annotated_stack.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_counter_dur.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flamegraph.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flat_slice.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h" #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h" #include "src/trace_processor/perfetto_sql/prelude/tables_views.h" #include "src/trace_processor/perfetto_sql/stdlib/stdlib.h" #include "src/trace_processor/sqlite/bindings/sqlite_aggregate_function.h" #include "src/trace_processor/sqlite/bindings/sqlite_result.h" #include "src/trace_processor/sqlite/scoped_db.h" #include "src/trace_processor/sqlite/sql_source.h" #include "src/trace_processor/sqlite/sql_stats_table.h" #include "src/trace_processor/sqlite/stats_table.h" #include "src/trace_processor/storage/metadata.h" #include "src/trace_processor/storage/trace_storage.h" #include "src/trace_processor/tp_metatrace.h" #include "src/trace_processor/trace_processor_storage_impl.h" #include "src/trace_processor/trace_reader_registry.h" #include "src/trace_processor/types/trace_processor_context.h" #include "src/trace_processor/types/variadic.h" #include "src/trace_processor/util/descriptors.h" #include "src/trace_processor/util/gzip_utils.h" #include "src/trace_processor/util/protozero_to_json.h" #include "src/trace_processor/util/protozero_to_text.h" #include "src/trace_processor/util/regex.h" #include "src/trace_processor/util/sql_modules.h" #include "src/trace_processor/util/status_macros.h" #include "src/trace_processor/util/trace_type.h" #include "protos/perfetto/common/builtin_clock.pbzero.h" #include "protos/perfetto/trace/clock_snapshot.pbzero.h" #include "protos/perfetto/trace/perfetto/perfetto_metatrace.pbzero.h" #include "protos/perfetto/trace/trace.pbzero.h" #include "protos/perfetto/trace/trace_packet.pbzero.h" #include "protos/perfetto/trace_processor/metatrace_categories.pbzero.h" namespace perfetto::trace_processor { namespace { template void RegisterFunction(PerfettoSqlEngine* engine, const char* name, int argc, Ptr context = nullptr, bool deterministic = true) { auto status = engine->RegisterStaticFunction( name, argc, std::move(context), deterministic); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } void RegisterAllProtoBuilderFunctions(DescriptorPool* pool, PerfettoSqlEngine* engine, TraceProcessor* tp) { for (uint32_t i = 0; i < pool->descriptors().size(); ++i) { // Convert the full name (e.g. .perfetto.protos.TraceMetrics.SubMetric) // into a function name of the form (TraceMetrics_SubMetric). const auto& desc = pool->descriptors()[i]; auto fn_name = desc.full_name().substr(desc.package_name().size() + 1); std::replace(fn_name.begin(), fn_name.end(), '.', '_'); RegisterFunction( engine, fn_name.c_str(), -1, std::make_unique( metrics::BuildProto::Context{tp, pool, i})); } } void BuildBoundsTable(sqlite3* db, std::pair bounds) { char* error = nullptr; sqlite3_exec(db, "DELETE FROM trace_bounds", nullptr, nullptr, &error); if (error) { PERFETTO_ELOG("Error deleting from bounds table: %s", error); sqlite3_free(error); return; } base::StackString<1024> sql("INSERT INTO trace_bounds VALUES(%" PRId64 ", %" PRId64 ")", bounds.first, bounds.second); sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &error); if (error) { PERFETTO_ELOG("Error inserting bounds table: %s", error); sqlite3_free(error); } } class ValueAtMaxTs : public SqliteAggregateFunction { public: static constexpr char kName[] = "VALUE_AT_MAX_TS"; static constexpr int kArgCount = 2; struct Context { bool initialized; int value_type; int64_t max_ts; int64_t int_value_at_max_ts; double double_value_at_max_ts; }; static void Step(sqlite3_context* ctx, int, sqlite3_value** argv) { sqlite3_value* ts = argv[0]; sqlite3_value* value = argv[1]; // Note that sqlite3_aggregate_context zeros the memory for us so all the // variables of the struct should be zero. auto* fn_ctx = reinterpret_cast( sqlite3_aggregate_context(ctx, sizeof(Context))); // For performance reasons, we only do the check for the type of ts and // value on the first call of the function. if (PERFETTO_UNLIKELY(!fn_ctx->initialized)) { if (sqlite3_value_type(ts) != SQLITE_INTEGER) { return sqlite::result::Error( ctx, "VALUE_AT_MAX_TS: ts passed was not an integer"); } fn_ctx->value_type = sqlite3_value_type(value); if (fn_ctx->value_type != SQLITE_INTEGER && fn_ctx->value_type != SQLITE_FLOAT) { return sqlite::result::Error( ctx, "VALUE_AT_MAX_TS: value passed was not an integer or float"); } fn_ctx->max_ts = std::numeric_limits::min(); fn_ctx->initialized = true; } // On dcheck builds however, we check every passed ts and value. #if PERFETTO_DCHECK_IS_ON() if (sqlite3_value_type(ts) != SQLITE_INTEGER) { return sqlite::result::Error( ctx, "VALUE_AT_MAX_TS: ts passed was not an integer"); } if (sqlite3_value_type(value) != fn_ctx->value_type) { return sqlite::result::Error( ctx, "VALUE_AT_MAX_TS: value type is inconsistent"); } #endif int64_t ts_int = sqlite3_value_int64(ts); if (PERFETTO_LIKELY(fn_ctx->max_ts <= ts_int)) { fn_ctx->max_ts = ts_int; if (fn_ctx->value_type == SQLITE_INTEGER) { fn_ctx->int_value_at_max_ts = sqlite3_value_int64(value); } else { fn_ctx->double_value_at_max_ts = sqlite3_value_double(value); } } } static void Final(sqlite3_context* ctx) { auto* fn_ctx = static_cast(sqlite3_aggregate_context(ctx, 0)); if (!fn_ctx) { sqlite::result::Null(ctx); return; } if (fn_ctx->value_type == SQLITE_INTEGER) { sqlite::result::Long(ctx, fn_ctx->int_value_at_max_ts); } else { sqlite::result::Double(ctx, fn_ctx->double_value_at_max_ts); } } }; void RegisterValueAtMaxTsFunction(PerfettoSqlEngine& engine) { base::Status status = engine.RegisterSqliteAggregateFunction(nullptr); if (!status.ok()) { PERFETTO_ELOG("Error initializing VALUE_AT_MAX_TS"); } } std::vector SanitizeMetricMountPaths( const std::vector& mount_paths) { std::vector sanitized; for (const auto& path : mount_paths) { if (path.empty()) continue; sanitized.push_back(path); if (path.back() != '/') sanitized.back().append("/"); } return sanitized; } void InsertIntoTraceMetricsTable(sqlite3* db, const std::string& metric_name) { char* insert_sql = sqlite3_mprintf( "INSERT INTO trace_metrics(name) VALUES('%q')", metric_name.c_str()); char* insert_error = nullptr; sqlite3_exec(db, insert_sql, nullptr, nullptr, &insert_error); sqlite3_free(insert_sql); if (insert_error) { PERFETTO_ELOG("Error registering table: %s", insert_error); sqlite3_free(insert_error); } } const char* TraceTypeToString(TraceType trace_type) { switch (trace_type) { case kUnknownTraceType: return "unknown"; case kProtoTraceType: return "proto"; case kJsonTraceType: return "json"; case kFuchsiaTraceType: return "fuchsia"; case kSystraceTraceType: return "systrace"; case kGzipTraceType: return "gzip"; case kCtraceTraceType: return "ctrace"; case kNinjaLogTraceType: return "ninja_log"; case kZipFile: return "zip"; case kPerfDataTraceType: return "perf_data"; } PERFETTO_FATAL("For GCC"); } sql_modules::NameToModule GetStdlibModules() { sql_modules::NameToModule modules; for (const auto& file_to_sql : stdlib::kFileToSql) { std::string import_key = sql_modules::GetIncludeKey(file_to_sql.path); std::string module = sql_modules::GetModuleName(import_key); modules.Insert(module, {}).first->push_back({import_key, file_to_sql.sql}); } return modules; } void InitializePreludeTablesViews(sqlite3* db) { for (const auto& file_to_sql : prelude::tables_views::kFileToSql) { char* errmsg_raw = nullptr; int err = sqlite3_exec(db, file_to_sql.sql, nullptr, nullptr, &errmsg_raw); ScopedSqliteString errmsg(errmsg_raw); if (err != SQLITE_OK) { PERFETTO_FATAL("Failed to initialize prelude %s", errmsg_raw); } } } } // namespace TraceProcessorImpl::TraceProcessorImpl(const Config& cfg) : TraceProcessorStorageImpl(cfg), config_(cfg) { context_.reader_registry->RegisterTraceReader( kFuchsiaTraceType); context_.fuchsia_record_parser = std::make_unique(&context_); context_.reader_registry->RegisterTraceReader( kSystraceTraceType); context_.reader_registry->RegisterTraceReader( kNinjaLogTraceType); context_.reader_registry ->RegisterTraceReader( kPerfDataTraceType); context_.perf_record_parser = std::make_unique(&context_); if (util::IsGzipSupported()) { context_.reader_registry->RegisterTraceReader( kGzipTraceType); context_.reader_registry->RegisterTraceReader( kCtraceTraceType); context_.reader_registry->RegisterTraceReader(kZipFile); } if (json::IsJsonSupported()) { context_.reader_registry->RegisterTraceReader( kJsonTraceType); context_.json_trace_parser = std::make_unique(&context_); } if (context_.config.analyze_trace_proto_content) { context_.content_analyzer = std::make_unique(&context_); } // Add metrics to descriptor pool const std::vector sanitized_extension_paths = SanitizeMetricMountPaths(config_.skip_builtin_metric_paths); std::vector skip_prefixes; skip_prefixes.reserve(sanitized_extension_paths.size()); for (const auto& path : sanitized_extension_paths) { skip_prefixes.push_back(kMetricProtoRoot + path); } pool_.AddFromFileDescriptorSet(kMetricsDescriptor.data(), kMetricsDescriptor.size(), skip_prefixes); pool_.AddFromFileDescriptorSet(kAllChromeMetricsDescriptor.data(), kAllChromeMetricsDescriptor.size(), skip_prefixes); pool_.AddFromFileDescriptorSet(kAllWebviewMetricsDescriptor.data(), kAllWebviewMetricsDescriptor.size(), skip_prefixes); RegisterAdditionalModules(&context_); InitPerfettoSqlEngine(); sqlite_objects_post_constructor_initialization_ = engine_->SqliteRegisteredObjectCount(); bool skip_all_sql = std::find(config_.skip_builtin_metric_paths.begin(), config_.skip_builtin_metric_paths.end(), "") != config_.skip_builtin_metric_paths.end(); if (!skip_all_sql) { for (const auto& file_to_sql : sql_metrics::kFileToSql) { if (base::StartsWithAny(file_to_sql.path, sanitized_extension_paths)) continue; RegisterMetric(file_to_sql.path, file_to_sql.sql); } } } TraceProcessorImpl::~TraceProcessorImpl() = default; base::Status TraceProcessorImpl::Parse(TraceBlobView blob) { bytes_parsed_ += blob.size(); return TraceProcessorStorageImpl::Parse(std::move(blob)); } std::string TraceProcessorImpl::GetCurrentTraceName() { if (current_trace_name_.empty()) return ""; auto size = " (" + std::to_string(bytes_parsed_ / 1024 / 1024) + " MB)"; return current_trace_name_ + size; } void TraceProcessorImpl::SetCurrentTraceName(const std::string& name) { current_trace_name_ = name; } void TraceProcessorImpl::Flush() { TraceProcessorStorageImpl::Flush(); context_.metadata_tracker->SetMetadata( metadata::trace_size_bytes, Variadic::Integer(static_cast(bytes_parsed_))); const StringId trace_type_id = context_.storage->InternString(TraceTypeToString(context_.trace_type)); context_.metadata_tracker->SetMetadata(metadata::trace_type, Variadic::String(trace_type_id)); BuildBoundsTable(engine_->sqlite_engine()->db(), context_.storage->GetTraceTimestampBoundsNs()); } void TraceProcessorImpl::NotifyEndOfFile() { if (notify_eof_called_) { PERFETTO_ELOG( "NotifyEndOfFile should only be called once. Try calling Flush instead " "if trying to commit the contents of the trace to tables."); return; } notify_eof_called_ = true; if (current_trace_name_.empty()) current_trace_name_ = "Unnamed trace"; // Last opportunity to flush all pending data. Flush(); TraceProcessorStorageImpl::NotifyEndOfFile(); context_.storage->ShrinkToFitTables(); // Rebuild the bounds table once everything has been completed: we do this // so that if any data was added to tables in // TraceProcessorStorageImpl::NotifyEndOfFile, this will be counted in // trace bounds: this is important for parsers like ninja which wait until // the end to flush all their data. BuildBoundsTable(engine_->sqlite_engine()->db(), context_.storage->GetTraceTimestampBoundsNs()); TraceProcessorStorageImpl::DestroyContext(); } size_t TraceProcessorImpl::RestoreInitialTables() { // We should always have at least as many objects now as we did in the // constructor. uint64_t registered_count_before = engine_->SqliteRegisteredObjectCount(); PERFETTO_CHECK(registered_count_before >= sqlite_objects_post_constructor_initialization_); InitPerfettoSqlEngine(); // The registered count should now be the same as it was in the constructor. uint64_t registered_count_after = engine_->SqliteRegisteredObjectCount(); PERFETTO_CHECK(registered_count_after == sqlite_objects_post_constructor_initialization_); return static_cast(registered_count_before - registered_count_after); } Iterator TraceProcessorImpl::ExecuteQuery(const std::string& sql) { PERFETTO_TP_TRACE(metatrace::Category::API_TIMELINE, "EXECUTE_QUERY"); uint32_t sql_stats_row = context_.storage->mutable_sql_stats()->RecordQueryBegin( sql, base::GetWallTimeNs().count()); std::string non_breaking_sql = base::ReplaceAll(sql, "\u00A0", " "); base::StatusOr result = engine_->ExecuteUntilLastStatement( SqlSource::FromExecuteQuery(std::move(non_breaking_sql))); std::unique_ptr impl( new IteratorImpl(this, std::move(result), sql_stats_row)); return Iterator(std::move(impl)); } void TraceProcessorImpl::InterruptQuery() { if (!engine_->sqlite_engine()->db()) return; query_interrupted_.store(true); sqlite3_interrupt(engine_->sqlite_engine()->db()); } bool TraceProcessorImpl::IsRootMetricField(const std::string& metric_name) { std::optional desc_idx = pool_.FindDescriptorIdx(".perfetto.protos.TraceMetrics"); if (!desc_idx.has_value()) return false; const auto* field_idx = pool_.descriptors()[*desc_idx].FindFieldByName(metric_name); return field_idx != nullptr; } base::Status TraceProcessorImpl::RegisterSqlModule(SqlModule sql_module) { sql_modules::RegisteredModule new_module; std::string name = sql_module.name; if (engine_->FindModule(name) && !sql_module.allow_module_override) { return base::ErrStatus( "Module '%s' is already registered. Choose a different name.\n" "If you want to replace the existing module using trace processor " "shell, you need to pass the --dev flag and use " "--override-sql-module " "to pass the module path.", name.c_str()); } for (auto const& name_and_sql : sql_module.files) { if (sql_modules::GetModuleName(name_and_sql.first) != name) { return base::ErrStatus( "File import key doesn't match the module name. First part of " "import " "key should be module name. Import key: %s, module name: %s.", name_and_sql.first.c_str(), name.c_str()); } new_module.include_key_to_file.Insert(name_and_sql.first, {name_and_sql.second, false}); } engine_->RegisterModule(name, std::move(new_module)); return base::OkStatus(); } base::Status TraceProcessorImpl::RegisterMetric(const std::string& path, const std::string& sql) { // Check if the metric with the given path already exists and if it does, // just update the SQL associated with it. auto it = std::find_if( sql_metrics_.begin(), sql_metrics_.end(), [&path](const metrics::SqlMetricFile& m) { return m.path == path; }); if (it != sql_metrics_.end()) { it->sql = sql; return base::OkStatus(); } auto sep_idx = path.rfind('/'); std::string basename = sep_idx == std::string::npos ? path : path.substr(sep_idx + 1); auto sql_idx = basename.rfind(".sql"); if (sql_idx == std::string::npos) { return base::ErrStatus("Unable to find .sql extension for metric"); } auto no_ext_name = basename.substr(0, sql_idx); metrics::SqlMetricFile metric; metric.path = path; metric.sql = sql; if (IsRootMetricField(no_ext_name)) { metric.proto_field_name = no_ext_name; metric.output_table_name = no_ext_name + "_output"; auto field_it_and_inserted = proto_field_to_sql_metric_path_.emplace(*metric.proto_field_name, path); if (!field_it_and_inserted.second) { // We already had a metric with this field name in the map. However, if // this was the case, we should have found the metric in // |path_to_sql_metric_file_| above if we are simply overriding the // metric. Return an error since this means we have two different SQL // files which are trying to output the same metric. const auto& prev_path = field_it_and_inserted.first->second; PERFETTO_DCHECK(prev_path != path); return base::ErrStatus( "RegisterMetric Error: Metric paths %s (which is already " "registered) " "and %s are both trying to output the proto field %s", prev_path.c_str(), path.c_str(), metric.proto_field_name->c_str()); } } if (metric.proto_field_name) { InsertIntoTraceMetricsTable(engine_->sqlite_engine()->db(), *metric.proto_field_name); } sql_metrics_.emplace_back(metric); return base::OkStatus(); } base::Status TraceProcessorImpl::ExtendMetricsProto(const uint8_t* data, size_t size) { return ExtendMetricsProto(data, size, /*skip_prefixes*/ {}); } base::Status TraceProcessorImpl::ExtendMetricsProto( const uint8_t* data, size_t size, const std::vector& skip_prefixes) { RETURN_IF_ERROR(pool_.AddFromFileDescriptorSet(data, size, skip_prefixes)); RegisterAllProtoBuilderFunctions(&pool_, engine_.get(), this); return base::OkStatus(); } base::Status TraceProcessorImpl::ComputeMetric( const std::vector& metric_names, std::vector* metrics_proto) { auto opt_idx = pool_.FindDescriptorIdx(".perfetto.protos.TraceMetrics"); if (!opt_idx.has_value()) return base::Status("Root metrics proto descriptor not found"); const auto& root_descriptor = pool_.descriptors()[opt_idx.value()]; return metrics::ComputeMetrics(engine_.get(), metric_names, sql_metrics_, pool_, root_descriptor, metrics_proto); } base::Status TraceProcessorImpl::ComputeMetricText( const std::vector& metric_names, TraceProcessor::MetricResultFormat format, std::string* metrics_string) { std::vector metrics_proto; base::Status status = ComputeMetric(metric_names, &metrics_proto); if (!status.ok()) return status; switch (format) { case TraceProcessor::MetricResultFormat::kProtoText: *metrics_string = protozero_to_text::ProtozeroToText( pool_, ".perfetto.protos.TraceMetrics", protozero::ConstBytes{metrics_proto.data(), metrics_proto.size()}, protozero_to_text::kIncludeNewLines); break; case TraceProcessor::MetricResultFormat::kJson: *metrics_string = protozero_to_json::ProtozeroToJson( pool_, ".perfetto.protos.TraceMetrics", protozero::ConstBytes{metrics_proto.data(), metrics_proto.size()}, protozero_to_json::kPretty | protozero_to_json::kInlineErrors | protozero_to_json::kInlineAnnotations); break; } return status; } std::vector TraceProcessorImpl::GetMetricDescriptors() { return pool_.SerializeAsDescriptorSet(); } void TraceProcessorImpl::EnableMetatrace(MetatraceConfig config) { metatrace::Enable(config); } void TraceProcessorImpl::InitPerfettoSqlEngine() { engine_.reset(new PerfettoSqlEngine(context_.storage->mutable_string_pool())); sqlite3* db = engine_->sqlite_engine()->db(); sqlite3_str_split_init(db); // Register SQL functions only used in local development instances. if (config_.enable_dev_features) { RegisterFunction(engine_.get(), "WRITE_FILE", 2); } RegisterFunction(engine_.get(), "glob", 2); RegisterFunction(engine_.get(), "HASH", -1); RegisterFunction(engine_.get(), "BASE64_ENCODE", 1); RegisterFunction(engine_.get(), "DEMANGLE", 1); RegisterFunction(engine_.get(), "SOURCE_GEQ", -1); RegisterFunction(engine_.get(), "__intrinsic_table_ptr_bind", -1); RegisterFunction(engine_.get(), "EXPORT_JSON", 1, context_.storage.get(), false); RegisterFunction(engine_.get(), "EXTRACT_ARG", 2, context_.storage.get()); RegisterFunction(engine_.get(), "ABS_TIME_STR", 1, context_.clock_converter.get()); RegisterFunction(engine_.get(), "REVERSE", 1); RegisterFunction(engine_.get(), "TO_MONOTONIC", 1, context_.clock_converter.get()); RegisterFunction(engine_.get(), "TO_REALTIME", 1, context_.clock_converter.get()); RegisterFunction(engine_.get(), "TO_TIMECODE", 1); RegisterFunction(engine_.get(), "CREATE_FUNCTION", 3, engine_.get()); RegisterFunction(engine_.get(), "CREATE_VIEW_FUNCTION", 3, engine_.get()); RegisterFunction(engine_.get(), "EXPERIMENTAL_MEMOIZE", 1, engine_.get()); RegisterFunction( engine_.get(), "IMPORT", 1, std::make_unique(Import::Context{engine_.get()})); RegisterFunction( engine_.get(), "TO_FTRACE", 1, std::make_unique(ToFtrace::Context{ context_.storage.get(), SystraceSerializer(&context_)})); if constexpr (regex::IsRegexSupported()) { RegisterFunction(engine_.get(), "regexp", 2); } // Old style function registration. // TODO(lalitm): migrate this over to using RegisterFunction once aggregate // functions are supported. RegisterValueAtMaxTsFunction(*engine_); { base::Status status = RegisterLastNonNullFunction(*engine_); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } { base::Status status = RegisterStackFunctions(engine_.get(), &context_); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } { base::Status status = PprofFunctions::Register(*engine_, &context_); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } { base::Status status = RegisterLayoutFunctions(*engine_); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } { base::Status status = RegisterMathFunctions(*engine_); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } { base::Status status = RegisterBase64Functions(*engine_); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } TraceStorage* storage = context_.storage.get(); // Operator tables. engine_->sqlite_engine()->RegisterVirtualTableModule( "span_join", std::make_unique(engine_.get())); engine_->sqlite_engine()->RegisterVirtualTableModule( "span_left_join", std::make_unique(engine_.get())); engine_->sqlite_engine()->RegisterVirtualTableModule( "span_outer_join", std::make_unique(engine_.get())); engine_->sqlite_engine()->RegisterVirtualTableModule( "window", std::make_unique()); engine_->sqlite_engine()->RegisterVirtualTableModule( "__intrinsic_counter_mipmap", std::make_unique(engine_.get())); engine_->sqlite_engine()->RegisterVirtualTableModule( "__intrinsic_slice_mipmap", std::make_unique(engine_.get())); engine_->sqlite_engine() ->RegisterVirtualTableModule( "__intrinsic_ii_with_interval_tree", std::make_unique(engine_.get())); // Initalize the tables and views in the prelude. InitializePreludeTablesViews(db); // Register stdlib modules. auto stdlib_modules = GetStdlibModules(); for (auto module_it = stdlib_modules.GetIterator(); module_it; ++module_it) { base::Status status = RegisterSqlModule({module_it.key(), module_it.value(), false}); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } // Register metrics functions. { base::Status status = engine_->RegisterSqliteAggregateFunction( nullptr); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } RegisterFunction(engine_.get(), "NULL_IF_EMPTY", 1); RegisterFunction(engine_.get(), "UNWRAP_METRIC_PROTO", 2); RegisterFunction( engine_.get(), "RUN_METRIC", -1, std::make_unique( metrics::RunMetric::Context{engine_.get(), &sql_metrics_})); // Legacy tables. engine_->sqlite_engine()->RegisterVirtualTableModule( "sqlstats", storage); engine_->sqlite_engine()->RegisterVirtualTableModule("stats", storage); engine_->sqlite_engine()->RegisterVirtualTableModule( "__intrinsic_table_ptr", nullptr); // New style db-backed tables. // Note: if adding a table here which might potentially contain many rows // (O(rows in sched/slice/counter)), then consider calling ShrinkToFit on // that table in TraceStorage::ShrinkToFitTables. RegisterStaticTable(storage->machine_table()); RegisterStaticTable(storage->arg_table()); RegisterStaticTable(storage->raw_table()); RegisterStaticTable(storage->ftrace_event_table()); RegisterStaticTable(storage->thread_table()); RegisterStaticTable(storage->process_table()); RegisterStaticTable(storage->filedescriptor_table()); RegisterStaticTable(storage->slice_table()); RegisterStaticTable(storage->flow_table()); RegisterStaticTable(storage->sched_slice_table()); RegisterStaticTable(storage->spurious_sched_wakeup_table()); RegisterStaticTable(storage->thread_state_table()); RegisterStaticTable(storage->gpu_slice_table()); RegisterStaticTable(storage->track_table()); RegisterStaticTable(storage->thread_track_table()); RegisterStaticTable(storage->process_track_table()); RegisterStaticTable(storage->cpu_track_table()); RegisterStaticTable(storage->gpu_track_table()); RegisterStaticTable(storage->uid_track_table()); RegisterStaticTable(storage->gpu_work_period_track_table()); RegisterStaticTable(storage->counter_table()); RegisterStaticTable(storage->counter_track_table()); RegisterStaticTable(storage->process_counter_track_table()); RegisterStaticTable(storage->thread_counter_track_table()); RegisterStaticTable(storage->cpu_counter_track_table()); RegisterStaticTable(storage->irq_counter_track_table()); RegisterStaticTable(storage->softirq_counter_track_table()); RegisterStaticTable(storage->gpu_counter_track_table()); RegisterStaticTable(storage->gpu_counter_group_table()); RegisterStaticTable(storage->perf_counter_track_table()); RegisterStaticTable(storage->energy_counter_track_table()); RegisterStaticTable(storage->linux_device_track_table()); RegisterStaticTable(storage->uid_counter_track_table()); RegisterStaticTable(storage->energy_per_uid_counter_track_table()); RegisterStaticTable(storage->heap_graph_object_table()); RegisterStaticTable(storage->heap_graph_reference_table()); RegisterStaticTable(storage->heap_graph_class_table()); RegisterStaticTable(storage->symbol_table()); RegisterStaticTable(storage->heap_profile_allocation_table()); RegisterStaticTable(storage->cpu_profile_stack_sample_table()); RegisterStaticTable(storage->perf_session_table()); RegisterStaticTable(storage->perf_sample_table()); RegisterStaticTable(storage->stack_profile_callsite_table()); RegisterStaticTable(storage->stack_profile_mapping_table()); RegisterStaticTable(storage->stack_profile_frame_table()); RegisterStaticTable(storage->package_list_table()); RegisterStaticTable(storage->profiler_smaps_table()); RegisterStaticTable(storage->android_log_table()); RegisterStaticTable(storage->android_dumpstate_table()); RegisterStaticTable(storage->android_game_intervention_list_table()); RegisterStaticTable(storage->android_key_events_table()); RegisterStaticTable(storage->android_motion_events_table()); RegisterStaticTable(storage->android_input_event_dispatch_table()); RegisterStaticTable(storage->vulkan_memory_allocations_table()); RegisterStaticTable(storage->graphics_frame_slice_table()); RegisterStaticTable(storage->expected_frame_timeline_slice_table()); RegisterStaticTable(storage->actual_frame_timeline_slice_table()); RegisterStaticTable(storage->android_network_packets_table()); RegisterStaticTable(storage->v8_isolate_table()); RegisterStaticTable(storage->v8_js_script_table()); RegisterStaticTable(storage->v8_wasm_script_table()); RegisterStaticTable(storage->v8_js_function_table()); RegisterStaticTable(storage->v8_js_code_table()); RegisterStaticTable(storage->v8_internal_code_table()); RegisterStaticTable(storage->v8_wasm_code_table()); RegisterStaticTable(storage->v8_regexp_code_table()); RegisterStaticTable(storage->jit_code_table()); RegisterStaticTable(storage->jit_frame_table()); RegisterStaticTable(storage->inputmethod_clients_table()); RegisterStaticTable(storage->inputmethod_manager_service_table()); RegisterStaticTable(storage->inputmethod_service_table()); RegisterStaticTable(storage->surfaceflinger_layers_snapshot_table()); RegisterStaticTable(storage->surfaceflinger_layer_table()); RegisterStaticTable(storage->surfaceflinger_transactions_table()); RegisterStaticTable(storage->viewcapture_table()); RegisterStaticTable(storage->window_manager_shell_transitions_table()); RegisterStaticTable( storage->window_manager_shell_transition_handlers_table()); RegisterStaticTable(storage->protolog_table()); RegisterStaticTable(storage->metadata_table()); RegisterStaticTable(storage->cpu_table()); RegisterStaticTable(storage->cpu_freq_table()); RegisterStaticTable(storage->clock_snapshot_table()); RegisterStaticTable(storage->memory_snapshot_table()); RegisterStaticTable(storage->process_memory_snapshot_table()); RegisterStaticTable(storage->memory_snapshot_node_table()); RegisterStaticTable(storage->memory_snapshot_edge_table()); RegisterStaticTable(storage->experimental_proto_path_table()); RegisterStaticTable(storage->experimental_proto_content_table()); RegisterStaticTable(storage->experimental_missing_chrome_processes_table()); // Tables dynamically generated at query time. engine_->RegisterStaticTableFunction( std::make_unique(&context_)); engine_->RegisterStaticTableFunction( std::make_unique(storage->counter_table())); engine_->RegisterStaticTableFunction( std::make_unique( context_.storage->mutable_string_pool(), &storage->slice_table())); engine_->RegisterStaticTableFunction(std::make_unique( context_.storage->mutable_string_pool(), engine_.get())); engine_->RegisterStaticTableFunction(std::make_unique( Ancestor::Type::kSlice, context_.storage.get())); engine_->RegisterStaticTableFunction(std::make_unique( Ancestor::Type::kStackProfileCallsite, context_.storage.get())); engine_->RegisterStaticTableFunction(std::make_unique( Ancestor::Type::kSliceByStack, context_.storage.get())); engine_->RegisterStaticTableFunction(std::make_unique( Descendant::Type::kSlice, context_.storage.get())); engine_->RegisterStaticTableFunction(std::make_unique( Descendant::Type::kSliceByStack, context_.storage.get())); engine_->RegisterStaticTableFunction(std::make_unique( ConnectedFlow::Mode::kDirectlyConnectedFlow, context_.storage.get())); engine_->RegisterStaticTableFunction(std::make_unique( ConnectedFlow::Mode::kPrecedingFlow, context_.storage.get())); engine_->RegisterStaticTableFunction(std::make_unique( ConnectedFlow::Mode::kFollowingFlow, context_.storage.get())); engine_->RegisterStaticTableFunction(std::make_unique( storage->sched_slice_table(), storage->thread_table())); engine_->RegisterStaticTableFunction( std::make_unique(&context_)); engine_->RegisterStaticTableFunction( std::make_unique(&context_)); engine_->RegisterStaticTableFunction(std::make_unique( context_.storage->mutable_string_pool())); engine_->RegisterStaticTableFunction(std::make_unique( context_.storage->mutable_string_pool())); // Value table aggregate functions. engine_->RegisterSqliteAggregateFunction( context_.storage->mutable_string_pool()); engine_->RegisterSqliteAggregateFunction( context_.storage->mutable_string_pool()); engine_->RegisterSqliteAggregateFunction( context_.storage->mutable_string_pool()); // Metrics. RegisterAllProtoBuilderFunctions(&pool_, engine_.get(), this); for (const auto& metric : sql_metrics_) { if (metric.proto_field_name) { InsertIntoTraceMetricsTable(db, *metric.proto_field_name); } } // Import prelude module. { auto result = engine_->Execute(SqlSource::FromTraceProcessorImplementation( "INCLUDE PERFETTO MODULE prelude.*")); if (!result.status().ok()) { PERFETTO_FATAL("Failed to import prelude: %s", result.status().c_message()); } } // Fill trace bounds table. BuildBoundsTable(db, context_.storage->GetTraceTimestampBoundsNs()); } namespace { class StringInterner { public: StringInterner(protos::pbzero::PerfettoMetatrace& event, base::FlatHashMap& interned_strings) : event_(event), interned_strings_(interned_strings) {} ~StringInterner() { for (const auto& interned_string : new_interned_strings_) { auto* interned_string_proto = event_.add_interned_strings(); interned_string_proto->set_iid(interned_string.first); interned_string_proto->set_value(interned_string.second); } } uint64_t InternString(const std::string& str) { uint64_t new_iid = interned_strings_.size(); auto insert_result = interned_strings_.Insert(str, new_iid); if (insert_result.second) { new_interned_strings_.emplace_back(new_iid, str); } return *insert_result.first; } private: protos::pbzero::PerfettoMetatrace& event_; base::FlatHashMap& interned_strings_; base::SmallVector, 16> new_interned_strings_; }; } // namespace base::Status TraceProcessorImpl::DisableAndReadMetatrace( std::vector* trace_proto) { protozero::HeapBuffered trace; { uint64_t realtime_timestamp = static_cast( std::chrono::system_clock::now().time_since_epoch() / std::chrono::nanoseconds(1)); uint64_t boottime_timestamp = metatrace::TraceTimeNowNs(); auto* clock_snapshot = trace->add_packet()->set_clock_snapshot(); { auto* realtime_clock = clock_snapshot->add_clocks(); realtime_clock->set_clock_id( protos::pbzero::BuiltinClock::BUILTIN_CLOCK_REALTIME); realtime_clock->set_timestamp(realtime_timestamp); } { auto* boottime_clock = clock_snapshot->add_clocks(); boottime_clock->set_clock_id( protos::pbzero::BuiltinClock::BUILTIN_CLOCK_BOOTTIME); boottime_clock->set_timestamp(boottime_timestamp); } } base::FlatHashMap interned_strings; metatrace::DisableAndReadBuffer([&trace, &interned_strings]( metatrace::Record* record) { auto* packet = trace->add_packet(); packet->set_timestamp(record->timestamp_ns); auto* evt = packet->set_perfetto_metatrace(); StringInterner interner(*evt, interned_strings); evt->set_event_name_iid(interner.InternString(record->event_name)); evt->set_event_duration_ns(record->duration_ns); evt->set_thread_id(1); // Not really important, just required for the ui. if (record->args_buffer_size == 0) return; base::StringSplitter s( record->args_buffer, record->args_buffer_size, '\0', base::StringSplitter::EmptyTokenMode::ALLOW_EMPTY_TOKENS); for (; s.Next();) { auto* arg_proto = evt->add_args(); arg_proto->set_key_iid(interner.InternString(s.cur_token())); bool has_next = s.Next(); PERFETTO_CHECK(has_next); arg_proto->set_value_iid(interner.InternString(s.cur_token())); } }); *trace_proto = trace.SerializeAsArray(); return base::OkStatus(); } } // namespace perfetto::trace_processor