@nearform/heap-profiler
Advanced tools
Comparing version 1.0.0 to 2.0.0
@@ -5,71 +5,43 @@ # Contributor Covenant Code of Conduct | ||
In the interest of fostering an open and welcoming environment, we as | ||
contributors and maintainers pledge to making participation in our project and | ||
our community a harassment-free experience for everyone, regardless of age, body | ||
size, disability, ethnicity, sex characteristics, gender identity and expression, | ||
level of experience, education, socio-economic status, nationality, personal | ||
appearance, race, religion, or sexual identity and orientation. | ||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. | ||
## Our Standards | ||
Examples of behavior that contributes to creating a positive environment | ||
include: | ||
Examples of behavior that contributes to creating a positive environment include: | ||
* Using welcoming and inclusive language | ||
* Being respectful of differing viewpoints and experiences | ||
* Gracefully accepting constructive criticism | ||
* Focusing on what is best for the community | ||
* Showing empathy towards other community members | ||
- Using welcoming and inclusive language | ||
- Being respectful of differing viewpoints and experiences | ||
- Gracefully accepting constructive criticism | ||
- Focusing on what is best for the community | ||
- Showing empathy towards other community members | ||
Examples of unacceptable behavior by participants include: | ||
* The use of sexualized language or imagery and unwelcome sexual attention or | ||
advances | ||
* Trolling, insulting/derogatory comments, and personal or political attacks | ||
* Public or private harassment | ||
* Publishing others' private information, such as a physical or electronic | ||
address, without explicit permission | ||
* Other conduct which could reasonably be considered inappropriate in a | ||
professional setting | ||
- The use of sexualized language or imagery and unwelcome sexual attention or advances | ||
- Trolling, insulting/derogatory comments, and personal or political attacks | ||
- Public or private harassment | ||
- Publishing others' private information, such as a physical or electronic address, without explicit permission | ||
- Other conduct which could reasonably be considered inappropriate in a professional setting | ||
## Our Responsibilities | ||
Project maintainers are responsible for clarifying the standards of acceptable | ||
behavior and are expected to take appropriate and fair corrective action in | ||
response to any instances of unacceptable behavior. | ||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. | ||
Project maintainers have the right and responsibility to remove, edit, or | ||
reject comments, commits, code, wiki edits, issues, and other contributions | ||
that are not aligned to this Code of Conduct, or to ban temporarily or | ||
permanently any contributor for other behaviors that they deem inappropriate, | ||
threatening, offensive, or harmful. | ||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. | ||
## Scope | ||
This Code of Conduct applies both within project spaces and in public spaces | ||
when an individual is representing the project or its community. Examples of | ||
representing a project or community include using an official project e-mail | ||
address, posting via an official social media account, or acting as an appointed | ||
representative at an online or offline event. Representation of a project may be | ||
further defined and clarified by project maintainers. | ||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. | ||
## Enforcement | ||
Instances of abusive, harassing, or otherwise unacceptable behavior may be | ||
reported by contacting the project team at [clinic@nearform.com][clinic]. All | ||
complaints will be reviewed and investigated and will result in a response that | ||
is deemed necessary and appropriate to the circumstances. The project team is | ||
obligated to maintain confidentiality with regard to the reporter of an incident. | ||
Further details of specific enforcement policies may be posted separately. | ||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at opensource@nearform.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. | ||
Project maintainers who do not follow or enforce the Code of Conduct in good | ||
faith may face temporary or permanent repercussions as determined by other | ||
members of the project's leadership. | ||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. | ||
## Attribution | ||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, | ||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html | ||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] | ||
[clinic]: mailto:clinic@nearform.com | ||
[homepage]: https://www.contributor-covenant.org | ||
[homepage]: http://contributor-covenant.org | ||
[version]: http://contributor-covenant.org/version/1/4/ |
@@ -1,52 +0,66 @@ | ||
# Contributing to Clinic.js | ||
# Welcome to @nearform/heap-profiler! | ||
The Clinic.js project welcomes all contributions from anyone willing to | ||
work in good faith with other contributors and the community. No contribution | ||
is too small and all contributions are valued. | ||
Please take a second to read over this before opening an issue. Providing complete information upfront will help us address any issue (and ship new features!) faster. | ||
## Code of Conduct | ||
We greatly appreciate bug fixes, documentation improvements and new features, however when contributing a new major feature, it is a good idea to idea to first open an issue, to make sure the feature it fits with the goal of the project, so we don't waste your or our time. | ||
The Clinic.js project has a [Code of Conduct][CoC] that all contributors are | ||
expected to follow. | ||
## Bug Reports | ||
## Licensing and Certification | ||
A perfect bug report would have the following: | ||
All contributions to the Clinic.js project are submitted to the | ||
project under the MIT license. | ||
1. Summary of the issue you are experiencing. | ||
2. Details on what versions of node and XZY you are using (`node -v`). | ||
3. A simple repeatable test case for us to run. Please try to run through it 2-3 times to ensure it is completely repeatable. | ||
The Clinic.js project uses a Contribution Certification that is derived from | ||
the [Developer Certificate of Origin][DCO]. It is important to note that the | ||
Contribution Certification *is not the same as the standard DCO* and we do not | ||
use the term "DCO" or "Developer Certificate of Origin" to describe it to avoid | ||
confusion. Nevertheless, the intent and purpose is effectively the same. | ||
We would like to avoid issues that require a follow up questions to identify the bug. These follow ups are difficult to do unless we have a repeatable test case. | ||
Every contributor agrees to the Contribution Certification by including a | ||
`Signed-off-by` statement within each commit. The statement *must* include | ||
the contributor's real full name and email address. | ||
## For Developers | ||
All contributions should fit the [standard](https://github.com/standard/standard) linter, and pass the tests. | ||
You can test this by running: | ||
``` | ||
Signed-off-by: J. Random User <j.random.user@example.com> | ||
npm lint | ||
npm test | ||
``` | ||
### Certification | ||
## For Collaborators | ||
Make sure to get a `:thumbsup:`, `+1` or `LGTM` from another collaborator before merging a PR. If you aren't sure if a release should happen, open an issue. | ||
Release process: | ||
- `npm test` | ||
- `npm version <major|minor|patch>` | ||
- `git push && git push --tags` | ||
- `npm publish` | ||
--- | ||
<a id="developers-certificate-of-origin"></a> | ||
## Developer's Certificate of Origin 1.1 | ||
By making a contribution to this project, I certify that: | ||
(a) The contribution was created in whole or in part by me and I have the right | ||
to and hereby submit it under the MIT license; or | ||
- (a) The contribution was created in whole or in part by me and I | ||
have the right to submit it under the open source license | ||
indicated in the file; or | ||
(b) The contribution is based upon previous work that, to the best of my | ||
knowledge, is covered under an appropriate open source license and I have the | ||
right under that license to submit that work with modifications, whether created | ||
in whole or in part by me, under the MIT License; or | ||
- (b) The contribution is based upon previous work that, to the best | ||
of my knowledge, is covered under an appropriate open source | ||
license and I have the right under that license to submit that | ||
work with modifications, whether created in whole or in part | ||
by me, under the same open source license (unless I am | ||
permitted to submit under a different license), as indicated | ||
in the file; or | ||
(c) The contribution was provided directly to me by some other person who | ||
certified (a), (b) or (c) and I have not modified it. | ||
- (c) The contribution was provided directly to me by some other | ||
person who certified (a), (b) or (c) and I have not modified | ||
it. | ||
(d) I understand and agree that this project and the contribution are public | ||
and that a record of the contribution (including all personal information I | ||
submit with it, including my sign-off) is maintained indefinitely and may be | ||
redistributed consistent with this project or license(s) involved. | ||
[CoC]: CODE_OF_CONDUCT.md | ||
[DCO]: https://developercertificate.org/ | ||
- (d) I understand and agree that this project and the contribution | ||
are public and that a record of the contribution (including all | ||
personal information I submit with it, including my sign-off) is | ||
maintained indefinitely and may be redistributed consistent with | ||
this project or the open source license(s) involved. |
109
package.json
{ | ||
"name": "@nearform/heap-profiler", | ||
"version": "1.0.0", | ||
"engines": { | ||
"node": ">= 10.12.0" | ||
"version": "2.0.0", | ||
"description": "Heap dump, sample profiler and allocation timeline generator for Node.", | ||
"author": "NearForm Ltd", | ||
"homepage": "https://github.com/nearform/heap-profiler", | ||
"contributors": [ | ||
{ | ||
"name": "Paolo Insogna", | ||
"url": "https://github.com/ShogunPanda" | ||
}, | ||
{ | ||
"name": "Damien Simonin Feugas", | ||
"url": "https://github.com/feugy" | ||
}, | ||
{ | ||
"name": "Matteo Collina", | ||
"url": "https://github.com/mcollina" | ||
}, | ||
{ | ||
"name": "James M Snell", | ||
"url": "https://github.com/jasnell" | ||
} | ||
], | ||
"license": "Apache-2.0", | ||
"licenses": [ | ||
{ | ||
"type": "Apache-2.0", | ||
"url": "http://www.apache.org/licenses/LICENSE-2.0" | ||
} | ||
], | ||
"keywords": [ | ||
"heap", | ||
"profile", | ||
"sampling", | ||
"snapshot", | ||
"allocation", | ||
"timeline" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/nearform/heap-profiler.git" | ||
}, | ||
"description": "Programmable interface to Clinic.js Heap Profiler", | ||
"repository": "https://github.com/clinicjs/node-clinic-heap-profiler", | ||
"bugs": { | ||
"url": "https://github.com/nearform/heap-profiler/issues" | ||
}, | ||
"main": "src/index.js", | ||
"scripts": { | ||
"visualize-watch": "node debug/visualize-watch.js", | ||
"visualize-all": "node debug/visualize-all.js", | ||
"test": "npm run lint && tap", | ||
"test:file": "standard | snazzy && tap --no-cov", | ||
"ci-lint": "npm run lint", | ||
"ci-test-cov": "tap", | ||
"ci-test-no-cov": "tap --no-cov", | ||
"lint": "standard --fix | snazzy" | ||
"prepublishOnly": "npm run ci", | ||
"postpublish": "git push origin && git push origin -f --tags", | ||
"lint": "eslint src/**/*.js test/**/*.js", | ||
"test": "tap --gc --reporter=spec --coverage-report=html --coverage-report=text --no-browser test/*.spec.js", | ||
"ci": "npm run lint && tap --gc --no-color --reporter=spec --coverage-report=json --coverage-report=text --branches 90 --functions 90 --lines 90 --statements 90 --timeout 180 test/*.spec.js" | ||
}, | ||
"tap": { | ||
"nyc-arg": [ | ||
"--exclude=test/*.js", | ||
"--exclude=visualizer/d3.js", | ||
"--exclude=visualizer/data-tree.js", | ||
"--exclude=visualizer/no-data-node.js" | ||
] | ||
}, | ||
"author": "", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@nearform/clinic-common": "^4.0.0", | ||
"@nearform/heap-profiler": "^0.8.1", | ||
"abort-controller": "^3.0.0", | ||
"copy-to-clipboard": "^3.0.8", | ||
"d3-array": "^2.0.2", | ||
"d3-fg": "^6.13.1", | ||
"d3-selection": "^1.3.2", | ||
"fs-extra": "^9.0.1", | ||
"lodash.debounce": "^4.0.8", | ||
"on-net-listen": "1.1.2", | ||
"pump": "^3.0.0", | ||
"querystringify": "^2.1.0", | ||
"sinusoidal-decimal": "^1.0.0" | ||
"sonic-boom": "^1.0.1", | ||
"abort-controller": "^3.0.0" | ||
}, | ||
"devDependencies": { | ||
"autocannon": "^6.5.0", | ||
"chalk": "^4.1.0", | ||
"chokidar": "^3.4.2", | ||
"fastify": "^3.7.0", | ||
"ioredis": "^4.17.3", | ||
"open": "^7.3.0", | ||
"snazzy": "^8.0.0", | ||
"standard": "^14.3.1", | ||
"tap": "^14.10.6" | ||
"docker-namesgenerator": "0.0.1", | ||
"eslint": "^6.8.0", | ||
"eslint-config-standard": "^14.1.0", | ||
"eslint-plugin-import": "^2.20.0", | ||
"eslint-plugin-node": "^11.0.0", | ||
"eslint-plugin-promise": "^4.2.1", | ||
"eslint-plugin-standard": "^4.0.1", | ||
"prettier": "^1.19.1", | ||
"sinon": "^9.0.1", | ||
"tap": "^14.10.7", | ||
"tmp-promise": "^2.0.2" | ||
}, | ||
"engines": { | ||
"node": ">= 12.13.0" | ||
} | ||
} |
141
README.md
@@ -1,103 +0,108 @@ | ||
# Clinic.js Heap Profiler | ||
# @nearform/heap-profiler | ||
[![npm version][npm-version]][npm-url] [![Stability Stable][stability-stable]][stability-docs] [![Github Actions build status][actions-status]][actions-url] | ||
[![Downloads][npm-downloads]][npm-url] [![Code style][lint-standard]][lint-standard-url] | ||
[![Package Version](https://img.shields.io/npm/v/@nearform/heap-profiler.svg)](https://npm.im/@nearform/heap-profiler) | ||
[![Dependency Status](https://img.shields.io/david/nearform/heap-profiler)](https://david-dm.org/nearform/heap-profiler) | ||
[![Build Status](https://img.shields.io/github/workflow/status/nearform/heap-profiler/CI)](https://github.com/nearform/heap-profiler/actions?query=workflow%3ACI) | ||
Programmable interface to [Clinic.js][clinic-url] Heap Profiler. Learn more about Clinic.js: https://clinicjs.org/ | ||
Heap dump and sample profiler generator for Node. | ||
![Screenshot](screenshot.png) | ||
## Installation | ||
## Supported node versions | ||
Just run: | ||
- Node.js 10 and above | ||
```bash | ||
npm install @nearform/heap-profiler | ||
``` | ||
## Example | ||
## Preloader | ||
```js | ||
const ClinicHeapProfiler = require('@nearform/clinic-heap-profiler') | ||
const heapProfiler = new ClinicHeapProfiler() | ||
Once installed, the profiler can be used as a preloader that adds a listener to `SIGUSR2` signal. | ||
heapProfiler.collect(['node', './path-to-script.js'], function (err, filepath) { | ||
if (err) throw err | ||
If you start your application like this: | ||
heapProfiler.visualize(filepath, filepath + '.html', function (err) { | ||
if (err) throw err | ||
}) | ||
}) | ||
``` | ||
node -r @nearform/heap-profiler index.js | ||
``` | ||
## Documentation | ||
Then you will be able make a snapshot, start profiling heap, and start tracking allocation timeline by sending the process a SIGUSR2 signal, like this: | ||
```js | ||
const ClinicHeapProfiler = require('@nearform/clinic-heap-profiler') | ||
const heapProfiler = new ClinicHeapProfiler() | ||
``` | ||
kill -USR2 $PID | ||
``` | ||
### new ClinicHeapProfiler([settings]) | ||
Heap snapshot will be generated immediately. | ||
- settings [`<Object>`][] | ||
- detectPort [`<boolean>`][] **Default**: false | ||
- collectOnFailure [`<boolean>`][] If set to true, the collected data will be returned even if the process exits with non-zero code. | ||
**Default**: false | ||
- debug [`<boolean>`][] If set to true, the generated html will not be minified. | ||
**Default**: false | ||
- dest [`<String>`][] The file where the collected data is stored. | ||
**Default**: `./.clinic/<process.pid>.clinic-heapprofile` | ||
Heap sampling profiler and allocation timeline must be stopped, by sending another SIGUSR2 signal to the process. | ||
#### `heapProfiler.collect(args, callback)` | ||
Then the tool will await on the next signal, to resume profiling/tracking/shooting the heap. | ||
Starts a process by using [@nearform/heap-profiler](https://github.com/nearform/heap-profiler). | ||
The preloader uses the following environment variables to control its behavior: | ||
The process sampling is started as soon as the process starts. The filepath with collected data will be the value in the callback. | ||
- `HEAP_PROFILER_PRELOADER_DISABLED`: If set to `true`, the preloader is not installed and you need to use the API to sample the process. | ||
`stdout`, `stderr`, and `stdin` will be relayed to the calling process. | ||
- `HEAP_PROFILER_SNAPSHOT`: If set to `false`, it will not generate heap dump snapshots. | ||
The sampling is stopped and data collected right before the process exits. | ||
- `HEAP_PROFILER_SNAPSHOT_DESTINATION`: The path where to store the snapshot. The default will be a `.heapsnapshot` in the current directory. | ||
If you want to collect data earlier, you can send the process a `SIGINT` or, if `detectPort` is `true`, you can call `heapProfiler.stopViaIPC()`. | ||
- `HEAP_PROFILER_SNAPSHOT_RUN_GC`: If to run the garbage collector before taking the snapshot. The default is `false` and it is ignored if the process is not started with the `--expose-gc` flag. | ||
#### `heapProfiler.visualize(dataFilename, outputFilename, callback)` | ||
- `HEAP_PROFILER_PROFILE`: If set to `false`, it will not generate heap sampling profile. | ||
Will consume the datafile specified by `dataFilename`, this datafile will be | ||
produced by the sampler using `heapProfiler.collect`. | ||
- `HEAP_PROFILER_PROFILE_DESTINATION`: The path where to store the profile. The default will be a `.heapprofile` in the current directory. | ||
`heapProfiler.visualize` will then output a standalone HTML file to `outputFilename`. | ||
When completed the `callback` will be called with no extra arguments, except a | ||
possible error. | ||
- `HEAP_PROFILER_PROFILE_INTERVAL`: Heap sampling profile interval, in bytes. Default is `32768` (32KB). | ||
#### `heapProfiler.stopViaIPC()` | ||
- `HEAP_PROFILER_TIMELINE`: If set to `false`, it will not start tracking timeline allocation. | ||
When the profiler is started with `detectPort=true`, the profiler establish a TCP based IPC communication. | ||
- `HEAP_PROFILER_TIMELINE_DESTINATION`: The path where to store the allocation timeline. The default will be a `.heaptimeline` in the current directory. | ||
This method can therefore be called to collect the data at any time. | ||
- `HEAP_PROFILER_TIMELINE_RUN_GC`: Whether or not running Garbage Collector before and after the allocation timeline, to see only remaining objects (default to false). | ||
If no TCP channel is opened or available, the method will perform no operation so it is safe to call at all times. | ||
- `HEAP_PROFILER_LOGGING_DISABLED`: If set to `true`, it will disable logging. | ||
## Examples | ||
## API | ||
See the `examples` folder. All example should be run from the repository main folder: | ||
All module functions can be used with promises and by providing a callback as last option. | ||
```sh | ||
node examples/redis-web-service | ||
``` | ||
The promise resolved value (or the callback argument) will be the generated file path. | ||
Each `index.js` will contain any specific setup step required by the example, if any. | ||
The available functions are: | ||
- `generateHeapSnapshot([options], [callback]): [Promise]`: Generates a heap dump | ||
- `destination`: The path where to store the snapshot. The default will be a `.heapsnapshot` in the current directory. | ||
- `runGC`: If to run the garbage collector before taking the snapshot. The default is `false` and it is ignored if the process is not started with the `--expose-gc` flag. | ||
- `generateHeapSamplingProfile([options], [callback]): [Promise]`: Starts generating a heap sampling profiler. The valid options are: | ||
- `destination`: The path where to store the profile. The default will be a `.heapprofile` in the current directory. | ||
- `interval`: Sample interval, in bytes. Default is `32768` (32KB). | ||
- `duration`: Sample duration, in milliseconds. Default is `10000` (10 seconds), and it is ignored if `signal` is provided. | ||
- `signal`: The [AbortController](http://npm.im/abort-controller) `signal` to use to stop the operation. | ||
The function accepts a callback function, otherwise it returns a Promise. The resolved value (or the callback argument) will be | ||
the generated file path. | ||
- `recordAllocationTimeline([options], [callback]): [Promise]`: Starts recording allocation on heap. The valid options are: | ||
- `destination`: The path where to store the timeline. The default will be a `.heaptimeline` in the current directory. | ||
- `runGC`: If to run the garbage collector at the begining and the end of the timeline. The default is `false` and it is ignored if the process is not started with the `--expose-gc` flag. | ||
- `duration`: Recording duration, in milliseconds. Default is `10000` (10 seconds), and it is ignored if `signal` is provided. | ||
- `signal`: The [AbortController](http://npm.im/abort-controller) `signal` to use to stop the operation. | ||
The function accepts a callback function, otherwise it returns a Promise. The resolved value (or the callback argument) will be | ||
the generated file path. | ||
## Performance impact | ||
Generating a heap dump snapshot is handled synchronously by Node and therefore **will block your process completely**. | ||
Generating a heap sampling profile or record allocation timeline is instead asynchronous and lightweight. Our test showed that the **performance decrease is around 10%**. | ||
## Contributing | ||
See [CONTRIBUTING.md](./CONTRIBUTING.md) | ||
## License | ||
[MIT](LICENSE) | ||
[stability-stable]: https://img.shields.io/badge/stability-stable-green.svg?style=flat-square | ||
[stability-docs]: https://nodejs.org/api/documentation.html#documentation_stability_index | ||
[npm-version]: https://img.shields.io/npm/v/@nearform/clinic-heap-profiler.svg?style=flat-square | ||
[npm-url]: https://www.npmjs.org/@nearform/clinic-heap-profiler | ||
[npm-downloads]: http://img.shields.io/npm/dm/@nearform/clinic-heap-profiler.svg?style=flat-square | ||
[lint-standard]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square | ||
[lint-standard-url]: https://github.com/feross/standard | ||
[clinic-url]: https://github.com/nearform/node-clinic | ||
[`<object>`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object | ||
[`<number>`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number | ||
[`<boolean>`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type | ||
[`<string>`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String | ||
[actions-status]: https://github.com/nearform/node-clinic-flame/workflows/CI/badge.svg | ||
[actions-url]: https://github.com/nearform/node-clinic-flame/actions | ||
Copyright NearForm Ltd 2020. Licensed under the [Apache-2.0 license](http://www.apache.org/licenses/LICENSE-2.0). |
201
src/index.js
'use strict' | ||
const { spawn } = require('child_process') | ||
const events = require('events') | ||
const fs = require('fs') | ||
const { ensureDir } = require('fs-extra') | ||
const { createConnection, createServer } = require('net') | ||
const path = require('path') | ||
const pump = require('pump') | ||
const buildJs = require('@nearform/clinic-common/scripts/build-js') | ||
const buildCss = require('@nearform/clinic-common/scripts/build-css') | ||
const mainTemplate = require('@nearform/clinic-common/templates/main') | ||
const { analyse } = require('./analysis/index.js') | ||
const generateHeapSnapshot = require('./snapshot') | ||
const generateHeapSamplingProfile = require('./profile') | ||
/* istanbul ignore next */ | ||
const noop = function () {} | ||
module.exports = { generateHeapSnapshot, generateHeapSamplingProfile } | ||
function execute (instance, args, env, nodeOptions, cb) { | ||
ensureDir(path.dirname(instance.dest), err => { | ||
/* istanbul ignore if */ | ||
if (err) { | ||
cb(err) | ||
return | ||
} | ||
if (process.env.HEAP_PROFILER_PRELOADER_DISABLED !== 'true' && require.main === undefined) { | ||
let logger = console | ||
env.NODE_OPTIONS = nodeOptions | ||
/* | ||
By default spawn creates a process in the same process group of the current one. | ||
This means that SIGINT are received both from the parent and child processes. | ||
We handle SIGINT in the child, so we want to ignore in the parent. | ||
*/ | ||
process.once('SIGINT', noop) | ||
instance.process = spawn(args[0], args.slice(1), { stdio: ['ignore', 'inherit', 'inherit'], env }) | ||
instance.process.once('exit', (code, signal) => { | ||
process.removeListener('SIGINT', noop) | ||
instance.emit('analysing') | ||
if (code && !instance.collectOnFailure) { | ||
cb(new Error(`Child process exited with code ${code}.`)) | ||
return | ||
} | ||
cb(null, instance.dest) | ||
}) | ||
}) | ||
} | ||
function writeHtml (data, outputFilename, debug, cb) { | ||
const fakeDataPath = path.join(__dirname, 'visualizer', 'data.json') | ||
const stylePath = path.join(__dirname, 'visualizer', 'style.css') | ||
const scriptPath = path.join(__dirname, 'visualizer', 'main.js') | ||
const logoPath = path.join(__dirname, 'visualizer/assets', 'heap-profiler-logo.svg') | ||
const nearFormLogoPath = path.join(__dirname, 'visualizer', 'nearform-logo.svg') | ||
const clinicFaviconPath = path.join(__dirname, 'visualizer', 'clinic-favicon.png.b64') | ||
// add logos | ||
const logoFile = fs.createReadStream(logoPath) | ||
const nearFormLogoFile = fs.createReadStream(nearFormLogoPath) | ||
const clinicFaviconBase64 = fs.createReadStream(clinicFaviconPath) | ||
const version = require('../package.json').version | ||
// build JS | ||
const scriptFile = buildJs({ | ||
basedir: __dirname, | ||
debug, | ||
fakeDataPath, | ||
scriptPath, | ||
beforeBundle (b) { | ||
b.require({ source: JSON.stringify(data), file: fakeDataPath }) | ||
}, | ||
env: { | ||
PRESENTATION_MODE: process.env.PRESENTATION_MODE | ||
} | ||
}) | ||
// build CSS | ||
const styleFile = buildCss({ stylePath, debug }) | ||
// generate HTML | ||
const outputFile = mainTemplate({ | ||
favicon: clinicFaviconBase64, | ||
title: 'Clinic Heap Profiler', | ||
styles: styleFile, | ||
script: scriptFile, | ||
headerLogoUrl: 'https://clinicjs.org/heap-profiler/', | ||
headerLogoTitle: 'Clinic Heap Profiler on Clinicjs.org', | ||
headerLogo: logoFile, | ||
headerText: 'Heap Profiler', | ||
toolVersion: version, | ||
nearFormLogo: nearFormLogoFile, | ||
uploadId: outputFilename.split('/').pop().split('.html').shift(), | ||
body: '<main></main>' | ||
}) | ||
pump(outputFile, fs.createWriteStream(outputFilename), cb) | ||
} | ||
class ClinicHeapProfiler extends events.EventEmitter { | ||
constructor (settings = {}) { | ||
super() | ||
const { | ||
detectPort = false, | ||
collectOnFailure = false, | ||
debug = false, | ||
dest = `.clinic/${process.pid}.clinic-heapprofile` | ||
} = settings | ||
this.detectPort = !!detectPort | ||
this.collectOnFailure = !!collectOnFailure | ||
this.debug = debug | ||
this.dest = dest | ||
if (process.env.HEAP_PROFILER_LOGGING_DISABLED === 'true') { | ||
logger = { info: () => {}, error: () => {} } | ||
} | ||
collect (args, cb) { | ||
const nodeOptions = ` -r ${path.join(__dirname, './injects/ipc.js')}` | ||
const env = { | ||
...process.env, | ||
HEAP_PROFILER_DESTINATION: this.dest, | ||
HEAP_PROFILER_PRELOADER_DISABLED: 'true', | ||
HEAP_PROFILER_USE_IPC: this.detectPort | ||
} | ||
if (!this.detectPort) { | ||
execute(this, args, env, nodeOptions, cb) | ||
return | ||
} | ||
let applicationPort | ||
const server = createServer(socket => { | ||
socket.on('data', raw => { | ||
const port = parseInt(raw.toString(), 0) | ||
/* istanbul ignore if */ | ||
if (isNaN(port)) { | ||
return | ||
} | ||
// That's the IPC port | ||
if (port < 0) { | ||
this.ipcPort = -port | ||
} else { | ||
applicationPort = port | ||
} | ||
if (this.detectPort && this.ipcPort && applicationPort) { | ||
server.close() | ||
/* | ||
The last argument, by clinic CLI contract, is required when using --autocannon or --on-port option. | ||
The CLI invokes the callback when the tool --autocannon or --on-port tool has finished. | ||
*/ | ||
this.emit('port', applicationPort, null, this.stopViaIPC.bind(this)) | ||
} | ||
}) | ||
}).on( | ||
'error', | ||
/* istanbul ignore next */ | ||
err => { | ||
return cb(err) | ||
} | ||
) | ||
// Grab an arbitrary unused port | ||
server.listen(0, () => { | ||
env.CLINIC_HEAP_PROFILER_PORT = server.address().port | ||
execute(this, args, env, nodeOptions, cb) | ||
}) | ||
} | ||
visualize (sourceFile, outputFilename, cb) { | ||
analyse(sourceFile, (err, converted) => { | ||
if (err) { | ||
return cb(err) | ||
} | ||
writeHtml(converted, outputFilename, this.debug, cb) | ||
}) | ||
} | ||
stopViaIPC () { | ||
if (!this.ipcPort) { | ||
return | ||
} | ||
const client = createConnection({ port: this.ipcPort }, () => { | ||
client.end('clinic-heap-profiler:stop') | ||
}) | ||
// Ignore if nobody is listening | ||
client.on('error', noop) | ||
} | ||
require('./preloader')(logger) | ||
} | ||
module.exports = ClinicHeapProfiler |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 12 instances in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 7 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
2
0
0
109
1
53158
11
21
1146
52
+ Addedsonic-boom@^1.0.1
- Removed@nearform/clinic-common@^4.0.0
- Removed@nearform/heap-profiler@^0.8.1
- Removedcopy-to-clipboard@^3.0.8
- Removedd3-array@^2.0.2
- Removedd3-fg@^6.13.1
- Removedd3-selection@^1.3.2
- Removedfs-extra@^9.0.1
- Removedlodash.debounce@^4.0.8
- Removedon-net-listen@1.1.2
- Removedpump@^3.0.0
- Removedquerystringify@^2.1.0
- Removedsinusoidal-decimal@^1.0.0
- Removed@nearform/clinic-common@4.0.0(transitive)
- Removed@nearform/heap-profiler@0.8.1(transitive)
- RemovedJSONStream@1.3.5(transitive)
- Removedacorn@7.4.1(transitive)
- Removedacorn-node@1.8.2(transitive)
- Removedacorn-walk@7.2.0(transitive)
- Removedansi-styles@3.2.1(transitive)
- Removedarray-from@2.1.1(transitive)
- Removedasn1.js@4.10.1(transitive)
- Removedassert@1.5.1(transitive)
- Removedat-least-node@1.0.0(transitive)
- Removedbalanced-match@1.0.2(transitive)
- Removedbase64-js@1.5.1(transitive)
- Removedbn.js@4.12.05.2.1(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedbrfs@2.0.2(transitive)
- Removedbrorand@1.1.0(transitive)
- Removedbrowser-pack@6.1.0(transitive)
- Removedbrowser-resolve@2.0.0(transitive)
- Removedbrowserify@16.5.2(transitive)
- Removedbrowserify-aes@1.2.0(transitive)
- Removedbrowserify-cipher@1.0.1(transitive)
- Removedbrowserify-des@1.0.2(transitive)
- Removedbrowserify-rsa@4.1.0(transitive)
- Removedbrowserify-sign@4.2.3(transitive)
- Removedbrowserify-zlib@0.2.0(transitive)
- Removedbuffer@5.2.1(transitive)
- Removedbuffer-equal@0.0.1(transitive)
- Removedbuffer-from@1.1.2(transitive)
- Removedbuffer-xor@1.0.3(transitive)
- Removedbuiltin-status-codes@3.0.0(transitive)
- Removedcached-path-relative@1.1.0(transitive)
- Removedcall-bind@1.0.7(transitive)
- Removedchalk@2.4.2(transitive)
- Removedcipher-base@1.0.4(transitive)
- Removedcolor-convert@1.9.3(transitive)
- Removedcolor-name@1.1.3(transitive)
- Removedcombine-source-map@0.8.0(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removedconcat-stream@1.6.2(transitive)
- Removedconsole-browserify@1.2.0(transitive)
- Removedconstants-browserify@1.0.0(transitive)
- Removedconvert-source-map@1.1.31.9.0(transitive)
- Removedcopy-to-clipboard@3.3.3(transitive)
- Removedcore-util-is@1.0.3(transitive)
- Removedcreate-ecdh@4.0.4(transitive)
- Removedcreate-hash@1.2.0(transitive)
- Removedcreate-hmac@1.1.7(transitive)
- Removedcrypto-browserify@3.12.0(transitive)
- Removedd@1.0.2(transitive)
- Removedd3-array@2.12.1(transitive)
- Removedd3-color@1.4.12.0.0(transitive)
- Removedd3-dispatch@1.0.6(transitive)
- Removedd3-drag@1.2.5(transitive)
- Removedd3-ease@1.0.7(transitive)
- Removedd3-fg@6.14.0(transitive)
- Removedd3-format@2.0.0(transitive)
- Removedd3-hierarchy@1.1.9(transitive)
- Removedd3-interpolate@1.4.02.0.1(transitive)
- Removedd3-scale@3.3.0(transitive)
- Removedd3-selection@1.4.2(transitive)
- Removedd3-time@2.1.1(transitive)
- Removedd3-time-format@3.0.0(transitive)
- Removedd3-timer@1.0.10(transitive)
- Removedd3-transition@1.3.2(transitive)
- Removedd3-zoom@1.8.3(transitive)
- Removeddash-ast@1.0.02.0.1(transitive)
- Removeddeep-is@0.1.4(transitive)
- Removeddefine-data-property@1.1.4(transitive)
- Removeddefine-properties@1.2.1(transitive)
- Removeddefined@1.0.1(transitive)
- Removeddeps-sort@2.0.1(transitive)
- Removeddes.js@1.1.0(transitive)
- Removeddetective@5.2.1(transitive)
- Removeddiffie-hellman@5.0.3(transitive)
- Removeddomain-browser@1.2.0(transitive)
- Removedduplexer2@0.1.4(transitive)
- Removedelliptic@6.5.5(transitive)
- Removedend-of-stream@1.4.4(transitive)
- Removedes-define-property@1.0.0(transitive)
- Removedes-errors@1.3.0(transitive)
- Removedes5-ext@0.10.64(transitive)
- Removedes6-iterator@2.0.3(transitive)
- Removedes6-map@0.1.5(transitive)
- Removedes6-set@0.1.6(transitive)
- Removedes6-symbol@3.1.4(transitive)
- Removedescape-string-regexp@1.0.5(transitive)
- Removedescodegen@1.14.32.1.0(transitive)
- Removedesniff@2.0.1(transitive)
- Removedesprima@4.0.1(transitive)
- Removedestraverse@4.3.05.3.0(transitive)
- Removedestree-is-function@1.0.0(transitive)
- Removedesutils@2.0.3(transitive)
- Removedevent-emitter@0.3.5(transitive)
- Removedevents@2.1.0(transitive)
- Removedevp_bytestokey@1.0.3(transitive)
- Removedext@1.7.0(transitive)
- Removedfast-levenshtein@2.0.6(transitive)
- Removedfast-safe-stringify@2.1.1(transitive)
- Removedfs-extra@9.1.0(transitive)
- Removedfs.realpath@1.0.0(transitive)
- Removedfunction-bind@1.1.2(transitive)
- Removedget-assigned-identifiers@1.2.0(transitive)
- Removedget-intrinsic@1.2.4(transitive)
- Removedglob@7.2.3(transitive)
- Removedgopd@1.0.1(transitive)
- Removedgraceful-fs@4.2.11(transitive)
- Removedhas@1.0.4(transitive)
- Removedhas-flag@3.0.0(transitive)
- Removedhas-property-descriptors@1.0.2(transitive)
- Removedhas-proto@1.0.3(transitive)
- Removedhas-symbols@1.0.3(transitive)
- Removedhash-base@3.0.4(transitive)
- Removedhash.js@1.1.7(transitive)
- Removedhasown@2.0.2(transitive)
- Removedhmac-drbg@1.0.1(transitive)
- Removedhsl-to-rgb-for-reals@1.1.1(transitive)
- Removedhtmlescape@1.1.1(transitive)
- Removedhttps-browserify@1.0.0(transitive)
- Removedieee754@1.2.1(transitive)
- Removedinflight@1.0.6(transitive)
- Removedinherits@2.0.32.0.4(transitive)
- Removedinline-source-map@0.6.3(transitive)
- Removedinsert-module-globals@7.2.1(transitive)
- Removedinternmap@1.0.1(transitive)
- Removedis-buffer@1.1.6(transitive)
- Removedis-core-module@2.13.1(transitive)
- Removedisarray@1.0.0(transitive)
- Removedjs-tokens@4.0.0(transitive)
- Removedjson-stable-stringify@0.0.1(transitive)
- Removedjsonfile@6.1.0(transitive)
- Removedjsonify@0.0.1(transitive)
- Removedjsonparse@1.3.1(transitive)
- Removedlabeled-stream-splicer@2.0.2(transitive)
- Removedlevn@0.3.0(transitive)
- Removedlodash.debounce@4.0.8(transitive)
- Removedlodash.memoize@3.0.4(transitive)
- Removedloose-envify@1.4.0(transitive)
- Removedmagic-string@0.25.1(transitive)
- Removedmd5.js@1.3.5(transitive)
- Removedmerge-source-map@1.0.4(transitive)
- Removedmiller-rabin@4.0.1(transitive)
- Removedminimalistic-assert@1.0.1(transitive)
- Removedminimalistic-crypto-utils@1.0.1(transitive)
- Removedminimatch@3.1.2(transitive)
- Removedminimist@1.2.8(transitive)
- Removedmkdirp-classic@0.5.3(transitive)
- Removedmodule-deps@6.2.3(transitive)
- Removednext-tick@1.1.0(transitive)
- Removedobject-inspect@1.13.1(transitive)
- Removedobject-keys@1.1.1(transitive)
- Removedobject.assign@4.1.5(transitive)
- Removedon-net-listen@1.1.2(transitive)
- Removedonce@1.4.0(transitive)
- Removedoptionator@0.8.3(transitive)
- Removedos-browserify@0.3.0(transitive)
- Removedpako@1.0.11(transitive)
- Removedparents@1.0.1(transitive)
- Removedparse-asn1@5.1.7(transitive)
- Removedpath-browserify@0.0.1(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
- Removedpath-parse@1.0.7(transitive)
- Removedpath-platform@0.11.15(transitive)
- Removedpbkdf2@3.1.2(transitive)
- Removedpicocolors@0.2.1(transitive)
- Removedpify@2.3.0(transitive)
- Removedpostcss@7.0.39(transitive)
- Removedpostcss-import@12.0.1(transitive)
- Removedpostcss-value-parser@3.3.1(transitive)
- Removedprelude-ls@1.1.2(transitive)
- Removedprocess@0.11.10(transitive)
- Removedprocess-nextick-args@2.0.1(transitive)
- Removedpublic-encrypt@4.0.3(transitive)
- Removedpump@3.0.0(transitive)
- Removedpunycode@1.4.1(transitive)
- Removedqs@6.12.1(transitive)
- Removedquerystring-es3@0.2.1(transitive)
- Removedquerystringify@2.2.0(transitive)
- Removedquote-stream@1.0.2(transitive)
- Removedrandombytes@2.1.0(transitive)
- Removedrandomfill@1.0.4(transitive)
- Removedread-cache@1.0.0(transitive)
- Removedread-only-stream@2.0.0(transitive)
- Removedreadable-stream@2.3.83.6.2(transitive)
- Removedresolve@1.22.8(transitive)
- Removedripemd160@2.0.2(transitive)
- Removedsafe-buffer@5.1.25.2.1(transitive)
- Removedscope-analyzer@2.1.2(transitive)
- Removedset-function-length@1.2.2(transitive)
- Removedsha.js@2.4.11(transitive)
- Removedshallow-copy@0.0.1(transitive)
- Removedshasum@1.0.2(transitive)
- Removedshasum-object@1.0.0(transitive)
- Removedshell-quote@1.8.1(transitive)
- Removedside-channel@1.0.6(transitive)
- Removedsimple-concat@1.0.1(transitive)
- Removedsinusoidal-decimal@1.0.0(transitive)
- Removedsource-map@0.5.70.6.1(transitive)
- Removedsourcemap-codec@1.4.8(transitive)
- Removedstatic-eval@2.1.1(transitive)
- Removedstatic-module@3.0.4(transitive)
- Removedstream-browserify@2.0.2(transitive)
- Removedstream-combiner2@1.1.1(transitive)
- Removedstream-http@3.2.0(transitive)
- Removedstream-splicer@2.0.1(transitive)
- Removedstream-template@0.0.10(transitive)
- Removedstring_decoder@1.1.11.3.0(transitive)
- Removedsubarg@1.0.0(transitive)
- Removedsupports-color@5.5.0(transitive)
- Removedsupports-preserve-symlinks-flag@1.0.0(transitive)
- Removedsyntax-error@1.4.0(transitive)
- Removedthrough@2.3.8(transitive)
- Removedthrough2@2.0.5(transitive)
- Removedtimers-browserify@1.4.2(transitive)
- Removedtoggle-selection@1.0.6(transitive)
- Removedtty-browserify@0.0.1(transitive)
- Removedtype@2.7.2(transitive)
- Removedtype-check@0.3.2(transitive)
- Removedtypedarray@0.0.6(transitive)
- Removedumd@3.0.3(transitive)
- Removedundeclared-identifiers@1.1.3(transitive)
- Removeduniversalify@2.0.1(transitive)
- Removedurl@0.11.3(transitive)
- Removedutil@0.10.4(transitive)
- Removedutil-deprecate@1.0.2(transitive)
- Removedvm-browserify@1.1.2(transitive)
- Removedwebfontloader@1.6.28(transitive)
- Removedword-wrap@1.2.5(transitive)
- Removedwrappy@1.0.2(transitive)
- Removedxtend@4.0.2(transitive)