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: 107 231 46.3 %
Date: 2020-06-24 22:13:30 Branches: 24 106 22.6 %

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


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