1d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui#!/usr/bin/env python 2d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# 3d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# Copyright (C) 2016 The Android Open Source Project 4d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# 5d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# Licensed under the Apache License, Version 2.0 (the "License"); 6d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# you may not use this file except in compliance with the License. 7d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# You may obtain a copy of the License at 8d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# 9d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# http://www.apache.org/licenses/LICENSE-2.0 10d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# 11d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# Unless required by applicable law or agreed to in writing, software 12d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# distributed under the License is distributed on an "AS IS" BASIS, 13d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# See the License for the specific language governing permissions and 15d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# limitations under the License. 16d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# 17d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 18d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui"""annotate.py: annotate source files based on perf.data. 19d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui""" 20d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 21d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 22d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuiimport argparse 23d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuiimport os 24d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuiimport os.path 25d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuiimport shutil 26d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuiimport subprocess 27d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuiimport sys 28d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 29d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuifrom simpleperf_report_lib import * 30d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuifrom utils import * 31d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 32129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cuiclass SourceLine(object): 33129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui def __init__(self, file, function, line): 34129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.file = file 35129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.function = function 36129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.line = line 37129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui 38129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui @property 39129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui def file_key(self): 40129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui return self.file 41129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui 42129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui @property 43129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui def function_key(self): 44129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui return (self.file, self.function) 45129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui 46129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui @property 47129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui def line_key(self): 48129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui return (self.file, self.line) 49129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui 50129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui 51d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# TODO: using addr2line can't convert from function_start_address to 52d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# source_file:line very well for java code. Because in .debug_line section, 53d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# there is some distance between function_start_address and the address 54d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui# of the first instruction which can be mapped to source line. 55d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuiclass Addr2Line(object): 56d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """collect information of how to map [dso_name,vaddr] to [source_file:line]. 57d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """ 58129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui def __init__(self, addr2line_path, symfs_dir=None): 59d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.dso_dict = dict() 60b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui if addr2line_path and is_executable_available(addr2line_path): 61b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui self.addr2line_path = addr2line_path 62b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui else: 63b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui self.addr2line_path = find_tool_path('addr2line') 64b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui if not self.addr2line_path: 65b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui log_exit("Can't find addr2line.") 66129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.symfs_dir = symfs_dir 67d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 68d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 69d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def add_addr(self, dso_name, addr): 70d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui dso = self.dso_dict.get(dso_name) 71d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if dso is None: 72d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.dso_dict[dso_name] = dso = dict() 7398058a897528620916427e96485c58b8a534c6b3Yabin Cui if addr not in dso: 74d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui dso[addr] = None 75d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 76d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 77d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def convert_addrs_to_lines(self): 78d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # store a list of source files 79d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.file_list = [] 80d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # map from file to id with file_list[id] == file 81d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.file_dict = {} 82129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.file_list.append('') 83129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.file_dict[''] = 0 84d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 85d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for dso_name in self.dso_dict.keys(): 86d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._convert_addrs_to_lines(dso_name, self.dso_dict[dso_name]) 87d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._combine_source_files() 88d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 89d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 90d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _convert_addrs_to_lines(self, dso_name, dso): 91129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui dso_path = self._find_dso_path(dso_name) 92d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if dso_path is None: 93d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui log_warning("can't find dso '%s'" % dso_name) 94d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui dso.clear() 95d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return 96129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui addrs = sorted(dso.keys()) 97d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui addr_str = [] 98d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for addr in addrs: 99d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui addr_str.append('0x%x' % addr) 100d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui addr_str = '\n'.join(addr_str) 101129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui subproc = subprocess.Popen([self.addr2line_path, '-e', dso_path, '-aifC'], 102d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui stdin=subprocess.PIPE, stdout=subprocess.PIPE) 10398058a897528620916427e96485c58b8a534c6b3Yabin Cui (stdoutdata, _) = subproc.communicate(str_to_bytes(addr_str)) 10498058a897528620916427e96485c58b8a534c6b3Yabin Cui stdoutdata = bytes_to_str(stdoutdata) 105129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui stdoutdata = stdoutdata.strip().split('\n') 106d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if len(stdoutdata) < len(addrs): 107d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui log_fatal("addr2line didn't output enough lines") 108129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui addr_pos = 0 109129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui out_pos = 0 110129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui while addr_pos < len(addrs) and out_pos < len(stdoutdata): 111129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui addr_line = stdoutdata[out_pos] 112129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui out_pos += 1 113129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui assert addr_line[:2] == "0x" 114129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui assert out_pos < len(stdoutdata) 115129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui source_lines = [] 116129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui while out_pos < len(stdoutdata) and stdoutdata[out_pos][:2] != "0x": 117129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui function = stdoutdata[out_pos] 118129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui out_pos += 1 119129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui assert out_pos < len(stdoutdata) 120cbddd3829b6cf125340d1795f04931a5c8ff83efYabin Cui # Handle lines like "C:\Users\...\file:32". 121cbddd3829b6cf125340d1795f04931a5c8ff83efYabin Cui items = stdoutdata[out_pos].rsplit(':', 1) 122cbddd3829b6cf125340d1795f04931a5c8ff83efYabin Cui if len(items) != 2: 123cbddd3829b6cf125340d1795f04931a5c8ff83efYabin Cui continue 124cbddd3829b6cf125340d1795f04931a5c8ff83efYabin Cui (file, line) = items 125129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui line = line.split()[0] # Remove comments after line number 126129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui out_pos += 1 127129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if file.find('?') != -1: 128129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui file = 0 129129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui else: 130129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui file = self._get_file_id(file) 131129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if line.find('?') != -1: 132d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui line = 0 133d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui else: 134129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui line = int(line) 135129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui source_lines.append(SourceLine(file, function, line)) 136cbddd3829b6cf125340d1795f04931a5c8ff83efYabin Cui dso[addrs[addr_pos]] = source_lines 137cbddd3829b6cf125340d1795f04931a5c8ff83efYabin Cui addr_pos += 1 138129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui assert addr_pos == len(addrs) 139129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui 140d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 141129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui def _get_file_id(self, file): 142129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui id = self.file_dict.get(file) 143129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if id is None: 144129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui id = len(self.file_list) 145129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.file_list.append(file) 146129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.file_dict[file] = id 147129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui return id 148d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 149d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _combine_source_files(self): 150d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """It is possible that addr2line gives us different names for the same 151d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui file, like: 152d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui /usr/local/.../src/main/jni/sudo-game-jni.cpp 153d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui sudo-game-jni.cpp 154d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui We'd better combine these two files. We can do it by combining 155d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui source files with no conflicts in path. 156d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """ 157d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # Collect files having the same filename. 158d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui filename_dict = dict() 159d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for file in self.file_list: 160d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui index = max(file.rfind('/'), file.rfind(os.sep)) 161d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui filename = file[index+1:] 162d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui entry = filename_dict.get(filename) 163d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if entry is None: 164d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui filename_dict[filename] = entry = [] 165d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui entry.append(file) 166d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 167d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # Combine files having the same filename and having no conflicts in path. 168d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for filename in filename_dict.keys(): 169d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui files = filename_dict[filename] 170d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if len(files) == 1: 171d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui continue 172d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for file in files: 173d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui to_file = file 174d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # Test if we can merge files[i] with another file having longer 175d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # path. 176d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for f in files: 177d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if len(f) > len(to_file) and f.find(file) != -1: 178d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui to_file = f 179d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if to_file != file: 180d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui from_id = self.file_dict[file] 181d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui to_id = self.file_dict[to_file] 182d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.file_list[from_id] = self.file_list[to_id] 183d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 184d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 185129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui def get_sources(self, dso_name, addr): 186d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui dso = self.dso_dict.get(dso_name) 187d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if dso is None: 188129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui return [] 189129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui item = dso.get(addr, []) 190129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui source_lines = [] 191129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui for source in item: 192129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui source_lines.append(SourceLine(self.file_list[source.file], 193129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui source.function, source.line)) 194129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui return source_lines 195129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui 196129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui 197129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui def _find_dso_path(self, dso): 198129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if dso[0] != '/' or dso == '//anon': 199129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui return None 200129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if self.symfs_dir: 201129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui dso_path = os.path.join(self.symfs_dir, dso[1:]) 202129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if os.path.isfile(dso_path): 203129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui return dso_path 204129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if os.path.isfile(dso): 205129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui return dso 206129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui return None 207d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 208d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 209d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuiclass Period(object): 210d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """event count information. It can be used to represent event count 211d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui of a line, a function, a source file, or a binary. It contains two 212d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui parts: period and acc_period. 213d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui When used for a line, period is the event count occurred when running 214d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui that line, acc_period is the accumulated event count occurred when 215d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui running that line and functions called by that line. Same thing applies 216d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui when it is used for a function, a source file, or a binary. 217d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """ 218d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def __init__(self, period=0, acc_period=0): 219d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.period = period 220d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.acc_period = acc_period 221d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 222d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 223d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def __iadd__(self, other): 224d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.period += other.period 225d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.acc_period += other.acc_period 226d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return self 227d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 228d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 229d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuiclass DsoPeriod(object): 230d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """Period for each shared library""" 231d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def __init__(self, dso_name): 232d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.dso_name = dso_name 233d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.period = Period() 234d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 235d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 236d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def add_period(self, period): 237d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.period += period 238d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 239d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 240d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuiclass FilePeriod(object): 241d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """Period for each source file""" 242d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def __init__(self, file): 243d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.file = file 244d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.period = Period() 245d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # Period for each line in the file. 246d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.line_dict = {} 247d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # Period for each function in the source file. 248d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.function_dict = {} 249d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 250d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 251d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def add_period(self, period): 252d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.period += period 253d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 254d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 255d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def add_line_period(self, line, period): 256d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui a = self.line_dict.get(line) 257d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if a is None: 258d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.line_dict[line] = a = Period() 259d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui a += period 260d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 261d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 262d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def add_function_period(self, function_name, function_start_line, period): 263d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui a = self.function_dict.get(function_name) 264129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if not a: 265d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if function_start_line is None: 266d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui function_start_line = -1 267d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.function_dict[function_name] = a = [function_start_line, Period()] 268d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui a[1] += period 269d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 270d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 271d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cuiclass SourceFileAnnotator(object): 272d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """group code for annotating source files""" 273d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def __init__(self, config): 274d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # check config variables 275b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui config_names = ['perf_data_list', 'source_dirs', 'comm_filters', 276b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui 'pid_filters', 'tid_filters', 'dso_filters', 'addr2line_path'] 277d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for name in config_names: 27898058a897528620916427e96485c58b8a534c6b3Yabin Cui if name not in config: 279b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui log_exit('config [%s] is missing' % name) 280b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui symfs_dir = 'binary_cache' 281b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui if not os.path.isdir(symfs_dir): 282b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui symfs_dir = None 283b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui kallsyms = 'binary_cache/kallsyms' 284b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui if not os.path.isfile(kallsyms): 285b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui kallsyms = None 286d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui source_dirs = config['source_dirs'] 287d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for dir in source_dirs: 288d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if not os.path.isdir(dir): 289b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui log_exit('[source_dirs] "%s" is not a dir' % dir) 290b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui if not config['source_dirs']: 291b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui log_exit('Please set source directories.') 292d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 293d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # init member variables 294d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.config = config 295b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui self.symfs_dir = symfs_dir 296b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui self.kallsyms = kallsyms 297129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.comm_filter = set(config['comm_filters']) if config.get('comm_filters') else None 298129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if config.get('pid_filters'): 299129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.pid_filter = {int(x) for x in config['pid_filters']} 300129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui else: 301129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.pid_filter = None 302129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if config.get('tid_filters'): 303129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.tid_filter = {int(x) for x in config['tid_filters']} 304129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui else: 305129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.tid_filter = None 306129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.dso_filter = set(config['dso_filters']) if config.get('dso_filters') else None 307d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 308b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui config['annotate_dest_dir'] = 'annotated_files' 309d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui output_dir = config['annotate_dest_dir'] 310d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if os.path.isdir(output_dir): 311d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui shutil.rmtree(output_dir) 312d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui os.makedirs(output_dir) 313d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 314129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self.addr2line = Addr2Line(self.config['addr2line_path'], symfs_dir) 315d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 316d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 317d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def annotate(self): 318d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._collect_addrs() 319d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._convert_addrs_to_lines() 320d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._generate_periods() 321d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._write_summary() 322d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._collect_source_files() 323d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._annotate_files() 324d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 325d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 326d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _collect_addrs(self): 327d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """Read perf.data, collect all addresses we need to convert to 328d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui source file:line. 329d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """ 330d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for perf_data in self.config['perf_data_list']: 331d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui lib = ReportLib() 332129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui lib.SetRecordFile(perf_data) 333129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if self.symfs_dir: 334d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui lib.SetSymfs(self.symfs_dir) 335129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if self.kallsyms: 336d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui lib.SetKallsymsFile(self.kallsyms) 337d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui while True: 338d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui sample = lib.GetNextSample() 339d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if sample is None: 340d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui lib.Close() 341d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui break 342d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if not self._filter_sample(sample): 343d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui continue 344d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui symbols = [] 345d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui symbols.append(lib.GetSymbolOfCurrentSample()) 346d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui callchain = lib.GetCallChainOfCurrentSample() 347d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for i in range(callchain.nr): 348d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui symbols.append(callchain.entries[i].symbol) 349d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for symbol in symbols: 350d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if self._filter_symbol(symbol): 351d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.addr2line.add_addr(symbol.dso_name, symbol.vaddr_in_file) 352d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.addr2line.add_addr(symbol.dso_name, symbol.symbol_addr) 353d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 354d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 355d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _filter_sample(self, sample): 356d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """Return true if the sample can be used.""" 357129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if self.comm_filter: 358d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if sample.thread_comm not in self.comm_filter: 359d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return False 360129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if self.pid_filter: 361d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if sample.pid not in self.pid_filter: 362d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return False 363129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if self.tid_filter: 364d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if sample.tid not in self.tid_filter: 365d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return False 366d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return True 367d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 368d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 369d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _filter_symbol(self, symbol): 370129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if not self.dso_filter or symbol.dso_name in self.dso_filter: 371d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return True 372d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return False 373d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 374d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 375d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _convert_addrs_to_lines(self): 376d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.addr2line.convert_addrs_to_lines() 377d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 378d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 379d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _generate_periods(self): 380d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """read perf.data, collect Period for all types: 381d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui binaries, source files, functions, lines. 382d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """ 383d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.period = 0 384d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.dso_periods = dict() 385d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.file_periods = dict() 386d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for perf_data in self.config['perf_data_list']: 387d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui lib = ReportLib() 388129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui lib.SetRecordFile(perf_data) 389129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if self.symfs_dir: 390d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui lib.SetSymfs(self.symfs_dir) 391129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if self.kallsyms: 392d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui lib.SetKallsymsFile(self.kallsyms) 393d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui while True: 394d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui sample = lib.GetNextSample() 395d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if sample is None: 396d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui lib.Close() 397d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui break 398d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if not self._filter_sample(sample): 399d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui continue 400d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui symbols = [] 401d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui symbols.append(lib.GetSymbolOfCurrentSample()) 402d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui callchain = lib.GetCallChainOfCurrentSample() 403d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for i in range(callchain.nr): 404d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui symbols.append(callchain.entries[i].symbol) 405d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # Each sample has a callchain, but its period is only used once 406d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # to add period for each function/source_line/source_file/binary. 407d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # For example, if more than one entry in the callchain hits a 408d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # function, the event count of that function is only increased once. 409d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # Otherwise, we may get periods > 100%. 410d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui is_sample_used = False 411d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui used_dso_dict = dict() 412d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui used_file_dict = dict() 413d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui used_function_dict = dict() 414d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui used_line_dict = dict() 415d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui period = Period(sample.period, sample.period) 416d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for i in range(len(symbols)): 417d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui symbol = symbols[i] 418d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if i == 1: 419d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui period = Period(0, sample.period) 420d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if not self._filter_symbol(symbol): 421d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui continue 422d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui is_sample_used = True 423d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # Add period to dso. 424d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._add_dso_period(symbol.dso_name, period, used_dso_dict) 425d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # Add period to source file. 426129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui sources = self.addr2line.get_sources(symbol.dso_name, symbol.vaddr_in_file) 427129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui for source in sources: 428129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if source.file: 429129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self._add_file_period(source, period, used_file_dict) 430129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui # Add period to line. 431129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if source.line: 432129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self._add_line_period(source, period, used_line_dict) 433d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # Add period to function. 434129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui sources = self.addr2line.get_sources(symbol.dso_name, symbol.symbol_addr) 435129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui for source in sources: 436129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if source.file: 437129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self._add_file_period(source, period, used_file_dict) 438129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui if source.function: 439129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui self._add_function_period(source, period, used_function_dict) 440d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 441d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if is_sample_used: 442d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.period += sample.period 443d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 444d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 445d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _add_dso_period(self, dso_name, period, used_dso_dict): 44698058a897528620916427e96485c58b8a534c6b3Yabin Cui if dso_name not in used_dso_dict: 447d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui used_dso_dict[dso_name] = True 448d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui dso_period = self.dso_periods.get(dso_name) 449d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if dso_period is None: 450d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui dso_period = self.dso_periods[dso_name] = DsoPeriod(dso_name) 451d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui dso_period.add_period(period) 452d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 453d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 454129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui def _add_file_period(self, source, period, used_file_dict): 45598058a897528620916427e96485c58b8a534c6b3Yabin Cui if source.file_key not in used_file_dict: 456129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui used_file_dict[source.file_key] = True 457129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui file_period = self.file_periods.get(source.file) 458d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if file_period is None: 459129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui file_period = self.file_periods[source.file] = FilePeriod(source.file) 460d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui file_period.add_period(period) 461d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 462d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 463d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _add_line_period(self, source, period, used_line_dict): 46498058a897528620916427e96485c58b8a534c6b3Yabin Cui if source.line_key not in used_line_dict: 465129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui used_line_dict[source.line_key] = True 466129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui file_period = self.file_periods[source.file] 467129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui file_period.add_line_period(source.line, period) 468d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 469d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 470129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui def _add_function_period(self, source, period, used_function_dict): 47198058a897528620916427e96485c58b8a534c6b3Yabin Cui if source.function_key not in used_function_dict: 472129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui used_function_dict[source.function_key] = True 473129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui file_period = self.file_periods[source.file] 474129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui file_period.add_function_period(source.function, source.line, period) 475d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 476d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 477d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _write_summary(self): 478d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui summary = os.path.join(self.config['annotate_dest_dir'], 'summary') 479d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui with open(summary, 'w') as f: 480d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui f.write('total period: %d\n\n' % self.period) 481d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui dso_periods = sorted(self.dso_periods.values(), 48298058a897528620916427e96485c58b8a534c6b3Yabin Cui key=lambda x: x.period.acc_period, reverse=True) 483d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for dso_period in dso_periods: 484d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui f.write('dso %s: %s\n' % (dso_period.dso_name, 485d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._get_percentage_str(dso_period.period))) 486d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui f.write('\n') 487d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 488d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui file_periods = sorted(self.file_periods.values(), 48998058a897528620916427e96485c58b8a534c6b3Yabin Cui key=lambda x: x.period.acc_period, reverse=True) 490d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for file_period in file_periods: 491d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui f.write('file %s: %s\n' % (file_period.file, 492d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._get_percentage_str(file_period.period))) 493d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for file_period in file_periods: 494d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui f.write('\n\n%s: %s\n' % (file_period.file, 495d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._get_percentage_str(file_period.period))) 496d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui values = [] 497d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for func_name in file_period.function_dict.keys(): 498d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui func_start_line, period = file_period.function_dict[func_name] 499d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui values.append((func_name, func_start_line, period)) 50098058a897528620916427e96485c58b8a534c6b3Yabin Cui values = sorted(values, key=lambda x: x[2].acc_period, reverse=True) 501d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for value in values: 502d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui f.write('\tfunction (%s): line %d, %s\n' % ( 503d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui value[0], value[1], self._get_percentage_str(value[2]))) 504d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui f.write('\n') 505d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for line in sorted(file_period.line_dict.keys()): 506d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui f.write('\tline %d: %s\n' % ( 507d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui line, self._get_percentage_str(file_period.line_dict[line]))) 508d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 509d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 510d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _get_percentage_str(self, period, short=False): 511d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui s = 'acc_p: %f%%, p: %f%%' if short else 'accumulated_period: %f%%, period: %f%%' 512d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return s % self._get_percentage(period) 513d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 514d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 515d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _get_percentage(self, period): 516d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if self.period == 0: 517d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return (0, 0) 518d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui acc_p = 100.0 * period.acc_period / self.period 519d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui p = 100.0 * period.period / self.period 520d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return (acc_p, p) 521d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 522d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 523d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _collect_source_files(self): 524d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self.source_file_dict = dict() 5256cc52fdf2d2c25f4583eb3b0159f71005966dc79Yabin Cui source_file_suffix = ['h', 'c', 'cpp', 'cc', 'java', 'kt'] 526d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for source_dir in self.config['source_dirs']: 527d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for root, _, files in os.walk(source_dir): 528d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for file in files: 529d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if file[file.rfind('.')+1:] in source_file_suffix: 530d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui entry = self.source_file_dict.get(file) 531d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if entry is None: 532d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui entry = self.source_file_dict[file] = [] 533d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui entry.append(os.path.join(root, file)) 534d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 535d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 536d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _find_source_file(self, file): 537d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui filename = file[file.rfind(os.sep)+1:] 538d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui source_files = self.source_file_dict.get(filename) 539d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if source_files is None: 540d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return None 541d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui match_count = 0 542d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui result = None 543d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for path in source_files: 544d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if path.find(file) != -1: 545d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui match_count += 1 546d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui result = path 547d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if match_count > 1: 548d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui log_warning('multiple source for %s, select %s' % (file, result)) 549d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui return result 550d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 551d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 552d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _annotate_files(self): 553d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """Annotate Source files: add acc_period/period for each source file. 554d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 1. Annotate java source files, which have $JAVA_SRC_ROOT prefix. 555d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 2. Annotate c++ source files. 556d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """ 557d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui dest_dir = self.config['annotate_dest_dir'] 558d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for key in self.file_periods.keys(): 5595fac4383a39399c9643c286a7d342c8d5f2d7cabYabin Cui is_java = False 560d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if key.startswith('$JAVA_SRC_ROOT/'): 561d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui path = key[len('$JAVA_SRC_ROOT/'):] 562d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui items = path.split('/') 563d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui path = os.sep.join(items) 564d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui from_path = self._find_source_file(path) 565d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui to_path = os.path.join(dest_dir, 'java', path) 566d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui is_java = True 567d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui elif key.startswith('/') and os.path.isfile(key): 568d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui path = key 569d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui from_path = path 570d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui to_path = os.path.join(dest_dir, path[1:]) 571cbddd3829b6cf125340d1795f04931a5c8ff83efYabin Cui elif is_windows() and key.find(':\\') != -1 and os.path.isfile(key): 572cbddd3829b6cf125340d1795f04931a5c8ff83efYabin Cui from_path = key 573cbddd3829b6cf125340d1795f04931a5c8ff83efYabin Cui to_path = os.path.join(dest_dir, key.replace(':\\', '\\')) 574d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui else: 575d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui path = key[1:] if key.startswith('/') else key 576d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui # Change path on device to path on host 577d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui path = os.sep.join(path.split('/')) 578d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui from_path = self._find_source_file(path) 579d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui to_path = os.path.join(dest_dir, path) 580d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if from_path is None: 581d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui log_warning("can't find source file for path %s" % key) 582d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui continue 583d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui self._annotate_file(from_path, to_path, self.file_periods[key], is_java) 584d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 585d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 586d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui def _annotate_file(self, from_path, to_path, file_period, is_java): 587d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """Annotate a source file. 588d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 589d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui Annotate a source file in three steps: 590d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 1. In the first line, show periods of this file. 591d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 2. For each function, show periods of this function. 592d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 3. For each line not hitting the same line as functions, show 593d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui line periods. 594d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui """ 595d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui log_info('annotate file %s' % from_path) 596d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui with open(from_path, 'r') as rf: 597d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui lines = rf.readlines() 598d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 599d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui annotates = dict() 600d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for line in file_period.line_dict.keys(): 601d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui annotates[line] = self._get_percentage_str(file_period.line_dict[line], True) 602d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for func_name in file_period.function_dict.keys(): 603d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui func_start_line, period = file_period.function_dict[func_name] 604d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if func_start_line == -1: 605d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui continue 606d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui line = func_start_line - 1 if is_java else func_start_line 607d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui annotates[line] = '[func] ' + self._get_percentage_str(period, True) 608d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui annotates[1] = '[file] ' + self._get_percentage_str(file_period.period, True) 609d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 610d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui max_annotate_cols = 0 611d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for key in annotates.keys(): 612d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui max_annotate_cols = max(max_annotate_cols, len(annotates[key])) 613d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 614d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui empty_annotate = ' ' * (max_annotate_cols + 6) 615d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 616d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui dirname = os.path.dirname(to_path) 617d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if not os.path.isdir(dirname): 618d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui os.makedirs(dirname) 619d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui with open(to_path, 'w') as wf: 620d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui for line in range(1, len(lines) + 1): 621d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui annotate = annotates.get(line) 622d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui if annotate is None: 62306956f3dd31a7e71bac119982a9a7db31259050eYabin Cui if not lines[line-1].strip(): 62406956f3dd31a7e71bac119982a9a7db31259050eYabin Cui annotate = '' 62506956f3dd31a7e71bac119982a9a7db31259050eYabin Cui else: 62606956f3dd31a7e71bac119982a9a7db31259050eYabin Cui annotate = empty_annotate 627d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui else: 628d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui annotate = '/* ' + annotate + ( 629d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui ' ' * (max_annotate_cols - len(annotate))) + ' */' 630d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui wf.write(annotate) 631d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui wf.write(lines[line-1]) 632d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 633b8439265477bac5e32131a757e90abf510a77bf9Yabin Cuidef main(): 634b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui parser = argparse.ArgumentParser(description= 635b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui"""Annotate source files based on profiling data. It reads line information from 636b8439265477bac5e32131a757e90abf510a77bf9Yabin Cuibinary_cache generated by app_profiler.py or binary_cache_builder.py, and 637b8439265477bac5e32131a757e90abf510a77bf9Yabin Cuigenerate annotated source files in annotated_files directory.""") 638b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui parser.add_argument('-i', '--perf_data_list', nargs='+', action='append', help= 639b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui"""The paths of profiling data. Default is perf.data.""") 640b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui parser.add_argument('-s', '--source_dirs', nargs='+', action='append', help= 641b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui"""Directories to find source files.""") 642b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui parser.add_argument('--comm', nargs='+', action='append', help= 643b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui"""Use samples only in threads with selected names.""") 644b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui parser.add_argument('--pid', nargs='+', action='append', help= 645b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui"""Use samples only in processes with selected process ids.""") 646b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui parser.add_argument('--tid', nargs='+', action='append', help= 647b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui"""Use samples only in threads with selected thread ids.""") 648b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui parser.add_argument('--dso', nargs='+', action='append', help= 649b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui"""Use samples only in selected binaries.""") 650b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui parser.add_argument('--addr2line', help= 651b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui"""Set the path of addr2line.""") 652d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui 653d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui args = parser.parse_args() 654b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui config = {} 655b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui config['perf_data_list'] = flatten_arg_list(args.perf_data_list) 656b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui if not config['perf_data_list']: 657b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui config['perf_data_list'].append('perf.data') 658b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui config['source_dirs'] = flatten_arg_list(args.source_dirs) 659b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui config['comm_filters'] = flatten_arg_list(args.comm) 660b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui config['pid_filters'] = flatten_arg_list(args.pid) 661b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui config['tid_filters'] = flatten_arg_list(args.tid) 662b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui config['dso_filters'] = flatten_arg_list(args.dso) 663b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui config['addr2line_path'] = args.addr2line 664b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui 665d09d15b441f8a59cdb4b316db1170811bd384f2fYabin Cui annotator = SourceFileAnnotator(config) 666129b5d2f72fbee901a504d328da5a9037b03a7b6Yabin Cui annotator.annotate() 6676cc52fdf2d2c25f4583eb3b0159f71005966dc79Yabin Cui log_info('annotate finish successfully, please check result in annotated_files/.') 668b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui 669b8439265477bac5e32131a757e90abf510a77bf9Yabin Cuiif __name__ == '__main__': 670b8439265477bac5e32131a757e90abf510a77bf9Yabin Cui main() 671