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_OBSERVER_H_
6#define V8_HEAP_ALLOCATION_OBSERVER_H_
7
8#include <cstdint>
9#include <unordered_set>
10#include <vector>
11
12#include "src/common/globals.h"
13
14namespace v8 {
15namespace internal {
16
17// Observer for allocations that is aware of LAB-based allocation.
18class AllocationObserver {
19 public:
20  explicit AllocationObserver(intptr_t step_size) : step_size_(step_size) {
21    DCHECK_LE(kTaggedSize, step_size);
22  }
23  virtual ~AllocationObserver() = default;
24  AllocationObserver(const AllocationObserver&) = delete;
25  AllocationObserver& operator=(const AllocationObserver&) = delete;
26
27 protected:
28  // Called when at least `step_size_` bytes have been allocated. `soon_object`
29  // points to the uninitialized memory that has just been allocated and is the
30  // result for a request of `size` bytes.
31  //
32  // Some caveats:
33  // 1. `soon_object` will be nullptr in cases where the allocation returns a
34  //    filler object, which is e.g. needed at page boundaries.
35  // 2. `soon_object`  may actually be the first object in an
36  //    allocation-folding group. In such a case size is the size of the group
37  //    rather than the first object.
38  // 3. `size` is the requested size at the time of allocation. Right-trimming
39  //    may change the object size dynamically.
40  virtual void Step(int bytes_allocated, Address soon_object, size_t size) = 0;
41
42  // Subclasses can override this method to make step size dynamic.
43  virtual intptr_t GetNextStepSize() { return step_size_; }
44
45 private:
46  const intptr_t step_size_;
47
48  friend class AllocationCounter;
49};
50
51// A global allocation counter observers can be added to.
52class AllocationCounter final {
53 public:
54  AllocationCounter() = default;
55
56  // Adds an observer. May be called from `AllocationObserver::Step()`.
57  V8_EXPORT_PRIVATE void AddAllocationObserver(AllocationObserver* observer);
58
59  // Removes an observer. May be called from `AllocationObserver::Step()`.
60  V8_EXPORT_PRIVATE void RemoveAllocationObserver(AllocationObserver* observer);
61
62  // Advances forward by `allocated` bytes. Does not invoke any observers.
63  V8_EXPORT_PRIVATE void AdvanceAllocationObservers(size_t allocated);
64
65  // Invokes observers via `AllocationObserver::Step()` and computes new step
66  // sizes. Does not advance the current allocation counter.
67  V8_EXPORT_PRIVATE void InvokeAllocationObservers(Address soon_object,
68                                                   size_t object_size,
69                                                   size_t aligned_object_size);
70
71  bool IsActive() const { return !IsPaused() && observers_.size() > 0; }
72
73  bool IsStepInProgress() const { return step_in_progress_; }
74
75  size_t NextBytes() const {
76    DCHECK(IsActive());
77    return next_counter_ - current_counter_;
78  }
79
80  void Pause() {
81    DCHECK(!step_in_progress_);
82    paused_++;
83  }
84
85  void Resume() {
86    DCHECK_NE(0, paused_);
87    DCHECK(!step_in_progress_);
88    paused_--;
89  }
90
91 private:
92  bool IsPaused() const { return paused_; }
93
94  struct AllocationObserverCounter final {
95    AllocationObserverCounter(AllocationObserver* observer, size_t prev_counter,
96                              size_t next_counter)
97        : observer_(observer),
98          prev_counter_(prev_counter),
99          next_counter_(next_counter) {}
100
101    AllocationObserver* observer_;
102    size_t prev_counter_;
103    size_t next_counter_;
104  };
105
106  std::vector<AllocationObserverCounter> observers_;
107  std::vector<AllocationObserverCounter> pending_added_;
108  std::unordered_set<AllocationObserver*> pending_removed_;
109
110  size_t current_counter_ = 0;
111  size_t next_counter_ = 0;
112  bool step_in_progress_ = false;
113  int paused_ = 0;
114};
115
116class V8_EXPORT_PRIVATE V8_NODISCARD PauseAllocationObserversScope {
117 public:
118  explicit PauseAllocationObserversScope(Heap* heap);
119  ~PauseAllocationObserversScope();
120  PauseAllocationObserversScope(const PauseAllocationObserversScope&) = delete;
121  PauseAllocationObserversScope& operator=(
122      const PauseAllocationObserversScope&) = delete;
123
124 private:
125  Heap* heap_;
126};
127
128}  // namespace internal
129}  // namespace v8
130
131#endif  // V8_HEAP_ALLOCATION_OBSERVER_H_
132