summaryrefslogtreecommitdiff
path: root/chromium/v8/src/heap/embedder-tracing.h
blob: 8a1b14a32b47b3f1040f82eba315a054f8f33401 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Copyright 2016 the V8 project 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 V8_HEAP_EMBEDDER_TRACING_H_
#define V8_HEAP_EMBEDDER_TRACING_H_

#include "include/v8-cppgc.h"
#include "include/v8.h"
#include "src/common/globals.h"
#include "src/flags/flags.h"

namespace v8 {
namespace internal {

class Heap;
class JSObject;

class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
 public:
  using WrapperInfo = std::pair<void*, void*>;
  using WrapperCache = std::vector<WrapperInfo>;

  // WrapperInfo is passed over the API. Use VerboseWrapperInfo to access pair
  // internals in a named way. See ProcessingScope::TracePossibleJSWrapper()
  // below on how a V8 object is parsed to gather the information.
  struct VerboseWrapperInfo {
    constexpr explicit VerboseWrapperInfo(const WrapperInfo& raw_info)
        : raw_info(raw_info) {}

    // Information describing the type pointed to via instance().
    void* type_info() const { return raw_info.first; }
    // Direct pointer to an instance described by type_info().
    void* instance() const { return raw_info.second; }
    // Returns whether the info is empty and thus does not keep a C++ object
    // alive.
    bool is_empty() const { return !type_info() || !instance(); }

    const WrapperInfo& raw_info;
  };

  class V8_EXPORT_PRIVATE V8_NODISCARD ProcessingScope {
   public:
    explicit ProcessingScope(LocalEmbedderHeapTracer* tracer);
    ~ProcessingScope();

    void TracePossibleWrapper(JSObject js_object);

    void AddWrapperInfoForTesting(WrapperInfo info);

   private:
    static constexpr size_t kWrapperCacheSize = 1000;

    void FlushWrapperCacheIfFull();

    LocalEmbedderHeapTracer* const tracer_;
    const WrapperDescriptor wrapper_descriptor_;
    WrapperCache wrapper_cache_;
  };

  explicit LocalEmbedderHeapTracer(Isolate* isolate) : isolate_(isolate) {}

  ~LocalEmbedderHeapTracer() {
    if (remote_tracer_) remote_tracer_->isolate_ = nullptr;
  }

  bool InUse() const { return remote_tracer_ != nullptr; }
  EmbedderHeapTracer* remote_tracer() const { return remote_tracer_; }

  void SetRemoteTracer(EmbedderHeapTracer* tracer);
  void TracePrologue(EmbedderHeapTracer::TraceFlags flags);
  void TraceEpilogue();
  void EnterFinalPause();
  bool Trace(double deadline);
  bool IsRemoteTracingDone();

  bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) {
    return !InUse() || remote_tracer_->IsRootForNonTracingGC(handle);
  }

  bool IsRootForNonTracingGC(const v8::TracedReference<v8::Value>& handle) {
    return !InUse() || remote_tracer_->IsRootForNonTracingGC(handle);
  }

  void ResetHandleInNonTracingGC(const v8::TracedReference<v8::Value>& handle) {
    // Resetting is only called when IsRootForNonTracingGC returns false which
    // can only happen the EmbedderHeapTracer is set on API level.
    DCHECK(InUse());
    remote_tracer_->ResetHandleInNonTracingGC(handle);
  }

  bool ShouldFinalizeIncrementalMarking() {
    return !FLAG_incremental_marking_wrappers || !InUse() ||
           (IsRemoteTracingDone() && embedder_worklist_empty_);
  }

  void SetEmbedderStackStateForNextFinalization(
      EmbedderHeapTracer::EmbedderStackState stack_state);

  void SetEmbedderWorklistEmpty(bool is_empty) {
    embedder_worklist_empty_ = is_empty;
  }

  void IncreaseAllocatedSize(size_t bytes) {
    remote_stats_.used_size += bytes;
    remote_stats_.allocated_size += bytes;
    if (remote_stats_.allocated_size >
        remote_stats_.allocated_size_limit_for_check) {
      StartIncrementalMarkingIfNeeded();
      remote_stats_.allocated_size_limit_for_check =
          remote_stats_.allocated_size + kEmbedderAllocatedThreshold;
    }
  }

  void DecreaseAllocatedSize(size_t bytes) {
    DCHECK_GE(remote_stats_.used_size, bytes);
    remote_stats_.used_size -= bytes;
  }

  void StartIncrementalMarkingIfNeeded();

  size_t used_size() const { return remote_stats_.used_size; }
  size_t allocated_size() const { return remote_stats_.allocated_size; }

  WrapperInfo ExtractWrapperInfo(Isolate* isolate, JSObject js_object);

  void SetWrapperDescriptor(const WrapperDescriptor& wrapper_descriptor) {
    wrapper_descriptor_ = wrapper_descriptor;
  }

  void UpdateRemoteStats(size_t, double);

 private:
  static constexpr size_t kEmbedderAllocatedThreshold = 128 * KB;

  static constexpr WrapperDescriptor::InternalFieldIndex
      kDefaultWrapperTypeEmbedderIndex = 0;
  static constexpr WrapperDescriptor::InternalFieldIndex
      kDefaultWrapperInstanceEmbedderIndex = 1;

  static constexpr WrapperDescriptor GetDefaultWrapperDescriptor() {
    // The default descriptor assumes the indices that known embedders use.
    return WrapperDescriptor(kDefaultWrapperTypeEmbedderIndex,
                             kDefaultWrapperInstanceEmbedderIndex,
                             WrapperDescriptor::kUnknownEmbedderId);
  }

  Isolate* const isolate_;
  EmbedderHeapTracer* remote_tracer_ = nullptr;

  EmbedderHeapTracer::EmbedderStackState embedder_stack_state_ =
      EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers;
  // Indicates whether the embedder worklist was observed empty on the main
  // thread. This is opportunistic as concurrent marking tasks may hold local
  // segments of potential embedder fields to move to the main thread.
  bool embedder_worklist_empty_ = false;

  struct RemoteStatistics {
    // Used size of objects in bytes reported by the embedder. Updated via
    // TraceSummary at the end of tracing and incrementally when the GC is not
    // in progress.
    size_t used_size = 0;
    // Totally bytes allocated by the embedder. Monotonically
    // increasing value. Used to approximate allocation rate.
    size_t allocated_size = 0;
    // Limit for |allocated_size| in bytes to avoid checking for starting a GC
    // on each increment.
    size_t allocated_size_limit_for_check = 0;
  } remote_stats_;

  // Default descriptor only used when the embedder is using EmbedderHeapTracer.
  // The value is overriden by CppHeap with values that the embedder provided
  // upon initialization.
  WrapperDescriptor wrapper_descriptor_ = GetDefaultWrapperDescriptor();

  friend class EmbedderStackStateScope;
};

class V8_EXPORT_PRIVATE V8_NODISCARD EmbedderStackStateScope final {
 public:
  EmbedderStackStateScope(LocalEmbedderHeapTracer* local_tracer,
                          EmbedderHeapTracer::EmbedderStackState stack_state)
      : local_tracer_(local_tracer),
        old_stack_state_(local_tracer_->embedder_stack_state_) {
    local_tracer_->embedder_stack_state_ = stack_state;
    if (EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers ==
        stack_state) {
      if (local_tracer->remote_tracer())
        local_tracer->remote_tracer()->NotifyEmptyEmbedderStack();
    }
  }

  ~EmbedderStackStateScope() {
    local_tracer_->embedder_stack_state_ = old_stack_state_;
  }

 private:
  LocalEmbedderHeapTracer* const local_tracer_;
  const EmbedderHeapTracer::EmbedderStackState old_stack_state_;
};

}  // namespace internal
}  // namespace v8

#endif  // V8_HEAP_EMBEDDER_TRACING_H_