forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Set framework for visualizing Android dependencies
Rough code, mostly copied from wnwen's visualization, as a framework for future visualization work. It is forecast that this code will be restructured heavily in the future, hence the lack of more thorough documentation. npm is used for this project, please read the README on how to run locally. The dependencies so far are: - ESLint - d3 (however, as we continue to identify what is required, I plan to break this up so we only import the v4 microlibraries we actually need) The page is currently served the JSON file via a small Python script. This is easy to use for development, but will hopefully be replaced with either a user file upload or being served from somewhere else. Bug: 1093962 Change-Id: I8d35ae2cde64581ff7d22fb06a29cd850e42cc86 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2242171 Commit-Queue: James Long <yjlong@google.com> Reviewed-by: Samuel Huang <huangs@chromium.org> Reviewed-by: Henrique Nakashima <hnakashima@chromium.org> Cr-Commit-Position: refs/heads/master@{#777955}
- Loading branch information
James Long
authored and
Commit Bot
committed
Jun 12, 2020
1 parent
1875930
commit 06c3b58
Showing
7 changed files
with
234 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"env": { | ||
"browser": true, | ||
"es2020": true | ||
}, | ||
"extends": [ | ||
"google" | ||
], | ||
"parserOptions": { | ||
"ecmaVersion": 11, | ||
"sourceType": "module" | ||
}, | ||
"rules": { | ||
"require-jsdoc": ["warn", { | ||
"require": { | ||
"FunctionDeclaration": true, | ||
"MethodDefinition": true, | ||
"ClassDeclaration": true, | ||
"ArrowFunctionExpression": true, | ||
"FunctionExpression": true | ||
} | ||
}] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules/ | ||
package-lock.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Chrome Android Dependency Analysis Visualization | ||
## Development Setup | ||
### Shell variables | ||
|
||
This setup assumes Chromium is in a `cr` directory (`~/cr/src/...`). To make setup easier, you can modify and export the following variables: | ||
``` | ||
export DEP_ANALYSIS_DIR=~/cr/src/tools/android/dependency_analysis | ||
export DEP_ANALYSIS_JAR=~/cr/src/out/Default/obj/chrome/android/chrome_java__process_prebuilt.desugar.jar | ||
``` | ||
|
||
### Generate JSON | ||
|
||
See `../README.md` for instructions on using `generate_json_dependency_graph.py`, then generate a graph file in this directory (`js/json_graph.txt`) with that exact name: | ||
|
||
``` | ||
cd $DEP_ANALYSIS_DIR | ||
./generate_json_dependency_graph.py --target $DEP_ANALYSIS_JAR --output js/json_graph.txt | ||
``` | ||
**The following instructions assume you are in the `dependency_analysis/js` = `$DEP_ANALYSIS_DIR/js` directory.** | ||
|
||
### Install dependencies | ||
You will need to install `npm` if it is not already installed (check with `npm -v`), either [from the site](https://www.npmjs.com/get-npm) or via [nvm](https://github.com/nvm-sh/nvm#about) (Node Version Manager). | ||
|
||
To install dependencies: | ||
|
||
``` | ||
npm install | ||
``` | ||
|
||
### (TEMP) Run visualization | ||
|
||
To run the (highly temporary) Python server to serve the JSON at `localhost:8888/json_graph.txt` : | ||
``` | ||
python3 -m http.server 8888 | ||
``` | ||
The visualization will make requests to this server for the JSON graph on load. | ||
|
||
**To view the visualization, open `localhost:8888/index.html`.** | ||
|
||
### Miscellaneous | ||
To run [ESLint](https://eslint.org/) on the JS (and fix fixable errors) using [npx](https://www.npmjs.com/package/npx) (bundled with npm): | ||
``` | ||
npx eslint --fix *.js | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* Copyright 2020 The Chromium Authors. All rights reserved. | ||
* Use of this source code is governed by a BSD-style license that can be | ||
* found in the LICENSE file. */ | ||
|
||
.graph-edges line { | ||
stroke: #999; | ||
stroke-opacity: 0.6; | ||
} | ||
|
||
.graph-nodes circle { | ||
stroke: #fff; | ||
stroke-width: 1.5px; | ||
} | ||
|
||
.graph-labels text { | ||
font-family: sans-serif; | ||
font-size: 12px; | ||
} | ||
|
||
circle.dragging { | ||
stroke: #000; | ||
stroke-width: 3; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<link rel="stylesheet" type="text/css" href="./index.css"></link> | ||
<script type="text/javascript" src="./node_modules/d3/dist/d3.min.js"></script> | ||
</head> | ||
<body> | ||
<svg width="960" height="600"></svg> | ||
<script src="./index.js"></script> | ||
</body> | ||
</html> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// Copyright 2020 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
/** | ||
* TODO(yjlong): Compute the "shortest disambiguated name" in Python and include | ||
* it in the JSON so we do not need to do this calculation. | ||
* @param {number} name The full package name to shorten. | ||
* @return {number} The shortened package name. | ||
*/ | ||
const shortName = (name) => name.substring(name.lastIndexOf('.') + 1); | ||
|
||
/* | ||
* Transforms a graph JSON generated by Python scripts | ||
* (generate_json_dependency_graph.py) into a working format for d3. | ||
* TODO(yjlong): Figure out if we want to typecheck the graph. | ||
*/ | ||
function parseNodesAndEdgesFromJson(graph) { | ||
const nodes = graph.nodes.map((node) => ({ | ||
...node, | ||
id: node.name, | ||
short_name: shortName(node.name), | ||
})); | ||
const edges = graph.edges.map((edge) => ({ | ||
...edge, | ||
id: `${edge.begin}>${edge.end}`, | ||
// The names source/target are needed for d3-force links. | ||
source: edge.begin, | ||
target: edge.end, | ||
})); | ||
return [nodes, edges]; | ||
} | ||
|
||
function renderJsonGraph(data) { | ||
const [jsonNodes, jsonEdges] = parseNodesAndEdgesFromJson(data.package_graph); | ||
|
||
const svg = d3.select('svg'); | ||
|
||
// TODO(yjlong): SVG should be resizable & these values updated. | ||
const width = +svg.attr('width'); | ||
const height = +svg.attr('height'); | ||
|
||
const simulation = d3.forceSimulation() | ||
.nodes(jsonNodes) | ||
.alphaMin(0.1) // Stop the simulation faster than default (0.001). | ||
.force('chargeForce', d3.forceManyBody().strength(-300)) | ||
.force('centerForce', d3.forceCenter(width / 2, height / 2)) | ||
.force('links', d3.forceLink(jsonEdges).id((d) => d.id)); | ||
|
||
const edgeGroup = svg.append('g') | ||
.classed('graph-edges', true) | ||
.attr('stroke-width', 1); | ||
const edges = edgeGroup.selectAll('line') | ||
.data(jsonEdges) | ||
.join((enter) => enter.append('line')); | ||
|
||
const dragStarted = (d, i, nodes) => { | ||
d3.event.sourceEvent.stopPropagation(); | ||
d3.select(nodes[i]).classed('dragging', false); | ||
d.fx = null; | ||
d.fy = null; | ||
}; | ||
|
||
const dragged = (d, i, nodes) => { | ||
simulation.alpha(0.3).restart(); // Reheat the simulation on drag. | ||
d3.select(nodes[i]).classed('dragging', true); | ||
// Fix the node's position after it has been dragged. | ||
d.fx = d3.event.x; | ||
d.fy = d3.event.y; | ||
}; | ||
|
||
const nodeGroup = svg.append('g') | ||
.classed('graph-nodes', true) | ||
.attr('fill', 'red'); | ||
const nodes = nodeGroup.selectAll('circle') | ||
.data(jsonNodes) | ||
.join((enter) => enter.append('circle')) | ||
.attr('r', 5) | ||
.call(d3.drag() | ||
.on('start', dragStarted) | ||
.on('drag', dragged)); | ||
|
||
const labelGroup = svg.append('g') | ||
.classed('graph-labels', true); | ||
const labels = labelGroup.selectAll('text') | ||
.data(jsonNodes) | ||
.join((enter) => enter.append('text')) | ||
.attr('dx', 12) | ||
.attr('dy', '.35em') | ||
.text((d) => d.short_name); | ||
|
||
// The simulation updates position variables in the JSON, it's up to us | ||
// to update the visualization to match on each tick. | ||
const tickActions = () => { | ||
nodes | ||
.attr('cx', (d) => d.x) | ||
.attr('cy', (d) => d.y); | ||
|
||
edges | ||
.attr('x1', (d) => d.source.x) | ||
.attr('y1', (d) => d.source.y) | ||
.attr('x2', (d) => d.target.x) | ||
.attr('y2', (d) => d.target.y); | ||
|
||
labels | ||
.attr('x', (d) => d.x) | ||
.attr('y', (d) => d.y); | ||
}; | ||
|
||
simulation.on('tick', tickActions); | ||
} | ||
|
||
// TODO(yjlong): Currently we take JSON served by a Python server running on | ||
// the side. Replace this with a user upload or pull from some other source. | ||
document.addEventListener('DOMContentLoaded', () => { | ||
d3.json('http://localhost:8888/json_graph.txt') | ||
.then((data) => renderJsonGraph(data)); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"name": "dependency-analysis", | ||
"version": "1.0.0", | ||
"dependencies": { | ||
"d3": "^5.16.0" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^7.2.0", | ||
"eslint-config-google": "^0.14.0" | ||
} | ||
} |