From 226d68b8caa7de34a5e8ba9d34bea5c6abcd23a5 Mon Sep 17 00:00:00 2001 From: Howard Hellyer Date: Thu, 22 Jun 2017 10:50:17 +0100 Subject: [PATCH] Improve the way the tar.gz is created to have a top 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. --- .gitignore | 1 + README.md | 4 +-- index.js | 67 +++++++++++++++++++++++++-------------- package.json | 6 +++- test/test_collect_core.js | 10 +++--- 5 files changed, 58 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 0b4f40c..f024e86 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules build gencore.node +core_* diff --git a/README.md b/README.md index bc6a8bc..182daf7 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/index.js b/index.js index e628ba6..adf3c52 100644 --- a/index.js +++ b/index.js @@ -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); @@ -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. @@ -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); } @@ -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); @@ -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; +} diff --git a/package.json b/package.json index 2586b2c..97fe5ca 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/test/test_collect_core.js b/test/test_collect_core.js index 4535f14..79cfd4f 100644 --- a/test/test_collect_core.js +++ b/test/test_collect_core.js @@ -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.'); @@ -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?