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