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 #include "src/trace_processor/perfetto_sql/intrinsics/functions/pprof_functions.h"
18 
19 #include <stdlib.h>
20 #include <cinttypes>
21 #include <cstddef>
22 #include <cstdint>
23 #include <limits>
24 #include <memory>
25 #include <string>
26 #include <utility>
27 #include <vector>
28 
29 #include "perfetto/base/logging.h"
30 #include "perfetto/base/status.h"
31 #include "perfetto/ext/base/status_or.h"
32 #include "perfetto/ext/base/utils.h"
33 #include "perfetto/protozero/packed_repeated_fields.h"
34 #include "perfetto/trace_processor/basic_types.h"
35 #include "perfetto/trace_processor/status.h"
36 #include "protos/perfetto/trace_processor/stack.pbzero.h"
37 #include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
38 #include "src/trace_processor/sqlite/sqlite_utils.h"
39 #include "src/trace_processor/types/trace_processor_context.h"
40 #include "src/trace_processor/util/profile_builder.h"
41 #include "src/trace_processor/util/status_macros.h"
42 
43 // TODO(carlscab): We currently recreate the GProfileBuilder for every group. We
44 // should cache this somewhere maybe even have a helper table that stores all
45 // this data.
46 
47 namespace perfetto::trace_processor {
48 namespace {
49 
50 using protos::pbzero::Stack;
51 
52 template <typename T>
WrapUnique(T * ptr)53 std::unique_ptr<T> WrapUnique(T* ptr) {
54   return std::unique_ptr<T>(ptr);
55 }
56 
57 class AggregateContext {
58  public:
59   static base::StatusOr<std::unique_ptr<AggregateContext>>
Create(TraceProcessorContext * tp_context,size_t argc,sqlite3_value ** argv)60   Create(TraceProcessorContext* tp_context, size_t argc, sqlite3_value** argv) {
61     base::StatusOr<std::vector<GProfileBuilder::ValueType>> sample_types =
62         GetSampleTypes(argc, argv);
63     if (!sample_types.ok()) {
64       return sample_types.status();
65     }
66     return WrapUnique(new AggregateContext(tp_context, sample_types.value()));
67   }
68 
Step(size_t argc,sqlite3_value ** argv)69   base::Status Step(size_t argc, sqlite3_value** argv) {
70     RETURN_IF_ERROR(UpdateSampleValue(argc, argv));
71 
72     base::StatusOr<SqlValue> value = sqlite::utils::ExtractArgument(
73         argc, argv, "stack", 0, SqlValue::kBytes);
74     if (!value.ok()) {
75       return value.status();
76     }
77 
78     Stack::Decoder stack(static_cast<const uint8_t*>(value->bytes_value),
79                          value->bytes_count);
80     if (stack.bytes_left() != 0) {
81       return sqlite::utils::ToInvalidArgumentError(
82           "stack", 0, base::ErrStatus("failed to deserialize Stack proto"));
83     }
84     if (!builder_.AddSample(stack, sample_values_)) {
85       return base::ErrStatus("Failed to add callstack");
86     }
87     return util::OkStatus();
88   }
89 
Final(sqlite3_context * ctx)90   void Final(sqlite3_context* ctx) {
91     // TODO(carlscab): A lot of copies are happening here.
92     std::string profile_proto = builder_.Build();
93 
94     std::unique_ptr<uint8_t[], base::FreeDeleter> data(
95         static_cast<uint8_t*>(malloc(profile_proto.size())));
96     memcpy(data.get(), profile_proto.data(), profile_proto.size());
97     return sqlite::result::RawBytes(
98         ctx, data.release(), static_cast<int>(profile_proto.size()), free);
99   }
100 
101  private:
GetSampleTypes(size_t argc,sqlite3_value ** argv)102   static base::StatusOr<std::vector<GProfileBuilder::ValueType>> GetSampleTypes(
103       size_t argc,
104       sqlite3_value** argv) {
105     std::vector<GProfileBuilder::ValueType> sample_types;
106 
107     if (argc == 1) {
108       sample_types.push_back({"samples", "count"});
109     }
110 
111     for (size_t i = 1; i < argc; i += 3) {
112       base::StatusOr<SqlValue> type = sqlite::utils::ExtractArgument(
113           argc, argv, "sample_type", i, SqlValue::kString);
114       if (!type.ok()) {
115         return type.status();
116       }
117 
118       base::StatusOr<SqlValue> units = sqlite::utils::ExtractArgument(
119           argc, argv, "sample_units", i + 1, SqlValue::kString);
120       if (!units.ok()) {
121         return units.status();
122       }
123 
124       sample_types.push_back({type->AsString(), units->AsString()});
125     }
126     return std::move(sample_types);
127   }
128 
AggregateContext(TraceProcessorContext * tp_context,const std::vector<GProfileBuilder::ValueType> & sample_types)129   AggregateContext(TraceProcessorContext* tp_context,
130                    const std::vector<GProfileBuilder::ValueType>& sample_types)
131       : builder_(tp_context, sample_types) {
132     sample_values_.resize(sample_types.size(), 1);
133   }
134 
UpdateSampleValue(size_t argc,sqlite3_value ** argv)135   base::Status UpdateSampleValue(size_t argc, sqlite3_value** argv) {
136     if (argc == 1) {
137       PERFETTO_CHECK(sample_values_.size() == 1);
138       return base::OkStatus();
139     }
140 
141     PERFETTO_CHECK(argc == 1 + (sample_values_.size() * 3));
142     for (size_t i = 0; i < sample_values_.size(); ++i) {
143       base::StatusOr<SqlValue> value = sqlite::utils::ExtractArgument(
144           argc, argv, "sample_value", 3 + i * 3, SqlValue::kLong);
145       if (!value.ok()) {
146         return value.status();
147       }
148       sample_values_[i] = value->AsLong();
149     }
150 
151     return base::OkStatus();
152   }
153 
154   GProfileBuilder builder_;
155   std::vector<int64_t> sample_values_;
156 };
157 
StepStatus(sqlite3_context * ctx,size_t argc,sqlite3_value ** argv)158 base::Status StepStatus(sqlite3_context* ctx,
159                         size_t argc,
160                         sqlite3_value** argv) {
161   auto** agg_context_ptr = static_cast<AggregateContext**>(
162       sqlite3_aggregate_context(ctx, sizeof(AggregateContext*)));
163   if (!agg_context_ptr) {
164     return base::ErrStatus("Failed to allocate aggregate context");
165   }
166 
167   if (!*agg_context_ptr) {
168     auto* tp_context =
169         static_cast<TraceProcessorContext*>(sqlite3_user_data(ctx));
170     base::StatusOr<std::unique_ptr<AggregateContext>> agg_context =
171         AggregateContext::Create(tp_context, argc, argv);
172     if (!agg_context.ok()) {
173       return agg_context.status();
174     }
175 
176     *agg_context_ptr = agg_context->release();
177   }
178 
179   return (*agg_context_ptr)->Step(argc, argv);
180 }
181 
182 struct ProfileBuilder {
183   static constexpr char kName[] = "EXPERIMENTAL_PROFILE";
184   static constexpr int kArgCount = -1;
185   using UserDataContext = TraceProcessorContext;
186 
Stepperfetto::trace_processor::__anonb336f1ca0111::ProfileBuilder187   static void Step(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
188     PERFETTO_CHECK(argc >= 0);
189 
190     base::Status status = StepStatus(ctx, static_cast<size_t>(argc), argv);
191     if (!status.ok()) {
192       sqlite::utils::SetError(ctx, kName, status);
193     }
194   }
195 
Finalperfetto::trace_processor::__anonb336f1ca0111::ProfileBuilder196   static void Final(sqlite3_context* ctx) {
197     auto** agg_context_ptr =
198         static_cast<AggregateContext**>(sqlite3_aggregate_context(ctx, 0));
199 
200     if (!agg_context_ptr) {
201       return;
202     }
203 
204     (*agg_context_ptr)->Final(ctx);
205 
206     delete (*agg_context_ptr);
207   }
208 };
209 
210 }  // namespace
211 
Register(PerfettoSqlEngine & engine,TraceProcessorContext * context)212 base::Status PprofFunctions::Register(PerfettoSqlEngine& engine,
213                                       TraceProcessorContext* context) {
214   return engine.RegisterSqliteAggregateFunction<ProfileBuilder>(context);
215 }
216 
217 }  // namespace perfetto::trace_processor
218