1 /*
2  * Copyright (C) 2023 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/functions/stack_functions.h"
18 
19 #include <stdlib.h>
20 #include <cstdint>
21 #include <cstring>
22 #include <deque>
23 #include <iterator>
24 #include <optional>
25 #include <type_traits>
26 #include <vector>
27 
28 #include "perfetto/base/logging.h"
29 #include "perfetto/base/status.h"
30 #include "perfetto/protozero/scattered_heap_buffer.h"
31 #include "perfetto/trace_processor/basic_types.h"
32 #include "perfetto/trace_processor/status.h"
33 #include "protos/perfetto/trace_processor/stack.pbzero.h"
34 #include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
35 #include "src/trace_processor/perfetto_sql/intrinsics/functions/sql_function.h"
36 #include "src/trace_processor/sqlite/sqlite_utils.h"
37 #include "src/trace_processor/storage/trace_storage.h"
38 #include "src/trace_processor/types/trace_processor_context.h"
39 #include "src/trace_processor/util/status_macros.h"
40 
41 namespace perfetto {
42 namespace trace_processor {
43 namespace {
44 
45 using protos::pbzero::Stack;
46 
SetBytesOutputValue(const std::vector<uint8_t> & src,SqlValue & out,SqlFunction::Destructors & destructors)47 util::Status SetBytesOutputValue(const std::vector<uint8_t>& src,
48                                  SqlValue& out,
49                                  SqlFunction::Destructors& destructors) {
50   void* dest = malloc(src.size());
51   if (dest == nullptr) {
52     return base::ErrStatus("Out of memory");
53   }
54   memcpy(dest, src.data(), src.size());
55   out = SqlValue::Bytes(dest, src.size());
56   destructors.bytes_destructor = free;
57   return util::OkStatus();
58 }
59 
60 // CAT_STACKS(root BLOB/STRING, level_1 BLOB/STRING, …, leaf BLOB/STRING)
61 // Creates a Stack by concatenating other Stacks. Also accepts strings for which
62 // it generates a fake Frame
63 struct CatStacksFunction : public SqlFunction {
64   static constexpr char kFunctionName[] = "CAT_STACKS";
65   using Context = void;
66 
Runperfetto::trace_processor::__anon7a4157590111::CatStacksFunction67   static base::Status Run(void* cxt,
68                           size_t argc,
69                           sqlite3_value** argv,
70                           SqlValue& out,
71                           Destructors& destructors) {
72     base::Status status = RunImpl(cxt, argc, argv, out, destructors);
73     if (!status.ok()) {
74       return base::ErrStatus("%s: %s", kFunctionName, status.message().c_str());
75     }
76     return status;
77   }
78 
RunImplperfetto::trace_processor::__anon7a4157590111::CatStacksFunction79   static base::Status RunImpl(void*,
80                               size_t argc,
81                               sqlite3_value** argv,
82                               SqlValue& out,
83                               Destructors& destructors) {
84     protozero::HeapBuffered<Stack> stack;
85 
86     // Note, this SQL function expects the root frame to be the first argument.
87     // Stack expects the opposite, thus iterates the args in reverse order.
88     for (size_t i = argc; i > 0; --i) {
89       size_t arg_index = i - 1;
90       SqlValue value = sqlite::utils::SqliteValueToSqlValue(argv[arg_index]);
91       switch (value.type) {
92         case SqlValue::kBytes: {
93           stack->AppendRawProtoBytes(value.bytes_value, value.bytes_count);
94           break;
95         }
96         case SqlValue::kString: {
97           stack->add_entries()->set_name(value.AsString());
98           break;
99         }
100         case SqlValue::kNull:
101           break;
102         case SqlValue::kLong:
103         case SqlValue::kDouble:
104           return sqlite::utils::InvalidArgumentTypeError(
105               "entry", arg_index, value.type, SqlValue::kBytes,
106               SqlValue::kString, SqlValue::kNull);
107       }
108     }
109 
110     return SetBytesOutputValue(stack.SerializeAsArray(), out, destructors);
111   }
112 };
113 
114 // STACK_FROM_STACK_PROFILE_CALLSITE(callsite_id LONG, [annotate BOOLEAN])
115 // Creates a stack by taking a callsite_id (reference to the
116 // stack_profile_callsite table) and generating a list of frames (by walking the
117 // stack_profile_callsite table)
118 // Optionally annotates frames (annotate param has a default value of false)
119 //
120 // Important: Annotations might interfere with certain aggregations, as we
121 // will could have a frame that is annotated with different annotations. That
122 // will lead to multiple functions being generated (same name, line etc, but
123 // different annotation).
124 struct StackFromStackProfileCallsiteFunction : public SqlFunction {
125   static constexpr char kFunctionName[] = "STACK_FROM_STACK_PROFILE_CALLSITE";
126   using Context = TraceStorage;
127 
Runperfetto::trace_processor::__anon7a4157590111::StackFromStackProfileCallsiteFunction128   static base::Status Run(TraceStorage* storage,
129                           size_t argc,
130                           sqlite3_value** argv,
131                           SqlValue& out,
132                           Destructors& destructors) {
133     base::Status status = RunImpl(storage, argc, argv, out, destructors);
134     if (!status.ok()) {
135       return base::ErrStatus("%s: %s", kFunctionName, status.message().c_str());
136     }
137     return status;
138   }
139 
RunImplperfetto::trace_processor::__anon7a4157590111::StackFromStackProfileCallsiteFunction140   static base::Status RunImpl(TraceStorage* storage,
141                               size_t argc,
142                               sqlite3_value** argv,
143                               SqlValue& out,
144                               Destructors& destructors) {
145     if (argc != 1 && argc != 2) {
146       return base::ErrStatus(
147           "%s; Invalid number of arguments: expected 1 or 2, actual %zu",
148           kFunctionName, argc);
149     }
150 
151     base::StatusOr<SqlValue> value = sqlite::utils::ExtractArgument(
152         argc, argv, "callsite_id", 0, SqlValue::kNull, SqlValue::kLong);
153     if (!value.ok()) {
154       return value.status();
155     }
156     if (value->is_null()) {
157       return base::OkStatus();
158     }
159 
160     if (value->AsLong() > std::numeric_limits<uint32_t>::max() ||
161         !storage->stack_profile_callsite_table()
162              .FindById(tables::StackProfileCallsiteTable::Id(
163                  static_cast<uint32_t>(value->AsLong())))
164              .has_value()) {
165       return sqlite::utils::ToInvalidArgumentError(
166           "callsite_id", 0,
167           base::ErrStatus("callsite_id does not exist: %" PRId64,
168                           value->AsLong()));
169     }
170 
171     uint32_t callsite_id = static_cast<uint32_t>(value->AsLong());
172 
173     bool annotate = false;
174     if (argc == 2) {
175       value = sqlite::utils::ExtractArgument(argc, argv, "annotate", 1,
176                                              SqlValue::Type::kLong);
177       if (!value.ok()) {
178         return value.status();
179       }
180       // true = 1 and false = 0 in SQL
181       annotate = (value->AsLong() != 0);
182     }
183 
184     protozero::HeapBuffered<Stack> stack;
185     if (annotate) {
186       stack->add_entries()->set_annotated_callsite_id(callsite_id);
187     } else {
188       stack->add_entries()->set_callsite_id(callsite_id);
189     }
190     return SetBytesOutputValue(stack.SerializeAsArray(), out, destructors);
191   }
192 };
193 
194 // STACK_FROM_STACK_PROFILE_FRAME(frame_id LONG)
195 // Creates a stack with just the frame referenced by frame_id (reference to the
196 // stack_profile_frame table)
197 struct StackFromStackProfileFrameFunction : public SqlFunction {
198   static constexpr char kFunctionName[] = "STACK_FROM_STACK_PROFILE_FRAME";
199   using Context = TraceStorage;
200 
Runperfetto::trace_processor::__anon7a4157590111::StackFromStackProfileFrameFunction201   static base::Status Run(TraceStorage* storage,
202                           size_t argc,
203                           sqlite3_value** argv,
204                           SqlValue& out,
205                           Destructors& destructors) {
206     base::Status status = RunImpl(storage, argc, argv, out, destructors);
207     if (!status.ok()) {
208       return base::ErrStatus("%s: %s", kFunctionName, status.message().c_str());
209     }
210     return status;
211   }
212 
RunImplperfetto::trace_processor::__anon7a4157590111::StackFromStackProfileFrameFunction213   static base::Status RunImpl(TraceStorage* storage,
214                               size_t argc,
215                               sqlite3_value** argv,
216                               SqlValue& out,
217                               Destructors& destructors) {
218     base::StatusOr<SqlValue> value = sqlite::utils::ExtractArgument(
219         argc, argv, "frame_id", 0, SqlValue::kNull, SqlValue::kLong);
220 
221     if (!value.ok()) {
222       return value.status();
223     }
224 
225     if (value->is_null()) {
226       return util::OkStatus();
227     }
228 
229     if (value->AsLong() > std::numeric_limits<uint32_t>::max() ||
230         !storage->stack_profile_frame_table()
231              .FindById(tables::StackProfileFrameTable::Id(
232                  static_cast<uint32_t>(value->AsLong())))
233              .has_value()) {
234       return base::ErrStatus("%s; frame_id does not exist: %" PRId64,
235                              kFunctionName, value->AsLong());
236     }
237 
238     uint32_t frame_id = static_cast<uint32_t>(value->AsLong());
239     protozero::HeapBuffered<Stack> stack;
240     stack->add_entries()->set_frame_id(frame_id);
241     return SetBytesOutputValue(stack.SerializeAsArray(), out, destructors);
242   }
243 };
244 
245 }  // namespace
246 
RegisterStackFunctions(PerfettoSqlEngine * engine,TraceProcessorContext * context)247 base::Status RegisterStackFunctions(PerfettoSqlEngine* engine,
248                                     TraceProcessorContext* context) {
249   RETURN_IF_ERROR(engine->RegisterStaticFunction<CatStacksFunction>(
250       CatStacksFunction::kFunctionName, -1, context->storage.get()));
251   RETURN_IF_ERROR(
252       engine->RegisterStaticFunction<StackFromStackProfileFrameFunction>(
253           StackFromStackProfileFrameFunction::kFunctionName, 1,
254           context->storage.get()));
255   return engine->RegisterStaticFunction<StackFromStackProfileCallsiteFunction>(
256       StackFromStackProfileCallsiteFunction::kFunctionName, -1,
257       context->storage.get());
258 }
259 
260 }  // namespace trace_processor
261 }  // namespace perfetto
262