GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/node_report.cc Lines: 252 255 98.8 %
Date: 2019-05-05 22:32:45 Branches: 60 84 71.4 %

Line Branch Exec Source
1
2
#include "node_report.h"
3
#include "debug_utils.h"
4
#include "node_internals.h"
5
#include "node_metadata.h"
6
7
#ifdef _WIN32
8
#include <Windows.h>
9
#else  // !_WIN32
10
#include <sys/resource.h>
11
#include <cxxabi.h>
12
#include <dlfcn.h>
13
#endif
14
15
#include <cstring>
16
#include <ctime>
17
#include <cwctype>
18
#include <fstream>
19
#include <iomanip>
20
#include <climits>  // PATH_MAX
21
22
#ifdef _WIN32
23
/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
24
#define PATH_MAX_BYTES (MAX_PATH * 4)
25
#else
26
#define PATH_MAX_BYTES (PATH_MAX)
27
#endif
28
29
#ifndef _WIN32
30
extern char** environ;
31
#endif
32
33
constexpr int NANOS_PER_SEC = 1000 * 1000 * 1000;
34
constexpr double SEC_PER_MICROS = 1e-6;
35
36
namespace report {
37
using node::arraysize;
38
using node::DiagnosticFilename;
39
using node::Environment;
40
using node::Mutex;
41
using node::NativeSymbolDebuggingContext;
42
using node::PerIsolateOptions;
43
using node::TIME_TYPE;
44
using v8::HeapSpaceStatistics;
45
using v8::HeapStatistics;
46
using v8::Isolate;
47
using v8::Local;
48
using v8::Number;
49
using v8::StackTrace;
50
using v8::String;
51
using v8::V8;
52
using v8::Value;
53
54
// Internal/static function declarations
55
static void WriteNodeReport(Isolate* isolate,
56
                            Environment* env,
57
                            const char* message,
58
                            const char* trigger,
59
                            const std::string& filename,
60
                            std::ostream& out,
61
                            Local<String> stackstr);
62
static void PrintVersionInformation(JSONWriter* writer);
63
static void PrintJavaScriptStack(JSONWriter* writer,
64
                                 Isolate* isolate,
65
                                 Local<String> stackstr,
66
                                 const char* trigger);
67
static void PrintNativeStack(JSONWriter* writer);
68
static void PrintResourceUsage(JSONWriter* writer);
69
static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate);
70
static void PrintSystemInformation(JSONWriter* writer);
71
static void PrintLoadedLibraries(JSONWriter* writer);
72
static void PrintComponentVersions(JSONWriter* writer);
73
static void PrintRelease(JSONWriter* writer);
74
75
// External function to trigger a report, writing to file.
76
// The 'name' parameter is in/out: an input filename is used
77
// if supplied, and the actual filename is returned.
78
10
std::string TriggerNodeReport(Isolate* isolate,
79
                              Environment* env,
80
                              const char* message,
81
                              const char* trigger,
82
                              const std::string& name,
83
                              Local<String> stackstr) {
84
10
  std::string filename;
85
20
  std::shared_ptr<PerIsolateOptions> options;
86
10
  if (env != nullptr) options = env->isolate_data()->options();
87
88
  // Determine the required report filename. In order of priority:
89
  //   1) supplied on API 2) configured on startup 3) default generated
90
10
  if (!name.empty()) {
91
    // Filename was specified as API parameter.
92
4
    filename = name;
93

6
  } else if (env != nullptr && options->report_filename.length() > 0) {
94
    // File name was supplied via start-up option.
95
1
    filename = options->report_filename;
96
  } else {
97
10
    filename = *DiagnosticFilename(env != nullptr ? env->thread_id() : 0,
98
5
                                   "report", "json");
99
  }
100
101
  // Open the report file stream for writing. Supports stdout/err,
102
  // user-specified or (default) generated name
103
20
  std::ofstream outfile;
104
  std::ostream* outstream;
105
10
  if (filename == "stdout") {
106
1
    outstream = &std::cout;
107
9
  } else if (filename == "stderr") {
108
1
    outstream = &std::cerr;
109
  } else {
110
    // Regular file. Append filename to directory path if one was specified
111

8
    if (env != nullptr && options->report_directory.length() > 0) {
112
8
      std::string pathname = options->report_directory;
113
8
      pathname += PATHSEP;
114
8
      pathname += filename;
115
8
      outfile.open(pathname, std::ios::out | std::ios::binary);
116
    } else {
117
      outfile.open(filename, std::ios::out | std::ios::binary);
118
    }
119
    // Check for errors on the file open
120
8
    if (!outfile.is_open()) {
121
1
      std::cerr << std::endl
122
1
                << "Failed to open Node.js report file: " << filename;
123
124

1
      if (env != nullptr && options->report_directory.length() > 0)
125
1
        std::cerr << " directory: " << options->report_directory;
126
127
1
      std::cerr << " (errno: " << errno << ")" << std::endl;
128
1
      return "";
129
    }
130
7
    outstream = &outfile;
131
7
    std::cerr << std::endl << "Writing Node.js report to file: " << filename;
132
  }
133
134
  WriteNodeReport(isolate, env, message, trigger, filename, *outstream,
135
9
                  stackstr);
136
137
  // Do not close stdout/stderr, only close files we opened.
138
9
  if (outfile.is_open()) {
139
7
    outfile.close();
140
  }
141
142
9
  std::cerr << std::endl << "Node.js report completed" << std::endl;
143
19
  return filename;
144
}
145
146
// External function to trigger a report, writing to a supplied stream.
147
4
void GetNodeReport(Isolate* isolate,
148
                   Environment* env,
149
                   const char* message,
150
                   const char* trigger,
151
                   Local<String> stackstr,
152
                   std::ostream& out) {
153
4
  WriteNodeReport(isolate, env, message, trigger, "", out, stackstr);
154
4
}
155
156
// Internal function to coordinate and write the various
157
// sections of the report to the supplied stream
158
13
static void WriteNodeReport(Isolate* isolate,
159
                            Environment* env,
160
                            const char* message,
161
                            const char* trigger,
162
                            const std::string& filename,
163
                            std::ostream& out,
164
                            Local<String> stackstr) {
165
  // Obtain the current time and the pid.
166
  TIME_TYPE tm_struct;
167
13
  DiagnosticFilename::LocalTime(&tm_struct);
168
13
  uv_pid_t pid = uv_os_getpid();
169
170
  // Save formatting for output stream.
171
13
  std::ios old_state(nullptr);
172
13
  old_state.copyfmt(out);
173
174
  // File stream opened OK, now start printing the report content:
175
  // the title and header information (event, filename, timestamp and pid)
176
177
13
  JSONWriter writer(out);
178
13
  writer.json_start();
179
13
  writer.json_objectstart("header");
180
181
13
  writer.json_keyvalue("event", message);
182
13
  writer.json_keyvalue("trigger", trigger);
183
13
  if (!filename.empty())
184
9
    writer.json_keyvalue("filename", filename);
185
  else
186
4
    writer.json_keyvalue("filename", JSONWriter::Null{});
187
188
  // Report dump event and module load date/time stamps
189
  char timebuf[64];
190
#ifdef _WIN32
191
  snprintf(timebuf,
192
           sizeof(timebuf),
193
           "%4d-%02d-%02dT%02d:%02d:%02dZ",
194
           tm_struct.wYear,
195
           tm_struct.wMonth,
196
           tm_struct.wDay,
197
           tm_struct.wHour,
198
           tm_struct.wMinute,
199
           tm_struct.wSecond);
200
  writer.json_keyvalue("dumpEventTime", timebuf);
201
#else  // UNIX, OSX
202
  snprintf(timebuf,
203
           sizeof(timebuf),
204
           "%4d-%02d-%02dT%02d:%02d:%02dZ",
205
           tm_struct.tm_year + 1900,
206
           tm_struct.tm_mon + 1,
207
           tm_struct.tm_mday,
208
           tm_struct.tm_hour,
209
           tm_struct.tm_min,
210
13
           tm_struct.tm_sec);
211
13
  writer.json_keyvalue("dumpEventTime", timebuf);
212
#endif
213
214
  uv_timeval64_t ts;
215
13
  if (uv_gettimeofday(&ts) == 0) {
216
    writer.json_keyvalue("dumpEventTimeStamp",
217
13
                         std::to_string(ts.tv_sec * 1000 + ts.tv_usec / 1000));
218
  }
219
220
  // Report native process ID
221
13
  writer.json_keyvalue("processId", pid);
222
223
  {
224
    // Report the process cwd.
225
    char buf[PATH_MAX_BYTES];
226
13
    size_t cwd_size = sizeof(buf);
227
13
    if (uv_cwd(buf, &cwd_size) == 0)
228
13
      writer.json_keyvalue("cwd", buf);
229
  }
230
231
  // Report out the command line.
232
13
  if (!node::per_process::cli_options->cmdline.empty()) {
233
13
    writer.json_arraystart("commandLine");
234
57
    for (const std::string& arg : node::per_process::cli_options->cmdline) {
235
44
      writer.json_element(arg);
236
    }
237
13
    writer.json_arrayend();
238
  }
239
240
  // Report Node.js and OS version information
241
13
  PrintVersionInformation(&writer);
242
13
  writer.json_objectend();
243
244
  // Report summary JavaScript stack backtrace
245
13
  PrintJavaScriptStack(&writer, isolate, stackstr, trigger);
246
247
  // Report native stack backtrace
248
13
  PrintNativeStack(&writer);
249
250
  // Report V8 Heap and Garbage Collector information
251
13
  PrintGCStatistics(&writer, isolate);
252
253
  // Report OS and current thread resource usage
254
13
  PrintResourceUsage(&writer);
255
256
13
  writer.json_arraystart("libuv");
257
13
  if (env != nullptr) {
258
13
    uv_walk(env->event_loop(), WalkHandle, static_cast<void*>(&writer));
259
260
13
    writer.json_start();
261
13
    writer.json_keyvalue("type", "loop");
262
    writer.json_keyvalue("is_active",
263
13
        static_cast<bool>(uv_loop_alive(env->event_loop())));
264
    writer.json_keyvalue("address",
265
13
        ValueToHexString(reinterpret_cast<int64_t>(env->event_loop())));
266
13
    writer.json_end();
267
  }
268
269
13
  writer.json_arrayend();
270
271
  // Report operating system information
272
13
  PrintSystemInformation(&writer);
273
274
13
  writer.json_objectend();
275
276
  // Restore output stream formatting.
277
13
  out.copyfmt(old_state);
278
13
}
279
280
// Report Node.js version, OS version and machine information.
281
13
static void PrintVersionInformation(JSONWriter* writer) {
282
13
  std::ostringstream buf;
283
  // Report Node version
284
13
  buf << "v" << NODE_VERSION_STRING;
285
13
  writer->json_keyvalue("nodejsVersion", buf.str());
286
13
  buf.str("");
287
288
#ifndef _WIN32
289
  // Report compiler and runtime glibc versions where possible.
290
  const char* (*libc_version)();
291
  *(reinterpret_cast<void**>(&libc_version)) =
292
13
      dlsym(RTLD_DEFAULT, "gnu_get_libc_version");
293
13
  if (libc_version != nullptr)
294
13
    writer->json_keyvalue("glibcVersionRuntime", (*libc_version)());
295
#endif /* _WIN32 */
296
297
#ifdef __GLIBC__
298
13
  buf << __GLIBC__ << "." << __GLIBC_MINOR__;
299
13
  writer->json_keyvalue("glibcVersionCompiler", buf.str());
300
13
  buf.str("");
301
#endif
302
303
  // Report Process word size
304
13
  writer->json_keyvalue("wordSize", sizeof(void*) * 8);
305
13
  writer->json_keyvalue("arch", node::per_process::metadata.arch);
306
13
  writer->json_keyvalue("platform", node::per_process::metadata.platform);
307
308
  // Report deps component versions
309
13
  PrintComponentVersions(writer);
310
311
  // Report release metadata.
312
13
  PrintRelease(writer);
313
314
  // Report operating system and machine information
315
  uv_utsname_t os_info;
316
317
13
  if (uv_os_uname(&os_info) == 0) {
318
13
    writer->json_keyvalue("osName", os_info.sysname);
319
13
    writer->json_keyvalue("osRelease", os_info.release);
320
13
    writer->json_keyvalue("osVersion", os_info.version);
321
13
    writer->json_keyvalue("osMachine", os_info.machine);
322
  }
323
324
  char host[UV_MAXHOSTNAMESIZE];
325
13
  size_t host_size = sizeof(host);
326
327
13
  if (uv_os_gethostname(host, &host_size) == 0)
328
13
    writer->json_keyvalue("host", host);
329
13
}
330
331
// Report the JavaScript stack.
332
13
static void PrintJavaScriptStack(JSONWriter* writer,
333
                                 Isolate* isolate,
334
                                 Local<String> stackstr,
335
                                 const char* trigger) {
336
13
  writer->json_objectstart("javascriptStack");
337
338
13
  std::string ss;
339

26
  if ((!strcmp(trigger, "FatalError")) ||
340
13
      (!strcmp(trigger, "Signal"))) {
341
1
    ss = "No stack.\nUnavailable.\n";
342
  } else {
343
12
    String::Utf8Value sv(isolate, stackstr);
344
12
    ss = std::string(*sv, sv.length());
345
  }
346
13
  int line = ss.find('\n');
347
13
  if (line == -1) {
348
    writer->json_keyvalue("message", ss);
349
    writer->json_objectend();
350
  } else {
351
13
    std::string l = ss.substr(0, line);
352
13
    writer->json_keyvalue("message", l);
353
13
    writer->json_arraystart("stack");
354
13
    ss = ss.substr(line + 1);
355
13
    line = ss.find('\n');
356
104
    while (line != -1) {
357
78
      l = ss.substr(0, line);
358
386
      l.erase(l.begin(), std::find_if(l.begin(), l.end(), [](int ch) {
359
386
                return !std::iswspace(ch);
360
464
              }));
361
78
      writer->json_element(l);
362
78
      ss = ss.substr(line + 1);
363
78
      line = ss.find('\n');
364
13
    }
365
  }
366
13
  writer->json_arrayend();
367
13
  writer->json_objectend();
368
13
}
369
370
// Report a native stack backtrace
371
13
static void PrintNativeStack(JSONWriter* writer) {
372
13
  auto sym_ctx = NativeSymbolDebuggingContext::New();
373
  void* frames[256];
374
13
  const int size = sym_ctx->GetStackTrace(frames, arraysize(frames));
375
13
  writer->json_arraystart("nativeStack");
376
  int i;
377
117
  for (i = 1; i < size; i++) {
378
104
    void* frame = frames[i];
379
104
    writer->json_start();
380
    writer->json_keyvalue("pc",
381
104
                          ValueToHexString(reinterpret_cast<uintptr_t>(frame)));
382
104
    writer->json_keyvalue("symbol", sym_ctx->LookupSymbol(frame).Display());
383
104
    writer->json_end();
384
  }
385
13
  writer->json_arrayend();
386
13
}
387
388
// Report V8 JavaScript heap information.
389
// This uses the existing V8 HeapStatistics and HeapSpaceStatistics APIs.
390
// The isolate->GetGCStatistics(&heap_stats) internal V8 API could potentially
391
// provide some more useful information - the GC history and the handle counts
392
13
static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate) {
393
13
  HeapStatistics v8_heap_stats;
394
13
  isolate->GetHeapStatistics(&v8_heap_stats);
395
13
  HeapSpaceStatistics v8_heap_space_stats;
396
397
13
  writer->json_objectstart("javascriptHeap");
398
13
  writer->json_keyvalue("totalMemory", v8_heap_stats.total_heap_size());
399
  writer->json_keyvalue("totalCommittedMemory",
400
13
                        v8_heap_stats.total_physical_size());
401
13
  writer->json_keyvalue("usedMemory", v8_heap_stats.used_heap_size());
402
  writer->json_keyvalue("availableMemory",
403
13
                        v8_heap_stats.total_available_size());
404
13
  writer->json_keyvalue("memoryLimit", v8_heap_stats.heap_size_limit());
405
406
13
  writer->json_objectstart("heapSpaces");
407
  // Loop through heap spaces
408
117
  for (size_t i = 0; i < isolate->NumberOfHeapSpaces(); i++) {
409
104
    isolate->GetHeapSpaceStatistics(&v8_heap_space_stats, i);
410
104
    writer->json_objectstart(v8_heap_space_stats.space_name());
411
104
    writer->json_keyvalue("memorySize", v8_heap_space_stats.space_size());
412
    writer->json_keyvalue(
413
        "committedMemory",
414
104
        v8_heap_space_stats.physical_space_size());
415
    writer->json_keyvalue(
416
        "capacity",
417
208
        v8_heap_space_stats.space_used_size() +
418
208
            v8_heap_space_stats.space_available_size());
419
104
    writer->json_keyvalue("used", v8_heap_space_stats.space_used_size());
420
    writer->json_keyvalue(
421
104
        "available", v8_heap_space_stats.space_available_size());
422
104
    writer->json_objectend();
423
  }
424
425
13
  writer->json_objectend();
426
13
  writer->json_objectend();
427
13
}
428
429
13
static void PrintResourceUsage(JSONWriter* writer) {
430
  // Get process uptime in seconds
431
  uint64_t uptime =
432
13
      (uv_hrtime() - node::per_process::node_start_time) / (NANOS_PER_SEC);
433
13
  if (uptime == 0) uptime = 1;  // avoid division by zero.
434
435
  // Process and current thread usage statistics
436
  uv_rusage_t rusage;
437
13
  writer->json_objectstart("resourceUsage");
438
13
  if (uv_getrusage(&rusage) == 0) {
439
    double user_cpu =
440
13
        rusage.ru_utime.tv_sec + SEC_PER_MICROS * rusage.ru_utime.tv_usec;
441
    double kernel_cpu =
442
13
        rusage.ru_stime.tv_sec + SEC_PER_MICROS * rusage.ru_stime.tv_usec;
443
13
    writer->json_keyvalue("userCpuSeconds", user_cpu);
444
13
    writer->json_keyvalue("kernelCpuSeconds", kernel_cpu);
445
13
    double cpu_abs = user_cpu + kernel_cpu;
446
13
    double cpu_percentage = (cpu_abs / uptime) * 100.0;
447
13
    writer->json_keyvalue("cpuConsumptionPercent", cpu_percentage);
448
13
    writer->json_keyvalue("maxRss", rusage.ru_maxrss * 1024);
449
13
    writer->json_objectstart("pageFaults");
450
13
    writer->json_keyvalue("IORequired", rusage.ru_majflt);
451
13
    writer->json_keyvalue("IONotRequired", rusage.ru_minflt);
452
13
    writer->json_objectend();
453
13
    writer->json_objectstart("fsActivity");
454
13
    writer->json_keyvalue("reads", rusage.ru_inblock);
455
13
    writer->json_keyvalue("writes", rusage.ru_oublock);
456
13
    writer->json_objectend();
457
  }
458
13
  writer->json_objectend();
459
#ifdef RUSAGE_THREAD
460
  struct rusage stats;
461
13
  if (getrusage(RUSAGE_THREAD, &stats) == 0) {
462
13
    writer->json_objectstart("uvthreadResourceUsage");
463
    double user_cpu =
464
13
        stats.ru_utime.tv_sec + SEC_PER_MICROS * stats.ru_utime.tv_usec;
465
    double kernel_cpu =
466
13
        stats.ru_stime.tv_sec + SEC_PER_MICROS * stats.ru_stime.tv_usec;
467
13
    writer->json_keyvalue("userCpuSeconds", user_cpu);
468
13
    writer->json_keyvalue("kernelCpuSeconds", kernel_cpu);
469
13
    double cpu_abs = user_cpu + kernel_cpu;
470
13
    double cpu_percentage = (cpu_abs / uptime) * 100.0;
471
13
    writer->json_keyvalue("cpuConsumptionPercent", cpu_percentage);
472
13
    writer->json_objectstart("fsActivity");
473
13
    writer->json_keyvalue("reads", stats.ru_inblock);
474
13
    writer->json_keyvalue("writes", stats.ru_oublock);
475
13
    writer->json_objectend();
476
13
    writer->json_objectend();
477
  }
478
#endif
479
13
}
480
481
// Report operating system information.
482
13
static void PrintSystemInformation(JSONWriter* writer) {
483
#ifndef _WIN32
484
  static struct {
485
    const char* description;
486
    int id;
487
  } rlimit_strings[] = {
488
    {"core_file_size_blocks", RLIMIT_CORE},
489
    {"data_seg_size_kbytes", RLIMIT_DATA},
490
    {"file_size_blocks", RLIMIT_FSIZE},
491
#if !(defined(_AIX) || defined(__sun))
492
    {"max_locked_memory_bytes", RLIMIT_MEMLOCK},
493
#endif
494
#ifndef __sun
495
    {"max_memory_size_kbytes", RLIMIT_RSS},
496
#endif
497
    {"open_files", RLIMIT_NOFILE},
498
    {"stack_size_bytes", RLIMIT_STACK},
499
    {"cpu_time_seconds", RLIMIT_CPU},
500
#ifndef __sun
501
    {"max_user_processes", RLIMIT_NPROC},
502
#endif
503
    {"virtual_memory_kbytes", RLIMIT_AS}
504
  };
505
#endif  // _WIN32
506
13
  writer->json_objectstart("environmentVariables");
507
13
  Mutex::ScopedLock lock(node::per_process::env_var_mutex);
508
#ifdef _WIN32
509
  LPWSTR lpszVariable;
510
  LPWCH lpvEnv;
511
512
  // Get pointer to the environment block
513
  lpvEnv = GetEnvironmentStringsW();
514
  if (lpvEnv != nullptr) {
515
    // Variable strings are separated by null bytes,
516
    // and the block is terminated by a null byte.
517
    lpszVariable = reinterpret_cast<LPWSTR>(lpvEnv);
518
    while (*lpszVariable) {
519
      DWORD size = WideCharToMultiByte(
520
          CP_UTF8, 0, lpszVariable, -1, nullptr, 0, nullptr, nullptr);
521
      char* str = new char[size];
522
      WideCharToMultiByte(
523
          CP_UTF8, 0, lpszVariable, -1, str, size, nullptr, nullptr);
524
      std::string env(str);
525
      int sep = env.rfind('=');
526
      std::string key = env.substr(0, sep);
527
      std::string value = env.substr(sep + 1);
528
      writer->json_keyvalue(key, value);
529
      lpszVariable += lstrlenW(lpszVariable) + 1;
530
    }
531
    FreeEnvironmentStringsW(lpvEnv);
532
  }
533
  writer->json_objectend();
534
#else
535
26
  std::string pair;
536
832
  for (char** env = environ; *env != nullptr; ++env) {
537
819
    std::string pair(*env);
538
819
    int separator = pair.find('=');
539
1638
    std::string key = pair.substr(0, separator);
540
1638
    std::string str = pair.substr(separator + 1);
541
819
    writer->json_keyvalue(key, str);
542
819
  }
543
13
  writer->json_objectend();
544
545
13
  writer->json_objectstart("userLimits");
546
  struct rlimit limit;
547
26
  std::string soft, hard;
548
549
143
  for (size_t i = 0; i < arraysize(rlimit_strings); i++) {
550
130
    if (getrlimit(rlimit_strings[i].id, &limit) == 0) {
551
130
      writer->json_objectstart(rlimit_strings[i].description);
552
553
130
      if (limit.rlim_cur == RLIM_INFINITY)
554
78
        writer->json_keyvalue("soft", "unlimited");
555
      else
556
52
        writer->json_keyvalue("soft", limit.rlim_cur);
557
558
130
      if (limit.rlim_max == RLIM_INFINITY)
559
91
        writer->json_keyvalue("hard", "unlimited");
560
      else
561
39
        writer->json_keyvalue("hard", limit.rlim_max);
562
563
130
      writer->json_objectend();
564
    }
565
  }
566
13
  writer->json_objectend();
567
#endif
568
569
26
  PrintLoadedLibraries(writer);
570
13
}
571
572
// Report a list of loaded native libraries.
573
13
static void PrintLoadedLibraries(JSONWriter* writer) {
574
13
  writer->json_arraystart("sharedObjects");
575
  std::vector<std::string> modules =
576
13
      NativeSymbolDebuggingContext::GetLoadedLibraries();
577
13
  for (auto const& module_name : modules) writer->json_element(module_name);
578
13
  writer->json_arrayend();
579
13
}
580
581
// Obtain and report the node and subcomponent version strings.
582
13
static void PrintComponentVersions(JSONWriter* writer) {
583
13
  std::stringstream buf;
584
585
13
  writer->json_objectstart("componentVersions");
586
587
#define V(key)                                                                 \
588
  writer->json_keyvalue(#key, node::per_process::metadata.versions.key);
589
13
  NODE_VERSIONS_KEYS(V)
590
#undef V
591
592
13
  writer->json_objectend();
593
13
}
594
595
// Report runtime release information.
596
13
static void PrintRelease(JSONWriter* writer) {
597
13
  writer->json_objectstart("release");
598
13
  writer->json_keyvalue("name", node::per_process::metadata.release.name);
599
#if NODE_VERSION_IS_LTS
600
  writer->json_keyvalue("lts", node::per_process::metadata.release.lts);
601
#endif
602
603
#ifdef NODE_HAS_RELEASE_URLS
604
  writer->json_keyvalue("headersUrl",
605
                        node::per_process::metadata.release.headers_url);
606
  writer->json_keyvalue("sourceUrl",
607
                        node::per_process::metadata.release.source_url);
608
#ifdef _WIN32
609
  writer->json_keyvalue("libUrl", node::per_process::metadata.release.lib_url);
610
#endif  // _WIN32
611
#endif  // NODE_HAS_RELEASE_URLS
612
613
13
  writer->json_objectend();
614
13
}
615
616

13575
}  // namespace report