Coverage Report

Created: 2025-12-10 07:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/node/src/inspector_profiler.cc
Line
Count
Source
1
#include "inspector_profiler.h"
2
#include "base_object-inl.h"
3
#include "debug_utils-inl.h"
4
#include "diagnosticfilename-inl.h"
5
#include "memory_tracker-inl.h"
6
#include "node_errors.h"
7
#include "node_external_reference.h"
8
#include "node_file.h"
9
#include "node_internals.h"
10
#include "util-inl.h"
11
#include "uv.h"
12
#include "v8-inspector.h"
13
14
#include <cinttypes>
15
#include <limits>
16
#include <sstream>
17
#include "simdutf.h"
18
19
namespace node {
20
namespace profiler {
21
22
using errors::TryCatchScope;
23
using v8::Context;
24
using v8::Function;
25
using v8::FunctionCallbackInfo;
26
using v8::HandleScope;
27
using v8::Isolate;
28
using v8::Local;
29
using v8::NewStringType;
30
using v8::Object;
31
using v8::String;
32
using v8::Value;
33
34
using v8_inspector::StringView;
35
36
V8ProfilerConnection::V8ProfilerConnection(Environment* env)
37
0
    : session_(env->inspector_agent()->Connect(
38
0
          std::make_unique<V8ProfilerConnection::V8ProfilerSessionDelegate>(
39
0
              this),
40
0
          false)),
41
0
      env_(env) {}
42
43
uint64_t V8ProfilerConnection::DispatchMessage(const char* method,
44
                                               const char* params,
45
0
                                               bool is_profile_request) {
46
0
  std::stringstream ss;
47
0
  uint64_t id = next_id();
48
  // V8's inspector protocol cannot take an integer beyond the int32_t limit.
49
  // In practice the id we use is up to 3-5 for the profilers we have
50
  // here.
51
0
  CHECK_LT(id, static_cast<uint64_t>(std::numeric_limits<int32_t>::max()));
52
0
  ss << R"({ "id": )" << id;
53
0
  DCHECK(method != nullptr);
54
0
  ss << R"(, "method": ")" << method << '"';
55
0
  if (params != nullptr) {
56
0
    ss << R"(, "params": )" << params;
57
0
  }
58
0
  ss << " }";
59
0
  std::string message = ss.str();
60
0
  const uint8_t* message_data =
61
0
      reinterpret_cast<const uint8_t*>(message.c_str());
62
  // Save the id of the profile request to identify its response.
63
0
  if (is_profile_request) {
64
0
    profile_ids_.insert(id);
65
0
  }
66
0
  Debug(env(),
67
0
        DebugCategory::INSPECTOR_PROFILER,
68
0
        "Dispatching message %s\n",
69
0
        message.c_str());
70
0
  session_->Dispatch(StringView(message_data, message.length()));
71
0
  return id;
72
0
}
73
74
static void WriteResult(Environment* env,
75
                        const char* path,
76
0
                        std::string_view profile) {
77
0
  uv_buf_t buf =
78
0
      uv_buf_init(const_cast<char*>(profile.data()), profile.length());
79
0
  int ret = WriteFileSync(path, buf);
80
0
  if (ret != 0) {
81
0
    char err_buf[128];
82
0
    uv_err_name_r(ret, err_buf, sizeof(err_buf));
83
0
    fprintf(stderr, "%s: Failed to write file %s\n", err_buf, path);
84
0
    return;
85
0
  }
86
0
  Debug(env, DebugCategory::INSPECTOR_PROFILER, "Written result to %s\n", path);
87
0
}
88
89
bool StringViewToUTF8(const v8_inspector::StringView& source,
90
                      std::vector<char>* utf8_out,
91
                      size_t* utf8_length,
92
0
                      size_t padding) {
93
0
  size_t source_len = source.length();
94
0
  if (source.is8Bit()) {
95
0
    const char* latin1 = reinterpret_cast<const char*>(source.characters8());
96
0
    *utf8_length = simdutf::utf8_length_from_latin1(latin1, source_len);
97
0
    utf8_out->resize(*utf8_length + padding);
98
0
    size_t result_len =
99
0
        simdutf::convert_latin1_to_utf8(latin1, source_len, utf8_out->data());
100
0
    return *utf8_length == result_len;
101
0
  }
102
103
0
  const char16_t* utf16 =
104
0
      reinterpret_cast<const char16_t*>(source.characters16());
105
0
  *utf8_length = simdutf::utf8_length_from_utf16(utf16, source_len);
106
0
  utf8_out->resize(*utf8_length + padding);
107
0
  size_t result_len =
108
0
      simdutf::convert_utf16_to_utf8(utf16, source_len, utf8_out->data());
109
0
  return *utf8_length == result_len;
110
0
}
111
112
void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend(
113
0
    const v8_inspector::StringView& message) {
114
0
  Environment* env = connection_->env();
115
0
  Isolate* isolate = env->isolate();
116
0
  HandleScope handle_scope(isolate);
117
0
  Local<Context> context = env->context();
118
0
  Context::Scope context_scope(context);
119
0
  const char* type = connection_->type();
120
121
0
  Debug(env,
122
0
        DebugCategory::INSPECTOR_PROFILER,
123
0
        "Received %s profile message\n",
124
0
        type);
125
126
0
  std::vector<char> message_utf8;
127
0
  size_t message_utf8_length;
128
0
  if (!StringViewToUTF8(message,
129
0
                        &message_utf8,
130
0
                        &message_utf8_length,
131
0
                        simdjson::SIMDJSON_PADDING)) {
132
0
    fprintf(
133
0
        stderr, "Failed to convert %s profile message to UTF8 string\n", type);
134
0
    return;
135
0
  }
136
137
0
  simdjson::ondemand::document parsed;
138
0
  simdjson::ondemand::object response;
139
0
  if (connection_->json_parser_
140
0
          .iterate(
141
0
              message_utf8.data(), message_utf8_length, message_utf8.size())
142
0
          .get(parsed) ||
143
0
      parsed.get_object().get(response)) {
144
0
    fprintf(
145
0
        stderr, "Failed to parse %s profile result as JSON object:\n", type);
146
0
    fprintf(stderr,
147
0
            "%.*s\n",
148
0
            static_cast<int>(message_utf8_length),
149
0
            message_utf8.data());
150
0
    return;
151
0
  }
152
153
0
  uint64_t id;
154
0
  if (response["id"].get_uint64().get(id)) {
155
0
    fprintf(stderr, "Cannot retrieve id from %s profile response:\n", type);
156
0
    fprintf(stderr,
157
0
            "%.*s\n",
158
0
            static_cast<int>(message_utf8_length),
159
0
            message_utf8.data());
160
0
    return;
161
0
  }
162
163
0
  if (!connection_->HasProfileId(id)) {
164
0
    Debug(env,
165
0
          DebugCategory::INSPECTOR_PROFILER,
166
0
          "%s\n",
167
0
          std::string_view(message_utf8.data(), message_utf8_length));
168
0
    return;
169
0
  } else {
170
0
    Debug(env,
171
0
          DebugCategory::INSPECTOR_PROFILER,
172
0
          "Writing profile response (id = %" PRIu64 ")\n",
173
0
          id);
174
0
  }
175
176
0
  simdjson::ondemand::object result;
177
  // Get message.result from the response.
178
0
  if (response["result"].get_object().get(result)) {
179
0
    fprintf(stderr, "Failed to get 'result' from %s profile response:\n", type);
180
0
    fprintf(stderr,
181
0
            "%.*s\n",
182
0
            static_cast<int>(message_utf8_length),
183
0
            message_utf8.data());
184
0
    return;
185
0
  }
186
187
0
  connection_->WriteProfile(&result);
188
0
  connection_->RemoveProfileId(id);
189
0
}
190
191
0
static bool EnsureDirectory(const std::string& directory, const char* type) {
192
0
  fs::FSReqWrapSync req_wrap_sync;
193
0
  int ret = fs::MKDirpSync(nullptr, &req_wrap_sync.req, directory, 0777,
194
0
                           nullptr);
195
0
  if (ret < 0 && ret != UV_EEXIST) {
196
0
    char err_buf[128];
197
0
    uv_err_name_r(ret, err_buf, sizeof(err_buf));
198
0
    fprintf(stderr,
199
0
            "%s: Failed to create %s profile directory %s\n",
200
0
            err_buf,
201
0
            type,
202
0
            directory.c_str());
203
0
    return false;
204
0
  }
205
0
  return true;
206
0
}
207
208
0
std::string V8CoverageConnection::GetFilename() const {
209
0
  uint64_t timestamp =
210
0
      static_cast<uint64_t>(GetCurrentTimeInMicroseconds() / 1000);
211
0
  return SPrintF("coverage-%s-%s-%s.json",
212
0
      uv_os_getpid(),
213
0
      timestamp,
214
0
      env()->thread_id());
215
0
}
216
217
std::optional<std::string_view> V8ProfilerConnection::GetProfile(
218
0
    simdjson::ondemand::object* result) {
219
0
  simdjson::ondemand::object profile_object;
220
0
  if ((*result)["profile"].get_object().get(profile_object)) {
221
0
    fprintf(
222
0
        stderr, "'profile' from %s profile result is not an Object\n", type());
223
0
    return std::nullopt;
224
0
  }
225
0
  std::string_view profile_raw;
226
0
  if (profile_object.raw_json().get(profile_raw)) {
227
0
    fprintf(stderr,
228
0
            "Cannot get raw string of the 'profile' field from %s profile\n",
229
0
            type());
230
0
    return std::nullopt;
231
0
  }
232
0
  return profile_raw;
233
0
}
234
235
0
void V8ProfilerConnection::WriteProfile(simdjson::ondemand::object* result) {
236
  // Generate the profile output from the subclass.
237
0
  auto profile_opt = GetProfile(result);
238
0
  if (!profile_opt.has_value()) {
239
0
    return;
240
0
  }
241
0
  std::string_view profile = profile_opt.value();
242
243
  // Create the directory if necessary.
244
0
  std::string directory = GetDirectory();
245
0
  DCHECK(!directory.empty());
246
0
  if (!EnsureDirectory(directory, type())) {
247
0
    return;
248
0
  }
249
250
0
  std::string filename = GetFilename();
251
0
  DCHECK(!filename.empty());
252
0
  std::string path = directory + kPathSeparator + filename;
253
254
0
  WriteResult(env_, path.c_str(), profile);
255
0
}
256
257
0
void V8CoverageConnection::WriteProfile(simdjson::ondemand::object* result) {
258
0
  Isolate* isolate = env_->isolate();
259
0
  HandleScope handle_scope(isolate);
260
261
  // This is only set up during pre-execution (when the environment variables
262
  // becomes available in the JS land). If it's empty, we don't have coverage
263
  // directory path (which is resolved in JS land at the moment) either, so
264
  // the best we could to is to just discard the profile and do nothing.
265
  // This should only happen in half-baked Environments created using the
266
  // embedder API.
267
0
  if (env_->source_map_cache_getter().IsEmpty()) {
268
0
    return;
269
0
  }
270
271
0
  Local<Context> context = env_->context();
272
0
  Context::Scope context_scope(context);
273
274
  // Generate the profile output from the subclass.
275
0
  auto profile_opt = GetProfile(result);
276
0
  if (!profile_opt.has_value()) {
277
0
    return;
278
0
  }
279
0
  std::string_view profile = profile_opt.value();
280
281
  // append source-map cache information to coverage object:
282
0
  Local<Value> source_map_cache_v;
283
0
  {
284
0
    TryCatchScope try_catch(env());
285
0
    {
286
0
      Isolate::AllowJavascriptExecutionScope allow_js_here(isolate);
287
0
      Local<Function> source_map_cache_getter = env_->source_map_cache_getter();
288
0
      if (!source_map_cache_getter->Call(
289
0
              context, Undefined(isolate), 0, nullptr)
290
0
              .ToLocal(&source_map_cache_v)) {
291
0
        return;
292
0
      }
293
0
    }
294
0
    if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
295
0
      PrintCaughtException(isolate, context, try_catch);
296
0
    }
297
0
  }
298
299
  // Create the directory if necessary.
300
0
  std::string directory = GetDirectory();
301
0
  DCHECK(!directory.empty());
302
0
  if (!EnsureDirectory(directory, type())) {
303
0
    return;
304
0
  }
305
306
0
  std::string filename = GetFilename();
307
0
  DCHECK(!filename.empty());
308
0
  std::string path = directory + kPathSeparator + filename;
309
310
  // Only insert source map cache when there's source map data at all.
311
0
  if (!source_map_cache_v->IsUndefined()) {
312
    // It would be more performant to just find the last } and insert the source
313
    // map cache in front of it, but source map cache is still experimental
314
    // anyway so just re-parse it with V8 for now.
315
0
    Local<String> profile_str;
316
0
    if (!v8::String::NewFromUtf8(isolate,
317
0
                                 profile.data(),
318
0
                                 v8::NewStringType::kNormal,
319
0
                                 profile.length())
320
0
             .ToLocal(&profile_str)) {
321
0
      fprintf(stderr, "Failed to re-parse %s profile as UTF8\n", type());
322
0
      return;
323
0
    }
324
0
    Local<Value> profile_value;
325
0
    if (!v8::JSON::Parse(context, profile_str).ToLocal(&profile_value) ||
326
0
        !profile_value->IsObject()) {
327
0
      fprintf(stderr, "Failed to re-parse %s profile from JSON\n", type());
328
0
      return;
329
0
    }
330
0
    if (profile_value.As<Object>()
331
0
            ->Set(context,
332
0
                  FIXED_ONE_BYTE_STRING(isolate, "source-map-cache"),
333
0
                  source_map_cache_v)
334
0
            .IsNothing()) {
335
0
      fprintf(stderr,
336
0
              "Failed to insert source map cache into %s profile\n",
337
0
              type());
338
0
      return;
339
0
    }
340
0
    Local<String> result_s;
341
0
    if (!v8::JSON::Stringify(context, profile_value).ToLocal(&result_s)) {
342
0
      fprintf(stderr, "Failed to stringify %s profile result\n", type());
343
0
      return;
344
0
    }
345
0
    Utf8Value result_utf8(isolate, result_s);
346
0
    WriteResult(env_, path.c_str(), result_utf8.ToStringView());
347
0
  } else {
348
0
    WriteResult(env_, path.c_str(), profile);
349
0
  }
350
0
}
351
352
std::optional<std::string_view> V8CoverageConnection::GetProfile(
353
0
    simdjson::ondemand::object* result) {
354
0
  std::string_view profile_raw;
355
0
  if (result->raw_json().get(profile_raw)) {
356
0
    fprintf(stderr,
357
0
            "Cannot get raw string of the 'profile' field from %s profile\n",
358
0
            type());
359
0
    return std::nullopt;
360
0
  }
361
0
  return profile_raw;
362
0
}
363
364
0
std::string V8CoverageConnection::GetDirectory() const {
365
0
  return env()->coverage_directory();
366
0
}
367
368
0
void V8CoverageConnection::Start() {
369
0
  DispatchMessage("Profiler.enable");
370
0
  DispatchMessage("Profiler.startPreciseCoverage",
371
0
                  R"({ "callCount": true, "detailed": true })");
372
0
}
373
374
0
void V8CoverageConnection::TakeCoverage() {
375
0
  DispatchMessage("Profiler.takePreciseCoverage", nullptr, true);
376
0
}
377
378
0
void V8CoverageConnection::StopCoverage() {
379
0
  DispatchMessage("Profiler.stopPreciseCoverage");
380
0
}
381
382
0
void V8CoverageConnection::End() {
383
0
  Debug(env_,
384
0
      DebugCategory::INSPECTOR_PROFILER,
385
0
      "V8CoverageConnection::End(), ending = %d\n", ending_);
386
0
  if (ending_) {
387
0
    return;
388
0
  }
389
0
  ending_ = true;
390
0
  TakeCoverage();
391
0
}
392
393
0
std::string V8CpuProfilerConnection::GetDirectory() const {
394
0
  return env()->cpu_prof_dir();
395
0
}
396
397
0
std::string V8CpuProfilerConnection::GetFilename() const {
398
0
  return env()->cpu_prof_name();
399
0
}
400
401
0
void V8CpuProfilerConnection::Start() {
402
0
  DispatchMessage("Profiler.enable");
403
0
  std::string params = R"({ "interval": )";
404
0
  params += std::to_string(env()->cpu_prof_interval());
405
0
  params += " }";
406
0
  DispatchMessage("Profiler.setSamplingInterval", params.c_str());
407
0
  DispatchMessage("Profiler.start");
408
0
}
409
410
0
void V8CpuProfilerConnection::End() {
411
0
  Debug(env_,
412
0
      DebugCategory::INSPECTOR_PROFILER,
413
0
      "V8CpuProfilerConnection::End(), ending = %d\n", ending_);
414
0
  if (ending_) {
415
0
    return;
416
0
  }
417
0
  ending_ = true;
418
0
  DispatchMessage("Profiler.stop", nullptr, true);
419
0
}
420
421
0
std::string V8HeapProfilerConnection::GetDirectory() const {
422
0
  return env()->heap_prof_dir();
423
0
}
424
425
0
std::string V8HeapProfilerConnection::GetFilename() const {
426
0
  return env()->heap_prof_name();
427
0
}
428
429
0
void V8HeapProfilerConnection::Start() {
430
0
  DispatchMessage("HeapProfiler.enable");
431
0
  std::string params = R"({ "samplingInterval": )";
432
0
  params += std::to_string(env()->heap_prof_interval());
433
0
  params += " }";
434
0
  DispatchMessage("HeapProfiler.startSampling", params.c_str());
435
0
}
436
437
0
void V8HeapProfilerConnection::End() {
438
0
  Debug(env_,
439
0
      DebugCategory::INSPECTOR_PROFILER,
440
0
      "V8HeapProfilerConnection::End(), ending = %d\n", ending_);
441
0
  if (ending_) {
442
0
    return;
443
0
  }
444
0
  ending_ = true;
445
0
  DispatchMessage("HeapProfiler.stopSampling", nullptr, true);
446
0
}
447
448
// For now, we only support coverage profiling, but we may add more
449
// in the future.
450
35
static void EndStartedProfilers(Environment* env) {
451
  // TODO(joyeechueng): merge these connections and use one session per env.
452
35
  Debug(env, DebugCategory::INSPECTOR_PROFILER, "EndStartedProfilers\n");
453
35
  V8ProfilerConnection* connection = env->cpu_profiler_connection();
454
35
  if (connection != nullptr) {
455
0
    connection->End();
456
0
  }
457
458
35
  connection = env->heap_profiler_connection();
459
35
  if (connection != nullptr) {
460
0
    connection->End();
461
0
  }
462
463
35
  connection = env->coverage_connection();
464
35
  if (connection != nullptr) {
465
0
    connection->End();
466
0
  }
467
35
}
468
469
0
static std::string ReplacePlaceholders(const std::string& pattern) {
470
0
  std::string result = pattern;
471
472
0
  static const std::unordered_map<std::string, std::function<std::string()>>
473
0
      kPlaceholderMap = {
474
0
          {"${pid}", []() { return std::to_string(uv_os_getpid()); }},
475
          // TODO(haramj): Add more placeholders as needed.
476
0
      };
477
478
0
  for (const auto& [placeholder, getter] : kPlaceholderMap) {
479
0
    size_t pos = 0;
480
0
    while ((pos = result.find(placeholder, pos)) != std::string::npos) {
481
0
      const std::string value = getter();
482
0
      result.replace(pos, placeholder.length(), value);
483
0
      pos += value.length();
484
0
    }
485
0
  }
486
487
0
  return result;
488
0
}
489
490
35
void StartProfilers(Environment* env) {
491
35
  AtExit(env, [](void* env) {
492
35
    EndStartedProfilers(static_cast<Environment*>(env));
493
35
  }, env);
494
495
35
  std::string coverage_str =
496
35
      env->env_vars()->Get("NODE_V8_COVERAGE").value_or(std::string());
497
35
  if (!coverage_str.empty() || env->options()->test_runner_coverage) {
498
0
    CHECK_NULL(env->coverage_connection());
499
0
    env->set_coverage_connection(std::make_unique<V8CoverageConnection>(env));
500
0
    env->coverage_connection()->Start();
501
0
  }
502
35
  if (env->options()->cpu_prof) {
503
0
    const std::string& dir = env->options()->cpu_prof_dir;
504
0
    env->set_cpu_prof_interval(env->options()->cpu_prof_interval);
505
0
    env->set_cpu_prof_dir(dir.empty() ? Environment::GetCwd(env->exec_path())
506
0
                                      : dir);
507
0
    if (env->options()->cpu_prof_name.empty()) {
508
0
      DiagnosticFilename filename(env, "CPU", "cpuprofile");
509
0
      env->set_cpu_prof_name(*filename);
510
0
    } else {
511
0
      std::string resolved_name =
512
0
          ReplacePlaceholders(env->options()->cpu_prof_name);
513
0
      env->set_cpu_prof_name(resolved_name);
514
0
    }
515
0
    CHECK_NULL(env->cpu_profiler_connection());
516
0
    env->set_cpu_profiler_connection(
517
0
        std::make_unique<V8CpuProfilerConnection>(env));
518
0
    env->cpu_profiler_connection()->Start();
519
0
  }
520
35
  if (env->options()->heap_prof) {
521
0
    const std::string& dir = env->options()->heap_prof_dir;
522
0
    env->set_heap_prof_interval(env->options()->heap_prof_interval);
523
0
    env->set_heap_prof_dir(dir.empty() ? Environment::GetCwd(env->exec_path())
524
0
                                       : dir);
525
0
    if (env->options()->heap_prof_name.empty()) {
526
0
      DiagnosticFilename filename(env, "Heap", "heapprofile");
527
0
      env->set_heap_prof_name(*filename);
528
0
    } else {
529
0
      env->set_heap_prof_name(env->options()->heap_prof_name);
530
0
    }
531
0
    env->set_heap_profiler_connection(
532
0
        std::make_unique<profiler::V8HeapProfilerConnection>(env));
533
0
    env->heap_profiler_connection()->Start();
534
0
  }
535
35
}
536
537
0
static void SetCoverageDirectory(const FunctionCallbackInfo<Value>& args) {
538
0
  CHECK(args[0]->IsString());
539
0
  Environment* env = Environment::GetCurrent(args);
540
0
  node::Utf8Value directory(env->isolate(), args[0].As<String>());
541
0
  env->set_coverage_directory(*directory);
542
0
}
543
544
545
0
static void SetSourceMapCacheGetter(const FunctionCallbackInfo<Value>& args) {
546
0
  CHECK(args[0]->IsFunction());
547
0
  Environment* env = Environment::GetCurrent(args);
548
0
  env->set_source_map_cache_getter(args[0].As<Function>());
549
0
}
550
551
0
static void TakeCoverage(const FunctionCallbackInfo<Value>& args) {
552
0
  Environment* env = Environment::GetCurrent(args);
553
0
  V8CoverageConnection* connection = env->coverage_connection();
554
555
0
  Debug(
556
0
    env,
557
0
    DebugCategory::INSPECTOR_PROFILER,
558
0
    "TakeCoverage, connection %s nullptr\n",
559
0
    connection == nullptr ? "==" : "!=");
560
561
0
  if (connection != nullptr) {
562
0
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "taking coverage\n");
563
0
    connection->TakeCoverage();
564
0
  }
565
0
}
566
567
0
static void StopCoverage(const FunctionCallbackInfo<Value>& args) {
568
0
  Environment* env = Environment::GetCurrent(args);
569
0
  V8CoverageConnection* connection = env->coverage_connection();
570
571
0
  Debug(env,
572
0
        DebugCategory::INSPECTOR_PROFILER,
573
0
        "StopCoverage, connection %s nullptr\n",
574
0
        connection == nullptr ? "==" : "!=");
575
576
0
  if (connection != nullptr) {
577
0
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "Stopping coverage\n");
578
0
    connection->StopCoverage();
579
0
  }
580
0
}
581
582
0
static void EndCoverage(const FunctionCallbackInfo<Value>& args) {
583
0
  Environment* env = Environment::GetCurrent(args);
584
0
  V8CoverageConnection* connection = env->coverage_connection();
585
586
0
  Debug(env,
587
0
        DebugCategory::INSPECTOR_PROFILER,
588
0
        "EndCoverage, connection %s nullptr\n",
589
0
        connection == nullptr ? "==" : "!=");
590
591
0
  if (connection != nullptr) {
592
0
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage\n");
593
0
    connection->End();
594
0
  }
595
0
}
596
597
static void Initialize(Local<Object> target,
598
                       Local<Value> unused,
599
                       Local<Context> context,
600
0
                       void* priv) {
601
0
  SetMethod(context, target, "setCoverageDirectory", SetCoverageDirectory);
602
0
  SetMethod(
603
0
      context, target, "setSourceMapCacheGetter", SetSourceMapCacheGetter);
604
0
  SetMethod(context, target, "takeCoverage", TakeCoverage);
605
0
  SetMethod(context, target, "stopCoverage", StopCoverage);
606
0
  SetMethod(context, target, "endCoverage", EndCoverage);
607
0
}
608
609
0
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
610
0
  registry->Register(SetCoverageDirectory);
611
0
  registry->Register(SetSourceMapCacheGetter);
612
0
  registry->Register(TakeCoverage);
613
0
  registry->Register(StopCoverage);
614
0
  registry->Register(EndCoverage);
615
0
}
616
617
}  // namespace profiler
618
}  // namespace node
619
620
NODE_BINDING_CONTEXT_AWARE_INTERNAL(profiler, node::profiler::Initialize)
621
NODE_BINDING_EXTERNAL_REFERENCE(profiler,
622
                                node::profiler::RegisterExternalReferences)