1 /*
2  * Copyright (C) 2024 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/operators/counter_mipmap_operator.h"
18 
19 #include <sqlite3.h>
20 #include <algorithm>
21 #include <cstddef>
22 #include <cstdint>
23 #include <functional>
24 #include <iterator>
25 #include <memory>
26 #include <string>
27 #include <utility>
28 
29 #include "perfetto/base/logging.h"
30 #include "perfetto/base/status.h"
31 #include "perfetto/ext/base/status_or.h"
32 #include "src/trace_processor/containers/implicit_segment_forest.h"
33 #include "src/trace_processor/sqlite/bindings/sqlite_result.h"
34 #include "src/trace_processor/sqlite/module_lifecycle_manager.h"
35 #include "src/trace_processor/sqlite/sql_source.h"
36 #include "src/trace_processor/sqlite/sqlite_utils.h"
37 
38 namespace perfetto::trace_processor {
39 namespace {
40 
41 constexpr char kSchema[] = R"(
42   CREATE TABLE x(
43     in_window_start BIGINT HIDDEN,
44     in_window_end BIGINT HIDDEN,
45     in_window_step BIGINT HIDDEN,
46     min_value DOUBLE,
47     max_value DOUBLE,
48     last_ts BIGINT,
49     last_value DOUBLE,
50     PRIMARY KEY(last_ts)
51   ) WITHOUT ROWID
52 )";
53 
54 enum ColumnIndex : size_t {
55   kInWindowStart = 0,
56   kInWindowEnd,
57   kInWindowStep,
58 
59   kMinValue,
60   kMaxValue,
61   kLastTs,
62   kLastValue,
63 };
64 
65 constexpr size_t kArgCount = kInWindowStep + 1;
66 
IsArgColumn(size_t index)67 bool IsArgColumn(size_t index) {
68   return index < kArgCount;
69 }
70 
71 using Counter = CounterMipmapOperator::Counter;
72 using Agg = CounterMipmapOperator::Agg;
73 using Forest = ImplicitSegmentForest<Counter, Agg>;
74 
75 }  // namespace
76 
Create(sqlite3 * db,void * raw_ctx,int argc,const char * const * argv,sqlite3_vtab ** vtab,char ** zErr)77 int CounterMipmapOperator::Create(sqlite3* db,
78                                   void* raw_ctx,
79                                   int argc,
80                                   const char* const* argv,
81                                   sqlite3_vtab** vtab,
82                                   char** zErr) {
83   if (argc != 4) {
84     *zErr = sqlite3_mprintf("counter_mipmap: wrong number of arguments");
85     return SQLITE_ERROR;
86   }
87 
88   if (int ret = sqlite3_declare_vtab(db, kSchema); ret != SQLITE_OK) {
89     return ret;
90   }
91 
92   auto* ctx = GetContext(raw_ctx);
93   auto state = std::make_unique<State>();
94 
95   std::string sql = "SELECT ts, value FROM ";
96   sql.append(argv[3]);
97   auto res = ctx->engine->ExecuteUntilLastStatement(
98       SqlSource::FromTraceProcessorImplementation(std::move(sql)));
99   if (!res.ok()) {
100     *zErr = sqlite3_mprintf("%s", res.status().c_message());
101     return SQLITE_ERROR;
102   }
103   do {
104     int64_t ts = sqlite3_column_int64(res->stmt.sqlite_stmt(), 0);
105     auto value =
106         static_cast<float>(sqlite3_column_double(res->stmt.sqlite_stmt(), 1));
107     state->timestamps.push_back(ts);
108     state->forest.Push(Counter{value, value});
109   } while (res->stmt.Step());
110   if (!res->stmt.status().ok()) {
111     *zErr = sqlite3_mprintf("%s", res->stmt.status().c_message());
112     return SQLITE_ERROR;
113   }
114 
115   std::unique_ptr<Vtab> vtab_res = std::make_unique<Vtab>();
116   vtab_res->state = ctx->manager.OnCreate(argv, std::move(state));
117   *vtab = vtab_res.release();
118   return SQLITE_OK;
119 }
120 
Destroy(sqlite3_vtab * vtab)121 int CounterMipmapOperator::Destroy(sqlite3_vtab* vtab) {
122   std::unique_ptr<Vtab> tab(GetVtab(vtab));
123   sqlite::ModuleStateManager<CounterMipmapOperator>::OnDestroy(tab->state);
124   return SQLITE_OK;
125 }
126 
Connect(sqlite3 * db,void * raw_ctx,int argc,const char * const * argv,sqlite3_vtab ** vtab,char **)127 int CounterMipmapOperator::Connect(sqlite3* db,
128                                    void* raw_ctx,
129                                    int argc,
130                                    const char* const* argv,
131                                    sqlite3_vtab** vtab,
132                                    char**) {
133   PERFETTO_CHECK(argc == 4);
134   if (int ret = sqlite3_declare_vtab(db, kSchema); ret != SQLITE_OK) {
135     return ret;
136   }
137   auto* ctx = GetContext(raw_ctx);
138   std::unique_ptr<Vtab> res = std::make_unique<Vtab>();
139   res->state = ctx->manager.OnConnect(argv);
140   *vtab = res.release();
141   return SQLITE_OK;
142 }
143 
Disconnect(sqlite3_vtab * vtab)144 int CounterMipmapOperator::Disconnect(sqlite3_vtab* vtab) {
145   std::unique_ptr<Vtab> tab(GetVtab(vtab));
146   sqlite::ModuleStateManager<CounterMipmapOperator>::OnDisconnect(tab->state);
147   return SQLITE_OK;
148 }
149 
BestIndex(sqlite3_vtab *,sqlite3_index_info * info)150 int CounterMipmapOperator::BestIndex(sqlite3_vtab*, sqlite3_index_info* info) {
151   base::Status status =
152       sqlite::utils::ValidateFunctionArguments(info, kArgCount, IsArgColumn);
153   if (!status.ok()) {
154     return SQLITE_CONSTRAINT;
155   }
156   if (info->nConstraint != kArgCount) {
157     return SQLITE_CONSTRAINT;
158   }
159   return SQLITE_OK;
160 }
161 
Open(sqlite3_vtab *,sqlite3_vtab_cursor ** cursor)162 int CounterMipmapOperator::Open(sqlite3_vtab*, sqlite3_vtab_cursor** cursor) {
163   std::unique_ptr<Cursor> c = std::make_unique<Cursor>();
164   *cursor = c.release();
165   return SQLITE_OK;
166 }
167 
Close(sqlite3_vtab_cursor * cursor)168 int CounterMipmapOperator::Close(sqlite3_vtab_cursor* cursor) {
169   std::unique_ptr<Cursor> c(GetCursor(cursor));
170   return SQLITE_OK;
171 }
172 
Filter(sqlite3_vtab_cursor * cursor,int,const char *,int argc,sqlite3_value ** argv)173 int CounterMipmapOperator::Filter(sqlite3_vtab_cursor* cursor,
174                                   int,
175                                   const char*,
176                                   int argc,
177                                   sqlite3_value** argv) {
178   auto* c = GetCursor(cursor);
179   auto* t = GetVtab(c->pVtab);
180   auto* state =
181       sqlite::ModuleStateManager<CounterMipmapOperator>::GetState(t->state);
182   PERFETTO_CHECK(argc == kArgCount);
183 
184   int64_t start_ts = sqlite3_value_int64(argv[0]);
185   int64_t end_ts = sqlite3_value_int64(argv[1]);
186   int64_t step_ts = sqlite3_value_int64(argv[2]);
187   if (start_ts == end_ts) {
188     return sqlite::utils::SetError(t, "counter_mipmap: empty range provided");
189   }
190 
191   c->index = 0;
192   c->counters.clear();
193 
194   // If there is a counter value before the start of this window, include it in
195   // the aggregation as well becaue it contributes to what should be rendered
196   // here.
197   auto ts_lb = std::lower_bound(state->timestamps.begin(),
198                                 state->timestamps.end(), start_ts);
199   if (ts_lb != state->timestamps.begin() &&
200       (ts_lb == state->timestamps.end() || *ts_lb != start_ts)) {
201     --ts_lb;
202   }
203   int64_t start_idx = std::distance(state->timestamps.begin(), ts_lb);
204   for (int64_t s = start_ts; s < end_ts; s += step_ts) {
205     int64_t end_idx =
206         std::distance(state->timestamps.begin(),
207                       std::lower_bound(state->timestamps.begin() +
208                                            static_cast<int64_t>(start_idx),
209                                        state->timestamps.end(), s + step_ts));
210     if (start_idx == end_idx) {
211       continue;
212     }
213     c->counters.emplace_back(Cursor::Result{
214         state->forest.Query(static_cast<uint32_t>(start_idx),
215                             static_cast<uint32_t>(end_idx)),
216         state->forest[static_cast<uint32_t>(end_idx) - 1],
217         state->timestamps[static_cast<uint32_t>(end_idx) - 1],
218     });
219     start_idx = end_idx;
220   }
221   return SQLITE_OK;
222 }
223 
Next(sqlite3_vtab_cursor * cursor)224 int CounterMipmapOperator::Next(sqlite3_vtab_cursor* cursor) {
225   GetCursor(cursor)->index++;
226   return SQLITE_OK;
227 }
228 
Eof(sqlite3_vtab_cursor * cursor)229 int CounterMipmapOperator::Eof(sqlite3_vtab_cursor* cursor) {
230   auto* c = GetCursor(cursor);
231   return c->index >= c->counters.size();
232 }
233 
Column(sqlite3_vtab_cursor * cursor,sqlite3_context * ctx,int N)234 int CounterMipmapOperator::Column(sqlite3_vtab_cursor* cursor,
235                                   sqlite3_context* ctx,
236                                   int N) {
237   auto* t = GetVtab(cursor->pVtab);
238   auto* c = GetCursor(cursor);
239   const auto& res = c->counters[c->index];
240   switch (N) {
241     case ColumnIndex::kMinValue:
242       sqlite::result::Double(ctx, static_cast<double>(res.min_max_counter.min));
243       return SQLITE_OK;
244     case ColumnIndex::kMaxValue:
245       sqlite::result::Double(ctx, static_cast<double>(res.min_max_counter.max));
246       return SQLITE_OK;
247     case ColumnIndex::kLastTs:
248       sqlite::result::Long(ctx, res.last_ts);
249       return SQLITE_OK;
250     case ColumnIndex::kLastValue:
251       PERFETTO_DCHECK(
252           std::equal_to<>()(res.last_counter.min, res.last_counter.max));
253       sqlite::result::Double(ctx, static_cast<double>(res.last_counter.min));
254       return SQLITE_OK;
255     default:
256       return sqlite::utils::SetError(t, "Bad column");
257   }
258   PERFETTO_FATAL("For GCC");
259 }
260 
Rowid(sqlite3_vtab_cursor *,sqlite_int64 *)261 int CounterMipmapOperator::Rowid(sqlite3_vtab_cursor*, sqlite_int64*) {
262   return SQLITE_ERROR;
263 }
264 
265 }  // namespace perfetto::trace_processor
266