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