Coder Social home page Coder Social logo

facebook / memlab Goto Github PK

View Code? Open in Web Editor NEW
4.2K 39.0 109.0 22.94 MB

A framework for finding JavaScript memory leaks and analyzing heap snapshots

Home Page: https://facebook.github.io/memlab/

License: MIT License

JavaScript 26.58% TypeScript 72.29% HTML 0.07% CSS 0.41% MDX 0.65%
facebook javascript memory detector heap leak nodejs snapshot v8 e2e

memlab's Introduction

Licensed under the MIT License PR's Welcome npm version

memlab is an end-to-end testing and analysis framework for identifying JavaScript memory leaks and optimization opportunities.

Online Resources: [Website and Demo] | [Documentation] | [Meta Engineering Blog Post]

Features:

  • Browser memory leak detection - Write test scenarios with the Puppeteer API, and memlab will automatically compare JavaScript heap snapshots, filter out memory leaks, and aggregate the results
  • Object-oriented heap traversing API - Supports the creation of self-defined memory leak detector, and enables programmatic analysis JS heap snapshots taken from Chromium-based browsers, Node.js, Electron.js, and Hermes
  • Memory CLI toolbox - Built-in toolbox and APIs for finding memory optimization opportunities (not necessarily just memory leaks)
  • Memory assertions in Node.js - Enables unit tests or running node.js programs to take a heap snapshot of their own state, perform self memory checking, or write advanced memory assertions

CLI Usage

Install the CLI

npm install -g memlab

Find Memory Leaks

To find memory leaks in Google Maps, you can create a scenario file defining how to interact with the Google Maps, let's name it test-google-maps.js:

// initial page load url: Google Maps
function url() {
  return 'https://www.google.com/maps/@37.386427,-122.0428214,11z';
}

// action where we want to detect memory leaks: click the Hotels button
async function action(page) {
  // puppeteer page API
  await page.click('button[aria-label="Hotels"]');
}

// action where we want to go back to the step before: click clear search
async function back(page) {
  // puppeteer page API
  await page.click('[aria-label="Close"]');
}

module.exports = {action, back, url};

Now run memlab with the scenario file, memlab will interact with the web page and detect memory leaks with built-in leak detectors:

memlab run --scenario test-google-maps.js

memlab will print memory leak results showing one representative retainer trace for each cluster of leaked objects.

Retainer traces: This is the result from an example website, the retainer trace is an object reference chain from the GC root to a leaked object. The trace shows why and how a leaked object is still kept alive in memory. Breaking the reference chain means the leaked object will no longer be reachable from the GC root, and therefore can be garbage collected. By following the leak trace one step at a time, you will be able to find a reference that should be set to null (but it wasn't due to a bug).

MemLab found 46 leak(s)
--Similar leaks in this run: 4--
--Retained size of leaked objects: 8.3MB--
[Window] (native) @35847 [8.3MB]
  --20 (element)--->  [InternalNode] (native) @130981728 [8.3MB]
  --8 (element)--->  [InternalNode] (native) @130980288 [8.3MB]
  --1 (element)--->  [EventListener] (native) @131009888 [8.3MB]
  --1 (element)--->  [V8EventListener] (native) @224808192 [8.3MB]
  --1 (element)--->  [eventHandler] (closure) @168079 [8.3MB]
  --context (internal)--->  [<function scope>] (object) @181905 [8.3MB]
  --bigArray (variable)--->  [Array] (object) @182925 [8.3MB]
  --elements (internal)--->  [(object elements)] (array) @182929 [8.3MB]
...

To get readable trace, the web site under test needs to serve non-minified code (or at least minified code with readable variables, function name, and property names on objects).

Alternatively, you can debug the leak by loading the heap snapshot taken by memlab (saved in $(memlab get-default-work-dir)/data/cur) in Chrome DevTool and search for the leaked object ID (@182929).

View Retainer Trace Interactively

View memory issues detected by memlab based on a single JavaScript heap snapshot taken from Chromium, Hermes, memlab, or any node.js or Electron.js program:

memlab view-heap --snapshot <PATH TO .heapsnapshot FILE>

You can optionally specify a specific heap object with the object's id: --node-id @28173 to pinpoint a specific object.

heap-view

Self-defined leak detector: If you want to use a self-defined leak detector, add a leakFilter callback (doc) in the scenario file. leakFilter will be called for every unreleased heap object (node) allocated by the target interaction.

function leakFilter(node, heap) {
  // ... your leak detector logic
  // return true to mark the node as a memory leak
};

heap is the graph representation of the final JavaScript heap snapshot. For more details, view the doc site.

Heap Analysis and Investigation

View which object keeps growing in size during interaction in the previous run:

memlab analyze unbound-object

Analyze pre-fetched V8/hermes .heapsnapshot files:

memlab analyze unbound-object --snapshot-dir <DIR_OF_SNAPSHOT_FILES>

Use memlab analyze to view all built-in memory analyses. For extension, view the doc site.

View retainer trace of a particular object:

memlab trace --node-id <HEAP_OBJECT_ID>

Use memlab help to view all CLI commands.

APIs

Use the memlab npm package to start a E2E run in browser and detect memory leaks.

const memlab = require('memlab');

const scenario = {
    // initial page load url
    url: () => 'https://www.google.com/maps/@37.386427,-122.0428214,11z',

    // action where we want to detect memory leaks
    action: async (page) => await page.click('button[aria-label="Hotels"]'),

    // action where we want to go back to the step before
    back: async (page) => await page.click('[aria-label="Close"]'),
}
memlab.run({scenario});

Memory Assertions

memlab makes it possible to enable a unit test or running node.js program to take a heap snapshot of its own state, and write advanced memory assertions:

// save as example.test.ts
import type {IHeapSnapshot, Nullable} from '@memlab/core';
import {config, takeNodeMinimalHeap} from '@memlab/core';

class TestObject {
  public arr1 = [1, 2, 3];
  public arr2 = ['1', '2', '3'];
}

test('memory test with heap assertion', async () => {
  config.muteConsole = true; // no console output

  let obj: Nullable<TestObject> = new TestObject();
  // get a heap snapshot of the current program state
  let heap: IHeapSnapshot = await takeNodeMinimalHeap();

  // call some function that may add references to obj
  rabbitHole(obj)

  expect(heap.hasObjectWithClassName('TestObject')).toBe(true);
  obj = null;

  heap = await takeNodeMinimalHeap();
  // if rabbitHole does not have any side effect that
  // adds new references to obj, then obj can be GCed
  expect(heap.hasObjectWithClassName('TestObject')).toBe(false);

}, 30000);

For other APIs check out the API documentation.

Development

Use node version 16 or above. To build on Windows, please use Git Bash.

First build the project as follows:

npm install
npm run build

Then keep this helper script running to ensure that local changes are picked up and compiled automatically during development:

npm run dev

NOTE: To run the memlab cli locally, make sure to prefix the memlab command with npx from within the memlab repo e.g. npx memlab

Run tests:

npm run test

License

memlab is MIT licensed, as found in the LICENSE file.

Contributing

Check our contributing guide to learn about how to contribute to the project.

Code of Conduct

Check our Code Of Conduct to learn more about our contributor standards and expectations.

memlab's People

Contributors

arunsathiya avatar dependabot[bot] avatar designlook avatar drizzlecrj avatar eltociear avatar jacksongl avatar jongwooo avatar lucinyan avatar mrsharpoblunto avatar peter-gy avatar stoyan avatar tulga1970 avatar yarinba 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

memlab's Issues

How to use memlab in websites which requires login credentials

I am trying to use memlab in a website which accepts login credentials using ADFS. The first page should launch after entering the user name and password. How can this scenario be implemented using memlab?

Steps -

  1. Launch the application in Chrome
  2. Login to the application
  3. We land on the base page (take snapshot - s1)
  4. Search a patient
  5. Navigate to target page (take snapshot - s2)
  6. Navigate to base page (take snapshot - s3)

Browser is not supported error

Hi Team,

I am getting unsupported browser issue during initial launch, while trying to run memlab on an application. (Ran the script in headful mode).
Observed the same script is working in Puppeteer(stand alone version) for the same chromium version.
Chrome Version is 101.0.4950.0

Tried using below code in setup function but it did'nt help:
async function setup() {
const browser = await puppeteer.launch({ product: "firefox" });
const page = await browser.newPage();
}

Can you please confirm if there is any way to select a different browser via some code in script or in runtime while executing using CLI?

Below is the screenshot of error :
image

Error on console:
page-load(baseline)[s1] > action-on-page(target)[s2] > revert(final)[s3]
interaction fail, making 2 more attempt(s)...
interaction fail, making 1 more attempt(s)...
Use memlab help or memlab <COMMAND> -h to get helper text
interaction fail

How to use memlab in websites which requires login credentials

let theUrlAfterLogin;

async function beforeInitialPageLoad(page) {
await page.goto("https://XYZ");
// input login credentials and get the url after login is successful
await page.waitForSelector('input[id=userNameInput]');
await page.$eval('#userNameInput', el => el.value = 'tw\schuser1');
await page.waitForSelector('input[id=passwordInput]');
await page.$eval('#passwordInput', el => el.value = 'xxxxx');
await page.click('[id="submitButton"]');
theUrlAfterLogin = "https://XYZ" //URL is same as page.goto
}

function url() {
return theUrlAfterLogin;
}

// action where we want to detect memory leaks
async function action(page) {

}

// action where we want to go back to the step before
async function back(page) {

}

module.exports = {beforeInitialPageLoad, action, back, url};

MEMLAB Output

'command' is not recognized as an internal or external command,
operable program or batch file.
Cannot read property 'startsWith' of undefined
total time: 905ms
snapshot meta data invalid or missing

Discussion: Diagnosis accuracy & how to deal with hidden state, closures and more...

Hi memlab devs, I have a question on how memlab deals with the hidden state that cannot be captured by simply taking heap snapshots. As we know that, many native methods (implemented in C++) do not expose their state to JavaScript heap snapshots. For example Besides, the variables in closures cannot be easily diagnosed because of scoping. BLeak paper has a solution for this by rewriting the closure variables. https://github.com/plasma-umass/BLeak/blob/master/src/lib/closure_state_transform.ts As far as I go through the memlab codebase, I haven't seen the logic related to it. Will this issue affect the detection diagnosis accuracy? Is this kind of feature on the roadmap?

Heap data parsing failure

Sample code I am using:

const head = await takeNodeMinimalHeap();
console.log(head.getAnyObjectWithClassName('Player'))

Stack trace:

calculating basic meta info...
building reference index...
building referrers index...
propagating detachedness state...
building node index...
building location index...
building extra meta info...
identifying snapshot engine...

#
# Fatal error in , line 0
# Check failed: inference.RelyOnMapsViaStability(dependencies()).
#
#
#
#FailureMessage Object: 000000DABC5FDBE0
 1: 00007FF790AFACCF node_api_throw_syntax_error+175359
 2: 00007FF790A0EEAF std::basic_ios<char,std::char_traits<char> >::setstate+25487
 3: 00007FF791950FF2 V8_Fatal+162
 4: 00007FF791AE93B3 v8::internal::compiler::JSCallReducer::ReduceArrayBufferViewAccessor+1107
 5: 00007FF791AFBB41 v8::internal::compiler::JSCallReducer::ReduceJSCall+913
 6: 00007FF791AFAB63 v8::internal::compiler::JSCallReducer::ReduceJSCall+387
 7: 00007FF791AFA165 v8::internal::compiler::JSCallReducer::ReduceFunctionPrototypeCall+869
 8: 00007FF791AFB8E7 v8::internal::compiler::JSCallReducer::ReduceJSCall+311
 9: 00007FF791AFAB63 v8::internal::compiler::JSCallReducer::ReduceJSCall+387
10: 00007FF791AFAD49 v8::internal::compiler::JSCallReducer::ReduceJSCall+873
11: 00007FF791AE8D91 v8::internal::compiler::JSCallReducer::Reduce+113
12: 00007FF791A48537 v8::internal::compiler::Reducer::Reduce+39
13: 00007FF791A14E21 v8::internal::compiler::LiveRange::NextStart+10129
14: 00007FF791A48144 v8::internal::compiler::GraphReducer::Reduce+148
15: 00007FF791A487E6 v8::internal::compiler::GraphReducer::ReduceTop+358
16: 00007FF791A485D2 v8::internal::compiler::GraphReducer::ReduceNode+50
17: 00007FF791A19364 v8::internal::compiler::LiveRange::ResetCurrentHintPosition+6756
18: 00007FF791A09508 v8::internal::compiler::LoopPeeler::CanPeel+6472
19: 00007FF791A0AB15 v8::internal::compiler::LiveRange::End+2549
20: 00007FF791508191 v8::internal::OptimizedCompilationJob::ExecuteJob+49
21: 00007FF7914D6014 v8::internal::OptimizingCompileDispatcher::CompileNext+52
22: 00007FF7914D68C6 v8::internal::OptimizingCompileDispatcher::QueueForOptimization+726
23: 00007FF790A1190D X509_STORE_get_cleanup+813
24: 00007FF790B46E2D uv_poll_stop+237
25: 00007FF791C29960 inflateValidate+150576
26: 00007FFC212826BD BaseThreadInitThunk+29
27: 00007FFC2256DFB8 RtlUserThreadStart+40

setup callback is not invoked

Hi Team,

I am trying a scenario for an app which has login functionality. Used setup callback function which has login steps in the form of puppeteer script.

It's not working. Have updated the memlab version too.

Steps:
page-load(baseline)[s1] > action-on-page(target)[s2] > revert(final)[s3]
interaction fail, making 2 more attempt(s)...

Version:
[email protected]
@memlab/[email protected]
@memlab/[email protected]
@memlab/[email protected]
@memlab/[email protected]
@memlab/[email protected]

Can you please help on this issue?

More recent Chrome version / Apple Silicon support

Currently memlab is using a relatively old version of Puppeteer, which uses an old version of Chrome (101). It’s also running the Intel version on Apple Silicon macs, which likely is not helping performance when taking huge heap snapshots. (I’m writing this issue while waiting on a 1GB heap snapshot.)

Can this be upgraded to a more recent version?

how to position qiankun + vue2 leaked code?

hello, i used memlab to detect my project which is coded by Vue2 + qiankun, find some leaks.but trace info is almost complied code that variable's name is not similar to origin code . can u give me some help to solve the problem that is too difficult to detected leaked code's position in webapck service? thanks
mmexport1667874851257

oversized-object.js conclusion is incorrect

I run the command(memlab run --scenario ~/memlab/scenarios/oversized-object.js)and find the data and graphics are correct, but the conclusion is incorrect(No leaks found, MemLab found 0 leak(s)).

04

Can not found leaks! (not as expected)

I created a SPA. The code was written below.

import * as React from "react";
import { Routes, Route, Outlet, Link } from "react-router-dom";
window.cache = [];
export default function App() {
  return (
    <div>
      <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="about" element={<About />} />
        </Route>
      </Routes>
    </div>
  );
}

function Layout() {
  return (
    <div>
      <nav>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
        </ul>
      </nav>
      <hr />
      <Outlet />
    </div>
  );
}

function Home() {
  return (
    <div>
      <h2>Home</h2>
    </div>
  );
}

function About() {
  let cache = []
  for(let i = 0; i < 100000; i++) {
    cache.push({index: i});
  } 
  window.cache.push(cache);
  return (
    <div>
      <h2>About</h2>
    </div>
  );
}

scenario file:

function url() {
  return 'http://127.0.0.1:3000/';
}
async function action(page) {
  // puppeteer page API
  await page.click('a[href="/about"]');
}
async function back(page) {
  // puppeteer page API
  await page.click('a[href="/"]');
}
module.exports = {action, back, url};

image

I wonder how to define whether there are leaks, tks for your answer~

setup callback is not called even if provided

According to the documentation if you provide setup function it will be called before action callback is executed.

It does not seem to be working, consider the following example:

test.js:

function url() {
    return 'http://127.0.0.1:3000';
}

async function setup(page) {
    console.log('setup');
    throw new Error('setup');
}

async function action(page) {
    console.log('action');
    throw new Error('action');
}

async function back(page) {
    throw new Error('back');
}

module.exports = { action, back, setup, url };

executed with memlab run --headful --scenario test.js

result:

page-load[11.4MB](baseline)[s1] > action-on-page(target)[s2] > revert(final)[s3]
action
page-load[11.4MB](baseline)[s1] > action-on-page(target)[s2] > revert(final)[s3]
Use `memlab help` or `memlab <COMMAND> -h` to get helper text
interaction fail

as you can see, the setup callback was never called.

memlab version:

Discussion: How to run memlab on a test suite.

It's all in the title, we are running tests with Testing Library + MSW on Jest and with some legacy jest mocks. The pipeline looks a bit flaky on the CI and we'd like to rule out memory leaks by running memlab on the existing test suite as opposed to creating a scenario and re-write all the test base.

Is there any chance to achieve this with the current implementation? The get started doc seemed that you need to boot up the app and run scenario to find leaks by headlessly browsing the app as opposed to running existing tests.

Analyzing GCs

Is it possible to use this tool to analyze why minor/major GCs happen? i.e. it'd be useful to get some kind of list of objects that get allocated and GC'd between the page load and when the final snapshot happen, so that one can try to trim it.

Possibility to analyze memory leaks/usage in Jest

Hi Team,

First of all, Thank you very much for making this project open source, AFAIK there are very few opensource tools like memlab to analyze memory leaks in SPA apps, Hope this will be an excellent help for frontend developers to identify memory leaks in the Single Page Apps.

My question is, Is it possible to analyze the memory usage or leaks in tests written using the Jest framework?

Build failure on windows

Hello,

I was following the instructions at https://facebookincubator.github.io/memlab/docs/guides/guides-detached-dom and the npm run build command failed. I am running the command on the Windows 10 using Powershell.
Please see the command output.

`PS C:\code\memlab> npm run build

@memlab/[email protected] build C:\code\memlab
npm run clean > /dev/null && npm run build-pkg --workspaces

The system cannot find the path specified.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @memlab/[email protected] build: npm run clean > /dev/null && npm run build-pkg --workspaces
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @memlab/[email protected] build 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\pvaddepa\AppData\Roaming\npm-cache_logs\2022-09-12T20_21_26_708Z-debug.log`

The command npm run clean > /dev/null fails as below:
`PS C:\code\memlab> npm run clean > /dev/null
out-file : Could not find a part of the path 'C:\dev\null'.
At line:1 char:1

  • npm run clean > /dev/null
  •   + CategoryInfo          : OpenError: (:) [Out-File], DirectoryNotFoundException
      + FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand`
    
    

When I ran npm run clean I could see that there was an error message asking to upgrade node version to 16+.

`PS C:\code\memlab> npm run clean

@memlab/[email protected] clean C:\code\memlab
npm run clean-pkg --workspaces && rm -rf ./website/build

@memlab/[email protected] clean-pkg C:\code\memlab
npm run error

@memlab/[email protected] error C:\code\memlab
echo 'upgrade node.js to version 16.0.0 or above. Consider using nvm.'; exit 1

'upgrade node.js to version 16.0.0 or above. Consider using nvm.'; exit 1
'rm' is not recognized as an internal or external command,
operable program or batch file.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @memlab/[email protected] clean: npm run clean-pkg --workspaces && rm -rf ./website/build
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @memlab/[email protected] clean script.

npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\pvaddepa\AppData\Roaming\npm-cache_logs\2022-09-12T20_26_27_043Z-debug.log`

After removing "/dev/null", I see below error as rm is being used to delete the directorys:
`PS C:\code\memlab> npm run clean

@memlab/[email protected] clean
npm run clean-pkg --workspaces && rm -rf ./website/build

@memlab/[email protected] clean-pkg
rm -rf ./dist && rm -rf ./node_modules && rm -f ./tsconfig.tsbuildinfo

'rm' is not recognized as an internal or external command,`

I had to switch to Git Bash to run the commands and I get below error.

`$ npm run build

@memlab/[email protected] build
npm run clean > /dev/null && npm run build-pkg --workspaces

The system cannot find the path specified.
`

I removed "> /dev/null" to make it work.

So, the issues I see is

  1. The documentation should be updated to mention the minimum node.js version and on Windows the command should be run using Git Bash.
  2. The Unix style redirection of stdout does not work on Powershell.
  3. The stdout should not be suppressed (> /dev/null).

Enhancements,

  1. It would be nice to add .nvmrc file in the repository.
  2. Support for running in Powershell.

Not Headless and more examples

As a performance, I do not have experience w/ Puppeteer. But I would like to heavily use to troubleshoot the client-side performance using Memlab.

  1. How do we run with non-headless mode? If I try with headless: false (from Puppeteer doc), it is not working.
  2. It would be great, if we need more examples on the automation front.

Thanks.

[Question] Can page reload , page refresh scenarios be handled using memlab?

Can we handle page reload or page refresh scenarios using memlab?
I am getting below error while running scenario:
The page is reloaded. MemLab cannot analyze heap across page reloads. Please remove window.reload() calls, page.goto() calls, or any reload logic.

I am testing login in and going back scenario.

How to run beforeInitialPageLoad conditionally?

I can run the login logic in beforeInitialPageLoad but the session is maintained in the subsequent runs. Is there any way to either:

  • Run beforeInitiaPageLoad only when the session is not found plus how to check if the user already has a session.

OR

  • Clear the session so that beforeInitialPageLoad can work without interruption (i.e. interaction fail error due to email/password fields not found on the page as the user is not redirected to a login screen).

Thanks for this wonderful project. Loving it so far.

How to go back to home using while loop

Hi 👋
I am executing Memlab on a website locally, where the home logo's click reloads the page and goes to home URL. But since Memlab does not support reload, I figured the only way I can go back to home (the initial URL), is to page.goBack while URL !== [home URL]:

Screen Shot 2022-12-07 at 11 48 16 AM

but it runs into this error:
Screen Shot 2022-12-07 at 11 49 31 AM

I also tried this way
Screen Shot 2022-12-07 at 11 42 36 AM

but same result :/
Would apprictae any guidance/help in this regard.
Cheers! ✌️

[#64] Q & A continue

          Hi @JacksonGL , Thanks for the updates. 

I tried following the above suggestions, In my case in the memlab logs I don't see any variable (leakedObjects from the https://facebook.github.io/memlab/docs/guides/guides-detached-dom , for example). I only see random numbers being shown and from this I'm not able to narrow down to the code that causes the leaks. I also used memlab view-heap --node-id <LEAKED_OBJECT_ID> and was not able to get any useful informations.

While using Chrome Dev tools memory profile, I'm able to see a link to the code that causes the leak. Can you guide me here? Not sure if I've missed something while using memlab.

I actually want to get the same info (links to the code that causes the leaks) that Chrome dev tools memory profile gives.

Memlab logs from my application:-
MemLab found 7 leak(s)

--Similar leaks in this run: 13--
--Retained size of leaked objects: 105.4KB--
[] (synthetic) @1 [5.3MB]
--2 (shortcut)---> [Window / http://localhost:8000] (object) @9823 [48.6KB]
--require (property)---> [requirejs] (closure) @69911 [3.1KB]
--s (property)---> [Object] (object) @69913 [212 bytes]
--contexts (property)---> [Object] (object) @54171 [96 bytes]
--_ (property)---> [Object] (object) @54173 [2.3KB]
--defined (property)---> [Object] (object) @53929 [37.8KB]
--preact/compat (property)---> [Object] (object) @54089 [9.3KB]
--useTransition (property)---> [K] (closure) @137041 [68 bytes]
--context (internal)---> [] (object) @122325 [116.3KB]
--U (variable)---> [p] (object) @227695 [107.6KB]
--__P (property)---> [Detached HTMLElement] (native) @212991 [105.4KB]
--11 (element)---> [Detached InternalNode] (native) @262836032 [228 bytes]
--2 (element)---> [Detached DOMTokenList] (native) @213075 [84 bytes]

--Similar leaks in this run: 32--
--Retained size of leaked objects: 5.4KB--
[Window] (native) @41625 [52.8KB]
--4 (element)---> [HTMLDocument] (native) @41611 [10.7KB]
--jQuery361067752674121521841 (property)---> [Object] (object) @79307 [836 bytes]
--events (property)---> [Object] (object) @130275 [584 bytes]
--touchcancel (property)---> [Array] (object) @130523 [180 bytes]
--0 (element)---> [Object] (object) @130539 [68 bytes]
--handler (property)---> [native_bind] (closure) @130503 [136 bytes]
--bound_this (internal)---> [constructor] (object) @129375 [8.9KB]
--m_preActiveItem (property)---> [jQuery.fn.init] (object) @242125 [192 bytes]
--0 (element)---> [Detached HTMLLIElement] (native) @212863 [1.3KB]
--8 (element)---> [Detached HTMLAnchorElement] (native) @213121 [1.2KB]
--5 (element)---> [Detached DOMTokenList] (native) @274847456 [56 bytes]

--Similar leaks in this run: 3--
--Retained size of leaked objects: 292 bytes--
[(GC roots)] (synthetic) @3 [598.2KB]
--13 (element)---> [(Global handles)] (synthetic) @29 [1KB]
--10 / DevTools console (internal)---> [Detached HTMLSpanElement] (native) @40985 [688 bytes]
--3 (element)---> [Detached InternalNode] (native) @75016864 [548 bytes]
--6 (element)---> [Detached InternalNode] (native) @75057344 [232 bytes]
--1 (element)---> [Detached NodeList] (native) @213115 [84 bytes]

--Similar leaks in this run: 1--
--Retained size of leaked objects: 272 bytes--
[] (synthetic) @1 [5.3MB]
--2 (shortcut)---> [Window / http://localhost:8000] (object) @9823 [48.6KB]
--oj (property)---> [Object] (object) @53949 [5.8KB]
--CompositeTemplateRenderer (property)---> [Object] (object) @99535 [836 bytes]
--_BINDING_CONTEXT (property)---> [ko.bindingContext] (object) @131325 [68 bytes]
--ko (property)---> [Object] (object) @41837 [38.8KB]
--tasks (property)---> [Object] (object) @123809 [1.4KB]
--scheduler (property)---> [] (closure) @192441 [548 bytes]
--context (internal)---> [] (object) @192757 [480 bytes]
--div (variable)---> [Detached HTMLDivElement] (native) @41691 [412 bytes]
--3 (element)---> [Detached InternalNode] (native) @274838656 [272 bytes]
--1 (element)---> [Detached InternalNode] (native) @274900896 [272 bytes]
--1 (element)---> [Detached InternalNode] (native) @274849216 [272 bytes]
--1 (element)---> [Detached MutationObserverRegistration] (native) @262804992 [272 bytes]

--Similar leaks in this run: 3--
--Retained size of leaked objects: 248 bytes--
[Window] (native) @41625 [52.8KB]
--4 (element)---> [HTMLDocument] (native) @41611 [10.7KB]
--10 (element)---> [Range] (native) @56703552 [96 bytes]
--2 (element)---> [HTMLUListElement] (native) @41341 [6.3KB]
--8 (element)---> [HTMLDivElement] (native) @41373 [472 bytes]
--6 (element)---> [HTMLDivElement] (native) @41345 [1.3KB]
--7 (element)---> [HTMLElement] (native) @41083 [14.4KB]
--13 (element)---> [HTMLDivElement] (native) @41085 [664 bytes]
--9 (element)---> [HTMLDivElement] (native) @41567 [752 bytes]
--8 (element)---> [Text] (native) @41031 [124 bytes]
--4 (element)---> [HTMLElement] (native) @41039 [700 bytes]
--6 (element)---> [Text] (native) @41383 [124 bytes]
--4 (element)---> [HTMLDivElement] (native) @41381 [456 bytes]
--7 (element)---> [Text] (native) @41421 [124 bytes]
--4 (element)---> [HTMLUListElement] (native) @41419 [452 bytes]
--6 (element)---> [Text] (native) @41441 [124 bytes]
--4 (element)---> [Comment] (native) @41121 [124 bytes]
--5 (element)---> [Comment] (native) @41125 [3.8KB]
--_templateNode (property)---> [Detached HTMLTemplateElement] (native) @41115 [1.7KB]
--6 (element)---> [Detached DocumentFragment] (native) @41135 [1.1KB]
--5 (element)---> [Detached Text] (native) @75038304 [96 bytes]
--2 (element)---> [Detached HTMLLIElement] (native) @74974464 [808 bytes]
--2 (element)---> [Detached Text] (native) @74994624 [96 bytes]
--2 (element)---> [Detached HTMLAnchorElement] (native) @75012704 [504 bytes]
--1 (element)---> [Detached DOMTokenList] (native) @274834336 [56 bytes]

--Similar leaks in this run: 1--
--Retained size of leaked objects: 56 bytes--
[Window] (native) @41625 [52.8KB]
--4 (element)---> [HTMLDocument] (native) @41611 [10.7KB]
--12 (element)---> [HTMLHtmlElement] (native) @41515 [7.4KB]
--6 (element)---> [HTMLBodyElement] (native) @41589 [312 bytes]
--5 (element)---> [Text] (native) @75010304 [96 bytes]
--2 (element)---> [Comment] (native) @75009984 [96 bytes]
--3 (element)---> [Text] (native) @262852832 [96 bytes]
--3 (element)---> [HTMLScriptElement] (native) @40819 [3KB]
--__ko__1679020612580 (property)---> [Object] (object) @131615 [2.5KB]
--4__ko__1679020612580 (property)---> [Object] (object) @131629 [2.4KB]
--containerData (property)---> [Detached HTMLDivElement] (native) @40829 [2.2KB]
--4 (element)---> [Detached Text] (native) @40825 [124 bytes]
--4 (element)---> [Detached HTMLLIElement] (native) @40823 [1.8KB]
--3 (element)---> [Detached HTMLAnchorElement] (native) @40835 [372 bytes]
--3 (element)---> [Detached DOMTokenList] (native) @274896416 [56 bytes]

--Similar leaks in this run: 1--
--Retained size of leaked objects: 48 bytes--
[] (synthetic) @1 [5.3MB]
--2 (shortcut)---> [Window / http://localhost:8000] (object) @9823 [48.6KB]
--$ (property)---> [jQuery] (closure) @50567 [26.1KB]
--support (property)---> [Object] (object) @93481 [1.2KB]
--reliableTrDimensions (property)---> [reliableTrDimensions] (closure) @192215 [68 bytes]
--context (internal)---> [] (object) @193043 [640 bytes]
--div (variable)---> [Detached HTMLDivElement] (native) @41553 [188 bytes]
--3 (element)---> [Detached InternalNode] (native) @262884032 [48 bytes]
--1 (element)---> [Detached CSSStyleDeclaration] (native) @274845856 [48 bytes]

Originally posted by @MaheshDevaraj in #64 (comment)

interaction fail error

Hi, I am just follow the tutorial and even youtube one fails with below

page-load(baseline)[s1] > action-on-page(target)[s2] > revert(final)[s3]  
interaction fail, making 2 more attempt(s)...
interaction fail, making 1 more attempt(s)...
Use `memlab help` or `memlab <COMMAND> -h` to get helper text
interaction fail
onur@Onurs-MBP scenarios % 

Error seems cryptic and I don't understand what's wrong.

Discussion: Is it possible to add return value to heap-analysis plugins?

Hi team,

Thanks for a lot for let this amazing project open source!

I am from the Lark desktop Team of ByteDance, and we would like to use memlab to help us find memory leaks.

In our scenario, Lark is a desktop app, not a SPA webpage, we can't use memlab to start a puppeteer and run the e2e test, and the cost to rewrite all of out test cases using memlab is too high to afford.

Thus we want to use our existing e2e framwork to take snapshot, and pass it to a node server running memlab to analyse it.

Here is a minimal demo running memlab at a node server
https://github.com/MoonShadowV/memlab-demo-server

It works fine for findLeaks as it dumps the result to the disk => /data/out/leaks.txt

import { findLeaks, BrowserInteractionResultReader } from '@memlab/api';
import { MEMLAB_WORKDIR } from './constant';
async function memlabFindLeak() {
  try {
    const reader = BrowserInteractionResultReader.from(MEMLAB_WORKDIR);
    const leaks = await findLeaks(reader);
    return Boolean(leaks);
  } catch (error) {
    throw new Error(`memlab analyse failed: ${error}`);
  }
}

export { memlabFindLeak };

But if we want to analyse single snapshot, the heap-analysis plugin return nothing, just print it to the termianl. We can't get the output.

eg: https://github.com/facebook/memlab/blob/main/packages/heap-analysis/src/plugins/DetachedDOMElementAnalysis.ts#L50
In this plugin, the process function just call

  async process(options: HeapAnalysisOptions): Promise<void> {
    const snapshot = await pluginUtils.loadHeapSnapshot(options);
    this.detachedElements = pluginUtils.filterOutLargestObjects(
      snapshot,
      utils.isDetachedDOMNode,
    );
    pluginUtils.printNodeListInTerminal(this.detachedElements); // print result to terminal, return nothing
  }

to print the detachedElements in the terminal.

we can not access it from outside. ↓

(async function () {
  const analyse = new DetachedDOMElementAnalysis();
  await analyse.analyzeSnapshotFromFile(`./s2.heapsnapshot`); // just return null
})();

Is it possible to add return value to heap-analysis plugins so that we can to access it's result in the code?

(async function () {
  const analyse = new DetachedDOMElementAnalysis();
  // get the detachedeElements
  const result = await analyse.analyzeSnapshotFromFile(`./s2.heapsnapshot`); 
})();

Discussion: How to map traced leaks to React components, or HTML DOM path

Hi memlab team,
Is there any tricks / instruction for mapping console output to react code / html components?
I did some homework by reading https://facebook.github.io/memlab/docs/guides/guides-detached-dom
However the case I met is not as straightforward as shown in the tutorial.

Detailed: Questions:

  1. In addition to [window], I also see [(GC roots)], [ < synthetic > ] . How to interpret these kinds?
  2. How should I translated the below output to a HTML path? In the tutorial, it seems there is --leaked_objects, I don't see this tag in the below output.
--Similar leaks in this run: 1--
--Retained size of leaked objects: 192 bytes--
[Window] (native) @263277 [6.8KB]
  --4 (element)--->  [HTMLDocument] (native) @263275 [21.5KB]
  --12 (element)--->  [Range] (native) @2340276992 [96 bytes]
  --3 (element)--->  [HTMLDivElement] (native) @262245 [140 bytes]
  --4 (element)--->  [HTMLDivElement] (native) @262385 [228 bytes]
  --3 (element)--->  [HTMLDivElement] (native) @262251 [704 bytes]
  --__reactInternalInstance$vcd965b8sak (property)--->  [FiberNode div HostComponent] (object) @1608501 [304 bytes]
  --child (property)--->  [FiberNode ClassComponent] (object) @1608615 [220 bytes]
  --_debugOwner (property)--->  [FiberNode FunctionComponent] (object) @1373349 [3.2KB]
  --return (property)--->  [FiberNode Modal FunctionComponent] (object) @1373295 [1KB]
  --pendingProps (property)--->  [Object] (object) @1239415 [60 bytes]
  --children (property)--->  [Object] (object) @1239305 [388 bytes]
  --_owner (property)--->  [FiberNode FunctionComponent] (object) @1252139 [192 bytes]
  --_debugOwner (property)--->  [FiberNode FunctionComponent] (object) @1235961 [192 bytes]
  --firstEffect (property)--->  [Detached FiberNode render ForwardRef] (object) @2894599 [192 bytes]

Thanks a lot.

Here is the whole console output:

page-load [75.7MB] (baseline) [s1] > action-on-page [69.4MB] (target) [s2] > revert [67.5MB] (final) [s3]  
------6 clusters------

--Similar leaks in this run: 1182--
--Retained size of leaked objects: 781.1KB--
[(GC roots)] (synthetic) @3 [819.1KB]
  --13 (element)--->  [(Global handles)] (synthetic) @29 [10.9KB]
  --37 / DevTools console (internal)--->  [Detached HTMLSpanElement] (native) @2875845 [781.1KB]
  --6 (element)--->  [Detached HTMLDivElement] (native) @2875843 [160 bytes]
  --8 (element)--->  [Detached HTMLDivElement] (native) @2875841 [160 bytes]
  --9 (element)--->  [Detached HTMLDivElement] (native) @2875709 [160 bytes]
  --6 (element)--->  [Detached HTMLFormElement] (native) @2875701 [792 bytes]
  --7 (element)--->  [Detached InternalNode] (native) @2347418720 [0 byte]
  --8 (element)--->  [Detached HTMLInputElement] (native) @2875595 [1.1KB]
  --16 (element)--->  [Detached InternalNode] (native) @2340601344 [328 bytes]
  --1 (element)--->  [Detached ShadowRoot] (native) @2340276032 [328 bytes]

--Similar leaks in this run: 2--
--Retained size of leaked objects: 352 bytes--
[<synthetic>] (synthetic) @1 [72.2MB]
  --2 (shortcut)--->  [Window / http://localhost:3000/] (object) @9821 [28.5KB]
  --FullCalendarVDom (property)--->  [Object] (object) @1017785 [544 bytes]
  --unmountComponentAtNode (property)--->  [unmountComponentAtNode] (closure) @887173 [68 bytes]
  --context (internal)--->  [<function scope>] (object) @886495 [181KB]
  --currentFiber (variable)--->  [FiberNode HostRoot] (object) @2936353 [960 bytes]
  --firstEffect (property)--->  [Detached FiberNode render MemoComponent] (object) @2936359 [192 bytes]

--Similar leaks in this run: 2--
--Retained size of leaked objects: 192 bytes--
[<synthetic>] (synthetic) @1 [72.2MB]
  --2 (shortcut)--->  [Window / http://localhost:3000/] (object) @9821 [28.5KB]
  --asap (property)--->  [asap] (closure) @981181 [760 bytes]
  --context (internal)--->  [<function scope>] (object) @981185 [624 bytes]
  --microtask (variable)--->  [<closure>] (closure) @2461961 [536 bytes]
  --context (internal)--->  [<function scope>] (object) @838179 [504 bytes]
  --node (variable)--->  [Detached Text] (native) @263405 [396 bytes]
  --3 (element)--->  [Detached InternalNode] (native) @2347375360 [272 bytes]
  --1 (element)--->  [Detached InternalNode] (native) @2340565184 [272 bytes]
  --1 (element)--->  [Detached InternalNode] (native) @2340565344 [272 bytes]
  --1 (element)--->  [Detached MutationObserverRegistration] (native) @2340565504 [272 bytes]
  --1 (element)--->  [Detached MutationObserver] (native) @1005061568 [192 bytes]
  --1 (element)--->  [Detached MutationObserver::Delegate] (native) @1005061408 [80 bytes]

--Similar leaks in this run: 2--
--Retained size of leaked objects: 192 bytes--
[<synthetic>] (synthetic) @1 [72.2MB]
  --2 (shortcut)--->  [Window / http://localhost:3000/] (object) @9821 [28.5KB]
  --Observable (property)--->  [Observable] (closure) @981213 [3.3KB]
  --context (internal)--->  [<function scope>] (object) @981227 [2KB]
  --microtask (variable)--->  [<closure>] (closure) @2461925 [536 bytes]
  --context (internal)--->  [<function scope>] (object) @838181 [504 bytes]
  --node (variable)--->  [Detached Text] (native) @263411 [396 bytes]
  --3 (element)--->  [Detached InternalNode] (native) @2347375680 [272 bytes]
  --1 (element)--->  [Detached InternalNode] (native) @2340839104 [272 bytes]
  --1 (element)--->  [Detached InternalNode] (native) @2340560864 [272 bytes]
  --1 (element)--->  [Detached MutationObserverRegistration] (native) @2340560704 [272 bytes]
  --1 (element)--->  [Detached MutationObserver] (native) @1005062048 [192 bytes]
  --1 (element)--->  [Detached MutationObserver::Delegate] (native) @1005061888 [80 bytes]

--Similar leaks in this run: 1--
--Retained size of leaked objects: 192 bytes--
[Window] (native) @263277 [6.8KB]
  --4 (element)--->  [HTMLDocument] (native) @263275 [21.5KB]
  --12 (element)--->  [Range] (native) @2340276992 [96 bytes]
  --3 (element)--->  [HTMLDivElement] (native) @262245 [140 bytes]
  --4 (element)--->  [HTMLDivElement] (native) @262385 [228 bytes]
  --3 (element)--->  [HTMLDivElement] (native) @262251 [704 bytes]
  --__reactInternalInstance$vcd965b8sak (property)--->  [FiberNode div HostComponent] (object) @1608501 [304 bytes]
  --child (property)--->  [FiberNode ClassComponent] (object) @1608615 [220 bytes]
  --_debugOwner (property)--->  [FiberNode FunctionComponent] (object) @1373349 [3.2KB]
  --return (property)--->  [FiberNode Modal FunctionComponent] (object) @1373295 [1KB]
  --pendingProps (property)--->  [Object] (object) @1239415 [60 bytes]
  --children (property)--->  [Object] (object) @1239305 [388 bytes]
  --_owner (property)--->  [FiberNode FunctionComponent] (object) @1252139 [192 bytes]
  --_debugOwner (property)--->  [FiberNode FunctionComponent] (object) @1235961 [192 bytes]
  --firstEffect (property)--->  [Detached FiberNode render ForwardRef] (object) @2894599 [192 bytes]

--Similar leaks in this run: 1--
--Retained size of leaked objects: 48 bytes--
[Window] (native) @263277 [6.8KB]
  --4 (element)--->  [HTMLDocument] (native) @263275 [21.5KB]
  --jQuery360089431674213560691 (property)--->  [Object] (object) @1021743 [2.5KB]
  --handle (property)--->  [<closure>] (closure) @263137 [52 bytes]
  --context (internal)--->  [<function scope>] (object) @1577477 [20 bytes]
  --previous (internal)--->  [<function scope>] (object) @320449 [18.5KB]
  --support (variable)--->  [Object] (object) @837833 [1.2KB]
  --reliableTrDimensions (property)--->  [reliableTrDimensions] (closure) @1368867 [68 bytes]
  --context (internal)--->  [<function scope>] (object) @2206413 [640 bytes]
  --div (variable)--->  [Detached HTMLDivElement] (native) @263591 [188 bytes]
  --3 (element)--->  [Detached InternalNode] (native) @995444160 [48 bytes]
  --1 (element)--->  [Detached CSSStyleDeclaration] (native) @995444000 [48 bytes]

Support for cypress

Hello,

I would like to know if memlab can work with Cypress. We have a large test suite of Cypress based tests, it would be nice to use memlabs with those tests rather than creating new ones with pupeteer.

"snapshot for the following tabs are missing:" error on find-leaks

memlab find-leaks --baseline snapshots/Heap-baseline.heapsnapshot --target snapshots/Heap-target.heapsnapshot --final snapshots/Heap-final.heapsnapshot --snapshot-dir snapshots --work-dir workdir

Error: snapshot for the following tabs are missing:{
"1": {
"name": "baseline",
"url": "",
"type": "baseline"
},
"2": {
"name": "target",
"url": "",
"type": "target"
},
"3": {
"name": "final",
"url": "",
"type": "final"
}
}
The error is
Use memlab help or memlab <COMMAND> -h to get helper text
snapshot for the following tabs are missing:
baseline
target
final

Invalid Scenario file: Xvfb supports: false

I wrote a basic scenario file that looks the same as the Getting Started Guide.

Here is my scenario.js which just clicks two buttons that should trigger mount/unmount:

function url() {
  return 'http://myAppRunningLocally';
}

async function action(page) {
  return await page.click('button[aria-label="Good"]');
}

async function back(page) {
  return await page.click('button[aria-label="Bad"]');
}

const s = { url, action, back };

module.exports = s;

Here is the entire output when run with command memlab run --scenario ./src/memlab/scenario.js -v:

Xvfb supports: false
Invalid scenario file: ./src/memlab/scenario.js
Error: Invalid scenario file: ./src/memlab/scenario.js
    at convertToError (~/.nvm/versions/node/v16.14.2/lib/node_modules/memlab/node_modules/@memlab/core/dist/lib/Utils.js:1670:16)
    at getError (~/.nvm/versions/node/v16.14.2/lib/node_modules/memlab/node_modules/@memlab/core/dist/lib/Utils.js:1662:12)
    at haltOrThrow (~/.nvm/versions/node/v16.14.2/lib/node_modules/memlab/node_modules/@memlab/core/dist/lib/Utils.js:1595:17)
    at Object.loadScenario (~/.nvm/versions/node/v16.14.2/lib/node_modules/memlab/node_modules/@memlab/core/dist/lib/Utils.js:533:15)
    at ScenarioFileOption.<anonymous> (~/.nvm/versions/node/v16.14.2/lib/node_modules/memlab/node_modules/@memlab/cli/dist/options/ScenarioFileOption.js:38:48)
    at Generator.next (<anonymous>)
    at ~/.nvm/versions/node/v16.14.2/lib/node_modules/memlab/node_modules/@memlab/cli/dist/options/ScenarioFileOption.js:17:71
    at new Promise (<anonymous>)
    at __awaiter (~/.nvm/versions/node/v16.14.2/lib/node_modules/memlab/node_modules/@memlab/cli/dist/options/ScenarioFileOption.js:13:12)
    at ScenarioFileOption.parse (/~.nvm/versions/node/v16.14.2/lib/node_modules/memlab/node_modules/@memlab/cli/dist/options/ScenarioFileOption.js:34:16)

Steps to repeat:

  1. run npm install -g memlab with npm v8.8.0
  2. create scenario file pointing at locally running app for the url function
  3. made sure app is running locally
  4. run command memlab run --scenario ./src/memlab/scenario.js -v from project directory
  5. get invalid scenario file error

OS: Mac with M1

I have also tracked down the function that is throwing in Utils.js -> checkScenarioInstance, verified I had what is expected.
I copied the code into the scenario file and ran it with node just to check if I was missing something. It looked like this below the function definitions:

const s = { url, action, back };

// module.exports = s;

console.log(typeof s);
console.log(typeof url);
console.log(typeof action);
console.log(typeof back);

if (
  typeof s !== 'object' ||
  typeof s.url !== 'function' ||
  (s.action && typeof s.action !== 'function') ||
  (s.back && typeof s.back !== 'function') ||
  (s.repeat && typeof s.repeat !== 'function') ||
  (s.isPageLoaded && typeof s.isPageLoaded !== 'function') ||
  (s.leakFilter && typeof s.leakFilter !== 'function') ||
  (s.beforeLeakFilter && typeof s.beforeLeakFilter !== 'function')
) {
  throw new Error('Invalid senario'); 
}

this was run with node ./src/memlab/scenario.js on node v16.14.2. It did not throw and the output was

object
function
function
function

as expected.

The last thing is the Xvfb supports: false log. This is a dependency of memlab that should come with the installation?

If I missed a step, please let me know. Any idea why this would throw when run with memlab? Is it an installation failure?

memlab not navigating pages

When I run the below scenario, the script is not navigating to any page clicks. It is just taking snapshots from the home page.

function url() {
  console.log("Returning URL");
  return 'https://store.qainsights.com/';
}

async function accessories(page) {
  console.log("Click on Accessories link");
  await page.click('//a[href="/accessories"]');
}

async function apparel(page) {
  console.log("Click on Apparel link");
  await page.click('//a[href="/apparel"]');
}

async function back(page) {
  console.log("Go to Home page");
  await page.click('//a[href="/"]');
}

module.exports = {accessories, apparel, back, url};

memlab version

memlab run --scenario results in a bad state

I am running the tool following the documentation, but the runs ended in a bad state - retried a few times but still same results.

$ memlab run --scenario dv-report-explorer.js
'command' is not recognized as an internal or external command,
operable program or batch file.
page-load[35.9MB](baseline)[s1] > action-on-page[34.5MB](target)[s2] > revert[34.7MB](final)[s3]

total time: 1.9min
Memory usage across all steps:
LIBERTY LIBERTY LIBERTY
LIBERTY LIBERTY LIBERTY
LIBERTY LIBERTY LIBERTY
                                   !             H|H|H|H|H           H__________________________________             H|§|§|§|H           H|* * * * * *|---------------------|             H|§|∞|§|H           H| * * * * * |---------------------|             H|§|§|§|H           H|* * * * * *|---------------------|             H|H|H|H|H           H| * * * * * |---------------------|             H|H|H|H|H           H|---------------------------------|          ===============        H|---------------------------------|            /| _   _ |          H|---------------------------------|            (| O   O |)          H|---------------------------------|            /|   U   |          H-----------------------------------             |  =/  |           H              _..._/            H              _|I/|_            H      _______/| H |/_______    H     /           / /          H    |          | | /         |  H    |          ||o||          |  H    |    |     ||o||     |    |  H    |    |     ||o||     |    |  H   Carl Pilcher
test ing testi ng tes ting te sti ng test ing t estin g testin

OS: Windows 10.0.19044
Node: 16.14.0

Login functionality

Hey there! Great tool, would love to give this a try, and was curious what would be the best approach to invoke a login process.

Found here the IScenario, and I'm curious if the only strategy here is the Cookie setup.

MemLab cannot analyze heap across page reloads

I'm trying to use Memlab on my app that is login protected. The login process causes a redirect to the login page and then redirects back to my app after a successful login. This causes the error "The page is reloaded. MemLab cannot analyze heap across page reloads. Please remove window.reload() calls or disable any reload logic."

Are there any plans to enable Memlab to work with apps that have this login flow?

Memory snapshots analysis

Hi,

I'm trying to use memlab for leak detection, once I run the script I get three snapshots and also some logs on the terminal. Tried to understand what they mean but was not able to. I'm actually trying to drill down on a leak and get to the code that causes the leak. Is there a way to analyse the heaps better and get to the exact code that causes the leaks?

Thanks,
Mahesh

Discussion: How memlab supports NodeJS memory leak detection

Hi memlab devs, thanks for the great tool. I'm excited to try to integrate it into my current NodeJS app tests. I'm wondering what's current status or future road map is on supporting NodeJS memory leak detection.

Currently, most test cases and demos are running in browsers detecting front-end side memory leaks. Would you plan to give more support on NodeJS?

[Guide] Integration with existing e2e framework

Hello! This project looks amazing and is something our team at NASA Open MCT would love to used within our nascent performance testing suite.

I've seen a few requests for integration #15 and #14.

We're leveraging the playwright/test package which is nearly identical to jest+puppeteer in terms of capabilities. The only missing API from puppeteer is microsoft/playwright#14134 , but otherwise has API parity and should be interchangeable.

I was wondering if you could provide a guide on how to integrate memlab into our existing playwright/test framework. I could help keep it up to date by integrating these two projects as part of our CI pipeline. I'd just like to know about how we should go about getting started using some of your core APIs. Thanks!

A glance into the exact way heap size is determined

I would like to better understand how heap size is determined exactly

For instance, when using performance.memory api in chrome, under the name of usedJSHeapSize it seems to return the allocated heap size, although certain sections of that heap portion may be not used by the app at that moment.
This results in the inability to track rigorously every byte that is used by the app to later evaluate optimization efforts.

However memlab seems to return much smaller heap sized compared to performance.memory.
I suppose it means that memlab does a more precise measurement of the portion of the heap that is actually used by the app.

In this context I would want to better understand the mechanics which memlab uses to calculate the heap size

If I put all of this into two question, it would be like this:

  • Does memlab give a precise measurement of the actual portion of the heap that is used by the app?
  • If I regularly(say with every merged pr) collect statistics of heap size utilizing memlab, would it be accurate enough to evaluate optimization efforts based on that? (if not, what would be the way to do that?)

How to run memlab in the web browser / SPA

We have an Angular application which connects to different REST api's using Windows Authentication.

To overcome issues with authentication we could add a middleware who authenticates a user automatically, which we have to add to each REST api.

The memlab CLI has to be feeded with a scenario file. The action function needs to be programmed with the use-case interactions on each page, which is very tedious.

Is it possible to run memlab within the SPA, the browser does the authentication and the use-case interactions are interactively done by the user.

The memlab documentation has several functions that result files are created on disk, which is not possible in the browser. Such structures need to be held in memory. Can they be held in memory?

The handling of memlab would be done inside an Angular service.

Which memlab API calls have to be made to process and retrieve valuable results for the following scenario?

function url() {
  return "http://localhost:4200/test/memlab";  // this is a minimal page where the app is not bootstrapped
}

// action where you suspect the memory leak might be happening
async function action(page) {
  const guide =  await page.$('a[href="/test/business-page"]');
  guide.evaluate((h)=>{h.click()});
}

// how to go back to the state before action
async function back(page) {
  const handles12 =  await page.$('a[href="/test/memlab"]');
  handles12[0].evaluate((h)=>{h.click()});
}

module.exports = { action, back, url };

node packages/cli/dist/runner.js  run --scenario .\memlab\memlab-scenario-001.js --work-dir .\memlab\results\

Which further api calls could be made to show valuable information (which needs an adapter to display in HTML (maybe just in a pre))?

Maybe this is already possible, I just need a kickstart. Can you provide an implementation. Any help is welcome.

Is there a plan to support lower versions of node?

Thanks for taking time and effort in coming up with this library!
Since Node 16 is relatively new, is there a way to run or use this in projects which run on a lower version of node? (Say 12 - 14)?

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.