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: 225 258 87.2 %
Date: 2020-07-19 22:14:24 Branches: 89 130 68.5 %

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
5312
V8ProfilerConnection::V8ProfilerConnection(Environment* env)
33
    : session_(env->inspector_agent()->Connect(
34
10624
          std::make_unique<V8ProfilerConnection::V8ProfilerSessionDelegate>(
35
              this),
36
          false)),
37
15936
      env_(env) {}
38
39
15927
size_t V8ProfilerConnection::DispatchMessage(const char* method,
40
                                             const char* params) {
41
31863
  std::stringstream ss;
42
15932
  size_t id = next_id();
43
15925
  ss << R"({ "id": )" << id;
44
  DCHECK(method != nullptr);
45
15931
  ss << R"(, "method": ")" << method << '"';
46
15924
  if (params != nullptr) {
47
5311
    ss << R"(, "params": )" << params;
48
  }
49
15924
  ss << " }";
50
31865
  std::string message = ss.str();
51
  const uint8_t* message_data =
52
15930
      reinterpret_cast<const uint8_t*>(message.c_str());
53
15924
  Debug(env(),
54
        DebugCategory::INSPECTOR_PROFILER,
55
        "Dispatching message %s\n",
56
31856
        message.c_str());
57
15930
  session_->Dispatch(StringView(message_data, message.length()));
58
  // TODO(joyeecheung): use this to identify the ending message.
59
31869
  return id;
60
}
61
62
5270
static void WriteResult(Environment* env,
63
                        const char* path,
64
                        Local<String> result) {
65
5270
  int ret = WriteFileSync(env->isolate(), path, result);
66
5270
  if (ret != 0) {
67
    char err_buf[128];
68
25
    uv_err_name_r(ret, err_buf, sizeof(err_buf));
69
25
    fprintf(stderr, "%s: Failed to write file %s\n", err_buf, path);
70
25
    return;
71
  }
72
  Debug(env, DebugCategory::INSPECTOR_PROFILER, "Written result to %s\n", path);
73
}
74
75
15931
void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend(
76
    const v8_inspector::StringView& message) {
77
15931
  Environment* env = connection_->env();
78
15932
  Isolate* isolate = env->isolate();
79
21235
  HandleScope handle_scope(isolate);
80
21238
  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
15936
  const char* type = connection_->type();
86
  Debug(env,
87
        DebugCategory::INSPECTOR_PROFILER,
88
        "Receive %s profile message, ending = %s\n",
89
        type,
90
31870
        connection_->ending() ? "true" : "false");
91
15934
  if (!connection_->ending()) {
92
10632
    return;
93
  }
94
95
  // Convert StringView to a Local<String>.
96
  Local<String> message_str;
97
10604
  if (!String::NewFromTwoByte(isolate,
98
                              message.characters16(),
99
                              NewStringType::kNormal,
100
5302
                              message.length())
101
5302
           .ToLocal(&message_str)) {
102
    fprintf(stderr, "Failed to convert %s profile message\n", type);
103
    return;
104
  }
105
106
5302
  connection_->WriteProfile(message_str);
107
}
108
109
5270
static bool EnsureDirectory(const std::string& directory, const char* type) {
110
10540
  fs::FSReqWrapSync req_wrap_sync;
111
  int ret = fs::MKDirpSync(nullptr, &req_wrap_sync.req, directory, 0777,
112
5270
                           nullptr);
113

5270
  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
5270
  return true;
124
}
125
126
5250
std::string V8CoverageConnection::GetFilename() const {
127
10500
  std::string thread_id = std::to_string(env()->thread_id());
128
10500
  std::string pid = std::to_string(uv_os_getpid());
129
  std::string timestamp = std::to_string(
130
10500
      static_cast<uint64_t>(GetCurrentTimeInMicroseconds() / 1000));
131
  char filename[1024];
132
5250
  snprintf(filename,
133
           sizeof(filename),
134
           "coverage-%s-%s-%s.json",
135
           pid.c_str(),
136
           timestamp.c_str(),
137
5250
           thread_id.c_str());
138
10500
  return filename;
139
}
140
141
5276
static MaybeLocal<Object> ParseProfile(Environment* env,
142
                                       Local<String> message,
143
                                       const char* type) {
144
5276
  Local<Context> context = env->context();
145
5276
  Isolate* isolate = env->isolate();
146
147
  // Get message.result from the response
148
  Local<Value> parsed;
149

15828
  if (!v8::JSON::Parse(context, message).ToLocal(&parsed) ||
150
5276
      !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
21104
  if (!parsed.As<Object>()
157
21104
           ->Get(context, FIXED_ONE_BYTE_STRING(isolate, "result"))
158
5276
           .ToLocal(&result_v)) {
159
    fprintf(stderr, "Failed to get 'result' from %s profile message\n", type);
160
    return MaybeLocal<Object>();
161
  }
162
163
5276
  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
5276
  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
5282
void V8CoverageConnection::WriteProfile(Local<String> message) {
207
5282
  Isolate* isolate = env_->isolate();
208
5282
  Local<Context> context = env_->context();
209
10532
  HandleScope handle_scope(isolate);
210
5250
  Context::Scope context_scope(context);
211
212
  // This is only set up during pre-execution (when the environment variables
213
  // becomes available in the JS land). If it's empty, we don't have coverage
214
  // directory path (which is resolved in JS land at the moment) either, so
215
  // the best we could to is to just discard the profile and do nothing.
216
  // This should only happen in half-baked Environments created using the
217
  // embedder API.
218
10564
  if (env_->source_map_cache_getter().IsEmpty()) {
219
26
    return;
220
  }
221
222
  // Get message.result from the response.
223
  Local<Object> result;
224
10512
  if (!ParseProfile(env_, message, type()).ToLocal(&result)) {
225
    return;
226
  }
227
  // Generate the profile output from the subclass.
228
  Local<Object> profile;
229
10512
  if (!GetProfile(result).ToLocal(&profile)) {
230
    return;
231
  }
232
233
  // append source-map cache information to coverage object:
234
  Local<Value> source_map_cache_v;
235
  {
236
10506
    TryCatchScope try_catch(env());
237
    {
238
10506
      Isolate::AllowJavascriptExecutionScope allow_js_here(isolate);
239
5256
      Local<Function> source_map_cache_getter = env_->source_map_cache_getter();
240
15768
      if (!source_map_cache_getter->Call(
241
10512
              context, Undefined(isolate), 0, nullptr)
242
5256
              .ToLocal(&source_map_cache_v)) {
243
6
        return;
244
      }
245
    }
246

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

5283
  if (connection != nullptr && !connection->ending()) {
379
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending cpu profiling\n");
380
10
    connection->End();
381
  }
382
383
5283
  connection = env->heap_profiler_connection();
384

5287
  if (connection != nullptr && !connection->ending()) {
385
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending heap profiling\n");
386
10
    connection->End();
387
  }
388
389
5287
  connection = env->coverage_connection();
390

5285
  if (connection != nullptr && !connection->ending()) {
391
    Debug(
392
        env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n");
393
5282
    connection->End();
394
  }
395
5286
}
396
397
10
std::string GetCwd(Environment* env) {
398
  char cwd[PATH_MAX_BYTES];
399
10
  size_t size = PATH_MAX_BYTES;
400
10
  const int err = uv_cwd(cwd, &size);
401
402
10
  if (err == 0) {
403
10
    CHECK_GT(size, 0);
404
10
    return cwd;
405
  }
406
407
  // This can fail if the cwd is deleted. In that case, fall back to
408
  // exec_path.
409
  const std::string& exec_path = env->exec_path();
410
  return exec_path.substr(0, exec_path.find_last_of(kPathSeparator));
411
}
412
413
5297
void StartProfilers(Environment* env) {
414
26453
  AtExit(env, [](void* env) {
415
5281
    EndStartedProfilers(static_cast<Environment*>(env));
416
21162
  }, env);
417
418
5297
  Isolate* isolate = env->isolate();
419
15891
  Local<String> coverage_str = env->env_vars()->Get(
420
5297
      isolate, FIXED_ONE_BYTE_STRING(isolate, "NODE_V8_COVERAGE"))
421
10594
      .FromMaybe(Local<String>());
422

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

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