• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16const commander = require('commander');
17const program = new commander.Command();
18const path = require('path');
19const fs = require('fs');
20const nodeCmd = require('node-cmd');
21
22class ModulesFetcher {
23  // Path to hdc, or path to hdc with hdc server ip.
24  private hdc_: string | undefined;
25  private pathChecksumMap_: Map<string, number>;
26
27  private moduleFile_: string = '';
28  private outputDir_: string = '';
29  private appName_: string = '';
30
31  constructor() {
32    this.pathChecksumMap_ = new Map<string, number>();
33  }
34
35  private parseCliArgs(): boolean {
36    program
37      .description('Fetch libraries from device in order to use them to create valide flamegraph.');
38    program
39      .requiredOption('-m, --module_file <type>', 'Input file with modules paths.')
40      .requiredOption('-o, --output_dir <type>', 'Directory in which libs will be fetched.')
41      .requiredOption('-a, --app_name <type>', 'Name of the application that was profiled.');
42
43    program.parse(process.argv);
44
45    const args = program.opts();
46    this.moduleFile_ = args.module_file;
47    this.outputDir_ = args.output_dir;
48    this.appName_ = args.app_name;
49
50    if (!fs.existsSync(this.outputDir_)) {
51      console.log('Directory ', this.outputDir_, 'doesnt exist.');
52      return false;
53    }
54
55    return true;
56  }
57
58  private isEnvVarsSet(): boolean {
59    let isEnvVarSet: boolean = true;
60
61    if (!process.env.HDC_PATH && !process.env.HDC_IP) {
62      console.log('Error in isEnvVarsSet: Please export HDC_PATH or HDC_IP(with port).');
63      isEnvVarSet = false;
64    }
65
66    return isEnvVarSet;
67  }
68
69  // file line format: <checksum path>
70  private parseModuleFile(): boolean {
71    const fileContent = fs.readFileSync(this.moduleFile_, 'utf-8');
72    fileContent.split(/\r?\n/).forEach((line) => {
73      if (line) {
74        let splitParts: Array<string> = line.split(' ');
75        let checksum: number = Number(splitParts[0]);
76        let path: string = this.mapSandboxDirToPhysicalDir(splitParts[1]);
77
78        if (!this.pathChecksumMap_.has(path)) {
79          this.pathChecksumMap_.set(path, checksum);
80        }
81      }
82    });
83
84    return !(this.pathChecksumMap_.size === 0);
85  }
86
87  // According to OHOS docs we need to do mapping in this way:
88  // /data/storage/el1/bundle ---> /data/app/el1/bundle/public/<PACKAGENAME>
89  private mapSandboxDirToPhysicalDir(path: string): string {
90    return path.replace(/\/data\/storage\/el1\/bundle/g, `/data/app/el1/bundle/public/${this.appName_}`);
91  }
92
93  public DoFetching(): boolean {
94    this.pathChecksumMap_.forEach((checksum: number, path: string) => {
95      if (!this.areLibrariesIdentical(path, checksum)) {
96        nodeCmd.runSync(`${this.hdc_} file recv ${path} ${this.outputDir_}`, (err, data, stderr) => console.log(data));
97      }
98    });
99
100    return true;
101  }
102
103  private areLibrariesIdentical(devicePath: string, deviceChecksum: number): boolean {
104    let hostFilepath : string = path.join(this.outputDir_, path.basename(devicePath));
105    if (!fs.existsSync(hostFilepath)) {
106      return false;
107    }
108
109    let hostChecksum: number = this.getHostChecksumFromPandaFile(hostFilepath);
110    return deviceChecksum === hostChecksum;
111  }
112
113  private getHostChecksumFromPandaFile(hostFilepath: string): number {
114    let magicSize: number = 8;
115    let magicArraySize: number = 1 * magicSize;
116    let checksumOffset: number = magicArraySize;
117
118    let data = fs.readFileSync(hostFilepath);
119
120    // read checksum (uint32_t) as a little-endian
121    return data.readUInt32LE(checksumOffset);
122  }
123
124  private getHdc(): string | undefined {
125    if (process.env.HDC_IP) {
126      return 'hdc'.concat(' -s ', process.env.HDC_IP);
127    } else if (process.env.HDC_PATH) {
128      return process.env.HDC_PATH;
129    }
130
131    return undefined;
132  }
133
134  public fetchModulesFromDevice(): void {
135    if (!this.isEnvVarsSet()) {
136      console.log('Error: enviroment variables is not set');
137      return;
138    }
139
140    this.hdc_ = this.getHdc();
141    if (this.hdc_ === undefined) {
142      console.log('Error: cant get hdc.');
143      return;
144    }
145
146    if (!this.parseCliArgs()) {
147      console.log('Error: error when parsing CLI args.');
148      return;
149    }
150
151    if (!this.parseModuleFile()) {
152      console.log('Error: can not parse module file.');
153      return;
154    }
155
156    if (!this.DoFetching()) {
157      console.log('Error: Can not fetch modules from device.');
158      return;
159    }
160  }
161}
162
163const fetcher = new ModulesFetcher();
164fetcher.fetchModulesFromDevice();
165