148f512ceSopenharmony_ci#!/usr/bin/env python 248f512ceSopenharmony_ci# -*- coding: utf-8 -*- 348f512ceSopenharmony_ci# Copyright (c) 2021 Huawei Device Co., Ltd. 448f512ceSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 548f512ceSopenharmony_ci# you may not use this file except in compliance with the License. 648f512ceSopenharmony_ci# You may obtain a copy of the License at 748f512ceSopenharmony_ci# 848f512ceSopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 948f512ceSopenharmony_ci# 1048f512ceSopenharmony_ci# Unless required by applicable law or agreed to in writing, software 1148f512ceSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 1248f512ceSopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1348f512ceSopenharmony_ci# See the License for the specific language governing permissions and 1448f512ceSopenharmony_ci# limitations under the License. 1548f512ceSopenharmony_ci# 1648f512ceSopenharmony_ciimport argparse 1748f512ceSopenharmony_ciimport os 1848f512ceSopenharmony_ciimport shutil 1948f512ceSopenharmony_ci 2048f512ceSopenharmony_cifrom ctypes import * 2148f512ceSopenharmony_cifrom hiperf_utils import HdcInterface, get_build_id 2248f512ceSopenharmony_cifrom hiperf_utils import dir_check 2348f512ceSopenharmony_cifrom hiperf_utils import file_check 2448f512ceSopenharmony_cifrom hiperf_utils import get_lib 2548f512ceSopenharmony_cifrom hiperf_utils import get_arg_list 2648f512ceSopenharmony_ci 2748f512ceSopenharmony_ciABS_PATH = os.path.split(os.path.realpath(__file__))[0] 2848f512ceSopenharmony_ci 2948f512ceSopenharmony_ci 3048f512ceSopenharmony_ciclass GetLibFiles(object): 3148f512ceSopenharmony_ci """Collect all binaries needed by perf.data in binary_cache.""" 3248f512ceSopenharmony_ci 3348f512ceSopenharmony_ci def __init__(self): 3448f512ceSopenharmony_ci self.local_cache_dir = os.path.join(ABS_PATH, 'binary_cache') 3548f512ceSopenharmony_ci if not os.path.isdir(self.local_cache_dir): 3648f512ceSopenharmony_ci os.makedirs(self.local_cache_dir) 3748f512ceSopenharmony_ci self.binary_map = {} 3848f512ceSopenharmony_ci 3948f512ceSopenharmony_ci def recv_binary_cache(self, perf_data, lib_dirs, copy): 4048f512ceSopenharmony_ci self.get_used_binaries(perf_data) 4148f512ceSopenharmony_ci self.copy_binaries_from_lib_dirs(lib_dirs) 4248f512ceSopenharmony_ci self.hdc = HdcInterface() 4348f512ceSopenharmony_ci if copy: 4448f512ceSopenharmony_ci self.recv_binaries_from_device() 4548f512ceSopenharmony_ci self.recv_kernel_symbols() 4648f512ceSopenharmony_ci 4748f512ceSopenharmony_ci def get_used_binaries(self, perf_data): 4848f512ceSopenharmony_ci """read perf.data, get all used binaries and their build id 4948f512ceSopenharmony_ci (if available).""" 5048f512ceSopenharmony_ci # A dict mapping from binary name to build_id 5148f512ceSopenharmony_ci lib_hiperf_report = get_lib() 5248f512ceSopenharmony_ci lib_hiperf_report.ReportGetSymbolFiles.restype = c_char_p 5348f512ceSopenharmony_ci lib_hiperf_report.ReportGetSymbolFiles.argtypes = [c_char_p] 5448f512ceSopenharmony_ci ret = lib_hiperf_report.ReportGetSymbolFiles(perf_data.encode("utf-8")) 5548f512ceSopenharmony_ci 5648f512ceSopenharmony_ci dso_build_id = str(ret, encoding="utf-8", errors='ignore') 5748f512ceSopenharmony_ci dso_list = dso_build_id.split('],[') 5848f512ceSopenharmony_ci dso_list[0] = dso_list[0][1:] 5948f512ceSopenharmony_ci dso_list[-1] = dso_list[-1][0:-1] 6048f512ceSopenharmony_ci dso_dict = {} 6148f512ceSopenharmony_ci for i in dso_list: 6248f512ceSopenharmony_ci group = i.split(',') 6348f512ceSopenharmony_ci if len(group) == 2: 6448f512ceSopenharmony_ci dso_dict[group[0]] = group[1] 6548f512ceSopenharmony_ci else: 6648f512ceSopenharmony_ci dso_dict[group[0]] = None 6748f512ceSopenharmony_ci self.binary_map = dso_dict 6848f512ceSopenharmony_ci 6948f512ceSopenharmony_ci # open source 7048f512ceSopenharmony_ci def copy_binaries_from_lib_dirs(self, lib_dirs): 7148f512ceSopenharmony_ci """collect all files in lib_dirs.""" 7248f512ceSopenharmony_ci if not lib_dirs: 7348f512ceSopenharmony_ci return 7448f512ceSopenharmony_ci 7548f512ceSopenharmony_ci # key is binary name , value is path list 7648f512ceSopenharmony_ci file_dict = {} 7748f512ceSopenharmony_ci self.get_local_bin_map(file_dict) 7848f512ceSopenharmony_ci 7948f512ceSopenharmony_ci for lib_dir in lib_dirs: 8048f512ceSopenharmony_ci for root, _, files in os.walk(lib_dir): 8148f512ceSopenharmony_ci self.confirm_copy_file(root, files, file_dict) 8248f512ceSopenharmony_ci 8348f512ceSopenharmony_ci def get_local_bin_map(self, file_dict): 8448f512ceSopenharmony_ci for bin_file in self.binary_map: 8548f512ceSopenharmony_ci # if path is /system/lib/libhi_irq.so , get libhi_irq.so 8648f512ceSopenharmony_ci filename = bin_file[bin_file.rfind('/') + 1:] 8748f512ceSopenharmony_ci if file_dict.get(filename) is None: 8848f512ceSopenharmony_ci file_dict[filename] = [bin_file] 8948f512ceSopenharmony_ci else: 9048f512ceSopenharmony_ci file_dict[filename].append(bin_file) 9148f512ceSopenharmony_ci 9248f512ceSopenharmony_ci def confirm_copy_file(self, root, files, file_dict): 9348f512ceSopenharmony_ci for filename in files: 9448f512ceSopenharmony_ci paths = file_dict.get(filename) 9548f512ceSopenharmony_ci if not paths: 9648f512ceSopenharmony_ci continue 9748f512ceSopenharmony_ci 9848f512ceSopenharmony_ci build_id = get_build_id(os.path.join(root, filename)) 9948f512ceSopenharmony_ci if not build_id: 10048f512ceSopenharmony_ci continue 10148f512ceSopenharmony_ci 10248f512ceSopenharmony_ci for bin_file in paths: 10348f512ceSopenharmony_ci req_build_id = self.binary_map.get(bin_file) 10448f512ceSopenharmony_ci if req_build_id == build_id: 10548f512ceSopenharmony_ci self.copy_to_binary_cache( 10648f512ceSopenharmony_ci os.path.join(root, filename), bin_file) 10748f512ceSopenharmony_ci 10848f512ceSopenharmony_ci def copy_to_binary_cache(self, from_path, target_file): 10948f512ceSopenharmony_ci if target_file[0] == '/': 11048f512ceSopenharmony_ci target_file = target_file[1:] 11148f512ceSopenharmony_ci 11248f512ceSopenharmony_ci target_file = target_file.replace('/', os.sep) 11348f512ceSopenharmony_ci target_file = os.path.join(self.local_cache_dir, target_file) 11448f512ceSopenharmony_ci target_dir = os.path.dirname(target_file) 11548f512ceSopenharmony_ci 11648f512ceSopenharmony_ci if not os.path.isdir(target_dir): 11748f512ceSopenharmony_ci os.makedirs(target_dir) 11848f512ceSopenharmony_ci print('copy to binary_cache: %s to %s' % (from_path, target_file)) 11948f512ceSopenharmony_ci shutil.copy(from_path, target_file) 12048f512ceSopenharmony_ci 12148f512ceSopenharmony_ci def recv_binaries_from_device(self): 12248f512ceSopenharmony_ci """pull binaries needed in perf.data to binary_cache.""" 12348f512ceSopenharmony_ci for binary in self.binary_map: 12448f512ceSopenharmony_ci # [kernel.kallsyms] or something can't find binary. 12548f512ceSopenharmony_ci if not binary.startswith('/') or \ 12648f512ceSopenharmony_ci binary.startswith("/dev/" or binary == "//anon"): 12748f512ceSopenharmony_ci continue 12848f512ceSopenharmony_ci # fit all platform 12948f512ceSopenharmony_ci binary_cache_file = binary[1:].replace('/', os.sep) 13048f512ceSopenharmony_ci local_cache_file = os.path.join(self.local_cache_dir, 13148f512ceSopenharmony_ci binary_cache_file) 13248f512ceSopenharmony_ci self.check_and_recv_binary(binary, local_cache_file) 13348f512ceSopenharmony_ci 13448f512ceSopenharmony_ci def check_and_recv_binary(self, binary, local_cache_file): 13548f512ceSopenharmony_ci """If the binary_cache_file exists and has the expected_build_id, there 13648f512ceSopenharmony_ci is no need to pull the binary from device. Otherwise, pull it. 13748f512ceSopenharmony_ci """ 13848f512ceSopenharmony_ci req_build_id = self.binary_map[binary] 13948f512ceSopenharmony_ci need_pull = True 14048f512ceSopenharmony_ci # compare with build id adjust is match file 14148f512ceSopenharmony_ci if os.path.isfile(local_cache_file): 14248f512ceSopenharmony_ci need_pull = False 14348f512ceSopenharmony_ci build_id = get_build_id(local_cache_file) 14448f512ceSopenharmony_ci if req_build_id != build_id: 14548f512ceSopenharmony_ci print('local file build id is %s is not request build %s' 14648f512ceSopenharmony_ci % (build_id,req_build_id)) 14748f512ceSopenharmony_ci need_pull = True 14848f512ceSopenharmony_ci 14948f512ceSopenharmony_ci if need_pull: 15048f512ceSopenharmony_ci target_dir = os.path.dirname(local_cache_file) 15148f512ceSopenharmony_ci if not os.path.isdir(target_dir): 15248f512ceSopenharmony_ci os.makedirs(target_dir) 15348f512ceSopenharmony_ci if os.path.isfile(local_cache_file): 15448f512ceSopenharmony_ci os.remove(local_cache_file) 15548f512ceSopenharmony_ci self.pull_file_from_device(binary, local_cache_file) 15648f512ceSopenharmony_ci print('recv file to binary_cache: %s to %s' % (binary, 15748f512ceSopenharmony_ci local_cache_file)) 15848f512ceSopenharmony_ci self.confirm_del_file(binary,local_cache_file) 15948f512ceSopenharmony_ci else: 16048f512ceSopenharmony_ci print('not need recv, use host file in binary_cache: %s' % 16148f512ceSopenharmony_ci local_cache_file) 16248f512ceSopenharmony_ci 16348f512ceSopenharmony_ci def pull_file_from_device(self, device_path, host_path): 16448f512ceSopenharmony_ci if self.hdc.run_hdc_cmd(['file recv', device_path, host_path]): 16548f512ceSopenharmony_ci return True 16648f512ceSopenharmony_ci # In non-root device, we can't pull /data/app/XXX/base.odex directly. 16748f512ceSopenharmony_ci # Instead, we can first copy the file to /data/local/tmp, then pull it. 16848f512ceSopenharmony_ci filename = device_path[device_path.rfind('/') + 1:] 16948f512ceSopenharmony_ci if (self.hdc.run_hdc_cmd(['shell', 'cp', device_path, 17048f512ceSopenharmony_ci '/data/local/tmp']) and 17148f512ceSopenharmony_ci self.hdc.run_hdc_cmd(['file recv', 17248f512ceSopenharmony_ci os.path.join('/data/local/tmp/', filename), 17348f512ceSopenharmony_ci host_path])): 17448f512ceSopenharmony_ci self.hdc.run_hdc_cmd(['shell', 'rm', 17548f512ceSopenharmony_ci os.path.join('/data/local/tmp/', filename)]) 17648f512ceSopenharmony_ci return True 17748f512ceSopenharmony_ci print('failed to pull %s from device' % device_path) 17848f512ceSopenharmony_ci return False 17948f512ceSopenharmony_ci 18048f512ceSopenharmony_ci def confirm_del_file(self, device_path,host_path): 18148f512ceSopenharmony_ci build_id = get_build_id(os.path.join(host_path)) 18248f512ceSopenharmony_ci if not build_id: 18348f512ceSopenharmony_ci return 18448f512ceSopenharmony_ci req_build_id = self.binary_map.get(device_path) 18548f512ceSopenharmony_ci if not req_build_id == build_id: 18648f512ceSopenharmony_ci print('recv file %s build id %s not match request build id %s,' 18748f512ceSopenharmony_ci ' delete it' % (host_path,build_id,req_build_id)) 18848f512ceSopenharmony_ci os.remove(host_path) 18948f512ceSopenharmony_ci 19048f512ceSopenharmony_ci 19148f512ceSopenharmony_ci def recv_kernel_symbols(self): 19248f512ceSopenharmony_ci file_path = os.path.join(self.local_cache_dir, 'kallsyms') 19348f512ceSopenharmony_ci if os.path.isfile(file_path): 19448f512ceSopenharmony_ci os.remove(file_path) 19548f512ceSopenharmony_ci if self.hdc.switch_root(): 19648f512ceSopenharmony_ci old_kptr_restrict = os.popen('hdc shell cat /proc/sys/kernel/kptr_restrict').read() 19748f512ceSopenharmony_ci self.hdc.run_hdc_cmd(['shell', 19848f512ceSopenharmony_ci '"echo 0 >/proc/sys/kernel/kptr_restrict"']) 19948f512ceSopenharmony_ci self.hdc.run_hdc_cmd(['file recv', '/proc/kallsyms', file_path]) 20048f512ceSopenharmony_ci self.hdc.run_hdc_cmd(['shell', 20148f512ceSopenharmony_ci '"echo ' + old_kptr_restrict[0] + ' >/proc/sys/kernel/kptr_restrict"']) 20248f512ceSopenharmony_ci 20348f512ceSopenharmony_ci 20448f512ceSopenharmony_cidef main(): 20548f512ceSopenharmony_ci parser = argparse.ArgumentParser(description=""" Recv binaries needed by 20648f512ceSopenharmony_ci perf.data from device to binary_cache directory.""") 20748f512ceSopenharmony_ci parser.add_argument('-i', '--perf_data', default='perf.data', 20848f512ceSopenharmony_ci type=file_check, help=""" The path of profiling 20948f512ceSopenharmony_ci data.""") 21048f512ceSopenharmony_ci parser.add_argument('-l', '--local_lib_dir', type=dir_check, nargs='+', 21148f512ceSopenharmony_ci help="""Path to find debug version of local shared 21248f512ceSopenharmony_ci libraries used in the app.""", action='append') 21348f512ceSopenharmony_ci parser.add_argument('-c', '--copy_symbol_from_device', default='0', 21448f512ceSopenharmony_ci help=""" Copy symbol files from device.""") 21548f512ceSopenharmony_ci args = parser.parse_args() 21648f512ceSopenharmony_ci 21748f512ceSopenharmony_ci recver = GetLibFiles() 21848f512ceSopenharmony_ci lib_dirs = get_arg_list(args.local_lib_dir) 21948f512ceSopenharmony_ci recver.recv_binary_cache(args.perf_data, lib_dirs, 22048f512ceSopenharmony_ci args.copy_symbol_from_device == '1' or args.copy_symbol_from_device == 'true') 22148f512ceSopenharmony_ci 22248f512ceSopenharmony_ci 22348f512ceSopenharmony_ciif __name__ == '__main__': 22448f512ceSopenharmony_ci main() 225