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: 147 166 88.6 %
Date: 2019-05-05 22:32:45 Branches: 57 80 71.3 %

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 "node_file.h"
6
#include "node_internals.h"
7
#include "v8-inspector.h"
8
9
namespace node {
10
namespace profiler {
11
12
using v8::Context;
13
using v8::Function;
14
using v8::FunctionCallbackInfo;
15
using v8::HandleScope;
16
using v8::Isolate;
17
using v8::Local;
18
using v8::MaybeLocal;
19
using v8::NewStringType;
20
using v8::Object;
21
using v8::String;
22
using v8::Value;
23
24
using v8_inspector::StringView;
25
26
#ifdef _WIN32
27
const char* const kPathSeparator = "\\/";
28
/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
29
#define CWD_BUFSIZE (MAX_PATH * 4)
30
#else
31
#include <climits>  // PATH_MAX on Solaris.
32
const char* const kPathSeparator = "/";
33
#define CWD_BUFSIZE (PATH_MAX)
34
#endif
35
36
4656
V8ProfilerConnection::V8ProfilerConnection(Environment* env)
37
    : session_(env->inspector_agent()->Connect(
38
          std::make_unique<V8ProfilerConnection::V8ProfilerSessionDelegate>(
39
              this),
40
          false)),
41
4656
      env_(env) {}
42
43
13807
size_t V8ProfilerConnection::DispatchMessage(const char* method,
44
                                             const char* params) {
45
13807
  std::stringstream ss;
46
13807
  size_t id = next_id();
47
13806
  ss << R"({ "id": )" << id;
48
  DCHECK(method != nullptr);
49
13806
  ss << R"(, "method": ")" << method << '"';
50
13807
  if (params != nullptr) {
51
4656
    ss << R"(, "params": )" << params;
52
  }
53
13807
  ss << " }";
54
27613
  std::string message = ss.str();
55
  const uint8_t* message_data =
56
13807
      reinterpret_cast<const uint8_t*>(message.c_str());
57
  Debug(env(),
58
        DebugCategory::INSPECTOR_PROFILER,
59
        "Dispatching message %s\n",
60
27614
        message.c_str());
61
13807
  session_->Dispatch(StringView(message_data, message.length()));
62
  // TODO(joyeecheung): use this to identify the ending message.
63
27614
  return id;
64
}
65
66
4467
static void WriteResult(Environment* env,
67
                        const char* path,
68
                        Local<String> result) {
69
4467
  int ret = WriteFileSync(env->isolate(), path, result);
70
4467
  if (ret != 0) {
71
    char err_buf[128];
72
    uv_err_name_r(ret, err_buf, sizeof(err_buf));
73
    fprintf(stderr, "%s: Failed to write file %s\n", err_buf, path);
74
4467
    return;
75
  }
76
  Debug(env, DebugCategory::INSPECTOR_PROFILER, "Written result to %s\n", path);
77
}
78
79
13807
void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend(
80
    const v8_inspector::StringView& message) {
81
13807
  Environment* env = connection_->env();
82
13807
  Isolate* isolate = env->isolate();
83
13807
  HandleScope handle_scope(isolate);
84
18292
  Context::Scope context_scope(env->context());
85
86
  // TODO(joyeecheung): always parse the message so that we can use the id to
87
  // identify ending messages as well as printing the message in the debug
88
  // output when there is an error.
89
13807
  const char* type = connection_->type();
90
  Debug(env,
91
        DebugCategory::INSPECTOR_PROFILER,
92
        "Receive %s profile message, ending = %s\n",
93
        type,
94
27614
        connection_->ending() ? "true" : "false");
95
13807
  if (!connection_->ending()) {
96
9322
    return;
97
  }
98
99
  // Convert StringView to a Local<String>.
100
  Local<String> message_str;
101
8970
  if (!String::NewFromTwoByte(isolate,
102
                              message.characters16(),
103
                              NewStringType::kNormal,
104
4485
                              message.length())
105
13455
           .ToLocal(&message_str)) {
106
    fprintf(stderr, "Failed to covert %s profile message\n", type);
107
    return;
108
  }
109
110
8970
  connection_->WriteProfile(message_str);
111
}
112
113
4467
static bool EnsureDirectory(const std::string& directory, const char* type) {
114
  uv_fs_t req;
115
4467
  int ret = fs::MKDirpSync(nullptr, &req, directory, 0777, nullptr);
116
4467
  uv_fs_req_cleanup(&req);
117

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


13437
  if (!v8::JSON::Parse(context, message).ToLocal(&parsed) ||
154
4467
      !parsed->IsObject()) {
155
18
    fprintf(stderr, "Failed to parse %s profile result as JSON object\n", type);
156
18
    return MaybeLocal<Object>();
157
  }
158
159
  Local<Value> result_v;
160
8934
  if (!parsed.As<Object>()
161
17868
           ->Get(context, FIXED_ONE_BYTE_STRING(isolate, "result"))
162
13401
           .ToLocal(&result_v)) {
163
    fprintf(stderr, "Failed to get 'result' from %s profile message\n", type);
164
    return MaybeLocal<Object>();
165
  }
166
167
4467
  if (!result_v->IsObject()) {
168
    fprintf(
169
        stderr, "'result' from %s profile message is not an object\n", type);
170
    return MaybeLocal<Object>();
171
  }
172
173
4467
  return result_v.As<Object>();
174
}
175
176
4485
void V8ProfilerConnection::WriteProfile(Local<String> message) {
177
4485
  Local<Context> context = env_->context();
178
179
  // Get message.result from the response.
180
  Local<Object> result;
181
8970
  if (!ParseProfile(env_, message, type()).ToLocal(&result)) {
182
36
    return;
183
  }
184
  // Generate the profile output from the subclass.
185
  Local<Object> profile;
186
8934
  if (!GetProfile(result).ToLocal(&profile)) {
187
    return;
188
  }
189
  Local<String> result_s;
190
8934
  if (!v8::JSON::Stringify(context, profile).ToLocal(&result_s)) {
191
    fprintf(stderr, "Failed to stringify %s profile result\n", type());
192
    return;
193
  }
194
195
  // Create the directory if necessary.
196
4467
  std::string directory = GetDirectory();
197
  DCHECK(!directory.empty());
198
4467
  if (!EnsureDirectory(directory, type())) {
199
    return;
200
  }
201
202
8934
  std::string filename = GetFilename();
203
  DCHECK(!filename.empty());
204
8934
  std::string path = directory + kPathSeparator + filename;
205
206
8934
  WriteResult(env_, path.c_str(), result_s);
207
}
208
209
4457
MaybeLocal<Object> V8CoverageConnection::GetProfile(Local<Object> result) {
210
4457
  return result;
211
}
212
213
4457
std::string V8CoverageConnection::GetDirectory() const {
214
4457
  return env()->coverage_directory();
215
}
216
217
4646
void V8CoverageConnection::Start() {
218
4646
  DispatchMessage("Profiler.enable");
219
  DispatchMessage("Profiler.startPreciseCoverage",
220
4646
                  R"({ "callCount": true, "detailed": true })");
221
4646
}
222
223
4475
void V8CoverageConnection::End() {
224
4475
  CHECK_EQ(ending_, false);
225
4475
  ending_ = true;
226
4475
  DispatchMessage("Profiler.takePreciseCoverage");
227
4475
}
228
229
10
std::string V8CpuProfilerConnection::GetDirectory() const {
230
10
  return env()->cpu_prof_dir();
231
}
232
233
10
std::string V8CpuProfilerConnection::GetFilename() const {
234
10
  return env()->cpu_prof_name();
235
}
236
237
10
MaybeLocal<Object> V8CpuProfilerConnection::GetProfile(Local<Object> result) {
238
  Local<Value> profile_v;
239
20
  if (!result
240
           ->Get(env()->context(),
241
40
                 FIXED_ONE_BYTE_STRING(env()->isolate(), "profile"))
242
30
           .ToLocal(&profile_v)) {
243
    fprintf(stderr, "'profile' from CPU profile result is undefined\n");
244
    return MaybeLocal<Object>();
245
  }
246
10
  if (!profile_v->IsObject()) {
247
    fprintf(stderr, "'profile' from CPU profile result is not an Object\n");
248
    return MaybeLocal<Object>();
249
  }
250
10
  return profile_v.As<Object>();
251
}
252
253
10
void V8CpuProfilerConnection::Start() {
254
10
  DispatchMessage("Profiler.enable");
255
10
  DispatchMessage("Profiler.start");
256
10
  std::string params = R"({ "interval": )";
257
10
  params += std::to_string(env()->cpu_prof_interval());
258
10
  params += " }";
259
10
  DispatchMessage("Profiler.setSamplingInterval", params.c_str());
260
10
}
261
262
10
void V8CpuProfilerConnection::End() {
263
10
  CHECK_EQ(ending_, false);
264
10
  ending_ = true;
265
10
  DispatchMessage("Profiler.stop");
266
10
}
267
268
// For now, we only support coverage profiling, but we may add more
269
// in the future.
270
4549
void EndStartedProfilers(Environment* env) {
271
  Debug(env, DebugCategory::INSPECTOR_PROFILER, "EndStartedProfilers\n");
272
4549
  V8ProfilerConnection* connection = env->cpu_profiler_connection();
273

4549
  if (connection != nullptr && !connection->ending()) {
274
    Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending cpu profiling\n");
275
10
    connection->End();
276
  }
277
278
4549
  connection = env->coverage_connection();
279

4549
  if (connection != nullptr && !connection->ending()) {
280
    Debug(
281
        env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n");
282
4475
    connection->End();
283
  }
284
4549
}
285
286
5
std::string GetCwd() {
287
  char cwd[CWD_BUFSIZE];
288
5
  size_t size = CWD_BUFSIZE;
289
5
  int err = uv_cwd(cwd, &size);
290
  // This can fail if the cwd is deleted.
291
  // TODO(joyeecheung): store this in the Environment during Environment
292
  // creation and fallback to exec_path and argv0, then we no longer need
293
  // SetCoverageDirectory().
294
5
  CHECK_EQ(err, 0);
295
5
  CHECK_GT(size, 0);
296
5
  return cwd;
297
}
298
299
4651
void StartProfilers(Environment* env) {
300
4651
  Isolate* isolate = env->isolate();
301
9302
  Local<String> coverage_str = env->env_vars()->Get(
302
4651
      isolate, FIXED_ONE_BYTE_STRING(isolate, "NODE_V8_COVERAGE"));
303

9299
  if (!coverage_str.IsEmpty() && coverage_str->Length() > 0) {
304
4646
    CHECK_NULL(env->coverage_connection());
305
4646
    env->set_coverage_connection(std::make_unique<V8CoverageConnection>(env));
306
4646
    env->coverage_connection()->Start();
307
  }
308
4651
  if (env->options()->cpu_prof) {
309
10
    const std::string& dir = env->options()->cpu_prof_dir;
310
10
    env->set_cpu_prof_interval(env->options()->cpu_prof_interval);
311
10
    env->set_cpu_prof_dir(dir.empty() ? GetCwd() : dir);
312
10
    if (env->options()->cpu_prof_name.empty()) {
313
8
      DiagnosticFilename filename(env, "CPU", "cpuprofile");
314
8
      env->set_cpu_prof_name(*filename);
315
    } else {
316
2
      env->set_cpu_prof_name(env->options()->cpu_prof_name);
317
    }
318
10
    CHECK_NULL(env->cpu_profiler_connection());
319
    env->set_cpu_profiler_connection(
320
10
        std::make_unique<V8CpuProfilerConnection>(env));
321
10
    env->cpu_profiler_connection()->Start();
322
  }
323
4651
}
324
325
4638
static void SetCoverageDirectory(const FunctionCallbackInfo<Value>& args) {
326
13914
  CHECK(args[0]->IsString());
327
4638
  Environment* env = Environment::GetCurrent(args);
328
9276
  node::Utf8Value directory(env->isolate(), args[0].As<String>());
329
4638
  env->set_coverage_directory(*directory);
330
4638
}
331
332
4638
static void Initialize(Local<Object> target,
333
                       Local<Value> unused,
334
                       Local<Context> context,
335
                       void* priv) {
336
4638
  Environment* env = Environment::GetCurrent(context);
337
4638
  env->SetMethod(target, "setCoverageDirectory", SetCoverageDirectory);
338
4638
}
339
340
}  // namespace profiler
341
}  // namespace node
342
343
4524
NODE_MODULE_CONTEXT_AWARE_INTERNAL(profiler, node::profiler::Initialize)