1 // Copyright 2020 the V8 project authors. All rights reserved.
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 V8_HEAP_ALLOCATION_STATS_H_
6 #define V8_HEAP_ALLOCATION_STATS_H_
7 
8 #include <atomic>
9 #include <unordered_map>
10 
11 #include "src/base/macros.h"
12 #include "src/heap/basic-memory-chunk.h"
13 
14 namespace v8 {
15 namespace internal {
16 
17 // An abstraction of the accounting statistics of a page-structured space.
18 //
19 // The stats are only set by functions that ensure they stay balanced. These
20 // functions increase or decrease one of the non-capacity stats in conjunction
21 // with capacity, or else they always balance increases and decreases to the
22 // non-capacity stats.
23 class AllocationStats {
24  public:
AllocationStats()25   AllocationStats() { Clear(); }
26 
27   AllocationStats& operator=(const AllocationStats& stats) V8_NOEXCEPT {
28     capacity_ = stats.capacity_.load();
29     max_capacity_ = stats.max_capacity_;
30     size_.store(stats.size_);
31 #ifdef DEBUG
32     allocated_on_page_ = stats.allocated_on_page_;
33 #endif
34     return *this;
35   }
36 
37   // Zero out all the allocation statistics (i.e., no capacity).
Clear()38   void Clear() {
39     capacity_ = 0;
40     max_capacity_ = 0;
41     ClearSize();
42   }
43 
ClearSize()44   void ClearSize() {
45     size_ = 0;
46 #ifdef DEBUG
47     allocated_on_page_.clear();
48 #endif
49   }
50 
51   // Accessors for the allocation statistics.
Capacity()52   size_t Capacity() const { return capacity_; }
MaxCapacity()53   size_t MaxCapacity() const { return max_capacity_; }
Size()54   size_t Size() const { return size_; }
55 #ifdef DEBUG
AllocatedOnPage(const BasicMemoryChunk * page)56   size_t AllocatedOnPage(const BasicMemoryChunk* page) const {
57     return allocated_on_page_.at(page);
58   }
59 #endif
60 
IncreaseAllocatedBytes(size_t bytes,const BasicMemoryChunk * page)61   void IncreaseAllocatedBytes(size_t bytes, const BasicMemoryChunk* page) {
62 #ifdef DEBUG
63     size_t size = size_;
64     DCHECK_GE(size + bytes, size);
65 #endif
66     size_.fetch_add(bytes);
67 #ifdef DEBUG
68     allocated_on_page_[page] += bytes;
69 #endif
70   }
71 
DecreaseAllocatedBytes(size_t bytes,const BasicMemoryChunk * page)72   void DecreaseAllocatedBytes(size_t bytes, const BasicMemoryChunk* page) {
73     DCHECK_GE(size_, bytes);
74     size_.fetch_sub(bytes);
75 #ifdef DEBUG
76     DCHECK_GE(allocated_on_page_[page], bytes);
77     allocated_on_page_[page] -= bytes;
78 #endif
79   }
80 
DecreaseCapacity(size_t bytes)81   void DecreaseCapacity(size_t bytes) {
82     DCHECK_GE(capacity_, bytes);
83     DCHECK_GE(capacity_ - bytes, size_);
84     capacity_ -= bytes;
85   }
86 
IncreaseCapacity(size_t bytes)87   void IncreaseCapacity(size_t bytes) {
88     DCHECK_GE(capacity_ + bytes, capacity_);
89     capacity_ += bytes;
90     if (capacity_ > max_capacity_) {
91       max_capacity_ = capacity_;
92     }
93   }
94 
95  private:
96   // |capacity_|: The number of object-area bytes (i.e., not including page
97   // bookkeeping structures) currently in the space.
98   // During evacuation capacity of the main spaces is accessed from multiple
99   // threads to check the old generation hard limit.
100   std::atomic<size_t> capacity_;
101 
102   // |max_capacity_|: The maximum capacity ever observed.
103   size_t max_capacity_;
104 
105   // |size_|: The number of allocated bytes.
106   std::atomic<size_t> size_;
107 
108 #ifdef DEBUG
109   std::unordered_map<const BasicMemoryChunk*, size_t, BasicMemoryChunk::Hasher>
110       allocated_on_page_;
111 #endif
112 };
113 
114 }  // namespace internal
115 }  // namespace v8
116 
117 #endif  // V8_HEAP_ALLOCATION_STATS_H_
118