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: 182 207 87.9 %
Date: 2019-07-28 22:34:34 Branches: 73 102 71.6 %

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

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


14370
  if (!v8::JSON::Parse(context, message).ToLocal(&parsed) ||
147
4776
      !parsed->IsObject()) {
148
21
    fprintf(stderr, "Failed to parse %s profile result as JSON object\n", type);
149
21
    return MaybeLocal<Object>();
150
  }
151
152
  Local<Value> result_v;
153
9552
  if (!parsed.As<Object>()
154
19104
           ->Get(context, FIXED_ONE_BYTE_STRING(isolate, "result"))
155
14328
           .ToLocal(&result_v)) {
156
    fprintf(stderr, "Failed to get 'result' from %s profile message\n", type);
157
    return MaybeLocal<Object>();
158
  }
159
160
4776
  if (!result_v->IsObject()) {
161
    fprintf(
162
        stderr, "'result' from %s profile message is not an object\n", type);
163
    return MaybeLocal<Object>();
164
  }
165
166
4776
  return result_v.As<Object>();
167
}
168
169
4797
void V8ProfilerConnection::WriteProfile(Local<String> message) {
170
4797
  Local<Context> context = env_->context();
171
172
  // Get message.result from the response.
173
  Local<Object> result;
174
9594
  if (!ParseProfile(env_, message, type()).ToLocal(&result)) {
175
42
    return;
176
  }
177
  // Generate the profile output from the subclass.
178
  Local<Object> profile;
179
9552
  if (!GetProfile(result).ToLocal(&profile)) {
180
    return;
181
  }
182
  Local<String> result_s;
183
9552
  if (!v8::JSON::Stringify(context, profile).ToLocal(&result_s)) {
184
    fprintf(stderr, "Failed to stringify %s profile result\n", type());
185
    return;
186
  }
187
188
  // Create the directory if necessary.
189
4776
  std::string directory = GetDirectory();
190
  DCHECK(!directory.empty());
191
4776
  if (!EnsureDirectory(directory, type())) {
192
    return;
193
  }
194
195
9552
  std::string filename = GetFilename();
196
  DCHECK(!filename.empty());
197
9552
  std::string path = directory + kPathSeparator + filename;
198
199
9552
  WriteResult(env_, path.c_str(), result_s);
200
}
201
202
4756
MaybeLocal<Object> V8CoverageConnection::GetProfile(Local<Object> result) {
203
4756
  return result;
204
}
205
206
4756
std::string V8CoverageConnection::GetDirectory() const {
207
4756
  return env()->coverage_directory();
208
}
209
210
4954
void V8CoverageConnection::Start() {
211
4954
  DispatchMessage("Profiler.enable");
212
  DispatchMessage("Profiler.startPreciseCoverage",
213
4955
                  R"({ "callCount": true, "detailed": true })");
214
4955
}
215
216
4777
void V8CoverageConnection::End() {
217
4777
  CHECK_EQ(ending_, false);
218
4777
  ending_ = true;
219
4777
  DispatchMessage("Profiler.takePreciseCoverage");
220
4777
}
221
222
10
std::string V8CpuProfilerConnection::GetDirectory() const {
223
10
  return env()->cpu_prof_dir();
224
}
225
226
10
std::string V8CpuProfilerConnection::GetFilename() const {
227
10
  return env()->cpu_prof_name();
228
}
229
230
10
MaybeLocal<Object> V8CpuProfilerConnection::GetProfile(Local<Object> result) {
231
  Local<Value> profile_v;
232
20
  if (!result
233
           ->Get(env()->context(),
234
40
                 FIXED_ONE_BYTE_STRING(env()->isolate(), "profile"))
235
30
           .ToLocal(&profile_v)) {
236
    fprintf(stderr, "'profile' from CPU profile result is undefined\n");
237
    return MaybeLocal<Object>();
238
  }
239
10
  if (!profile_v->IsObject()) {
240
    fprintf(stderr, "'profile' from CPU profile result is not an Object\n");
241
    return MaybeLocal<Object>();
242
  }
243
10
  return profile_v.As<Object>();
244
}
245
246
10
void V8CpuProfilerConnection::Start() {
247
10
  DispatchMessage("Profiler.enable");
248
10
  DispatchMessage("Profiler.start");
249
10
  std::string params = R"({ "interval": )";
250
10
  params += std::to_string(env()->cpu_prof_interval());
251
10
  params += " }";
252
10
  DispatchMessage("Profiler.setSamplingInterval", params.c_str());
253
10
}
254
255
10
void V8CpuProfilerConnection::End() {
256
10
  CHECK_EQ(ending_, false);
257
10
  ending_ = true;
258
10
  DispatchMessage("Profiler.stop");
259
10
}
260
261
10
std::string V8HeapProfilerConnection::GetDirectory() const {
262
10
  return env()->heap_prof_dir();
263
}
264
265
10
std::string V8HeapProfilerConnection::GetFilename() const {
266
10
  return env()->heap_prof_name();
267
}
268
269
10
MaybeLocal<Object> V8HeapProfilerConnection::GetProfile(Local<Object> result) {
270
  Local<Value> profile_v;
271
20
  if (!result
272
           ->Get(env()->context(),
273
40
                 FIXED_ONE_BYTE_STRING(env()->isolate(), "profile"))
274
30
           .ToLocal(&profile_v)) {
275
    fprintf(stderr, "'profile' from heap profile result is undefined\n");
276
    return MaybeLocal<Object>();
277
  }
278
10
  if (!profile_v->IsObject()) {
279
    fprintf(stderr, "'profile' from heap profile result is not an Object\n");
280
    return MaybeLocal<Object>();
281
  }
282
10
  return profile_v.As<Object>();
283
}
284
285
10
void V8HeapProfilerConnection::Start() {
286
10
  DispatchMessage("HeapProfiler.enable");
287
10
  std::string params = R"({ "samplingInterval": )";
288
10
  params += std::to_string(env()->heap_prof_interval());
289
10
  params += " }";
290
10
  DispatchMessage("HeapProfiler.startSampling", params.c_str());
291
10
}
292
293
10
void V8HeapProfilerConnection::End() {
294
10
  CHECK_EQ(ending_, false);
295
10
  ending_ = true;
296
10
  DispatchMessage("HeapProfiler.stopSampling");
297
10
}
298
299
// For now, we only support coverage profiling, but we may add more
300
// in the future.
301
4869
void EndStartedProfilers(Environment* env) {
302
  Debug(env, DebugCategory::INSPECTOR_PROFILER, "EndStartedProfilers\n");
303
4869
  V8ProfilerConnection* connection = env->cpu_profiler_connection();
304

4869
  if (connection != nullptr && !connection->ending()) {
305
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending cpu profiling\n");
306
10
    connection->End();
307
  }
308
309
4869
  connection = env->heap_profiler_connection();
310

4869
  if (connection != nullptr && !connection->ending()) {
311
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending heap profiling\n");
312
10
    connection->End();
313
  }
314
315
4869
  connection = env->coverage_connection();
316

4869
  if (connection != nullptr && !connection->ending()) {
317
    Debug(
318
        env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n");
319
4777
    connection->End();
320
  }
321
4869
}
322
323
10
std::string GetCwd(Environment* env) {
324
  char cwd[PATH_MAX_BYTES];
325
10
  size_t size = PATH_MAX_BYTES;
326
10
  int err = uv_cwd(cwd, &size);
327
328
10
  if (err == 0) {
329
10
    CHECK_GT(size, 0);
330
10
    return cwd;
331
  }
332
333
  // This can fail if the cwd is deleted. In that case, fall back to
334
  // exec_path.
335
  const std::string& exec_path = env->exec_path();
336
  return exec_path.substr(0, exec_path.find_last_of(kPathSeparator));
337
}
338
339
4960
void StartProfilers(Environment* env) {
340
4960
  Isolate* isolate = env->isolate();
341
9920
  Local<String> coverage_str = env->env_vars()->Get(
342
4960
      isolate, FIXED_ONE_BYTE_STRING(isolate, "NODE_V8_COVERAGE"));
343

9917
  if (!coverage_str.IsEmpty() && coverage_str->Length() > 0) {
344
4955
    CHECK_NULL(env->coverage_connection());
345
4955
    env->set_coverage_connection(std::make_unique<V8CoverageConnection>(env));
346
4954
    env->coverage_connection()->Start();
347
  }
348
4960
  if (env->options()->cpu_prof) {
349
10
    const std::string& dir = env->options()->cpu_prof_dir;
350
10
    env->set_cpu_prof_interval(env->options()->cpu_prof_interval);
351
10
    env->set_cpu_prof_dir(dir.empty() ? GetCwd(env) : dir);
352
10
    if (env->options()->cpu_prof_name.empty()) {
353
8
      DiagnosticFilename filename(env, "CPU", "cpuprofile");
354
8
      env->set_cpu_prof_name(*filename);
355
    } else {
356
2
      env->set_cpu_prof_name(env->options()->cpu_prof_name);
357
    }
358
10
    CHECK_NULL(env->cpu_profiler_connection());
359
    env->set_cpu_profiler_connection(
360
10
        std::make_unique<V8CpuProfilerConnection>(env));
361
10
    env->cpu_profiler_connection()->Start();
362
  }
363
4960
  if (env->options()->heap_prof) {
364
10
    const std::string& dir = env->options()->heap_prof_dir;
365
10
    env->set_heap_prof_interval(env->options()->heap_prof_interval);
366
10
    env->set_heap_prof_dir(dir.empty() ? GetCwd(env) : dir);
367
10
    if (env->options()->heap_prof_name.empty()) {
368
8
      DiagnosticFilename filename(env, "Heap", "heapprofile");
369
8
      env->set_heap_prof_name(*filename);
370
    } else {
371
2
      env->set_heap_prof_name(env->options()->heap_prof_name);
372
    }
373
    env->set_heap_profiler_connection(
374
10
        std::make_unique<profiler::V8HeapProfilerConnection>(env));
375
10
    env->heap_profiler_connection()->Start();
376
  }
377
4960
}
378
379
4955
static void SetCoverageDirectory(const FunctionCallbackInfo<Value>& args) {
380
14865
  CHECK(args[0]->IsString());
381
4955
  Environment* env = Environment::GetCurrent(args);
382
9910
  node::Utf8Value directory(env->isolate(), args[0].As<String>());
383
4955
  env->set_coverage_directory(*directory);
384
4955
}
385
386
4955
static void Initialize(Local<Object> target,
387
                       Local<Value> unused,
388
                       Local<Context> context,
389
                       void* priv) {
390
4955
  Environment* env = Environment::GetCurrent(context);
391
4955
  env->SetMethod(target, "setCoverageDirectory", SetCoverageDirectory);
392
4955
}
393
394
}  // namespace profiler
395
}  // namespace node
396
397

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