Skip to content

Commit 226d68b

Browse files
committed
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.
1 parent 50a85c6 commit 226d68b

File tree

5 files changed

+58
-30
lines changed

5 files changed

+58
-30
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
node_modules
44
build
55
gencore.node
6+
core_*

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ Creates a core dump. The callback signature is (err, filename) where err is set
3737

3838
- `gencore.collectCore(callback)`
3939

40-
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.
41-
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.
40+
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.
41+
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.
4242
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.
4343

4444
*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.

index.js

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,17 @@ function createCore(callback) {
3838
callback(new Error('Function not supported on Windows.'));
3939
return;
4040
}
41-
const work_dir = fs.mkdtempSync('core_');
41+
42+
// Create a directory for the child process to crash in.
43+
// Use the timestamp to create a (hopefully) unique namespace
44+
// that allows the core to be related back to which process
45+
// crashed and when.
46+
const timestamp = generateTimestamp();
47+
48+
// Run synchronously until fork() returns.
49+
const work_dir = `core_${timestamp}`;
50+
fs.mkdirSync(work_dir);
51+
4252
let result = null;
4353
try {
4454
result = gencore.forkCore(work_dir);
@@ -91,26 +101,15 @@ function collectCore(callback) {
91101
// process changes between requesting a core and the core
92102
// being created.
93103

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

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

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

@@ -223,11 +221,11 @@ function copyFile(source, dest, closeCb) {
223221
}
224222

225223
function tarGzDir(work_dir, result, callback) {
226-
let tar_file = `core_${result.timestamp}.tar.gz`;
224+
let tar_file = `${work_dir}.tar.gz`;
227225

228226
// Use ls to obtain a list of files in work dir so the
229227
// resulting paths don't start with "./"
230-
exec(`tar -C ${work_dir} -czf ${tar_file} \`ls ${work_dir}\``,
228+
exec(`tar -czf ${tar_file} ${work_dir}`,
231229
(error, stdout, stderr) => {
232230
exec(`rm -r ${work_dir}`);
233231
callback(error, tar_file);
@@ -257,3 +255,26 @@ function findCore(work_dir, pid) {
257255
}
258256
return undefined;
259257
}
258+
259+
function generateTimestamp() {
260+
261+
const now = new Date();
262+
function pad(n, len) {
263+
if( len === undefined ) {
264+
len = 2;
265+
}
266+
let str = `${n}`;
267+
while(str.length < len) {
268+
str = '0' + str;
269+
}
270+
return str;
271+
}
272+
273+
// Create a time stamp that include the process id and a sequence number
274+
// to make the core identifiable and unique.
275+
const timestamp = `${pad(now.getFullYear())}${pad(now.getMonth()+1)}` +
276+
`${pad(now.getDate())}.${pad(now.getHours())}${pad(now.getMinutes())}` +
277+
`${pad(now.getSeconds())}.${process.pid}.${pad(++seq,3)}`;
278+
279+
return timestamp;
280+
}

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
{
22
"name": "gencore",
3-
"version": "0.0.3",
3+
"version": "0.0.4",
44
"description": "Create a core dump from the currently running process without terminating or attaching a debugger.",
55
"main": "index.js",
66
"dependencies": {
77
"nan": "^2.3.5",
88
"fstream": ""
99
},
10+
"devDependencies": {
11+
"tar": "<3.0.0",
12+
"tap": ""
13+
},
1014
"scripts": {
1115
"test": "tap --timeout=300 test/test*.js"
1216
},

test/test_collect_core.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ const fs = require('fs');
33
const tap = require('tap');
44
const zlib = require('zlib');
55
const tar = require('tar');
6+
const path = require('path');
67

7-
var core_count = 0;
8+
let core_count = 0;
89
let callback_count = 0;
910

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

3435
function checkEntry(entry) {
35-
var name = entry.path;
36-
var size = entry.size;
37-
var type = entry.type;
36+
// Trim off the top level directory containing our timestamp.
37+
let name = entry.path.split(path.sep).slice(1).join(path.sep);
38+
let size = entry.size;
39+
let type = entry.type;
3840

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

0 commit comments

Comments
 (0)