GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/heap_utils.cc Lines: 113 234 48.3 %
Date: 2021-05-26 04:16:18 Branches: 26 110 23.6 %

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
using v8::Array;
9
using v8::Boolean;
10
using v8::Context;
11
using v8::EmbedderGraph;
12
using v8::EscapableHandleScope;
13
using v8::FunctionCallbackInfo;
14
using v8::FunctionTemplate;
15
using v8::Global;
16
using v8::HandleScope;
17
using v8::HeapSnapshot;
18
using v8::Isolate;
19
using v8::Local;
20
using v8::MaybeLocal;
21
using v8::Number;
22
using v8::Object;
23
using v8::ObjectTemplate;
24
using v8::String;
25
using v8::Value;
26
27
namespace node {
28
namespace heap {
29
30
class JSGraphJSNode : public EmbedderGraph::Node {
31
 public:
32
  const char* Name() override { return "<JS Node>"; }
33
  size_t SizeInBytes() override { return 0; }
34
  bool IsEmbedderNode() override { return false; }
35
  Local<Value> JSValue() { return PersistentToLocal::Strong(persistent_); }
36
37
  int IdentityHash() {
38
    Local<Value> v = JSValue();
39
    if (v->IsObject()) return v.As<Object>()->GetIdentityHash();
40
    if (v->IsName()) return v.As<v8::Name>()->GetIdentityHash();
41
    if (v->IsInt32()) return v.As<v8::Int32>()->Value();
42
    return 0;
43
  }
44
45
  JSGraphJSNode(Isolate* isolate, Local<Value> val)
46
      : persistent_(isolate, val) {
47
    CHECK(!val.IsEmpty());
48
  }
49
50
  struct Hash {
51
    inline size_t operator()(JSGraphJSNode* n) const {
52
      return static_cast<size_t>(n->IdentityHash());
53
    }
54
  };
55
56
  struct Equal {
57
    inline bool operator()(JSGraphJSNode* a, JSGraphJSNode* b) const {
58
      return a->JSValue()->SameValue(b->JSValue());
59
    }
60
  };
61
62
 private:
63
  Global<Value> persistent_;
64
};
65
66
class JSGraph : public EmbedderGraph {
67
 public:
68
  explicit JSGraph(Isolate* isolate) : isolate_(isolate) {}
69
70
  Node* V8Node(const Local<Value>& value) override {
71
    std::unique_ptr<JSGraphJSNode> n { new JSGraphJSNode(isolate_, value) };
72
    auto it = engine_nodes_.find(n.get());
73
    if (it != engine_nodes_.end())
74
      return *it;
75
    engine_nodes_.insert(n.get());
76
    return AddNode(std::unique_ptr<Node>(n.release()));
77
  }
78
79
  Node* AddNode(std::unique_ptr<Node> node) override {
80
    Node* n = node.get();
81
    nodes_.emplace(std::move(node));
82
    return n;
83
  }
84
85
  void AddEdge(Node* from, Node* to, const char* name = nullptr) override {
86
    edges_[from].insert(std::make_pair(name, to));
87
  }
88
89
  MaybeLocal<Array> CreateObject() const {
90
    EscapableHandleScope handle_scope(isolate_);
91
    Local<Context> context = isolate_->GetCurrentContext();
92
    Environment* env = Environment::GetCurrent(context);
93
94
    std::unordered_map<Node*, Local<Object>> info_objects;
95
    Local<Array> nodes = Array::New(isolate_, nodes_.size());
96
    Local<String> edges_string = FIXED_ONE_BYTE_STRING(isolate_, "edges");
97
    Local<String> is_root_string = FIXED_ONE_BYTE_STRING(isolate_, "isRoot");
98
    Local<String> name_string = env->name_string();
99
    Local<String> size_string = env->size_string();
100
    Local<String> value_string = env->value_string();
101
    Local<String> wraps_string = FIXED_ONE_BYTE_STRING(isolate_, "wraps");
102
    Local<String> to_string = FIXED_ONE_BYTE_STRING(isolate_, "to");
103
104
    for (const std::unique_ptr<Node>& n : nodes_)
105
      info_objects[n.get()] = Object::New(isolate_);
106
107
    {
108
      HandleScope handle_scope(isolate_);
109
      size_t i = 0;
110
      for (const std::unique_ptr<Node>& n : nodes_) {
111
        Local<Object> obj = info_objects[n.get()];
112
        Local<Value> value;
113
        std::string name_str;
114
        const char* prefix = n->NamePrefix();
115
        if (prefix == nullptr) {
116
          name_str = n->Name();
117
        } else {
118
          name_str = n->NamePrefix();
119
          name_str += " ";
120
          name_str += n->Name();
121
        }
122
        if (!String::NewFromUtf8(isolate_, name_str.c_str()).ToLocal(&value) ||
123
            obj->Set(context, name_string, value).IsNothing() ||
124
            obj->Set(context,
125
                     is_root_string,
126
                     Boolean::New(isolate_, n->IsRootNode()))
127
                .IsNothing() ||
128
            obj->Set(
129
                   context,
130
                   size_string,
131
                   Number::New(isolate_, static_cast<double>(n->SizeInBytes())))
132
                .IsNothing() ||
133
            obj->Set(context, edges_string, Array::New(isolate_)).IsNothing()) {
134
          return MaybeLocal<Array>();
135
        }
136
        if (nodes->Set(context, i++, obj).IsNothing())
137
          return MaybeLocal<Array>();
138
        if (!n->IsEmbedderNode()) {
139
          value = static_cast<JSGraphJSNode*>(n.get())->JSValue();
140
          if (obj->Set(context, value_string, value).IsNothing())
141
            return MaybeLocal<Array>();
142
        }
143
      }
144
    }
145
146
    for (const std::unique_ptr<Node>& n : nodes_) {
147
      Node* wraps = n->WrapperNode();
148
      if (wraps == nullptr) continue;
149
      Local<Object> from = info_objects[n.get()];
150
      Local<Object> to = info_objects[wraps];
151
      if (from->Set(context, wraps_string, to).IsNothing())
152
        return MaybeLocal<Array>();
153
    }
154
155
    for (const auto& edge_info : edges_) {
156
      Node* source = edge_info.first;
157
      Local<Value> edges;
158
      if (!info_objects[source]->Get(context, edges_string).ToLocal(&edges) ||
159
          !edges->IsArray()) {
160
        return MaybeLocal<Array>();
161
      }
162
163
      size_t i = 0;
164
      size_t j = 0;
165
      for (const auto& edge : edge_info.second) {
166
        Local<Object> to_object = info_objects[edge.second];
167
        Local<Object> edge_obj = Object::New(isolate_);
168
        Local<Value> edge_name_value;
169
        const char* edge_name = edge.first;
170
        if (edge_name != nullptr) {
171
          if (!String::NewFromUtf8(isolate_, edge_name)
172
              .ToLocal(&edge_name_value)) {
173
            return MaybeLocal<Array>();
174
          }
175
        } else {
176
          edge_name_value = Number::New(isolate_, static_cast<double>(j++));
177
        }
178
        if (edge_obj->Set(context, name_string, edge_name_value).IsNothing() ||
179
            edge_obj->Set(context, to_string, to_object).IsNothing() ||
180
            edges.As<Array>()->Set(context, i++, edge_obj).IsNothing()) {
181
          return MaybeLocal<Array>();
182
        }
183
      }
184
    }
185
186
    return handle_scope.Escape(nodes);
187
  }
188
189
 private:
190
  Isolate* isolate_;
191
  std::unordered_set<std::unique_ptr<Node>> nodes_;
192
  std::unordered_set<JSGraphJSNode*, JSGraphJSNode::Hash, JSGraphJSNode::Equal>
193
      engine_nodes_;
194
  std::unordered_map<Node*, std::set<std::pair<const char*, Node*>>> edges_;
195
};
196
197
void BuildEmbedderGraph(const FunctionCallbackInfo<Value>& args) {
198
  Environment* env = Environment::GetCurrent(args);
199
  JSGraph graph(env->isolate());
200
  Environment::BuildEmbedderGraph(env->isolate(), &graph, env);
201
  Local<Array> ret;
202
  if (graph.CreateObject().ToLocal(&ret))
203
    args.GetReturnValue().Set(ret);
204
}
205
206
namespace {
207
5
class FileOutputStream : public v8::OutputStream {
208
 public:
209
5
  explicit FileOutputStream(FILE* stream) : stream_(stream) {}
210
211
10
  int GetChunkSize() override {
212
10
    return 65536;  // big chunks == faster
213
  }
214
215
5
  void EndOfStream() override {}
216
217
348
  WriteResult WriteAsciiChunk(char* data, int size) override {
218
348
    const size_t len = static_cast<size_t>(size);
219
348
    size_t off = 0;
220
221


1044
    while (off < len && !feof(stream_) && !ferror(stream_))
222
348
      off += fwrite(data + off, 1, len - off, stream_);
223
224
348
    return off == len ? kContinue : kAbort;
225
  }
226
227
 private:
228
  FILE* stream_;
229
};
230
231
class HeapSnapshotStream : public AsyncWrap,
232
                           public StreamBase,
233
                           public v8::OutputStream {
234
 public:
235
14
  HeapSnapshotStream(
236
      Environment* env,
237
      HeapSnapshotPointer&& snapshot,
238
14
      Local<Object> obj) :
239
      AsyncWrap(env, obj, AsyncWrap::PROVIDER_HEAPSNAPSHOT),
240
      StreamBase(env),
241
14
      snapshot_(std::move(snapshot)) {
242
14
    MakeWeak();
243
14
    StreamBase::AttachToObject(GetObject());
244
14
  }
245
246
28
  ~HeapSnapshotStream() override {}
247
248
22
  int GetChunkSize() override {
249
22
    return 65536;  // big chunks == faster
250
  }
251
252
11
  void EndOfStream() override {
253
11
    EmitRead(UV_EOF);
254
11
    snapshot_.reset();
255
11
  }
256
257
727
  WriteResult WriteAsciiChunk(char* data, int size) override {
258
727
    int len = size;
259
2181
    while (len != 0) {
260
727
      uv_buf_t buf = EmitAlloc(size);
261
727
      ssize_t avail = len;
262
727
      if (static_cast<ssize_t>(buf.len) < avail)
263
        avail = buf.len;
264
727
      memcpy(buf.base, data, avail);
265
727
      data += avail;
266
727
      len -= static_cast<int>(avail);
267
727
      EmitRead(size, buf);
268
    }
269
727
    return kContinue;
270
  }
271
272
11
  int ReadStart() override {
273
11
    CHECK_NE(snapshot_, nullptr);
274
11
    snapshot_->Serialize(this, HeapSnapshot::kJSON);
275
11
    return 0;
276
  }
277
278
738
  int ReadStop() override {
279
738
    return 0;
280
  }
281
282
  int DoShutdown(ShutdownWrap* req_wrap) override {
283
    UNREACHABLE();
284
  }
285
286
  int DoWrite(WriteWrap* w,
287
              uv_buf_t* bufs,
288
              size_t count,
289
              uv_stream_t* send_handle) override {
290
    UNREACHABLE();
291
  }
292
293
749
  bool IsAlive() override { return snapshot_ != nullptr; }
294
  bool IsClosing() override { return snapshot_ == nullptr; }
295
1501
  AsyncWrap* GetAsyncWrap() override { return this; }
296
297
7
  void MemoryInfo(MemoryTracker* tracker) const override {
298
7
    if (snapshot_ != nullptr) {
299
      tracker->TrackFieldWithSize(
300
7
          "snapshot", sizeof(*snapshot_), "HeapSnapshot");
301
    }
302
7
  }
303
304
7
  SET_MEMORY_INFO_NAME(HeapSnapshotStream)
305
7
  SET_SELF_SIZE(HeapSnapshotStream)
306
307
 private:
308
  HeapSnapshotPointer snapshot_;
309
};
310
311
5
inline void TakeSnapshot(Isolate* isolate, v8::OutputStream* out) {
312
  HeapSnapshotPointer snapshot {
313
10
      isolate->GetHeapProfiler()->TakeHeapSnapshot() };
314
5
  snapshot->Serialize(out, HeapSnapshot::kJSON);
315
5
}
316
317
}  // namespace
318
319
5
bool WriteSnapshot(Isolate* isolate, const char* filename) {
320
5
  FILE* fp = fopen(filename, "w");
321
5
  if (fp == nullptr)
322
    return false;
323
10
  FileOutputStream stream(fp);
324
5
  TakeSnapshot(isolate, &stream);
325
5
  fclose(fp);
326
5
  return true;
327
}
328
329
19
void DeleteHeapSnapshot(const HeapSnapshot* snapshot) {
330
19
  const_cast<HeapSnapshot*>(snapshot)->Delete();
331
19
}
332
333
14
BaseObjectPtr<AsyncWrap> CreateHeapSnapshotStream(
334
    Environment* env, HeapSnapshotPointer&& snapshot) {
335
28
  HandleScope scope(env->isolate());
336
337
28
  if (env->streambaseoutputstream_constructor_template().IsEmpty()) {
338
    // Create FunctionTemplate for HeapSnapshotStream
339
5
    Local<FunctionTemplate> os = FunctionTemplate::New(env->isolate());
340
10
    os->Inherit(AsyncWrap::GetConstructorTemplate(env));
341
5
    Local<ObjectTemplate> ost = os->InstanceTemplate();
342
5
    ost->SetInternalFieldCount(StreamBase::kInternalFieldCount);
343
10
    os->SetClassName(
344
5
        FIXED_ONE_BYTE_STRING(env->isolate(), "HeapSnapshotStream"));
345
5
    StreamBase::AddMethods(env, os);
346
5
    env->set_streambaseoutputstream_constructor_template(ost);
347
  }
348
349
  Local<Object> obj;
350
42
  if (!env->streambaseoutputstream_constructor_template()
351
42
           ->NewInstance(env->context())
352
14
           .ToLocal(&obj)) {
353
    return {};
354
  }
355
14
  return MakeBaseObject<HeapSnapshotStream>(env, std::move(snapshot), obj);
356
}
357
358
14
void CreateHeapSnapshotStream(const FunctionCallbackInfo<Value>& args) {
359
14
  Environment* env = Environment::GetCurrent(args);
360
  HeapSnapshotPointer snapshot {
361
28
      env->isolate()->GetHeapProfiler()->TakeHeapSnapshot() };
362
14
  CHECK(snapshot);
363
  BaseObjectPtr<AsyncWrap> stream =
364
28
      CreateHeapSnapshotStream(env, std::move(snapshot));
365
14
  if (stream)
366
42
    args.GetReturnValue().Set(stream->object());
367
14
}
368
369
4
void TriggerHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
370
4
  Environment* env = Environment::GetCurrent(args);
371
4
  Isolate* isolate = args.GetIsolate();
372
373
4
  Local<Value> filename_v = args[0];
374
375
8
  if (filename_v->IsUndefined()) {
376
6
    DiagnosticFilename name(env, "Heap", "heapsnapshot");
377
3
    if (!WriteSnapshot(isolate, *name))
378
      return;
379
6
    if (String::NewFromUtf8(isolate, *name).ToLocal(&filename_v)) {
380
6
      args.GetReturnValue().Set(filename_v);
381
    }
382
3
    return;
383
  }
384
385
2
  BufferValue path(isolate, filename_v);
386
1
  CHECK_NOT_NULL(*path);
387
1
  if (!WriteSnapshot(isolate, *path))
388
    return;
389
2
  return args.GetReturnValue().Set(filename_v);
390
}
391
392
462
void Initialize(Local<Object> target,
393
                Local<Value> unused,
394
                Local<Context> context,
395
                void* priv) {
396
462
  Environment* env = Environment::GetCurrent(context);
397
398
462
  env->SetMethod(target, "buildEmbedderGraph", BuildEmbedderGraph);
399
462
  env->SetMethod(target, "triggerHeapSnapshot", TriggerHeapSnapshot);
400
462
  env->SetMethod(target, "createHeapSnapshotStream", CreateHeapSnapshotStream);
401
462
}
402
403
4774
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
404
4774
  registry->Register(BuildEmbedderGraph);
405
4774
  registry->Register(TriggerHeapSnapshot);
406
4774
  registry->Register(CreateHeapSnapshotStream);
407
4774
}
408
409
}  // namespace heap
410
}  // namespace node
411
412
4832
NODE_MODULE_CONTEXT_AWARE_INTERNAL(heap_utils, node::heap::Initialize)
413

19294
NODE_MODULE_EXTERNAL_REFERENCE(heap_utils,
414
                               node::heap::RegisterExternalReferences)