Skip to content
This repository was archived by the owner on Jun 18, 2021. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ void Initialize(v8::Local<v8::Object> exports) {

SetLoadTime();
SetVersionString(isolate);
SetCommandLine();

const char* verbose_switch = secure_getenv("NODEREPORT_VERBOSE");
if (verbose_switch != nullptr) {
Expand Down
88 changes: 86 additions & 2 deletions src/node_report.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include <dlfcn.h>
#ifndef _AIX
#include <execinfo.h>
#else
#include <sys/procfs.h>
#endif
#include <sys/utsname.h>
#endif
Expand All @@ -41,6 +43,11 @@
#define UNKNOWN_NODEVERSION_STRING "Unable to determine Node.js version\n"
#endif

#ifdef __APPLE__
// Include _NSGetArgv and _NSGetArgc for command line arguments.
#include <crt_externs.h>
#endif

#ifndef _WIN32
extern char** environ;
#endif
Expand All @@ -58,6 +65,7 @@ using v8::String;
using v8::V8;

// Internal/static function declarations
static void PrintCommandLine(FILE* fp);
static void PrintVersionInformation(FILE* fp, Isolate* isolate);
static void PrintJavaScriptStack(FILE* fp, Isolate* isolate, DumpEvent event, const char* location);
static void PrintStackFromStackTrace(FILE* fp, Isolate* isolate, DumpEvent event);
Expand All @@ -77,6 +85,7 @@ static bool report_active = false; // recursion protection
static char report_filename[NR_MAXNAME + 1] = "";
static char report_directory[NR_MAXPATH + 1] = ""; // defaults to current working directory
static std::string version_string = UNKNOWN_NODEVERSION_STRING;
static std::string commandline_string = "";
#ifdef _WIN32
static SYSTEMTIME loadtime_tm_struct; // module load time
#else // UNIX, OSX
Expand Down Expand Up @@ -299,6 +308,67 @@ void SetLoadTime() {
localtime_r(&time_val.tv_sec, &loadtime_tm_struct);
#endif
}

/*******************************************************************************
* Function to save the process command line
*******************************************************************************/
void SetCommandLine() {
#ifdef __linux__
// Read the command line from /proc/self/cmdline
char buf[64];
FILE* cmdline_fd = fopen("/proc/self/cmdline", "r");
if (cmdline_fd == nullptr) {
return;
}
commandline_string = "";
int bytesread = fread(buf, 1, sizeof(buf), cmdline_fd);
while (bytesread > 0) {
for (int i = 0; i < bytesread; i++) {
// Arguments are null separated.
if (buf[i] == '\0') {
commandline_string += " ";
} else {
commandline_string += buf[i];
}
}
bytesread = fread(buf, 1, sizeof(buf), cmdline_fd);
}
fclose(cmdline_fd);
#elif __APPLE__
char **argv = *_NSGetArgv();
int argc = *_NSGetArgc();

commandline_string = "";
std::string separator = "";
for (int i = 0; i < argc; i++) {
commandline_string += separator + argv[i];
separator = " ";
}
#elif _AIX
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation for this #elif block is different from the rest of this function, please can you make it consistent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest of the function was wrong and didn't match the file. I'd indented when starting #ifdef sections. I've adjusted the rest of the function to be correct.

// Read the command line from /proc/self/cmdline
char procbuf[64];
snprintf(procbuf, sizeof(procbuf), "/proc/%d/psinfo", getpid());
FILE* psinfo_fd = fopen(procbuf, "r");
if (psinfo_fd == nullptr) {
return;
}
psinfo_t info;
int bytesread = fread(&info, 1, sizeof(psinfo_t), psinfo_fd);
fclose(psinfo_fd);
if (bytesread == sizeof(psinfo_t)) {
commandline_string = "";
std::string separator = "";
char **argv = *((char ***) info.pr_argv);
for (uint32_t i = 0; i < info.pr_argc; i++) {
commandline_string += separator + argv[i];
separator = " ";
}
}
#elif _WIN32
commandline_string = GetCommandLine();
#endif
}

/*******************************************************************************
* Main API function to write a NodeReport to file.
*
Expand All @@ -311,7 +381,7 @@ void SetLoadTime() {
******************************************************************************/
void TriggerNodeReport(Isolate* isolate, DumpEvent event, const char* message, const char* location, char* name) {
// Recursion check for NodeReport in progress, bail out
if (report_active) return;
if (report_active) return;
report_active = true;

// Obtain the current time and the pid (platform dependent)
Expand Down Expand Up @@ -414,6 +484,10 @@ void TriggerNodeReport(Isolate* isolate, DumpEvent event, const char* message, c
fprintf(fp, "Process ID: %d\n", pid);
fflush(fp);

// Print out the command line.
PrintCommandLine(fp);
fflush(fp);

// Print Node.js and OS version information
PrintVersionInformation(fp, isolate);
fflush(fp);
Expand Down Expand Up @@ -463,6 +537,16 @@ void TriggerNodeReport(Isolate* isolate, DumpEvent event, const char* message, c
report_active = false;
}

/*******************************************************************************
* Function to print process command line.
*
******************************************************************************/
static void PrintCommandLine(FILE* fp) {
if (commandline_string != "") {
fprintf(fp, "Command line: %s\n", commandline_string.c_str());
}
}

/*******************************************************************************
* Function to print Node.js version, OS version and machine information
*
Expand Down Expand Up @@ -688,7 +772,7 @@ void PrintNativeStack(FILE* fp) {
SymInitialize(hProcess, nullptr, TRUE);

WORD numberOfFrames = CaptureStackBackTrace(2, 64, frames, nullptr);

// Walk the frames printing symbolic information if available
for (int i = 0; i < numberOfFrames; i++) {
DWORD64 dwOffset64 = 0;
Expand Down
1 change: 1 addition & 0 deletions src/node_report.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ unsigned int ProcessNodeReportVerboseSwitch(const char* args);

void SetLoadTime();
void SetVersionString(Isolate* isolate);
void SetCommandLine();

// Local implementation of secure_getenv()
inline const char* secure_getenv(const char* key) {
Expand Down
21 changes: 17 additions & 4 deletions test/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@ exports.validate = (t, report, options) => {
const expectedVersions = options ?
options.expectedVersions || nodeComponents :
nodeComponents;
const plan = REPORT_SECTIONS.length + nodeComponents.length + 2;
var plan = REPORT_SECTIONS.length + nodeComponents.length + 2;
if( options.commandline ) plan++;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spaces -> if (options.commandline) plan++;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

t.plan(plan);

// Check all sections are present
REPORT_SECTIONS.forEach((section) => {
t.match(reportContents, new RegExp('==== ' + section),
'Checking report contains ' + section + ' section');
});

// Check NodeReport section
// Check NodeReport header section
const nodeReportSection = getSection(reportContents, 'NodeReport');
t.match(nodeReportSection, new RegExp('Process ID: ' + pid),
t.contains(nodeReportSection, new RegExp('Process ID: ' + pid),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this changed?

Copy link
Contributor

@rnchamberlain rnchamberlain Jan 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hhellyer said it fixed a test failure, but http://www.node-tap.org/asserts/ indicates that .contains is simply a synonym for .match, so I have reverted it. The tests pass ok for me on Linux, Windows and Mac.

'NodeReport section contains expected process ID');
if (options && options.expectNodeVersion === false) {
t.match(nodeReportSection, /Unable to determine Node.js version/,
Expand All @@ -57,6 +57,19 @@ exports.validate = (t, report, options) => {
new RegExp('Node.js version: ' + process.version),
'NodeReport section contains expected Node.js version');
}
if (options && options.commandline) {
if (this.isWindows()) {
// On Windows we need to strip out double quotes from the command line in the
// report, and escape the backslashes in the regex comparison string.
t.match(nodeReportSection.replace(/"/g,''),
new RegExp('Command line: ' + (options.commandline).replace(/\\/g,'\\\\')),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this line is > 80 chars.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

'Checking report contains expected command line');
} else {
t.match(nodeReportSection,
new RegExp('Command line: ' + options.commandline),
'Checking report contains expected command line');
}
}
nodeComponents.forEach((c) => {
if (c !== 'node') {
if (expectedVersions.indexOf(c) === -1) {
Expand Down
3 changes: 2 additions & 1 deletion test/test-api-bad-processobj.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ if (process.argv[2] === 'child') {
const reports = common.findReports(child.pid);
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
const validateOpts = { pid: child.pid, expectedVersions: [] };
const validateOpts = { pid: child.pid, expectedVersions: [],
commandline: child.spawnargs.join(' '), };
common.validate(tap, report, validateOpts);
});
}
3 changes: 2 additions & 1 deletion test/test-api-bad-processversion.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ if (process.argv[2] === 'child') {
const reports = common.findReports(child.pid);
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
const validateOpts = { pid: child.pid, expectNodeVersion: true };
const validateOpts = { pid: child.pid, expectNodeVersion: true,
commandline: child.spawnargs.join(' '), };
common.validate(tap, report, validateOpts);
});
}
3 changes: 2 additions & 1 deletion test/test-api-bad-processversions.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ if (process.argv[2] === 'child') {
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
const validateOpts = { pid: child.pid,
expectedVersions: Object.keys(process.versions).filter((c) => c !== 'uv')
expectedVersions: Object.keys(process.versions).filter((c) => c !== 'uv'),
commandline: child.spawnargs.join(' ')
};
common.validate(tap, report, validateOpts);
});
Expand Down
4 changes: 3 additions & 1 deletion test/test-api-nohooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ if (process.argv[2] === 'child') {
const reports = common.findReports(child.pid);
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
common.validate(tap, report, {pid: child.pid});
common.validate(tap, report, {pid: child.pid,
commandline: child.spawnargs.join(' ')
});
});
}
3 changes: 2 additions & 1 deletion test/test-api-noversioninfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ if (process.argv[2] === 'child') {
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
const validateOpts = { pid: child.pid, expectNodeVersion: false,
expectedVersions: [] };
expectedVersions: [], commandline: child.spawnargs.join(' ')
};
common.validate(tap, report, validateOpts);
});
}
4 changes: 3 additions & 1 deletion test/test-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ if (process.argv[2] === 'child') {
const reports = common.findReports(child.pid);
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
common.validate(tap, report, {pid: child.pid});
common.validate(tap, report, {pid: child.pid,
commandline: child.spawnargs.join(' ')
});
});
}
4 changes: 3 additions & 1 deletion test/test-exception.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ if (process.argv[2] === 'child') {
const reports = common.findReports(child.pid);
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
common.validate(tap, report, {pid: child.pid});
common.validate(tap, report, {pid: child.pid,
commandline: child.spawnargs.join(' ')
});
});
}
4 changes: 3 additions & 1 deletion test/test-fatal-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ if (process.argv[2] === 'child') {
const reports = common.findReports(child.pid);
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
common.validate(tap, report, {pid: child.pid});
common.validate(tap, report, {pid: child.pid,
commandline: child.spawnargs.join(' ')
});
});
}
4 changes: 3 additions & 1 deletion test/test-signal.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ if (process.argv[2] === 'child') {
const reports = common.findReports(child.pid);
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
common.validate(tap, report, {pid: child.pid});
common.validate(tap, report, {pid: child.pid,
commandline: child.spawnargs.join(' ')
});
});
}