GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
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 |
5204 |
V8ProfilerConnection::V8ProfilerConnection(Environment* env) |
|
31 |
: session_(env->inspector_agent()->Connect( |
||
32 |
std::make_unique<V8ProfilerConnection::V8ProfilerSessionDelegate>( |
||
33 |
this), |
||
34 |
false)), |
||
35 |
5204 |
env_(env) {} |
|
36 |
|||
37 |
15438 |
size_t V8ProfilerConnection::DispatchMessage(const char* method, |
|
38 |
const char* params) { |
||
39 |
15438 |
std::stringstream ss; |
|
40 |
15438 |
size_t id = next_id(); |
|
41 |
15438 |
ss << R"({ "id": )" << id; |
|
42 |
DCHECK(method != nullptr); |
||
43 |
15438 |
ss << R"(, "method": ")" << method << '"'; |
|
44 |
✓✓ | 15438 |
if (params != nullptr) { |
45 |
5204 |
ss << R"(, "params": )" << params; |
|
46 |
} |
||
47 |
15438 |
ss << " }"; |
|
48 |
30876 |
std::string message = ss.str(); |
|
49 |
const uint8_t* message_data = |
||
50 |
15438 |
reinterpret_cast<const uint8_t*>(message.c_str()); |
|
51 |
Debug(env(), |
||
52 |
DebugCategory::INSPECTOR_PROFILER, |
||
53 |
"Dispatching message %s\n", |
||
54 |
30876 |
message.c_str()); |
|
55 |
15438 |
session_->Dispatch(StringView(message_data, message.length())); |
|
56 |
// TODO(joyeecheung): use this to identify the ending message. |
||
57 |
30876 |
return id; |
|
58 |
} |
||
59 |
|||
60 |
5001 |
static void WriteResult(Environment* env, |
|
61 |
const char* path, |
||
62 |
Local<String> result) { |
||
63 |
5001 |
int ret = WriteFileSync(env->isolate(), path, result); |
|
64 |
✗✓ | 5001 |
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 |
5001 |
return; |
|
69 |
} |
||
70 |
Debug(env, DebugCategory::INSPECTOR_PROFILER, "Written result to %s\n", path); |
||
71 |
} |
||
72 |
|||
73 |
15438 |
void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend( |
|
74 |
const v8_inspector::StringView& message) { |
||
75 |
15438 |
Environment* env = connection_->env(); |
|
76 |
15438 |
Isolate* isolate = env->isolate(); |
|
77 |
15438 |
HandleScope handle_scope(isolate); |
|
78 |
✓✓ | 20458 |
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 |
15438 |
const char* type = connection_->type(); |
|
84 |
Debug(env, |
||
85 |
DebugCategory::INSPECTOR_PROFILER, |
||
86 |
"Receive %s profile message, ending = %s\n", |
||
87 |
type, |
||
88 |
✓✓ | 30876 |
connection_->ending() ? "true" : "false"); |
89 |
✓✓ | 15438 |
if (!connection_->ending()) { |
90 |
10418 |
return; |
|
91 |
} |
||
92 |
|||
93 |
// Convert StringView to a Local<String>. |
||
94 |
Local<String> message_str; |
||
95 |
✗✓ | 10040 |
if (!String::NewFromTwoByte(isolate, |
96 |
message.characters16(), |
||
97 |
NewStringType::kNormal, |
||
98 |
5020 |
message.length()) |
|
99 |
15060 |
.ToLocal(&message_str)) { |
|
100 |
fprintf(stderr, "Failed to convert %s profile message\n", type); |
||
101 |
return; |
||
102 |
} |
||
103 |
|||
104 |
10040 |
connection_->WriteProfile(message_str); |
|
105 |
} |
||
106 |
|||
107 |
5001 |
static bool EnsureDirectory(const std::string& directory, const char* type) { |
|
108 |
uv_fs_t req; |
||
109 |
5001 |
int ret = fs::MKDirpSync(nullptr, &req, directory, 0777, nullptr); |
|
110 |
5001 |
uv_fs_req_cleanup(&req); |
|
111 |
✗✓✗✗ |
5001 |
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 |
5001 |
return true; |
|
122 |
} |
||
123 |
|||
124 |
4981 |
std::string V8CoverageConnection::GetFilename() const { |
|
125 |
4981 |
std::string thread_id = std::to_string(env()->thread_id()); |
|
126 |
9962 |
std::string pid = std::to_string(uv_os_getpid()); |
|
127 |
std::string timestamp = std::to_string( |
||
128 |
9962 |
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 |
4981 |
thread_id.c_str()); |
|
136 |
9962 |
return filename; |
|
137 |
} |
||
138 |
|||
139 |
5020 |
static MaybeLocal<Object> ParseProfile(Environment* env, |
|
140 |
Local<String> message, |
||
141 |
const char* type) { |
||
142 |
5020 |
Local<Context> context = env->context(); |
|
143 |
5020 |
Isolate* isolate = env->isolate(); |
|
144 |
|||
145 |
// Get message.result from the response |
||
146 |
Local<Value> parsed; |
||
147 |
✓✗✗✓ ✓✗✗✓ |
15060 |
if (!v8::JSON::Parse(context, message).ToLocal(&parsed) || |
148 |
5020 |
!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 |
✗✓ | 10040 |
if (!parsed.As<Object>() |
155 |
20080 |
->Get(context, FIXED_ONE_BYTE_STRING(isolate, "result")) |
|
156 |
15060 |
.ToLocal(&result_v)) { |
|
157 |
fprintf(stderr, "Failed to get 'result' from %s profile message\n", type); |
||
158 |
return MaybeLocal<Object>(); |
||
159 |
} |
||
160 |
|||
161 |
✗✓ | 5020 |
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 |
5020 |
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 |
5000 |
void V8CoverageConnection::WriteProfile(Local<String> message) { |
|
205 |
5000 |
Isolate* isolate = env_->isolate(); |
|
206 |
5000 |
Local<Context> context = env_->context(); |
|
207 |
5000 |
HandleScope handle_scope(isolate); |
|
208 |
✓✓ | 4981 |
Context::Scope context_scope(context); |
209 |
|||
210 |
// Get message.result from the response. |
||
211 |
Local<Object> result; |
||
212 |
✗✓ | 10000 |
if (!ParseProfile(env_, message, type()).ToLocal(&result)) { |
213 |
return; |
||
214 |
} |
||
215 |
// Generate the profile output from the subclass. |
||
216 |
Local<Object> profile; |
||
217 |
✗✓ | 10000 |
if (!GetProfile(result).ToLocal(&profile)) { |
218 |
return; |
||
219 |
} |
||
220 |
|||
221 |
// append source-map cache information to coverage object: |
||
222 |
5000 |
Local<Function> source_map_cache_getter = env_->source_map_cache_getter(); |
|
223 |
Local<Value> source_map_cache_v; |
||
224 |
✓✓ | 9998 |
if (!source_map_cache_getter->Call(env()->context(), |
225 |
14999 |
Undefined(isolate), 0, nullptr) |
|
226 |
14998 |
.ToLocal(&source_map_cache_v)) { |
|
227 |
19 |
return; |
|
228 |
} |
||
229 |
// Avoid writing to disk if no source-map data: |
||
230 |
✓✓ | 9960 |
if (!source_map_cache_v->IsUndefined()) { |
231 |
profile->Set(context, FIXED_ONE_BYTE_STRING(isolate, "source-map-cache"), |
||
232 |
42 |
source_map_cache_v); |
|
233 |
} |
||
234 |
|||
235 |
Local<String> result_s; |
||
236 |
✗✓ | 9961 |
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 |
9962 |
std::string directory = GetDirectory(); |
|
243 |
DCHECK(!directory.empty()); |
||
244 |
✗✓ | 4981 |
if (!EnsureDirectory(directory, type())) { |
245 |
return; |
||
246 |
} |
||
247 |
|||
248 |
✓✗ | 9962 |
std::string filename = GetFilename(); |
249 |
DCHECK(!filename.empty()); |
||
250 |
9962 |
std::string path = directory + kPathSeparator + filename; |
|
251 |
|||
252 |
9962 |
WriteResult(env_, path.c_str(), result_s); |
|
253 |
} |
||
254 |
|||
255 |
5000 |
MaybeLocal<Object> V8CoverageConnection::GetProfile(Local<Object> result) { |
|
256 |
5000 |
return result; |
|
257 |
} |
||
258 |
|||
259 |
4981 |
std::string V8CoverageConnection::GetDirectory() const { |
|
260 |
4981 |
return env()->coverage_directory(); |
|
261 |
} |
||
262 |
|||
263 |
5184 |
void V8CoverageConnection::Start() { |
|
264 |
5184 |
DispatchMessage("Profiler.enable"); |
|
265 |
DispatchMessage("Profiler.startPreciseCoverage", |
||
266 |
5184 |
R"({ "callCount": true, "detailed": true })"); |
|
267 |
5184 |
} |
|
268 |
|||
269 |
5000 |
void V8CoverageConnection::End() { |
|
270 |
✗✓ | 5000 |
CHECK_EQ(ending_, false); |
271 |
5000 |
ending_ = true; |
|
272 |
5000 |
DispatchMessage("Profiler.takePreciseCoverage"); |
|
273 |
5000 |
} |
|
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 |
5096 |
void EndStartedProfilers(Environment* env) { |
|
355 |
Debug(env, DebugCategory::INSPECTOR_PROFILER, "EndStartedProfilers\n"); |
||
356 |
5096 |
V8ProfilerConnection* connection = env->cpu_profiler_connection(); |
|
357 |
✓✓✓✗ ✓✓ |
5096 |
if (connection != nullptr && !connection->ending()) { |
358 |
Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending cpu profiling\n"); |
||
359 |
10 |
connection->End(); |
|
360 |
} |
||
361 |
|||
362 |
5096 |
connection = env->heap_profiler_connection(); |
|
363 |
✓✓✓✗ ✓✓ |
5096 |
if (connection != nullptr && !connection->ending()) { |
364 |
Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending heap profiling\n"); |
||
365 |
10 |
connection->End(); |
|
366 |
} |
||
367 |
|||
368 |
5096 |
connection = env->coverage_connection(); |
|
369 |
✓✓✓✓ ✓✓ |
5096 |
if (connection != nullptr && !connection->ending()) { |
370 |
Debug( |
||
371 |
env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n"); |
||
372 |
5000 |
connection->End(); |
|
373 |
} |
||
374 |
5096 |
} |
|
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 |
5190 |
void StartProfilers(Environment* env) { |
|
393 |
5190 |
Isolate* isolate = env->isolate(); |
|
394 |
10380 |
Local<String> coverage_str = env->env_vars()->Get( |
|
395 |
5190 |
isolate, FIXED_ONE_BYTE_STRING(isolate, "NODE_V8_COVERAGE")) |
|
396 |
15570 |
.FromMaybe(Local<String>()); |
|
397 |
✓✓✓✓ ✓✓ |
10377 |
if (!coverage_str.IsEmpty() && coverage_str->Length() > 0) { |
398 |
✗✓ | 5184 |
CHECK_NULL(env->coverage_connection()); |
399 |
5184 |
env->set_coverage_connection(std::make_unique<V8CoverageConnection>(env)); |
|
400 |
5184 |
env->coverage_connection()->Start(); |
|
401 |
} |
||
402 |
✓✓ | 5190 |
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 |
✓✓ | 5190 |
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 |
5190 |
} |
|
432 |
|||
433 |
5188 |
static void SetCoverageDirectory(const FunctionCallbackInfo<Value>& args) { |
|
434 |
✗✓ | 15564 |
CHECK(args[0]->IsString()); |
435 |
5188 |
Environment* env = Environment::GetCurrent(args); |
|
436 |
10376 |
node::Utf8Value directory(env->isolate(), args[0].As<String>()); |
|
437 |
5188 |
env->set_coverage_directory(*directory); |
|
438 |
5188 |
} |
|
439 |
|||
440 |
|||
441 |
5188 |
static void SetSourceMapCacheGetter(const FunctionCallbackInfo<Value>& args) { |
|
442 |
✗✓ | 10376 |
CHECK(args[0]->IsFunction()); |
443 |
5188 |
Environment* env = Environment::GetCurrent(args); |
|
444 |
10376 |
env->set_source_map_cache_getter(args[0].As<Function>()); |
|
445 |
5188 |
} |
|
446 |
|||
447 |
5188 |
static void Initialize(Local<Object> target, |
|
448 |
Local<Value> unused, |
||
449 |
Local<Context> context, |
||
450 |
void* priv) { |
||
451 |
5188 |
Environment* env = Environment::GetCurrent(context); |
|
452 |
5188 |
env->SetMethod(target, "setCoverageDirectory", SetCoverageDirectory); |
|
453 |
5188 |
env->SetMethod(target, "setSourceMapCacheGetter", SetSourceMapCacheGetter); |
|
454 |
5188 |
} |
|
455 |
|||
456 |
} // namespace profiler |
||
457 |
} // namespace node |
||
458 |
|||
459 |
✓✗✓✗ |
20195 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(profiler, node::profiler::Initialize) |
Generated by: GCOVR (Version 3.4) |