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