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