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: 204 229 89.1 %
Date: 2019-05-05 22:32:45 Branches: 90 156 57.7 %

Line Branch Exec Source
1
#include "env-inl.h"
2
#include "stream_base-inl.h"
3
4
using v8::Array;
5
using v8::Boolean;
6
using v8::Context;
7
using v8::EmbedderGraph;
8
using v8::EscapableHandleScope;
9
using v8::FunctionCallbackInfo;
10
using v8::FunctionTemplate;
11
using v8::Global;
12
using v8::HandleScope;
13
using v8::HeapSnapshot;
14
using v8::Isolate;
15
using v8::JSON;
16
using v8::Local;
17
using v8::MaybeLocal;
18
using v8::Number;
19
using v8::Object;
20
using v8::ObjectTemplate;
21
using v8::String;
22
using v8::Value;
23
24
namespace node {
25
namespace heap {
26
27
13710
class JSGraphJSNode : public EmbedderGraph::Node {
28
 public:
29
4570
  const char* Name() override { return "<JS Node>"; }
30
4570
  size_t SizeInBytes() override { return 0; }
31
4570
  bool IsEmbedderNode() override { return false; }
32
13710
  Local<Value> JSValue() { return PersistentToLocal::Strong(persistent_); }
33
34
9140
  int IdentityHash() {
35
9140
    Local<Value> v = JSValue();
36
11420
    if (v->IsObject()) return v.As<Object>()->GetIdentityHash();
37
24000
    if (v->IsName()) return v.As<v8::Name>()->GetIdentityHash();
38
    if (v->IsInt32()) return v.As<v8::Int32>()->Value();
39
    return 0;
40
  }
41
42
4570
  JSGraphJSNode(Isolate* isolate, Local<Value> val)
43
4570
      : persistent_(isolate, val) {
44
4570
    CHECK(!val.IsEmpty());
45
4570
  }
46
47
  struct Hash {
48
9140
    inline size_t operator()(JSGraphJSNode* n) const {
49
9140
      return static_cast<size_t>(n->IdentityHash());
50
    }
51
  };
52
53
  struct Equal {
54
    inline bool operator()(JSGraphJSNode* a, JSGraphJSNode* b) const {
55
      return a->JSValue()->SameValue(b->JSValue());
56
    }
57
  };
58
59
 private:
60
  Global<Value> persistent_;
61
};
62
63
16
class JSGraph : public EmbedderGraph {
64
 public:
65
16
  explicit JSGraph(Isolate* isolate) : isolate_(isolate) {}
66
67
4570
  Node* V8Node(const Local<Value>& value) override {
68
4570
    std::unique_ptr<JSGraphJSNode> n { new JSGraphJSNode(isolate_, value) };
69
4570
    auto it = engine_nodes_.find(n.get());
70
4570
    if (it != engine_nodes_.end())
71
      return *it;
72
4570
    engine_nodes_.insert(n.get());
73
4570
    return AddNode(std::unique_ptr<Node>(n.release()));
74
  }
75
76
6363
  Node* AddNode(std::unique_ptr<Node> node) override {
77
6363
    Node* n = node.get();
78
6363
    nodes_.emplace(std::move(node));
79
6363
    return n;
80
  }
81
82
6411
  void AddEdge(Node* from, Node* to, const char* name = nullptr) override {
83
6411
    edges_[from].insert(std::make_pair(name, to));
84
6411
  }
85
86
16
  MaybeLocal<Array> CreateObject() const {
87
16
    EscapableHandleScope handle_scope(isolate_);
88
16
    Local<Context> context = isolate_->GetCurrentContext();
89
90
32
    std::unordered_map<Node*, Local<Object>> info_objects;
91
16
    Local<Array> nodes = Array::New(isolate_, nodes_.size());
92
16
    Local<String> edges_string = FIXED_ONE_BYTE_STRING(isolate_, "edges");
93
16
    Local<String> is_root_string = FIXED_ONE_BYTE_STRING(isolate_, "isRoot");
94
16
    Local<String> name_string = FIXED_ONE_BYTE_STRING(isolate_, "name");
95
16
    Local<String> size_string = FIXED_ONE_BYTE_STRING(isolate_, "size");
96
16
    Local<String> value_string = FIXED_ONE_BYTE_STRING(isolate_, "value");
97
16
    Local<String> wraps_string = FIXED_ONE_BYTE_STRING(isolate_, "wraps");
98
16
    Local<String> to_string = FIXED_ONE_BYTE_STRING(isolate_, "to");
99
100
6379
    for (const std::unique_ptr<Node>& n : nodes_)
101
6363
      info_objects[n.get()] = Object::New(isolate_);
102
103
    {
104
16
      HandleScope handle_scope(isolate_);
105
16
      size_t i = 0;
106

6379
      for (const std::unique_ptr<Node>& n : nodes_) {
107
6363
        Local<Object> obj = info_objects[n.get()];
108
        Local<Value> value;
109
6363
        std::string name_str;
110
6363
        const char* prefix = n->NamePrefix();
111
6363
        if (prefix == nullptr) {
112
4570
          name_str = n->Name();
113
        } else {
114
1793
          name_str = n->NamePrefix();
115
1793
          name_str += " ";
116
1793
          name_str += n->Name();
117
        }
118
12726
        if (!String::NewFromUtf8(
119
6363
                 isolate_, name_str.c_str(), v8::NewStringType::kNormal)
120

38178
                 .ToLocal(&value) ||
121

38178
            obj->Set(context, name_string, value).IsNothing() ||
122
            obj->Set(context,
123
                     is_root_string,
124

19089
                     Boolean::New(isolate_, n->IsRootNode()))
125

44541
                .IsNothing() ||
126
            obj->Set(context,
127
                     size_string,
128

19089
                     Number::New(isolate_, n->SizeInBytes()))
129

63630
                .IsNothing() ||
130

38178
            obj->Set(context, edges_string, Array::New(isolate_)).IsNothing()) {
131
          return MaybeLocal<Array>();
132
        }
133
19089
        if (nodes->Set(context, i++, obj).IsNothing())
134
          return MaybeLocal<Array>();
135
6363
        if (!n->IsEmbedderNode()) {
136
4570
          value = static_cast<JSGraphJSNode*>(n.get())->JSValue();
137
9140
          if (obj->Set(context, value_string, value).IsNothing())
138
            return MaybeLocal<Array>();
139
        }
140
6379
      }
141
    }
142
143
6379
    for (const std::unique_ptr<Node>& n : nodes_) {
144
6363
      Node* wraps = n->WrapperNode();
145
6363
      if (wraps == nullptr) continue;
146
      Local<Object> from = info_objects[n.get()];
147
      Local<Object> to = info_objects[wraps];
148
      if (from->Set(context, wraps_string, to).IsNothing())
149
        return MaybeLocal<Array>();
150
    }
151
152
375
    for (const auto& edge_info : edges_) {
153
359
      Node* source = edge_info.first;
154
      Local<Value> edges;
155


1795
      if (!info_objects[source]->Get(context, edges_string).ToLocal(&edges) ||
156
359
          !edges->IsArray()) {
157
        return MaybeLocal<Array>();
158
      }
159
160
359
      size_t i = 0;
161
359
      size_t j = 0;
162
6770
      for (const auto& edge : edge_info.second) {
163
6411
        Local<Object> to_object = info_objects[edge.second];
164
6411
        Local<Object> edge_obj = Object::New(isolate_);
165
        Local<Value> edge_name_value;
166
6411
        const char* edge_name = edge.first;
167
6411
        if (edge_name != nullptr) {
168
8460
          if (!String::NewFromUtf8(
169
4230
                  isolate_, edge_name, v8::NewStringType::kNormal)
170
12690
                  .ToLocal(&edge_name_value)) {
171
            return MaybeLocal<Array>();
172
          }
173
        } else {
174
4362
          edge_name_value = Number::New(isolate_, j++);
175
        }
176


44877
        if (edge_obj->Set(context, name_string, edge_name_value).IsNothing() ||
177


64110
            edge_obj->Set(context, to_string, to_object).IsNothing() ||
178

32055
            edges.As<Array>()->Set(context, i++, edge_obj).IsNothing()) {
179
          return MaybeLocal<Array>();
180
        }
181
      }
182
    }
183
184
16
    return handle_scope.Escape(nodes);
185
  }
186
187
 private:
188
  Isolate* isolate_;
189
  std::unordered_set<std::unique_ptr<Node>> nodes_;
190
  std::unordered_set<JSGraphJSNode*, JSGraphJSNode::Hash, JSGraphJSNode::Equal>
191
      engine_nodes_;
192
  std::unordered_map<Node*, std::set<std::pair<const char*, Node*>>> edges_;
193
};
194
195
16
void BuildEmbedderGraph(const FunctionCallbackInfo<Value>& args) {
196
16
  Environment* env = Environment::GetCurrent(args);
197
16
  JSGraph graph(env->isolate());
198
16
  Environment::BuildEmbedderGraph(env->isolate(), &graph, env);
199
  Local<Array> ret;
200
32
  if (graph.CreateObject().ToLocal(&ret))
201
32
    args.GetReturnValue().Set(ret);
202
16
}
203
204
namespace {
205
4
class FileOutputStream : public v8::OutputStream {
206
 public:
207
4
  explicit FileOutputStream(FILE* stream) : stream_(stream) {}
208
209
8
  int GetChunkSize() override {
210
8
    return 65536;  // big chunks == faster
211
  }
212
213
4
  void EndOfStream() override {}
214
215
184
  WriteResult WriteAsciiChunk(char* data, int size) override {
216
184
    const size_t len = static_cast<size_t>(size);
217
184
    size_t off = 0;
218
219


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