1 /* 2 * Copyright (C) 2020 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_DB_TYPED_COLUMN_H_ 18 #define SRC_TRACE_PROCESSOR_DB_TYPED_COLUMN_H_ 19 20 #include "src/trace_processor/db/column.h" 21 #include "src/trace_processor/db/typed_column_internal.h" 22 23 namespace perfetto { 24 namespace trace_processor { 25 26 // TypedColumn<T> 27 // 28 // Introduction: 29 // TypedColumn exists to allow efficient access to the data in a Column without 30 // having to go through dynamic type checking. There are two main reasons for 31 // this: 32 // 1. Performance: dynamic type checking is not free and so if this is used 33 // in a particularily hot codepath, the typechecking can be a significant 34 // overhead. 35 // 2. Ergonomics: having to convert back and forth from/to SqlValue causes 36 // signifcant clutter in parts of the code which can already be quite hard 37 // to follow (e.g. trackers like SequenceStackProfileTracker which perform 38 // cross checking of various ids). 39 // 40 // Implementation: 41 // TypedColumn is implemented as a memberless subclass of Column. This allows 42 // us to static cast from a Column* to a TypedColumn<T> where we know the 43 // type T. The methods of TypedColumn are type-specialized methods of Column 44 // which allow callers to pass real types instead of using SqlValue. 45 // 46 // There are two helper classes (tc_internal::TypeHandler and 47 // tc_internal::Serializer) where we specialize behaviour which needs to be 48 // different based on T. See their class documentation and below for details 49 // on their purpose. 50 template <typename T> 51 class TypedColumn : public Column { 52 private: 53 using TH = tc_internal::TypeHandler<T>; 54 55 public: 56 // The type of the data in this column. 57 using type = T; 58 59 // The non-optional type of the data in this column. 60 using non_optional_type = typename TH::non_optional_type; 61 62 // The type which should be passed to SqlValue functions. 63 using sql_value_type = typename TH::sql_value_type; 64 65 // The type of which is actually stored inside ColumnStorage. Can be different 66 // from T because we treat table ids to just be uint32_t inside the storage 67 // (handling ids would add an extra type to consider when filtering for no 68 // benefit. 69 using stored_type = typename TH::stored_type; 70 71 private: 72 using Serializer = tc_internal::Serializer<non_optional_type>; 73 74 public: 75 T operator[](uint32_t row) const { return GetAtIdx(overlay().Get(row)); } 76 77 // Special function only for string types to allow retrieving the string 78 // directly from the column. 79 template <bool is_string = TH::is_string> GetString(uint32_t row)80 typename std::enable_if<is_string, NullTermStringView>::type GetString( 81 uint32_t row) const { 82 return GetStringAtIdx(overlay().Get(row)); 83 } 84 85 // Sets the data in the column at index |row|. Set(uint32_t row,non_optional_type v)86 void Set(uint32_t row, non_optional_type v) { 87 SetAtIdx(overlay().Get(row), v); 88 } 89 90 // Inserts the value at the end of the column. Append(T v)91 void Append(T v) { mutable_storage()->Append(Serializer::Serialize(v)); } 92 93 // Returns the row containing the given value in the Column. IndexOf(sql_value_type v)94 std::optional<uint32_t> IndexOf(sql_value_type v) const { 95 return Column::IndexOf(ToSqlValue(v)); 96 } 97 ToVectorForTesting()98 std::vector<T> ToVectorForTesting() const { 99 std::vector<T> result(overlay().size()); 100 for (uint32_t i = 0; i < overlay().size(); ++i) 101 result[i] = (*this)[i]; 102 return result; 103 } 104 105 // Helper functions to create constraints for the given value. eq(sql_value_type v)106 Constraint eq(sql_value_type v) const { return eq_value(ToSqlValue(v)); } gt(sql_value_type v)107 Constraint gt(sql_value_type v) const { return gt_value(ToSqlValue(v)); } lt(sql_value_type v)108 Constraint lt(sql_value_type v) const { return lt_value(ToSqlValue(v)); } ne(sql_value_type v)109 Constraint ne(sql_value_type v) const { return ne_value(ToSqlValue(v)); } ge(sql_value_type v)110 Constraint ge(sql_value_type v) const { return ge_value(ToSqlValue(v)); } le(sql_value_type v)111 Constraint le(sql_value_type v) const { return le_value(ToSqlValue(v)); } 112 113 // Implements equality between two items of type |T|. Equals(T a,T b)114 static constexpr bool Equals(T a, T b) { return TH::Equals(a, b); } 115 116 // Encodes the default flags for a column of the current type. default_flags()117 static constexpr uint32_t default_flags() { 118 return TH::is_optional ? Flag::kNoFlag : Flag::kNonNull; 119 } 120 121 // Converts the static type T into the dynamic SqlValue type of this column. SqlValueType()122 static SqlValue::Type SqlValueType() { 123 return Column::ToSqlValueType<stored_type>(); 124 } 125 126 // Cast a Column to TypedColumn or crash if that is unsafe. FromColumn(Column * column)127 static TypedColumn<T>* FromColumn(Column* column) { 128 return FromColumnInternal<TypedColumn<T>>(column); 129 } 130 131 // Cast a Column to TypedColumn or crash if that is unsafe. FromColumn(const Column * column)132 static const TypedColumn<T>* FromColumn(const Column* column) { 133 return FromColumnInternal<const TypedColumn<T>>(column); 134 } 135 136 // Public for use by macro tables. SetAtIdx(uint32_t idx,non_optional_type v)137 void SetAtIdx(uint32_t idx, non_optional_type v) { 138 auto serialized = Serializer::Serialize(v); 139 mutable_storage()->Set(idx, serialized); 140 } 141 142 // Public for use by macro tables. GetAtIdx(uint32_t idx)143 T GetAtIdx(uint32_t idx) const { 144 return Serializer::Deserialize(TH::Get(storage(), idx)); 145 } 146 147 template <bool is_string = TH::is_string> GetStringAtIdx(uint32_t idx)148 typename std::enable_if<is_string, NullTermStringView>::type GetStringAtIdx( 149 uint32_t idx) const { 150 return string_pool().Get(storage().Get(idx)); 151 } 152 153 private: 154 friend class Table; 155 156 template <typename Output, typename Input> FromColumnInternal(Input * column)157 static Output* FromColumnInternal(Input* column) { 158 // While casting from a base to derived without constructing as a derived is 159 // technically UB, in practice, this is at the heart of protozero (see 160 // Message::BeginNestedMessage) so we use it here. 161 static_assert(sizeof(TypedColumn<T>) == sizeof(Column), 162 "TypedColumn cannot introduce extra state."); 163 164 if (column->template IsColumnType<stored_type>() && 165 (column->IsNullable() == TH::is_optional) && !column->IsId()) { 166 return static_cast<Output*>(column); 167 } else { 168 PERFETTO_FATAL("Unsafe to convert Column TypedColumn (%s)", 169 column->name()); 170 } 171 } 172 storage()173 const ColumnStorage<stored_type>& storage() const { 174 return Column::storage<stored_type>(); 175 } mutable_storage()176 ColumnStorage<stored_type>* mutable_storage() { 177 return Column::mutable_storage<stored_type>(); 178 } 179 }; 180 181 // Represents a column containing ids. 182 template <typename Id> 183 class IdColumn : public Column { 184 public: 185 // The type of the data in this column. 186 using type = Id; 187 188 // The underlying type used when comparing ids. 189 using stored_type = uint32_t; 190 191 Id operator[](uint32_t row) const { return Id(overlay().Get(row)); } 192 IndexOf(Id id)193 std::optional<uint32_t> IndexOf(Id id) const { 194 return overlay().RowOf(id.value); 195 } 196 197 // Public for use by macro tables. GetAtIdx(uint32_t idx)198 Id GetAtIdx(uint32_t idx) const { return Id(idx); } 199 200 // Static cast a Column to IdColumn or crash if that is likely to be 201 // unsafe. FromColumn(const Column * column)202 static const IdColumn<Id>* FromColumn(const Column* column) { 203 // While casting from a base to derived without constructing as a derived is 204 // technically UB, in practice, this is at the heart of protozero (see 205 // Message::BeginNestedMessage) so we use it here. 206 static_assert(sizeof(IdColumn<Id>) == sizeof(Column), 207 "TypedColumn cannot introduce extra state."); 208 209 if (column->IsId()) { 210 return static_cast<const IdColumn<Id>*>(column); 211 } else { 212 PERFETTO_FATAL("Unsafe to convert Column to IdColumn (%s)", 213 column->name()); 214 } 215 } 216 217 // Helper functions to create constraints for the given value. eq(uint32_t v)218 Constraint eq(uint32_t v) const { return eq_value(SqlValue::Long(v)); } gt(uint32_t v)219 Constraint gt(uint32_t v) const { return gt_value(SqlValue::Long(v)); } lt(uint32_t v)220 Constraint lt(uint32_t v) const { return lt_value(SqlValue::Long(v)); } ne(uint32_t v)221 Constraint ne(uint32_t v) const { return ne_value(SqlValue::Long(v)); } ge(uint32_t v)222 Constraint ge(uint32_t v) const { return ge_value(SqlValue::Long(v)); } le(uint32_t v)223 Constraint le(uint32_t v) const { return le_value(SqlValue::Long(v)); } 224 225 private: 226 friend class Table; 227 }; 228 229 } // namespace trace_processor 230 } // namespace perfetto 231 232 #endif // SRC_TRACE_PROCESSOR_DB_TYPED_COLUMN_H_ 233