• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3"""
4Copyright (c) 2024 Huawei Device Co., Ltd.
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9    http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17Description: Utils for action words.
18"""
19
20import asyncio
21import os
22import re
23import subprocess
24import threading
25from typing import Union
26
27from hypium import SystemUI, BY, MatchPattern
28
29from fport import Fport
30from taskpool import TaskPool
31from toolchain_websocket import ToolchainWebSocket
32
33
34class TimeRecord:
35    def __init__(self, expected_time, actual_time):
36        self.expected_time = expected_time
37        self.actual_times = [actual_time]
38
39    def avg_actual_time(self):
40        return sum(self.actual_times) / len(self.actual_times)
41
42    def max_actual_time(self):
43        return max(self.actual_times)
44
45    def min_actual_time(self):
46        return min(self.actual_times)
47
48    def add_actual_time(self, actual_time):
49        self.actual_times.append(actual_time)
50
51    def avg_proportion(self):
52        return self.avg_actual_time * 100 / self.expected_time
53
54    def rounds(self):
55        return len(self.actual_times)
56
57    def mid_actual_time(self):
58        self.actual_times.sort()
59        return self.actual_times[len(self.actual_times) // 2]
60
61
62class CommonUtils(object):
63    def __init__(self, driver):
64        self.driver = driver
65
66    @staticmethod
67    async def communicate_with_debugger_server(websocket, connection, command, message_id):
68        '''
69        Assembles and send the commands, then return the response.
70        Send message to the debugger server corresponding to the to_send_queue.
71        Return the response from the received_queue.
72        '''
73        command['id'] = message_id
74        await websocket.send_msg_to_debugger_server(connection.instance_id, connection.send_msg_queue, command)
75        response = await websocket.recv_msg_of_debugger_server(connection.instance_id,
76                                                               connection.received_msg_message)
77        return response
78
79    @staticmethod
80    def get_custom_protocols():
81        protocols = ["removeBreakpointsByUrl",
82                     "setMixedDebugEnabled",
83                     "replyNativeCalling",
84                     "getPossibleAndSetBreakpointByUrl",
85                     "dropFrame",
86                     "setNativeRange",
87                     "resetSingleStepper",
88                     "callFunctionOn",
89                     "smartStepInto",
90                     "saveAllPossibleBreakpoints"]
91        return protocols
92
93    @staticmethod
94    def message_id_generator():
95        message_id = 1
96        while True:
97            yield message_id
98            message_id += 1
99
100    @staticmethod
101    def assert_equal(actual, expected):
102        '''
103        判断actual和expected是否相同,若不相同,则抛出异常
104        '''
105        assert actual == expected, f"\nExpected: {expected}\nActual: {actual}"
106
107    @staticmethod
108    def get_variables_from_properties(properties, prefix_name):
109        '''
110        properties是Runtime.getProperties协议返回的变量信息
111        该方法会根据prefix_name前缀匹配properties中的变量名,符合的变量会以字典形式返回变量名和相关描述
112        描述首先采用description中的内容,但会删掉其中的哈希值,没有description则会采用subtype或type中的内容
113        '''
114        variables = {}
115        for var in properties:
116            if not var['name'].startswith(prefix_name):
117                continue
118            name = var['name']
119            value = var['value']
120            description = value.get('description')
121            if description is not None:
122                index_of_at = description.find('@')
123                if index_of_at == -1:
124                    variables[name] = description
125                    continue
126                variables[name] = description[:index_of_at]
127                index_of_bracket = description.find('[', index_of_at + 1)
128                if index_of_bracket != -1:
129                    variables[name] += description[index_of_bracket:]
130            else:
131                subtype = value.get('subtype')
132                variables[name] = subtype if subtype is not None else value.get('type')
133        return variables
134
135    def get_pid(self, bundle_name):
136        '''
137        获取bundle_name对应的pid
138        '''
139        pid = self.driver.shell("pidof " + bundle_name).strip()
140        if not pid.isdigit():
141            return 0
142        self.driver.log_info(f'pid of {bundle_name}: ' + pid)
143        return int(pid)
144
145    def attach(self, bundle_name):
146        '''
147        通过bundle_name使指定应用进入调试模式
148        '''
149        attach_result = self.driver.shell(f"aa attach b {bundle_name}").strip()
150        self.driver.log_info(attach_result)
151        self.assert_equal(attach_result, 'attach app debug successfully.')
152
153    def connect_server(self, config):
154        '''
155        根据config连接ConnectServer
156        '''
157        fport = Fport(self.driver)
158        fport.clear_fport()
159        connect_server_port = fport.fport_connect_server(config['connect_server_port'], config['pid'],
160                                                         config['bundle_name'])
161        assert connect_server_port > 0, 'Failed to fport connect server for 3 times, the port is very likely occupied'
162        config['connect_server_port'] = connect_server_port
163        config['websocket'] = ToolchainWebSocket(self.driver, config['connect_server_port'],
164                                                config['debugger_server_port'], config.get('print_protocol', True))
165        config['taskpool'] = TaskPool()
166
167    def hot_reload(self, hqf_path: Union[str, list]):
168        '''
169        根据hqf_path路径对应用进行热重载
170        '''
171        assert isinstance(hqf_path, (str, list)), 'Tyep of hqf_path is NOT string or list!'
172        if isinstance(hqf_path, str):
173            cmd = f'bm quickfix -a -f {hqf_path} -d'
174        elif isinstance(hqf_path, list):
175            cmd = f'bm quickfix -a -f {" ".join(hqf_path)} -d'
176        self.driver.log_info('hot reload: ' + cmd)
177        result = self.driver.shell(cmd).strip()
178        self.driver.log_info(result)
179        self.assert_equal(result, 'apply quickfic succeed.')
180
181    async def async_wait_timeout(self, task, timeout=3):
182        '''
183        在timeout内执行task异步任务,若执行超时则抛出异常
184        '''
185        try:
186            result = await asyncio.wait_for(task, timeout)
187            return result
188        except asyncio.TimeoutError:
189            self.driver.log_error('await timeout!')
190
191    def hdc_target_mount(self):
192        '''
193        挂载设备文件系统
194        '''
195        cmd = 'target mount'
196        self.driver.log_info('Mount finish: ' + cmd)
197        result = self.driver.hdc(cmd).strip()
198        self.driver.log_info(result)
199        self.assert_equal(result, 'Mount finish')
200
201    def hdc_file_send(self, source, sink):
202        '''
203        将source中的文件发送到设备的sink路径下
204        '''
205        cmd = f'file send {source} {sink}'
206        self.driver.log_info(cmd)
207        result = self.driver.hdc(cmd)
208        self.driver.log_info(result)
209        assert 'FileTransfer finish' in result, result
210
211    def clear_fault_log(self):
212        '''
213        清楚故障日志
214        '''
215        cmd = 'rm /data/log/faultlog/faultlogger/*'
216        self.driver.log_info(cmd)
217        result = self.driver.shell(cmd)
218        self.driver.log_info(result)
219        assert 'successfully' in result, result
220
221    def save_fault_log(self, log_path):
222        '''
223        保存故障日志到log_path
224        '''
225        if not os.path.exists(log_path):
226            os.makedirs(log_path)
227
228        cmd = f'file recv /data/log/faultlog/faultlogger/ {log_path}'
229        self.driver.log_info(cmd)
230        result = self.driver.hdc(cmd)
231        self.driver.log_info(result)
232        assert 'successfully' in result, result
233
234
235class UiUtils(object):
236    def __init__(self, driver):
237        self.driver = driver
238
239    def get_screen_size(self):
240        '''
241        获取屏幕大小
242        '''
243        screen_info = self.driver.shell(f"hidumper -s RenderService -a screen")
244        match = re.search(r'physical screen resolution: (\d+)x(\d+)', screen_info)
245        # 新版本镜像中screen_info信息格式有变,需要用下方正则表达式获取
246        if match is None:
247            match = re.search(r'physical resolution=(\d+)x(\d+)', screen_info)
248        assert match is not None, f"screen_info is incorrect: {screen_info}"
249        return int(match.group(1)), int(match.group(2))
250
251    def click(self, x, y):
252        '''
253        点击屏幕指定坐标位置
254        '''
255        click_result = self.driver.shell(f"uinput -T -c {x} {y}")
256        self.driver.log_info(click_result)
257        assert "click coordinate" in click_result, f"click_result is incorrect: {click_result}"
258
259    def click_on_middle(self):
260        '''
261        点击屏幕中心
262        '''
263        width, height = self.get_screen_size()
264        middle_x = width // 2
265        middle_y = height // 2
266        self.click(middle_x, middle_y)
267
268    def back(self):
269        '''
270        返回上一步
271        '''
272        cmd = 'uitest uiInput keyEvent Back'
273        self.driver.log_info('click the back button: ' + cmd)
274        result = self.driver.shell(cmd).strip()
275        self.driver.log_info(result)
276        CommonUtils.assert_equal(result, 'No Error')
277
278    def keep_awake(self):
279        '''
280        保持屏幕常亮,要在屏幕亮起时使用该方法才有效
281        '''
282        cmd = 'power-shell wakeup'
283        result = self.driver.shell(cmd).strip()
284        assert "WakeupDevice is called" in result, result
285        cmd = 'power-shell timeout -o 214748647'
286        result = self.driver.shell(cmd).strip()
287        assert "Override screen off time to 214748647" in result, result
288        cmd = 'power-shell setmode 602'
289        result = self.driver.shell(cmd).strip()
290        assert "Set Mode Success!" in result, result
291        self.driver.log_info('Keep the screen awake Success!')
292
293    def open_control_center(self):
294        '''
295        滑动屏幕顶部右侧打开控制中心
296        '''
297        width, height = self.get_screen_size()
298        start = (int(width * 0.75), 20)
299        end = (int(width * 0.75), int(height * 0.3))
300        cmd = f"uinput -T -m {start[0]} {start[1]} {end[0]} {end[1]} 500"
301        self.driver.log_info('open control center')
302        result = self.driver.shell(cmd)
303        self.driver.log_info(result)
304
305    def click_location_component(self):
306        '''
307        点击控制中心中的位置控件
308        '''
309        self.open_control_center()
310        self.driver.wait(1)
311        width, height = self.get_screen_size()
312        self.click(int(width * 0.2), int(height * 0.95))
313        self.back()
314
315    def disable_location(self):
316        '''
317        关闭位置信息(GPS),不能在异步任务中使用
318        '''
319        self.driver.wait(1)
320        SystemUI.open_control_center(self.driver)
321        comp = self.driver.find_component(BY.key("ToggleBaseComponent_Image_location", MatchPattern.CONTAINS))
322        result = comp.getAllProperties()
323        try:
324            bg = result.backgroudColor
325            value = int("0x" + bg[3:], base=16)
326            # 读取背景颜色判断开启或关闭
327            if value < 0xffffff:
328                comp.click()
329                self.driver.wait(1)
330            self.driver.press_back()
331        except Exception as e:
332            raise RuntimeError("Fail to disable location")
333
334    def enable_location(self):
335        '''
336        开启位置信息(GPS),不能在异步任务中使用
337        '''
338        self.driver.wait(1)
339        SystemUI.open_control_center(self.driver)
340        comp = self.driver.find_component(BY.key("ToggleBaseComponent_Image_location", MatchPattern.CONTAINS))
341        result = comp.getAllProperties()
342        try:
343            bg = result.backgroudColor
344            value = int("0x" + bg[3:], base=16)
345            # 读取背景颜色判断开启或关闭
346            if value == 0xffffff:
347                comp.click()
348                self.driver.wait(1)
349            self.driver.press_back()
350        except Exception as e:
351            raise RuntimeError("Fail to disable location")
352
353
354class PerformanceUtils(object):
355    def __init__(self, driver):
356        self.driver = driver
357        self.time_data = {}
358        self.file_path_hwpower_genie_engine = "/system/app"
359        self.file_name_hwpower_genie_engine = "HwPowerGenieEngine3"
360        self.cpu_num_list = [0] * 3
361        self.cpu_start_id = [0] * 3
362        self.total_cpu_num = 0
363        self.cpu_cluster_num = 0
364        self.prev_mode = 0
365        self.board_ipa_support = False
366        self.perfg_version = "0.0"
367
368    def show_performace_data_in_html(self):
369        '''
370        将性能数据以html格式展示出来
371        '''
372        header = ['测试用例', '执行次数', '期望执行时间(ms)', '最大执行时间(ms)',
373                  '最小执行时间(ms)', '执行时间中位数(ms)', '平均执行时间(ms)',
374                  '平均执行时间/期望执行时间(%)']
375        content = []
376        failed_cases = []
377        for key, value in self.time_data.items():
378            if value.avg_proportion() >= 130:
379                failed_cases.append(key)
380            content.append([key, value.rounds(), value.expected_time, value.max_actual_time(),
381                            value.min_actual_time(), value.mid_actual_time(),
382                            f'{value.avg_actual_time()}:.2f',
383                            f'{value.avg_proportion()}:.2f'])
384        table_html = '<table border="1" cellpadding="5" cellspacing="0"'
385        header_html = '<thead>'
386        for i in header:
387            header_html += f'<th>{i}</th>'
388        header_html += '</thead>'
389        content_html = 'tbody'
390        for row in content:
391            row_html = '<tr style="color: red;">' if row[0] in failed_cases else '<tr>'
392            for i in row:
393                row_html += f'<td>{i}</td>'
394            row_html += '</tr>'
395            content_html += row_html
396        table_html += header_html
397        table_html += content_html
398        table_html += '</table>'
399        self.driver.lg_info(table_html)
400        assert len(failed_cases) == 0, "The following cases have obvious deterioration: " + " ".join(failed_cases)
401
402    def add_time_data(self, case_name: str, expected_time: int, actual_time: int):
403        '''
404        添加指定性能用例对应的耗时
405        '''
406        if case_name in self.time_data:
407            self.time_data[case_name].add_actual_time(actual_time)
408        else:
409            self.time_data[case_name] = TimeRecord(expected_time, actual_time)
410
411    def get_perf_data_from_hilog(self):
412        '''
413        从hilog日志中获取性能数据
414        '''
415        # config the hilog
416        cmd = 'hilog -r'
417        self.driver.log_info(cmd)
418        result = self.driver.shell(cmd)
419        assert 'successfully' in result, result
420
421        cmd = 'hilog -G 16M'
422        self.driver.log_info(cmd)
423        result = self.driver.shell(cmd)
424        assert 'successfully' in result, result
425
426        cmd = 'hilog -Q pidoff'
427        self.driver.log_info(cmd)
428        result = self.driver.shell(cmd)
429        assert 'successfully' in result, result
430
431        cmd = 'hilog -Q domainoff'
432        self.driver.log_info(cmd)
433        result = self.driver.shell(cmd)
434        assert 'successfully' in result, result
435
436        cmd = 'hilog -b INFO'
437        self.driver.log_info(cmd)
438        result = self.driver.shell(cmd)
439        assert 'successfully' in result, result
440
441        # create a sub-process to receive the hilog
442        hilog_process = subprocess.Popen(['hdc', 'shell', 'hilog'],
443                                         stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
444
445        def get_time_from_records(records):
446            # 解析records的数据
447            pattern = r"\((.*?)\) Expected Time = (\d+), Actual Time = (\d+)"
448            for record in records:
449                match = re.search(pattern, record)
450                if match:
451                    expected_time = int(match.group(2))
452                    actual_time = int(match.group(3))
453                    case_name = match.group(1)
454                    if case_name in self.time_data:
455                        self.time_data[case_name].add_actual_time(actual_time)
456                    else:
457                        self.time_data[case_name] = TimeRecord(expected_time, actual_time)
458
459        def get_perf_records():
460            records = []
461            try:
462                for line in iter(hilog_process.stdout.readline, b''):
463                    decode_line = line.decode('utf-8')
464                    if '[ArkCompilerPerfTest]' in decode_line:
465                        records.append(decode_line)
466            except ValueError:
467                self.driver.log_info('hilog stream is closed.')
468            finally:
469                get_time_from_records(records)
470
471        perf_records_thread = threading.Thread(target=get_perf_records)
472        perf_records_thread.start()
473
474        return hilog_process, perf_records_thread
475
476    def lock_for_performance(self):
477        '''
478        执行性能用例前先进行锁频锁核操作
479        '''
480        if self._check_if_already_locked():
481            self.driver.log_info("The device has locked the frequency and core.")
482            return True
483        # 获取系统参数
484        self._check_if_support_ipa()
485        self._check_perf_genius_version()
486        if not self._get_core_number():
487            self.driver.log_info("Get core number failed.")
488            return False
489        # 取消性能限制
490        self._disable_perf_limitation()
491        # 锁频
492        self._lock_frequency()
493        self._lock_ddr_frequency()
494        # 锁中核和大核
495        self._lock_core_by_id(self.cpu_start_id[1], self.total_cpu_num - 1)
496        return True
497
498    def _check_if_support_ipa(self):
499        '''
500        壳温IPA支持情况检查,检查结果存在self.board_ipa_support中
501        '''
502        result = self.driver.shell("cat /sys/class/thermal/thermal_zone1/type").strip()
503        self.board_ipa_support = (result == 'board_thermal')
504        self.driver.log_info('If support IPA: ' + str(self.board_ipa_support))
505
506    def _check_perf_genius_version(self):
507        '''
508        perfGenius版本号检查
509        '''
510        result = self.driver.shell("ps -ef | grep perfg")
511        idx = result.find('perfgenius@')
512        if idx != -1:
513            start_idx = idx + len('perfgenius') + 1
514            self.perfg_version = result[start_idx:start_idx + 4]
515        self.driver.log_info('PerfGenius Version: ' + self.perfg_version)
516
517    def _get_core_number(self):
518        '''
519        从设备上读取大中小核个数
520        '''
521        # 获取CPU总个数
522        result = self.driver.shell("cat /sys/devices/system/cpu/possible").strip()
523        idx = result.find('-')
524        if idx == -1 or result[idx + 1:] == '0':
525            self.driver.log_info('Get total cpu num failed')
526            return False
527        self.total_cpu_num = int(result[idx + 1:]) + 1
528        self.driver.log_info('total_cpu_num = ' + str(self.total_cpu_num))
529        # 获取CPU小核个数
530        result = self.driver.shell("cat /sys/devices/system/cpu/cpu0/topology/core_siblings_list").strip()
531        idx = result.find('-')
532        if idx == -1 or result[idx + 1:] == '0':
533            self.driver.log_info('Get small-core cpu num failed')
534            return False
535        self.cpu_start_id[1] = int(result[idx + 1:]) + 1
536        self.driver.log_info('cpu_start_id[1] = ' + str(self.cpu_start_id[1]))
537        # 获取CPU中核个数
538        result = self.driver.shell(
539            f"cat /sys/devices/system/cpu/cpu{self.cpu_start_id[1]}/topology/core_siblings_list").strip()
540        idx = result.find('-')
541        if idx == -1 or result[idx + 1:] == '0':
542            self.driver.log_info('Get medium-core cpu num failed')
543            return False
544        self.cpu_start_id[2] = int(result[idx + 1:]) + 1
545        if self.cpu_start_id[2] == self.total_cpu_num:
546            self.cpu_start_id[2] = self.cpu_start_id[1]
547            return True
548        self.driver.log_info('cpu_start_id[2] = ' + str(self.cpu_start_id[2]))
549        # 获取CPU大核个数
550        result = self.driver.shell(
551            f"cat /sys/devices/system/cpu/cpu{self.cpu_start_id[2]}/topology/core_siblings_list").strip()
552        if result == '':
553            self.driver.log_info('Get large-core cpu num failed')
554            return False
555        idx = result.find('-')
556        tmp_total_cpu_num = (int(result) if idx == -1 else int(result[idx + 1])) + 1
557        if tmp_total_cpu_num != self.total_cpu_num:
558            self.driver.log_info('Get large-core cpu num failed')
559            return False
560        self.cpu_num_list[0] = self.cpu_start_id[1]
561        self.cpu_num_list[1] = self.cpu_start_id[2] - self.cpu_start_id[1]
562        self.cpu_num_list[2] = self.total_cpu_num - self.cpu_start_id[2]
563        self.driver.log_info(f'Small-core cpu number: {self.cpu_num_list[0]};'
564                             f'Medium-core cpu number: {self.cpu_num_list[1]};'
565                             f'Large-core cpu number: {self.cpu_num_list[2]};')
566        return True
567
568    def _disable_perf_limitation(self):
569        '''
570        查杀省电精灵并关闭IPA和PerfGenius
571        '''
572        # 如果存在省电精灵,强制删除并重启手机
573        result = self.driver.shell("find /system/app -name HwPowerGenieEngine3").strip()
574        if result != '':
575            self.driver.shell("rm -rf /system/app/HwPowerGenieEngine3")
576            self.driver.System.reboot()
577            self.driver.System.wait_for_boot_complete()
578
579        # 关闭IPA
580        self.driver.shell("echo disabled > /sys/class/thermal/thermal_zone0/mode")
581        if self.board_ipa_support:
582            self.driver.shell("echo disabled > /sys/class/thermal/thermal_zone0/mode")
583            self.driver.shell("echo 10000 > /sys/class/thermal/thermal_zone0/sustainable_power")
584        # 关闭perfGenius, 使用时需要手动修改下方bundle_name
585        bundle_name = ''
586        if self.perfg_version == '0.0':
587            self.driver.shell("dumpsys perfhub --perfhub_dis")
588        else:
589            self.driver.shell(f"lshal debug {bundle_name}@{self.perfg_version}"
590                              f"::IPerfGenius/perfgenius --perfgenius_dis")
591
592    def _lock_frequency_by_start_id(self, start_id):
593        '''
594        锁id为start_id的CPU核的频率
595        '''
596        self.driver.log_info(f"Lock frequency, start_id = {start_id}")
597        result = self.driver.shell(
598            f"cat /sys/devices/system/cpu/cpu{start_id}/cpufreq/scaling_available_frequencies").strip()
599        freq_list = list(map(int, result.strip()))
600        max_freq = max(freq_list)
601        self.driver.shell(f"echo {max_freq} > cat /sys/devices/system/cpu/cpu{start_id}/cpufreq/scaling_max_freq")
602        self.driver.shell(f"echo {max_freq} > cat /sys/devices/system/cpu/cpu{start_id}/cpufreq/scaling_min_freq")
603        self.driver.shell(f"echo {max_freq} > cat /sys/devices/system/cpu/cpu{start_id}/cpufreq/scaling_max_freq")
604
605    def _lock_ddr_frequency(self):
606        '''
607        DDR锁频,锁最接近749000000的频率
608        '''
609        std_freq = 749000000
610        self.driver.log_info("Lock ddr frequency")
611        result = self.driver.shell("cat /sys/class/devfreq/ddrfreq/available_frequencies").strip()
612        freq_list = list(map(int, result.strip()))
613        freq = min(freq_list, key=lambda x: abs(x - std_freq))
614        self.driver.shell(f"echo {freq} > /sys/class/devfreq/ddrfreq/max_freq")
615        self.driver.shell(f"echo {freq} > /sys/class/devfreq/ddrfreq/min_freq")
616        self.driver.shell(f"echo {freq} > /sys/class/devfreq/ddrfreq/max_freq")
617        self.driver.shell(f"echo {freq} > /sys/class/devfreq/ddrfreq_up_threshold/max_freq")
618        self.driver.shell(f"echo {freq} > /sys/class/devfreq/ddrfreq_up_threshold/min_freq")
619        self.driver.shell(f"echo {freq} > /sys/class/devfreq/ddrfreq_up_threshold/max_freq")
620
621    def _lock_frequency(self):
622        '''
623        锁CPU的所有核的频率
624        '''
625        # 小核锁频
626        self._lock_frequency_by_start_id(self.cpu_start_id[0])
627        # 中核锁频
628        if self.cpu_num_list[1] != 0:
629            self._lock_frequency_by_start_id(self.cpu_start_id[1])
630        # 大核锁频
631        self._lock_frequency_by_start_id(self.cpu_start_id[2])
632
633    def _lock_core_by_id(self, start_id, end_id):
634        '''
635        锁start_id到end_id的CPU核
636        '''
637        for i in range(start_id, end_id + 1):
638            self.driver.shell(f"echo 0 > sys/devices/system/cpu/cpu{i}/online")
639        self.driver.shell("echo test > sys/power/wake_unlock")
640
641    def _check_if_already_locked(self):
642        '''
643        根据DDR频率来判断是否已经锁频锁核
644        '''
645        result = self.driver.shell("cat /sys/class/devfreq/ddrfreq/cur_freq").strip()
646        return result == "749000000"
647