GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/inspector_profiler.cc Lines: 220 256 85.9 %
Date: 2020-02-27 22:14:15 Branches: 86 128 67.2 %

Line Branch Exec 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_file.h"
7
#include "node_errors.h"
8
#include "node_internals.h"
9
#include "util-inl.h"
10
#include "v8-inspector.h"
11
12
#include <sstream>
13
14
namespace node {
15
namespace profiler {
16
17
using errors::TryCatchScope;
18
using v8::Context;
19
using v8::Function;
20
using v8::FunctionCallbackInfo;
21
using v8::HandleScope;
22
using v8::Isolate;
23
using v8::Local;
24
using v8::MaybeLocal;
25
using v8::NewStringType;
26
using v8::Object;
27
using v8::String;
28
using v8::Value;
29
30
using v8_inspector::StringView;
31
32
4376
V8ProfilerConnection::V8ProfilerConnection(Environment* env)
33
    : session_(env->inspector_agent()->Connect(
34
8753
          std::make_unique<V8ProfilerConnection::V8ProfilerSessionDelegate>(
35
              this),
36
          false)),
37
13128
      env_(env) {}
38
39
13129
size_t V8ProfilerConnection::DispatchMessage(const char* method,
40
                                             const char* params) {
41
26260
  std::stringstream ss;
42
13129
  size_t id = next_id();
43
13128
  ss << R"({ "id": )" << id;
44
  DCHECK(method != nullptr);
45
13130
  ss << R"(, "method": ")" << method << '"';
46
13131
  if (params != nullptr) {
47
4377
    ss << R"(, "params": )" << params;
48
  }
49
13131
  ss << " }";
50
26260
  std::string message = ss.str();
51
  const uint8_t* message_data =
52
13130
      reinterpret_cast<const uint8_t*>(message.c_str());
53
13129
  Debug(env(),
54
        DebugCategory::INSPECTOR_PROFILER,
55
        "Dispatching message %s\n",
56
26258
        message.c_str());
57
13129
  session_->Dispatch(StringView(message_data, message.length()));
58
  // TODO(joyeecheung): use this to identify the ending message.
59
26262
  return id;
60
}
61
62
4350
static void WriteResult(Environment* env,
63
                        const char* path,
64
                        Local<String> result) {
65
4350
  int ret = WriteFileSync(env->isolate(), path, result);
66
4350
  if (ret != 0) {
67
    char err_buf[128];
68
    uv_err_name_r(ret, err_buf, sizeof(err_buf));
69
    fprintf(stderr, "%s: Failed to write file %s\n", err_buf, path);
70
    return;
71
  }
72
  Debug(env, DebugCategory::INSPECTOR_PROFILER, "Written result to %s\n", path);
73
}
74
75
13131
void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend(
76
    const v8_inspector::StringView& message) {
77
13131
  Environment* env = connection_->env();
78
13131
  Isolate* isolate = env->isolate();
79
17498
  HandleScope handle_scope(isolate);
80
17498
  Context::Scope context_scope(env->context());
81
82
  // TODO(joyeecheung): always parse the message so that we can use the id to
83
  // identify ending messages as well as printing the message in the debug
84
  // output when there is an error.
85
13131
  const char* type = connection_->type();
86
  Debug(env,
87
        DebugCategory::INSPECTOR_PROFILER,
88
        "Receive %s profile message, ending = %s\n",
89
        type,
90
26262
        connection_->ending() ? "true" : "false");
91
13131
  if (!connection_->ending()) {
92
8764
    return;
93
  }
94
95
  // Convert StringView to a Local<String>.
96
  Local<String> message_str;
97
8734
  if (!String::NewFromTwoByte(isolate,
98
                              message.characters16(),
99
                              NewStringType::kNormal,
100
4367
                              message.length())
101
4367
           .ToLocal(&message_str)) {
102
    fprintf(stderr, "Failed to convert %s profile message\n", type);
103
    return;
104
  }
105
106
4367
  connection_->WriteProfile(message_str);
107
}
108
109
4350
static bool EnsureDirectory(const std::string& directory, const char* type) {
110
8700
  fs::FSReqWrapSync req_wrap_sync;
111
  int ret = fs::MKDirpSync(nullptr, &req_wrap_sync.req, directory, 0777,
112
4350
                           nullptr);
113

4350
  if (ret < 0 && ret != UV_EEXIST) {
114
    char err_buf[128];
115
    uv_err_name_r(ret, err_buf, sizeof(err_buf));
116
    fprintf(stderr,
117
            "%s: Failed to create %s profile directory %s\n",
118
            err_buf,
119
            type,
120
            directory.c_str());
121
    return false;
122
  }
123
4350
  return true;
124
}
125
126
4330
std::string V8CoverageConnection::GetFilename() const {
127
8660
  std::string thread_id = std::to_string(env()->thread_id());
128
8660
  std::string pid = std::to_string(uv_os_getpid());
129
  std::string timestamp = std::to_string(
130
8660
      static_cast<uint64_t>(GetCurrentTimeInMicroseconds() / 1000));
131
  char filename[1024];
132
4330
  snprintf(filename,
133
           sizeof(filename),
134
           "coverage-%s-%s-%s.json",
135
           pid.c_str(),
136
           timestamp.c_str(),
137
4330
           thread_id.c_str());
138
8660
  return filename;
139
}
140
141
4367
static MaybeLocal<Object> ParseProfile(Environment* env,
142
                                       Local<String> message,
143
                                       const char* type) {
144
4367
  Local<Context> context = env->context();
145
4367
  Isolate* isolate = env->isolate();
146
147
  // Get message.result from the response
148
  Local<Value> parsed;
149

13101
  if (!v8::JSON::Parse(context, message).ToLocal(&parsed) ||
150
4367
      !parsed->IsObject()) {
151
    fprintf(stderr, "Failed to parse %s profile result as JSON object\n", type);
152
    return MaybeLocal<Object>();
153
  }
154
155
  Local<Value> result_v;
156
17468
  if (!parsed.As<Object>()
157
17468
           ->Get(context, FIXED_ONE_BYTE_STRING(isolate, "result"))
158
4367
           .ToLocal(&result_v)) {
159
    fprintf(stderr, "Failed to get 'result' from %s profile message\n", type);
160
    return MaybeLocal<Object>();
161
  }
162
163
4367
  if (!result_v->IsObject()) {
164
    fprintf(
165
        stderr, "'result' from %s profile message is not an object\n", type);
166
    return MaybeLocal<Object>();
167
  }
168
169
4367
  return result_v.As<Object>();
170
}
171
172
20
void V8ProfilerConnection::WriteProfile(Local<String> message) {
173
20
  Local<Context> context = env_->context();
174
175
  // Get message.result from the response.
176
  Local<Object> result;
177
40
  if (!ParseProfile(env_, message, type()).ToLocal(&result)) {
178
    return;
179
  }
180
  // Generate the profile output from the subclass.
181
  Local<Object> profile;
182
40
  if (!GetProfile(result).ToLocal(&profile)) {
183
    return;
184
  }
185
186
  Local<String> result_s;
187
40
  if (!v8::JSON::Stringify(context, profile).ToLocal(&result_s)) {
188
    fprintf(stderr, "Failed to stringify %s profile result\n", type());
189
    return;
190
  }
191
192
  // Create the directory if necessary.
193
40
  std::string directory = GetDirectory();
194
  DCHECK(!directory.empty());
195
20
  if (!EnsureDirectory(directory, type())) {
196
    return;
197
  }
198
199
40
  std::string filename = GetFilename();
200
  DCHECK(!filename.empty());
201
40
  std::string path = directory + kPathSeparator + filename;
202
203
20
  WriteResult(env_, path.c_str(), result_s);
204
}
205
206
4347
void V8CoverageConnection::WriteProfile(Local<String> message) {
207
4347
  Isolate* isolate = env_->isolate();
208
4347
  Local<Context> context = env_->context();
209
8677
  HandleScope handle_scope(isolate);
210
4330
  Context::Scope context_scope(context);
211
212
  // Get message.result from the response.
213
  Local<Object> result;
214
8694
  if (!ParseProfile(env_, message, type()).ToLocal(&result)) {
215
    return;
216
  }
217
  // Generate the profile output from the subclass.
218
  Local<Object> profile;
219
8694
  if (!GetProfile(result).ToLocal(&profile)) {
220
    return;
221
  }
222
223
  // append source-map cache information to coverage object:
224
  Local<Value> source_map_cache_v;
225
  {
226
8676
    TryCatchScope try_catch(env());
227
    {
228
8677
      Isolate::AllowJavascriptExecutionScope allow_js_here(isolate);
229
4347
      Local<Function> source_map_cache_getter = env_->source_map_cache_getter();
230
13039
      if (!source_map_cache_getter->Call(
231
8693
              context, Undefined(isolate), 0, nullptr)
232
4346
              .ToLocal(&source_map_cache_v)) {
233
17
        return;
234
      }
235
    }
236

4330
    if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
237
      PrintCaughtException(isolate, context, try_catch);
238
    }
239
  }
240
  // Avoid writing to disk if no source-map data:
241
8658
  if (!source_map_cache_v->IsUndefined()) {
242
60
    profile->Set(context, FIXED_ONE_BYTE_STRING(isolate, "source-map-cache"),
243
90
                source_map_cache_v).ToChecked();
244
  }
245
246
  Local<String> result_s;
247
8659
  if (!v8::JSON::Stringify(context, profile).ToLocal(&result_s)) {
248
    fprintf(stderr, "Failed to stringify %s profile result\n", type());
249
    return;
250
  }
251
252
  // Create the directory if necessary.
253
8660
  std::string directory = GetDirectory();
254
  DCHECK(!directory.empty());
255
4330
  if (!EnsureDirectory(directory, type())) {
256
    return;
257
  }
258
259
8660
  std::string filename = GetFilename();
260
  DCHECK(!filename.empty());
261
8660
  std::string path = directory + kPathSeparator + filename;
262
263
4330
  WriteResult(env_, path.c_str(), result_s);
264
}
265
266
4347
MaybeLocal<Object> V8CoverageConnection::GetProfile(Local<Object> result) {
267
4347
  return result;
268
}
269
270
4330
std::string V8CoverageConnection::GetDirectory() const {
271
4330
  return env()->coverage_directory();
272
}
273
274
4357
void V8CoverageConnection::Start() {
275
4357
  DispatchMessage("Profiler.enable");
276
4357
  DispatchMessage("Profiler.startPreciseCoverage",
277
4357
                  R"({ "callCount": true, "detailed": true })");
278
4357
}
279
280
4345
void V8CoverageConnection::End() {
281
4345
  CHECK_EQ(ending_, false);
282
4345
  ending_ = true;
283
4345
  DispatchMessage("Profiler.takePreciseCoverage");
284
4347
}
285
286
10
std::string V8CpuProfilerConnection::GetDirectory() const {
287
10
  return env()->cpu_prof_dir();
288
}
289
290
10
std::string V8CpuProfilerConnection::GetFilename() const {
291
10
  return env()->cpu_prof_name();
292
}
293
294
10
MaybeLocal<Object> V8CpuProfilerConnection::GetProfile(Local<Object> result) {
295
  Local<Value> profile_v;
296
20
  if (!result
297
20
           ->Get(env()->context(),
298
40
                 FIXED_ONE_BYTE_STRING(env()->isolate(), "profile"))
299
10
           .ToLocal(&profile_v)) {
300
    fprintf(stderr, "'profile' from CPU profile result is undefined\n");
301
    return MaybeLocal<Object>();
302
  }
303
10
  if (!profile_v->IsObject()) {
304
    fprintf(stderr, "'profile' from CPU profile result is not an Object\n");
305
    return MaybeLocal<Object>();
306
  }
307
10
  return profile_v.As<Object>();
308
}
309
310
10
void V8CpuProfilerConnection::Start() {
311
10
  DispatchMessage("Profiler.enable");
312
10
  DispatchMessage("Profiler.start");
313
20
  std::string params = R"({ "interval": )";
314
10
  params += std::to_string(env()->cpu_prof_interval());
315
10
  params += " }";
316
10
  DispatchMessage("Profiler.setSamplingInterval", params.c_str());
317
10
}
318
319
10
void V8CpuProfilerConnection::End() {
320
10
  CHECK_EQ(ending_, false);
321
10
  ending_ = true;
322
10
  DispatchMessage("Profiler.stop");
323
10
}
324
325
10
std::string V8HeapProfilerConnection::GetDirectory() const {
326
10
  return env()->heap_prof_dir();
327
}
328
329
10
std::string V8HeapProfilerConnection::GetFilename() const {
330
10
  return env()->heap_prof_name();
331
}
332
333
10
MaybeLocal<Object> V8HeapProfilerConnection::GetProfile(Local<Object> result) {
334
  Local<Value> profile_v;
335
20
  if (!result
336
20
           ->Get(env()->context(),
337
40
                 FIXED_ONE_BYTE_STRING(env()->isolate(), "profile"))
338
10
           .ToLocal(&profile_v)) {
339
    fprintf(stderr, "'profile' from heap profile result is undefined\n");
340
    return MaybeLocal<Object>();
341
  }
342
10
  if (!profile_v->IsObject()) {
343
    fprintf(stderr, "'profile' from heap profile result is not an Object\n");
344
    return MaybeLocal<Object>();
345
  }
346
10
  return profile_v.As<Object>();
347
}
348
349
10
void V8HeapProfilerConnection::Start() {
350
10
  DispatchMessage("HeapProfiler.enable");
351
20
  std::string params = R"({ "samplingInterval": )";
352
10
  params += std::to_string(env()->heap_prof_interval());
353
10
  params += " }";
354
10
  DispatchMessage("HeapProfiler.startSampling", params.c_str());
355
10
}
356
357
10
void V8HeapProfilerConnection::End() {
358
10
  CHECK_EQ(ending_, false);
359
10
  ending_ = true;
360
10
  DispatchMessage("HeapProfiler.stopSampling");
361
10
}
362
363
// For now, we only support coverage profiling, but we may add more
364
// in the future.
365
4353
static void EndStartedProfilers(Environment* env) {
366
  Debug(env, DebugCategory::INSPECTOR_PROFILER, "EndStartedProfilers\n");
367
4353
  V8ProfilerConnection* connection = env->cpu_profiler_connection();
368

4353
  if (connection != nullptr && !connection->ending()) {
369
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending cpu profiling\n");
370
10
    connection->End();
371
  }
372
373
4353
  connection = env->heap_profiler_connection();
374

4352
  if (connection != nullptr && !connection->ending()) {
375
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending heap profiling\n");
376
10
    connection->End();
377
  }
378
379
4352
  connection = env->coverage_connection();
380

4353
  if (connection != nullptr && !connection->ending()) {
381
    Debug(
382
        env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n");
383
4346
    connection->End();
384
  }
385
4353
}
386
387
10
std::string GetCwd(Environment* env) {
388
  char cwd[PATH_MAX_BYTES];
389
10
  size_t size = PATH_MAX_BYTES;
390
10
  const int err = uv_cwd(cwd, &size);
391
392
10
  if (err == 0) {
393
10
    CHECK_GT(size, 0);
394
10
    return cwd;
395
  }
396
397
  // This can fail if the cwd is deleted. In that case, fall back to
398
  // exec_path.
399
  const std::string& exec_path = env->exec_path();
400
  return exec_path.substr(0, exec_path.find_last_of(kPathSeparator));
401
}
402
403
4363
void StartProfilers(Environment* env) {
404
21791
  AtExit(env, [](void* env) {
405
4351
    EndStartedProfilers(static_cast<Environment*>(env));
406
17430
  }, env);
407
408
4363
  Isolate* isolate = env->isolate();
409
13089
  Local<String> coverage_str = env->env_vars()->Get(
410
4363
      isolate, FIXED_ONE_BYTE_STRING(isolate, "NODE_V8_COVERAGE"))
411
8726
      .FromMaybe(Local<String>());
412

8723
  if (!coverage_str.IsEmpty() && coverage_str->Length() > 0) {
413
4356
    CHECK_NULL(env->coverage_connection());
414
4356
    env->set_coverage_connection(std::make_unique<V8CoverageConnection>(env));
415
4356
    env->coverage_connection()->Start();
416
  }
417
4364
  if (env->options()->cpu_prof) {
418
10
    const std::string& dir = env->options()->cpu_prof_dir;
419
10
    env->set_cpu_prof_interval(env->options()->cpu_prof_interval);
420
10
    env->set_cpu_prof_dir(dir.empty() ? GetCwd(env) : dir);
421
10
    if (env->options()->cpu_prof_name.empty()) {
422
16
      DiagnosticFilename filename(env, "CPU", "cpuprofile");
423
8
      env->set_cpu_prof_name(*filename);
424
    } else {
425
2
      env->set_cpu_prof_name(env->options()->cpu_prof_name);
426
    }
427
10
    CHECK_NULL(env->cpu_profiler_connection());
428
10
    env->set_cpu_profiler_connection(
429
20
        std::make_unique<V8CpuProfilerConnection>(env));
430
10
    env->cpu_profiler_connection()->Start();
431
  }
432
4363
  if (env->options()->heap_prof) {
433
10
    const std::string& dir = env->options()->heap_prof_dir;
434
10
    env->set_heap_prof_interval(env->options()->heap_prof_interval);
435
10
    env->set_heap_prof_dir(dir.empty() ? GetCwd(env) : dir);
436
10
    if (env->options()->heap_prof_name.empty()) {
437
16
      DiagnosticFilename filename(env, "Heap", "heapprofile");
438
8
      env->set_heap_prof_name(*filename);
439
    } else {
440
2
      env->set_heap_prof_name(env->options()->heap_prof_name);
441
    }
442
10
    env->set_heap_profiler_connection(
443
20
        std::make_unique<profiler::V8HeapProfilerConnection>(env));
444
10
    env->heap_profiler_connection()->Start();
445
  }
446
4363
}
447
448
4361
static void SetCoverageDirectory(const FunctionCallbackInfo<Value>& args) {
449
13083
  CHECK(args[0]->IsString());
450
4361
  Environment* env = Environment::GetCurrent(args);
451
13083
  node::Utf8Value directory(env->isolate(), args[0].As<String>());
452
4361
  env->set_coverage_directory(*directory);
453
4361
}
454
455
456
4361
static void SetSourceMapCacheGetter(const FunctionCallbackInfo<Value>& args) {
457
8722
  CHECK(args[0]->IsFunction());
458
4361
  Environment* env = Environment::GetCurrent(args);
459
8722
  env->set_source_map_cache_getter(args[0].As<Function>());
460
4361
}
461
462
4361
static void Initialize(Local<Object> target,
463
                       Local<Value> unused,
464
                       Local<Context> context,
465
                       void* priv) {
466
4361
  Environment* env = Environment::GetCurrent(context);
467
4361
  env->SetMethod(target, "setCoverageDirectory", SetCoverageDirectory);
468
4361
  env->SetMethod(target, "setSourceMapCacheGetter", SetSourceMapCacheGetter);
469
4361
}
470
471
}  // namespace profiler
472
}  // namespace node
473
474

16807
NODE_MODULE_CONTEXT_AWARE_INTERNAL(profiler, node::profiler::Initialize)