Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
Ckins opened this issue Oct 13, 2022 · 8 comments
Closed

Comments

@Ckins
Copy link

Ckins commented Oct 13, 2022

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]
@Ckins Ckins changed the title Discussion: How to map traced leaks to the front-end components, or HTML DOM path Discussion: How to map traced leaks to React components, or HTML DOM path Oct 13, 2022
@JacksonGL
Copy link
Member

JacksonGL commented Oct 13, 2022

@Ckins

Is there any tricks / instruction for mapping console output to react code / html components?

There are a few ways:

  1. Open Chrome DevTool, load the final heap snapshot taken by memlab (located inside $(memlab get-default-work-dir)/data/cur) and search for the leaked object ID (e.g., @995444000)
  2. Use the following command to interactive with the leak trace in terminal
memlab view-heap --node-id <leaked object ID>

Either way, you can view object properties in objects and closure variables defined in closure context, so you can search object names, function names in your code base to locate the source. In case object names and function name is too general, we often do a conjunction search of all the object properties in objects and closure variables defined in closure context to narrow down the scope (which can quickly narrow down to the location most of the time in our use case).

PS: The internal UI mentioned in the engineering blog post has a few shortcuts to make the process easier (integrated with Meta infra so not open sourced). Just an idea if anyone is interested in integrating with their own infra.

@JacksonGL
Copy link
Member

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.

This is specific to debugging React. In either memlab view-heap or Chrome DevTool, follow the return chain of the React fiber nodes. Each Fiber node should have a state node that reveals some information about the component or the associated DOM elements, which gives you some clues about the HTML element.

@JacksonGL
Copy link
Member

In addition to [window], I also see [(GC roots)], [ < synthetic > ] . How to interpret these kinds?

MemLab faithfully presents the reference chain in V8 heap snapshots (serialization of C++ data structure in v8 engine), those are V8 internal data structure for organizing the heap, most of the time we can ignore those objects (with type internal, native etc.) and focus on the JavaScript references.

@LuciNyan
Copy link
Contributor

Hi! @JacksonGL. I just wanted to let you know that memlab is fantastic! But, as you say, it would be easier to use with a UI. So I wonder if making the UI open source will be part of the plans. If not, can I contribute an open-source UI to this repo?

@JacksonGL
Copy link
Member

@LuciNyan Thanks for your contributions. The internal UI is integrated with Meta infra, so there is no plan to open source the UI in the near future. Can you explain what kind of UI you have in mind? Is it a web app, electron app, or extending the Chrome DevTools?

@LuciNyan
Copy link
Contributor

@LuciNyan Thanks for your contributions. The internal UI is integrated with Meta infra, so there is no plan to open source the UI in the near future. Can you explain what kind of UI you have in mind? Is it a web app, electron app, or extending the Chrome DevTools?

I'm thinking of a web app or a chrome extension. I read that blog you mentioned, but I don't think I know enough details about how you guys use memlab. So I'm still lacking a detailed vision of the exact functionality of this UI.

By the way, I think memlab is a powerful tool. Large enterprises may have some internal tools to monitor and troubleshoot memory leaks. But for small to medium-sized teams, There is not yet a mature tool. Memlab can fill this gap.

I want to do something to make memlab easier to use. I currently have this in mind:

  1. A web UI, which might be like what you have integrated into Meta infra.
  2. Provide interfaces that make it easier for users to integrate memlab into existing tests (e.g., Playwright or Jest)

What do you think?

@JacksonGL
Copy link
Member

I agree having a better user interface and APIs for integration with other test frameworks would make MemLab easier to use.

A web UI, which might be like what you have integrated into Meta infra.

The scope and implementation of Web UI can vary depending on what functionalities to include. Here are some ideas:
For basic use cases, adding or extending either one of the following user interfaces could assist memory leak debugging:

  • An auto-generated static web page visualizing the retainer trace where people can interact with the trace (e.g., expanding and viewing object properties or closure context information)
  • A CLI command that automatically opens up Chrome DevTools, loads the last heap snapshot, and focuses on leaked objects detected by memlab.
  • The memlab view-heap command, which is a CLI user interface for interacting with a retainer trace (mostly useful on a shell connecting to a remote dev server)

For more extended use cases, the Web UI could be a memory leak monitoring and management system. This may involve integration with CI/CD, code searching/viewing system, and backend data stores depending on different orgs' infra.

Provide interfaces that make it easier for users to integrate memlab into existing tests (e.g., Playwright or Jest)

For integration with different testing frameworks, this could be implemented by providing APIs that take heap snapshots based on each testing framework (e.g., Playwright, Cypress, Jest). Calling those test-framework-specific APIs in existing tests dumps heap snapshots and meta data in the format that can be processed by the memlab heap analyzer.

Please also check out the discussion in #35.

@LuciNyan
Copy link
Contributor

Hi! @JacksonGL. Thanks for the detailed advice! This sounds great! I'm sure memlab will become an even more influential tool!

@Ckins Ckins closed this as completed Oct 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants