summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan Cantrill <bryan@joyent.com>2012-09-21 01:48:55 +0200
committerBert Belder <bertbelder@gmail.com>2012-09-21 01:51:57 +0200
commitcc1b09d6b7c3cc6b8729804cbf644634ba5d0815 (patch)
tree97d25564d761c84ad920c29b3e53030ecef29767
parentbbf6b4ecbb55668a3ccd63695b38c4c41a76b82a (diff)
downloadnode-cc1b09d6b7c3cc6b8729804cbf644634ba5d0815.tar.gz
test: add tests for postmortem and DTrace support
-rw-r--r--test/pummel/test-dtrace-jsstack.js107
-rw-r--r--test/pummel/test-postmortem-findjsobjects.js99
-rw-r--r--test/pummel/test-postmortem-jsstack.js179
3 files changed, 385 insertions, 0 deletions
diff --git a/test/pummel/test-dtrace-jsstack.js b/test/pummel/test-dtrace-jsstack.js
new file mode 100644
index 000000000..6779a766a
--- /dev/null
+++ b/test/pummel/test-dtrace-jsstack.js
@@ -0,0 +1,107 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var common = require('../common');
+var assert = require('assert');
+var os = require('os');
+var util = require('util');
+
+if (os.type() != 'SunOS') {
+ console.error('Skipping because DTrace not available.');
+ process.exit(0);
+}
+
+/*
+ * Some functions to create a recognizable stack.
+ */
+var frames = [ 'stalloogle', 'bagnoogle', 'doogle' ];
+var expected;
+
+var stalloogle = function (str) {
+ expected = str;
+ os.loadavg();
+};
+
+var bagnoogle = function (arg0, arg1) {
+ stalloogle(arg0 + ' is ' + arg1 + ' except that it is read-only');
+};
+
+var done = false;
+
+var doogle = function () {
+ if (!done)
+ setTimeout(doogle, 10);
+
+ bagnoogle('The bfs command', '(almost) like ed(1)');
+};
+
+var spawn = require('child_process').spawn;
+var prefix = '/var/tmp/node';
+var corefile = prefix + '.' + process.pid;
+
+/*
+ * We're going to use DTrace to stop us, gcore us, and set us running again
+ * when we call getloadavg() -- with the implicit assumption that our
+ * deepest function is the only caller of os.loadavg().
+ */
+var dtrace = spawn('dtrace', [ '-qwn', 'syscall::getloadavg:entry/pid == ' +
+ process.pid + '/{ustack(100, 8192); exit(0); }' ]);
+
+var output = '';
+
+dtrace.stderr.on('data', function (data) {
+ console.log('dtrace: ' + data);
+});
+
+dtrace.stdout.on('data', function (data) {
+ output += data;
+});
+
+dtrace.on('exit', function (code) {
+ if (code != 0) {
+ console.error('dtrace exited with code ' + code);
+ process.exit(code);
+ }
+
+ done = true;
+
+ var sentinel = '(anon) as ';
+ var lines = output.split('\n');
+
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+
+ if (line.indexOf(sentinel) == -1 || frames.length === 0)
+ continue;
+
+ var frame = line.substr(line.indexOf(sentinel) + sentinel.length);
+ var top = frames.shift();
+
+ assert.equal(frame.indexOf(top), 0, 'unexpected frame where ' +
+ top + ' was expected');
+ }
+
+ assert.equal(frames.length, 0, 'did not find expected frame ' + frames[0]);
+ process.exit(0);
+});
+
+setTimeout(doogle, 10);
+
diff --git a/test/pummel/test-postmortem-findjsobjects.js b/test/pummel/test-postmortem-findjsobjects.js
new file mode 100644
index 000000000..568e2ff05
--- /dev/null
+++ b/test/pummel/test-postmortem-findjsobjects.js
@@ -0,0 +1,99 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var common = require('../common');
+var assert = require('assert');
+var os = require('os');
+var util = require('util');
+
+if (os.type() != 'SunOS') {
+ console.error('Skipping because postmortem debugging not available.');
+ process.exit(0);
+}
+
+/*
+ * Now we're going to fork ourselves to gcore
+ */
+var spawn = require('child_process').spawn;
+var prefix = '/var/tmp/node';
+var corefile = prefix + '.' + process.pid;
+var gcore = spawn('gcore', [ '-o', prefix, process.pid + '' ]);
+var output = '';
+var unlinkSync = require('fs').unlinkSync;
+var args = [ corefile ];
+
+if (process.env.MDB_LIBRARY_PATH && process.env.MDB_LIBRARY_PATH != '')
+ args = args.concat([ '-L', process.env.MDB_LIBRARY_PATH ]);
+
+function LanguageH(chapter) { this.OBEY = 'CHAPTER ' + parseInt(chapter, 10); }
+var obj = new LanguageH(1);
+
+gcore.stderr.on('data', function (data) {
+ console.log('gcore: ' + data);
+});
+
+gcore.on('exit', function (code) {
+ if (code != 0) {
+ console.error('gcore exited with code ' + code);
+ process.exit(code);
+ }
+
+ var mdb = spawn('mdb', args, { stdio: 'pipe' });
+
+ mdb.on('exit', function (code) {
+ var retained = '; core retained as ' + corefile;
+
+ if (code != 0) {
+ console.error('mdb exited with code ' + util.inspect(code) + retained);
+ process.exit(code);
+ }
+
+ var lines = output.split('\n');
+ var found = 0, i, expected = 'OBEY: ' + obj.OBEY, nexpected = 2;
+
+ for (var i = 0; i < lines.length; i++) {
+ if (lines[i].indexOf(expected) != -1)
+ found++;
+ }
+
+ assert.equal(found, nexpected, 'expected ' + nexpected +
+ ' objects, found ' + found + retained);
+
+ unlinkSync(corefile);
+ process.exit(0);
+ });
+
+ mdb.stdout.on('data', function (data) {
+ output += data;
+ });
+
+ mdb.stderr.on('data', function (data) {
+ console.log('mdb stderr: ' + data);
+ });
+
+ mdb.stdin.write('::load v8.so\n');
+ mdb.stdin.write('::findjsobjects -c LanguageH | ');
+ mdb.stdin.write('::findjsobjects | ::jsprint\n');
+ mdb.stdin.write('::findjsobjects -p OBEY | ');
+ mdb.stdin.write('::findjsobjects | ::jsprint\n');
+ mdb.stdin.end();
+});
+
diff --git a/test/pummel/test-postmortem-jsstack.js b/test/pummel/test-postmortem-jsstack.js
new file mode 100644
index 000000000..735e39e21
--- /dev/null
+++ b/test/pummel/test-postmortem-jsstack.js
@@ -0,0 +1,179 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var common = require('../common');
+var assert = require('assert');
+var os = require('os');
+var util = require('util');
+
+if (os.type() != 'SunOS') {
+ console.error('Skipping because postmortem debugging not available.');
+ process.exit(0);
+}
+
+/*
+ * Some functions to create a recognizable stack.
+ */
+var frames = [ 'stalloogle', 'bagnoogle', 'doogle' ];
+var expected;
+
+var stalloogle = function (str) {
+ expected = str;
+ os.loadavg();
+};
+
+var bagnoogle = function (arg0, arg1) {
+ stalloogle(arg0 + ' is ' + arg1 + ' except that it is read-only');
+};
+
+var done = false;
+
+var doogle = function () {
+ if (!done)
+ setTimeout(doogle, 10);
+
+ bagnoogle('The bfs command', '(almost) like ed(1)');
+};
+
+var spawn = require('child_process').spawn;
+var prefix = '/var/tmp/node';
+var corefile = prefix + '.' + process.pid;
+var args = [ corefile ];
+
+if (process.env.MDB_LIBRARY_PATH && process.env.MDB_LIBRARY_PATH != '')
+ args = args.concat([ '-L', process.env.MDB_LIBRARY_PATH ]);
+
+/*
+ * We're going to use DTrace to stop us, gcore us, and set us running again
+ * when we call getloadavg() -- with the implicit assumption that our
+ * deepest function is the only caller of os.loadavg().
+ */
+var dtrace = spawn('dtrace', [ '-qwn', 'syscall::getloadavg:entry/pid == ' +
+ process.pid + '/{stop(); system("gcore -o ' +
+ prefix + ' %d", pid); system("prun %d", pid); exit(0); }' ]);
+
+var output = '';
+var unlinkSync = require('fs').unlinkSync;
+
+dtrace.stderr.on('data', function (data) {
+ console.log('dtrace: ' + data);
+});
+
+dtrace.on('exit', function (code) {
+ if (code != 0) {
+ console.error('dtrace exited with code ' + code);
+ process.exit(code);
+ }
+
+ done = true;
+
+ /*
+ * We have our core file. Now we need to fire up mdb to analyze it...
+ */
+ var mdb = spawn('mdb', args, { stdio: 'pipe' });
+
+ mdb.on('exit', function (code) {
+ var retained = '; core retained as ' + corefile;
+
+ if (code != 0) {
+ console.error('mdb exited with code ' + code + retained);
+ process.exit(code);
+ }
+
+ var sentinel = '<anonymous> (as ';
+ var arg1 = ' arg1: ';
+ var lines = output.split('\n');
+ var matched = 0;
+ var straddr = undefined;
+
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+
+ if (matched == 1 && line.indexOf(arg1) === 0) {
+ straddr = line.substr(arg1.length).split(' ')[0];
+ }
+
+ if (line.indexOf(sentinel) == -1 || frames.length === 0)
+ continue;
+
+ var frame = line.substr(line.indexOf(sentinel) + sentinel.length);
+ var top = frames.shift();
+
+ assert.equal(frame.indexOf(top), 0, 'unexpected frame where ' +
+ top + ' was expected' + retained);
+
+ matched++;
+ }
+
+ assert.equal(frames.length, 0, 'did not find expected frame ' +
+ frames[0] + retained);
+
+ assert.notEqual(straddr, undefined,
+ 'did not find arg1 for top frame' + retained);
+
+ /*
+ * Now we're going to take one more swing at the core file to print out
+ * the argument string that we found.
+ */
+ output = '';
+ mdb = spawn('mdb', args, { stdio: 'pipe' });
+
+ mdb.on('exit', function (code) {
+ if (code != 0) {
+ console.error('mdb (second) exited with code ' + code + retained);
+ process.exit(code);
+ }
+
+ assert.notEqual(output.indexOf(expected), -1, 'did not find arg1 (' +
+ straddr + ') to contain expected string' + retained);
+
+ unlinkSync(corefile);
+ process.exit(0);
+ });
+
+ mdb.stdout.on('data', function (data) {
+ output += data;
+ });
+
+ mdb.stderr.on('data', function (data) {
+ console.log('mdb (second) stderr: ' + data);
+ });
+
+ mdb.stdin.write('::load v8.so\n');
+ mdb.stdin.write(straddr + '::v8str\n');
+ mdb.stdin.end();
+ });
+
+ mdb.stdout.on('data', function (data) {
+ output += data;
+ });
+
+ mdb.stderr.on('data', function (data) {
+ console.log('mdb stderr: ' + data);
+ });
+
+ mdb.stdin.write('::load v8.so\n');
+ mdb.stdin.write('::jsstack -v\n');
+ mdb.stdin.end();
+});
+
+setTimeout(doogle, 10);
+