1 /* 2 * Copyright (C) 2019 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_METRICS_METRICS_H_ 18 #define SRC_TRACE_PROCESSOR_METRICS_METRICS_H_ 19 20 #include <sqlite3.h> 21 22 #include <cstddef> 23 #include <cstdint> 24 #include <optional> 25 #include <string> 26 #include <unordered_map> 27 #include <vector> 28 29 #include "perfetto/base/status.h" 30 #include "perfetto/ext/base/status_or.h" 31 #include "perfetto/ext/base/string_view.h" 32 #include "perfetto/protozero/message.h" 33 #include "perfetto/protozero/packed_repeated_fields.h" 34 #include "perfetto/protozero/scattered_heap_buffer.h" 35 #include "perfetto/trace_processor/basic_types.h" 36 #include "perfetto/trace_processor/trace_processor.h" 37 #include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h" 38 #include "src/trace_processor/perfetto_sql/intrinsics/functions/sql_function.h" 39 #include "src/trace_processor/sqlite/bindings/sqlite_aggregate_function.h" 40 #include "src/trace_processor/util/descriptors.h" 41 42 #include "protos/perfetto/trace_processor/metrics_impl.pbzero.h" 43 44 namespace perfetto::trace_processor::metrics { 45 46 // A description of a SQL metric in C++. 47 struct SqlMetricFile { 48 // The path of this file with the root at the metrics root. 49 std::string path; 50 51 // The field in the output proto which will be filled by the result of 52 // querying the table specified by |output_table_name|. 53 // Optional because not all protos need to have a field associated with them 54 // in the root proto; most files will be just be run using RUN_METRIC by 55 // other files. 56 std::optional<std::string> proto_field_name; 57 58 // The table name which will be created by the SQL below to read the proto 59 // bytes from. 60 // Should only be set when |proto_field_name| is set. 61 std::optional<std::string> output_table_name; 62 63 // The SQL run by this metric. 64 std::string sql; 65 }; 66 67 // Helper class to build a nested (metric) proto checking the schema against 68 // a descriptor. 69 // Visible for testing. 70 class ProtoBuilder { 71 public: 72 ProtoBuilder(const DescriptorPool*, const ProtoDescriptor*); 73 74 base::Status AppendSqlValue(const std::string& field_name, 75 const SqlValue& value); 76 77 // Returns the serialized |protos::ProtoBuilderResult| with the built proto 78 // as the nested |protobuf| message. 79 // Note: no other functions should be called on this class after this method 80 // is called. 81 std::vector<uint8_t> SerializeToProtoBuilderResult(); 82 83 // Returns the serialized version of the raw message being built. 84 // This function should only be used at the top level where type checking is 85 // no longer important because the proto will be returned as is. In all other 86 // instances, prefer |SerializeToProtoBuilderResult()| instead. 87 // Note: no other functions should be called on this class after this method 88 // is called. 89 std::vector<uint8_t> SerializeRaw(); 90 91 private: 92 base::Status AppendSingleLong(const FieldDescriptor& field, int64_t value); 93 base::Status AppendSingleDouble(const FieldDescriptor& field, double value); 94 base::Status AppendSingleString(const FieldDescriptor& field, 95 base::StringView data); 96 base::Status AppendSingleBytes(const FieldDescriptor& field, 97 const uint8_t* ptr, 98 size_t size); 99 base::Status AppendRepeated(const FieldDescriptor& field, 100 const uint8_t* ptr, 101 size_t size); 102 103 base::StatusOr<const FieldDescriptor*> FindFieldByName( 104 const std::string& field_name); 105 106 const DescriptorPool* pool_ = nullptr; 107 const ProtoDescriptor* descriptor_ = nullptr; 108 protozero::HeapBuffered<protozero::Message> message_; 109 }; 110 111 // Helper class to combine a set of repeated fields into a single proto blob 112 // to return to SQLite. 113 // Visible for testing. 114 class RepeatedFieldBuilder { 115 public: 116 RepeatedFieldBuilder(); 117 118 base::Status AddSqlValue(SqlValue value); 119 120 // Returns the serialized |protos::ProtoBuilderResult| with the set of 121 // repeated fields as |repeated_values| in the proto. 122 // Note: no other functions should be called on this class after this method 123 // is called. 124 std::vector<uint8_t> SerializeToProtoBuilderResult(); 125 126 private: 127 base::Status AddLong(int64_t value); 128 base::Status AddDouble(double value); 129 base::Status AddString(base::StringView value); 130 base::Status AddBytes(const uint8_t* data, size_t size); 131 132 base::Status EnsureType(SqlValue::Type); 133 134 protozero::HeapBuffered<protos::pbzero::ProtoBuilderResult> message_; 135 std::optional<SqlValue::Type> repeated_field_type_; 136 protos::pbzero::RepeatedBuilderResult* repeated_ = nullptr; 137 protozero::PackedFixedSizeInt<int64_t> int64_packed_repeated_; 138 protozero::PackedFixedSizeInt<double> double_packed_repeated_; 139 }; 140 141 // Replaces templated variables inside |raw_text| using the substitution given 142 // by |substitutions| writing the result to |out|. 143 // The syntax followed is a cut-down variant of Jinja. This means variables that 144 // are to be replaced use {{variable-name}} in the raw text with subsitutions 145 // containing a mapping from (variable-name -> replacement). 146 int TemplateReplace( 147 const std::string& raw_text, 148 const std::unordered_map<std::string, std::string>& substitutions, 149 std::string* out); 150 151 // Implements the NULL_IF_EMPTY SQL function. 152 struct NullIfEmpty : public SqlFunction { 153 static base::Status Run(void* ctx, 154 size_t argc, 155 sqlite3_value** argv, 156 SqlValue& out, 157 Destructors&); 158 }; 159 160 // Implements all the proto creation functions. 161 struct BuildProto : public SqlFunction { 162 struct Context { 163 TraceProcessor* tp; 164 const DescriptorPool* pool; 165 uint32_t descriptor_idx; 166 }; 167 static base::Status Run(Context* ctx, 168 size_t argc, 169 sqlite3_value** argv, 170 SqlValue& out, 171 Destructors&); 172 }; 173 174 // Implements the RUN_METRIC SQL function. 175 struct RunMetric : public SqlFunction { 176 struct Context { 177 PerfettoSqlEngine* engine; 178 std::vector<SqlMetricFile>* metrics; 179 }; 180 static constexpr bool kVoidReturn = true; 181 static base::Status Run(Context* ctx, 182 size_t argc, 183 sqlite3_value** argv, 184 SqlValue& out, 185 Destructors&); 186 }; 187 188 // Implements the UNWRAP_METRIC_PROTO SQL function. 189 struct UnwrapMetricProto : public SqlFunction { 190 static base::Status Run(Context* ctx, 191 size_t argc, 192 sqlite3_value** argv, 193 SqlValue& out, 194 Destructors&); 195 }; 196 197 // These functions implement the RepeatedField SQL aggregate functions. 198 struct RepeatedField : public SqliteAggregateFunction<RepeatedField> { 199 static constexpr char kName[] = "RepeatedField"; 200 static constexpr int kArgCount = 1; 201 202 static void Step(sqlite3_context* ctx, int argc, sqlite3_value** argv); 203 static void Final(sqlite3_context* ctx); 204 }; 205 206 base::Status ComputeMetrics(PerfettoSqlEngine*, 207 const std::vector<std::string>& metrics_to_compute, 208 const std::vector<SqlMetricFile>& metrics, 209 const DescriptorPool& pool, 210 const ProtoDescriptor& root_descriptor, 211 std::vector<uint8_t>* metrics_proto); 212 213 } // namespace perfetto::trace_processor::metrics 214 215 #endif // SRC_TRACE_PROCESSOR_METRICS_METRICS_H_ 216