├── .gitignore ├── README.md ├── aether ├── README.md ├── aether-cli.js ├── assets │ ├── .DS_Store │ ├── AetherLogo01.png │ ├── BubbleChartExample.png │ └── ChartExample.png ├── build │ └── bundle.js ├── client │ ├── components │ │ ├── App.js │ │ ├── bubblechart.jsx │ │ ├── dropdown.jsx │ │ ├── header.jsx │ │ └── linechart.jsx │ └── index.js ├── index.html ├── package-lock.json ├── package.json ├── server │ ├── server.js │ └── takeSnapShot.js ├── snapshot │ ├── .gitignore │ └── AetherLogo01.png ├── stylesheet.css └── webpack.config.js └── testing-sample-app ├── build └── bundle.js ├── client ├── assets │ └── closureSnippet.png ├── components │ ├── App.jsx │ ├── Section.jsx │ └── content.js ├── index.js └── styles.css ├── index.html ├── package-lock.json ├── package.json ├── server ├── reference.js ├── server.js └── test.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # DS_Store file 107 | .DS_Store 108 | 109 | # ignore snapshots 110 | .snapshot/* 111 | !.gitkeep 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # aether 6 | 7 | aether is a memory leak management tool for visualizing and tracking memory usage in real-time. 8 | 9 | The best way to evaluate your memory footprint is to look at heap usage. There's a distinct lack of tools for debugging and profiling memory usage in Node.JS/Javascript applications, so there's still a need for a tool to synthesize data with live updates in a chart format. 10 | 11 | After installing the `aether_memory` npm package to your server, you can observe memory usage and notice if you have a memory leak in your application, by just looking at the graph report. Once you've identified a leak, you can look at the two bubble charts to see self and retained size of nodes. 12 | 13 | ## Features 14 | 15 |

16 | 17 |

18 | 19 | - Presenting total memory usage over time in a chart format with live updates every 5 seconds, so you can analyze the overall health of your application's memory. 20 | 21 |

22 | 23 |

24 | 25 | - Visualizing the self size and retained size of arrays, strings, functions, and objects in your memory heap as a bubble chart to quickly see what is allocated the largest amount of memory. 26 | --- 27 | ## How it works 28 | 29 | `aether_memory` uses `node-heapdump` to take snapshots of the heap with 5 seconds intervals. The V8 Engine triggers garbage collection before each snapshot. The physical file is created in 'snapshot' folder and parsed through. Afterwards, the data is analyzed, displayed, and the file gets deleted before the next snapshot is taken, minimizing its own memory footprint and avoiding false results. 30 | 31 | --- 32 | ## Installation 33 | 34 | - Install directly in your application: 35 | 36 | ``` 37 | npm install aether_memory 38 | 39 | ``` 40 | --- 41 | ## Usage 42 | 43 | - Add this script to your package.json specifying how to start your server: 44 | ``` 45 | "aether": "node node_modules/aether_memory/server/server.js & ", 46 | ``` 47 | ``` 48 | FOR EXAMPLE: 49 | 50 | "aether": "node node_modules/aether_memory/server/server.js & node server/server.js", 51 | ``` 52 | - In your server file require and invoke aether by adding these 2 lines: 53 | ``` 54 | const aether = require('aether_memory'); 55 | aether.start(); 56 | ``` 57 | - In your terminal run: 58 | ``` 59 | npm run aether 60 | ``` 61 | - Your charts will be displayed on [localhost:9000](http://localhost:9000/) 62 | 63 | --- 64 | ## Technologies 65 | 66 | * [node-heapdump](https://github.com/bnoordhuis/node-heapdump) 67 | * [React](https://github.com/facebook/react) 68 | * [Express](https://github.com/expressjs/express) 69 | * [socket.io](https://github.com/socketio/socket.io) 70 | * [React-Router](https://github.com/ReactTraining/react-router) 71 | * [react-bubble-chart-d3](https://github.com/weknowinc/react-bubble-chart-d3) 72 | * [chartjs](https://github.com/chartjs) 73 | * [Webpack](https://github.com/webpack/webpack) 74 | --- 75 | ## Contributing and Issues 76 | 77 | We are always looking to improve. For major changes, please open an issue first to discuss what you would like to change, pull requests are welcome. 78 | 79 | Currently on our to-do list: 80 | - Updates to the retained-size algorithm to account for multiple nodes being attached to a single node 81 | - Fix a bug where React Router does not deep-link to a chart 82 | - Expand data on hover for each displayed node in bubble charts 83 | 84 | --- 85 | ## Authors 86 | 87 | - **Anna Konstantinovich** - [@anreko](https://github.com/anreko) 88 | - **Denis Belioglo** - [@DenisB27](https://github.com/DenisB27) 89 | - **Sejan Miah** - [@sejdemi](https://github.com/sejdemi) 90 | - **Vessy Shestorkina** - [@Ve33y](https://github.com/Ve33y) 91 | --- 92 | 93 | ## License 94 | This project is licensed under the [MIT](https://choosealicense.com/licenses/mit/) 95 | -------------------------------------------------------------------------------- /aether/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # aether 6 | 7 | aether is a memory leak management tool for visualizing and tracking memory usage in real-time. 8 | 9 | The best way to evaluate your memory footprint is to look at heap usage. There's a distinct lack of tools for debugging and profiling memory usage in Node.JS/Javascript applications, so there's still a need for a tool to synthesize data with live updates in a chart format. 10 | 11 | After installing the `aether_memory` npm package to your server, you can observe memory usage and notice if you have a memory leak in your application, by just looking at the graph report. Once you've identified a leak, you can look at the two bubble charts to see self and retained size of nodes. 12 | 13 | ## Features 14 | 15 |

16 | 17 |

18 | 19 | - Presenting total memory usage over time in a chart format with live updates every 5 seconds, so you can analyze the overall health of your application's memory. 20 | 21 |

22 | 23 |

24 | 25 | - Visualizing the self size and retained size of arrays, strings, functions, and objects in your memory heap as a bubble chart to quickly see what is allocated the largest amount of memory. 26 | --- 27 | ## How it works 28 | 29 | `aether_memory` uses `node-heapdump` to take snapshots of the heap with 5 seconds intervals. The V8 Engine triggers garbagage collection before each snapshot. The physical file is created in 'snapshot' folder and parsed through. Afterwards, the data is analyzed, displayed, and the file gets deleted before the next snapshot is taken, minimizing its own memory footprint and avoiding false results. 30 | 31 | --- 32 | ## Installation 33 | 34 | - Install directly in your application: 35 | 36 | ``` 37 | npm install aether_memory 38 | 39 | ``` 40 | --- 41 | ## Usage 42 | 43 | - Add this script to your package.json specifying how to start your server: 44 | ``` 45 | "aether": "node node_modules/aether_memory/server/server.js & ", 46 | ``` 47 | ``` 48 | FOR EXAMPLE: 49 | 50 | "aether": "node node_modules/aether_memory/server/server.js & node server/server.js", 51 | ``` 52 | - In your server file require and invoke aether by adding these 2 lines: 53 | ``` 54 | const aether = require('aether_memory'); 55 | aether.start(); 56 | ``` 57 | - In your terminal run: 58 | ``` 59 | npm run aether 60 | ``` 61 | - Your charts will be displayed on [localhost:9000](http://localhost:9000/) 62 | 63 | --- 64 | ## Technologies 65 | 66 | * [node-heapdump](https://github.com/bnoordhuis/node-heapdump) 67 | * [React](https://github.com/facebook/react) 68 | * [Express](https://github.com/expressjs/express) 69 | * [socket.io](https://github.com/socketio/socket.io) 70 | * [React-Router](https://github.com/ReactTraining/react-router) 71 | * [react-bubble-chart-d3](https://github.com/weknowinc/react-bubble-chart-d3) 72 | * [chartjs](https://github.com/chartjs) 73 | * [Webpack](https://github.com/webpack/webpack) 74 | --- 75 | ## Contributing and Issues 76 | 77 | We are always looking to improve. For major changes, please open an issue first to discuss what you would like to change, pull requests are welcome. 78 | 79 | Currently on our to-do list: 80 | - Updates to the retained-size algorithm to account for multiple nodes being attached to a single node 81 | - Fix a bug where React Router does not deep-link to a chart 82 | - Expand data on hover for each displayed node in bubble charts 83 | 84 | --- 85 | ## Authors 86 | 87 | - **Anna Konstantinovich** - [@anreko](https://github.com/anreko) 88 | - **Denis Belioglo** - [@DenisB27](https://github.com/DenisB27) 89 | - **Sejan Miah** - [@sejdemi](https://github.com/sejdemi) 90 | - **Vessy Shestorkina** - [@Ve33y](https://github.com/Ve33y) 91 | --- 92 | 93 | ## License 94 | This project is licensed under the [MIT](https://choosealicense.com/licenses/mit/) -------------------------------------------------------------------------------- /aether/aether-cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('./server/server.js') 4 | -------------------------------------------------------------------------------- /aether/assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/aether/02fdc6ce7d44e233d14c2dd9051225b9cfb38e69/aether/assets/.DS_Store -------------------------------------------------------------------------------- /aether/assets/AetherLogo01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/aether/02fdc6ce7d44e233d14c2dd9051225b9cfb38e69/aether/assets/AetherLogo01.png -------------------------------------------------------------------------------- /aether/assets/BubbleChartExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/aether/02fdc6ce7d44e233d14c2dd9051225b9cfb38e69/aether/assets/BubbleChartExample.png -------------------------------------------------------------------------------- /aether/assets/ChartExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/aether/02fdc6ce7d44e233d14c2dd9051225b9cfb38e69/aether/assets/ChartExample.png -------------------------------------------------------------------------------- /aether/client/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { BrowserRouter, Route, Switch, Link, Redirect} from 'react-router-dom'; 3 | import Header from './header.jsx'; 4 | import LineChart from './linechart.jsx'; 5 | import BubbleChartBlock from './bubblechart.jsx'; 6 | 7 | /* The constant variables create state. The first variable stores data for the bubbleChart and setBubbleData, 8 | updates state for bubbleData. The same state management logic applies to totalData and retainedSizeData. 9 | useState is pulled in from React to know that it's a piece of state. 10 | */ 11 | 12 | function App() { 13 | const [bubbleData, setBubbleData] = useState(); 14 | const [totalData, setTotalData] = useState([]); 15 | const [retainedSizeData, setRetainedSizeData] = useState(); 16 | 17 | /* Makes a call to the getData endpoint. When we are creating the new total data array, we are destructuring our old totalData array, 18 | the reason we do it in this manner is that we can't directly manipulate state, we are just adding onto state. 19 | */ 20 | 21 | function getData() { 22 | fetch('/getdata') 23 | .then((res) => res.json()) 24 | .then((res) => { 25 | setBubbleData(res.bubbles); 26 | setTotalData([...totalData, res.total]); 27 | setRetainedSizeData(res.retainedSize) 28 | }); 29 | } 30 | 31 | /* useEffect is the React Hook's replacement for component lifecycle aka componentDidMount. This takes in two argument, one is an anonymous function. It creates a variable called heapLoop, that triggers getData to run every 5 seconds. Upon that completing we are clearing the interval, this is the equivalent of setTimeOut. useEffect takes in a second parameter, called totalData, and it will only run when totalData changes. This could have been any one of our getData state management functions. 32 | */ 33 | 34 | useEffect(() => { 35 | const heapLoop = setInterval(() => { getData(); }, 5000); 36 | return function cleanup() { 37 | clearInterval(heapLoop); 38 | }; 39 | }, [totalData]); 40 | 41 | /* When using React-Router clicking on any specific endpoint, will link to inner state pages. Mainly relatred to page state management. We pass through data through props. 42 | TODOS -- fix endpoints. 43 | */ 44 | 45 | return ( 46 | 47 |
48 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ); 61 | } 62 | 63 | export default App; 64 | -------------------------------------------------------------------------------- /aether/client/components/bubblechart.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import BubbleChart from '@weknow/react-bubble-chart-d3'; 3 | 4 | //This child component accesses it's data via heapData on props, which contains it's bubbleData. 5 | 6 | function BubbleChartBlock(props) { 7 | return ( 8 | 37 | ); 38 | } 39 | export default BubbleChartBlock; 40 | -------------------------------------------------------------------------------- /aether/client/components/dropdown.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | //! Not currently in use 3 | 4 | function DropDown() { 5 | return ( 6 |
7 |
8 | 9 | 15 |
16 |
17 | 18 | 24 |
25 |
26 | 27 | 33 |
34 |
35 | ) 36 | } 37 | 38 | export default DropDown; -------------------------------------------------------------------------------- /aether/client/components/header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Header() { 4 | return ( 5 | 11 | ) 12 | } 13 | export default Header; -------------------------------------------------------------------------------- /aether/client/components/linechart.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Line } from 'react-chartjs-2'; 3 | 4 | function LineChart(props) { 5 | 6 | /* The Chart.js library uses two things -- the timeLabel, defaulting to 0 and 5000 milliseconds. 7 | This is how the chart builts out it's first time render. The date label is dynamically set and reflects the current time the user is accessing the app. 8 | */ 9 | 10 | const [timeLabel, setTimeLabel] = useState([0, 5000]) 11 | const [dateLabel, setDateLabel] = useState() 12 | 13 | /* Captures the current time and set's the data label to currentData, takes in two parameters like before, an anonymous function and an empty array, which is a non-semantic way of stating that only render the component once. Otherwise it will keep making an attempt to keep changing state. */ 14 | 15 | useEffect(() => { 16 | let currentDate = String(new Date(Date.now())) 17 | currentDate = currentDate.slice(4, 25) 18 | console.log("THE CURRENT DATA IS", currentDate) 19 | setDateLabel(currentDate) 20 | }, []) 21 | 22 | /* The second useEffect updates the label's time to add 5 more seconds to reflect the new data coming in. 23 | We grab the last time in our array. We call setTimeLabel, we passed back in all previous labels, containing our time and then we added one more, which is 5 seconds greater than the last one. We gave it a second parameter, where it updates itself anytime props.heapData is updated. 24 | */ 25 | 26 | useEffect(() => { 27 | const lastTime = timeLabel[timeLabel.length - 1] 28 | const allLabels = timeLabel 29 | setTimeLabel([...allLabels, lastTime + 5000]) 30 | }, [props.heapData]) 31 | 32 | /* 33 | TODO DropDowns is not currently functional, slated for future release. */ 34 | 35 | return ( 36 |
37 |
38 | 54 |
55 |
56 | ); 57 | } 58 | 59 | export default LineChart; -------------------------------------------------------------------------------- /aether/client/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './components/App'; 4 | 5 | render( 6 | , 7 | document.getElementById('root'), 8 | ); 9 | -------------------------------------------------------------------------------- /aether/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | aether Memory Report 5 | 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /aether/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aether_memory", 3 | "version": "1.0.15", 4 | "description": "All-in-One Memory Leak Testing Solution", 5 | "main": "server/takeSnapShot.js", 6 | "scripts": { 7 | "start": "node server/server.js", 8 | "build": "NODE_ENV=production webpack", 9 | "dev": "NODE_ENV=development webpack-dev-server --open --hot & node server/server.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/oslabs-beta/aether.git" 14 | }, 15 | "keywords": [], 16 | "author": "Sejan, Vessy, Anna, Denis", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/oslabs-beta/aether/issues" 20 | }, 21 | "homepage": "https://github.com/oslabs-beta/aether#readme", 22 | "dependencies": { 23 | "@babel/cli": "^7.8.3", 24 | "@weknow/react-bubble-chart-d3": "^1.0.12", 25 | "body-parser": "^1.19.0", 26 | "chart.js": "^2.9.3", 27 | "express": "^4.17.1", 28 | "heapdump": "^0.3.15", 29 | "heapsnapshot-parser": "^0.1.0", 30 | "path": "^0.12.7", 31 | "react": "^16.2.0", 32 | "react-chartjs-2": "^2.8.0", 33 | "react-dom": "^16.3.1", 34 | "react-router": "^5.1.2", 35 | "react-router-dom": "^5.1.2", 36 | "socket.io": "^2.3.0", 37 | "webpack": "^4.41.2" 38 | }, 39 | "devDependencies": { 40 | "@babel/core": "^7.7.2", 41 | "@babel/preset-env": "^7.7.1", 42 | "@babel/preset-react": "^7.7.0", 43 | "babel-cli": "^6.26.0", 44 | "babel-core": "^6.26.3", 45 | "babel-loader": "^8.0.6", 46 | "cross-env": "^6.0.3", 47 | "webpack": "^4.41.4", 48 | "webpack-cli": "^3.3.10", 49 | "webpack-dev-server": "^3.10.1" 50 | }, 51 | "bin": { 52 | "aether": "./aether-cli.js" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /aether/server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const path = require('path'); 4 | const port = 9000; 5 | const server = require('http').Server(app); 6 | server.listen(port); 7 | const io = require('socket.io')(server); 8 | let socketConnection; 9 | 10 | // Initiating a global variable to be stored and passed into front-end 11 | let heapData; 12 | 13 | /* Aether established a WebSocket connection and anyone else that has a sockt can join at the specific port. 14 | It's an open channell of communication where you can listen and emit based on the two specified events. 15 | We then held a reference to the specific socket connection via the variable socketConnection. 16 | Aether starts listening for an event called "serverEvent." If there is data emitted from the "serverEvent", 17 | we take that data and assign it to heapData. We then begin to see our first real event happening, 'clientEvent', 18 | this is the event used to transmit the heap snapshots from our client. The Testing Sample App responds on clientEvent with 19 | the heap data. */ 20 | 21 | io.on('connection', (socket) => { 22 | socketConnection = socket; 23 | socket.on('serverEvent', (data) => { 24 | heapData = data; 25 | }); 26 | socket.emit('clientEvent', 'update snapshot request'); 27 | }); 28 | 29 | app.use(express.static('/assets/')); 30 | 31 | app.get('/getdata', (req, res) => { 32 | if (socketConnection) { 33 | socketConnection.emit('clientEvent') 34 | } 35 | res.status(200).send(heapData); 36 | }); 37 | 38 | app.get('/', (req, res) => { 39 | res.sendFile(path.join(__dirname, '../index.html')); 40 | }); 41 | 42 | app.get('/build/bundle.js', (req, res) => { 43 | res.sendFile(path.join(__dirname, '../build/bundle.js')); 44 | }); 45 | app.get('/stylesheet.css', (req, res) => { 46 | res.sendFile(path.join(__dirname, '../stylesheet.css')); 47 | }); 48 | 49 | app.get('/assets/AetherLogo01.png', (req, res) => { 50 | res.sendFile(path.join(__dirname, '../assets/AetherLogo01.png')); 51 | }); -------------------------------------------------------------------------------- /aether/server/takeSnapShot.js: -------------------------------------------------------------------------------- 1 | const heapdump = require('heapdump'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const parser = require('heapsnapshot-parser'); 5 | const io = require('socket.io-client'); 6 | 7 | const socket = io.connect('http://localhost:9000/', { 8 | reconnection: true, 9 | }); 10 | 11 | // Primary algorithm that takes, parses and deletes snapshots 12 | // TODO Refactor function to use Single Responsibility Principle 13 | 14 | function takeSnapShot(input) { 15 | const filename = `../snapshot/${Date.now()}.heapsnapshot`; 16 | heapdump.writeSnapshot(path.resolve(__dirname, filename), (err, filename) => { 17 | const snapshotFile = fs.readFileSync(filename, { encoding: 'utf-8' }); 18 | const snapshot = parser.parse(snapshotFile); 19 | // total for total memory size 20 | let selfSizeTotal = 0; 21 | // array for bubble chart 22 | const bubblesArr = []; 23 | const retainedArr = []; 24 | const snapshotArray = Object.keys(snapshot.nodes); 25 | 26 | for (let i = 0; i < snapshotArray.length; i += 1) { 27 | let totalDependentObjSize = 0; 28 | const node = snapshot.nodes[i]; 29 | if (node.self_size >= 500) { 30 | // array of edges 31 | for (let j = 0; j < node.references.length; j += 1) { 32 | if (node.references[j].toNode.edge_count === 1) { 33 | // add the dependent sizes 34 | totalDependentObjSize += node.references[j].toNode.self_size; 35 | } 36 | } 37 | // the current node size plus its retained size --- this is what we want to return 38 | // This is the size of memory that is freed 39 | // once the object itself is deleted along with its dependent objects that were made unreachable from GC roots. 40 | totalDependentObjSize += node.self_size; 41 | selfSizeTotal += node.self_size; 42 | } 43 | 44 | if (node.self_size >= 500) { 45 | bubblesArr.push({ 46 | label: node.type, 47 | value: node.self_size, 48 | retained_size: totalDependentObjSize, 49 | }); 50 | retainedArr.push({ 51 | label: node.type, 52 | value: totalDependentObjSize, 53 | color: '#000000'.replace(/0/g, () => (~~(Math.random() * 16)).toString(16)), 54 | }); 55 | } 56 | } 57 | /* The function takes in a parameter, which is an object called input. We are using an object, 58 | because we can pass it by reference. If we didn't do this, then the object wouldn't exist outside of the functions lexical scope. */ 59 | input.input = JSON.stringify({ 60 | total: selfSizeTotal, 61 | bubbles: bubblesArr, 62 | retainedSize: retainedArr, 63 | }); 64 | // deletes the local snapshot files 65 | fs.unlink(filename, (err) => { 66 | if (err) throw err; 67 | }); 68 | }); 69 | } 70 | 71 | /* Use the WebSocket to establish a connection at the specific port, connecting to Aether. 72 | The Testing-Sample-App starts listens for an event called 'clientEvent.' The client event then gets triggered from Aether. 73 | this takes in a callback function and now that it's begun to listen it can respond via the next event. 74 | The Testing Sample App provides snapshot data via the 'serverEvent.' 75 | We use the emit function to broadcast new data at the specific event. */ 76 | 77 | function start() { 78 | socket.on('connect', () => { 79 | let newData = {}; 80 | takeSnapShot(newData); 81 | socket.on('clientEvent', (data) => { 82 | newData = {}; 83 | takeSnapShot(newData); 84 | socket.emit('serverEvent', newData.input); 85 | }); 86 | }); 87 | } 88 | 89 | module.exports = { 90 | takeSnapShot, 91 | start, 92 | }; 93 | -------------------------------------------------------------------------------- /aether/snapshot/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | #snapshot 4 | *.heapsnapshot 5 | 6 | .snapshot/* 7 | !.gitkeep -------------------------------------------------------------------------------- /aether/snapshot/AetherLogo01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/aether/02fdc6ce7d44e233d14c2dd9051225b9cfb38e69/aether/snapshot/AetherLogo01.png -------------------------------------------------------------------------------- /aether/stylesheet.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, Helvetica, sans-serif; 3 | color: #5F6063 4 | } 5 | 6 | h1 { 7 | color: #5F6063; 8 | } 9 | 10 | label { 11 | padding-right: 1rem; 12 | } 13 | 14 | select { 15 | color: #5F6063; 16 | background-color: white; 17 | padding: 5px; 18 | box-shadow: 1px 1px 1px 0px #d4d4d4; 19 | } 20 | 21 | nav a { 22 | padding: 1rem; 23 | } 24 | 25 | #logo { 26 | height: 75px; 27 | padding-right: 0.75rem; 28 | } 29 | 30 | #logotxt { 31 | display: flex; 32 | align-items: center; 33 | } 34 | 35 | #header { 36 | display: flex; 37 | justify-content: space-between; 38 | padding: 0 1rem; 39 | } 40 | 41 | #linechart { 42 | padding-top: 1rem 43 | } 44 | 45 | .options { 46 | display: flex; 47 | padding: 0 1rem; 48 | } 49 | 50 | 51 | .options > div:nth-child(n + 2) { 52 | margin-left: 4rem; 53 | } -------------------------------------------------------------------------------- /aether/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | target: 'node', 5 | entry: path.resolve(__dirname, 'client/index.js'), 6 | output: { 7 | path: path.resolve(__dirname, 'build'), 8 | filename: 'bundle.js', 9 | }, 10 | mode: process.env.NODE_ENV, 11 | devServer: { 12 | port: 5000, 13 | publicPath: '/build/', 14 | proxy: { 15 | '/': 'http://localhost:9000', 16 | '/assets/': 'http://localhost:9000', 17 | }, 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.jsx?/, 23 | exclude: /node_modules/, 24 | loader: 'babel-loader', 25 | options: { presets: ['@babel/preset-env', '@babel/preset-react'] }, 26 | }, 27 | ], 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /testing-sample-app/build/bundle.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var l=t[r]={i:r,l:!1,exports:{}};return e[r].call(l.exports,l,l.exports,n),l.l=!0,l.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var l in e)n.d(r,l,function(t){return e[t]}.bind(null,l));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=7)}([function(e,t,n){"use strict";e.exports=n(3)},function(e,t,n){"use strict"; 2 | /* 3 | object-assign 4 | (c) Sindre Sorhus 5 | @license MIT 6 | */var r=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;function i(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,o,u=i(e),c=1;cz.length&&z.push(e)}function D(e,t,n){return null==e?0:function e(t,n,r,l){var o=typeof t;"undefined"!==o&&"boolean"!==o||(t=null);var u=!1;if(null===t)u=!0;else switch(o){case"string":case"number":u=!0;break;case"object":switch(t.$$typeof){case a:case i:u=!0}}if(u)return r(l,t,""===n?"."+F(t,0):n),1;if(u=0,n=""===n?".":n+":",Array.isArray(t))for(var c=0;c