GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: heap_utils.cc Lines: 123 244 50.4 %
Date: 2022-05-07 04:15:18 Branches: 25 104 24.0 %

Line Branch Exec Source
1
#include "diagnosticfilename-inl.h"
2
#include "env-inl.h"
3
#include "memory_tracker-inl.h"
4
#include "node_external_reference.h"
5
#include "stream_base-inl.h"
6
#include "util-inl.h"
7
8
// Copied from https://github.com/nodejs/node/blob/b07dc4d19fdbc15b4f76557dc45b3ce3a43ad0c3/src/util.cc#L36-L41.
9
#ifdef _WIN32
10
#include <io.h>  // _S_IREAD _S_IWRITE
11
#ifndef S_IRUSR
12
#define S_IRUSR _S_IREAD
13
#endif  // S_IRUSR
14
#ifndef S_IWUSR
15
#define S_IWUSR _S_IWRITE
16
#endif  // S_IWUSR
17
#endif
18
19
using v8::Array;
20
using v8::Boolean;
21
using v8::Context;
22
using v8::EmbedderGraph;
23
using v8::EscapableHandleScope;
24
using v8::FunctionCallbackInfo;
25
using v8::FunctionTemplate;
26
using v8::Global;
27
using v8::HandleScope;
28
using v8::HeapSnapshot;
29
using v8::Isolate;
30
using v8::JustVoid;
31
using v8::Local;
32
using v8::Maybe;
33
using v8::MaybeLocal;
34
using v8::Nothing;
35
using v8::Number;
36
using v8::Object;
37
using v8::ObjectTemplate;
38
using v8::String;
39
using v8::Value;
40
41
namespace node {
42
namespace heap {
43
44
class JSGraphJSNode : public EmbedderGraph::Node {
45
 public:
46
  const char* Name() override { return "<JS Node>"; }
47
  size_t SizeInBytes() override { return 0; }
48
  bool IsEmbedderNode() override { return false; }
49
  Local<Value> JSValue() { return PersistentToLocal::Strong(persistent_); }
50
51
  int IdentityHash() {
52
    Local<Value> v = JSValue();
53
    if (v->IsObject()) return v.As<Object>()->GetIdentityHash();
54
    if (v->IsName()) return v.As<v8::Name>()->GetIdentityHash();
55
    if (v->IsInt32()) return v.As<v8::Int32>()->Value();
56
    return 0;
57
  }
58
59
  JSGraphJSNode(Isolate* isolate, Local<Value> val)
60
      : persistent_(isolate, val) {
61
    CHECK(!val.IsEmpty());
62
  }
63
64
  struct Hash {
65
    inline size_t operator()(JSGraphJSNode* n) const {
66
      return static_cast<size_t>(n->IdentityHash());
67
    }
68
  };
69
70
  struct Equal {
71
    inline bool operator()(JSGraphJSNode* a, JSGraphJSNode* b) const {
72
      return a->JSValue()->SameValue(b->JSValue());
73
    }
74
  };
75
76
 private:
77
  Global<Value> persistent_;
78
};
79
80
class JSGraph : public EmbedderGraph {
81
 public:
82
  explicit JSGraph(Isolate* isolate) : isolate_(isolate) {}
83
84
  Node* V8Node(const Local<Value>& value) override {
85
    std::unique_ptr<JSGraphJSNode> n { new JSGraphJSNode(isolate_, value) };
86
    auto it = engine_nodes_.find(n.get());
87
    if (it != engine_nodes_.end())
88
      return *it;
89
    engine_nodes_.insert(n.get());
90
    return AddNode(std::unique_ptr<Node>(n.release()));
91
  }
92
93
  Node* AddNode(std::unique_ptr<Node> node) override {
94
    Node* n = node.get();
95
    nodes_.emplace(std::move(node));
96
    return n;
97
  }
98
99
  void AddEdge(Node* from, Node* to, const char* name = nullptr) override {
100
    edges_[from].insert(std::make_pair(name, to));
101
  }
102
103
  MaybeLocal<Array> CreateObject() const {
104
    EscapableHandleScope handle_scope(isolate_);
105
    Local<Context> context = isolate_->GetCurrentContext();
106
    Environment* env = Environment::GetCurrent(context);
107
108
    std::unordered_map<Node*, Local<Object>> info_objects;
109
    Local<Array> nodes = Array::New(isolate_, nodes_.size());
110
    Local<String> edges_string = FIXED_ONE_BYTE_STRING(isolate_, "edges");
111
    Local<String> is_root_string = FIXED_ONE_BYTE_STRING(isolate_, "isRoot");
112
    Local<String> name_string = env->name_string();
113
    Local<String> size_string = env->size_string();
114
    Local<String> value_string = env->value_string();
115
    Local<String> wraps_string = FIXED_ONE_BYTE_STRING(isolate_, "wraps");
116
    Local<String> to_string = FIXED_ONE_BYTE_STRING(isolate_, "to");
117
118
    for (const std::unique_ptr<Node>& n : nodes_)
119
      info_objects[n.get()] = Object::New(isolate_);
120
121
    {
122
      HandleScope handle_scope(isolate_);
123
      size_t i = 0;
124
      for (const std::unique_ptr<Node>& n : nodes_) {
125
        Local<Object> obj = info_objects[n.get()];
126
        Local<Value> value;
127
        std::string name_str;
128
        const char* prefix = n->NamePrefix();
129
        if (prefix == nullptr) {
130
          name_str = n->Name();
131
        } else {
132
          name_str = n->NamePrefix();
133
          name_str += " ";
134
          name_str += n->Name();
135
        }
136
        if (!String::NewFromUtf8(isolate_, name_str.c_str()).ToLocal(&value) ||
137
            obj->Set(context, name_string, value).IsNothing() ||
138
            obj->Set(context,
139
                     is_root_string,
140
                     Boolean::New(isolate_, n->IsRootNode()))
141
                .IsNothing() ||
142
            obj->Set(
143
                   context,
144
                   size_string,
145
                   Number::New(isolate_, static_cast<double>(n->SizeInBytes())))
146
                .IsNothing() ||
147
            obj->Set(context, edges_string, Array::New(isolate_)).IsNothing()) {
148
          return MaybeLocal<Array>();
149
        }
150
        if (nodes->Set(context, i++, obj).IsNothing())
151
          return MaybeLocal<Array>();
152
        if (!n->IsEmbedderNode()) {
153
          value = static_cast<JSGraphJSNode*>(n.get())->JSValue();
154
          if (obj->Set(context, value_string, value).IsNothing())
155
            return MaybeLocal<Array>();
156
        }
157
      }
158
    }
159
160
    for (const std::unique_ptr<Node>& n : nodes_) {
161
      Node* wraps = n->WrapperNode();
162
      if (wraps == nullptr) continue;
163
      Local<Object> from = info_objects[n.get()];
164
      Local<Object> to = info_objects[wraps];
165
      if (from->Set(context, wraps_string, to).IsNothing())
166
        return MaybeLocal<Array>();
167
    }
168
169
    for (const auto& edge_info : edges_) {
170
      Node* source = edge_info.first;
171
      Local<Value> edges;
172
      if (!info_objects[source]->Get(context, edges_string).ToLocal(&edges) ||
173
          !edges->IsArray()) {
174
        return MaybeLocal<Array>();
175
      }
176
177
      size_t i = 0;
178
      size_t j = 0;
179
      for (const auto& edge : edge_info.second) {
180
        Local<Object> to_object = info_objects[edge.second];
181
        Local<Object> edge_obj = Object::New(isolate_);
182
        Local<Value> edge_name_value;
183
        const char* edge_name = edge.first;
184
        if (edge_name != nullptr) {
185
          if (!String::NewFromUtf8(isolate_, edge_name)
186
              .ToLocal(&edge_name_value)) {
187
            return MaybeLocal<Array>();
188
          }
189
        } else {
190
          edge_name_value = Number::New(isolate_, static_cast<double>(j++));
191
        }
192
        if (edge_obj->Set(context, name_string, edge_name_value).IsNothing() ||
193
            edge_obj->Set(context, to_string, to_object).IsNothing() ||
194
            edges.As<Array>()->Set(context, i++, edge_obj).IsNothing()) {
195
          return MaybeLocal<Array>();
196
        }
197
      }
198
    }
199
200
    return handle_scope.Escape(nodes);
201
  }
202
203
 private:
204
  Isolate* isolate_;
205
  std::unordered_set<std::unique_ptr<Node>> nodes_;
206
  std::unordered_set<JSGraphJSNode*, JSGraphJSNode::Hash, JSGraphJSNode::Equal>
207
      engine_nodes_;
208
  std::unordered_map<Node*, std::set<std::pair<const char*, Node*>>> edges_;
209
};
210
211
void BuildEmbedderGraph(const FunctionCallbackInfo<Value>& args) {
212
  Environment* env = Environment::GetCurrent(args);
213
  JSGraph graph(env->isolate());
214
  Environment::BuildEmbedderGraph(env->isolate(), &graph, env);
215
  Local<Array> ret;
216
  if (graph.CreateObject().ToLocal(&ret))
217
    args.GetReturnValue().Set(ret);
218
}
219
220
namespace {
221
class FileOutputStream : public v8::OutputStream {
222
 public:
223
5
  FileOutputStream(const int fd, uv_fs_t* req) : fd_(fd), req_(req) {}
224
225
10
  int GetChunkSize() override {
226
10
    return 65536;  // big chunks == faster
227
  }
228
229
5
  void EndOfStream() override {}
230
231
381
  WriteResult WriteAsciiChunk(char* data, const int size) override {
232
    DCHECK_EQ(status_, 0);
233
381
    int offset = 0;
234
762
    while (offset < size) {
235
381
      const uv_buf_t buf = uv_buf_init(data + offset, size - offset);
236
381
      const int num_bytes_written = uv_fs_write(nullptr,
237
                                                req_,
238
381
                                                fd_,
239
                                                &buf,
240
                                                1,
241
                                                -1,
242
                                                nullptr);
243
381
      uv_fs_req_cleanup(req_);
244
381
      if (num_bytes_written < 0) {
245
        status_ = num_bytes_written;
246
        return kAbort;
247
      }
248
      DCHECK_LE(num_bytes_written, buf.len);
249
381
      offset += num_bytes_written;
250
    }
251
    DCHECK_EQ(offset, size);
252
381
    return kContinue;
253
  }
254
255
5
  int status() const { return status_; }
256
257
 private:
258
  const int fd_;
259
  uv_fs_t* req_;
260
  int status_ = 0;
261
};
262
263
class HeapSnapshotStream : public AsyncWrap,
264
                           public StreamBase,
265
                           public v8::OutputStream {
266
 public:
267
15
  HeapSnapshotStream(
268
      Environment* env,
269
      HeapSnapshotPointer&& snapshot,
270
15
      Local<Object> obj) :
271
      AsyncWrap(env, obj, AsyncWrap::PROVIDER_HEAPSNAPSHOT),
272
      StreamBase(env),
273
15
      snapshot_(std::move(snapshot)) {
274
15
    MakeWeak();
275
15
    StreamBase::AttachToObject(GetObject());
276
15
  }
277
278
60
  ~HeapSnapshotStream() override {}
279
280
24
  int GetChunkSize() override {
281
24
    return 65536;  // big chunks == faster
282
  }
283
284
12
  void EndOfStream() override {
285
12
    EmitRead(UV_EOF);
286
12
    snapshot_.reset();
287
12
  }
288
289
927
  WriteResult WriteAsciiChunk(char* data, int size) override {
290
927
    int len = size;
291
1854
    while (len != 0) {
292
927
      uv_buf_t buf = EmitAlloc(size);
293
927
      ssize_t avail = len;
294
927
      if (static_cast<ssize_t>(buf.len) < avail)
295
        avail = buf.len;
296
927
      memcpy(buf.base, data, avail);
297
927
      data += avail;
298
927
      len -= static_cast<int>(avail);
299
927
      EmitRead(size, buf);
300
    }
301
927
    return kContinue;
302
  }
303
304
12
  int ReadStart() override {
305
12
    CHECK_NE(snapshot_, nullptr);
306
12
    snapshot_->Serialize(this, HeapSnapshot::kJSON);
307
12
    return 0;
308
  }
309
310
927
  int ReadStop() override {
311
927
    return 0;
312
  }
313
314
  int DoShutdown(ShutdownWrap* req_wrap) override {
315
    UNREACHABLE();
316
  }
317
318
  int DoWrite(WriteWrap* w,
319
              uv_buf_t* bufs,
320
              size_t count,
321
              uv_stream_t* send_handle) override {
322
    UNREACHABLE();
323
  }
324
325
939
  bool IsAlive() override { return snapshot_ != nullptr; }
326
  bool IsClosing() override { return snapshot_ == nullptr; }
327
1893
  AsyncWrap* GetAsyncWrap() override { return this; }
328
329
7
  void MemoryInfo(MemoryTracker* tracker) const override {
330
7
    if (snapshot_ != nullptr) {
331
7
      tracker->TrackFieldWithSize(
332
          "snapshot", sizeof(*snapshot_), "HeapSnapshot");
333
    }
334
7
  }
335
336
7
  SET_MEMORY_INFO_NAME(HeapSnapshotStream)
337
7
  SET_SELF_SIZE(HeapSnapshotStream)
338
339
 private:
340
  HeapSnapshotPointer snapshot_;
341
};
342
343
5
inline void TakeSnapshot(Environment* env, v8::OutputStream* out) {
344
  HeapSnapshotPointer snapshot {
345
10
      env->isolate()->GetHeapProfiler()->TakeHeapSnapshot() };
346
5
  snapshot->Serialize(out, HeapSnapshot::kJSON);
347
5
}
348
349
}  // namespace
350
351
6
Maybe<void> WriteSnapshot(Environment* env, const char* filename) {
352
  uv_fs_t req;
353
  int err;
354
355
6
  const int fd = uv_fs_open(nullptr,
356
                            &req,
357
                            filename,
358
                            O_WRONLY | O_CREAT | O_TRUNC,
359
                            S_IWUSR | S_IRUSR,
360
                            nullptr);
361
6
  uv_fs_req_cleanup(&req);
362
6
  if ((err = fd) < 0) {
363
1
    env->ThrowUVException(err, "open", nullptr, filename);
364
1
    return Nothing<void>();
365
  }
366
367
10
  FileOutputStream stream(fd, &req);
368
5
  TakeSnapshot(env, &stream);
369
5
  if ((err = stream.status()) < 0) {
370
    env->ThrowUVException(err, "write", nullptr, filename);
371
    return Nothing<void>();
372
  }
373
374
5
  err = uv_fs_close(nullptr, &req, fd, nullptr);
375
5
  uv_fs_req_cleanup(&req);
376
5
  if (err < 0) {
377
    env->ThrowUVException(err, "close", nullptr, filename);
378
    return Nothing<void>();
379
  }
380
381
5
  return JustVoid();
382
}
383
384
20
void DeleteHeapSnapshot(const HeapSnapshot* snapshot) {
385
20
  const_cast<HeapSnapshot*>(snapshot)->Delete();
386
20
}
387
388
15
BaseObjectPtr<AsyncWrap> CreateHeapSnapshotStream(
389
    Environment* env, HeapSnapshotPointer&& snapshot) {
390
30
  HandleScope scope(env->isolate());
391
392
30
  if (env->streambaseoutputstream_constructor_template().IsEmpty()) {
393
    // Create FunctionTemplate for HeapSnapshotStream
394
6
    Local<FunctionTemplate> os = FunctionTemplate::New(env->isolate());
395
6
    os->Inherit(AsyncWrap::GetConstructorTemplate(env));
396
6
    Local<ObjectTemplate> ost = os->InstanceTemplate();
397
6
    ost->SetInternalFieldCount(StreamBase::kInternalFieldCount);
398
6
    os->SetClassName(
399
        FIXED_ONE_BYTE_STRING(env->isolate(), "HeapSnapshotStream"));
400
6
    StreamBase::AddMethods(env, os);
401
6
    env->set_streambaseoutputstream_constructor_template(ost);
402
  }
403
404
  Local<Object> obj;
405
15
  if (!env->streambaseoutputstream_constructor_template()
406
15
           ->NewInstance(env->context())
407
15
           .ToLocal(&obj)) {
408
    return {};
409
  }
410
15
  return MakeBaseObject<HeapSnapshotStream>(env, std::move(snapshot), obj);
411
}
412
413
14
void CreateHeapSnapshotStream(const FunctionCallbackInfo<Value>& args) {
414
14
  Environment* env = Environment::GetCurrent(args);
415
  HeapSnapshotPointer snapshot {
416
28
      env->isolate()->GetHeapProfiler()->TakeHeapSnapshot() };
417
14
  CHECK(snapshot);
418
  BaseObjectPtr<AsyncWrap> stream =
419
28
      CreateHeapSnapshotStream(env, std::move(snapshot));
420
14
  if (stream)
421
28
    args.GetReturnValue().Set(stream->object());
422
14
}
423
424
5
void TriggerHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
425
5
  Environment* env = Environment::GetCurrent(args);
426
5
  Isolate* isolate = args.GetIsolate();
427
428
5
  Local<Value> filename_v = args[0];
429
430
10
  if (filename_v->IsUndefined()) {
431
6
    DiagnosticFilename name(env, "Heap", "heapsnapshot");
432
6
    if (WriteSnapshot(env, *name).IsNothing())
433
      return;
434
6
    if (String::NewFromUtf8(isolate, *name).ToLocal(&filename_v)) {
435
6
      args.GetReturnValue().Set(filename_v);
436
    }
437
3
    return;
438
  }
439
440
4
  BufferValue path(isolate, filename_v);
441
2
  CHECK_NOT_NULL(*path);
442
4
  if (WriteSnapshot(env, *path).IsNothing())
443
1
    return;
444
2
  return args.GetReturnValue().Set(filename_v);
445
}
446
447
849
void Initialize(Local<Object> target,
448
                Local<Value> unused,
449
                Local<Context> context,
450
                void* priv) {
451
849
  Environment* env = Environment::GetCurrent(context);
452
453
849
  env->SetMethod(target, "buildEmbedderGraph", BuildEmbedderGraph);
454
849
  env->SetMethod(target, "triggerHeapSnapshot", TriggerHeapSnapshot);
455
849
  env->SetMethod(target, "createHeapSnapshotStream", CreateHeapSnapshotStream);
456
849
}
457
458
5175
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
459
5175
  registry->Register(BuildEmbedderGraph);
460
5175
  registry->Register(TriggerHeapSnapshot);
461
5175
  registry->Register(CreateHeapSnapshotStream);
462
5175
}
463
464
}  // namespace heap
465
}  // namespace node
466
467
5243
NODE_MODULE_CONTEXT_AWARE_INTERNAL(heap_utils, node::heap::Initialize)
468
5175
NODE_MODULE_EXTERNAL_REFERENCE(heap_utils,
469
                               node::heap::RegisterExternalReferences)