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