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_IMPORTERS_COMMON_GLOBAL_ARGS_TRACKER_H_ 18 #define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_GLOBAL_ARGS_TRACKER_H_ 19 20 #include <cstdint> 21 #include <type_traits> 22 #include <vector> 23 #include "perfetto/base/logging.h" 24 #include "perfetto/ext/base/flat_hash_map.h" 25 #include "perfetto/ext/base/hash.h" 26 #include "perfetto/ext/base/small_vector.h" 27 #include "src/trace_processor/db/column.h" 28 #include "src/trace_processor/storage/trace_storage.h" 29 #include "src/trace_processor/tables/metadata_tables_py.h" 30 #include "src/trace_processor/types/variadic.h" 31 32 namespace perfetto { 33 namespace trace_processor { 34 35 // Interns args into the storage from all ArgsTrackers across trace processor. 36 // Note: most users will want to use ArgsTracker to push args to the strorage 37 // and not this class. This class is really intended for ArgsTracker to use for 38 // that purpose. 39 class GlobalArgsTracker { 40 public: 41 // How to behave if two or more args with the same key were added into the 42 // same ArgSet. If |kSkipIfExists|, the arg will be ignored if another arg 43 // with the same key already exists. If |kAddOrUpdate|, any existing arg with 44 // the same key will be overridden. 45 enum class UpdatePolicy { kSkipIfExists, kAddOrUpdate }; 46 47 struct CompactArg { 48 StringId flat_key = kNullStringId; 49 StringId key = kNullStringId; 50 Variadic value = Variadic::Integer(0); 51 UpdatePolicy update_policy = UpdatePolicy::kAddOrUpdate; 52 }; 53 static_assert(std::is_trivially_destructible<CompactArg>::value, 54 "Args must be trivially destructible"); 55 56 struct Arg : public CompactArg { 57 ColumnLegacy* column; 58 uint32_t row; 59 60 // Object slices this Arg to become a CompactArg. ToCompactArgArg61 CompactArg ToCompactArg() const { return CompactArg(*this); } 62 }; 63 static_assert(std::is_trivially_destructible<Arg>::value, 64 "Args must be trivially destructible"); 65 66 struct ArgHasher { operatorArgHasher67 uint64_t operator()(const CompactArg& arg) const noexcept { 68 base::Hasher hash; 69 hash.Update(arg.key.raw_id()); 70 // We don't hash arg.flat_key because it's a subsequence of arg.key. 71 switch (arg.value.type) { 72 case Variadic::Type::kInt: 73 hash.Update(arg.value.int_value); 74 break; 75 case Variadic::Type::kUint: 76 hash.Update(arg.value.uint_value); 77 break; 78 case Variadic::Type::kString: 79 hash.Update(arg.value.string_value.raw_id()); 80 break; 81 case Variadic::Type::kReal: 82 hash.Update(arg.value.real_value); 83 break; 84 case Variadic::Type::kPointer: 85 hash.Update(arg.value.pointer_value); 86 break; 87 case Variadic::Type::kBool: 88 hash.Update(arg.value.bool_value); 89 break; 90 case Variadic::Type::kJson: 91 hash.Update(arg.value.json_value.raw_id()); 92 break; 93 case Variadic::Type::kNull: 94 hash.Update(0); 95 break; 96 } 97 return hash.digest(); 98 } 99 }; 100 101 explicit GlobalArgsTracker(TraceStorage* storage); 102 AddArgSet(const std::vector<Arg> & args,uint32_t begin,uint32_t end)103 ArgSetId AddArgSet(const std::vector<Arg>& args, 104 uint32_t begin, 105 uint32_t end) { 106 return AddArgSet(args.data() + begin, args.data() + end, sizeof(Arg)); 107 } AddArgSet(Arg * args,uint32_t begin,uint32_t end)108 ArgSetId AddArgSet(Arg* args, uint32_t begin, uint32_t end) { 109 return AddArgSet(args + begin, args + end, sizeof(Arg)); 110 } AddArgSet(CompactArg * args,uint32_t begin,uint32_t end)111 ArgSetId AddArgSet(CompactArg* args, uint32_t begin, uint32_t end) { 112 return AddArgSet(args + begin, args + end, sizeof(CompactArg)); 113 } 114 115 private: 116 using ArgSetHash = uint64_t; 117 118 // Assumes that the interval [begin, end) of |args| has args with the same key 119 // grouped together. AddArgSet(const void * start,const void * end,uint32_t stride)120 ArgSetId AddArgSet(const void* start, const void* end, uint32_t stride) { 121 base::SmallVector<const CompactArg*, 64> valid; 122 123 // TODO(eseckler): Also detect "invalid" key combinations in args sets (e.g. 124 // "foo" and "foo.bar" in the same arg set)? 125 for (const void* ptr = start; ptr != end; 126 ptr = reinterpret_cast<const uint8_t*>(ptr) + stride) { 127 const auto& arg = *reinterpret_cast<const CompactArg*>(ptr); 128 if (!valid.empty() && valid.back()->key == arg.key) { 129 // Last arg had the same key as this one. In case of kSkipIfExists, skip 130 // this arg. In case of kAddOrUpdate, remove the last arg and add this 131 // arg instead. 132 if (arg.update_policy == UpdatePolicy::kSkipIfExists) { 133 continue; 134 } 135 PERFETTO_DCHECK(arg.update_policy == UpdatePolicy::kAddOrUpdate); 136 valid.pop_back(); 137 } 138 valid.emplace_back(&arg); 139 } 140 141 base::Hasher hash; 142 for (const auto* it : valid) { 143 hash.Update(ArgHasher()(*it)); 144 } 145 146 auto& arg_table = *storage_->mutable_arg_table(); 147 148 ArgSetHash digest = hash.digest(); 149 auto it_and_inserted = 150 arg_row_for_hash_.Insert(digest, arg_table.row_count()); 151 if (!it_and_inserted.second) { 152 // Already inserted. 153 return arg_table[*it_and_inserted.first].arg_set_id(); 154 } 155 156 // Taking size() after the Insert() ensures that nothing has an id == 0 157 // (0 == kInvalidArgSetId). 158 auto id = static_cast<uint32_t>(arg_row_for_hash_.size()); 159 for (const CompactArg* ptr : valid) { 160 const auto& arg = *ptr; 161 tables::ArgTable::Row row; 162 row.arg_set_id = id; 163 row.flat_key = arg.flat_key; 164 row.key = arg.key; 165 switch (arg.value.type) { 166 case Variadic::Type::kInt: 167 row.int_value = arg.value.int_value; 168 break; 169 case Variadic::Type::kUint: 170 row.int_value = static_cast<int64_t>(arg.value.uint_value); 171 break; 172 case Variadic::Type::kString: 173 row.string_value = arg.value.string_value; 174 break; 175 case Variadic::Type::kReal: 176 row.real_value = arg.value.real_value; 177 break; 178 case Variadic::Type::kPointer: 179 row.int_value = static_cast<int64_t>(arg.value.pointer_value); 180 break; 181 case Variadic::Type::kBool: 182 row.int_value = arg.value.bool_value; 183 break; 184 case Variadic::Type::kJson: 185 row.string_value = arg.value.json_value; 186 break; 187 case Variadic::Type::kNull: 188 break; 189 } 190 row.value_type = storage_->GetIdForVariadicType(arg.value.type); 191 arg_table.Insert(row); 192 } 193 return id; 194 } 195 196 base::FlatHashMap<ArgSetHash, uint32_t, base::AlreadyHashed<ArgSetHash>> 197 arg_row_for_hash_; 198 199 TraceStorage* storage_; 200 }; 201 202 } // namespace trace_processor 203 } // namespace perfetto 204 205 #endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_GLOBAL_ARGS_TRACKER_H_ 206