• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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