@@ -20,6 +30,7 @@
diff --git a/tools/ota_analysis/src/services/echarts_data.js b/tools/ota_analysis/src/services/echarts_data.js
index d45b2eabe..652fdfb5a 100644
--- a/tools/ota_analysis/src/services/echarts_data.js
+++ b/tools/ota_analysis/src/services/echarts_data.js
@@ -5,11 +5,13 @@ export class EchartsData {
* @param {Map} statisticData
* @param {String} title
* @param {String} unit
+ * @param {Number} maximumEntries
*/
- constructor(statisticData, title, unit) {
+ constructor(statisticData, title, unit, maximumEntries = 15) {
this.statisticData = statisticData
this.title = title
this.unit = unit
+ this.maximumEntries = maximumEntries
}
/**
@@ -32,6 +34,9 @@ export class EchartsData {
* @return {Object} an ECharts option object.
*/
getEchartsOption() {
+ if (this.statisticData.size > this.maximumEntries) {
+ this.statisticData = trimMap(this.statisticData, this.maximumEntries)
+ }
let /** Object */ option = new Object()
option.title = {
text: this.title,
@@ -67,4 +72,39 @@ export class EchartsData {
]
return option
}
+}
+
+/**
+ * When there are too many entries in the map, the pie chart can be very
+ * crowded. This function will return the entries that have high values.
+ * Specifically, the top will be stored and the others
+ * will be added into an entry called 'other'.
+ * @param {Map} map
+ * @param {Number} maximumEntries
+ * @return {Map}
+ */
+function trimMap(map, maximumEntries) {
+ if (map.size <= maximumEntries) return map
+ let /** Map */ new_map = new Map()
+ for (let i=0; i curr) {
+ curr = value
+ currKey = key
+ }
+ }
+ }
+ new_map.set(currKey, curr)
+ }
+ let /** Number */ restTotal = 0
+ for (let [key, value] of map) {
+ if (!new_map.get(key)) {
+ restTotal += value
+ }
+ }
+ new_map.set('other', restTotal)
+ return new_map
}
\ No newline at end of file
diff --git a/tools/ota_analysis/src/services/map_parser.js b/tools/ota_analysis/src/services/map_parser.js
new file mode 100644
index 000000000..f19e24f45
--- /dev/null
+++ b/tools/ota_analysis/src/services/map_parser.js
@@ -0,0 +1,119 @@
+/**
+ * @fileoverview Class MapParser will take in a Android build and construct
+ * several file name maps (physical address: file name) according to it.
+ * The map of each partitions is added by calling MapParser.add(partitionName).
+ * You can query the file name being operated by calling
+ * MapParser.query(address, datalength).
+ */
+
+import * as zip from '@zip.js/zip.js/dist/zip-full.min.js'
+
+export class MapParser {
+ /**
+ * This class will take in a .zip Android build and construct a file type map
+ * @param {File} targetFile
+ */
+ constructor(targetFile) {
+ this.build = new zip.ZipReader(new zip.BlobReader(targetFile))
+ this.mapFiles = new Map()
+ this.maps = new Map()
+ }
+
+ /**
+ * Find the .map entries in the .zip build file. Store them as a map with
+ * pairs of (partition name: zip.js entry).
+ */
+ async init() {
+ let /** Array */ entries = await this.build.getEntries()
+ const /** RegExp*/ regexPath = /IMAGES\/[a-z_]*\.map/g;
+ const /** RegExp*/ regexName = /[\w_]+(?=\.map)/g
+ entries.forEach((entry) => {
+ if (entry.filename.match(regexPath)) {
+ this.mapFiles.set(entry.filename.match(regexName)[0], entry)
+ }
+ });
+ }
+
+ /**
+ * According to the .map in the build, build a map for later query.
+ * @param {String} partitionName
+ * @param {Number} totalLength
+ */
+ async add(partitionName, totalLength) {
+ let /** Array */ map = []
+ const /** RegExp */ regexNumber = /(? */fileEntries = mapText.split('\n')
+ // Each line of the .map file in Android build starts with the filename
+ // Followed by the block address, either a number or a range, for example:
+ // //system/apex/com.android.adbd.apex 54-66 66 66-2663
+ for (let entry of fileEntries) {
+ let /** Array */ elements = entry.split(' ')
+ for (let j = 1; j < elements.length; j++) {
+ let /** Number */ left = 0
+ let /** Number */ right = 0
+ if (elements[j].match(regexRange)) {
+ left = parseInt(elements[j].match(/\d+/g)[0])
+ right = parseInt(elements[j].match(/\d+/g)[1])
+ } else {
+ left = parseInt(elements[j].match(regexNumber))
+ right = parseInt(elements[j].match(regexNumber))
+ }
+ InsertMap(map, elements[0], left, right)
+ }
+ }
+ this.maps.set(partitionName, map)
+ }
+ else {
+ this.maps.set(partitionName, map)
+ }
+ }
+
+ /**
+ * Return the filename of given address.
+ * @param {String} partitionName
+ * @param {Array} extents
+ * @return {Array}
+ */
+ query(partitionName, extents) {
+ let /** Array */ names = []
+ let /** Array */ map = this.maps.get(partitionName)
+ for (let ext of extents) {
+ names.push(queryMap(map,
+ ext.startBlock,
+ ext.startBlock + ext.numBlocks))
+ }
+ return names
+ }
+}
+
+/**
+ * Fill in the hashtable from to using .
+ * @param {Array} map
+ * @param {String} name
+ * @param {Number} left
+ * @param {Number} right
+ */
+function InsertMap(map, name, left, right) {
+ for (let i = left; i <= right; i++) {
+ map[i] = name
+ }
+}
+
+/**
+ * Query the hashtable