1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright (C) 2024 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 16import pytest 17import subprocess 18import re 19import time 20import threading 21import sqlite3 22from string import Template 23import os 24 25 26def get_pid_by_process_name(process_name): 27 pid = None 28 cmd = f"hdc shell \"pidof {process_name}\"" 29 try: 30 pid = subprocess.check_output(cmd, shell=False, encoding="utf-8", text=True) 31 pid = int(pid.strip().split()[0]) 32 except subprocess.CalledProcessError as e: 33 print(f"Command failed: {cmd}\nError: {e}") 34 except Exception as e: 35 print(f"Unexpected error: {e}") 36 return pid 37 38 39def get_file_size(file_path): 40 size = os.path.getsize(file_path) 41 return size 42 43 44def task_nativehook_dwarf(): 45 subprocess.check_output(f'hdc shell "hiprofiler_cmd -c /data/local/tmp/inputfiles/nativehook/config_nativehook_dwarf.txt -o /data/local/tmp/test_nativehook_dwarf.htrace -t 60 -s -k"') 46 47 48def write_str_file(file_path, large_string): 49 lines = large_string.split('\n') 50 with open(file_path, 'w') as file: 51 for line in lines: 52 file.write(line + '\n') 53 54 55#检查进程是否离线 56def check_process_offline(): 57 count = 0 58 while (count < 5): 59 pid_profiler = get_pid_by_process_name("hiprofilerd") 60 pid_plugin = get_pid_by_process_name("hiprofiler_plugins") 61 pid_daemon = get_pid_by_process_name("native_daemon") 62 assert (pid_profiler > 0) 63 assert (pid_plugin > 0) 64 assert (pid_daemon > 0) 65 time.sleep(10) 66 count = count + 1 67 68 69def hap_op_func(ability_name, bundle_name): 70 #打开系统设置的应用com.ohos.settings.MainAbility com.ohos.settings 71 subprocess.check_output(f"hdc shell uitest uiInput keyEvent Home", shell=False, 72 text=True, encoding="utf-8") 73 time.sleep(1) 74 subprocess.check_output(f"hdc shell aa start -a {ability_name} -b {bundle_name}", shell=False, 75 text=True, encoding="utf-8") 76 time.sleep(2) 77 78 79def hidumper_op_func(ability_name, bundle_name): 80 pid_text = subprocess.run(f"hdc shell pidof '{bundle_name}'", stdout=subprocess.PIPE, text=True, check=True) 81 pidinfo = pid_text.stdout 82 if pidinfo.strip() != "": 83 subprocess.check_output(f"hdc shell kill " + pidinfo.strip(), shell=False, text=True, encoding="utf-8") 84 #拉起hidumper 85 subprocess.check_output(f"hdc shell hidumper -h", shell=False, text=True, encoding="utf-8") 86 87 88def hidumper_prepare_func(ability_name, bundle_name): 89 pid_text = subprocess.run(f"hdc shell pidof '{bundle_name}'", stdout=subprocess.PIPE, text=True, check=True) 90 pidinfo = pid_text.stdout 91 #进程存在 92 if pidinfo.strip() != "": 93 subprocess.check_output(f"hdc shell hidumper -lc", shell=False, text=True, encoding="utf-8") 94 else: 95 subprocess.check_output(f"hdc shell hidumper -h", shell=False, text=True, encoding="utf-8") 96 97 98def hidumper_op_nostart_func(ability_name, bundle_name): 99 subprocess.check_output(f"hdc shell hidumper -lc", shell=False, text=True, encoding="utf-8") 100 subprocess.check_output(f"hdc shell hidumper -c", shell=False, text=True, encoding="utf-8") 101 102 103def nativehook_dwarf_startup(statistics_int, ability_name, bundle_name, op_func): 104 #获得该应用的进程PID com.huawei.hmos.settings 105 pid_text = subprocess.run(f"hdc shell pidof '{bundle_name}'", stdout=subprocess.PIPE, text=True, check=True) 106 pidinfo = pid_text.stdout 107 if pidinfo.strip() != "": 108 subprocess.check_output(f"hdc shell kill " + pidinfo.strip(), shell=False, text=True, encoding="utf-8") 109 #删除cppcrash 110 subprocess.check_output(f"hdc shell rm -f /data/log/faultlog/faultlogger/cppcrash-*", shell=False, text=True, encoding="utf-8") 111 #dwarf 统计模式 112 file_content = Template('request_id: 1 \n' 113 'session_config { \n' 114 ' buffers { \n' 115 ' pages: 16384 \n' 116 ' } \n' 117 '} \n' 118 'plugin_configs { \n' 119 ' plugin_name: "nativehook" \n' 120 ' sample_interval: 5000 \n' 121 ' config_data { \n' 122 ' save_file: false \n' 123 ' smb_pages: 16384 \n' 124 ' max_stack_depth: 8 \n' 125 ' process_name: "${s2}" \n' 126 ' string_compressed: true \n' 127 ' fp_unwind: false \n' 128 ' blocked: false \n' 129 ' callframe_compress: true \n' 130 ' record_accurately: true \n' 131 ' offline_symbolization: false \n' 132 ' statistics_interval: ${s1} \n' 133 ' startup_mode: true \n' 134 ' js_stack_report: 1 \n' 135 ' max_js_stack_depth: 2 \n' 136 ' } \n' 137 '} \n') 138 vmfile = file_content.safe_substitute(s1=statistics_int, s2=bundle_name) 139 #写入文件 140 write_str_file("./inputfiles/nativehook/config_nativehook_dwarf.txt", vmfile) 141 142 subprocess.check_output(f"hdc file send ./inputfiles/nativehook/config_nativehook_dwarf.txt /data/local/tmp/", shell=False, 143 text=True, encoding="utf-8") 144 task_thread = threading.Thread(target=task_nativehook_dwarf, args=()) 145 task_thread.start() 146 time.sleep(2) 147 check_thread = threading.Thread(target=check_process_offline, args=()) 148 check_thread.start() 149 op_func(ability_name, bundle_name) 150 check_thread.join() 151 task_thread.join() 152 subprocess.run(f'hdc file recv /data/local/tmp/test_nativehook_dwarf.htrace ./outputfiles/', shell=False, 153 text=True, encoding="utf-8") 154 # 检查文件大小 155 file_size = get_file_size(f"./outputfiles/test_nativehook_dwarf.htrace") 156 assert (file_size > 1024) 157 #检查是否存在crash 158 output_text = subprocess.run(f'hdc shell "ls /data/log/faultlog/faultlogger"', stdout=subprocess.PIPE, text=True, check=True) 159 process_info = output_text.stdout 160 lines = process_info.strip().split('\n') 161 check_crash = False 162 for line in lines: 163 if line.find("profiler") != -1 or line.find("native_daemon") != -1: 164 check_crash = True 165 break 166 assert (check_crash == False) 167 168 169def nativehook_dwarf_no_startup(prepare_op_func, statistics_int, ability_name, bundle_name, op_func): 170 prepare_op_func(ability_name, bundle_name) 171 #删除cppcrash 172 subprocess.check_output(f"hdc shell rm -f /data/log/faultlog/faultlogger/cppcrash-*", shell=False, text=True, encoding="utf-8") 173 #dwarf 174 file_content = Template('request_id: 1 \n' 175 'session_config { \n' 176 ' buffers { \n' 177 ' pages: 16384 \n' 178 ' } \n' 179 '} \n' 180 'plugin_configs { \n' 181 ' plugin_name: "nativehook" \n' 182 ' sample_interval: 5000 \n' 183 ' config_data { \n' 184 ' save_file: false \n' 185 ' smb_pages: 16384 \n' 186 ' max_stack_depth: 8 \n' 187 ' process_name: "${s2}" \n' 188 ' string_compressed: true \n' 189 ' fp_unwind: false \n' 190 ' blocked: false \n' 191 ' callframe_compress: true \n' 192 ' record_accurately: true \n' 193 ' offline_symbolization: false \n' 194 ' statistics_interval: ${s1} \n' 195 ' startup_mode: false \n' 196 ' js_stack_report: 1 \n' 197 ' max_js_stack_depth: 2 \n' 198 ' } \n' 199 '} \n') 200 vmfile = file_content.safe_substitute(s1=statistics_int, s2=bundle_name) 201 #写入文件 202 write_str_file("./inputfiles/nativehook/config_nativehook_dwarf.txt", vmfile) 203 204 subprocess.check_output(f"hdc file send ./inputfiles/nativehook/config_nativehook_dwarf.txt /data/local/tmp/", shell=False, 205 text=True, encoding="utf-8") 206 task_thread = threading.Thread(target=task_nativehook_dwarf, args=()) 207 task_thread.start() 208 time.sleep(2) 209 check_thread = threading.Thread(target=check_process_offline, args=()) 210 check_thread.start() 211 op_func(ability_name, bundle_name) 212 check_thread.join() 213 task_thread.join() 214 subprocess.run(f'hdc file recv /data/local/tmp/test_nativehook_dwarf.htrace ./outputfiles/', shell=False, 215 text=True, encoding="utf-8") 216 # 检查文件大小 217 file_size = get_file_size(f"./outputfiles/test_nativehook_dwarf.htrace") 218 assert (file_size > 1024) 219 #检查是否存在crash 220 output_text = subprocess.run(f'hdc shell "ls /data/log/faultlog/faultlogger"', stdout=subprocess.PIPE, text=True, check=True) 221 process_info = output_text.stdout 222 lines = process_info.strip().split('\n') 223 check_crash = False 224 for line in lines: 225 if line.find("profiler") != -1 or line.find("native_daemon") != -1: 226 check_crash = True 227 break 228 assert (check_crash == False) 229 230 231def nativehook_dwarf_check_data(statistics_flag): 232 subprocess.check_output(r"./inputfiles/trace_streamer_db.exe ./outputfiles/test_nativehook_dwarf.htrace -e ./outputfiles/test_nativehook_dwarf.db") 233 # 连接数据库文件 234 conn = sqlite3.connect(r'./outputfiles/test_nativehook_dwarf.db') 235 # # 创建游标对象 236 cursor = conn.cursor() 237 # 检查是否存在符号数据 238 cursor.execute('select * from native_hook_frame , data_dict where symbol_id = data_dict.id limit 10') 239 result = cursor.fetchall() 240 row_count = len(result) 241 assert (row_count > 0) 242 column_names = [description[0] for description in cursor.description] 243 for row in result: 244 #检查是否存在符号 245 assert (row[column_names.index('data')] is not None) 246 if statistics_flag: 247 #检查是否存在统计数据 248 cursor.execute('select * from native_hook_statistic limit 10') 249 result = cursor.fetchall() 250 row_count = len(result) 251 assert (row_count > 0) 252 column_names = [description[0] for description in cursor.description] 253 for row in result: 254 #检查是否统计数据 255 assert (row[column_names.index('apply_count')] > 0) 256 assert (row[column_names.index('release_count')] >= 0) 257 assert (row[column_names.index('apply_size')] > 0) 258 assert (row[column_names.index('release_size')] >= 0) 259 else: 260 #非统计模式 261 cursor.execute('select * from native_hook limit 10') 262 result = cursor.fetchall() 263 row_count = len(result) 264 assert (row_count > 0) 265 names = [description[0] for description in cursor.description] 266 for row in result: 267 assert (row[names.index('event_type')] == 'AllocEvent' or row[names.index('event_type')] == 'FreeEvent' or row[names.index('event_type')] == 'MmapEvent') 268 assert (row[names.index('heap_size')] > 0) 269 assert (row[names.index('all_heap_size')] >= 0) 270 271 cursor.close() 272 conn.close() 273 return True 274 275 276class TestHiprofilerMemoryPlugin: 277 @pytest.mark.L0 278 #启动模式 hap 应用 279 def test_nativehook_dwarf(self): 280 #获得32位还是64位 281 sys_bit = subprocess.run(f"hdc shell getconf LONG_BIT", stdout=subprocess.PIPE, text=True, check=True) 282 sysinfo = sys_bit.stdout 283 if sysinfo.strip() == "32": 284 #非统计模式 285 nativehook_dwarf_startup(0, "com.ohos.photos.MainAbility", "com.ohos.photos", hap_op_func) 286 assert nativehook_dwarf_check_data(False) 287 else: 288 #非统计模式 289 nativehook_dwarf_startup(0, "com.huawei.hmos.settings.MainAbility", "com.huawei.hmos.settings", hap_op_func) 290 assert nativehook_dwarf_check_data(False) 291 292 @pytest.mark.L0 293 #启动模式 10S 统计一次 hap应用 294 def test_nativehook_dwarf_statics(self): 295 #获得32位还是64位 296 sys_bit = subprocess.run(f"hdc shell getconf LONG_BIT", stdout=subprocess.PIPE, text=True, check=True) 297 sysinfo = sys_bit.stdout 298 if sysinfo.strip() == "32": 299 #统计模式 300 nativehook_dwarf_startup(10, "com.ohos.photos.MainAbility", "com.ohos.photos", hap_op_func) 301 assert nativehook_dwarf_check_data(True) 302 else: 303 #统计模式 304 nativehook_dwarf_startup(10, "com.huawei.hmos.settings.MainAbility", "com.huawei.hmos.settings", hap_op_func) 305 assert nativehook_dwarf_check_data(True) 306 307 @pytest.mark.L0 308 #启动模式 10S 统计一次 SA 进程 如:hidumper_service 309 def test_nativehook_dwarf_native_statics(self): 310 #统计模式 311 nativehook_dwarf_startup(10, "", "hidumper_service", hidumper_op_func) 312 assert nativehook_dwarf_check_data(True) 313 #非统计模式 314 nativehook_dwarf_startup(0, "", "hidumper_service", hidumper_op_func) 315 assert nativehook_dwarf_check_data(False) 316 317 #非启动模式 318 def test_nativehook_dwarf_native_not_startup(self): 319 nativehook_dwarf_no_startup(hidumper_prepare_func, 10, "", "hidumper_service", hidumper_op_nostart_func) 320 assert nativehook_dwarf_check_data(True) 321 #非统计模式 322 nativehook_dwarf_no_startup(hidumper_prepare_func, 0, "", "hidumper_service", hidumper_op_nostart_func) 323 assert nativehook_dwarf_check_data(False) 324