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