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