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/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
- this._count = 0;
11
- this._limitReached = false;
12
-
13
- if (this._opts.heapdumpOnOOM) {
14
- if (options.OOMImplementation === "NATIVE_HOOK") {
15
- require('bindings')('node_oom_heapdump_native.node').call(this._getHeapSnapshotPath(this._opts.path), this._opts.addTimestamp);
16
- } else if (options.OOMImplementation === "GC_MONITORING") {
17
- this._monitorHeap();
18
- }
19
- }
20
- }
21
-
22
- _monitorHeap() {
23
- // see https://www.npmjs.com/package/gc-stats
24
- let gcStats = new require('gc-stats')();
25
- gcStats.on('stats', (stats) => {
26
- // gctype 2 is a Full GC (Mark/Sweep/Compact)
27
- if (stats.gctype === 2 && !this._busy) {
28
- let memoryUsagePercentage = Math.round((stats.after.usedHeapSize / stats.after.heapSizeLimit) * 100);
29
- if (memoryUsagePercentage > this._opts.threshold) {
30
- if (this._count < this._opts.limit) {
31
- // this is a full GC and the used heap size is using more than x% of the assigned heap space limit
32
- console.warn('OoM is imminent: Full GC (Mark/Sweep/Compact) complete and still more than %s% (%s%) of the heap is used. Creating heapdump now. GC stats: ', this._opts.threshold, Math.round(memoryUsagePercentage), JSON.stringify(stats));
33
-
34
- this.createHeapSnapshot(this._opts.path, "OoM");
35
-
36
- // block execution of other code for a while (5 second) to enable snapshot to be created
37
- const time = Date.now();
38
- let diff = 0;
39
- do {
40
- diff = Date.now() - time;
41
- }
42
- while (diff < 5000);
43
- } else if (!this._limitReached) {
44
- this._limitReached = true;
45
- console.warn("OoM heapdump limit reached (%s); no more heapdumps will be created.", this._opts.limit);
46
- return;
47
- }
48
- }
49
- }
50
- });
51
- }
52
-
53
-
54
- /**
55
- * Calls the designated worker and returns a promise
56
- * @param {String} workerPath - path of the worker
57
- * @param {String[]} workerArgs - arguments to worker
58
- * @return {Promise} resolve on success, reject on error
59
- */
60
- _callWorker(workerPath, workerArgs) {
61
- if (this._busy) {
62
- return new Promise((resolve, reject) => {
63
- reject(new Error("A CPU profile or heapdump is already being created, please retry later."));
64
- });
65
- }
66
-
67
- var args = [path.resolve(__dirname, workerPath)].concat(workerArgs);
68
-
69
- // use 'require-main-filename' module instead of require.main.filename, see https://github.com/blueconic/node-oom-heapdump/issues/3
70
- let mainFilename = require('require-main-filename')();
71
-
72
- // start worker
73
- let child = cp.spawn('node', args, {
74
- cmd: path.dirname(mainFilename),
75
- stdio: 'inherit'
76
- });
77
-
78
- return new Promise((resolve, reject) => {
79
- this._busy = true;
80
- var error = null;
81
-
82
- child.on('error', (err) => {
83
- error = err;
84
- reject(err);
85
- });
86
- child.on('exit', (code) => {
87
- if (!error) {
88
- if (code === 0) {
89
- resolve();
90
- } else {
91
- reject(new Error("Worker exited with statusCode: " + code));
92
- }
93
- }
94
- this._busy = false;
95
- });
96
- });
97
- }
98
-
99
- /**
100
- * Returns the path to the created heap snapshot in a promise, or rejects on error
101
- * @param {String} snapshotPath - path of the snapshot
102
- * @param {String} logPrefix - optional log prefix message when heapdump is created
103
- * @return {Promise} the heap snapshot path on success or error on rejection
104
- */
105
- createHeapSnapshot(snapshotPath, logPrefix) {
106
- snapshotPath = this._getHeapSnapshotPath(snapshotPath, logPrefix);
107
-
108
- // start OoMworker to create heapdump
109
- return this._callWorker('./heapdumpWorker.js', [this._opts.port, snapshotPath, logPrefix || ""]).then(() => {
110
- if (logPrefix === "OoM") {
111
- this._count++;
112
- }
113
- if (!this._files.includes(snapshotPath)) {
114
- this._files.push(snapshotPath);
115
- }
116
- return snapshotPath;
117
- });
118
- }
119
-
120
- /**
121
- * Returns the path to the created CPU profile in a promise, or rejects on error
122
- * @param {String} cpuProfilePath - path of the CPU profile
123
- * @param {number} duration - the duration of the cpu profile (in ms)
124
- * @return {Promise} the CPU profile path on success or error on rejection
125
- */
126
- createCpuProfile(cpuProfilePath, duration) {
127
- if (!cpuProfilePath) {
128
- cpuProfilePath = path.resolve(__dirname, "../cpuprofile-" + Date.now());
129
- }
130
- if (!cpuProfilePath.endsWith(".cpuprofile")) {
131
- cpuProfilePath += ".cpuprofile";
132
- }
133
-
134
- // start OoMworker to create heapdump
135
- return this._callWorker('./cpuProfileWorker.js', [this._opts.port, cpuProfilePath, duration]).then(() => {
136
- if (!this._files.includes(cpuProfilePath)) {
137
- this._files.push(cpuProfilePath);
138
- }
139
- return cpuProfilePath;
140
- });
141
- }
142
-
143
- /**
144
- * Delete all created heap snapshots
145
- */
146
- deleteAllHeapSnapshots() {
147
- this._files.forEach((snapshotPath) => {
148
- if (snapshotPath.endsWith(".heapsnapshot")) {
149
- this.deleteHeapSnapshot(snapshotPath);
150
- }
151
- });
152
- }
153
-
154
- /**
155
- * Deletes a particular snapshot from disk
156
- * @param {String} snapshotPath - path of the heap snapshot to delete
157
- * @return {Promise}
158
- */
159
- deleteHeapSnapshot(snapshotPath) {
160
- return this._deleteFile(snapshotPath);
161
- }
162
-
163
- /**
164
- * Delete all created CPU profiles
165
- */
166
- deleteAllCpuProfiles() {
167
- this._files.forEach((path) => {
168
- if (path.endsWith(".cpuprofile")) {
169
- this.deleteCpuProfile(path);
170
- }
171
- });
172
- }
173
-
174
- /**
175
- * Deletes a particular CPU profile from disk
176
- * @param {String} cpuProfilePath - path of the CPU profile to delete
177
- * @return {Promise}
178
- */
179
- deleteCpuProfile(cpuProfilePath) {
180
- return this._deleteFile(cpuProfilePath);
181
- }
182
-
183
- /**
184
- * Deletes a given CPU profile or heapsnapshot from disk
185
- * @param {String} path - path to the file to delete
186
- * @return {Promise}
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": "2.1.0-progress.1",
4
- "description": "Create a V8 heap snapshot when an \"Out of Memory\" error occurs, or create a heap snapshot or CPU profile on request.",
5
- "main": "index.js",
6
- "scripts": {
7
- "rebuild": "node-pre-gyp install --build-from-source",
8
- "install": "node-pre-gyp install --fallback-to-build",
9
- "test": "node --max_old_space_size=80 --inspect ./tests/oom_app.js",
10
- "dummy": "node -e \"process.exit(0)\""
11
- },
12
- "repository": {
13
- "type": "git",
14
- "url": "git+https://github.com/blueconic/node-oom-heapdump.git"
15
- },
16
- "binary": {
17
- "module_name": "node_oom_heapdump_native",
18
- "module_path": "./build/Release",
19
- "host": "https://github.com/blueconic/node-oom-heapdump/releases/download/{version}"
20
- },
21
- "engines": {
22
- "node": ">=7.0.0"
23
- },
24
- "keywords": [
25
- "nodejs",
26
- "memory-leak",
27
- "out-of-memory",
28
- "heapdump",
29
- "memory",
30
- "OnFatalError",
31
- "OnOOMError"
32
- ],
33
- "author": "Paul Rütter",
34
- "license": "MIT",
35
- "bugs": {
36
- "url": "https://github.com/blueconic/node-oom-heapdump/issues"
37
- },
38
- "homepage": "https://github.com/blueconic/node-oom-heapdump#readme",
39
- "devDependencies": {
40
- "eslint-config-google": "^0.13.0"
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 }}
package/binding.gyp DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "targets" : [{
3
- "target_name" : "node_oom_heapdump_native",
4
- "sources" : [ "lib/node_oom_heapdump_native.cc" ],
5
- "include_dirs": [
6
- '<!(node -e "require(\'nan\')")'
7
- ],
8
- "win_delay_load_hook" : "false"
9
- }]
10
- }