// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_ #define BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base/base_export.h" #include "base/containers/circular_deque.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/containers/linked_list.h" #include "base/containers/lru_cache.h" #include "base/containers/queue.h" #include "base/memory/raw_ptr.h" #include "base/stl_util.h" #include "base/template_util.h" // Composable memory usage estimators. // // This file defines set of EstimateMemoryUsage(object) functions that return // approximate dynamically allocated memory usage of their argument. // // The ultimate goal is to make memory usage estimation for a class simply a // matter of aggregating EstimateMemoryUsage() results over all fields. // // That is achieved via composability: if EstimateMemoryUsage() is defined // for T then EstimateMemoryUsage() is also defined for any combination of // containers holding T (e.g. std::map>). // // There are two ways of defining EstimateMemoryUsage() for a type: // // 1. As a global function 'size_t EstimateMemoryUsage(T)' in // in base::trace_event namespace. // // 2. As 'size_t T::EstimateMemoryUsage() const' method. In this case // EstimateMemoryUsage(T) function in base::trace_event namespace is // provided automatically. // // Here is an example implementation: // // class MyClass { // ... // ... // size_t EstimateMemoryUsage() const { // return base::trace_event::EstimateMemoryUsage(set_) + // base::trace_event::EstimateMemoryUsage(name_) + // base::trace_event::EstimateMemoryUsage(foo_); // } // ... // private: // ... // std::set set_; // std::string name_; // Foo foo_; // int id_; // bool success_; // } // // The approach is simple: first call EstimateMemoryUsage() on all members, // then recursively fix compilation errors that are caused by types not // implementing EstimateMemoryUsage(). namespace base { namespace trace_event { // Declarations // If T declares 'EstimateMemoryUsage() const' member function, then // global function EstimateMemoryUsage(T) is available, and just calls // the member function. template auto EstimateMemoryUsage(const T& object) -> decltype(object.EstimateMemoryUsage()); // String template size_t EstimateMemoryUsage(const std::basic_string& string); // Arrays template size_t EstimateMemoryUsage(const std::array& array); template size_t EstimateMemoryUsage(T (&array)[N]); template size_t EstimateMemoryUsage(const T* array, size_t array_length); // std::unique_ptr template size_t EstimateMemoryUsage(const std::unique_ptr& ptr); template size_t EstimateMemoryUsage(const std::unique_ptr& array, size_t array_length); // std::shared_ptr template size_t EstimateMemoryUsage(const std::shared_ptr& ptr); // Containers template size_t EstimateMemoryUsage(const std::pair& pair); template size_t EstimateMemoryUsage(const std::vector& vector); template size_t EstimateMemoryUsage(const std::list& list); template size_t EstimateMemoryUsage(const base::LinkedList& list); template size_t EstimateMemoryUsage(const std::set& set); template size_t EstimateMemoryUsage(const std::multiset& set); template size_t EstimateMemoryUsage(const std::map& map); template size_t EstimateMemoryUsage(const std::multimap& map); template size_t EstimateMemoryUsage(const std::unordered_set& set); template size_t EstimateMemoryUsage(const std::unordered_multiset& set); template size_t EstimateMemoryUsage(const std::unordered_map& map); template size_t EstimateMemoryUsage(const std::unordered_multimap& map); template size_t EstimateMemoryUsage(const std::deque& deque); template size_t EstimateMemoryUsage(const std::queue& queue); template size_t EstimateMemoryUsage(const std::priority_queue& queue); template size_t EstimateMemoryUsage(const std::stack& stack); template size_t EstimateMemoryUsage(const base::circular_deque& deque); template size_t EstimateMemoryUsage(const base::flat_set& set); template size_t EstimateMemoryUsage(const base::flat_map& map); template size_t EstimateMemoryUsage(const base::LRUCache& lru); template size_t EstimateMemoryUsage(const base::HashingLRUCache& lru); template size_t EstimateMemoryUsage(const base::LRUCacheSet& lru); template size_t EstimateMemoryUsage(const base::HashingLRUCacheSet& lru); // TODO(dskiba): // std::forward_list // Definitions namespace internal { // HasEMU::value is true iff EstimateMemoryUsage(T) is available. // (This is the default version, which is false.) template struct HasEMU : std::false_type {}; // This HasEMU specialization is only picked up if there exists function // EstimateMemoryUsage(const T&) that returns size_t. Simpler ways to // achieve this don't work on MSVC. template struct HasEMU< T, typename std::enable_if()))>::value>::type> : std::true_type {}; // EMUCaller does three things: // 1. Defines Call() method that calls EstimateMemoryUsage(T) if it's // available. // 2. If EstimateMemoryUsage(T) is not available, but T has trivial dtor // (i.e. it's POD, integer, pointer, enum, etc.) then it defines Call() // method that returns 0. This is useful for containers, which allocate // memory regardless of T (also for cases like std::map). // 3. Finally, if EstimateMemoryUsage(T) is not available, then it triggers // a static_assert with a helpful message. That cuts numbers of errors // considerably - if you just call EstimateMemoryUsage(T) but it's not // available for T, then compiler will helpfully list *all* possible // variants of it, with an explanation for each. template struct EMUCaller { // std::is_same<> below makes static_assert depend on T, in order to // prevent it from asserting regardless instantiation. static_assert(std::is_same::value, "Neither global function 'size_t EstimateMemoryUsage(T)' " "nor member function 'size_t T::EstimateMemoryUsage() const' " "is defined for the type."); static size_t Call(const T&) { return 0; } }; template struct EMUCaller::value>::type> { static size_t Call(const T& value) { return EstimateMemoryUsage(value); } }; template