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-02-19 22:14:06 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::JSON;
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(
123
                 isolate_, name_str.c_str(), v8::NewStringType::kNormal)
124
                 .ToLocal(&value) ||
125
            obj->Set(context, name_string, value).IsNothing() ||
126
            obj->Set(context,
127
                     is_root_string,
128
                     Boolean::New(isolate_, n->IsRootNode()))
129
                .IsNothing() ||
130
            obj->Set(context,
131
                     size_string,
132
                     Number::New(isolate_, n->SizeInBytes()))
133
                .IsNothing() ||
134
            obj->Set(context, edges_string, Array::New(isolate_)).IsNothing()) {
135
          return MaybeLocal<Array>();
136
        }
137
        if (nodes->Set(context, i++, obj).IsNothing())
138
          return MaybeLocal<Array>();
139
        if (!n->IsEmbedderNode()) {
140
          value = static_cast<JSGraphJSNode*>(n.get())->JSValue();
141
          if (obj->Set(context, value_string, value).IsNothing())
142
            return MaybeLocal<Array>();
143
        }
144
      }
145
    }
146
147
    for (const std::unique_ptr<Node>& n : nodes_) {
148
      Node* wraps = n->WrapperNode();
149
      if (wraps == nullptr) continue;
150
      Local<Object> from = info_objects[n.get()];
151
      Local<Object> to = info_objects[wraps];
152
      if (from->Set(context, wraps_string, to).IsNothing())
153
        return MaybeLocal<Array>();
154
    }
155
156
    for (const auto& edge_info : edges_) {
157
      Node* source = edge_info.first;
158
      Local<Value> edges;
159
      if (!info_objects[source]->Get(context, edges_string).ToLocal(&edges) ||
160
          !edges->IsArray()) {
161
        return MaybeLocal<Array>();
162
      }
163
164
      size_t i = 0;
165
      size_t j = 0;
166
      for (const auto& edge : edge_info.second) {
167
        Local<Object> to_object = info_objects[edge.second];
168
        Local<Object> edge_obj = Object::New(isolate_);
169
        Local<Value> edge_name_value;
170
        const char* edge_name = edge.first;
171
        if (edge_name != nullptr) {
172
          if (!String::NewFromUtf8(
173
                  isolate_, edge_name, v8::NewStringType::kNormal)
174
                  .ToLocal(&edge_name_value)) {
175
            return MaybeLocal<Array>();
176
          }
177
        } else {
178
          edge_name_value = Number::New(isolate_, j++);
179
        }
180
        if (edge_obj->Set(context, name_string, edge_name_value).IsNothing() ||
181
            edge_obj->Set(context, to_string, to_object).IsNothing() ||
182
            edges.As<Array>()->Set(context, i++, edge_obj).IsNothing()) {
183
          return MaybeLocal<Array>();
184
        }
185
      }
186
    }
187
188
    return handle_scope.Escape(nodes);
189
  }
190
191
 private:
192
  Isolate* isolate_;
193
  std::unordered_set<std::unique_ptr<Node>> nodes_;
194
  std::unordered_set<JSGraphJSNode*, JSGraphJSNode::Hash, JSGraphJSNode::Equal>
195
      engine_nodes_;
196
  std::unordered_map<Node*, std::set<std::pair<const char*, Node*>>> edges_;
197
};
198
199
void BuildEmbedderGraph(const FunctionCallbackInfo<Value>& args) {
200
  Environment* env = Environment::GetCurrent(args);
201
  JSGraph graph(env->isolate());
202
  Environment::BuildEmbedderGraph(env->isolate(), &graph, env);
203
  Local<Array> ret;
204
  if (graph.CreateObject().ToLocal(&ret))
205
    args.GetReturnValue().Set(ret);
206
}
207
208
namespace {
209
4
class FileOutputStream : public v8::OutputStream {
210
 public:
211
4
  explicit FileOutputStream(FILE* stream) : stream_(stream) {}
212
213
8
  int GetChunkSize() override {
214
8
    return 65536;  // big chunks == faster
215
  }
216
217
4
  void EndOfStream() override {}
218
219
202
  WriteResult WriteAsciiChunk(char* data, int size) override {
220
202
    const size_t len = static_cast<size_t>(size);
221
202
    size_t off = 0;
222
223


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