Git Product home page Git Product logo

leakage's Introduction

Leakage - Memory Leak Testing for Node

Build Status NPM Version JavaScript Style Guide

Write leakage tests using Mocha or another test runner of your choice.

Does not only support spotting and fixing memory leaks, but writing tests also enables you to prevent regressions and show that the code does not leak.

Screencast

Table of Contents

Installation

npm install --save-dev leakage
# or
yarn --dev leakage

Usage

In theory you could use any testing framework to run leakage tests. In practice, though, you want to use one that generates a minimal memory overhead itself. We suggest using Mocha or Tape, since they are quite simple and don't produce much noise in the captured data.

Usage with Mocha

import myLib from 'my-lib'
import { iterate } from 'leakage'

describe('myLib', () => {
  it('does not leak when doing stuff', () => {
    iterate(() => {
      const instance = myLib.createInstance()
      instance.doStuff('foo', 'bar')
    })
  })
})

iterate() will run the function several times, create a heap snapshot and repeat that process until there is a set of heap diffs. If a memory leak has been detected an error with some debugging information will be thrown.

Make sure you run all tests serially in order to get clean heap diffs. Mocha should run them sequentially by default.

Use iterate.async() for asynchronous test code. See Asynchronous Tests and API for details.

Usage with tape

import test from 'tape'
import myLib from 'my-lib'
import { iterate } from 'leakage'

test('myLib does not leak when doing stuff', () => {
  iterate(() => {
    const instance = myLib.createInstance()
    instance.doStuff('foo', 'bar')
  })
})

Asynchronous Tests

Use iterate.async() to test asynchronous code. The iterator function is supposed to return a promise and iterate.async() will return a promise itself. In case of a memory leak that returned promise will be rejected instead of iterate failing synchronously.

Do not forget to return the promise in your test or use async functions and await iterate.async().

import fetch from 'isomorphic-fetch'
import { iterate } from 'leakage'

describe('isomorphic-fetch', () => {
  it('does not leak when requesting data and parsing JSON', async () => {
    await iterate.async(async () => {
      const response = await fetch()
      await response.json()
    })
  })
})

Memory Management in JS?

Since every JavaScript runtime comes with a garbage collector you should not have to care about memory allocation / freeing memory at all, right? Sadly not.

Memory leaks are a common problem in most programming languages. Memory gets allocated, but is not freed again, leading to a steadily increasing usage of memory. Since the memory you use is finite your application will eventually crash or become so slow it is rendered useless.

As soon as you still have a reference to an object, array, arrow function, ... you do not use anymore you might have already created a memory leak. Creating an object (incl. arrays and closures) means allocating heap memory that will be freed by the next automatic garbage collection only if all references to this object have vanished.

API

iterate(syncIterator: Function, options: ?Object): Result

Test for memory leaks. Will throw an error when a leak is recognized.

syncIterator can be any synchronous function. Let it perform your operations you want to test for memory leaks.

options.iterations is the number the iterator function is run for each heap diff / garbage collection. Defaults to 30.

options.gcollections is the number of heap snapshots to create. Defaults to 60.

iterate.async(asyncIterator: Function, options: ?Object): Promise

Test for memory leaks. Will return a rejecting promise when a leak is recognized.

asyncIterator can be any asynchronous function. Let it perform your operations you want to test for memory leaks.

options.iterations is the number the iterator function is run for each heap diff / garbage collection. Defaults to 30.

options.gcollections is the number of heap snapshots to create. Defaults to 60.

Result object

Properties:

  • heapDiffs - An array of heap diffs as created by node-memwatch
  • iterations - The number of iterator runs per heap diff
  • gcollections - The number of garbage collections / heap diffs performed

Methods:

  • printSummary(title: ?String, log: ?Function) - Prints a short summary. Can pass a title to print. log is the function used to output the summary line by line. Defaults to console.log.

MemoryLeakError

Memory leak errors are instances of this custom error. You can use it to check if an error is really a memory leak error or just a generic kind of problem (like a broken reference).

Import it as const { MemoryLeakError } = require('leakage').

CLI Parameters

You can pass special CLI parameters for leakage to your test runner:

mocha test/sample.test.js --heap-file heap-diff.json

--heap-file

Will make the library write a detailed heap diff JSON to the file system. Make sure you only run a single test using it.only. Otherwise you will only find the heap diff of the last test in the file. Useful for debugging.

Under the Hood

Leakage uses node-memwatch to trigger the garbage collector and create heap diffs.

You just specify an iterator function. It will be run 30 times by default then a garbage collection will be performed and a heap snapshot will be made. This process is iterated 6 times by default to collect several heap diffs, since especially in async tests there is always a bit of background noise.

If the heap size increased over more than [heapDiffCount * 2 / 3] subsequent garbage collections an error is thrown.

Travis CI

You might want your leakage tests to be run by your CI service. There is an issue with Travis CI's linux containers, g++ and a transitive dependency of node-memwatch (nan).

Fortunately there is a fix: You need to install and use version 4.8 of g++ in order to compile the dependency.

Have a look at leakage's .travis.yml file to see how it can be done or find further details by @btmills in this issue.

FAQ

I encountered a timeout error

If you see an error like Error: Timeout of 2000ms exceeded. (...) it means that your test took so long that the test runner cancelled it.

You can easily increase the timeout. Have a look at your test runner's documentation for that. When using Mocha, for instance, you can run it with --timeout 10000 to increase the timeout to 10000ms (10s).

Why are my tests slow anyway?

Leakage tests are rather slow compared to usual unit tests, since heap snapshotting and diffing takes some time and has to be done several times per test.

You can try to reduce the number of heap diffs created, but beware that fewer heap diffs can result in less accurate results. See API for details.

Contribute

Got any feedback, suggestions, ...? Feel free to open an issue and share your thoughts!

Used it successfully or were unable to use it? Let us know!

Have an improvement? Open a pull request any time.

License

Released under the terms of the MIT license. See LICENSE for details.

leakage's People

Contributors

alvis avatar andywer avatar brandonros avatar dependabot[bot] avatar iainjreid avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

leakage's Issues

Document best practices: console.log

Thoughts on adding a few best practices around garbage collection. Adding a console.log for instance...

describe.only('test()', () => {
  it('does not leak when doing stuff', () => {
    iterate(6, () => { const test = new Person(); console.log(test); });
  });
});

function Person() {
  this.age = 1;
  this.name = 'mike';
}

vs

describe.only('test()', () => {
  it('does not leak when doing stuff', () => {
    iterate(6, () => { const test = new Person();  });
  });
});

function Person() {
  this.age = 1;
  this.name = 'mike';
}

Package not working with NodeJS 12

This could possibly also be due to VS 2019, but I doubt it

> @airbnb/[email protected] install C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch
> node-gyp rebuild


C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch>if not defined npm_config_node_gyp (node "C:\Users\<User>\AppData\Roaming\npm\node_modules\npm\node_modules\npm-lifecycle\node-gyp-bin\\..\..\node_modules\node-gyp\bin\node-gyp.js" rebuild )  else (node "C:\Users\<User>\AppData\Roaming\npm\node_modules\npm\node_modules\node-gyp\bin\node-gyp.js" rebuild ) 
Building the projects in this solution one at a time. To enable parallel build, please add the "-m" switch.
  heapdiff.cc
  init.cc
  memwatch.cc
  util.cc
  win_delay_load_hook.cc
c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan_object_wrap.h(24): error C2039: 'IsNearDeath': is not a member of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>' [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwat
ch\build\memwatch.vcxproj]
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\memwatch.cc)
  c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan.h(1920): note: see declaration of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>'
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\memwatch.cc)
c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan_object_wrap.h(127): error C2039: 'IsNearDeath': is not a member of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>' [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwa
tch\build\memwatch.vcxproj]
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\memwatch.cc)
  c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan.h(1920): note: see declaration of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>'
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\memwatch.cc)
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.hh(18): error C2039: 'Handle': is not a member of 'v8' (compiling source file ..\src\memwatch.cc) [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\bui
ld\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8-profiler.h(48): note: see declaration of 'v8' (compiling source file ..\src\memwatch.cc)
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.hh(18): error C2061: syntax error: identifier 'Handle' (compiling source file ..\src\memwatch.cc) [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\bui
ld\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\memwatch.cc(19): fatal error C1083: Cannot open include file: 'sys/time.h': No such file or directory [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\me
mwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan_object_wrap.h(24): error C2039: 'IsNearDeath': is not a member of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>' [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwat
ch\build\memwatch.vcxproj]
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\heapdiff.cc)
  c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan.h(1920): note: see declaration of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>'
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\heapdiff.cc)
c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan_object_wrap.h(127): error C2039: 'IsNearDeath': is not a member of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>' [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwa
tch\build\memwatch.vcxproj]
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\heapdiff.cc)
  c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan.h(1920): note: see declaration of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>'
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\heapdiff.cc)
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.hh(18): error C2039: 'Handle': is not a member of 'v8' (compiling source file ..\src\heapdiff.cc) [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\bui
ld\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8-platform.h(16): note: see declaration of 'v8' (compiling source file ..\src\heapdiff.cc)
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.hh(18): error C2061: syntax error: identifier 'Handle' (compiling source file ..\src\heapdiff.cc) [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\bui
ld\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(46): error C2039: 'Handle': is not a member of 'v8' [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8-platform.h(16): note: see declaration of 'v8'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(46): error C2065: 'Handle': undeclared identifier [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(46): error C2275: 'v8::Object': illegal use of this type as an expression [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]  
  c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(46): note: see declaration of 'v8::Object'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(46): error C2065: 'target': undeclared identifier [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(46): error C2761: 'void heapdiff::HeapDiff::Initialize(void)': redeclaration of member is not allowed [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwa 
tch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(47): error C2448: 'heapdiff::HeapDiff::Initialize': function-style initializer appears to be a function definition [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airb 
nb\node-memwatch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan_object_wrap.h(24): error C2039: 'IsNearDeath': is not a member of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>' [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwat
ch\build\memwatch.vcxproj]
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\init.cc)
  c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan.h(1920): note: see declaration of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>'
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\init.cc)
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(93): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\bui 
ld\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan_object_wrap.h(127): error C2039: 'IsNearDeath': is not a member of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>' [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwa 
tch\build\memwatch.vcxproj]
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\init.cc)c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(93): error C2143: syntax error: missing ',' before '<'
  
  c:\users\<User>\desktop\programming\rest.js\node_modules\nan\nan.h(1920): note: see declaration of 'Nan::Persistent<v8::Object,v8::NonCopyablePersistentTraits<T>>'
          with
          [
              T=v8::Object
          ] (compiling source file ..\src\init.cc)
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(95): error C2065: 'str': undeclared identifier [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(95): error C2512: 'v8::String::Utf8Value::Utf8Value': no appropriate default constructor available [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch
\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.hh(18): error C2039: 'Handle': is not a member of 'v8' (compiling source file ..\src\init.cc) [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\m
emwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8-profiler.h(48): note: see declaration of 'v8' (compiling source file ..\src\init.cc)c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(110): error C2664: 'std::string handleToStr
  (const int)': cannot convert argument 1 from 'v8::Local<v8::String>' to 'const int'

  c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(110): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.hh(18): error C2061: syntax error: identifier 'Handle' (compiling source file ..\src\init.cc) [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\m 
emwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\init.cc(12): error C2039: 'Handle': is not a member of 'v8' [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8-profiler.h(48): note: see declaration of 'v8'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\init.cc(12): error C2065: 'Handle': undeclared identifier [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\init.cc(12): error C2275: 'v8::Object': illegal use of this type as an expression [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\init.cc(12): note: see declaration of 'v8::Object'c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(182): error C2664: 'std::string handleToStr 
  (const int)': cannot convert argument 1 from 'v8::Local<v8::String>' to 'const int'
  
  c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(182): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\init.cc(12): error C2065: 'target': undeclared identifier [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\init.cc(12): error C2182: 'init': illegal use of type 'void' [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\init.cc(13): error C2448: 'init': function-style initializer appears to be a function definition [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatc 
h.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\init.cc(24): warning C4312: 'type cast': conversion from 'int' to 'node::addon_register_func' of greater size [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\
build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(223): error C2143: syntax error: missing ';' before '<' [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(223): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\bu 
ild\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(224): error C2143: syntax error: missing ';' before '{' [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(224): error C2447: '{': missing function header (old-style formal list?) [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(307): error C3861: 'changesetToObject': identifier not found [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(252): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(254): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(257): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(259): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(265): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(266): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(271): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(272): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(277): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(278): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(279): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(284): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
c:\users\<User>\desktop\programming\rest.js\node_modules\@airbnb\node-memwatch\src\heapdiff.cc(300): warning C4996: 'v8::Object::Set': was declared deprecated [C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch\build\memwatch.vcxproj]
  c:\users\<User>\.node-gyp\12.2.0\include\node\v8.h(3358): note: see declaration of 'v8::Object::Set'
gyp ERR! build error 
gyp ERR! stack Error: `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe` failed with exit code: 1
gyp ERR! stack     at ChildProcess.onExit (C:\Users\<User>\AppData\Roaming\npm\node_modules\npm\node_modules\node-gyp\lib\build.js:262:23)
gyp ERR! stack     at ChildProcess.emit (events.js:196:13)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:257:12)
gyp ERR! System Windows_NT 10.0.18362
gyp ERR! command "C:\\Program Files\\nodejs\\node.exe" "C:\\Users\\<User>\\AppData\\Roaming\\npm\\node_modules\\npm\\node_modules\\node-gyp\\bin\\node-gyp.js" "rebuild"
gyp ERR! cwd C:\Users\<User>\Desktop\Programming\rest.js\node_modules\@airbnb\node-memwatch
gyp ERR! node -v v12.2.0
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: [email protected] (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @airbnb/[email protected] install: `node-gyp rebuild`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @airbnb/[email protected] install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\<User>\AppData\Roaming\npm-cache\_logs\2019-05-25T23_11_48_315Z-debug.log

Node version support

Again, thank you for building this! It's much easier than trying to implement a leak test for one of my modules on my own.

What versions of Node.js do you intend to support? I'm testing on versions 4 and up, but destructuring is only supporting starting in v6, so prior versions currently encounter a syntax error at iterations.js line 2. Would you be willing to accept a PR that makes this work on v4?

Can not 'passing'?

const { iterate } = require('leakage')

describe('leakage', () => {
  it('does not leak when doing something', () => {
    iterate(() => {
      console.log('-')
    })
  })
})
 1) does not leak when doing something


  0 passing (3s)
  1 failing

  1) leakage
       does not leak when doing something:
     MemoryLeakError: Heap grew on 6 subsequent garbage collections (180 of 180 iterations) by 25.6 kB.

  Iterations between GCs: 30

  Final GC details:
  [    2.4 kB] [+  60x] [-   0x] Array
  [   1.68 kB] [+  30x] [-   0x] TickObject
  [    -464 B] [+  63x] [-  63x] Native

      at testConstantHeapSize (node_modules/[email protected]@leakage/lib/testConstantHeapSize.js:24:12)
      at iterate (node_modules/[email protected]@leakage/lib/index.js:64:21)
      at Context.it (test/leakage.spec.js:5:5)

How many iterations to do?

Hello Maintainers,
Please accept a hat-tip, love what I see thus far in the project. I have a question/comment regarding the wording here:

Nevertheless you can try to reduce the iteration count (the first parameter passed to iterate()). If you use 100 iterations as in the sample, try 30 iterations instead.

From past experience, generally profiling tests encourage having a sufficiently large iteration count, so this suggestion sounds a tad counter-intuitive. A lower iteration count may produce false-positives (unless there is a glaringly obvious leak that is caught easily). I believe this phrase should be removed.
What are your thoughts?

What Terminal are you using?

I just have to know, what terminal app is this screenshot from? Is this even a terminal - or just some text editor?

Promise support

I can use your module for a database related module
Do you see any drawback adding support for Promises, let me know what you think!

I'm already working on it, it should be ready by the end of the day

Extremely low performance on node.js 11

Leakage is incredibly slow on my system (with an Intel(R) Core(TM) i7-3610QM CPU).

Take the following minimal example:

$ cat leakage.js
const sinon = require('sinon');
const leakage = require('leakage');

it(`test`, function(){
        this.timeout( 1000000 );
        leakage.iterate( ()=>{} );
});

$ npx mocha leakage.js
(node:7249) ExperimentalWarning: queueMicrotask() is experimental.
  โœ“ test (26544ms)

  1 passing (27s)

The test time decreases from 26s to ~4.6s if I avoid including sinon. In my real tests, where I require a bunch of other modules besides sinon, testing a no-op function for leaks takes minutes.

Is this the expected behavior?
I don't think so, unless it's due to recent fixes for the latest v8, since several issues mention a suggested timeout of 5000ms.

I'm running node v11.10.0 and mocha 5.2.0 on a Linux 4.20.7 system.

Document best practices

Here is a draft for what the best practices should contain. Follow-up of #18.

Topics

No console.log() on non-primitive values in tests

As @mjhea0 pointed out, the console.* methods will keep a reference to any object/array/function you pass them, thus artificially introducing a memory leak. See http://stackoverflow.com/a/28859493/1799408.

Leakage tests are quite slow

... so think twice before running it together with your other tests everytime. Point out why they are quite slow (as in the FAQ/Timeouts). Ideally present alternatives.

Make test fail first, then fix it

Similar to other kinds of test it is also best practice to first check that your leakage test actually works, by purposely introducing a leak and watching the test fail. Remove the initial leak again and make sure the test passes.

(to be filled with additional content)

Testing code with promises

Hey,
I'm trying to test code that uses promises and always getting the MemoryLeakError.
Is there anything I'm doing wrong, or is that just how promises work?

here's my code:

const Leakage =  require('leakage');

it('leak test', async() => {
    let i = 0;
    jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
    expect.hasAssertions();

     await Leakage.iterate.async(async () => {
        let dummyPromise = new Promise((resolve) => {
           setTimeout(() => resolve('ok ' + ++i), 1);
        });
        
        try {
            const result = await dummyPromise;
            expect(result).toBeTruthy();
        } catch (e) {
            throw 'this should not happen ' + e;
        }
    });
})

and very minimalistic package.json

{
  "name": "promise-leak",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
      "test": "jest --runInBand"
  },
  "license": "MIT",
  "dependencies": {
    "jest": "^23.6.0",
    "leakage": "^0.4.0"
  }
}

Rewrite of the lib for reliable async testing ๐ŸŽ‰๐ŸŽ‰

Hey everyone ๐Ÿ™Œ

It took sooo long, but I finally came up with a solution for async testing that can be trusted!

In the branch rewrite there is an almost complete rewrite of the lib that supports async testing using iterate.async() and an improved readme.

Since it's such a big change I decided to open this issue as a replacement for the long-standing #7.

Highlights:

  • Async testing using iterate.async()
  • Changed API: iterate(iterations: number, iterator: Function) => iterate(iterator: Function, options: ?Object)
  • Dedicated tests to check the library's own heap footprint
  • Leaks have been successfully detected, non-leaky tests have not thrown, lib's footprint has been very small; repeatedly over multiple builds, locally as well as on travis

Left to do in the next few days:

  • Overhaul readme (not yet completely done)
  • Fix small maintenance issues

Will close this issue when the new version is released, which will we very soon ๐Ÿ˜‰

Stay tuned!

Testing / CI

There must be at least some integration test showing that the whole thing works and works for different node versions.

Correlates with #3 and will be successor of a proposed change in #1.

Cannot install on node 10.15.1

../src/heapdiff.cc:95:20: warning: 'Utf8Value' is deprecated [-Wdeprecated-declarations]
        String::Utf8Value utfString(str->ToString());
                          ^
/Users/krzysztofszostak/.node-gyp/10.15.1/include/node/v8.h:2891:5: note: 'Utf8Value' has been explicitly marked deprecated here
    V8_DEPRECATED("Use Isolate version",
    ^
/Users/krzysztofszostak/.node-gyp/10.15.1/include/node/v8config.h:327:29: note: expanded from macro 'V8_DEPRECATED'
  declarator __attribute__((deprecated))
                            ^
1 warning generated.
  CXX(target) Release/obj.target/memwatch/src/init.o
../src/init.cc:20:9: error: no type named 'AddGCEpilogueCallback' in 'v8::V8'; did you mean 'Nan::GCEpilogueCallback'?
        v8::V8::AddGCEpilogueCallback(memwatch::after_gc);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        Nan::GCEpilogueCallback
../../nan/nan.h:745:35: note: 'Nan::GCEpilogueCallback' declared here
  typedef v8::Isolate::GCCallback GCEpilogueCallback;
                                  ^
../src/init.cc:20:49: error: definition or redeclaration of 'after_gc' not allowed inside a function
        v8::V8::AddGCEpilogueCallback(memwatch::after_gc);
                                      ~~~~~~~~~~^
2 errors generated.
make: *** [Release/obj.target/memwatch/src/init.o] Error 1
gyp ERR! build error 

Custom Error

this https://github.com/andywer/leakage/blob/master/lib/leakErrorFactory.js#L26 should use a custom error

The custom Error should be exported as well so we can do that:

import { iterate, LeakageError } from 'leakage';

try {
  iterate(100, () => {
    throw new Error('random error')
  })
} catch (err) {
  if (err instanceof LeakageError) {
    // leaked detected
  }

  // something else happened
}

I use LeakageError but we need to find a better name ;)

Vue.js example code

We really require this plugin for Vue.js, but I don't know how to proceed.

Failed to install on Windows

I am trying to installing the package on Win10 and it does not have sys/time.h for @airbnb\node-memwatch to be built.

d:\<...>\node_modules\@airbnb\node-memwatch\src\memwatch.cc(19): fatal error C1083: ็„กๆณ•้–‹ๅ•ŸๅŒ…ๅซๆช”ๆกˆ: 'sys/time.h': No such file or directory [D:\<...>\node_modules\@airbnb\node-memwatch\bui
ld\memwatch.vcxproj]

Any alternatives or solution I can try for this?
Throw Windows away and embrace Ubuntu? ๐Ÿ˜‚ (OMG

Thanks for the nice project anyway!

Varying results for same test

I am running the following test multiple times in a row:

describe('leaks', function() {
  it('leak test', function() {
    this.timeout(100000)

    return Leakage.iterate.async(async () => {
      return myFunction()
    })
  })
})

Sometimes it passes and other times I get something like this:

MemoryLeakError: Heap grew on 6 subsequent garbage collections (180 of 180 iterations) by 175 kB.

  Iterations between GCs: 30

  Final GC details:
  [   19.4 kB] [+ 123x] [-  84x] Array
  [     14 kB] [+   5x] [-   1x] Code
  [       0 B] [+   1x] [-   1x] Immediate
  [       0 B] [+   2x] [-   2x] Native
  ... (15 more)

Should it consistently pass or fail rather than be intermittent? Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.