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