1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_counter_dur.h"
18 
19 #include <cstdint>
20 #include <memory>
21 #include <string>
22 #include <unordered_map>
23 #include <vector>
24 
25 #include "perfetto/base/logging.h"
26 #include "perfetto/ext/base/status_or.h"
27 #include "perfetto/trace_processor/basic_types.h"
28 #include "src/trace_processor/db/column_storage.h"
29 #include "src/trace_processor/db/table.h"
30 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/tables_py.h"
31 #include "src/trace_processor/storage/trace_storage.h"
32 #include "src/trace_processor/tables/counter_tables_py.h"
33 
34 namespace perfetto::trace_processor {
35 namespace tables {
36 
37 ExperimentalCounterDurTable::~ExperimentalCounterDurTable() = default;
38 
39 }  // namespace tables
40 
ExperimentalCounterDur(const tables::CounterTable & table)41 ExperimentalCounterDur::ExperimentalCounterDur(
42     const tables::CounterTable& table)
43     : counter_table_(&table) {}
44 ExperimentalCounterDur::~ExperimentalCounterDur() = default;
45 
CreateSchema()46 Table::Schema ExperimentalCounterDur::CreateSchema() {
47   return tables::ExperimentalCounterDurTable::ComputeStaticSchema();
48 }
49 
TableName()50 std::string ExperimentalCounterDur::TableName() {
51   return tables::ExperimentalCounterDurTable::Name();
52 }
53 
EstimateRowCount()54 uint32_t ExperimentalCounterDur::EstimateRowCount() {
55   return counter_table_->row_count();
56 }
57 
ComputeTable(const std::vector<SqlValue> & arguments)58 base::StatusOr<std::unique_ptr<Table>> ExperimentalCounterDur::ComputeTable(
59     const std::vector<SqlValue>& arguments) {
60   PERFETTO_CHECK(arguments.empty());
61   if (!counter_dur_table_) {
62     counter_dur_table_ = tables::ExperimentalCounterDurTable::ExtendParent(
63         *counter_table_, ComputeDurColumn(*counter_table_),
64         ComputeDeltaColumn(*counter_table_));
65   }
66   return std::make_unique<Table>(counter_dur_table_->Copy());
67 }
68 
69 // static
ComputeDurColumn(const CounterTable & table)70 ColumnStorage<int64_t> ExperimentalCounterDur::ComputeDurColumn(
71     const CounterTable& table) {
72   // Keep track of the last seen row for each track id.
73   std::unordered_map<TrackId, CounterTable::RowNumber> last_row_for_track_id;
74   ColumnStorage<int64_t> dur;
75 
76   for (auto table_it = table.IterateRows(); table_it; ++table_it) {
77     // Check if we already have a previous row for the current track id.
78     TrackId track_id = table_it.track_id();
79     auto it = last_row_for_track_id.find(track_id);
80     if (it == last_row_for_track_id.end()) {
81       // This means we don't have any row - start tracking this row for the
82       // future.
83       last_row_for_track_id.emplace(track_id, table_it.row_number());
84     } else {
85       // This means we have an previous row for the current track id. Update
86       // the duration of the previous row to be up to the current ts.
87       CounterTable::RowNumber old_row = it->second;
88       it->second = table_it.row_number();
89       dur.Set(old_row.row_number(),
90               table_it.ts() - old_row.ToRowReference(table).ts());
91     }
92     // Append -1 to mark this event as not having been finished. On a later
93     // row, we may set this to have the correct value.
94     dur.Append(-1);
95   }
96   return dur;
97 }
98 
99 // static
ComputeDeltaColumn(const CounterTable & table)100 ColumnStorage<double> ExperimentalCounterDur::ComputeDeltaColumn(
101     const CounterTable& table) {
102   // Keep track of the last seen row for each track id.
103   std::unordered_map<TrackId, CounterTable::RowNumber> last_row_for_track_id;
104   ColumnStorage<double> delta;
105 
106   for (auto table_it = table.IterateRows(); table_it; ++table_it) {
107     // Check if we already have a previous row for the current track id.
108     TrackId track_id = table_it.track_id();
109     auto it = last_row_for_track_id.find(track_id);
110     if (it == last_row_for_track_id.end()) {
111       // This means we don't have any row - start tracking this row for the
112       // future.
113       last_row_for_track_id.emplace(track_id, table_it.row_number());
114     } else {
115       // This means we have an previous row for the current track id. Update
116       // the duration of the previous row to be up to the current ts.
117       CounterTable::RowNumber old_row = it->second;
118       it->second = table_it.row_number();
119       delta.Set(old_row.row_number(),
120                 table_it.value() - old_row.ToRowReference(table).value());
121     }
122     delta.Append(0);
123   }
124   return delta;
125 }
126 
127 }  // namespace perfetto::trace_processor
128