• 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 #include "src/trace_processor/prelude/functions/pprof_functions.h"
18 
19 #include <stdlib.h>
20 #include <cinttypes>
21 #include <cstddef>
22 #include <cstdint>
23 #include <limits>
24 #include <string>
25 #include <utility>
26 #include <vector>
27 
28 #include "perfetto/base/logging.h"
29 #include "perfetto/base/status.h"
30 #include "perfetto/ext/base/status_or.h"
31 #include "perfetto/protozero/packed_repeated_fields.h"
32 #include "perfetto/trace_processor/basic_types.h"
33 #include "perfetto/trace_processor/status.h"
34 #include "protos/perfetto/trace_processor/stack.pbzero.h"
35 #include "src/trace_processor/sqlite/sqlite_utils.h"
36 #include "src/trace_processor/types/trace_processor_context.h"
37 #include "src/trace_processor/util/profile_builder.h"
38 #include "src/trace_processor/util/status_macros.h"
39 
40 // TODO(carlscab): We currently recreate the GProfileBuilder for every group. We
41 // should cache this somewhere maybe even have a helper table that stores all
42 // this data.
43 
44 namespace perfetto {
45 namespace trace_processor {
46 namespace {
47 
48 using protos::pbzero::Stack;
49 
50 constexpr char kFunctionName[] = "EXPERIMENTAL_PROFILE";
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 =
73         sqlite_utils::ExtractArgument(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_value_)) {
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     sqlite3_result_blob(ctx, data.release(),
98                         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_value_.Append(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       return base::OkStatus();
138     }
139 
140     sample_value_.Reset();
141     for (size_t i = 3; i < argc; i += 3) {
142       base::StatusOr<SqlValue> value = sqlite_utils::ExtractArgument(
143           argc, argv, "sample_value", i, SqlValue::kLong);
144       if (!value.ok()) {
145         return value.status();
146       }
147       sample_value_.Append(value->AsLong());
148     }
149 
150     return base::OkStatus();
151   }
152 
153   GProfileBuilder builder_;
154   protozero::PackedVarInt sample_value_;
155 };
156 
Step(sqlite3_context * ctx,size_t argc,sqlite3_value ** argv)157 static base::Status Step(sqlite3_context* ctx,
158                          size_t argc,
159                          sqlite3_value** argv) {
160   AggregateContext** agg_context_ptr = static_cast<AggregateContext**>(
161       sqlite3_aggregate_context(ctx, sizeof(AggregateContext*)));
162   if (!agg_context_ptr) {
163     return base::ErrStatus("Failed to allocate aggregate context");
164   }
165 
166   if (!*agg_context_ptr) {
167     TraceProcessorContext* tp_context =
168         static_cast<TraceProcessorContext*>(sqlite3_user_data(ctx));
169     base::StatusOr<std::unique_ptr<AggregateContext>> agg_context =
170         AggregateContext::Create(tp_context, argc, argv);
171     if (!agg_context.ok()) {
172       return agg_context.status();
173     }
174 
175     *agg_context_ptr = agg_context->release();
176   }
177 
178   return (*agg_context_ptr)->Step(argc, argv);
179 }
180 
StepWrapper(sqlite3_context * ctx,int argc,sqlite3_value ** argv)181 static void StepWrapper(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
182   PERFETTO_CHECK(argc >= 0);
183 
184   base::Status status = Step(ctx, static_cast<size_t>(argc), argv);
185 
186   if (!status.ok()) {
187     sqlite_utils::SetSqliteError(ctx, kFunctionName, status);
188   }
189 }
190 
FinalWrapper(sqlite3_context * ctx)191 static void FinalWrapper(sqlite3_context* ctx) {
192   AggregateContext** agg_context_ptr =
193       static_cast<AggregateContext**>(sqlite3_aggregate_context(ctx, 0));
194 
195   if (!agg_context_ptr) {
196     return;
197   }
198 
199   (*agg_context_ptr)->Final(ctx);
200 
201   delete (*agg_context_ptr);
202 }
203 
204 }  // namespace
205 
Register(sqlite3 * db,TraceProcessorContext * context)206 base::Status PprofFunctions::Register(sqlite3* db,
207                                       TraceProcessorContext* context) {
208   int flags = SQLITE_UTF8 | SQLITE_DETERMINISTIC;
209   int ret =
210       sqlite3_create_function_v2(db, kFunctionName, -1, flags, context, nullptr,
211                                  StepWrapper, FinalWrapper, nullptr);
212   if (ret != SQLITE_OK) {
213     return base::ErrStatus("Unable to register function with name %s",
214                            kFunctionName);
215   }
216   return base::OkStatus();
217 }
218 
219 }  // namespace trace_processor
220 }  // namespace perfetto
221