• 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/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