├── .gitattributes
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── README_Zh.md
├── example
├── app.js
├── bootstrap.js
├── config.ts
├── f.yml
├── package.json
├── pkg
│ ├── package.json
│ ├── v8_profiler_rs.d.ts
│ ├── v8_profiler_rs.js
│ ├── v8_profiler_rs_bg.wasm
│ └── v8_profiler_rs_bg.wasm.d.ts
├── public
│ ├── close.svg
│ ├── file.txt
│ ├── logo.jpeg
│ └── worker.js
├── src
│ ├── config
│ │ └── config.default.ts
│ ├── configuration.ts
│ └── controller
│ │ └── index.ts
├── tailwind.config.js
├── tsconfig.json
└── web
│ ├── @types
│ └── global
│ │ └── index.d.ts
│ ├── analyze.ts
│ ├── common.less
│ ├── components
│ └── layout
│ │ ├── App.vue
│ │ ├── index.less
│ │ └── index.vue
│ ├── hooks
│ ├── config.ts
│ └── index.ts
│ ├── pages
│ ├── index
│ │ ├── index.less
│ │ ├── node
│ │ │ └── render$node.vue
│ │ └── render.vue
│ └── report
│ │ └── render.vue
│ ├── store
│ └── index.ts
│ ├── tsconfig.json
│ ├── type.ts
│ └── utils.ts
├── images
├── 5c286fe4dc2b5443.png
├── 6c52bdda69534e42bc51b57042242650_noop.png
├── 77d1693d497f482b8a61a7c2aaa8bce9_noop.png
├── v8-1.png
├── v8-10.png
├── v8-11.png
├── v8-12.png
├── v8-2.png
├── v8-3.png
├── v8-4.png
├── v8-5.png
├── v8-6.png
├── v8-7.png
├── v8-8.png
└── v8-9.png
└── package.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.** linguist-language=Rust
2 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | # Controls when the action will run. Triggers the workflow on push or pull request
4 | # events but only for the dev branch
5 | on:
6 | push:
7 | branches: [dev, main, feat/**, ci/test, test/**]
8 | pull_request:
9 | branches: [dev, main]
10 |
11 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
12 | jobs:
13 | deploy:
14 | if: ${{ contains(github.actor, 'zhangyuang') && github.ref_name == 'main'}}
15 |
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@v2
19 | - name: Setup Node.js
20 | uses: actions/setup-node@v2
21 | with:
22 | node-version: 18
23 | - run: |
24 | if git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+$";
25 | then
26 | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
27 | npm publish && cd example && yarn && npm run deploy
28 | elif git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+";
29 | then
30 | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
31 | npm publish && cd example && yarn && npm run deploy
32 | else
33 | echo "Not a release, skipping publish"
34 | fi
35 | env:
36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
38 | SERVERLESS_DEPLOY_AK: ${{ secrets.SERVERLESS_DEPLOY_AK }}
39 | SERVERLESS_DEPLOY_ENDPOINT: ${{ secrets.SERVERLESS_DEPLOY_ENDPOINT }}
40 | SERVERLESS_DEPLOY_ID: ${{ secrets.SERVERLESS_DEPLOY_ID }}
41 | SERVERLESS_DEPLOY_SECRET: ${{ secrets.SERVERLESS_DEPLOY_SECRET }}
42 | SERVERLESS_DEPLOY_TIMEOUT: ${{ secrets.SERVERLESS_DEPLOY_TIMEOUT }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/node
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node
4 |
5 | ### Node ###
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | lerna-debug.log*
13 |
14 | # Diagnostic reports (https://nodejs.org/api/report.html)
15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
16 |
17 | # Runtime data
18 | pids
19 | *.pid
20 | *.seed
21 | *.pid.lock
22 |
23 | # Directory for instrumented libs generated by jscoverage/JSCover
24 | lib-cov
25 |
26 | # Coverage directory used by tools like istanbul
27 | coverage
28 | *.lcov
29 |
30 | # nyc test coverage
31 | .nyc_output
32 |
33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
34 | .grunt
35 |
36 | # Bower dependency directory (https://bower.io/)
37 | bower_components
38 |
39 | # node-waf configuration
40 | .lock-wscript
41 |
42 | # Compiled binary addons (https://nodejs.org/api/addons.html)
43 | build/Release
44 |
45 | # Dependency directories
46 | node_modules/
47 | jspm_packages/
48 |
49 | # TypeScript v1 declaration files
50 | typings/
51 |
52 | # TypeScript cache
53 | *.tsbuildinfo
54 |
55 | # Optional npm cache directory
56 | .npm
57 |
58 | # Optional eslint cache
59 | .eslintcache
60 |
61 | # Microbundle cache
62 | .rpt2_cache/
63 | .rts2_cache_cjs/
64 | .rts2_cache_es/
65 | .rts2_cache_umd/
66 |
67 | # Optional REPL history
68 | .node_repl_history
69 |
70 | # Output of 'npm pack'
71 | *.tgz
72 |
73 | # Yarn Integrity file
74 | .yarn-integrity
75 |
76 | # dotenv environment variables file
77 | .env
78 | .env.test
79 |
80 | # parcel-bundler cache (https://parceljs.org/)
81 | .cache
82 |
83 | # Next.js build output
84 | .next
85 |
86 | # Nuxt.js build / generate output
87 | .nuxt
88 | dist
89 |
90 | # Gatsby files
91 | .cache/
92 | # Comment in the public line in if your project uses Gatsby and not Next.js
93 | # https://nextjs.org/blog/next-9-1#public-directory-support
94 | # public
95 |
96 | # vuepress build output
97 | .vuepress/dist
98 |
99 | # Serverless directories
100 | .serverless/
101 |
102 | # FuseBox cache
103 | .fusebox/
104 |
105 | # DynamoDB Local files
106 | .dynamodb/
107 |
108 | # TernJS port file
109 | .tern-port
110 |
111 | # Stores VSCode versions used for testing VSCode extensions
112 | .vscode-test
113 |
114 | # End of https://www.toptal.com/developers/gitignore/api/node
115 |
116 |
117 | #Added by cargo
118 |
119 | /target
120 | Cargo.lock
121 |
122 | *.node
123 | *.cpuprofile
124 | snapshot.json
125 | .DS_Store
126 | !example/**/*.json
127 | example/index.js
128 | example/index.d.ts
129 | .eslintcache
130 | run
131 | **/yarn.lock
132 |
133 | **.heapsnapshot
134 |
135 | !**/memoryleak-demo/**/*.heapsnapshot
136 |
137 |
138 | **/test.js
139 |
140 | .temp
141 | .temp2
142 | .s
143 | help
144 | HeapSnapshotLoader.js
145 | js-flags.json
146 | flags.json
147 | foo.js
148 | example/build
149 | **/yarn.lock
150 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 N-API for Rust
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [English](./README.md) | [简体中文](./README_Zh.md)
2 |
3 | # Introduction
4 |
5 | [v8-profiler-rs](https://github.com/zhangyuang/v8-profiler-rs) is a project developed in `Rust` for intelligent online analysis of `V8 heapsnapshot` files. It helps developers using V8-based applications like `Node.js/Chrome/Deno/Electron` to better understand program memory structure and assist in locating memory leaks.
6 |
7 | ## How to start
8 |
9 | Recommend using [Online Demo](https://v8.ssr-fc.com/), it's more convenient. Or you can start it in local.
10 |
11 | ```bash
12 | $ git clone https://github.com/zhangyuang/v8-profiler-rs.git
13 | $ cd v8-profiler-rs/example
14 | $ yarn && yarn start
15 | ```
16 |
17 | ## Online Demo
18 |
19 | We have deployed an [online website](https://v8.ssr-fc.com/) where you can upload and analyze V8 memory snapshots in real-time. We recommend using `Safari` or `Firefox` browsers, as `wasm` execution performance is significantly better than in `Chrome` .
20 |
21 | 
22 |
23 | ## Default example
24 |
25 | View default example by clicking the `Example` button on the top left corner.
26 |
27 | The code of default example is as follows:
28 |
29 | ```js
30 | const express = require('express');
31 | const app = express();
32 | const fs = require('fs')
33 | // the memory leak code
34 | let theThing = null;
35 | let replaceThing = function() {
36 | let leak = theThing;
37 | let unused = function() {
38 | if (leak)
39 | console.log("hi")
40 | };
41 |
42 | theThing = {
43 | bigNumber: 1,
44 | bigArr: [],
45 | longStr: new Array(1000000),
46 | someMethod: function() {
47 | console.log('a');
48 | }
49 | };
50 | };
51 | let index = 0
52 | app.get('/leak', function closureLeak(req, res, next) {
53 | replaceThing();
54 | index++
55 | if (index === 1) {
56 | const stream = require('v8').getHeapSnapshot()
57 | stream.pipe(fs.createWriteStream('small-closure.heapsnapshot'))
58 | }
59 | if (index === 40) {
60 | const stream = require('v8').getHeapSnapshot()
61 | stream.pipe(fs.createWriteStream('medium-closure.heapsnapshot'))
62 | }
63 | if (index === 50) {
64 | const stream = require('v8').getHeapSnapshot()
65 | stream.pipe(fs.createWriteStream('big-closure.heapsnapshot'))
66 | }
67 | res.send('Hello Node');
68 | });
69 |
70 | app.listen(3001);
71 | ```
72 |
73 | ## Analyze Report
74 |
75 | There are two types of reports: single report and compare report.
76 |
77 | ### Single Report
78 |
79 | ```yml
80 | Analyze Report with single
81 | Only show nodes with more than 20 occurrences
82 | Memory retained by constructor type
83 | closure: 156409.05 MB
84 |
85 | Object: 156408.32 MB
86 |
87 | system / Context: 154881.33 MB
88 |
89 | array: 4579.70 MB
90 |
91 | synthetic: 1533.60 MB
92 |
93 | Array: 1526.16 MB
94 |
95 | WeakMap: 1526.01 MB
96 |
97 | (compiled code): 2.96 MB
98 |
99 | string: 2.10 MB
100 |
101 | There are some nodes that are duplicated too many times
102 | someMethod @70761: 202(memoryleak - demo / closure / index.js)
103 | HttpError @95957: 44(node_modules / http - errors / index.js)
104 |
105 | parse @89241: 33(node_modules / ms / index.js)
106 |
107 | ClientError @50469: 31(node_modules / http - errors / index.js)
108 |
109 | indexOf @49211: 27(node_modules / object - inspect / index.js)
110 |
111 | send @126827: 23(node_modules / send / index.js)
112 |
113 | resolve @98543: 23(node_modules / express / lib / view.js)
114 | ```
115 |
116 | ### Compare Report
117 |
118 | ```yml
119 | Analyze Report with compare
120 | Only show nodes with more than 20 occurrences
121 | Additional nodes by constructor type
122 | (compiled code): 242
123 |
124 | ArrayBuffer: 127
125 |
126 | system / JSArrayBufferData: 127
127 |
128 | Buffer: 126
129 |
130 | Node / std::basic_string: 78
131 |
132 | closure: 69
133 |
134 | Array: 46
135 |
136 | array: 44
137 |
138 | system / Context: 34
139 |
140 | Object: 29
141 |
142 | Bigger nodes by constructor type
143 | (compiled code): 48
144 | closure: 25
145 |
146 | Bigger nodes by increased size
147 | @1: +83.54 MB
148 | Object @6613: +76.30 MB
149 |
150 | Array @154471: +76.30 MB
151 |
152 | Array @154499: +76.30 MB
153 |
154 | app @6603: +76.30 MB(node_modules / .pnpm / express @4 .18 .2 / node_modules / express / lib / express.js)
155 |
156 | router @6533: +76.30 MB(node_modules / .pnpm / express @4 .18 .2 / node_modules / express / lib / router / index.js)
157 |
158 | closureLeak @6481: +76.30 MB(memoryleak - demo / closure / index.js)
159 |
160 | Route @6511: +76.30 MB
161 |
162 | Layer @6517: +76.30 MB
163 |
164 | Layer @6497: +76.30 MB
165 |
166 | Server @6609: +76.30 MB
167 |
168 | system / Context @6483: +76.30 MB
169 | ```
170 |
171 | ## Implemented Features
172 |
173 | 🚀 indicates implemented features. This application is continuously being updated, and updates will be synchronized to the README.md. Please stay tuned. If this application helps you, please give it a Star ✨
174 |
175 | | Milestone | Status |
176 | |-----------|--------|
177 | | Parse V8 snapshot to generate complete node information | 🚀 |
178 | | View node source location and constructor | 🚀 |
179 | | Generate analysis reports | 🚀 |
180 | | Frontend visualization support | 🚀 |
181 | | Filter nodes by ID and name | 🚀 |
182 | | Filter number of nodes and view detailed node references | 🚀 |
183 | | Filter reference depth and number of references | 🚀 |
184 | | Compare two snapshot files | 🚀 |
185 | | Support two comparison types: new nodes/increased GC size | 🚀 |
186 | | Automatically filter non-business nodes based on node count | 🚀 |
187 | | Support uploading local serialized JSON files | 🚀 |
188 | | Implement `Wasm + WebWorker` parsing to avoid site unresponsiveness | 🚀 |
189 | | Optimize parsing performance and reduce memory usage | 🚀 |
190 |
191 | ## Why Rust
192 |
193 | Parsing V8 memory snapshots involves a lot of computational operations. CPU-intensive scenarios are inherently not JavaScript's strong suit.
194 |
195 | After reading Chrome's official memory analysis tool source code, I found it uses many tricks to ensure performance, making the code highly complex and difficult to maintain.
196 |
197 | Rust is perfect for this scenario, providing excellent performance while maintaining code readability. Rust's superior multi-threading capabilities are exactly what we need, as parsing computation logic is ideal for multi-threaded optimization. We will continue optimizing computational performance in future versions.
198 |
199 | This application's approach can be applied to any programming language with GC. We will strive to support memory analysis for more programming languages in the future.
200 |
201 | Finally, Rust's official WebAssembly support is excellent, allowing us to easily compile Rust code to WebAssembly for browser use.
202 |
203 | ## Handling Parse Timeout Issues
204 |
205 | For very large files, you may encounter Wasm memory overflow or long parse times. If this occurs, try using `Safari` or `Firefox` browsers.
206 |
207 | ## Contact
208 |
209 | The prohect has been updating, if you have any questions or suggestions, please submit an [issue](https://github.com/zhangyuang/v8-profiler-rs/issues)
210 |
211 |
212 |
213 |
214 | ### How to sponsor me
215 |
216 | There are two ways to sponsor me both Alipay and WeChat
217 |
218 | Eth address: 0x87a2575a5d4dbD5f965e3e3a3d20641BC9a5d192
219 |
220 |
221 |
222 |
223 |
224 |
225 | ## How to use
226 |
227 | Open the [online demo](https://v8.ssr-fc.com/) and upload the `heapsnapshot` file. You can upload one or two files for comparison.
228 |
229 | There will render heapsnapshot nodes in web page.
230 |
231 | The node fields are as follows:
232 |
233 | ```js
234 | {
235 | "node_type": string; // node type
236 | "name": string; // node name
237 | "id": number; // node id
238 | "size": number; // node self size
239 | "edge_count": number; // node edge count
240 | "retained_size": number; // node retained size, the free size of the node after GC
241 | "pt": number; // the ratio of self size / retained size
242 | "edges": {
243 | "edge_type": string; // edge type
244 | "to_node": number; // the id of the node that the edge points to
245 | "name_or_index": string; // the name or index(for array) of the edge
246 | } [];
247 | "source": string; // the source file of the node
248 | "constructor": string; // the constructor of the node
249 | "percent": string; // the retained size ratio of the node
250 | }
251 | ```
252 |
--------------------------------------------------------------------------------
/README_Zh.md:
--------------------------------------------------------------------------------
1 | # 项目介绍
2 |
3 | [v8-profiler-rs](https://github.com/zhangyuang/v8-profiler-rs) 是一个基于 `Rust` 开发的智能在线分析 `V8 堆快照` 文件的项目。它可以帮助使用 `Node.js/Chrome/Deno/Electron` 等 V8 应用的开发者更好地理解程序内存结构,辅助定位内存泄漏问题。
4 |
5 | ## 在线演示
6 |
7 | 我们部署了[在线网站](https://v8.ssr-fc.com/),您可以实时上传并分析 V8 内存快照。推荐使用 `Safari` 或 `Firefox` 浏览器,其 `wasm` 执行性能显著优于 `Chrome` 。
8 |
9 | ## 已实现功能
10 |
11 | 🚀 表示已实现功能。本应用持续更新中,更新将同步至 README.md,敬请关注。如果本应用对您有帮助,请给它一个 Star ✨
12 |
13 | | 功能模块 | 状态 |
14 | |-----------|--------|
15 | | 解析 V8 快照生成完整节点信息 | 🚀 |
16 | | 查看节点源码位置及构造函数 | 🚀 |
17 | | 生成分析报告 | 🚀 |
18 | | 前端可视化支持 | 🚀 |
19 | | 按 ID 和名称过滤节点 | 🚀 |
20 | | 过滤节点数量并查看详细节点引用 | 🚀 |
21 | | 过滤引用深度和引用次数 | 🚀 |
22 | | 对比两份快照文件 | 🚀 |
23 | | 支持两种对比类型:新增节点/GC 体积增长 | 🚀 |
24 | | 根据节点数量自动过滤非业务节点 | 🚀 |
25 | | 支持上传本地序列化 JSON 文件 | 🚀 |
26 | | 实现 `Wasm + WebWorker` 解析避免站点无响应 | 🚀 |
27 | | 优化解析性能并降低内存占用 | 🚀 |
28 |
29 | ## 为什么选择 Rust
30 |
31 | 解析 V8 内存快照涉及大量计算操作。CPU 密集型场景本就不是 JavaScript 的强项。
32 |
33 | 阅读 Chrome 官方内存分析工具源码后发现,其使用了许多技巧来保证性能,导致代码复杂度极高且难以维护。
34 |
35 | Rust 完美契合这个场景,在提供卓越性能的同时保持代码可读性。Rust 优异的多线程能力正是我们需要的,解析计算逻辑非常适合多线程优化。我们将在后续版本持续优化计算性能。
36 |
37 | 本应用的实现思路可应用于任何带 GC 的编程语言。未来我们将努力支持更多编程语言的内存分析。
38 |
39 | 最后,Rust 官方的 WebAssembly 支持非常完善,使我们能够轻松将 Rust 代码编译为 WebAssembly 供浏览器使用。
40 |
41 | ## 处理解析超时问题
42 |
43 | 对于非常大的文件,可能会遇到 Wasm 内存溢出或解析时间过长的情况。如果出现此问题,请尝试使用 `Safari` 或 `Firefox` 浏览器。
44 |
45 | ## 联系我们
46 |
47 | 项目持续更新中,如有任何问题或建议,请提交 [issue](https://github.com/zhangyuang/v8-profiler-rs/issues)
48 |
49 |