node-oom-heapdump 3.0.1-beta → 3.0.2
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/.github/workflows/publish-native-assets-to-github-releases.yml +41 -0
- package/.travis.yml +1 -0
- package/CHANGELOG.md +9 -9
- package/README.md +29 -15
- package/binding.gyp +10 -0
- package/build/binding.sln +3 -3
- package/build/node_oom_heapdump_native.vcxproj +8 -8
- package/index.js +10 -0
- package/lib/index.js +7 -0
- package/lib/node_oom_heapdump_native.cc +119 -0
- package/package.json +17 -7
- package/tests/index.js +27 -0
- package/tests/long_running_process.js +27 -0
- package/tests/long_running_process_cpu.js +35 -0
- package/tests/oom_app.js +22 -0
@@ -0,0 +1,41 @@
|
|
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: [10.x, 11.x, 12.x, 14.x, 15.x, 16.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: Add msbuild to PATH
|
23
|
+
if: matrix.os == 'windows-latest'
|
24
|
+
uses: microsoft/setup-msbuild@v1.1
|
25
|
+
- name: patch node gyp on windows to support Visual Studio 2019
|
26
|
+
if: matrix.os == 'windows-latest'
|
27
|
+
shell: powershell
|
28
|
+
run: |
|
29
|
+
npm install --global npm@latest
|
30
|
+
npm install --global node-gyp@latest
|
31
|
+
npm prefix -g | % {npm config set node_gyp "$_\node_modules\node-gyp\bin\node-gyp.js"}
|
32
|
+
- name: build using node-pre-gyp
|
33
|
+
run: |
|
34
|
+
npm install --build-from-source
|
35
|
+
./node_modules/.bin/node-pre-gyp package
|
36
|
+
- name: Upload native binaries for Node ${{ matrix.node-version }} for ${{ matrix.os }}
|
37
|
+
uses: csexton/release-asset-action@v2
|
38
|
+
with:
|
39
|
+
pattern: "build/stage/*.tar.gz"
|
40
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
41
|
+
release-url: ${{ github.event.release.upload_url }}
|
package/.travis.yml
CHANGED
package/CHANGELOG.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
|
1
|
+
10-02-2022 Paul Rütter
|
2
|
+
- 3.0.1
|
3
|
+
- Fixed building native artifacts on Windows, thanks spmiller! https://github.com/blueconic/node-oom-heapdump/issues/22
|
4
|
+
|
5
|
+
10-02-2022 Paul Rütter
|
2
6
|
- 3.0.0
|
3
|
-
-
|
4
|
-
|
5
|
-
- Removed
|
6
|
-
-
|
7
|
-
|
8
|
-
21-12-2020 Paul Rütter
|
9
|
-
- 2.2.0
|
10
|
-
- Updated ini
|
7
|
+
- Added Node 16 support (by merging https://github.com/blueconic/node-oom-heapdump/pull/20, Thanks Simon Abbott!).
|
8
|
+
This fixes a recursion issue.
|
9
|
+
- Removed "GC_MONITORING" at it relied on `gc-stats`, which is no longer maintained (and contained security issues)
|
10
|
+
- Updated `node-pre-gyp` to `@mapbox/node-pre-gyp` so security issues are mitigated
|
11
11
|
|
12
12
|
12-10-2020 Paul Rütter
|
13
13
|
- 2.1.0
|
package/README.md
CHANGED
@@ -1,30 +1,40 @@
|
|
1
|
-
[](https://github.com/blueconic/node-oom-heapdump/actions/workflows/publish-native-assets-to-github-releases.yml)
|
2
2
|
|
3
3
|
# node-oom-heapdump
|
4
|
-
Node module
|
4
|
+
Node module which will create a V8 heap snapshot right before an "Out of Memory" error occurs.
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
It can also create heapdumps and CPU profiles on request like 'v8-profiler', but does this off-process so it doesn't interfere with execution of the main process.
|
7
|
+
|
8
|
+
Tested on Node.js 10.x, 11.x, 12.x, 13.x, 14.x, 15.x and 16.x.
|
9
|
+
No support for Node.js < 10.x at the moment in version 3.0.0, use version 2.2.0 for if needed.
|
8
10
|
|
9
11
|
Also comes with prebuilt binaries (hosted on Github releases), thanks to Stuart Miller (https://github.com/spmiller).
|
10
12
|
|
11
|
-
## Node.js 14.18.x
|
13
|
+
## Node.js 14.18.x
|
12
14
|
https://github.com/nodejs/node/pull/33010 landed in Node.js 14.18.0, which makes this module no longer needed for heapdumps on out of memory.
|
13
|
-
One can use the `--heapsnapshot-near-heap-limit` Node.js CLI option as an
|
15
|
+
One can use the `--heapsnapshot-near-heap-limit` Node.js CLI option as an alternative.
|
14
16
|
See https://nodejs.org/dist/latest-v14.x/docs/api/cli.html#cli_heapsnapshot_near_heap_limit_max_count.
|
15
17
|
|
16
|
-
For Node versions older than 14, use the `2.2.0` release, where this functionality is still present (not maintained anymore).
|
17
|
-
For Node versions 14 and up, use `--heapsnapshot-near-heap-limit` for out-of-memory heapdumps. This functionality has been removed from `3.0.0`.
|
18
|
-
|
19
|
-
One can still use the API of this module to create CPU profiles and heapdumps on request
|
20
|
-
|
21
18
|
# Why?
|
22
|
-
|
19
|
+
When running nodejs processes in a low memory environment, every out of memory that occurs is interesting.
|
20
|
+
To figure out why a process went out of memory, a heap snapshot (e.g. heapdump) can help a lot.
|
21
|
+
This module creates a heap snapshot right before an out of memory error occurs (by leveraging 'SetOOMErrorHandler' of the V8 engine).
|
22
|
+
It shows what the heap was filled with right before the out of memory error occured and can be opened with Chrome DevTools (Memory tab).
|
23
|
+
|
24
|
+
There are several modules around which can create heapdumps (v8-profiler, node-heapdump), but these run in the same process as the one going out of memory. Often, creating heapdump won't work when the node process is already struggling.
|
25
|
+
This module creates the heap snapshot from a separate process, which solves this issue.
|
26
|
+
Also, these modules are not able to create a heapdump when an out of memory occurs.
|
23
27
|
|
24
28
|
# What?
|
25
|
-
|
29
|
+
Based on the work of 'trevnorris' (https://github.com/trevnorris/node-ofe/), this module uses 'isolate.SetOOMErrorHandler' (https://v8docs.nodesource.com/node-8.9/d5/dda/classv8_1_1_isolate.html#a08fd4087f39c33b4ac1c20ad953ce4e3) of the V8 engine, and then creates a heapdump when an actual Out of Memory occurs. To make this happen, a native C++ add-on is used.
|
30
|
+
Node-gyp is needed to compile this add-on.
|
31
|
+
|
32
|
+
When creating a heapdump of CPU profile on request, the DevTools protocol is used to create these files (no native add-on).
|
26
33
|
The --inspect node.js flag is needed to make this work (which is validated on startup).
|
27
34
|
|
35
|
+
# Example
|
36
|
+
Just run "npm test" to see it in action. It creates a heapdump named "my_heapdump.heapsnapshot" in the 'tests' directory of this module.
|
37
|
+
|
28
38
|
# Usage
|
29
39
|
|
30
40
|
```javascript
|
@@ -52,11 +62,15 @@ These might impact performance though.
|
|
52
62
|
On Node.js 12.x the latter two flags seem to cause some stability issues (see https://github.com/nodejs/node/issues/27552#issuecomment-542695931). So, if you encounter issues on Node.js 12.x in combination with those flags, please refrain from using these.
|
53
63
|
|
54
64
|
# Options
|
55
|
-
*
|
65
|
+
* heapdumpOnOOM - boolean whether to create a heapdump when an out of memory occurs. Default true.
|
66
|
+
* OOMImplementation - Only "NATIVE_HOOK" is supported starting from 3.0.0
|
67
|
+
"NATIVE_HOOK" relies on the native v8 hook and makes sure that the heapdump is actually created when the OoM occurs. It's more impacted by the OoMKiller of Unix systems though, when being run in memory restricted environments like Docker.
|
68
|
+
* path - the path where the heapdump ends up when an out of memory error occurs. '.heapsnapshot' is automatically appended. Defaults to this modules' directory.
|
69
|
+
* addTimestamp - add a timestamp to the out of memory heapdump filename, to make it unique. Default is false.
|
56
70
|
* port - optionally, the alternative DevTools protocol port. Defaults to 9229. Should map on the port given to the --inspect arg.
|
57
71
|
|
58
72
|
# API
|
59
|
-
|
73
|
+
Besides creating heapdumps when an out of memory error occurs, there also is an API for creating heapdumps and CPU profiles on request. See below for the currently available API.
|
60
74
|
|
61
75
|
Notice that you cannot create a heapdump while a CPU profile is being generated and vice versa; an Error will be thrown if this is the case.
|
62
76
|
|
package/binding.gyp
ADDED
package/build/binding.sln
CHANGED
@@ -4,14 +4,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "node_oom_heapdump_native",
|
|
4
4
|
EndProject
|
5
5
|
Global
|
6
6
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
7
|
-
Debug|x64 = Debug|x64
|
8
7
|
Release|x64 = Release|x64
|
8
|
+
Debug|x64 = Debug|x64
|
9
9
|
EndGlobalSection
|
10
10
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
11
|
-
{B44BD8E7-8AB3-68F6-9D96-154D16CEC9C0}.Debug|x64.ActiveCfg = Debug|x64
|
12
|
-
{B44BD8E7-8AB3-68F6-9D96-154D16CEC9C0}.Debug|x64.Build.0 = Debug|x64
|
13
11
|
{B44BD8E7-8AB3-68F6-9D96-154D16CEC9C0}.Release|x64.ActiveCfg = Release|x64
|
14
12
|
{B44BD8E7-8AB3-68F6-9D96-154D16CEC9C0}.Release|x64.Build.0 = Release|x64
|
13
|
+
{B44BD8E7-8AB3-68F6-9D96-154D16CEC9C0}.Debug|x64.ActiveCfg = Debug|x64
|
14
|
+
{B44BD8E7-8AB3-68F6-9D96-154D16CEC9C0}.Debug|x64.Build.0 = Debug|x64
|
15
15
|
EndGlobalSection
|
16
16
|
GlobalSection(SolutionProperties) = preSolution
|
17
17
|
HideSolutionNode = FALSE
|
@@ -48,8 +48,8 @@
|
|
48
48
|
</PropertyGroup>
|
49
49
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
50
50
|
<ClCompile>
|
51
|
-
<AdditionalIncludeDirectories>C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\16.
|
52
|
-
<AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>
|
51
|
+
<AdditionalIncludeDirectories>C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\include\node;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\src;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\openssl\config;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\openssl\openssl\include;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\uv\include;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\zlib;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\v8\include;..\node_modules\nan;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
52
|
+
<AdditionalOptions>/Zc:__cplusplus -std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
53
53
|
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
54
54
|
<BufferSecurityCheck>true</BufferSecurityCheck>
|
55
55
|
<DebugInformationFormat>OldStyle</DebugInformationFormat>
|
@@ -72,7 +72,7 @@
|
|
72
72
|
<AdditionalOptions>/LTCG:INCREMENTAL %(AdditionalOptions)</AdditionalOptions>
|
73
73
|
</Lib>
|
74
74
|
<Link>
|
75
|
-
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;DelayImp.lib;"C:\\Users\\paul.BLUECONIC\\AppData\\Local\\node-gyp\\Cache\\16.
|
75
|
+
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;DelayImp.lib;"C:\\Users\\paul.BLUECONIC\\AppData\\Local\\node-gyp\\Cache\\18.16.1\\x64\\node.lib"</AdditionalDependencies>
|
76
76
|
<AdditionalOptions>/LTCG:INCREMENTAL %(AdditionalOptions)</AdditionalOptions>
|
77
77
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
78
78
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
@@ -83,14 +83,14 @@
|
|
83
83
|
<TargetMachine>MachineX64</TargetMachine>
|
84
84
|
</Link>
|
85
85
|
<ResourceCompile>
|
86
|
-
<AdditionalIncludeDirectories>C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\16.
|
86
|
+
<AdditionalIncludeDirectories>C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\include\node;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\src;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\openssl\config;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\openssl\openssl\include;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\uv\include;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\zlib;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\v8\include;..\node_modules\nan;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
87
87
|
<PreprocessorDefinitions>NODE_GYP_MODULE_NAME=node_oom_heapdump_native;USING_UV_SHARED=1;USING_V8_SHARED=1;V8_DEPRECATION_WARNINGS=1;V8_DEPRECATION_WARNINGS;V8_IMMINENT_DEPRECATION_WARNINGS;_GLIBCXX_USE_CXX11_ABI=1;WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_HAS_EXCEPTIONS=0;OPENSSL_NO_PINSHARED;OPENSSL_THREADS;BUILDING_NODE_EXTENSION;DEBUG;_DEBUG;V8_ENABLE_CHECKS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
88
88
|
</ResourceCompile>
|
89
89
|
</ItemDefinitionGroup>
|
90
90
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
91
91
|
<ClCompile>
|
92
|
-
<AdditionalIncludeDirectories>C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\16.
|
93
|
-
<AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>
|
92
|
+
<AdditionalIncludeDirectories>C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\include\node;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\src;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\openssl\config;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\openssl\openssl\include;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\uv\include;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\zlib;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\v8\include;..\node_modules\nan;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
93
|
+
<AdditionalOptions>/Zc:__cplusplus -std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
94
94
|
<BufferSecurityCheck>true</BufferSecurityCheck>
|
95
95
|
<DebugInformationFormat>OldStyle</DebugInformationFormat>
|
96
96
|
<DisableSpecificWarnings>4351;4355;4800;4251;4275;4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
@@ -116,7 +116,7 @@
|
|
116
116
|
<AdditionalOptions>/LTCG:INCREMENTAL %(AdditionalOptions)</AdditionalOptions>
|
117
117
|
</Lib>
|
118
118
|
<Link>
|
119
|
-
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;DelayImp.lib;"C:\\Users\\paul.BLUECONIC\\AppData\\Local\\node-gyp\\Cache\\16.
|
119
|
+
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;DelayImp.lib;"C:\\Users\\paul.BLUECONIC\\AppData\\Local\\node-gyp\\Cache\\18.16.1\\x64\\node.lib"</AdditionalDependencies>
|
120
120
|
<AdditionalOptions>/LTCG:INCREMENTAL %(AdditionalOptions)</AdditionalOptions>
|
121
121
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
122
122
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
@@ -127,7 +127,7 @@
|
|
127
127
|
<TargetMachine>MachineX64</TargetMachine>
|
128
128
|
</Link>
|
129
129
|
<ResourceCompile>
|
130
|
-
<AdditionalIncludeDirectories>C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\16.
|
130
|
+
<AdditionalIncludeDirectories>C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\include\node;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\src;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\openssl\config;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\openssl\openssl\include;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\uv\include;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\zlib;C:\Users\paul.BLUECONIC\AppData\Local\node-gyp\Cache\18.16.1\deps\v8\include;..\node_modules\nan;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
131
131
|
<PreprocessorDefinitions>NODE_GYP_MODULE_NAME=node_oom_heapdump_native;USING_UV_SHARED=1;USING_V8_SHARED=1;V8_DEPRECATION_WARNINGS=1;V8_DEPRECATION_WARNINGS;V8_IMMINENT_DEPRECATION_WARNINGS;_GLIBCXX_USE_CXX11_ABI=1;WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_HAS_EXCEPTIONS=0;OPENSSL_NO_PINSHARED;OPENSSL_THREADS;BUILDING_NODE_EXTENSION;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
132
132
|
</ResourceCompile>
|
133
133
|
</ItemDefinitionGroup>
|
package/index.js
CHANGED
@@ -81,11 +81,21 @@ class NodeOomHeapdumpAPI {
|
|
81
81
|
|
82
82
|
// utility functions
|
83
83
|
function parseOptions(options) {
|
84
|
+
if (options.heapdumpOnOOM === undefined) {
|
85
|
+
options.heapdumpOnOOM = true;
|
86
|
+
}
|
87
|
+
if (options.OOMImplementation === undefined) {
|
88
|
+
// default is the "new" implementation
|
89
|
+
options.OOMImplementation = "NATIVE_HOOK";
|
90
|
+
}
|
84
91
|
if (options.port === undefined) {
|
85
92
|
options.port = 9229;
|
86
93
|
} else {
|
87
94
|
options.port = parseInt(options.port);
|
88
95
|
}
|
96
|
+
if (options.path === undefined) {
|
97
|
+
options.path = "OoM-pid-" + process.pid;
|
98
|
+
}
|
89
99
|
if (options.addTimestamp === undefined) {
|
90
100
|
options.addTimestamp = false;
|
91
101
|
} else {
|
package/lib/index.js
CHANGED
@@ -7,6 +7,13 @@ class NodeOomHeapDumpImpl {
|
|
7
7
|
this._opts = options;
|
8
8
|
this._files = [];
|
9
9
|
this._busy = false;
|
10
|
+
this._count = 0;
|
11
|
+
|
12
|
+
if (this._opts.heapdumpOnOOM) {
|
13
|
+
if (options.OOMImplementation === "NATIVE_HOOK") {
|
14
|
+
require('bindings')('node_oom_heapdump_native.node').call(this._getHeapSnapshotPath(this._opts.path), this._opts.addTimestamp);
|
15
|
+
}
|
16
|
+
}
|
10
17
|
}
|
11
18
|
|
12
19
|
|
@@ -0,0 +1,119 @@
|
|
1
|
+
#include <nan.h>
|
2
|
+
#include <v8-profiler.h>
|
3
|
+
#include <stdlib.h>
|
4
|
+
#if defined(_WIN32)
|
5
|
+
#include <time.h>
|
6
|
+
#define snprintf _snprintf
|
7
|
+
#else
|
8
|
+
#include <sys/time.h>
|
9
|
+
#endif
|
10
|
+
|
11
|
+
using namespace v8;
|
12
|
+
|
13
|
+
char filename[256];
|
14
|
+
bool addTimestamp;
|
15
|
+
bool processingOOM = false;
|
16
|
+
|
17
|
+
class FileOutputStream: public OutputStream {
|
18
|
+
public:
|
19
|
+
FileOutputStream(FILE* stream): stream_(stream) { }
|
20
|
+
virtual int GetChunkSize() {
|
21
|
+
return 65536;
|
22
|
+
}
|
23
|
+
virtual void EndOfStream() { }
|
24
|
+
virtual WriteResult WriteAsciiChunk(char* data, int size) {
|
25
|
+
const size_t len = static_cast<size_t>(size);
|
26
|
+
size_t off = 0;
|
27
|
+
while (off < len && !feof(stream_) && !ferror(stream_))
|
28
|
+
off += fwrite(data + off, 1, len - off, stream_);
|
29
|
+
return off == len ? kContinue : kAbort;
|
30
|
+
}
|
31
|
+
|
32
|
+
private:
|
33
|
+
FILE* stream_;
|
34
|
+
};
|
35
|
+
|
36
|
+
size_t RaiseLimit(void* data, size_t current_heap_limit, size_t initial_heap_limit) {
|
37
|
+
return current_heap_limit + 10u * 1024 * 1024; // 10MiB
|
38
|
+
}
|
39
|
+
|
40
|
+
void OnOOMError(const char *location, bool is_heap_oom) {
|
41
|
+
if (processingOOM) {
|
42
|
+
fprintf(stderr, "FATAL: OnOOMError called more than once.\n");
|
43
|
+
exit(2);
|
44
|
+
}
|
45
|
+
processingOOM = true;
|
46
|
+
|
47
|
+
if (addTimestamp) {
|
48
|
+
// Add timestamp to filename
|
49
|
+
time_t rawtime;
|
50
|
+
struct tm* timeinfo;
|
51
|
+
time(&rawtime);
|
52
|
+
timeinfo = localtime(&rawtime);
|
53
|
+
|
54
|
+
char * pch;
|
55
|
+
pch = strstr (filename,".heapsnapshot");
|
56
|
+
strncpy (pch,"",1);
|
57
|
+
strcat (filename, "-%Y%m%dT%H%M%S.heapsnapshot");
|
58
|
+
|
59
|
+
char newFilename[256];
|
60
|
+
strftime(newFilename, sizeof(filename), filename, timeinfo);
|
61
|
+
strcpy(filename, newFilename);
|
62
|
+
}
|
63
|
+
|
64
|
+
fprintf(stderr, "Generating Heapdump to '%s' now...\n", filename);
|
65
|
+
FILE* fp = fopen(filename, "w");
|
66
|
+
if (fp == NULL) abort();
|
67
|
+
|
68
|
+
auto* isolate = v8::Isolate::GetCurrent();
|
69
|
+
|
70
|
+
// Capturing a heap snapshot forces a garbage collection which can, in turn,
|
71
|
+
// trigger the OOM flow which causes recursion. To prevent this, this callback
|
72
|
+
// will raise the heap limit if the GC tries to go down that path again.
|
73
|
+
// Normally we would want to add a call to RemoveNearHeapLimitCallback() after
|
74
|
+
// we are done, but that is not necessary since we exit() before it matters.
|
75
|
+
isolate->AddNearHeapLimitCallback(RaiseLimit, nullptr);
|
76
|
+
|
77
|
+
// Create heapdump, depending on which Node.js version this can differ
|
78
|
+
// for now, just support Node.js 7 and higher
|
79
|
+
const HeapSnapshot* snap = isolate->GetHeapProfiler()->TakeHeapSnapshot();
|
80
|
+
|
81
|
+
FileOutputStream stream(fp);
|
82
|
+
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
83
|
+
fclose(fp);
|
84
|
+
|
85
|
+
fprintf(stderr, "Done! Exiting process now.\n");
|
86
|
+
exit(1);
|
87
|
+
}
|
88
|
+
|
89
|
+
void ParseArgumentsAndSetErrorHandler(const FunctionCallbackInfo<Value>& args) {
|
90
|
+
Isolate* isolate = args.GetIsolate();
|
91
|
+
isolate->SetOOMErrorHandler(OnOOMError);
|
92
|
+
|
93
|
+
// parse JS arguments
|
94
|
+
// 1: filename
|
95
|
+
// 2: addTimestamp boolean
|
96
|
+
#if NODE_VERSION_AT_LEAST(13, 0, 0)
|
97
|
+
Local<Context> context = isolate->GetCurrentContext();
|
98
|
+
String::Utf8Value fArg(isolate, args[0]->ToString(context).ToLocalChecked());
|
99
|
+
#elif NODE_VERSION_AT_LEAST(12, 0, 0)
|
100
|
+
String::Utf8Value fArg(isolate, args[0]->ToString(isolate));
|
101
|
+
#elif NODE_VERSION_AT_LEAST(9, 0, 0)
|
102
|
+
String::Utf8Value fArg(isolate, args[0]->ToString());
|
103
|
+
#else
|
104
|
+
String::Utf8Value fArg(args[0]->ToString());
|
105
|
+
#endif
|
106
|
+
strncpy(filename, (const char*)(*fArg), sizeof(filename) - 1);
|
107
|
+
|
108
|
+
#if NODE_VERSION_AT_LEAST(12, 0, 0)
|
109
|
+
addTimestamp = args[1]->BooleanValue(isolate);
|
110
|
+
#else
|
111
|
+
addTimestamp = args[1]->BooleanValue();
|
112
|
+
#endif
|
113
|
+
}
|
114
|
+
|
115
|
+
void init(Local<Object> exports) {
|
116
|
+
NODE_SET_METHOD(exports, "call", ParseArgumentsAndSetErrorHandler);
|
117
|
+
}
|
118
|
+
|
119
|
+
NODE_MODULE(NODE_OOM_HEAPDUMP_NATIVE, init)
|
package/package.json
CHANGED
@@ -1,18 +1,25 @@
|
|
1
1
|
{
|
2
2
|
"name": "node-oom-heapdump",
|
3
|
-
"version": "3.0.
|
4
|
-
"description": "Create a heap snapshot
|
3
|
+
"version": "3.0.2",
|
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
5
|
"main": "index.js",
|
6
6
|
"scripts": {
|
7
|
-
"
|
8
|
-
"
|
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)\""
|
9
11
|
},
|
10
12
|
"repository": {
|
11
13
|
"type": "git",
|
12
14
|
"url": "git+https://github.com/blueconic/node-oom-heapdump.git"
|
13
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
|
+
},
|
14
21
|
"engines": {
|
15
|
-
"node": ">=
|
22
|
+
"node": ">=7.0.0"
|
16
23
|
},
|
17
24
|
"keywords": [
|
18
25
|
"nodejs",
|
@@ -33,8 +40,11 @@
|
|
33
40
|
"eslint-config-google": "^0.14.0"
|
34
41
|
},
|
35
42
|
"dependencies": {
|
36
|
-
"
|
43
|
+
"@mapbox/node-pre-gyp": "^1.0.11",
|
44
|
+
"bindings": "^1.5.0",
|
45
|
+
"chrome-remote-interface": "^0.32.2",
|
46
|
+
"nan": "^2.17.0",
|
37
47
|
"require-main-filename": "^2.0.0",
|
38
|
-
"ws": "^8.
|
48
|
+
"ws": "^8.13.0"
|
39
49
|
}
|
40
50
|
}
|
package/tests/index.js
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
let cp = require("child_process");
|
2
|
+
let fs = require("fs");
|
3
|
+
let path = require("path");
|
4
|
+
|
5
|
+
describe('Heapdumps', function () {
|
6
|
+
it('should be created in x seconds', function (done) {
|
7
|
+
this.timeout(250000);
|
8
|
+
|
9
|
+
let child = cp.fork(path.resolve(__dirname, './oom_app.js'), null, {
|
10
|
+
cmd: path.dirname(require.main.filename),
|
11
|
+
stdio: 'inherit',
|
12
|
+
execArgv: ["--max_old_space_size=40", "--optimize_for_size", "--always_compact", "--inspect=9229"]
|
13
|
+
});
|
14
|
+
|
15
|
+
setTimeout(function () {
|
16
|
+
child.kill();
|
17
|
+
fs.lstat(path.resolve(__dirname, "../abc.heapsnapshot"), (err, stats) => {
|
18
|
+
if (!err && stats.isFile()) {
|
19
|
+
done();
|
20
|
+
} else {
|
21
|
+
done(err);
|
22
|
+
}
|
23
|
+
clearTimeout(handle);
|
24
|
+
})
|
25
|
+
}, 20000);
|
26
|
+
});
|
27
|
+
});
|
@@ -0,0 +1,27 @@
|
|
1
|
+
let oomLib = require("../index.js")({
|
2
|
+
heapdumpOnOOM: false
|
3
|
+
});
|
4
|
+
|
5
|
+
var i = 0;
|
6
|
+
var path = "";
|
7
|
+
|
8
|
+
var handle = setInterval(function () {
|
9
|
+
i++;
|
10
|
+
|
11
|
+
oomLib.createHeapSnapshot(require("path").resolve("../", "myName")).then((p) => {
|
12
|
+
path = p;
|
13
|
+
}).catch((err) => {
|
14
|
+
console.error(err);
|
15
|
+
});
|
16
|
+
|
17
|
+
if (i === 3) {
|
18
|
+
oomLib.deleteHeapSnapshot(path).then(() => {
|
19
|
+
//
|
20
|
+
}).catch((err) => {
|
21
|
+
console.error("err", err);
|
22
|
+
});
|
23
|
+
setTimeout(function () {
|
24
|
+
process.exit(0);
|
25
|
+
}, 100);
|
26
|
+
}
|
27
|
+
}, 2000);
|
@@ -0,0 +1,35 @@
|
|
1
|
+
let oomLib = require("../index.js")({
|
2
|
+
heapdumpOnOOM: false
|
3
|
+
});
|
4
|
+
|
5
|
+
var i = 0;
|
6
|
+
var path = "";
|
7
|
+
|
8
|
+
oomLib.createCpuProfile(require("path").resolve("myCPU.cpuprofile"), 3000).then((p) => {
|
9
|
+
console.error("CPU profile", p);
|
10
|
+
|
11
|
+
//oomLib.deleteAllCpuProfiles();
|
12
|
+
}).catch((err) => {
|
13
|
+
console.error(err);
|
14
|
+
});
|
15
|
+
|
16
|
+
var handle = setInterval(function () {
|
17
|
+
i++;
|
18
|
+
|
19
|
+
oomLib.createHeapSnapshot(require("path").resolve("../", "myName")).then((p) => {
|
20
|
+
path = p;
|
21
|
+
}).catch((err) => {
|
22
|
+
console.error(err);
|
23
|
+
});
|
24
|
+
|
25
|
+
if (i === 5) {
|
26
|
+
/* oomLib.deleteHeapSnapshot(path).then(() => {
|
27
|
+
//
|
28
|
+
}).catch((err) => {
|
29
|
+
console.error("err", err);
|
30
|
+
});*/
|
31
|
+
setTimeout(function () {
|
32
|
+
process.exit(0);
|
33
|
+
}, 100);
|
34
|
+
}
|
35
|
+
}, 1000);
|
package/tests/oom_app.js
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
let path = require('path');
|
2
|
+
|
3
|
+
let oom = require("../index.js")({
|
4
|
+
path: path.resolve(__dirname, 'my_heapdump'),
|
5
|
+
heapdumpOnOOM: true,
|
6
|
+
//OOMImplementation: "GC_MONITORING", // use the old implementation
|
7
|
+
addTimestamp: false
|
8
|
+
});
|
9
|
+
|
10
|
+
// It is important to use named constructors (like the one below), otherwise
|
11
|
+
// the heap snapshots will not produce useful outputs for you.
|
12
|
+
function LeakingClass1() {
|
13
|
+
}
|
14
|
+
|
15
|
+
var leaks = [];
|
16
|
+
var handle = setInterval(function () {
|
17
|
+
for (var i = 0; i < 100000; i++) {
|
18
|
+
leaks.push(new LeakingClass1);
|
19
|
+
}
|
20
|
+
|
21
|
+
console.error('Leaks: %d', leaks.length);
|
22
|
+
}, 100);
|