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