diff --git a/Android.bp b/Android.bp index b68991908c..d0c7a89d93 100644 --- a/Android.bp +++ b/Android.bp @@ -13249,6 +13249,7 @@ filegroup { name: "perfetto_src_trace_processor_perfetto_sql_intrinsics_functions_functions", srcs: [ "src/trace_processor/perfetto_sql/intrinsics/functions/base64.cc", + "src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.cc", "src/trace_processor/perfetto_sql/intrinsics/functions/create_function.cc", "src/trace_processor/perfetto_sql/intrinsics/functions/create_view_function.cc", "src/trace_processor/perfetto_sql/intrinsics/functions/dominator_tree.cc", diff --git a/BUILD b/BUILD index bee4ad433e..905fe462f8 100644 --- a/BUILD +++ b/BUILD @@ -2438,6 +2438,8 @@ perfetto_filegroup( "src/trace_processor/perfetto_sql/intrinsics/functions/base64.cc", "src/trace_processor/perfetto_sql/intrinsics/functions/base64.h", "src/trace_processor/perfetto_sql/intrinsics/functions/clock_functions.h", + "src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.cc", + "src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.h", "src/trace_processor/perfetto_sql/intrinsics/functions/create_function.cc", "src/trace_processor/perfetto_sql/intrinsics/functions/create_function.h", "src/trace_processor/perfetto_sql/intrinsics/functions/create_view_function.cc", @@ -2567,6 +2569,7 @@ perfetto_filegroup( name = "src_trace_processor_perfetto_sql_intrinsics_types_types", srcs = [ "src/trace_processor/perfetto_sql/intrinsics/types/array.h", + "src/trace_processor/perfetto_sql/intrinsics/types/counter.h", "src/trace_processor/perfetto_sql/intrinsics/types/node.h", "src/trace_processor/perfetto_sql/intrinsics/types/partitioned_intervals.h", "src/trace_processor/perfetto_sql/intrinsics/types/row_dataframe.h", diff --git a/src/trace_processor/db/column_storage.h b/src/trace_processor/db/column_storage.h index 597408110b..eae4bef1a3 100644 --- a/src/trace_processor/db/column_storage.h +++ b/src/trace_processor/db/column_storage.h @@ -137,6 +137,10 @@ class ColumnStorage> final : public ColumnStorageBase { data_.insert(data_.end(), count, val); valid_.Resize(valid_.size() + static_cast(count), true); } + void Append(const std::vector& vals) { + data_.insert(data_.end(), vals.begin(), vals.end()); + valid_.Resize(valid_.size() + static_cast(vals.size()), true); + } void Set(uint32_t idx, T val) { if (mode_ == Mode::kDense) { valid_.Set(idx); diff --git a/src/trace_processor/db/runtime_table.cc b/src/trace_processor/db/runtime_table.cc index 9019d002c9..43704995b7 100644 --- a/src/trace_processor/db/runtime_table.cc +++ b/src/trace_processor/db/runtime_table.cc @@ -274,27 +274,31 @@ base::Status RuntimeTable::Builder::AddText(uint32_t idx, const char* ptr) { } base::Status RuntimeTable::Builder::AddIntegers(uint32_t idx, - int64_t res, + int64_t val, uint32_t count) { auto* col = storage_[idx].get(); if (auto* leading_nulls_ptr = std::get_if(col)) { *col = Fill(*leading_nulls_ptr, std::nullopt); } if (auto* doubles = std::get_if(col)) { - if (!IsPerfectlyRepresentableAsDouble(res)) { + if (!IsPerfectlyRepresentableAsDouble(val)) { return base::ErrStatus("Column %s contains %" PRId64 " which cannot be represented as a double", - col_names_[idx].c_str(), res); + col_names_[idx].c_str(), val); } - doubles->AppendMultiple(static_cast(res), count); + doubles->AppendMultiple(static_cast(val), count); return base::OkStatus(); } - auto* ints = std::get_if(col); + if (auto* null_ints = std::get_if(col)) { + null_ints->AppendMultiple(val, count); + return base::OkStatus(); + } + auto* ints = std::get_if(col); if (!ints) { return base::ErrStatus("Column %s does not have consistent types", col_names_[idx].c_str()); } - ints->AppendMultiple(res, count); + ints->AppendMultiple(val, count); return base::OkStatus(); } @@ -369,9 +373,28 @@ void RuntimeTable::Builder::AddNonNullIntegersUnchecked( std::get(*storage_[idx]).Append(res); } +void RuntimeTable::Builder::AddNullIntegersUnchecked( + uint32_t idx, + const std::vector& res) { + std::get(*storage_[idx]).Append(res); +} + +void RuntimeTable::Builder::AddNonNullDoublesUnchecked( + uint32_t idx, + const std::vector& vals) { + std::get(*storage_[idx]).Append(vals); +} + +void RuntimeTable::Builder::AddNullDoublesUnchecked( + uint32_t idx, + const std::vector& vals) { + std::get(*storage_[idx]).Append(vals); +} + base::StatusOr> RuntimeTable::Builder::Build( uint32_t rows) && { - std::vector> storage_layers(col_names_.size() + 1); + std::vector> storage_layers(col_names_.size() + + 1); std::vector> null_layers(col_names_.size() + 1); std::vector legacy_columns; @@ -422,12 +445,24 @@ base::StatusOr> RuntimeTable::Builder::Build( i, col_names_[i].c_str(), std::get_if(col), storage_layers, overlay_layers, legacy_columns, legacy_overlays); - } else if (auto* doubles = std::get_if(col)) { - // The doubles column. + } else if (auto* doubles = std::get_if(col)) { + // The `doubles` column for tables where column types was provided before. PERFETTO_CHECK(doubles->size() == rows); - if (doubles->non_null_size() == doubles->size()) { + bool is_sorted = + std::is_sorted(doubles->vector().begin(), doubles->vector().end()); + uint32_t flags = + is_sorted ? ColumnLegacy::Flag::kNonNull | ColumnLegacy::Flag::kSorted + : ColumnLegacy::Flag::kNonNull; + legacy_columns.emplace_back(col_names_[i].c_str(), doubles, flags, i, 0); + storage_layers[i].reset(new column::NumericStorage( + &doubles->vector(), ColumnType::kDouble, is_sorted)); + + } else if (auto* null_doubles = std::get_if(col)) { + // The doubles column. + PERFETTO_CHECK(null_doubles->size() == rows); + if (null_doubles->non_null_size() == null_doubles->size()) { // The column is not nullable. - *col = DoubleStorage::CreateFromAssertNonNull(std::move(*doubles)); + *col = DoubleStorage::CreateFromAssertNonNull(std::move(*null_doubles)); auto* non_null_doubles = std::get_if(col); bool is_sorted = std::is_sorted(non_null_doubles->vector().begin(), @@ -441,12 +476,12 @@ base::StatusOr> RuntimeTable::Builder::Build( &non_null_doubles->vector(), ColumnType::kDouble, is_sorted)); } else { // The column is nullable. - legacy_columns.emplace_back(col_names_[i].c_str(), doubles, + legacy_columns.emplace_back(col_names_[i].c_str(), null_doubles, ColumnLegacy::Flag::kNoFlag, i, 0); storage_layers[i].reset(new column::NumericStorage( - &doubles->non_null_vector(), ColumnType::kDouble, false)); + &null_doubles->non_null_vector(), ColumnType::kDouble, false)); null_layers[i].reset( - new column::NullOverlay(&doubles->non_null_bit_vector())); + new column::NullOverlay(&null_doubles->non_null_bit_vector())); } } else if (auto* strings = std::get_if(col)) { diff --git a/src/trace_processor/db/runtime_table.h b/src/trace_processor/db/runtime_table.h index 3dd2a78175..ed93250637 100644 --- a/src/trace_processor/db/runtime_table.h +++ b/src/trace_processor/db/runtime_table.h @@ -80,8 +80,10 @@ class RuntimeTable : public Table { void AddNonNullIntegerUnchecked(uint32_t idx, int64_t res) { std::get(*storage_[idx]).Append(res); } - void AddNonNullIntegersUnchecked(uint32_t idx, - const std::vector& res); + void AddNonNullIntegersUnchecked(uint32_t idx, const std::vector&); + void AddNullIntegersUnchecked(uint32_t idx, const std::vector&); + void AddNonNullDoublesUnchecked(uint32_t idx, const std::vector&); + void AddNullDoublesUnchecked(uint32_t idx, const std::vector&); base::StatusOr> Build(uint32_t rows) &&; diff --git a/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn b/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn index 863c2e6f9c..d7ae79769f 100644 --- a/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn +++ b/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn @@ -22,6 +22,8 @@ source_set("functions") { "base64.cc", "base64.h", "clock_functions.h", + "counter_intervals.cc", + "counter_intervals.h", "create_function.cc", "create_function.h", "create_view_function.cc", diff --git a/src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.cc b/src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.cc new file mode 100644 index 0000000000..f9c8d486c8 --- /dev/null +++ b/src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.cc @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2024 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/perfetto_sql/intrinsics/functions/counter_intervals.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "perfetto/base/compiler.h" +#include "perfetto/base/logging.h" +#include "perfetto/base/status.h" +#include "perfetto/ext/base/flat_hash_map.h" +#include "perfetto/ext/base/status_or.h" +#include "perfetto/ext/base/string_utils.h" +#include "perfetto/trace_processor/basic_types.h" +#include "src/trace_processor/containers/string_pool.h" +#include "src/trace_processor/db/runtime_table.h" +#include "src/trace_processor/perfetto_sql/engine/function_util.h" +#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h" +#include "src/trace_processor/perfetto_sql/intrinsics/types/counter.h" +#include "src/trace_processor/perfetto_sql/intrinsics/types/partitioned_intervals.h" +#include "src/trace_processor/sqlite/bindings/sqlite_bind.h" +#include "src/trace_processor/sqlite/bindings/sqlite_column.h" +#include "src/trace_processor/sqlite/bindings/sqlite_function.h" +#include "src/trace_processor/sqlite/bindings/sqlite_result.h" +#include "src/trace_processor/sqlite/bindings/sqlite_stmt.h" +#include "src/trace_processor/sqlite/bindings/sqlite_type.h" +#include "src/trace_processor/sqlite/bindings/sqlite_value.h" +#include "src/trace_processor/sqlite/sqlite_utils.h" +#include "src/trace_processor/util/status_macros.h" + +namespace perfetto::trace_processor::perfetto_sql { +namespace { + +struct CounterIntervals : public SqliteFunction { + static constexpr char kName[] = "__intrinsic_counter_intervals"; + static constexpr int kArgCount = 3; + + struct UserDataContext { + PerfettoSqlEngine* engine; + StringPool* pool; + }; + + static void Step(sqlite3_context* ctx, int argc, sqlite3_value** argv) { + PERFETTO_DCHECK(argc == kArgCount); + const char* leading_str = sqlite::value::Text(argv[0]); + if (!leading_str) { + return sqlite::result::Error( + ctx, "interval intersect: column list cannot be null"); + } + + // TODO(mayzner): Support 'lagging'. + if (base::CaseInsensitiveEqual("lagging", leading_str)) { + return sqlite::result::Error( + ctx, "interval intersect: 'lagging' is not implemented"); + } + if (!base::CaseInsensitiveEqual("leading", leading_str)) { + return sqlite::result::Error(ctx, + "interval intersect: second argument has to " + "be either 'leading' or 'lagging"); + } + + int64_t trace_end = sqlite::value::Int64(argv[1]); + + // Get column names of return columns. + std::vector ret_col_names{ + "id", "ts", "dur", "track_id", "value", "next_value", "delta_value"}; + std::vector col_types{ + RuntimeTable::kInt, // id + RuntimeTable::kInt, // ts, + RuntimeTable::kInt, // dur + RuntimeTable::kInt, // track_id + RuntimeTable::kDouble, // value + RuntimeTable::kNullDouble, // next_value + RuntimeTable::kNullDouble, // delta_value + }; + + auto partitioned_counter = sqlite::value::Pointer( + argv[2], PartitionedCounter::kName); + if (!partitioned_counter) { + SQLITE_ASSIGN_OR_RETURN( + ctx, std::unique_ptr ret_table, + RuntimeTable::Builder(GetUserData(ctx)->pool, ret_col_names) + .Build(0)); + return sqlite::result::UniquePointer(ctx, std::move(ret_table), "TABLE"); + } + + RuntimeTable::Builder builder(GetUserData(ctx)->pool, ret_col_names, + col_types); + + uint32_t rows_count = 0; + for (auto track_counter = partitioned_counter->partitions_map.GetIterator(); + track_counter; ++track_counter) { + int64_t track_id = track_counter.key(); + const auto& cols = track_counter.value(); + size_t r_count = cols.id.size(); + rows_count += r_count; + + // Id + builder.AddNonNullIntegersUnchecked(0, cols.id); + // Ts + builder.AddNonNullIntegersUnchecked(1, cols.ts); + + // Dur + std::vector dur(r_count); + for (size_t i = 0; i < r_count - 1; i++) { + dur[i] = cols.ts[i + 1] - cols.ts[i]; + } + dur[r_count - 1] = trace_end - cols.ts.back(); + builder.AddNonNullIntegersUnchecked(2, dur); + + // Track id + builder.AddIntegers(3, track_id, static_cast(r_count)); + // Value + builder.AddNonNullDoublesUnchecked(4, cols.val); + + // Next value + std::vector next_vals(cols.val.begin() + 1, cols.val.end()); + builder.AddNullDoublesUnchecked(5, next_vals); + builder.AddNull(5); + + // Delta value + std::vector deltas(r_count - 1); + for (size_t i = 0; i < r_count - 1; i++) { + deltas[i] = cols.val[i + 1] - cols.val[i]; + } + builder.AddNull(6); + builder.AddNullDoublesUnchecked(6, deltas); + } + + SQLITE_ASSIGN_OR_RETURN(ctx, std::unique_ptr ret_tab, + std::move(builder).Build(rows_count)); + + return sqlite::result::UniquePointer(ctx, std::move(ret_tab), "TABLE"); + } +}; + +} // namespace + +base::Status RegisterCounterIntervalsFunctions(PerfettoSqlEngine& engine, + StringPool* pool) { + return engine.RegisterSqliteFunction( + std::make_unique( + CounterIntervals::UserDataContext{&engine, pool})); +} + +} // namespace perfetto::trace_processor::perfetto_sql diff --git a/src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.h b/src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.h new file mode 100644 index 0000000000..dc0249aab8 --- /dev/null +++ b/src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 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. + */ + +#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_COUNTER_INTERVALS_H_ +#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_COUNTER_INTERVALS_H_ + +#include "perfetto/base/status.h" +#include "src/trace_processor/containers/string_pool.h" +#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h" + +namespace perfetto::trace_processor::perfetto_sql { + +// Registers all interval intersect related functions with |engine|. +base::Status RegisterCounterIntervalsFunctions(PerfettoSqlEngine& engine, + StringPool* pool); + +} // namespace perfetto::trace_processor::perfetto_sql + +#endif // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_COUNTER_INTERVALS_H_ diff --git a/src/trace_processor/perfetto_sql/intrinsics/functions/type_builders.cc b/src/trace_processor/perfetto_sql/intrinsics/functions/type_builders.cc index 4ce84d59de..a9d928752b 100644 --- a/src/trace_processor/perfetto_sql/intrinsics/functions/type_builders.cc +++ b/src/trace_processor/perfetto_sql/intrinsics/functions/type_builders.cc @@ -19,9 +19,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -31,11 +33,13 @@ #include "perfetto/base/status.h" #include "perfetto/ext/base/flat_hash_map.h" #include "perfetto/ext/base/hash.h" +#include "perfetto/ext/base/small_vector.h" #include "perfetto/public/compiler.h" #include "perfetto/trace_processor/basic_types.h" #include "src/trace_processor/containers/interval_intersector.h" #include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h" #include "src/trace_processor/perfetto_sql/intrinsics/types/array.h" +#include "src/trace_processor/perfetto_sql/intrinsics/types/counter.h" #include "src/trace_processor/perfetto_sql/intrinsics/types/node.h" #include "src/trace_processor/perfetto_sql/intrinsics/types/partitioned_intervals.h" #include "src/trace_processor/perfetto_sql/intrinsics/types/row_dataframe.h" @@ -407,6 +411,55 @@ struct IntervalTreeIntervalsAgg } }; +struct CounterPerTrackAgg + : public SqliteAggregateFunction { + static constexpr char kName[] = "__intrinsic_counter_per_track_agg"; + static constexpr int kArgCount = 4; + struct AggCtx : SqliteAggregateContext { + perfetto_sql::PartitionedCounter tracks; + }; + + static void Step(sqlite3_context* ctx, int rargc, sqlite3_value** argv) { + auto argc = static_cast(rargc); + PERFETTO_DCHECK(argc == kArgCount); + auto& tracks = AggCtx::GetOrCreateContextForStep(ctx).tracks; + + // Fetch columns. + int64_t id = sqlite::value::Int64(argv[0]); + int64_t ts = sqlite::value::Int64(argv[1]); + int64_t track_id = static_cast(sqlite::value::Int64(argv[2])); + double val = sqlite::value::Double(argv[3]); + + auto* new_rows_track = tracks.partitions_map.Find(track_id); + if (!new_rows_track) { + new_rows_track = tracks.partitions_map.Insert(track_id, {}).first; + } else if (std::equal_to()(new_rows_track->val.back(), val)) { + // TODO(mayzner): This algorithm is focused on "leading" counters - if the + // counter before had the same value we can safely remove the new one as + // it adds no value. In the future we should also support "lagging" - if + // the next one has the same value as the previous, we should remove the + // previous. + return; + } + + new_rows_track->id.push_back(id); + new_rows_track->ts.push_back(ts); + new_rows_track->val.push_back(val); + } + + static void Final(sqlite3_context* ctx) { + auto raw_agg_ctx = AggCtx::GetContextOrNullForFinal(ctx); + if (!raw_agg_ctx) { + return sqlite::result::Null(ctx); + } + return sqlite::result::UniquePointer( + ctx, + std::make_unique( + std::move(raw_agg_ctx.get()->tracks)), + perfetto_sql::PartitionedCounter::kName); + } +}; + } // namespace base::Status RegisterTypeBuilderFunctions(PerfettoSqlEngine& engine) { @@ -417,6 +470,8 @@ base::Status RegisterTypeBuilderFunctions(PerfettoSqlEngine& engine) { RETURN_IF_ERROR( engine.RegisterSqliteAggregateFunction( nullptr)); + RETURN_IF_ERROR( + engine.RegisterSqliteAggregateFunction(nullptr)); return engine.RegisterSqliteAggregateFunction(nullptr); } diff --git a/src/trace_processor/perfetto_sql/intrinsics/types/BUILD.gn b/src/trace_processor/perfetto_sql/intrinsics/types/BUILD.gn index 628f05f5a5..26486e83e8 100644 --- a/src/trace_processor/perfetto_sql/intrinsics/types/BUILD.gn +++ b/src/trace_processor/perfetto_sql/intrinsics/types/BUILD.gn @@ -20,6 +20,7 @@ assert(enable_perfetto_trace_processor_sqlite) source_set("types") { sources = [ "array.h", + "counter.h", "node.h", "partitioned_intervals.h", "row_dataframe.h", diff --git a/src/trace_processor/perfetto_sql/intrinsics/types/counter.h b/src/trace_processor/perfetto_sql/intrinsics/types/counter.h new file mode 100644 index 0000000000..863bbc343d --- /dev/null +++ b/src/trace_processor/perfetto_sql/intrinsics/types/counter.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 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. + */ + +#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TYPES_COUNTER_H_ +#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TYPES_COUNTER_H_ + +#include +#include +#include +#include +#include "perfetto/ext/base/flat_hash_map.h" +#include "perfetto/trace_processor/basic_types.h" + +namespace perfetto::trace_processor::perfetto_sql { + +struct CounterTrackPartition { + std::vector id; + std::vector ts; + std::vector val; +}; + +struct PartitionedCounter { + static constexpr char kName[] = "COUNTER_TRACK_PARTITIONS"; + base:: + FlatHashMap> + partitions_map; +}; + +} // namespace perfetto::trace_processor::perfetto_sql + +#endif // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TYPES_COUNTER_H_ diff --git a/src/trace_processor/perfetto_sql/stdlib/counters/intervals.sql b/src/trace_processor/perfetto_sql/stdlib/counters/intervals.sql index cf9e5a3e81..7eac3597c6 100644 --- a/src/trace_processor/perfetto_sql/stdlib/counters/intervals.sql +++ b/src/trace_processor/perfetto_sql/stdlib/counters/intervals.sql @@ -49,23 +49,32 @@ CREATE PERFETTO MACRO counter_leading_intervals( -- value DOUBLE, next_value DOUBLE, delta_value DOUBLE). RETURNS TableOrSubquery AS ( - WITH base AS ( - SELECT - id, - ts, - track_id, - value, - LAG(value) OVER (PARTITION BY track_id ORDER BY ts) AS lag_value - FROM $counter_table - ) SELECT - id, - ts, - LEAD(ts, 1, trace_end()) OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur, - track_id, - value, - LEAD(value) OVER(PARTITION BY track_id ORDER BY ts) AS next_value, - value - lag_value AS delta_value - FROM base - WHERE value != lag_value OR lag_value IS NULL + c0 AS id, + c1 AS ts, + c2 AS dur, + c3 AS track_id, + c4 AS value, + c5 AS next_value, + c6 AS delta_value + FROM __intrinsic_table_ptr( + __intrinsic_counter_intervals( + "leading", TRACE_END(), + (SELECT __intrinsic_counter_per_track_agg( + input.id, + input.ts, + input.track_id, + input.value + ) + FROM (SELECT * FROM $counter_table ORDER BY ts) input) + ) + ) + + WHERE __intrinsic_table_ptr_bind(c0, 'id') + AND __intrinsic_table_ptr_bind(c1, 'ts') + AND __intrinsic_table_ptr_bind(c2, 'dur') + AND __intrinsic_table_ptr_bind(c3, 'track_id') + AND __intrinsic_table_ptr_bind(c4, 'value') + AND __intrinsic_table_ptr_bind(c5, 'next_value') + AND __intrinsic_table_ptr_bind(c6, 'delta_value') ); diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc index 114fb82898..f41f1c85b5 100644 --- a/src/trace_processor/trace_processor_impl.cc +++ b/src/trace_processor/trace_processor_impl.cc @@ -71,6 +71,7 @@ #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/counter_intervals.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/dominator_tree.h" @@ -815,6 +816,10 @@ void TraceProcessorImpl::InitPerfettoSqlEngine() { base::Status status = perfetto_sql::RegisterIntervalIntersectFunctions( *engine_, context_.storage->mutable_string_pool()); } + { + base::Status status = perfetto_sql::RegisterCounterIntervalsFunctions( + *engine_, context_.storage->mutable_string_pool()); + } TraceStorage* storage = context_.storage.get(); diff --git a/test/trace_processor/diff_tests/stdlib/counters/tests.py b/test/trace_processor/diff_tests/stdlib/counters/tests.py index d3ade3eb5c..1bf51b4b02 100644 --- a/test/trace_processor/diff_tests/stdlib/counters/tests.py +++ b/test/trace_processor/diff_tests/stdlib/counters/tests.py @@ -27,24 +27,20 @@ def test_intervals_counter_leading(self): query=""" INCLUDE PERFETTO MODULE counters.intervals; - WITH - foo AS ( - SELECT 0 AS id, 0 AS ts, 10 AS value, 1 AS track_id - UNION ALL - SELECT 1 AS id, 0 AS ts, 10 AS value, 2 AS track_id - UNION ALL - SELECT 2 AS id, 10 AS ts, 10 AS value, 1 AS track_id - UNION ALL - SELECT 3 AS id, 10 AS ts, 20 AS value, 2 AS track_id - UNION ALL - SELECT 4 AS id, 20 AS ts, 30 AS value, 1 AS track_id + WITH data(id, ts, value, track_id) AS ( + VALUES + (0, 0, 10, 1), + (1, 0, 10, 2), + (2, 10, 10, 1), + (3, 10, 20, 2), + (4, 20, 30, 1) ) - SELECT * FROM counter_leading_intervals !(foo); + SELECT * FROM counter_leading_intervals!(data); """, out=Csv(""" "id","ts","dur","track_id","value","next_value","delta_value" - 0,0,20,1,10,30,"[NULL]" - 4,20,19980,1,30,"[NULL]",20 - 1,0,10,2,10,20,"[NULL]" - 3,10,19990,2,20,"[NULL]",10 + 0,0,20,1,10.000000,30.000000,"[NULL]" + 4,20,19980,1,30.000000,"[NULL]",20.000000 + 1,0,10,2,10.000000,20.000000,"[NULL]" + 3,10,19990,2,20.000000,"[NULL]",10.000000 """))