node-oom-heapdump 2.1.0-progress.1 → 3.0.1-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +2 -2
- package/.travis.yml +13 -9
- package/CHANGELOG.md +107 -84
- package/LICENSE +21 -21
- package/README.md +129 -143
- package/build/binding.sln +19 -0
- package/build/node_oom_heapdump_native.vcxproj +145 -0
- package/build/node_oom_heapdump_native.vcxproj.filters +14 -0
- package/index.js +112 -133
- package/lib/cpuProfileWorker.js +48 -48
- package/lib/heapdumpWorker.js +48 -48
- package/lib/index.js +186 -227
- package/package.json +40 -51
- package/.github/workflows/publish-native-assets-to-github-releases.yml +0 -37
- package/binding.gyp +0 -10
- package/build/Makefile +0 -324
- package/build/binding.Makefile +0 -6
- package/build/node_oom_heapdump_native.target.mk +0 -157
- package/lib/node_oom_heapdump_native.cc +0 -110
- package/tests/index.js +0 -27
- package/tests/long_running_process.js +0 -27
- package/tests/long_running_process_cpu.js +0 -35
- package/tests/oom_app.js +0 -22
package/lib/index.js
CHANGED
@@ -1,228 +1,187 @@
|
|
1
|
-
let cp = require("child_process");
|
2
|
-
let fs = require("fs");
|
3
|
-
let path = require("path");
|
4
|
-
|
5
|
-
class NodeOomHeapDumpImpl {
|
6
|
-
constructor(options) {
|
7
|
-
this._opts = options;
|
8
|
-
this._files = [];
|
9
|
-
this._busy = false;
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
});
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
*
|
104
|
-
*/
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
*
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
*
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
}
|
162
|
-
|
163
|
-
/**
|
164
|
-
*
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
*/
|
188
|
-
_deleteFile(path) {
|
189
|
-
return new Promise((resolve, reject) => {
|
190
|
-
if (this._files.includes(path)) {
|
191
|
-
fs.unlink(path, (err) => {
|
192
|
-
if (err) {
|
193
|
-
reject(err);
|
194
|
-
} else {
|
195
|
-
resolve(path);
|
196
|
-
}
|
197
|
-
});
|
198
|
-
} else {
|
199
|
-
reject(new Error("File not found:" + path));
|
200
|
-
}
|
201
|
-
});
|
202
|
-
}
|
203
|
-
|
204
|
-
/**
|
205
|
-
* Returns a proper snapshot path, based on the given parameter
|
206
|
-
* @param {String} snapshotPath
|
207
|
-
* @param {String} logPrefix
|
208
|
-
* @return {String} path
|
209
|
-
*/
|
210
|
-
_getHeapSnapshotPath(snapshotPath, logPrefix) {
|
211
|
-
if (!snapshotPath) {
|
212
|
-
snapshotPath = path.resolve(__dirname, "../heapsnapshot-" + Date.now());
|
213
|
-
}
|
214
|
-
if (logPrefix === "OoM" && this._opts.addTimestamp) {
|
215
|
-
if (snapshotPath.endsWith(".heapsnapshot")) {
|
216
|
-
snapshotPath = snapshotPath.replace(".heapsnapshot", "");
|
217
|
-
}
|
218
|
-
// in case of OoM error, add timestamp if needed
|
219
|
-
snapshotPath += "-" + Date.now();
|
220
|
-
}
|
221
|
-
if (!snapshotPath.endsWith(".heapsnapshot")) {
|
222
|
-
snapshotPath += ".heapsnapshot";
|
223
|
-
}
|
224
|
-
return snapshotPath;
|
225
|
-
}
|
226
|
-
}
|
227
|
-
|
1
|
+
let cp = require("child_process");
|
2
|
+
let fs = require("fs");
|
3
|
+
let path = require("path");
|
4
|
+
|
5
|
+
class NodeOomHeapDumpImpl {
|
6
|
+
constructor(options) {
|
7
|
+
this._opts = options;
|
8
|
+
this._files = [];
|
9
|
+
this._busy = false;
|
10
|
+
}
|
11
|
+
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Calls the designated worker and returns a promise
|
15
|
+
* @param {String} workerPath - path of the worker
|
16
|
+
* @param {String[]} workerArgs - arguments to worker
|
17
|
+
* @return {Promise} resolve on success, reject on error
|
18
|
+
*/
|
19
|
+
_callWorker(workerPath, workerArgs) {
|
20
|
+
if (this._busy) {
|
21
|
+
return new Promise((resolve, reject) => {
|
22
|
+
reject(new Error("A CPU profile or heapdump is already being created, please retry later."));
|
23
|
+
});
|
24
|
+
}
|
25
|
+
|
26
|
+
var args = [path.resolve(__dirname, workerPath)].concat(workerArgs);
|
27
|
+
|
28
|
+
// use 'require-main-filename' module instead of require.main.filename, see https://github.com/blueconic/node-oom-heapdump/issues/3
|
29
|
+
let mainFilename = require('require-main-filename')();
|
30
|
+
|
31
|
+
// start worker
|
32
|
+
let child = cp.spawn('node', args, {
|
33
|
+
cmd: path.dirname(mainFilename),
|
34
|
+
stdio: 'inherit'
|
35
|
+
});
|
36
|
+
|
37
|
+
return new Promise((resolve, reject) => {
|
38
|
+
this._busy = true;
|
39
|
+
var error = null;
|
40
|
+
|
41
|
+
child.on('error', (err) => {
|
42
|
+
error = err;
|
43
|
+
reject(err);
|
44
|
+
});
|
45
|
+
child.on('exit', (code) => {
|
46
|
+
if (!error) {
|
47
|
+
if (code === 0) {
|
48
|
+
resolve();
|
49
|
+
} else {
|
50
|
+
reject(new Error("Worker exited with statusCode: " + code));
|
51
|
+
}
|
52
|
+
}
|
53
|
+
this._busy = false;
|
54
|
+
});
|
55
|
+
});
|
56
|
+
}
|
57
|
+
|
58
|
+
/**
|
59
|
+
* Returns the path to the created heap snapshot in a promise, or rejects on error
|
60
|
+
* @param {String} snapshotPath - path of the snapshot
|
61
|
+
* @param {String} logPrefix - optional log prefix message when heapdump is created
|
62
|
+
* @return {Promise} the heap snapshot path on success or error on rejection
|
63
|
+
*/
|
64
|
+
createHeapSnapshot(snapshotPath, logPrefix) {
|
65
|
+
snapshotPath = this._getHeapSnapshotPath(snapshotPath, logPrefix);
|
66
|
+
|
67
|
+
// start OoMworker to create heapdump
|
68
|
+
return this._callWorker('./heapdumpWorker.js', [this._opts.port, snapshotPath, logPrefix || ""]).then(() => {
|
69
|
+
if (logPrefix === "OoM") {
|
70
|
+
this._count++;
|
71
|
+
}
|
72
|
+
if (!this._files.includes(snapshotPath)) {
|
73
|
+
this._files.push(snapshotPath);
|
74
|
+
}
|
75
|
+
return snapshotPath;
|
76
|
+
});
|
77
|
+
}
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Returns the path to the created CPU profile in a promise, or rejects on error
|
81
|
+
* @param {String} cpuProfilePath - path of the CPU profile
|
82
|
+
* @param {number} duration - the duration of the cpu profile (in ms)
|
83
|
+
* @return {Promise} the CPU profile path on success or error on rejection
|
84
|
+
*/
|
85
|
+
createCpuProfile(cpuProfilePath, duration) {
|
86
|
+
if (!cpuProfilePath) {
|
87
|
+
cpuProfilePath = path.resolve(__dirname, "../cpuprofile-" + Date.now());
|
88
|
+
}
|
89
|
+
if (!cpuProfilePath.endsWith(".cpuprofile")) {
|
90
|
+
cpuProfilePath += ".cpuprofile";
|
91
|
+
}
|
92
|
+
|
93
|
+
// start OoMworker to create heapdump
|
94
|
+
return this._callWorker('./cpuProfileWorker.js', [this._opts.port, cpuProfilePath, duration]).then(() => {
|
95
|
+
if (!this._files.includes(cpuProfilePath)) {
|
96
|
+
this._files.push(cpuProfilePath);
|
97
|
+
}
|
98
|
+
return cpuProfilePath;
|
99
|
+
});
|
100
|
+
}
|
101
|
+
|
102
|
+
/**
|
103
|
+
* Delete all created heap snapshots
|
104
|
+
*/
|
105
|
+
deleteAllHeapSnapshots() {
|
106
|
+
this._files.forEach((snapshotPath) => {
|
107
|
+
if (snapshotPath.endsWith(".heapsnapshot")) {
|
108
|
+
this.deleteHeapSnapshot(snapshotPath);
|
109
|
+
}
|
110
|
+
});
|
111
|
+
}
|
112
|
+
|
113
|
+
/**
|
114
|
+
* Deletes a particular snapshot from disk
|
115
|
+
* @param {String} snapshotPath - path of the heap snapshot to delete
|
116
|
+
* @return {Promise}
|
117
|
+
*/
|
118
|
+
deleteHeapSnapshot(snapshotPath) {
|
119
|
+
return this._deleteFile(snapshotPath);
|
120
|
+
}
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Delete all created CPU profiles
|
124
|
+
*/
|
125
|
+
deleteAllCpuProfiles() {
|
126
|
+
this._files.forEach((path) => {
|
127
|
+
if (path.endsWith(".cpuprofile")) {
|
128
|
+
this.deleteCpuProfile(path);
|
129
|
+
}
|
130
|
+
});
|
131
|
+
}
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Deletes a particular CPU profile from disk
|
135
|
+
* @param {String} cpuProfilePath - path of the CPU profile to delete
|
136
|
+
* @return {Promise}
|
137
|
+
*/
|
138
|
+
deleteCpuProfile(cpuProfilePath) {
|
139
|
+
return this._deleteFile(cpuProfilePath);
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Deletes a given CPU profile or heapsnapshot from disk
|
144
|
+
* @param {String} path - path to the file to delete
|
145
|
+
* @return {Promise}
|
146
|
+
*/
|
147
|
+
_deleteFile(path) {
|
148
|
+
return new Promise((resolve, reject) => {
|
149
|
+
if (this._files.includes(path)) {
|
150
|
+
fs.unlink(path, (err) => {
|
151
|
+
if (err) {
|
152
|
+
reject(err);
|
153
|
+
} else {
|
154
|
+
resolve(path);
|
155
|
+
}
|
156
|
+
});
|
157
|
+
} else {
|
158
|
+
reject(new Error("File not found:" + path));
|
159
|
+
}
|
160
|
+
});
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Returns a proper snapshot path, based on the given parameter
|
165
|
+
* @param {String} snapshotPath
|
166
|
+
* @param {String} logPrefix
|
167
|
+
* @return {String} path
|
168
|
+
*/
|
169
|
+
_getHeapSnapshotPath(snapshotPath, logPrefix) {
|
170
|
+
if (!snapshotPath) {
|
171
|
+
snapshotPath = path.resolve(__dirname, "../heapsnapshot-" + Date.now());
|
172
|
+
}
|
173
|
+
if (logPrefix === "OoM" && this._opts.addTimestamp) {
|
174
|
+
if (snapshotPath.endsWith(".heapsnapshot")) {
|
175
|
+
snapshotPath = snapshotPath.replace(".heapsnapshot", "");
|
176
|
+
}
|
177
|
+
// in case of OoM error, add timestamp if needed
|
178
|
+
snapshotPath += "-" + Date.now();
|
179
|
+
}
|
180
|
+
if (!snapshotPath.endsWith(".heapsnapshot")) {
|
181
|
+
snapshotPath += ".heapsnapshot";
|
182
|
+
}
|
183
|
+
return snapshotPath;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
228
187
|
module.exports = NodeOomHeapDumpImpl;
|
package/package.json
CHANGED
@@ -1,51 +1,40 @@
|
|
1
|
-
{
|
2
|
-
"name": "node-oom-heapdump",
|
3
|
-
"version": "
|
4
|
-
"description": "Create a
|
5
|
-
"main": "index.js",
|
6
|
-
"scripts": {
|
7
|
-
"
|
8
|
-
"
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
"
|
19
|
-
"
|
20
|
-
|
21
|
-
|
22
|
-
"
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
"
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
"
|
36
|
-
"
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
},
|
42
|
-
"dependencies": {
|
43
|
-
"bindings": "^1.5.0",
|
44
|
-
"chrome-remote-interface": "^0.28.0",
|
45
|
-
"gc-stats": "^1.4.0",
|
46
|
-
"nan": "^2.14.0",
|
47
|
-
"node-pre-gyp": "^0.15.0",
|
48
|
-
"require-main-filename": "^2.0.0",
|
49
|
-
"ws": "^7.1.2"
|
50
|
-
}
|
51
|
-
}
|
1
|
+
{
|
2
|
+
"name": "node-oom-heapdump",
|
3
|
+
"version": "3.0.1-beta",
|
4
|
+
"description": "Create a heap snapshot or CPU profile on request, off-process through the DevTools protocol",
|
5
|
+
"main": "index.js",
|
6
|
+
"scripts": {
|
7
|
+
"dummy": "node -e \"process.exit(0)\"",
|
8
|
+
"api-publish-beta": "npm publish --tag beta"
|
9
|
+
},
|
10
|
+
"repository": {
|
11
|
+
"type": "git",
|
12
|
+
"url": "git+https://github.com/blueconic/node-oom-heapdump.git"
|
13
|
+
},
|
14
|
+
"engines": {
|
15
|
+
"node": ">=14.0.0"
|
16
|
+
},
|
17
|
+
"keywords": [
|
18
|
+
"nodejs",
|
19
|
+
"memory-leak",
|
20
|
+
"out-of-memory",
|
21
|
+
"heapdump",
|
22
|
+
"memory",
|
23
|
+
"OnFatalError",
|
24
|
+
"OnOOMError"
|
25
|
+
],
|
26
|
+
"author": "Paul Rütter",
|
27
|
+
"license": "MIT",
|
28
|
+
"bugs": {
|
29
|
+
"url": "https://github.com/blueconic/node-oom-heapdump/issues"
|
30
|
+
},
|
31
|
+
"homepage": "https://github.com/blueconic/node-oom-heapdump#readme",
|
32
|
+
"devDependencies": {
|
33
|
+
"eslint-config-google": "^0.14.0"
|
34
|
+
},
|
35
|
+
"dependencies": {
|
36
|
+
"chrome-remote-interface": "^0.31.2",
|
37
|
+
"require-main-filename": "^2.0.0",
|
38
|
+
"ws": "^8.5.0"
|
39
|
+
}
|
40
|
+
}
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# This workflow will prebuild native binaries for supported NodeJS versions, and add them to the Github release that triggered the workflow
|
2
|
-
|
3
|
-
name: Add native binaries to release
|
4
|
-
|
5
|
-
on:
|
6
|
-
release:
|
7
|
-
types: [created]
|
8
|
-
|
9
|
-
jobs:
|
10
|
-
augment-release:
|
11
|
-
runs-on: ${{ matrix.os }}
|
12
|
-
strategy:
|
13
|
-
matrix:
|
14
|
-
node-version: [7.x, 8.x, 9.x, 10.x, 11.x, 12.x]
|
15
|
-
os: [ubuntu-latest, macos-latest, windows-latest]
|
16
|
-
steps:
|
17
|
-
- uses: actions/checkout@v2
|
18
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
19
|
-
uses: actions/setup-node@v1
|
20
|
-
with:
|
21
|
-
node-version: ${{ matrix.node-version }}
|
22
|
-
- name: patch node gyp on windows to support Visual Studio 2019
|
23
|
-
if: matrix.os == 'windows-latest'
|
24
|
-
shell: powershell
|
25
|
-
run: |
|
26
|
-
npm install --global node-gyp@5.1.1
|
27
|
-
npm prefix -g | % {npm config set node_gyp "$_\node_modules\node-gyp\bin\node-gyp.js"}
|
28
|
-
- name: build using node-pre-gyp
|
29
|
-
run: |
|
30
|
-
npm install --build-from-source
|
31
|
-
./node_modules/.bin/node-pre-gyp package
|
32
|
-
- name: Upload native binaries for Node ${{ matrix.node-version }} for ${{ matrix.os }}
|
33
|
-
uses: csexton/release-asset-action@v2
|
34
|
-
with:
|
35
|
-
pattern: "build/stage/*.tar.gz"
|
36
|
-
github-token: ${{ secrets.GITHUB_TOKEN }}
|
37
|
-
release-url: ${{ github.event.release.upload_url }}
|