1#
2# Copyright (c) 2024 Huawei Device Co., Ltd.
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15
16import os
17import sys
18import subprocess
19import sqlite3
20import threading
21import time
22import re
23import shutil
24import uuid
25import random
26import json
27import sqlite3
28import datetime
29import pandas as pd
30import inspect
31
32# The following programs are packaged as exe commands
33# pyinstaller --onefile get_mem_excel.py
34
35now_time = "null"
36now_version = "null"
37hidumper_num = 0
38pid_list = []
39mem_file_name = ""
40mem_smaps_file_name = ""
41
42
43# Run a cmd command
44def run_cmd(cmd):
45    __func__ = inspect.currentframe().f_code.co_name
46    print(f"{__func__}: {cmd}")
47    output = subprocess.run(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True,
48                            check=True).stdout
49    print(f"{__func__}: result:{str(output)}")
50    return output
51
52
53# Wait for the HDC to connect to the device
54def wait_for_device():
55    __func__ = inspect.currentframe().f_code.co_name
56    print(f"{__func__}: in")
57    run_cmd("hdc wait-for-device")
58
59
60# Gets the current time, which is used as the file name part of the scraped data
61def update_now_time():
62    __func__ = inspect.currentframe().f_code.co_name
63    print(f"{__func__}: in")
64    global now_time
65    now_time = str(datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
66
67
68# Obtain the current device version number, which is used as the file name part of the scraped data
69def update_now_version():
70    __func__ = inspect.currentframe().f_code.co_name
71    print(f"{__func__}: in")
72    global now_version
73    now_version = str(run_cmd("hdc shell param get const.product.software.version")).replace("\n", "").replace(" ", "")
74
75
76# Obtain the list of process names to be captured from the pid_list.txt, if not configured, there is a default value
77def get_pid_list():
78    __func__ = inspect.currentframe().f_code.co_name
79    print(f"{__func__}: in")
80    global pid_list
81    list_file_path = "pid_list.txt"
82    if os.path.exists(list_file_path):
83        list_file = open(list_file_path, 'r')
84        for line in list_file.readlines():
85            pid_list.append(line.replace('\n', ''))
86    else:
87        print(f"{__func__}: pid_list.txt not exists, get mem for sensor_host,vibrator_host,audio_host,allocator_host")
88        pid_list.append('sensor_host')
89        pid_list.append('vibrator_host')
90        pid_list.append('audio_host')
91        pid_list.append('allocator_host')
92
93
94# Grab simple memory information for a process
95def get_mem(p_name):
96    __func__ = inspect.currentframe().f_code.co_name
97    print(f"{__func__}: in, p_name {p_name}")
98    global mem_file_name
99    mem_file_name = "result-mem" + now_version + p_name + now_time + ".txt"
100    cmd = "hdc shell \"hidumper --mem `pidof " + p_name + "`\" > " + mem_file_name
101    run_cmd(cmd)
102
103
104# Fetch detailed memory information for a process
105def get_mem_smaps(p_name):
106    __func__ = inspect.currentframe().f_code.co_name
107    print(f"{__func__}: in, p_name {p_name}")
108    global mem_smaps_file_name
109    mem_smaps_file_name = "result-mem_smaps" + now_version + p_name + now_time + ".txt"
110    cmd = "hdc shell \"hidumper --mem-smaps `pidof " + p_name + "` -v\" > " + mem_smaps_file_name
111    run_cmd(cmd)
112
113
114# Parse Excel sheets based on detailed memory information for a process
115def get_mem_smaps_excel(p_name):
116    global hidumper_num
117    __func__ = inspect.currentframe().f_code.co_name
118    print(f"{__func__}: in")
119    mem_file = open(mem_smaps_file_name, "r")
120    datas = mem_file.readlines()
121    result_map = {}
122    result_list = []
123    mem_index = -1
124    for line in datas:
125        fields = line.split()
126        if len(fields) > 2 and 'Pss' in fields:
127            hidumper_num = len(fields)
128            mem_index = fields.index("Pss")
129            continue
130        if len(fields) == 10:
131            mem_data = int(fields[mem_index])
132            result_map["总和"] = mem_data
133            continue
134        if len(fields) != hidumper_num or hidumper_num == 0 or mem_index == -1:
135            continue
136        mem_data = int(fields[mem_index])
137        mem_name = fields[hidumper_num - 1]
138        matchs = [
139            r'\[anon:guard:\d*\]',
140            r'\[anon:stack:\d*\]',
141            r'\[anon:signal_stack:\d*\]'
142        ]
143        for match in matchs:
144            if re.findall(match, mem_name):
145                mem_name = match
146        if mem_name not in result_map:
147            result_map[mem_name] = 0
148        result_map[mem_name] += mem_data
149    for key in result_map:
150        result_list.append([key, result_map[key]])
151    headers = ['作用域名', '内存值']
152    df = pd.DataFrame(result_list, columns=headers)
153    output_file = "result-mem-" + now_version + p_name + now_time + ".xlsx"
154    df.to_excel(output_file, index=False)
155
156
157# Scrape a process's in-memory data
158def get_data(p_name):
159    get_mem(p_name)
160    get_mem_smaps(p_name)
161    get_mem_smaps_excel(p_name)
162
163
164# Scrapes the memory data of all configured processes
165def get_all_process_data():
166    for p_name in pid_list:
167        get_data(p_name)
168
169
170# Perform a one-time crawl of memory data for all processes configured
171def get_data_once():
172    wait_for_device()
173    update_now_time()
174    update_now_version()
175    get_all_process_data()
176
177
178# Perform num fetch of the memory data of all processes configured at daily intervals
179def get_data_more(num, daily):
180    for i in range(num):
181        get_data_once()
182        time.sleep(daily)
183
184
185if __name__ == "__main__":
186    get_pid_list()
187    get_data_more(1, 10)
188    pass
189