xref: /aosp_15_r20/development/tools/ota_analysis/src/services/map_parser.js (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1/**
2 * @fileoverview Class MapParser will take in a Android build and construct
3 * several file name maps (physical address: file name) according to it.
4 * The map of each partitions is added by calling MapParser.add(partitionName).
5 * You can query the file name being operated by calling
6 * MapParser.query(address, datalength).
7 */
8
9import * as zip from '@zip.js/zip.js/dist/zip-full.min.js'
10
11export class MapParser {
12  /**
13   * This class will take in a .zip Android build and construct a file type map
14   * @param {File} targetFile
15   */
16  constructor(targetFile) {
17    this.build = new zip.ZipReader(new zip.BlobReader(targetFile))
18    this.mapFiles = new Map()
19    this.maps = new Map()
20  }
21
22  /**
23   * Find the .map entries in the .zip build file. Store them as a map with
24   * pairs of (partition name: zip.js entry).
25   */
26  async init() {
27    let /** Array<Entry> */ entries = await this.build.getEntries()
28    const /** RegExp*/ regexPath = /IMAGES\/[a-z_]*\.map/g;
29    const /** RegExp*/ regexName = /[\w_]+(?=\.map)/g
30    entries.forEach((entry) => {
31      if (entry.filename.match(regexPath)) {
32        this.mapFiles.set(entry.filename.match(regexName)[0], entry)
33      }
34    });
35  }
36
37  /**
38   * According to the .map in the build, build a map for later query.
39   * @param {String} partitionName
40   * @param {Number} totalLength
41   */
42  async add(partitionName, totalLength) {
43    let /** Array<String> */ map = []
44    const /** RegExp */ regexNumber = /\d+/g
45    const /** Reg */ regexRange = /\d+\-\d+/g
46    for (let i = 0; i < totalLength; i++) map[i] = 'unknown'
47    if (this.mapFiles.get(partitionName)) {
48      let /** String */mapText =
49        await this.mapFiles.get(partitionName).getData(
50          new zip.TextWriter()
51        )
52      let /** Array<String> */fileEntries = mapText.split('\n')
53      // Each line of the .map file in Android build starts with the filename
54      // Followed by the block address, either a number or a range, for example:
55      // //system/apex/com.android.adbd.apex 54-66 66 66-2663
56      for (let entry of fileEntries) {
57        let /** Array<String> */ elements = entry.split(' ')
58        for (let j = 1; j < elements.length; j++) {
59          let /** Number */ left = 0
60          let /** Number */ right = 0
61          if (elements[j].match(regexRange)) {
62            left = parseInt(elements[j].match(/\d+/g)[0])
63            right = parseInt(elements[j].match(/\d+/g)[1])
64          } else {
65            left = parseInt(elements[j].match(regexNumber))
66            right = parseInt(elements[j].match(regexNumber))
67          }
68          InsertMap(map, elements[0], left, right)
69        }
70      }
71      this.maps.set(partitionName, map)
72    }
73    else {
74      this.maps.set(partitionName, map)
75    }
76  }
77
78  /**
79   * Return the filename of given address.
80   * @param {String} partitionName
81   * @param {Array<PartitionUpdate>} extents
82   * @return {Array<String>}
83   */
84  query(partitionName, extents) {
85    let /** Array<String> */ names = []
86    let /** Array<String> */ map = this.maps.get(partitionName)
87    for (let ext of extents) {
88      names.push(queryMap(map,
89        ext.startBlock,
90        ext.startBlock + ext.numBlocks))
91    }
92    return names
93  }
94}
95
96/**
97 * Fill in the hashtable from <left> to <right> using <name>.
98 * @param {Array<String>} map
99 * @param {String} name
100 * @param {Number} left
101 * @param {Number} right
102 */
103function InsertMap(map, name, left, right) {
104  for (let i = left; i <= right; i++) {
105    map[i] = name
106  }
107}
108
109/**
110 * Query the hashtable <map> using index <address>.
111 * @param {Array<String>} map
112 * @param {Number} left
113 * @param {Number} right
114 */
115function queryMap(map, left, right) {
116  // Assuming the consecutive blocks belong to the same file
117  // Only the start block is queried here.
118  if (!map[left]) {
119    return 'unknown'
120  }
121  return map[left]
122}