• 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 <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     uint32_t arg_set_id = arg_table.row_count();
149     ArgSetHash digest = hash.digest();
150     auto [it, inserted] = arg_row_for_hash_.Insert(digest, arg_set_id);
151     if (!inserted) {
152       // Already inserted.
153       return *it;
154     }
155 
156     for (const CompactArg* ptr : valid) {
157       const auto& arg = *ptr;
158       tables::ArgTable::Row row;
159       row.arg_set_id = arg_set_id;
160       row.flat_key = arg.flat_key;
161       row.key = arg.key;
162       switch (arg.value.type) {
163         case Variadic::Type::kInt:
164           row.int_value = arg.value.int_value;
165           break;
166         case Variadic::Type::kUint:
167           row.int_value = static_cast<int64_t>(arg.value.uint_value);
168           break;
169         case Variadic::Type::kString:
170           row.string_value = arg.value.string_value;
171           break;
172         case Variadic::Type::kReal:
173           row.real_value = arg.value.real_value;
174           break;
175         case Variadic::Type::kPointer:
176           row.int_value = static_cast<int64_t>(arg.value.pointer_value);
177           break;
178         case Variadic::Type::kBool:
179           row.int_value = arg.value.bool_value;
180           break;
181         case Variadic::Type::kJson:
182           row.string_value = arg.value.json_value;
183           break;
184         case Variadic::Type::kNull:
185           break;
186       }
187       row.value_type = storage_->GetIdForVariadicType(arg.value.type);
188       arg_table.Insert(row);
189     }
190     return arg_set_id;
191   }
192 
193   base::FlatHashMap<ArgSetHash, uint32_t, base::AlreadyHashed<ArgSetHash>>
194       arg_row_for_hash_;
195 
196   TraceStorage* storage_;
197 };
198 
199 }  // namespace trace_processor
200 }  // namespace perfetto
201 
202 #endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_GLOBAL_ARGS_TRACKER_H_
203