Skip to content

Commit

Permalink
Improve the way the tar.gz is created to have a top
Browse files Browse the repository at this point in the history
level directory that matches the tar.gz name.
Use the same naming scheme for createCore so cores
end up in a meaningfully named directory rather than
one with a randomly generated name.

Also increment the npm version and put the testcase
dependencies in package.json.
  • Loading branch information
hhellyer committed Jun 22, 2017
1 parent 50a85c6 commit 226d68b
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
node_modules
build
gencore.node
core_*
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ Creates a core dump. The callback signature is (err, filename) where err is set

- `gencore.collectCore(callback)`

Creates a core dump and collects that and all the libraries loaded by the process into a tar.gz file. The callback signature is (err, filename) where err is set if an error occured and filename is the file containing the core and libraries.
The libraries are stored with their paths intact but relative to the directory they are extracted in. (The leading / is removed.) The core dump will be stored in the root directory of the tar.gz file.
Creates a core dump and collects that and all the libraries loaded by the process into a tar.gz file. The tar.gz is named "core_" followed by a timestamp, the pid of the Node.js process and a sequence number to ensure multiple files are unique. The callback signature is (err, filename) where err is set if an error occured and filename is the file containing the core and libraries.
All the files in the tar file are under a top level directory with the same name as the tar.gz file but without the .tar.gz extension. The libraries are stored with their paths intact but relative to the top level directory of the tar file. The core dump will be stored under the top level directory of the tar.gz file.
This function is intended to support analysis taking place on a different system to the one that generated the core dump. For example using lldb and llnode on a Mac to analyse a core from your production system.

*Note:* Core files are large (approximately your processes memory size) so you should ensure the files created by these APIs are deleted when you have finished with them. Repeatedly calling this API will without deleting the files it creates consume a large amount of disk space.
Expand Down
67 changes: 44 additions & 23 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,17 @@ function createCore(callback) {
callback(new Error('Function not supported on Windows.'));
return;
}
const work_dir = fs.mkdtempSync('core_');

// Create a directory for the child process to crash in.
// Use the timestamp to create a (hopefully) unique namespace
// that allows the core to be related back to which process
// crashed and when.
const timestamp = generateTimestamp();

// Run synchronously until fork() returns.
const work_dir = `core_${timestamp}`;
fs.mkdirSync(work_dir);

let result = null;
try {
result = gencore.forkCore(work_dir);
Expand Down Expand Up @@ -91,26 +101,15 @@ function collectCore(callback) {
// process changes between requesting a core and the core
// being created.

// Create a temporary directory for the child process to crash in
// since we can't be sure there isn't already a core file here or
// what the core file will be called.
const now = new Date();
function pad(n, len) {
if( len === undefined ) {
len = 2;
}
let str = `${n}`;
while(str.length < len) {
str = '0' + str;
}
return str;
}
// Create a directory for the child process to crash in.
// Use the timestamp to create a (hopefully) unique namespace
// that allows the core to be related back to which process
// crashed and when.
const timestamp = generateTimestamp();

// Create a time stamp for the tar.gz file name.
const timestamp = `${pad(now.getFullYear())}${pad(now.getMonth()+1)}` +
`${pad(now.getDate())}.${pad(now.getHours())}${pad(now.getMinutes())}` +
`${pad(now.getSeconds())}.${process.pid}.${pad(++seq,3)}`;
const work_dir = fs.mkdtempSync('core_');
// Run synchronously until fork() returns.
const work_dir = `core_${timestamp}`;
fs.mkdirSync(work_dir);

// Gather the library list before we allow async work
// that might change the list to run.
Expand All @@ -127,7 +126,6 @@ function collectCore(callback) {
// Now we can let other things run asyncrhonously!
result.libraries = libraries;
result.work_dir = work_dir;
result.timestamp = timestamp;
setImmediate(waitForCoreAndCollect, result, callback);
}

Expand Down Expand Up @@ -223,11 +221,11 @@ function copyFile(source, dest, closeCb) {
}

function tarGzDir(work_dir, result, callback) {
let tar_file = `core_${result.timestamp}.tar.gz`;
let tar_file = `${work_dir}.tar.gz`;

// Use ls to obtain a list of files in work dir so the
// resulting paths don't start with "./"
exec(`tar -C ${work_dir} -czf ${tar_file} \`ls ${work_dir}\``,
exec(`tar -czf ${tar_file} ${work_dir}`,
(error, stdout, stderr) => {
exec(`rm -r ${work_dir}`);
callback(error, tar_file);
Expand Down Expand Up @@ -257,3 +255,26 @@ function findCore(work_dir, pid) {
}
return undefined;
}

function generateTimestamp() {

const now = new Date();
function pad(n, len) {
if( len === undefined ) {
len = 2;
}
let str = `${n}`;
while(str.length < len) {
str = '0' + str;
}
return str;
}

// Create a time stamp that include the process id and a sequence number
// to make the core identifiable and unique.
const timestamp = `${pad(now.getFullYear())}${pad(now.getMonth()+1)}` +
`${pad(now.getDate())}.${pad(now.getHours())}${pad(now.getMinutes())}` +
`${pad(now.getSeconds())}.${process.pid}.${pad(++seq,3)}`;

return timestamp;
}
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
{
"name": "gencore",
"version": "0.0.3",
"version": "0.0.4",
"description": "Create a core dump from the currently running process without terminating or attaching a debugger.",
"main": "index.js",
"dependencies": {
"nan": "^2.3.5",
"fstream": ""
},
"devDependencies": {
"tar": "<3.0.0",
"tap": ""
},
"scripts": {
"test": "tap --timeout=300 test/test*.js"
},
Expand Down
10 changes: 6 additions & 4 deletions test/test_collect_core.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ const fs = require('fs');
const tap = require('tap');
const zlib = require('zlib');
const tar = require('tar');
const path = require('path');

var core_count = 0;
let core_count = 0;
let callback_count = 0;

tap.comment('Creating core and collecting libraries.');
Expand Down Expand Up @@ -32,9 +33,10 @@ function checkTarGz(error, filename) {
}

function checkEntry(entry) {
var name = entry.path;
var size = entry.size;
var type = entry.type;
// Trim off the top level directory containing our timestamp.
let name = entry.path.split(path.sep).slice(1).join(path.sep);
let size = entry.size;
let type = entry.type;

// Check there's a file in the root that has a core-ish name.
// TODO - How do I know how many files there are and when I'm done?
Expand Down

0 comments on commit 226d68b

Please sign in to comment.