• 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_PRELUDE_FUNCTIONS_WINDOW_FUNCTIONS_H_
18 #define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_WINDOW_FUNCTIONS_H_
19 
20 #include <sqlite3.h>
21 #include <unordered_map>
22 #include "perfetto/ext/base/base64.h"
23 #include "perfetto/ext/base/file_utils.h"
24 #include "perfetto/ext/trace_processor/demangle.h"
25 #include "protos/perfetto/common/builtin_clock.pbzero.h"
26 #include "src/trace_processor/export_json.h"
27 #include "src/trace_processor/importers/common/clock_tracker.h"
28 #include "src/trace_processor/prelude/functions/create_function_internal.h"
29 #include "src/trace_processor/util/status_macros.h"
30 
31 #include "src/trace_processor/prelude/functions/sql_function.h"
32 
33 namespace perfetto {
34 namespace trace_processor {
35 // Keeps track of the latest non null value and its position withing the
36 // window. Every time the window shrinks (`xInverse` is called) the window size
37 // is reduced by one and the position of the value moves one back, if it gets
38 // out of the window the value is discarded.
39 class LastNonNullAggregateContext {
40  public:
Get(sqlite3_context * ctx)41   static LastNonNullAggregateContext* Get(sqlite3_context* ctx) {
42     return reinterpret_cast<LastNonNullAggregateContext*>(
43         sqlite3_aggregate_context(ctx, 0));
44   }
45 
GetOrCreate(sqlite3_context * ctx)46   static LastNonNullAggregateContext* GetOrCreate(sqlite3_context* ctx) {
47     return reinterpret_cast<LastNonNullAggregateContext*>(
48         sqlite3_aggregate_context(ctx, sizeof(LastNonNullAggregateContext)));
49   }
50 
PopFront()51   inline void PopFront() {
52     PERFETTO_CHECK(window_size_ > 0);
53     --window_size_;
54     if (!last_non_null_value_) {
55       return;
56     }
57     if (value_index_ == 0) {
58       sqlite3_value_free(last_non_null_value_);
59       last_non_null_value_ = nullptr;
60       return;
61     }
62     PERFETTO_CHECK(value_index_ > 0);
63     --value_index_;
64   }
65 
PushBack(sqlite3_value * value)66   inline void PushBack(sqlite3_value* value) {
67     ++window_size_;
68     if (sqlite3_value_type(value) == SQLITE_NULL) {
69       return;
70     }
71 
72     Destroy();
73     last_non_null_value_ = sqlite3_value_dup(value);
74     value_index_ = window_size_ - 1;
75   }
76 
Destroy()77   inline void Destroy() {
78     if (last_non_null_value_) {
79       sqlite3_value_free(last_non_null_value_);
80     }
81   }
82 
last_non_null_value()83   sqlite3_value* last_non_null_value() const { return last_non_null_value_; }
84 
85  private:
86   int64_t window_size_;
87   // Index within the window of the last non null value. Only valid if `value`
88   // is set.
89   int64_t value_index_;
90   // Actual value
91   sqlite3_value* last_non_null_value_;
92 };
93 
94 static_assert(std::is_standard_layout<LastNonNullAggregateContext>::value,
95               "Must be able to be initialized by sqlite3_aggregate_context "
96               "(similar to calloc, i.e. no constructor called)");
97 static_assert(std::is_trivial<LastNonNullAggregateContext>::value,
98               "Must be able to be destroyed by just calling free (i.e. no "
99               "destructor called)");
100 
LastNonNullStep(sqlite3_context * ctx,int argc,sqlite3_value ** argv)101 inline void LastNonNullStep(sqlite3_context* ctx,
102                             int argc,
103                             sqlite3_value** argv) {
104   if (argc != 1) {
105     sqlite3_result_error(
106         ctx, "Unsupported number of args passed to LAST_NON_NULL", -1);
107     return;
108   }
109 
110   auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
111   if (!ptr) {
112     sqlite3_result_error(ctx, "LAST_NON_NULL: Failed to allocate context", -1);
113     return;
114   }
115 
116   ptr->PushBack(argv[0]);
117 }
118 
LastNonNullInverse(sqlite3_context * ctx,int,sqlite3_value **)119 inline void LastNonNullInverse(sqlite3_context* ctx, int, sqlite3_value**) {
120   auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
121   PERFETTO_CHECK(ptr != nullptr);
122   ptr->PopFront();
123 }
124 
LastNonNullValue(sqlite3_context * ctx)125 inline void LastNonNullValue(sqlite3_context* ctx) {
126   auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
127   if (!ptr || !ptr->last_non_null_value()) {
128     sqlite3_result_null(ctx);
129   } else {
130     sqlite3_result_value(ctx, ptr->last_non_null_value());
131   }
132 }
133 
LastNonNullFinal(sqlite3_context * ctx)134 inline void LastNonNullFinal(sqlite3_context* ctx) {
135   auto* ptr = LastNonNullAggregateContext::Get(ctx);
136   if (!ptr || !ptr->last_non_null_value()) {
137     sqlite3_result_null(ctx);
138   } else {
139     sqlite3_result_value(ctx, ptr->last_non_null_value());
140     ptr->Destroy();
141   }
142 }
143 
RegisterLastNonNullFunction(sqlite3 * db)144 inline void RegisterLastNonNullFunction(sqlite3* db) {
145   auto ret = sqlite3_create_window_function(
146       db, "LAST_NON_NULL", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
147       &LastNonNullStep, &LastNonNullFinal, &LastNonNullValue,
148       &LastNonNullInverse, nullptr);
149   if (ret) {
150     PERFETTO_ELOG("Error initializing LAST_NON_NULL");
151   }
152 }
153 }  // namespace trace_processor
154 }  // namespace perfetto
155 
156 #endif  // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_WINDOW_FUNCTIONS_H_
157