• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 #ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_WINDOW_FUNCTIONS_H_
18 #define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_WINDOW_FUNCTIONS_H_
19 
20 #include <sqlite3.h>
21 #include <cstdint>
22 #include <type_traits>
23 
24 #include "perfetto/base/logging.h"
25 #include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
26 #include "src/trace_processor/sqlite/bindings/sqlite_result.h"
27 #include "src/trace_processor/sqlite/bindings/sqlite_window_function.h"
28 
29 namespace perfetto::trace_processor {
30 
31 // Keeps track of the latest non null value and its position withing the
32 // window. Every time the window shrinks (`xInverse` is called) the window size
33 // is reduced by one and the position of the value moves one back, if it gets
34 // out of the window the value is discarded.
35 class LastNonNullAggregateContext {
36  public:
Get(sqlite3_context * ctx)37   static LastNonNullAggregateContext* Get(sqlite3_context* ctx) {
38     return reinterpret_cast<LastNonNullAggregateContext*>(
39         sqlite3_aggregate_context(ctx, 0));
40   }
41 
GetOrCreate(sqlite3_context * ctx)42   static LastNonNullAggregateContext* GetOrCreate(sqlite3_context* ctx) {
43     return reinterpret_cast<LastNonNullAggregateContext*>(
44         sqlite3_aggregate_context(ctx, sizeof(LastNonNullAggregateContext)));
45   }
46 
PopFront()47   inline void PopFront() {
48     PERFETTO_CHECK(window_size_ > 0);
49     --window_size_;
50     if (!last_non_null_value_) {
51       return;
52     }
53     if (value_index_ == 0) {
54       sqlite3_value_free(last_non_null_value_);
55       last_non_null_value_ = nullptr;
56       return;
57     }
58     PERFETTO_CHECK(value_index_ > 0);
59     --value_index_;
60   }
61 
PushBack(sqlite3_value * value)62   inline void PushBack(sqlite3_value* value) {
63     ++window_size_;
64     if (sqlite3_value_type(value) == SQLITE_NULL) {
65       return;
66     }
67 
68     Destroy();
69     last_non_null_value_ = sqlite3_value_dup(value);
70     value_index_ = window_size_ - 1;
71   }
72 
Destroy()73   inline void Destroy() {
74     if (last_non_null_value_) {
75       sqlite3_value_free(last_non_null_value_);
76     }
77   }
78 
last_non_null_value()79   sqlite3_value* last_non_null_value() const { return last_non_null_value_; }
80 
81  private:
82   int64_t window_size_;
83   // Index within the window of the last non null value. Only valid if `value`
84   // is set.
85   int64_t value_index_;
86   // Actual value
87   sqlite3_value* last_non_null_value_;
88 };
89 
90 static_assert(std::is_standard_layout_v<LastNonNullAggregateContext>,
91               "Must be able to be initialized by sqlite3_aggregate_context "
92               "(similar to calloc, i.e. no constructor called)");
93 static_assert(std::is_trivial_v<LastNonNullAggregateContext>,
94               "Must be able to be destroyed by just calling free (i.e. no "
95               "destructor called)");
96 
97 class LastNonNull : public SqliteWindowFunction {
98  public:
Step(sqlite3_context * ctx,int argc,sqlite3_value ** argv)99   static void Step(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
100     if (argc != 1) {
101       return sqlite::result::Error(
102           ctx, "Unsupported number of args passed to LAST_NON_NULL");
103     }
104 
105     auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
106     if (!ptr) {
107       return sqlite::result::Error(ctx,
108                                    "LAST_NON_NULL: Failed to allocate context");
109     }
110 
111     ptr->PushBack(argv[0]);
112   }
113 
Inverse(sqlite3_context * ctx,int,sqlite3_value **)114   static void Inverse(sqlite3_context* ctx, int, sqlite3_value**) {
115     auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
116     PERFETTO_CHECK(ptr != nullptr);
117     ptr->PopFront();
118   }
119 
Value(sqlite3_context * ctx)120   static void Value(sqlite3_context* ctx) {
121     auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
122     if (!ptr || !ptr->last_non_null_value()) {
123       return sqlite::result::Null(ctx);
124     }
125     sqlite3_result_value(ctx, ptr->last_non_null_value());
126   }
127 
Final(sqlite3_context * ctx)128   static void Final(sqlite3_context* ctx) {
129     auto* ptr = LastNonNullAggregateContext::Get(ctx);
130     if (!ptr || !ptr->last_non_null_value()) {
131       return sqlite::result::Null(ctx);
132     }
133     sqlite::result::Value(ctx, ptr->last_non_null_value());
134     ptr->Destroy();
135   }
136 };
137 
RegisterLastNonNullFunction(PerfettoSqlEngine & engine)138 inline base::Status RegisterLastNonNullFunction(PerfettoSqlEngine& engine) {
139   return engine.RegisterSqliteWindowFunction<LastNonNull>("LAST_NON_NULL", 1,
140                                                           nullptr);
141 }
142 
143 }  // namespace perfetto::trace_processor
144 
145 #endif  // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_WINDOW_FUNCTIONS_H_
146