1ba991379Sopenharmony_ci#!/usr/bin/env python3 2ba991379Sopenharmony_ci# -*- coding: utf-8 -*- 3ba991379Sopenharmony_ci# Copyright (c) 2022 Huawei Device Co., Ltd. 4ba991379Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 5ba991379Sopenharmony_ci# you may not use this file except in compliance with the License. 6ba991379Sopenharmony_ci# You may obtain a copy of the License at 7ba991379Sopenharmony_ci# 8ba991379Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 9ba991379Sopenharmony_ci# 10ba991379Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software 11ba991379Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 12ba991379Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ba991379Sopenharmony_ci# See the License for the specific language governing permissions and 14ba991379Sopenharmony_ci# limitations under the License. 15ba991379Sopenharmony_ci 16ba991379Sopenharmony_ci# This file is to implement the rom analyzation of standard device. 17ba991379Sopenharmony_ci# 18ba991379Sopenharmony_ci 19ba991379Sopenharmony_ciimport argparse 20ba991379Sopenharmony_ciimport copy 21ba991379Sopenharmony_ciimport glob 22ba991379Sopenharmony_ciimport json 23ba991379Sopenharmony_ciimport os 24ba991379Sopenharmony_ciimport re 25ba991379Sopenharmony_ciimport sys 26ba991379Sopenharmony_ciimport subprocess 27ba991379Sopenharmony_ciimport typing 28ba991379Sopenharmony_ciimport xml.dom.minidom as dom 29ba991379Sopenharmony_cifrom typing import Dict 30ba991379Sopenharmony_cifrom pprint import pprint 31ba991379Sopenharmony_cifrom pkgs.basic_tool import unit_adaptive 32ba991379Sopenharmony_ci 33ba991379Sopenharmony_cifrom pkgs.simple_excel_writer import SimpleExcelWriter 34ba991379Sopenharmony_ci 35ba991379Sopenharmony_cidebug = True if sys.gettrace() else False 36ba991379Sopenharmony_ci 37ba991379Sopenharmony_ci 38ba991379Sopenharmony_ciclass HDCTool: 39ba991379Sopenharmony_ci @classmethod 40ba991379Sopenharmony_ci def verify_hdc(cls, verify_str: str = "OpenHarmony") -> bool: 41ba991379Sopenharmony_ci """ 42ba991379Sopenharmony_ci 验证hdc是否可用 43ba991379Sopenharmony_ci True:可用 44ba991379Sopenharmony_ci False:不可用 45ba991379Sopenharmony_ci """ 46ba991379Sopenharmony_ci cp = subprocess.run(["hdc", "--help"], capture_output=True) 47ba991379Sopenharmony_ci stdout = str(cp.stdout) 48ba991379Sopenharmony_ci stderr = str(cp.stderr) 49ba991379Sopenharmony_ci return verify_str in stdout or verify_str in stderr 50ba991379Sopenharmony_ci 51ba991379Sopenharmony_ci @classmethod 52ba991379Sopenharmony_ci def verify_device(cls, device_num: str) -> bool: 53ba991379Sopenharmony_ci """ 54ba991379Sopenharmony_ci 验证设备是否已经连接 55ba991379Sopenharmony_ci True:已连接 56ba991379Sopenharmony_ci False:未连接 57ba991379Sopenharmony_ci """ 58ba991379Sopenharmony_ci cp = subprocess.run(["hdc", "list", "targets"], capture_output=True) 59ba991379Sopenharmony_ci stdout = str(cp.stdout) 60ba991379Sopenharmony_ci stderr = str(cp.stderr) 61ba991379Sopenharmony_ci return device_num in stderr or device_num in stdout 62ba991379Sopenharmony_ci 63ba991379Sopenharmony_ci @classmethod 64ba991379Sopenharmony_ci def exec(cls, args: list, output_from: str = "stdout"): 65ba991379Sopenharmony_ci cp = subprocess.run(args, capture_output=True) 66ba991379Sopenharmony_ci if output_from == "stdout": 67ba991379Sopenharmony_ci return cp.stdout.decode() 68ba991379Sopenharmony_ci elif output_from == "stderr": 69ba991379Sopenharmony_ci return cp.stderr.decode() 70ba991379Sopenharmony_ci else: 71ba991379Sopenharmony_ci print("error: 'output_from' must be stdout or stdin") 72ba991379Sopenharmony_ci 73ba991379Sopenharmony_ci 74ba991379Sopenharmony_cidef delete_values_from_dict(target_dict: typing.Dict, key_list: typing.Iterable): 75ba991379Sopenharmony_ci for k in key_list: 76ba991379Sopenharmony_ci if k not in target_dict.keys(): 77ba991379Sopenharmony_ci continue 78ba991379Sopenharmony_ci del target_dict[k] 79ba991379Sopenharmony_ci 80ba991379Sopenharmony_ci 81ba991379Sopenharmony_ciclass RamAnalyzer: 82ba991379Sopenharmony_ci @classmethod 83ba991379Sopenharmony_ci def analysis(cls, cfg_path: str, json_path: str, rom_result_json: str, device_num: str, 84ba991379Sopenharmony_ci output_file: str, ss: str, output_excel: bool, baseline_file: str, unit_adapt: bool): 85ba991379Sopenharmony_ci """ 86ba991379Sopenharmony_ci process size subsystem/component so so_size 87ba991379Sopenharmony_ci """ 88ba991379Sopenharmony_ci if not HDCTool.verify_hdc(): 89ba991379Sopenharmony_ci print("error: Command 'hdc' not found") 90ba991379Sopenharmony_ci return 91ba991379Sopenharmony_ci if not HDCTool.verify_device(device_num): 92ba991379Sopenharmony_ci print("error: {} is inaccessible or not found".format(device_num)) 93ba991379Sopenharmony_ci return 94ba991379Sopenharmony_ci with open(rom_result_json, 'r', encoding='utf-8') as f: 95ba991379Sopenharmony_ci rom_result_dict: typing.Dict = json.loads(f.read()) 96ba991379Sopenharmony_ci # 从rom的分析结果中将需要的elf信息重组 97ba991379Sopenharmony_ci so_info_dict: typing.Dict[ 98ba991379Sopenharmony_ci str, typing.Dict[str["component_name|subsystem_name|size"], str]] = cls.get_elf_info_from_rom_result( 99ba991379Sopenharmony_ci rom_result_json) 100ba991379Sopenharmony_ci process_elf_dict: typing.Dict[str, typing.List[str]] = cls.get_process_so_relationship(cfg_path, 101ba991379Sopenharmony_ci json_path) 102ba991379Sopenharmony_ci process_size_dict: typing.Dict[str, int] = cls.process_hidumper_info( 103ba991379Sopenharmony_ci device_num, ss) 104ba991379Sopenharmony_ci result_dict: typing.Dict[str, typing.Dict[str, typing.Any]] = dict() 105ba991379Sopenharmony_ci result_dict = cls.result_process4(result_dict, process_size_dict, rom_result_dict, process_elf_dict, 106ba991379Sopenharmony_ci so_info_dict) 107ba991379Sopenharmony_ci base_dir, _ = os.path.split(output_file) 108ba991379Sopenharmony_ci if len(base_dir) != 0 and not os.path.isdir(base_dir): 109ba991379Sopenharmony_ci os.makedirs(base_dir, exist_ok=True) 110ba991379Sopenharmony_ci with os.fdopen(os.open(output_file + ".json", os.O_WRONLY | os.O_CREAT, mode=0o640), 'w', encoding='utf-8') as f: 111ba991379Sopenharmony_ci json.dump(result_dict, f, indent=4) 112ba991379Sopenharmony_ci refactored_result: Dict[str, Dict] = refacotr_result(result_dict) 113ba991379Sopenharmony_ci if unit_adapt: 114ba991379Sopenharmony_ci cls.refactored_result_unit_adaptive(refactored_result) 115ba991379Sopenharmony_ci if baseline_file: 116ba991379Sopenharmony_ci cls.add_baseline(refactored_result, baseline_file) 117ba991379Sopenharmony_ci with os.fdopen(os.open(f"refactored_{output_file}.json", os.O_WRONLY | os.O_CREAT, mode=0o640), 'w', encoding='utf-8') as f: 118ba991379Sopenharmony_ci json.dump(refactored_result, f, indent=4) 119ba991379Sopenharmony_ci if output_excel: 120ba991379Sopenharmony_ci cls.__save_result_as_excel( 121ba991379Sopenharmony_ci refactored_result, output_file + ".xls", ss, baseline_file, unit_adapt) 122ba991379Sopenharmony_ci 123ba991379Sopenharmony_ci __ss_dict: typing.Dict[str, int] = { 124ba991379Sopenharmony_ci "Pss": 2, 125ba991379Sopenharmony_ci "Vss": 3, 126ba991379Sopenharmony_ci "Rss": 4, 127ba991379Sopenharmony_ci "Uss": 5 128ba991379Sopenharmony_ci } 129ba991379Sopenharmony_ci 130ba991379Sopenharmony_ci @classmethod 131ba991379Sopenharmony_ci def process_hidumper_info(cls, device_num: str, ss: str) -> typing.Dict[str, int]: 132ba991379Sopenharmony_ci """ 133ba991379Sopenharmony_ci 处理进程名与对应进程大小 134ba991379Sopenharmony_ci """ 135ba991379Sopenharmony_ci 136ba991379Sopenharmony_ci def exec_once() -> typing.Dict[str, int]: 137ba991379Sopenharmony_ci stdout = HDCTool.exec( 138ba991379Sopenharmony_ci ["hdc", "-t", device_num, "shell", "hidumper", "--mem"]) 139ba991379Sopenharmony_ci name_size_dict = cls.__parse_hidumper_mem(stdout, device_num, ss) 140ba991379Sopenharmony_ci return name_size_dict 141ba991379Sopenharmony_ci 142ba991379Sopenharmony_ci if not HDCTool.verify_hdc(): 143ba991379Sopenharmony_ci print("error: Command 'hdc' not found") 144ba991379Sopenharmony_ci return dict() 145ba991379Sopenharmony_ci if not HDCTool.verify_device(device_num): 146ba991379Sopenharmony_ci print("error: {} is inaccessible or not found".format(device_num)) 147ba991379Sopenharmony_ci return dict() 148ba991379Sopenharmony_ci 149ba991379Sopenharmony_ci return exec_once() 150ba991379Sopenharmony_ci 151ba991379Sopenharmony_ci @classmethod 152ba991379Sopenharmony_ci def get_elf_info_from_rom_result(cls, rom_result_json: str) -> typing.Dict[str, typing.Dict[str, str]]: 153ba991379Sopenharmony_ci """ 154ba991379Sopenharmony_ci 利用rom_analyzer.py的分析结果,重组成 155ba991379Sopenharmony_ci {file_base_name: {"subsystem_name":subsystem_name, "component_name":component_name}} 156ba991379Sopenharmony_ci 的形式 157ba991379Sopenharmony_ci """ 158ba991379Sopenharmony_ci with open(rom_result_json, 'r', encoding='utf-8') as f: 159ba991379Sopenharmony_ci rom_info_dict = json.load(f) 160ba991379Sopenharmony_ci elf_info_dict: typing.Dict[str, typing.Dict[str, str]] = dict() 161ba991379Sopenharmony_ci for subsystem_name in rom_info_dict.keys(): 162ba991379Sopenharmony_ci sub_val_dict: typing.Dict[str, typing.Any] = rom_info_dict.get( 163ba991379Sopenharmony_ci subsystem_name) 164ba991379Sopenharmony_ci delete_values_from_dict(sub_val_dict, ["size", "file_count"]) 165ba991379Sopenharmony_ci for component_name in sub_val_dict.keys(): 166ba991379Sopenharmony_ci component_val_dict: typing.Dict[str, str] = sub_val_dict.get( 167ba991379Sopenharmony_ci component_name) 168ba991379Sopenharmony_ci delete_values_from_dict(component_val_dict, [ 169ba991379Sopenharmony_ci "size", "file_count"]) 170ba991379Sopenharmony_ci for file_name, size in component_val_dict.items(): 171ba991379Sopenharmony_ci file_basename: str = os.path.split(file_name)[-1] 172ba991379Sopenharmony_ci elf_info_dict[file_basename] = { 173ba991379Sopenharmony_ci "subsystem_name": subsystem_name, 174ba991379Sopenharmony_ci "component_name": component_name, 175ba991379Sopenharmony_ci "size": size 176ba991379Sopenharmony_ci } 177ba991379Sopenharmony_ci 178ba991379Sopenharmony_ci return elf_info_dict 179ba991379Sopenharmony_ci 180ba991379Sopenharmony_ci @classmethod 181ba991379Sopenharmony_ci def get_process_so_relationship(cls, cfg_path: str, profile_path: str) -> typing.Dict[ 182ba991379Sopenharmony_ci str, typing.List[str]]: 183ba991379Sopenharmony_ci """ 184ba991379Sopenharmony_ci parse the relationship between process and elf file 185ba991379Sopenharmony_ci """ 186ba991379Sopenharmony_ci # 从merged_sa里面收集 187ba991379Sopenharmony_ci process_elf_dict: typing.Dict[str, typing.List[str]] = dict() 188ba991379Sopenharmony_ci cfg_list = glob.glob(cfg_path + os.sep + "*.cfg", recursive=True) 189ba991379Sopenharmony_ci for cfg in cfg_list: 190ba991379Sopenharmony_ci if debug: 191ba991379Sopenharmony_ci print("parsing: ", cfg) 192ba991379Sopenharmony_ci try: 193ba991379Sopenharmony_ci cls.__parse_process_cfg(cfg, profile_path, process_elf_dict) 194ba991379Sopenharmony_ci except: 195ba991379Sopenharmony_ci print("parse '{}' failed".format(cfg)) 196ba991379Sopenharmony_ci finally: 197ba991379Sopenharmony_ci ... 198ba991379Sopenharmony_ci return process_elf_dict 199ba991379Sopenharmony_ci 200ba991379Sopenharmony_ci @classmethod 201ba991379Sopenharmony_ci def find_elf_size_from_rom_result(cls, service_name: str, subsystem_name: str, component_name: str, 202ba991379Sopenharmony_ci evaluator: typing.Callable, rom_result_dict: typing.Dict[str, typing.Dict]) -> \ 203ba991379Sopenharmony_ci typing.Tuple[ 204ba991379Sopenharmony_ci bool, str, str, int]: 205ba991379Sopenharmony_ci """ 206ba991379Sopenharmony_ci 全局查找进程的相关elf文件 207ba991379Sopenharmony_ci subsystem_name与component_name可明确指定,或为*以遍历整个dict 208ba991379Sopenharmony_ci evaluator:评估elf文件的从phone下面开始的路径与service_name的关系,评判如何才是找到了 209ba991379Sopenharmony_ci returns: 是否查找到,elf文件名,部件名,size 210ba991379Sopenharmony_ci """ 211ba991379Sopenharmony_ci subsystem_name_list = [ 212ba991379Sopenharmony_ci subsystem_name] if subsystem_name != "*" else rom_result_dict.keys() 213ba991379Sopenharmony_ci for sn in subsystem_name_list: 214ba991379Sopenharmony_ci sub_val_dict = rom_result_dict.get(sn) 215ba991379Sopenharmony_ci component_name_list = [ 216ba991379Sopenharmony_ci component_name] if component_name != '*' else sub_val_dict.keys() 217ba991379Sopenharmony_ci for cn in component_name_list: 218ba991379Sopenharmony_ci if cn == "size" or cn == "file_count": 219ba991379Sopenharmony_ci continue 220ba991379Sopenharmony_ci component_val_dict: typing.Dict[str, 221ba991379Sopenharmony_ci int] = sub_val_dict.get(cn) 222ba991379Sopenharmony_ci for k, v in component_val_dict.items(): 223ba991379Sopenharmony_ci if k == "size" or k == "file_count": 224ba991379Sopenharmony_ci continue 225ba991379Sopenharmony_ci if not evaluator(service_name, k): 226ba991379Sopenharmony_ci continue 227ba991379Sopenharmony_ci return True, os.path.split(k)[-1], sn, cn, v 228ba991379Sopenharmony_ci return False, str(), str(), str(), int() 229ba991379Sopenharmony_ci 230ba991379Sopenharmony_ci @classmethod 231ba991379Sopenharmony_ci def add_baseline(self, refactored_result_dict: Dict, baseline_file: str) -> None: 232ba991379Sopenharmony_ci with open(baseline_file, 'r', encoding='utf-8') as f: 233ba991379Sopenharmony_ci baseline_dict = json.load(f) 234ba991379Sopenharmony_ci for subsystem_name, subsystem_info in refactored_result_dict.items(): 235ba991379Sopenharmony_ci for component_name, component_info in subsystem_info.items(): 236ba991379Sopenharmony_ci if component_name == "size": 237ba991379Sopenharmony_ci continue 238ba991379Sopenharmony_ci if not baseline_dict.get(subsystem_name): 239ba991379Sopenharmony_ci continue 240ba991379Sopenharmony_ci if not baseline_dict[subsystem_name].get(component_name): 241ba991379Sopenharmony_ci continue 242ba991379Sopenharmony_ci component_info["baseline"] = baseline_dict[subsystem_name][component_name].get( 243ba991379Sopenharmony_ci "ram") 244ba991379Sopenharmony_ci 245ba991379Sopenharmony_ci @classmethod 246ba991379Sopenharmony_ci def inside_refactored_result_unit_adaptive(cls, process_info): 247ba991379Sopenharmony_ci for elf_name, elf_size in process_info["elf"].items(): 248ba991379Sopenharmony_ci process_info["elf"][elf_name] = unit_adaptive(elf_size) 249ba991379Sopenharmony_ci return process_info 250ba991379Sopenharmony_ci 251ba991379Sopenharmony_ci @classmethod 252ba991379Sopenharmony_ci def refactored_result_unit_adaptive(cls, result_dict: Dict[str, Dict]) -> None: 253ba991379Sopenharmony_ci for subsystem_name, subsystem_info in result_dict.items(): 254ba991379Sopenharmony_ci sub_size = unit_adaptive(subsystem_info["size"]) 255ba991379Sopenharmony_ci del subsystem_info["size"] 256ba991379Sopenharmony_ci for component_name, component_info in subsystem_info.items(): 257ba991379Sopenharmony_ci com_size = unit_adaptive(component_info["size"]) 258ba991379Sopenharmony_ci del component_info["size"] 259ba991379Sopenharmony_ci for process_name, process_info in component_info.items(): 260ba991379Sopenharmony_ci pro_size = unit_adaptive(process_info["size"]) 261ba991379Sopenharmony_ci del process_info["size"] 262ba991379Sopenharmony_ci process_info = cls.inside_refactored_result_unit_adaptive(process_info) 263ba991379Sopenharmony_ci process_info["size"] = pro_size 264ba991379Sopenharmony_ci component_info["size"] = com_size 265ba991379Sopenharmony_ci subsystem_info["size"] = sub_size 266ba991379Sopenharmony_ci 267ba991379Sopenharmony_ci @classmethod 268ba991379Sopenharmony_ci def result_process1(cls, result_dict, process_name, process_size, elf, size): 269ba991379Sopenharmony_ci result_dict[process_name] = dict() 270ba991379Sopenharmony_ci result_dict[process_name]["size"] = process_size 271ba991379Sopenharmony_ci result_dict[process_name]["startup"] = dict() 272ba991379Sopenharmony_ci result_dict[process_name]["startup"]["init"] = dict() 273ba991379Sopenharmony_ci result_dict[process_name]["startup"]["init"][elf if len( 274ba991379Sopenharmony_ci elf) != 0 else "UNKNOWN"] = size 275ba991379Sopenharmony_ci return result_dict 276ba991379Sopenharmony_ci 277ba991379Sopenharmony_ci @classmethod 278ba991379Sopenharmony_ci def result_process2(cls, result_dict, process_name, subsystem_name, process_size, component_name, hap_name, size): 279ba991379Sopenharmony_ci result_dict[process_name] = dict() 280ba991379Sopenharmony_ci result_dict[process_name]["size"] = process_size 281ba991379Sopenharmony_ci result_dict[process_name][subsystem_name] = dict() 282ba991379Sopenharmony_ci result_dict[process_name][subsystem_name][component_name] = dict() 283ba991379Sopenharmony_ci result_dict[process_name][subsystem_name][component_name][hap_name if len( 284ba991379Sopenharmony_ci hap_name) != 0 else "UNKNOWN"] = size 285ba991379Sopenharmony_ci return result_dict 286ba991379Sopenharmony_ci 287ba991379Sopenharmony_ci @classmethod 288ba991379Sopenharmony_ci def result_process3(cls, result_dict, process_name, process_size): 289ba991379Sopenharmony_ci result_dict[process_name] = dict() 290ba991379Sopenharmony_ci result_dict[process_name]["size"] = process_size 291ba991379Sopenharmony_ci result_dict[process_name]["UNKNOWN"] = dict() 292ba991379Sopenharmony_ci result_dict[process_name]["UNKNOWN"]["UNKNOWN"] = dict() 293ba991379Sopenharmony_ci result_dict[process_name]["UNKNOWN"]["UNKNOWN"]["UNKNOWN"] = int() 294ba991379Sopenharmony_ci return result_dict 295ba991379Sopenharmony_ci 296ba991379Sopenharmony_ci @classmethod 297ba991379Sopenharmony_ci def result_process4(cls, result_dict, process_size_dict, rom_result_dict, process_elf_dict, so_info_dict): 298ba991379Sopenharmony_ci def get(key: typing.Any, dt: typing.Dict[str, typing.Any]): 299ba991379Sopenharmony_ci for k, v in dt.items(): 300ba991379Sopenharmony_ci if k.startswith(key) or (len(v) > 0 and key == v[0]): 301ba991379Sopenharmony_ci # 要么uinput_inject的对应key为mmi_uinput_inject。对于此类特殊处理,即:如果service_name找不到,但是直接执行的bin等于这个名字,也认为找到 302ba991379Sopenharmony_ci return v 303ba991379Sopenharmony_ci 304ba991379Sopenharmony_ci for process_name, process_size in process_size_dict.items(): # 从进程出发 305ba991379Sopenharmony_ci if not process_name: 306ba991379Sopenharmony_ci print("warning: an empty 'process_name' has been found.") 307ba991379Sopenharmony_ci continue 308ba991379Sopenharmony_ci # 如果部件是init,特殊处理 309ba991379Sopenharmony_ci if process_name == "init": 310ba991379Sopenharmony_ci _, elf, _, _, size = cls.find_elf_size_from_rom_result(process_name, "startup", "init", 311ba991379Sopenharmony_ci lambda x, y: os.path.split(y)[ 312ba991379Sopenharmony_ci -1].lower() == x.lower(), 313ba991379Sopenharmony_ci rom_result_dict) 314ba991379Sopenharmony_ci result_dict = cls.result_process1(result_dict, process_name, process_size, elf, size) 315ba991379Sopenharmony_ci continue 316ba991379Sopenharmony_ci # 如果是hap,特殊处理 317ba991379Sopenharmony_ci if (process_name.startswith("com.") or process_name.startswith("ohos.")): 318ba991379Sopenharmony_ci _, hap_name, subsystem_name, component_name, size = cls.find_elf_size_from_rom_result(process_name, "*", 319ba991379Sopenharmony_ci "*", 320ba991379Sopenharmony_ci lambda x, y: len( 321ba991379Sopenharmony_ci y.split( 322ba991379Sopenharmony_ci '/')) >= 3 and x.lower().startswith( 323ba991379Sopenharmony_ci y.split('/')[ 324ba991379Sopenharmony_ci 2].lower()), 325ba991379Sopenharmony_ci rom_result_dict) 326ba991379Sopenharmony_ci result_dict = cls.result_process2(result_dict, process_name, subsystem_name, process_size, 327ba991379Sopenharmony_ci component_name, hap_name, size) 328ba991379Sopenharmony_ci continue 329ba991379Sopenharmony_ci # 得到进程相关的elf文件list 330ba991379Sopenharmony_ci so_list: list = get(process_name, process_elf_dict) 331ba991379Sopenharmony_ci if so_list is None: 332ba991379Sopenharmony_ci print("warning: process '{}' not found in .json or .cfg".format( 333ba991379Sopenharmony_ci process_name)) 334ba991379Sopenharmony_ci result_dict = cls.result_process3(result_dict, process_name, process_size) 335ba991379Sopenharmony_ci continue 336ba991379Sopenharmony_ci result_dict[process_name] = dict() 337ba991379Sopenharmony_ci result_dict[process_name]["size"] = process_size 338ba991379Sopenharmony_ci for so in so_list: 339ba991379Sopenharmony_ci unit = so_info_dict.get(so) 340ba991379Sopenharmony_ci if unit is None: 341ba991379Sopenharmony_ci result_dict[process_name]["UNKNOWN"] = dict() 342ba991379Sopenharmony_ci result_dict[process_name]["UNKNOWN"]["UNKNOWN"] = dict() 343ba991379Sopenharmony_ci result_dict[process_name]["UNKNOWN"]["UNKNOWN"][so] = int() 344ba991379Sopenharmony_ci print("warning: '{}' in {} not found in json from rom analysis result".format( 345ba991379Sopenharmony_ci so, process_name)) 346ba991379Sopenharmony_ci continue 347ba991379Sopenharmony_ci component_name = unit.get("component_name") 348ba991379Sopenharmony_ci subsystem_name = unit.get("subsystem_name") 349ba991379Sopenharmony_ci so_size = unit.get("size") 350ba991379Sopenharmony_ci if result_dict.get(process_name).get(subsystem_name) is None: 351ba991379Sopenharmony_ci result_dict[process_name][subsystem_name] = dict() 352ba991379Sopenharmony_ci if result_dict.get(process_name).get(subsystem_name).get(component_name) is None: 353ba991379Sopenharmony_ci result_dict[process_name][subsystem_name][component_name] = dict() 354ba991379Sopenharmony_ci result_dict[process_name][subsystem_name][component_name][so] = so_size 355ba991379Sopenharmony_ci return result_dict 356ba991379Sopenharmony_ci 357ba991379Sopenharmony_ci @classmethod 358ba991379Sopenharmony_ci def __hidumper_mem_line_process(cls, content: typing.Text) -> typing.List[typing.Text]: 359ba991379Sopenharmony_ci """ 360ba991379Sopenharmony_ci 将hidumper的拥有的数据行进行分割,得到 361ba991379Sopenharmony_ci [pid, name, pss, vss, rss, uss]格式的list 362ba991379Sopenharmony_ci """ 363ba991379Sopenharmony_ci trival_pattern = re.compile(r"kB|\(.*\)(?#去除单位kB以及小括号内的任意数据,包括小括号)") 364ba991379Sopenharmony_ci content = re.sub(trival_pattern, "", content) 365ba991379Sopenharmony_ci blank_pattern = re.compile(r"\s+(?#匹配一个或多个空格)") 366ba991379Sopenharmony_ci return re.sub(blank_pattern, ' ', content.strip()).split() 367ba991379Sopenharmony_ci 368ba991379Sopenharmony_ci @classmethod 369ba991379Sopenharmony_ci def __parse_hidumper_mem(cls, content: typing.Text, device_num: str, ss: str = "Pss") -> typing.Dict[ 370ba991379Sopenharmony_ci typing.Text, int]: 371ba991379Sopenharmony_ci """ 372ba991379Sopenharmony_ci 解析:hidumper --meme的结果 373ba991379Sopenharmony_ci 返回{process_name: pss}形式的字典 374ba991379Sopenharmony_ci '248 samgr 1464(0 in SwapPss) kB 15064 kB 6928 kB 1072 kB\r' 375ba991379Sopenharmony_ci """ 376ba991379Sopenharmony_ci 377ba991379Sopenharmony_ci def find_full_process_name(hname: str) -> str: 378ba991379Sopenharmony_ci for lname in __process_name_list: 379ba991379Sopenharmony_ci if lname.startswith(hname): 380ba991379Sopenharmony_ci return lname 381ba991379Sopenharmony_ci return str() 382ba991379Sopenharmony_ci 383ba991379Sopenharmony_ci def process_ps_ef(content: str) -> list: 384ba991379Sopenharmony_ci line_list = content.strip().split("\n")[1:] 385ba991379Sopenharmony_ci process_name_list = list() 386ba991379Sopenharmony_ci for line in line_list: 387ba991379Sopenharmony_ci process_name = line.split()[7] 388ba991379Sopenharmony_ci if process_name.startswith('['): 389ba991379Sopenharmony_ci continue 390ba991379Sopenharmony_ci process_name_list.append(process_name) 391ba991379Sopenharmony_ci return process_name_list 392ba991379Sopenharmony_ci 393ba991379Sopenharmony_ci if ss not in cls.__ss_dict.keys(): 394ba991379Sopenharmony_ci print("error: {} is not a valid parameter".format(ss)) 395ba991379Sopenharmony_ci return dict() 396ba991379Sopenharmony_ci output = content.split('\n') 397ba991379Sopenharmony_ci process_pss_dict = dict() 398ba991379Sopenharmony_ci __process_name_list: typing.List[str] = process_ps_ef( 399ba991379Sopenharmony_ci HDCTool.exec(["hdc", "-t", device_num, "shell", "ps", "-ef"])) 400ba991379Sopenharmony_ci for line in output: 401ba991379Sopenharmony_ci if "Total Memory Usage by Size" in line: 402ba991379Sopenharmony_ci break 403ba991379Sopenharmony_ci if line.isspace(): 404ba991379Sopenharmony_ci continue 405ba991379Sopenharmony_ci processed: typing.List[typing.Text] = cls.__hidumper_mem_line_process( 406ba991379Sopenharmony_ci line) 407ba991379Sopenharmony_ci # 如果第一列不是数字(pid),就过 408ba991379Sopenharmony_ci if not processed or not processed[0].isnumeric(): 409ba991379Sopenharmony_ci continue 410ba991379Sopenharmony_ci name = processed[1] # 否则的话就取名字,和对应的size 411ba991379Sopenharmony_ci size = int(processed[cls.__ss_dict.get(ss)]) * \ 412ba991379Sopenharmony_ci 1024 # kilo byte to byte 413ba991379Sopenharmony_ci full_process_name = find_full_process_name(name) 414ba991379Sopenharmony_ci if not full_process_name: 415ba991379Sopenharmony_ci print( 416ba991379Sopenharmony_ci f"warning: process \"{full_process_name}\" not found in the result of command \"ps -ef\"") 417ba991379Sopenharmony_ci continue 418ba991379Sopenharmony_ci process_pss_dict[full_process_name] = size 419ba991379Sopenharmony_ci return process_pss_dict 420ba991379Sopenharmony_ci 421ba991379Sopenharmony_ci @classmethod 422ba991379Sopenharmony_ci def __parse_process_json(cls, file_path: str, result_dict: typing.Dict[str, typing.List[str]]): 423ba991379Sopenharmony_ci """ 424ba991379Sopenharmony_ci 解析json文件,结存存入 result_dict中,格式:{process_name: os_list} 425ba991379Sopenharmony_ci 其中,so_list中是so的base_name 426ba991379Sopenharmony_ci """ 427ba991379Sopenharmony_ci if not (os.path.isfile(file_path) and file_path.endswith(".json")): 428ba991379Sopenharmony_ci print("warning: {} not exist or not a json file".format(file_path)) 429ba991379Sopenharmony_ci return 430ba991379Sopenharmony_ci with open(file_path, 'r', encoding='utf-8') as f: 431ba991379Sopenharmony_ci j_content: typing.Dict[str, typing.Any] = json.load(f) 432ba991379Sopenharmony_ci if "process" not in j_content.keys() or "systemability" not in j_content.keys(): 433ba991379Sopenharmony_ci print( 434ba991379Sopenharmony_ci f"warning: {file_path} has no field 'process' or 'systemability'") 435ba991379Sopenharmony_ci return 436ba991379Sopenharmony_ci process_name: str = j_content.get("process") 437ba991379Sopenharmony_ci elf_list: typing.List[str] = list() 438ba991379Sopenharmony_ci for sa in j_content.get("systemability"): 439ba991379Sopenharmony_ci libpath: str = sa.get("libpath") 440ba991379Sopenharmony_ci if not libpath: 441ba991379Sopenharmony_ci continue 442ba991379Sopenharmony_ci elf_list.append(libpath) 443ba991379Sopenharmony_ci result_dict[process_name] = elf_list 444ba991379Sopenharmony_ci 445ba991379Sopenharmony_ci @classmethod 446ba991379Sopenharmony_ci def __parse_process_cfg(cls, cfg_path: str, profile_path: str, result_dict: dict): 447ba991379Sopenharmony_ci """ 448ba991379Sopenharmony_ci 解析cfg,因为有的cfg会拉起xml中的进程,所以也可能会去解析xml 449ba991379Sopenharmony_ci """ 450ba991379Sopenharmony_ci with open(cfg_path, 'r', encoding='utf-8') as f: 451ba991379Sopenharmony_ci cfg_dict = json.loads(f.read()) 452ba991379Sopenharmony_ci services = cfg_dict.get("services") 453ba991379Sopenharmony_ci if services is None: 454ba991379Sopenharmony_ci print("warning: 'services' not in {}".format(cfg_path)) 455ba991379Sopenharmony_ci return 456ba991379Sopenharmony_ci for service in services: 457ba991379Sopenharmony_ci process_name = service.get("name") 458ba991379Sopenharmony_ci first, *path_list = service.get("path") 459ba991379Sopenharmony_ci if first.endswith("sa_main"): 460ba991379Sopenharmony_ci # 由sa_main去来起进程 461ba991379Sopenharmony_ci xml_base_name = os.path.split(path_list[0])[-1] 462ba991379Sopenharmony_ci cls.__parse_process_json(os.path.join( 463ba991379Sopenharmony_ci profile_path, xml_base_name), result_dict) 464ba991379Sopenharmony_ci else: 465ba991379Sopenharmony_ci # 直接执行 466ba991379Sopenharmony_ci if result_dict.get(process_name) is None: 467ba991379Sopenharmony_ci result_dict[process_name] = list() 468ba991379Sopenharmony_ci result_dict.get(process_name).append(os.path.split(first)[-1]) 469ba991379Sopenharmony_ci 470ba991379Sopenharmony_ci @classmethod 471ba991379Sopenharmony_ci def __inside_save_result_as_excel(cls, baseline_file, subsystem_name, component_name, component_size, 472ba991379Sopenharmony_ci component_baseline, process_name, process_size, elf_name, elf_size): 473ba991379Sopenharmony_ci if baseline_file: 474ba991379Sopenharmony_ci return [subsystem_name, component_name, component_size, 475ba991379Sopenharmony_ci component_baseline, process_name, process_size, elf_name, elf_size] 476ba991379Sopenharmony_ci else: 477ba991379Sopenharmony_ci return [subsystem_name, component_name, component_size, 478ba991379Sopenharmony_ci process_name, process_size, elf_name, elf_size] 479ba991379Sopenharmony_ci 480ba991379Sopenharmony_ci @classmethod 481ba991379Sopenharmony_ci def __save_result_as_excel(cls, data_dict: dict, filename: str, ss: str, baseline_file: str, unit_adapt: bool): 482ba991379Sopenharmony_ci """ 483ba991379Sopenharmony_ci 保存结果到excel中 484ba991379Sopenharmony_ci 子系统:{ 485ba991379Sopenharmony_ci "size": 1234, 486ba991379Sopenharmony_ci 部件:{ 487ba991379Sopenharmony_ci "size":123, 488ba991379Sopenharmony_ci "base_line":124, 489ba991379Sopenharmony_ci 进程:{ 490ba991379Sopenharmony_ci "size":12, 491ba991379Sopenharmony_ci "elf":{ 492ba991379Sopenharmony_ci "elf_file_1":elf_size, 493ba991379Sopenharmony_ci ... 494ba991379Sopenharmony_ci } 495ba991379Sopenharmony_ci } 496ba991379Sopenharmony_ci } 497ba991379Sopenharmony_ci } 498ba991379Sopenharmony_ci """ 499ba991379Sopenharmony_ci tmp_dict = copy.deepcopy(data_dict) 500ba991379Sopenharmony_ci writer = SimpleExcelWriter("ram_info") 501ba991379Sopenharmony_ci header_unit = "" if unit_adapt else ", Byte" 502ba991379Sopenharmony_ci header = [ 503ba991379Sopenharmony_ci "subsystem_name", "component_name", f"component_size(ram{header_unit})", "process_name", 504ba991379Sopenharmony_ci f"process_size({ss}{header_unit})", "elf", f"elf_size{'' if unit_adapt else '(Byte)'}" 505ba991379Sopenharmony_ci ] 506ba991379Sopenharmony_ci if baseline_file: 507ba991379Sopenharmony_ci header = [ 508ba991379Sopenharmony_ci "subsystem_name", "component_name", f"component_size(ram{header_unit})", "baseline", "process_name", 509ba991379Sopenharmony_ci f"process_size({ss}{header_unit})", "elf", f"elf_size{'' if unit_adapt else '(Byte)'}" 510ba991379Sopenharmony_ci ] 511ba991379Sopenharmony_ci writer.set_sheet_header(header) 512ba991379Sopenharmony_ci subsystem_c = 0 513ba991379Sopenharmony_ci subsystem_start_r = 1 514ba991379Sopenharmony_ci subsystem_end_r = 0 515ba991379Sopenharmony_ci 516ba991379Sopenharmony_ci component_c = 1 517ba991379Sopenharmony_ci component_start_r = 1 518ba991379Sopenharmony_ci component_end_r = 0 519ba991379Sopenharmony_ci component_size_c = 2 520ba991379Sopenharmony_ci baseline_c = 3 521ba991379Sopenharmony_ci 522ba991379Sopenharmony_ci process_start_r = 1 523ba991379Sopenharmony_ci process_end_r = 0 524ba991379Sopenharmony_ci process_c = 4 525ba991379Sopenharmony_ci process_size_c = 5 526ba991379Sopenharmony_ci if not baseline_file: 527ba991379Sopenharmony_ci process_c -= 1 528ba991379Sopenharmony_ci process_size_c -= 1 529ba991379Sopenharmony_ci for subsystem_name, subsystem_info in tmp_dict.items(): 530ba991379Sopenharmony_ci subsystem_size = subsystem_info.get("size") 531ba991379Sopenharmony_ci if subsystem_size: 532ba991379Sopenharmony_ci del subsystem_info["size"] 533ba991379Sopenharmony_ci for component_name, component_info in subsystem_info.items(): 534ba991379Sopenharmony_ci component_size = component_info.get("size") 535ba991379Sopenharmony_ci component_baseline = component_info.get("baseline") 536ba991379Sopenharmony_ci if "size" in component_info.keys(): 537ba991379Sopenharmony_ci del component_info["size"] 538ba991379Sopenharmony_ci if "baseline" in component_info.keys(): 539ba991379Sopenharmony_ci del component_info["baseline"] 540ba991379Sopenharmony_ci for process_name, process_info in component_info.items(): 541ba991379Sopenharmony_ci process_size = process_info.get("size") 542ba991379Sopenharmony_ci elf_info = process_info.get("elf") 543ba991379Sopenharmony_ci for elf_name, elf_size in elf_info.items(): 544ba991379Sopenharmony_ci line = cls.__inside_save_result_as_excel(baseline_file, subsystem_name, component_name, 545ba991379Sopenharmony_ci component_size, 546ba991379Sopenharmony_ci component_baseline, process_name, process_size, 547ba991379Sopenharmony_ci elf_name, elf_size) 548ba991379Sopenharmony_ci writer.append_line(line) 549ba991379Sopenharmony_ci elf_count = len(elf_info) 550ba991379Sopenharmony_ci process_end_r += elf_count 551ba991379Sopenharmony_ci component_end_r += elf_count 552ba991379Sopenharmony_ci subsystem_end_r += elf_count 553ba991379Sopenharmony_ci writer.write_merge( 554ba991379Sopenharmony_ci process_start_r, process_c, process_end_r, process_c, process_name) 555ba991379Sopenharmony_ci writer.write_merge( 556ba991379Sopenharmony_ci process_start_r, process_size_c, process_end_r, process_size_c, process_size) 557ba991379Sopenharmony_ci process_start_r = process_end_r + 1 558ba991379Sopenharmony_ci writer.write_merge(component_start_r, component_c, 559ba991379Sopenharmony_ci component_end_r, component_c, component_name) 560ba991379Sopenharmony_ci writer.write_merge(component_start_r, component_size_c, 561ba991379Sopenharmony_ci component_end_r, component_size_c, component_size) 562ba991379Sopenharmony_ci if baseline_file: 563ba991379Sopenharmony_ci writer.write_merge(component_start_r, baseline_c, 564ba991379Sopenharmony_ci component_end_r, baseline_c, component_baseline) 565ba991379Sopenharmony_ci component_start_r = component_end_r + 1 566ba991379Sopenharmony_ci writer.write_merge(subsystem_start_r, subsystem_c, 567ba991379Sopenharmony_ci subsystem_end_r, subsystem_c, subsystem_name) 568ba991379Sopenharmony_ci subsystem_start_r = subsystem_end_r + 1 569ba991379Sopenharmony_ci writer.save(filename) 570ba991379Sopenharmony_ci 571ba991379Sopenharmony_ci 572ba991379Sopenharmony_cidef inside_refacotr_result(component_info, refactored_ram_dict, subsystem_name, component_name, process_name, 573ba991379Sopenharmony_ci process_size): 574ba991379Sopenharmony_ci for elf_name, elf_size in component_info.items(): 575ba991379Sopenharmony_ci if not refactored_ram_dict.get(subsystem_name): 576ba991379Sopenharmony_ci refactored_ram_dict[subsystem_name] = dict() 577ba991379Sopenharmony_ci refactored_ram_dict[subsystem_name]["size"] = 0 578ba991379Sopenharmony_ci if not refactored_ram_dict[subsystem_name].get(component_name): 579ba991379Sopenharmony_ci refactored_ram_dict[subsystem_name][component_name] = dict( 580ba991379Sopenharmony_ci ) 581ba991379Sopenharmony_ci refactored_ram_dict[subsystem_name][component_name]["size"] = 0 582ba991379Sopenharmony_ci refactored_ram_dict[subsystem_name][component_name][process_name] = dict( 583ba991379Sopenharmony_ci ) 584ba991379Sopenharmony_ci refactored_ram_dict[subsystem_name][component_name][process_name]["size"] = process_size 585ba991379Sopenharmony_ci refactored_ram_dict[subsystem_name][component_name][process_name]["elf"] = dict( 586ba991379Sopenharmony_ci ) 587ba991379Sopenharmony_ci refactored_ram_dict[subsystem_name][component_name][process_name]["elf"][elf_name] = elf_size 588ba991379Sopenharmony_ci refactored_ram_dict[subsystem_name]["size"] += process_size 589ba991379Sopenharmony_ci refactored_ram_dict[subsystem_name][component_name]["size"] += process_size 590ba991379Sopenharmony_ci return refactored_ram_dict 591ba991379Sopenharmony_ci 592ba991379Sopenharmony_ci 593ba991379Sopenharmony_cidef refacotr_result(ram_result: Dict[str, Dict]) -> Dict[str, Dict]: 594ba991379Sopenharmony_ci refactored_ram_dict: Dict[str, Dict] = dict() 595ba991379Sopenharmony_ci for process_name, process_info in ram_result.items(): 596ba991379Sopenharmony_ci process_size = process_info.get("size") 597ba991379Sopenharmony_ci del process_info["size"] 598ba991379Sopenharmony_ci for subsystem_name, subsystem_info in process_info.items(): 599ba991379Sopenharmony_ci for component_name, component_info in subsystem_info.items(): 600ba991379Sopenharmony_ci refactored_ram_dict = inside_refacotr_result(component_info, refactored_ram_dict, subsystem_name, 601ba991379Sopenharmony_ci component_name, process_name, process_size) 602ba991379Sopenharmony_ci return refactored_ram_dict 603ba991379Sopenharmony_ci 604ba991379Sopenharmony_ci 605ba991379Sopenharmony_cidef get_args(): 606ba991379Sopenharmony_ci version_num = 1.0 607ba991379Sopenharmony_ci parser = argparse.ArgumentParser( 608ba991379Sopenharmony_ci description="analyze ram size of component" 609ba991379Sopenharmony_ci ) 610ba991379Sopenharmony_ci parser.add_argument("-v", "-version", action="version", 611ba991379Sopenharmony_ci version=f"version {version_num}") 612ba991379Sopenharmony_ci parser.add_argument("-s", "--json_path", type=str, required=True, 613ba991379Sopenharmony_ci help="path of sa json file. eg: -x ~/openharmony/out/rk3568/packages/phone/system/profile") 614ba991379Sopenharmony_ci parser.add_argument("-c", "--cfg_path", type=str, required=True, 615ba991379Sopenharmony_ci help="path of cfg files. eg: -c ./cfgs/") 616ba991379Sopenharmony_ci parser.add_argument("-j", "--rom_result", type=str, default="./rom_analysis_result.json", 617ba991379Sopenharmony_ci help="json file produced by rom_analyzer_v1.0.py, default: ./rom_analysis_result.json." 618ba991379Sopenharmony_ci "eg: -j ./demo/rom_analysis_result.json") 619ba991379Sopenharmony_ci parser.add_argument("-n", "--device_num", type=str, required=True, 620ba991379Sopenharmony_ci help="device number to be collect hidumper info. eg: -n 7001005458323933328a01fce16d3800") 621ba991379Sopenharmony_ci parser.add_argument("-b", "--baseline_file", type=str, default="", 622ba991379Sopenharmony_ci help="baseline file of rom and ram generated by rom analysis.") 623ba991379Sopenharmony_ci parser.add_argument("-o", "--output_filename", default="ram_analysis_result", type=str, 624ba991379Sopenharmony_ci help="base name of output file, default: ram_analysis_result. eg: -o ram_analysis_result") 625ba991379Sopenharmony_ci parser.add_argument("-u", "--unit_adaptive", 626ba991379Sopenharmony_ci action="store_true", help="unit adaptive") 627ba991379Sopenharmony_ci parser.add_argument("-e", "--excel", type=bool, default=False, 628ba991379Sopenharmony_ci help="if output result as excel, default: False. eg: -e True") 629ba991379Sopenharmony_ci args = parser.parse_args() 630ba991379Sopenharmony_ci return args 631ba991379Sopenharmony_ci 632ba991379Sopenharmony_ci 633ba991379Sopenharmony_cidef abspath(path: str) -> str: 634ba991379Sopenharmony_ci return os.path.abspath(os.path.expanduser(path)) 635ba991379Sopenharmony_ci 636ba991379Sopenharmony_ci 637ba991379Sopenharmony_ciif __name__ == '__main__': 638ba991379Sopenharmony_ci args = get_args() 639ba991379Sopenharmony_ci cfg_path_name = abspath(args.cfg_path) 640ba991379Sopenharmony_ci profile_path_name = abspath(args.json_path) 641ba991379Sopenharmony_ci rom_result = args.rom_result 642ba991379Sopenharmony_ci device = args.device_num 643ba991379Sopenharmony_ci output_filename = args.output_filename 644ba991379Sopenharmony_ci baseline = args.baseline_file 645ba991379Sopenharmony_ci output_excel_path = args.excel 646ba991379Sopenharmony_ci unit_adaptiv = args.unit_adaptive 647ba991379Sopenharmony_ci RamAnalyzer.analysis(cfg_path_name, profile_path_name, rom_result, 648ba991379Sopenharmony_ci device_num=device, output_file=output_filename, ss="Pss", output_excel=output_excel_path, 649ba991379Sopenharmony_ci baseline_file=baseline, unit_adapt=unit_adaptiv)