• 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 INCLUDE_PERFETTO_TRACING_TRACK_EVENT_CATEGORY_REGISTRY_H_
18 #define INCLUDE_PERFETTO_TRACING_TRACK_EVENT_CATEGORY_REGISTRY_H_
19 
20 #include "perfetto/tracing/data_source.h"
21 
22 #include <stddef.h>
23 
24 #include <atomic>
25 #include <utility>
26 
27 namespace perfetto {
28 class DynamicCategory;
29 
30 // A compile-time representation of a track event category. See
31 // PERFETTO_DEFINE_CATEGORIES for registering your own categories.
32 struct PERFETTO_EXPORT_COMPONENT Category {
33   using Tags = std::array<const char*, 4>;
34 
35   const char* const name = nullptr;
36   const char* const description = nullptr;
37   const Tags tags = {};
38 
39   constexpr Category(const Category&) = default;
CategoryCategory40   constexpr explicit Category(const char* name_)
41       : name(CheckIsValidCategory(name_)),
42         name_sizes_(ComputeNameSizes(name_)) {}
43 
SetDescriptionCategory44   constexpr Category SetDescription(const char* description_) const {
45     return Category(name, description_, tags, name_sizes_);
46   }
47 
48   template <typename... Args>
SetTagsCategory49   constexpr Category SetTags(Args&&... args) const {
50     return Category(name, description, {std::forward<Args>(args)...},
51                     name_sizes_);
52   }
53 
54   // A comma separated list of multiple categories to be used in a single trace
55   // point.
GroupCategory56   static constexpr Category Group(const char* names) {
57     return Category(names, AllowGroup{});
58   }
59 
60   // Used for parsing dynamic category groups. Note that |name| and
61   // |DynamicCategory| must outlive the returned object because the category
62   // name isn't copied.
63   static Category FromDynamicCategory(const char* name);
64   static Category FromDynamicCategory(const DynamicCategory&);
65 
IsGroupCategory66   constexpr bool IsGroup() const { return GetNameSize(1) > 0; }
67 
68   // Returns the number of character in the category name. Not valid for
69   // category groups.
name_sizeCategory70   size_t name_size() const {
71     PERFETTO_DCHECK(!IsGroup());
72     return GetNameSize(0);
73   }
74 
75   // Iterates over all the members of this category group, or just the name of
76   // the category itself if this isn't a category group. Return false from
77   // |callback| to stop iteration.
78   template <typename T>
ForEachGroupMemberCategory79   void ForEachGroupMember(T callback) const {
80     const char* name_ptr = name;
81     size_t i = 0;
82     while (size_t name_size = GetNameSize(i++)) {
83       if (!callback(name_ptr, name_size))
84         break;
85       name_ptr += name_size + 1;
86     }
87   }
88 
89  private:
90   static constexpr size_t kMaxGroupSize = 4;
91   using NameSizes = std::array<uint8_t, kMaxGroupSize>;
92 
CategoryCategory93   constexpr Category(const char* name_,
94                      const char* description_,
95                      Tags tags_,
96                      NameSizes name_sizes)
97       : name(name_),
98         description(description_),
99         tags(tags_),
100         name_sizes_(name_sizes) {}
101 
102   enum AllowGroup {};
CategoryCategory103   constexpr Category(const char* name_, AllowGroup)
104       : name(CheckIsValidCategoryGroup(name_)),
105         name_sizes_(ComputeNameSizes(name_)) {}
106 
GetNameSizeCategory107   constexpr size_t GetNameSize(size_t i) const {
108     return i < name_sizes_.size() ? name_sizes_[i] : 0;
109   }
110 
ComputeNameSizesCategory111   static constexpr NameSizes ComputeNameSizes(const char* s) {
112     static_assert(kMaxGroupSize == 4, "Unexpected maximum category group size");
113     return NameSizes{{static_cast<uint8_t>(GetNthNameSize(0, s, s)),
114                       static_cast<uint8_t>(GetNthNameSize(1, s, s)),
115                       static_cast<uint8_t>(GetNthNameSize(2, s, s)),
116                       static_cast<uint8_t>(GetNthNameSize(3, s, s))}};
117   }
118 
119   static constexpr ptrdiff_t GetNthNameSize(int n,
120                                             const char* start,
121                                             const char* end,
122                                             int counter = 0) {
123     return (!*end || *end == ',')
124                ? ((!*end || counter == n)
125                       ? (counter == n ? end - start : 0)
126                       : GetNthNameSize(n, end + 1, end + 1, counter + 1))
127                : GetNthNameSize(n, start, end + 1, counter);
128   }
129 
CheckIsValidCategoryCategory130   static constexpr const char* CheckIsValidCategory(const char* n) {
131     // We just replace invalid input with a nullptr here; it will trigger a
132     // static assert in TrackEventCategoryRegistry::ValidateCategories().
133     return GetNthNameSize(1, n, n) ? nullptr : n;
134   }
135 
CheckIsValidCategoryGroupCategory136   static constexpr const char* CheckIsValidCategoryGroup(const char* n) {
137     // Same as above: replace invalid input with nullptr.
138     return !GetNthNameSize(1, n, n) || GetNthNameSize(kMaxGroupSize, n, n)
139                ? nullptr
140                : n;
141   }
142 
143   // An array of lengths of the different names associated with this category.
144   // If this category doesn't represent a group of multiple categories, only the
145   // first element is non-zero.
146   const NameSizes name_sizes_ = {};
147 };
148 
149 // Dynamically constructed category names should marked as such through this
150 // container type to make it less likely for trace points to accidentally start
151 // using dynamic categories. Events with dynamic categories will always be
152 // slightly more expensive than regular events, so use them sparingly.
153 class PERFETTO_EXPORT_COMPONENT DynamicCategory final {
154  public:
DynamicCategory(const std::string & name_)155   explicit DynamicCategory(const std::string& name_) : name(name_) {}
DynamicCategory(const char * name_)156   explicit DynamicCategory(const char* name_) : name(name_) {}
DynamicCategory()157   DynamicCategory() {}
158   ~DynamicCategory() = default;
159 
160   DynamicCategory(const DynamicCategory&) = default;
161   DynamicCategory& operator=(const DynamicCategory&) = delete;
162 
163   DynamicCategory(DynamicCategory&&) = default;
164   DynamicCategory& operator=(DynamicCategory&&) = delete;
165 
166   const std::string name;
167 };
168 
169 namespace internal {
170 
NullCategory(const char *)171 constexpr const char* NullCategory(const char*) {
172   return nullptr;
173 }
174 
175 perfetto::DynamicCategory NullCategory(const perfetto::DynamicCategory&);
176 
StringMatchesPrefix(const char * str,const char * prefix)177 constexpr bool StringMatchesPrefix(const char* str, const char* prefix) {
178   return !*str ? !*prefix
179                : !*prefix ? true
180                           : *str != *prefix
181                                 ? false
182                                 : StringMatchesPrefix(str + 1, prefix + 1);
183 }
184 
IsStringInPrefixList(const char *)185 constexpr bool IsStringInPrefixList(const char*) {
186   return false;
187 }
188 
189 template <typename... Args>
IsStringInPrefixList(const char * str,const char * prefix,Args...args)190 constexpr bool IsStringInPrefixList(const char* str,
191                                     const char* prefix,
192                                     Args... args) {
193   return StringMatchesPrefix(str, prefix) ||
194          IsStringInPrefixList(str, std::forward<Args>(args)...);
195 }
196 
197 // Holds all the registered categories for one category namespace. See
198 // PERFETTO_DEFINE_CATEGORIES for building the registry.
199 class PERFETTO_EXPORT_COMPONENT TrackEventCategoryRegistry {
200  public:
TrackEventCategoryRegistry(size_t category_count,const Category * categories,std::atomic<uint8_t> * state_storage)201   constexpr TrackEventCategoryRegistry(size_t category_count,
202                                        const Category* categories,
203                                        std::atomic<uint8_t>* state_storage)
204       : categories_(categories),
205         category_count_(category_count),
206         state_storage_(state_storage) {
207     static_assert(
208         sizeof(state_storage[0].load()) * 8 >= kMaxDataSourceInstances,
209         "The category state must have enough bits for all possible data source "
210         "instances");
211   }
212 
category_count()213   size_t category_count() const { return category_count_; }
214 
215   // Returns a category based on its index.
GetCategory(size_t index)216   const Category* GetCategory(size_t index) const {
217     PERFETTO_DCHECK(index < category_count_);
218     return &categories_[index];
219   }
220 
221   // Turn tracing on or off for the given category in a track event data source
222   // instance.
223   void EnableCategoryForInstance(size_t category_index,
224                                  uint32_t instance_index) const;
225   void DisableCategoryForInstance(size_t category_index,
226                                   uint32_t instance_index) const;
227 
GetCategoryState(size_t category_index)228   constexpr std::atomic<uint8_t>* GetCategoryState(
229       size_t category_index) const {
230     return &state_storage_[category_index];
231   }
232 
233   // --------------------------------------------------------------------------
234   // Trace point support
235   // --------------------------------------------------------------------------
236   //
237   // (The following methods are used by the track event trace point
238   // implementation and typically don't need to be called by other code.)
239 
240   // At compile time, turn a category name into an index into the registry.
241   // Returns kInvalidCategoryIndex if the category was not found, or
242   // kDynamicCategoryIndex if |is_dynamic| is true or a DynamicCategory was
243   // passed in.
244   static constexpr size_t kInvalidCategoryIndex = static_cast<size_t>(-1);
245   static constexpr size_t kDynamicCategoryIndex = static_cast<size_t>(-2);
Find(const char * name,bool is_dynamic)246   constexpr size_t Find(const char* name, bool is_dynamic) const {
247     return CheckIsValidCategoryIndex(FindImpl(name, is_dynamic));
248   }
249 
Find(const DynamicCategory &,bool)250   constexpr size_t Find(const DynamicCategory&, bool) const {
251     return kDynamicCategoryIndex;
252   }
253 
254   constexpr bool ValidateCategories(size_t index = 0) const {
255     return (index == category_count_)
256                ? true
257                : IsValidCategoryName(categories_[index].name)
258                      ? ValidateCategories(index + 1)
259                      : false;
260   }
261 
262  private:
263   // TODO(skyostil): Make the compile-time routines nicer with C++14.
264   constexpr size_t FindImpl(const char* name,
265                             bool is_dynamic,
266                             size_t index = 0) const {
267     return is_dynamic ? kDynamicCategoryIndex
268                       : (index == category_count_)
269                             ? kInvalidCategoryIndex
270                             : StringEq(categories_[index].name, name)
271                                   ? index
272                                   : FindImpl(name, false, index + 1);
273   }
274 
275   // A compile time helper for checking that a category index is valid.
CheckIsValidCategoryIndex(size_t index)276   static constexpr size_t CheckIsValidCategoryIndex(size_t index) {
277     // Relies on PERFETTO_CHECK() (and the surrounding lambda) being a
278     // non-constexpr function, which will fail the build if the given |index| is
279     // invalid. The funny formatting here is so that clang shows the comment
280     // below as part of the error message.
281     // clang-format off
282     return index != kInvalidCategoryIndex ? index : \
283         /* Invalid category -- add it to PERFETTO_DEFINE_CATEGORIES(). */ [] {
284         PERFETTO_CHECK(
285             false &&
286             "A track event used an unknown category. Please add it to "
287             "PERFETTO_DEFINE_CATEGORIES().");
288         return kInvalidCategoryIndex;
289       }();
290     // clang-format on
291   }
292 
IsValidCategoryName(const char * name)293   static constexpr bool IsValidCategoryName(const char* name) {
294     return (!name || *name == '\"' || *name == '*' || *name == ' ')
295                ? false
296                : *name ? IsValidCategoryName(name + 1) : true;
297   }
298 
StringEq(const char * a,const char * b)299   static constexpr bool StringEq(const char* a, const char* b) {
300     return *a != *b ? false
301                     : (!*a || !*b) ? (*a == *b) : StringEq(a + 1, b + 1);
302   }
303 
304   const Category* const categories_;
305   const size_t category_count_;
306   std::atomic<uint8_t>* const state_storage_;
307 };
308 
309 }  // namespace internal
310 }  // namespace perfetto
311 
312 #endif  // INCLUDE_PERFETTO_TRACING_TRACK_EVENT_CATEGORY_REGISTRY_H_
313