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: 206 239 86.2 %
Date: 2019-10-08 22:34:21 Branches: 82 120 68.3 %

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

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


15390
  if (!v8::JSON::Parse(context, message).ToLocal(&parsed) ||
148
5130
      !parsed->IsObject()) {
149
    fprintf(stderr, "Failed to parse %s profile result as JSON object\n", type);
150
    return MaybeLocal<Object>();
151
  }
152
153
  Local<Value> result_v;
154
10260
  if (!parsed.As<Object>()
155
20520
           ->Get(context, FIXED_ONE_BYTE_STRING(isolate, "result"))
156
15390
           .ToLocal(&result_v)) {
157
    fprintf(stderr, "Failed to get 'result' from %s profile message\n", type);
158
    return MaybeLocal<Object>();
159
  }
160
161
5130
  if (!result_v->IsObject()) {
162
    fprintf(
163
        stderr, "'result' from %s profile message is not an object\n", type);
164
    return MaybeLocal<Object>();
165
  }
166
167
5130
  return result_v.As<Object>();
168
}
169
170
20
void V8ProfilerConnection::WriteProfile(Local<String> message) {
171
20
  Local<Context> context = env_->context();
172
173
  // Get message.result from the response.
174
  Local<Object> result;
175
40
  if (!ParseProfile(env_, message, type()).ToLocal(&result)) {
176
    return;
177
  }
178
  // Generate the profile output from the subclass.
179
  Local<Object> profile;
180
40
  if (!GetProfile(result).ToLocal(&profile)) {
181
    return;
182
  }
183
184
  Local<String> result_s;
185
40
  if (!v8::JSON::Stringify(context, profile).ToLocal(&result_s)) {
186
    fprintf(stderr, "Failed to stringify %s profile result\n", type());
187
    return;
188
  }
189
190
  // Create the directory if necessary.
191
20
  std::string directory = GetDirectory();
192
  DCHECK(!directory.empty());
193
20
  if (!EnsureDirectory(directory, type())) {
194
    return;
195
  }
196
197
40
  std::string filename = GetFilename();
198
  DCHECK(!filename.empty());
199
40
  std::string path = directory + kPathSeparator + filename;
200
201
40
  WriteResult(env_, path.c_str(), result_s);
202
}
203
204
5110
void V8CoverageConnection::WriteProfile(Local<String> message) {
205
5110
  Isolate* isolate = env_->isolate();
206
5110
  Local<Context> context = env_->context();
207
5110
  HandleScope handle_scope(isolate);
208
5082
  Context::Scope context_scope(context);
209
210
  // Get message.result from the response.
211
  Local<Object> result;
212
10220
  if (!ParseProfile(env_, message, type()).ToLocal(&result)) {
213
    return;
214
  }
215
  // Generate the profile output from the subclass.
216
  Local<Object> profile;
217
10220
  if (!GetProfile(result).ToLocal(&profile)) {
218
    return;
219
  }
220
221
  // append source-map cache information to coverage object:
222
5110
  Local<Function> source_map_cache_getter = env_->source_map_cache_getter();
223
  Local<Value> source_map_cache_v;
224
10220
  if (!source_map_cache_getter->Call(env()->context(),
225
15330
      Undefined(isolate), 0, nullptr)
226
15330
      .ToLocal(&source_map_cache_v)) {
227
28
    return;
228
  }
229
  // Avoid writing to disk if no source-map data:
230
10164
  if (!source_map_cache_v->IsUndefined()) {
231
    profile->Set(context, FIXED_ONE_BYTE_STRING(isolate, "source-map-cache"),
232
81
                source_map_cache_v).ToChecked();
233
  }
234
235
  Local<String> result_s;
236
10164
  if (!v8::JSON::Stringify(context, profile).ToLocal(&result_s)) {
237
    fprintf(stderr, "Failed to stringify %s profile result\n", type());
238
    return;
239
  }
240
241
  // Create the directory if necessary.
242
10164
  std::string directory = GetDirectory();
243
  DCHECK(!directory.empty());
244
5082
  if (!EnsureDirectory(directory, type())) {
245
    return;
246
  }
247
248
10164
  std::string filename = GetFilename();
249
  DCHECK(!filename.empty());
250
10164
  std::string path = directory + kPathSeparator + filename;
251
252
10164
  WriteResult(env_, path.c_str(), result_s);
253
}
254
255
5110
MaybeLocal<Object> V8CoverageConnection::GetProfile(Local<Object> result) {
256
5110
  return result;
257
}
258
259
5082
std::string V8CoverageConnection::GetDirectory() const {
260
5082
  return env()->coverage_directory();
261
}
262
263
5123
void V8CoverageConnection::Start() {
264
5123
  DispatchMessage("Profiler.enable");
265
  DispatchMessage("Profiler.startPreciseCoverage",
266
5123
                  R"({ "callCount": true, "detailed": true })");
267
5123
}
268
269
5110
void V8CoverageConnection::End() {
270
5110
  CHECK_EQ(ending_, false);
271
5110
  ending_ = true;
272
5110
  DispatchMessage("Profiler.takePreciseCoverage");
273
5110
}
274
275
10
std::string V8CpuProfilerConnection::GetDirectory() const {
276
10
  return env()->cpu_prof_dir();
277
}
278
279
10
std::string V8CpuProfilerConnection::GetFilename() const {
280
10
  return env()->cpu_prof_name();
281
}
282
283
10
MaybeLocal<Object> V8CpuProfilerConnection::GetProfile(Local<Object> result) {
284
  Local<Value> profile_v;
285
20
  if (!result
286
           ->Get(env()->context(),
287
40
                 FIXED_ONE_BYTE_STRING(env()->isolate(), "profile"))
288
30
           .ToLocal(&profile_v)) {
289
    fprintf(stderr, "'profile' from CPU profile result is undefined\n");
290
    return MaybeLocal<Object>();
291
  }
292
10
  if (!profile_v->IsObject()) {
293
    fprintf(stderr, "'profile' from CPU profile result is not an Object\n");
294
    return MaybeLocal<Object>();
295
  }
296
10
  return profile_v.As<Object>();
297
}
298
299
10
void V8CpuProfilerConnection::Start() {
300
10
  DispatchMessage("Profiler.enable");
301
10
  DispatchMessage("Profiler.start");
302
10
  std::string params = R"({ "interval": )";
303
10
  params += std::to_string(env()->cpu_prof_interval());
304
10
  params += " }";
305
10
  DispatchMessage("Profiler.setSamplingInterval", params.c_str());
306
10
}
307
308
10
void V8CpuProfilerConnection::End() {
309
10
  CHECK_EQ(ending_, false);
310
10
  ending_ = true;
311
10
  DispatchMessage("Profiler.stop");
312
10
}
313
314
10
std::string V8HeapProfilerConnection::GetDirectory() const {
315
10
  return env()->heap_prof_dir();
316
}
317
318
10
std::string V8HeapProfilerConnection::GetFilename() const {
319
10
  return env()->heap_prof_name();
320
}
321
322
10
MaybeLocal<Object> V8HeapProfilerConnection::GetProfile(Local<Object> result) {
323
  Local<Value> profile_v;
324
20
  if (!result
325
           ->Get(env()->context(),
326
40
                 FIXED_ONE_BYTE_STRING(env()->isolate(), "profile"))
327
30
           .ToLocal(&profile_v)) {
328
    fprintf(stderr, "'profile' from heap profile result is undefined\n");
329
    return MaybeLocal<Object>();
330
  }
331
10
  if (!profile_v->IsObject()) {
332
    fprintf(stderr, "'profile' from heap profile result is not an Object\n");
333
    return MaybeLocal<Object>();
334
  }
335
10
  return profile_v.As<Object>();
336
}
337
338
10
void V8HeapProfilerConnection::Start() {
339
10
  DispatchMessage("HeapProfiler.enable");
340
10
  std::string params = R"({ "samplingInterval": )";
341
10
  params += std::to_string(env()->heap_prof_interval());
342
10
  params += " }";
343
10
  DispatchMessage("HeapProfiler.startSampling", params.c_str());
344
10
}
345
346
10
void V8HeapProfilerConnection::End() {
347
10
  CHECK_EQ(ending_, false);
348
10
  ending_ = true;
349
10
  DispatchMessage("HeapProfiler.stopSampling");
350
10
}
351
352
// For now, we only support coverage profiling, but we may add more
353
// in the future.
354
5208
void EndStartedProfilers(Environment* env) {
355
  Debug(env, DebugCategory::INSPECTOR_PROFILER, "EndStartedProfilers\n");
356
5208
  V8ProfilerConnection* connection = env->cpu_profiler_connection();
357

5208
  if (connection != nullptr && !connection->ending()) {
358
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending cpu profiling\n");
359
10
    connection->End();
360
  }
361
362
5208
  connection = env->heap_profiler_connection();
363

5208
  if (connection != nullptr && !connection->ending()) {
364
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending heap profiling\n");
365
10
    connection->End();
366
  }
367
368
5208
  connection = env->coverage_connection();
369

5208
  if (connection != nullptr && !connection->ending()) {
370
    Debug(
371
        env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n");
372
5110
    connection->End();
373
  }
374
5208
}
375
376
10
std::string GetCwd(Environment* env) {
377
  char cwd[PATH_MAX_BYTES];
378
10
  size_t size = PATH_MAX_BYTES;
379
10
  const int err = uv_cwd(cwd, &size);
380
381
10
  if (err == 0) {
382
10
    CHECK_GT(size, 0);
383
10
    return cwd;
384
  }
385
386
  // This can fail if the cwd is deleted. In that case, fall back to
387
  // exec_path.
388
  const std::string& exec_path = env->exec_path();
389
  return exec_path.substr(0, exec_path.find_last_of(kPathSeparator));
390
}
391
392
5129
void StartProfilers(Environment* env) {
393
5129
  Isolate* isolate = env->isolate();
394
10258
  Local<String> coverage_str = env->env_vars()->Get(
395
5129
      isolate, FIXED_ONE_BYTE_STRING(isolate, "NODE_V8_COVERAGE"))
396
15387
      .FromMaybe(Local<String>());
397

10255
  if (!coverage_str.IsEmpty() && coverage_str->Length() > 0) {
398
5123
    CHECK_NULL(env->coverage_connection());
399
5123
    env->set_coverage_connection(std::make_unique<V8CoverageConnection>(env));
400
5123
    env->coverage_connection()->Start();
401
  }
402
5129
  if (env->options()->cpu_prof) {
403
10
    const std::string& dir = env->options()->cpu_prof_dir;
404
10
    env->set_cpu_prof_interval(env->options()->cpu_prof_interval);
405
10
    env->set_cpu_prof_dir(dir.empty() ? GetCwd(env) : dir);
406
10
    if (env->options()->cpu_prof_name.empty()) {
407
8
      DiagnosticFilename filename(env, "CPU", "cpuprofile");
408
8
      env->set_cpu_prof_name(*filename);
409
    } else {
410
2
      env->set_cpu_prof_name(env->options()->cpu_prof_name);
411
    }
412
10
    CHECK_NULL(env->cpu_profiler_connection());
413
    env->set_cpu_profiler_connection(
414
10
        std::make_unique<V8CpuProfilerConnection>(env));
415
10
    env->cpu_profiler_connection()->Start();
416
  }
417
5129
  if (env->options()->heap_prof) {
418
10
    const std::string& dir = env->options()->heap_prof_dir;
419
10
    env->set_heap_prof_interval(env->options()->heap_prof_interval);
420
10
    env->set_heap_prof_dir(dir.empty() ? GetCwd(env) : dir);
421
10
    if (env->options()->heap_prof_name.empty()) {
422
8
      DiagnosticFilename filename(env, "Heap", "heapprofile");
423
8
      env->set_heap_prof_name(*filename);
424
    } else {
425
2
      env->set_heap_prof_name(env->options()->heap_prof_name);
426
    }
427
    env->set_heap_profiler_connection(
428
10
        std::make_unique<profiler::V8HeapProfilerConnection>(env));
429
10
    env->heap_profiler_connection()->Start();
430
  }
431
5129
}
432
433
5122
static void SetCoverageDirectory(const FunctionCallbackInfo<Value>& args) {
434
15366
  CHECK(args[0]->IsString());
435
5122
  Environment* env = Environment::GetCurrent(args);
436
10244
  node::Utf8Value directory(env->isolate(), args[0].As<String>());
437
5122
  env->set_coverage_directory(*directory);
438
5122
}
439
440
441
5122
static void SetSourceMapCacheGetter(const FunctionCallbackInfo<Value>& args) {
442
10244
  CHECK(args[0]->IsFunction());
443
5122
  Environment* env = Environment::GetCurrent(args);
444
10244
  env->set_source_map_cache_getter(args[0].As<Function>());
445
5122
}
446
447
5122
static void Initialize(Local<Object> target,
448
                       Local<Value> unused,
449
                       Local<Context> context,
450
                       void* priv) {
451
5122
  Environment* env = Environment::GetCurrent(context);
452
5122
  env->SetMethod(target, "setCoverageDirectory", SetCoverageDirectory);
453
5122
  env->SetMethod(target, "setSourceMapCacheGetter", SetSourceMapCacheGetter);
454
5122
}
455
456
}  // namespace profiler
457
}  // namespace node
458
459

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