• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
6 #define BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
7 
8 #include <stdint.h>
9 
10 #include <array>
11 #include <deque>
12 #include <list>
13 #include <map>
14 #include <memory>
15 #include <queue>
16 #include <set>
17 #include <stack>
18 #include <string>
19 #include <type_traits>
20 #include <unordered_map>
21 #include <unordered_set>
22 #include <vector>
23 
24 #include "base/base_export.h"
25 #include "base/containers/circular_deque.h"
26 #include "base/containers/flat_map.h"
27 #include "base/containers/flat_set.h"
28 #include "base/containers/linked_list.h"
29 #include "base/containers/lru_cache.h"
30 #include "base/containers/queue.h"
31 #include "base/memory/raw_ptr.h"
32 #include "base/stl_util.h"
33 #include "base/template_util.h"
34 
35 // Composable memory usage estimators.
36 //
37 // This file defines set of EstimateMemoryUsage(object) functions that return
38 // approximate dynamically allocated memory usage of their argument.
39 //
40 // The ultimate goal is to make memory usage estimation for a class simply a
41 // matter of aggregating EstimateMemoryUsage() results over all fields.
42 //
43 // That is achieved via composability: if EstimateMemoryUsage() is defined
44 // for T then EstimateMemoryUsage() is also defined for any combination of
45 // containers holding T (e.g. std::map<int, std::vector<T>>).
46 //
47 // There are two ways of defining EstimateMemoryUsage() for a type:
48 //
49 // 1. As a global function 'size_t EstimateMemoryUsage(T)' in
50 //    in base::trace_event namespace.
51 //
52 // 2. As 'size_t T::EstimateMemoryUsage() const' method. In this case
53 //    EstimateMemoryUsage(T) function in base::trace_event namespace is
54 //    provided automatically.
55 //
56 // Here is an example implementation:
57 //
58 // class MyClass {
59 //   ...
60 //   ...
61 //   size_t EstimateMemoryUsage() const {
62 //     return base::trace_event::EstimateMemoryUsage(set_) +
63 //            base::trace_event::EstimateMemoryUsage(name_) +
64 //            base::trace_event::EstimateMemoryUsage(foo_);
65 //   }
66 //   ...
67 //  private:
68 //   ...
69 //   std::set<int> set_;
70 //   std::string name_;
71 //   Foo foo_;
72 //   int id_;
73 //   bool success_;
74 // }
75 //
76 // The approach is simple: first call EstimateMemoryUsage() on all members,
77 // then recursively fix compilation errors that are caused by types not
78 // implementing EstimateMemoryUsage().
79 
80 namespace base {
81 namespace trace_event {
82 
83 // Declarations
84 
85 // If T declares 'EstimateMemoryUsage() const' member function, then
86 // global function EstimateMemoryUsage(T) is available, and just calls
87 // the member function.
88 template <class T>
89 auto EstimateMemoryUsage(const T& object)
90     -> decltype(object.EstimateMemoryUsage());
91 
92 // String
93 
94 template <class C, class T, class A>
95 size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string);
96 
97 // Arrays
98 
99 template <class T, size_t N>
100 size_t EstimateMemoryUsage(const std::array<T, N>& array);
101 
102 template <class T, size_t N>
103 size_t EstimateMemoryUsage(T (&array)[N]);
104 
105 template <class T>
106 size_t EstimateMemoryUsage(const T* array, size_t array_length);
107 
108 // std::unique_ptr
109 
110 template <class T, class D>
111 size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr);
112 
113 template <class T, class D>
114 size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
115                            size_t array_length);
116 
117 // std::shared_ptr
118 
119 template <class T>
120 size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr);
121 
122 // Containers
123 
124 template <class F, class S>
125 size_t EstimateMemoryUsage(const std::pair<F, S>& pair);
126 
127 template <class T, class A>
128 size_t EstimateMemoryUsage(const std::vector<T, A>& vector);
129 
130 template <class T, class A>
131 size_t EstimateMemoryUsage(const std::list<T, A>& list);
132 
133 template <class T>
134 size_t EstimateMemoryUsage(const base::LinkedList<T>& list);
135 
136 template <class T, class C, class A>
137 size_t EstimateMemoryUsage(const std::set<T, C, A>& set);
138 
139 template <class T, class C, class A>
140 size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set);
141 
142 template <class K, class V, class C, class A>
143 size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map);
144 
145 template <class K, class V, class C, class A>
146 size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map);
147 
148 template <class T, class H, class KE, class A>
149 size_t EstimateMemoryUsage(const std::unordered_set<T, H, KE, A>& set);
150 
151 template <class T, class H, class KE, class A>
152 size_t EstimateMemoryUsage(const std::unordered_multiset<T, H, KE, A>& set);
153 
154 template <class K, class V, class H, class KE, class A>
155 size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map);
156 
157 template <class K, class V, class H, class KE, class A>
158 size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map);
159 
160 template <class T, class A>
161 size_t EstimateMemoryUsage(const std::deque<T, A>& deque);
162 
163 template <class T, class C>
164 size_t EstimateMemoryUsage(const std::queue<T, C>& queue);
165 
166 template <class T, class C>
167 size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue);
168 
169 template <class T, class C>
170 size_t EstimateMemoryUsage(const std::stack<T, C>& stack);
171 
172 template <class T>
173 size_t EstimateMemoryUsage(const base::circular_deque<T>& deque);
174 
175 template <class T, class C>
176 size_t EstimateMemoryUsage(const base::flat_set<T, C>& set);
177 
178 template <class K, class V, class C>
179 size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map);
180 
181 template <class K, class V, class C>
182 size_t EstimateMemoryUsage(const base::LRUCache<K, V, C>& lru);
183 
184 template <class K, class V, class C>
185 size_t EstimateMemoryUsage(const base::HashingLRUCache<K, V, C>& lru);
186 
187 template <class V, class C>
188 size_t EstimateMemoryUsage(const base::LRUCacheSet<V, C>& lru);
189 
190 template <class V, class C>
191 size_t EstimateMemoryUsage(const base::HashingLRUCacheSet<V, C>& lru);
192 
193 // TODO(dskiba):
194 //   std::forward_list
195 
196 // Definitions
197 
198 namespace internal {
199 
200 // HasEMU<T>::value is true iff EstimateMemoryUsage(T) is available.
201 // (This is the default version, which is false.)
202 template <class T, class X = void>
203 struct HasEMU : std::false_type {};
204 
205 // This HasEMU specialization is only picked up if there exists function
206 // EstimateMemoryUsage(const T&) that returns size_t. Simpler ways to
207 // achieve this don't work on MSVC.
208 template <class T>
209 struct HasEMU<
210     T,
211     typename std::enable_if<std::is_same<
212         size_t,
213         decltype(EstimateMemoryUsage(std::declval<const T&>()))>::value>::type>
214     : std::true_type {};
215 
216 // EMUCaller<T> does three things:
217 // 1. Defines Call() method that calls EstimateMemoryUsage(T) if it's
218 //    available.
219 // 2. If EstimateMemoryUsage(T) is not available, but T has trivial dtor
220 //    (i.e. it's POD, integer, pointer, enum, etc.) then it defines Call()
221 //    method that returns 0. This is useful for containers, which allocate
222 //    memory regardless of T (also for cases like std::map<int, MyClass>).
223 // 3. Finally, if EstimateMemoryUsage(T) is not available, then it triggers
224 //    a static_assert with a helpful message. That cuts numbers of errors
225 //    considerably - if you just call EstimateMemoryUsage(T) but it's not
226 //    available for T, then compiler will helpfully list *all* possible
227 //    variants of it, with an explanation for each.
228 template <class T, class X = void>
229 struct EMUCaller {
230   // std::is_same<> below makes static_assert depend on T, in order to
231   // prevent it from asserting regardless instantiation.
232   static_assert(std::is_same<T, std::false_type>::value,
233                 "Neither global function 'size_t EstimateMemoryUsage(T)' "
234                 "nor member function 'size_t T::EstimateMemoryUsage() const' "
235                 "is defined for the type.");
236 
237   static size_t Call(const T&) { return 0; }
238 };
239 
240 template <class T>
241 struct EMUCaller<T, typename std::enable_if<HasEMU<T>::value>::type> {
242   static size_t Call(const T& value) { return EstimateMemoryUsage(value); }
243 };
244 
245 template <template <class...> class Container, class I, class = void>
246 struct IsComplexIteratorForContainer : std::false_type {};
247 
248 template <template <class...> class Container, class I>
249 struct IsComplexIteratorForContainer<
250     Container,
251     I,
252     std::enable_if_t<!std::is_pointer<I>::value &&
253                      base::internal::is_iterator<I>::value>> {
254   using value_type = typename std::iterator_traits<I>::value_type;
255   using container_type = Container<value_type>;
256 
257   // We use enum instead of static constexpr bool, beause we don't have inline
258   // variables until c++17.
259   //
260   // The downside is - value is not of type bool.
261   enum : bool {
262     value =
263         std::is_same<typename container_type::iterator, I>::value ||
264         std::is_same<typename container_type::const_iterator, I>::value ||
265         std::is_same<typename container_type::reverse_iterator, I>::value ||
266         std::is_same<typename container_type::const_reverse_iterator, I>::value,
267   };
268 };
269 
270 template <class I, template <class...> class... Containers>
271 constexpr bool OneOfContainersComplexIterators() {
272   // We are forced to create a temporary variable to workaround a compilation
273   // error in msvs.
274   const bool all_tests[] = {
275       IsComplexIteratorForContainer<Containers, I>::value...};
276   for (bool test : all_tests)
277     if (test)
278       return true;
279   return false;
280 }
281 
282 // std::array has an extra required template argument. We curry it.
283 template <class T>
284 using array_test_helper = std::array<T, 1>;
285 
286 template <class I>
287 constexpr bool IsStandardContainerComplexIterator() {
288   // TODO(dyaroshev): deal with maps iterators if there is a need.
289   // It requires to parse pairs into keys and values.
290   // TODO(dyaroshev): deal with unordered containers: they do not have reverse
291   // iterators.
292   return OneOfContainersComplexIterators<
293       I, array_test_helper, std::vector, std::deque,
294       /*std::forward_list,*/ std::list, std::set, std::multiset>();
295 }
296 
297 // Work around MSVS bug. For some reason constexpr function doesn't work.
298 // However variable template does.
299 template <typename T>
300 constexpr bool IsKnownNonAllocatingType_v =
301     std::is_trivially_destructible<T>::value ||
302     IsStandardContainerComplexIterator<T>();
303 
304 template <class T>
305 struct EMUCaller<
306     T,
307     std::enable_if_t<!HasEMU<T>::value && IsKnownNonAllocatingType_v<T>>> {
308   static size_t Call(const T& value) { return 0; }
309 };
310 
311 }  // namespace internal
312 
313 // Proxy that deducts T and calls EMUCaller<T>.
314 // To be used by EstimateMemoryUsage() implementations for containers.
315 template <class T>
316 size_t EstimateItemMemoryUsage(const T& value) {
317   return internal::EMUCaller<T>::Call(value);
318 }
319 
320 template <class I>
321 size_t EstimateIterableMemoryUsage(const I& iterable) {
322   size_t memory_usage = 0;
323   for (const auto& item : iterable) {
324     memory_usage += EstimateItemMemoryUsage(item);
325   }
326   return memory_usage;
327 }
328 
329 // Global EstimateMemoryUsage(T) that just calls T::EstimateMemoryUsage().
330 template <class T>
331 auto EstimateMemoryUsage(const T& object)
332     -> decltype(object.EstimateMemoryUsage()) {
333   static_assert(
334       std::is_same<decltype(object.EstimateMemoryUsage()), size_t>::value,
335       "'T::EstimateMemoryUsage() const' must return size_t.");
336   return object.EstimateMemoryUsage();
337 }
338 
339 // String
340 
341 template <class C, class T, class A>
342 size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) {
343   using string_type = std::basic_string<C, T, A>;
344   using value_type = typename string_type::value_type;
345   // C++11 doesn't leave much room for implementors - std::string can
346   // use short string optimization, but that's about it. We detect SSO
347   // by checking that c_str() points inside |string|.
348   const uint8_t* cstr = reinterpret_cast<const uint8_t*>(string.c_str());
349   const uint8_t* inline_cstr = reinterpret_cast<const uint8_t*>(&string);
350   if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) {
351     // SSO string
352     return 0;
353   }
354   return (string.capacity() + 1) * sizeof(value_type);
355 }
356 
357 // Use explicit instantiations from the .cc file (reduces bloat).
358 extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::string&);
359 extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::u16string&);
360 
361 // Arrays
362 
363 template <class T, size_t N>
364 size_t EstimateMemoryUsage(const std::array<T, N>& array) {
365   return EstimateIterableMemoryUsage(array);
366 }
367 
368 template <class T, size_t N>
369 size_t EstimateMemoryUsage(T (&array)[N]) {
370   return EstimateIterableMemoryUsage(array);
371 }
372 
373 template <class T>
374 size_t EstimateMemoryUsage(const T* array, size_t array_length) {
375   size_t memory_usage = sizeof(T) * array_length;
376   for (size_t i = 0; i != array_length; ++i) {
377     memory_usage += EstimateItemMemoryUsage(array[i]);
378   }
379   return memory_usage;
380 }
381 
382 // std::unique_ptr
383 
384 template <class T, class D>
385 size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr) {
386   return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0;
387 }
388 
389 template <class T, class D>
390 size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
391                            size_t array_length) {
392   return EstimateMemoryUsage(array.get(), array_length);
393 }
394 
395 // std::shared_ptr
396 
397 template <class T>
398 size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr) {
399   auto use_count = ptr.use_count();
400   if (use_count == 0) {
401     return 0;
402   }
403   // Model shared_ptr after libc++,
404   // see __shared_ptr_pointer from include/memory
405   struct SharedPointer {
406     raw_ptr<void> vtbl;
407     long shared_owners;
408     long shared_weak_owners;
409     raw_ptr<T> value;
410   };
411   // If object of size S shared N > S times we prefer to (potentially)
412   // overestimate than to return 0.
413   return sizeof(SharedPointer) +
414          (EstimateItemMemoryUsage(*ptr) + (use_count - 1)) / use_count;
415 }
416 
417 // std::pair
418 
419 template <class F, class S>
420 size_t EstimateMemoryUsage(const std::pair<F, S>& pair) {
421   return EstimateItemMemoryUsage(pair.first) +
422          EstimateItemMemoryUsage(pair.second);
423 }
424 
425 // std::vector
426 
427 template <class T, class A>
428 size_t EstimateMemoryUsage(const std::vector<T, A>& vector) {
429   return sizeof(T) * vector.capacity() + EstimateIterableMemoryUsage(vector);
430 }
431 
432 // std::list
433 
434 template <class T, class A>
435 size_t EstimateMemoryUsage(const std::list<T, A>& list) {
436   using value_type = typename std::list<T, A>::value_type;
437   struct Node {
438     raw_ptr<Node> prev;
439     raw_ptr<Node> next;
440     value_type value;
441   };
442   return sizeof(Node) * list.size() +
443          EstimateIterableMemoryUsage(list);
444 }
445 
446 template <class T>
447 size_t EstimateMemoryUsage(const base::LinkedList<T>& list) {
448   size_t memory_usage = 0u;
449   for (base::LinkNode<T>* node = list.head(); node != list.end();
450        node = node->next()) {
451     // Since we increment by calling node = node->next() we know that node
452     // isn't nullptr.
453     memory_usage += EstimateMemoryUsage(*node->value()) + sizeof(T);
454   }
455   return memory_usage;
456 }
457 
458 // Tree containers
459 
460 template <class V>
461 size_t EstimateTreeMemoryUsage(size_t size) {
462   // Tree containers are modeled after libc++
463   // (__tree_node from include/__tree)
464   struct Node {
465     raw_ptr<Node> left;
466     raw_ptr<Node> right;
467     raw_ptr<Node> parent;
468     bool is_black;
469     V value;
470   };
471   return sizeof(Node) * size;
472 }
473 
474 template <class T, class C, class A>
475 size_t EstimateMemoryUsage(const std::set<T, C, A>& set) {
476   using value_type = typename std::set<T, C, A>::value_type;
477   return EstimateTreeMemoryUsage<value_type>(set.size()) +
478          EstimateIterableMemoryUsage(set);
479 }
480 
481 template <class T, class C, class A>
482 size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) {
483   using value_type = typename std::multiset<T, C, A>::value_type;
484   return EstimateTreeMemoryUsage<value_type>(set.size()) +
485          EstimateIterableMemoryUsage(set);
486 }
487 
488 template <class K, class V, class C, class A>
489 size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) {
490   using value_type = typename std::map<K, V, C, A>::value_type;
491   return EstimateTreeMemoryUsage<value_type>(map.size()) +
492          EstimateIterableMemoryUsage(map);
493 }
494 
495 template <class K, class V, class C, class A>
496 size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) {
497   using value_type = typename std::multimap<K, V, C, A>::value_type;
498   return EstimateTreeMemoryUsage<value_type>(map.size()) +
499          EstimateIterableMemoryUsage(map);
500 }
501 
502 // HashMap containers
503 
504 namespace internal {
505 
506 // While hashtable containers model doesn't depend on STL implementation, one
507 // detail still crept in: bucket_count. It's used in size estimation, but its
508 // value after inserting N items is not predictable.
509 // This function is specialized by unittests to return constant value, thus
510 // excluding bucket_count from testing.
511 template <class V>
512 size_t HashMapBucketCountForTesting(size_t bucket_count) {
513   return bucket_count;
514 }
515 
516 template <class LruCacheType>
517 size_t DoEstimateMemoryUsageForLruCache(const LruCacheType& lru_cache) {
518   return EstimateMemoryUsage(lru_cache.ordering_) +
519          EstimateMemoryUsage(lru_cache.index_);
520 }
521 
522 }  // namespace internal
523 
524 template <class V>
525 size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) {
526   // Hashtable containers are modeled after libc++
527   // (__hash_node from include/__hash_table)
528   struct Node {
529     raw_ptr<void> next;
530     size_t hash;
531     V value;
532   };
533   using Bucket = void*;
534   bucket_count = internal::HashMapBucketCountForTesting<V>(bucket_count);
535   return sizeof(Bucket) * bucket_count + sizeof(Node) * size;
536 }
537 
538 template <class K, class H, class KE, class A>
539 size_t EstimateMemoryUsage(const std::unordered_set<K, H, KE, A>& set) {
540   using value_type = typename std::unordered_set<K, H, KE, A>::value_type;
541   return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
542                                                 set.size()) +
543          EstimateIterableMemoryUsage(set);
544 }
545 
546 template <class K, class H, class KE, class A>
547 size_t EstimateMemoryUsage(const std::unordered_multiset<K, H, KE, A>& set) {
548   using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type;
549   return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
550                                                 set.size()) +
551          EstimateIterableMemoryUsage(set);
552 }
553 
554 template <class K, class V, class H, class KE, class A>
555 size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map) {
556   using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type;
557   return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
558                                                 map.size()) +
559          EstimateIterableMemoryUsage(map);
560 }
561 
562 template <class K, class V, class H, class KE, class A>
563 size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map) {
564   using value_type =
565       typename std::unordered_multimap<K, V, H, KE, A>::value_type;
566   return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
567                                                 map.size()) +
568          EstimateIterableMemoryUsage(map);
569 }
570 
571 // std::deque
572 
573 template <class T, class A>
574 size_t EstimateMemoryUsage(const std::deque<T, A>& deque) {
575 // Since std::deque implementations are wildly different
576 // (see crbug.com/674287), we can't have one "good enough"
577 // way to estimate.
578 
579 // kBlockSize      - minimum size of a block, in bytes
580 // kMinBlockLength - number of elements in a block
581 //                   if sizeof(T) > kBlockSize
582 #if defined(_LIBCPP_VERSION)
583   size_t kBlockSize = 4096;
584   size_t kMinBlockLength = 16;
585 #elif defined(__GLIBCXX__)
586   size_t kBlockSize = 512;
587   size_t kMinBlockLength = 1;
588 #elif defined(_MSC_VER)
589   size_t kBlockSize = 16;
590   size_t kMinBlockLength = 1;
591 #else
592   size_t kBlockSize = 0;
593   size_t kMinBlockLength = 1;
594 #endif
595 
596   size_t block_length =
597       (sizeof(T) > kBlockSize) ? kMinBlockLength : kBlockSize / sizeof(T);
598 
599   size_t blocks = (deque.size() + block_length - 1) / block_length;
600 
601 #if defined(__GLIBCXX__)
602   // libstdc++: deque always has at least one block
603   if (!blocks)
604     blocks = 1;
605 #endif
606 
607 #if defined(_LIBCPP_VERSION)
608   // libc++: deque keeps at most two blocks when it shrinks,
609   // so even if the size is zero, deque might be holding up
610   // to 4096 * 2 bytes. One way to know whether deque has
611   // ever allocated (and hence has 1 or 2 blocks) is to check
612   // iterator's pointer. Non-zero value means that deque has
613   // at least one block.
614   if (!blocks && deque.begin().operator->())
615     blocks = 1;
616 #endif
617 
618   return (blocks * block_length * sizeof(T)) +
619          EstimateIterableMemoryUsage(deque);
620 }
621 
622 // Container adapters
623 
624 template <class T, class C>
625 size_t EstimateMemoryUsage(const std::queue<T, C>& queue) {
626   return EstimateMemoryUsage(GetUnderlyingContainer(queue));
627 }
628 
629 template <class T, class C>
630 size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue) {
631   return EstimateMemoryUsage(GetUnderlyingContainer(queue));
632 }
633 
634 template <class T, class C>
635 size_t EstimateMemoryUsage(const std::stack<T, C>& stack) {
636   return EstimateMemoryUsage(GetUnderlyingContainer(stack));
637 }
638 
639 // base::circular_deque
640 
641 template <class T>
642 size_t EstimateMemoryUsage(const base::circular_deque<T>& deque) {
643   return sizeof(T) * deque.capacity() + EstimateIterableMemoryUsage(deque);
644 }
645 
646 // Flat containers
647 
648 template <class T, class C>
649 size_t EstimateMemoryUsage(const base::flat_set<T, C>& set) {
650   using value_type = typename base::flat_set<T, C>::value_type;
651   return sizeof(value_type) * set.capacity() + EstimateIterableMemoryUsage(set);
652 }
653 
654 template <class K, class V, class C>
655 size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map) {
656   using value_type = typename base::flat_map<K, V, C>::value_type;
657   return sizeof(value_type) * map.capacity() + EstimateIterableMemoryUsage(map);
658 }
659 
660 template <class K, class V, class C>
661 size_t EstimateMemoryUsage(const LRUCache<K, V, C>& lru_cache) {
662   return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
663 }
664 
665 template <class K, class V, class C>
666 size_t EstimateMemoryUsage(const HashingLRUCache<K, V, C>& lru_cache) {
667   return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
668 }
669 
670 template <class V, class C>
671 size_t EstimateMemoryUsage(const LRUCacheSet<V, C>& lru_cache) {
672   return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
673 }
674 
675 template <class V, class C>
676 size_t EstimateMemoryUsage(const HashingLRUCacheSet<V, C>& lru_cache) {
677   return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
678 }
679 
680 }  // namespace trace_event
681 }  // namespace base
682 
683 #endif  // BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
684