Update NDK prebuilts to build 4937834.

Taken from branch aosp-master.

Bug: none
Test: run test.py on linux/darwin/windows.

Change-Id: I79c41b36b0ad16d5b42b139f53eb19fbdc55fc80
(cherry picked from commit 58814c60c2c8256d88a17a0085a328d82a6247de)
diff --git a/ChangeLog b/ChangeLog
index 19be596..08aa018 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,20 @@
 
+ndk r18
+Improve support of profiling JITed/interpreted Java code on Android >= P:
+  1) Support JITed/interpreted Java code in system wide recording.
+  2) Support dex files extracted to memory.
+  3) Fix some bugs and improve inefficient code.
+Improve record command:
+  1) Add a user space buffer and a high priority record reading thread to reduce sampe lost rate.
+  2) Record full process name instead of only the last 16 bytes.
+Improve report_html.py:
+  1) Generate flamegraphs in Javascript code instead of using inferno, thus
+	   reducing the time used to generate and load report.
+  2) Use bootstrap 4 to format UI.
+  3) Use progressbar to show progress of loading contents.
+  4) Add --binary_filter option to only annotate selected binaries.
+Export tracing data in simpleperf_report_lib.py.
+
 ndk r17
 (release)
 Use new Android unwinder, which can unwind for archs different from build.
diff --git a/NOTICE b/NOTICE
index a1079dd..728392e 100644
--- a/NOTICE
+++ b/NOTICE
@@ -188,6 +188,7 @@
 
    END OF TERMS AND CONDITIONS
 
+------------------------------------------------------------------------
 
 Copyright jQuery Foundation and other contributors, https://jquery.org/
 
@@ -232,3 +233,58 @@
 externally maintained libraries used by this software which have their
 own licenses; we recommend you read them, as their terms may differ from
 the terms above.
+
+-----------------------------------------------------------------------------
+
+The MIT License (MIT)
+
+Copyright (c) 2011-2018 Twitter, Inc.
+Copyright (c) 2011-2018 The Bootstrap Authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+-----------------------------------------------------------------------------
+
+DataTables | MIT license
+
+DataTables is available under the MIT license. In short, this means that you are
+free to use DataTables as you wish, including modifying and redistributing the code,
+as long as the original copyright notice is retained.
+
+The MIT License (MIT)
+
+Copyright (C) 2008-2018, SpryMedia Ltd.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/annotate.py b/annotate.py
old mode 100644
new mode 100755
index 066e14e..fb80bfe
--- a/annotate.py
+++ b/annotate.py
@@ -23,15 +23,14 @@
 import os
 import os.path
 import shutil
-import subprocess
-import sys
 
-from simpleperf_report_lib import *
-from utils import *
+from simpleperf_report_lib import ReportLib
+from utils import log_info, log_warning, log_exit
+from utils import Addr2Nearestline, extant_dir, flatten_arg_list, is_windows, SourceFileSearcher
 
 class SourceLine(object):
-    def __init__(self, file, function, line):
-        self.file = file
+    def __init__(self, file_id, function, line):
+        self.file = file_id
         self.function = function
         self.line = line
 
@@ -48,162 +47,33 @@
         return (self.file, self.line)
 
 
-# TODO: using addr2line can't convert from function_start_address to
-# source_file:line very well for java code. Because in .debug_line section,
-# there is some distance between function_start_address and the address
-# of the first instruction which can be mapped to source line.
 class Addr2Line(object):
-    """collect information of how to map [dso_name,vaddr] to [source_file:line].
+    """collect information of how to map [dso_name, vaddr] to [source_file:line].
     """
-    def __init__(self, ndk_path, symfs_dir=None):
-        self.dso_dict = dict()
-        self.addr2line_path = find_tool_path('addr2line', ndk_path)
-        if self.addr2line_path is None:
-            log_exit("Can't find addr2line. Please set ndk path with --ndk-path option.")
-        self.readelf = ReadElf(ndk_path)
-        self.symfs_dir = symfs_dir
+    def __init__(self, ndk_path, binary_cache_path, source_dirs):
+        self.addr2line = Addr2Nearestline(ndk_path, binary_cache_path, True)
+        self.source_searcher = SourceFileSearcher(source_dirs)
 
-
-    def add_addr(self, dso_name, addr):
-        dso = self.dso_dict.get(dso_name)
-        if dso is None:
-            self.dso_dict[dso_name] = dso = dict()
-        if addr not in dso:
-            dso[addr] = None
-
+    def add_addr(self, dso_path, func_addr, addr):
+        self.addr2line.add_addr(dso_path, func_addr, addr)
 
     def convert_addrs_to_lines(self):
-        # store a list of source files
-        self.file_list = []
-        # map from file to id with file_list[id] == file
-        self.file_dict = {}
-        self.file_list.append('')
-        self.file_dict[''] = 0
+        self.addr2line.convert_addrs_to_lines()
 
-        for dso_name in self.dso_dict.keys():
-            self._convert_addrs_to_lines(dso_name, self.dso_dict[dso_name])
-        self._combine_source_files()
-
-
-    def _convert_addrs_to_lines(self, dso_name, dso):
-        dso_path = self._find_dso_path(dso_name)
-        if dso_path is None:
-            log_warning("can't find dso '%s'" % dso_name)
-            dso.clear()
-            return
-        if '.debug_line' not in self.readelf.get_sections(dso_path):
-            return
-        addrs = sorted(dso.keys())
-        addr_str = []
-        for addr in addrs:
-            addr_str.append('0x%x' % addr)
-        addr_str = '\n'.join(addr_str)
-        subproc = subprocess.Popen([self.addr2line_path, '-e', dso_path, '-aifC'],
-                                   stdin=subprocess.PIPE, stdout=subprocess.PIPE)
-        (stdoutdata, _) = subproc.communicate(str_to_bytes(addr_str))
-        stdoutdata = bytes_to_str(stdoutdata)
-        stdoutdata = stdoutdata.strip().split('\n')
-        if len(stdoutdata) < len(addrs):
-            log_fatal("addr2line didn't output enough lines")
-        addr_pos = 0
-        out_pos = 0
-        while addr_pos < len(addrs) and out_pos < len(stdoutdata):
-            addr_line = stdoutdata[out_pos]
-            out_pos += 1
-            assert addr_line[:2] == "0x"
-            assert out_pos < len(stdoutdata)
-            source_lines = []
-            while out_pos < len(stdoutdata) and stdoutdata[out_pos][:2] != "0x":
-                function = stdoutdata[out_pos]
-                out_pos += 1
-                assert out_pos < len(stdoutdata)
-                # Handle lines like "C:\Users\...\file:32".
-                items = stdoutdata[out_pos].rsplit(':', 1)
-                if len(items) != 2:
-                    continue
-                (file, line) = items
-                line = line.split()[0]  # Remove comments after line number
-                out_pos += 1
-                if '?' in file:
-                    file = 0
-                else:
-                    file = self._get_file_id(file)
-                if '?' in line:
-                    line = 0
-                else:
-                    line = int(line)
-                source_lines.append(SourceLine(file, function, line))
-            dso[addrs[addr_pos]] = source_lines
-            addr_pos += 1
-        assert addr_pos == len(addrs)
-
-
-    def _get_file_id(self, file):
-        id = self.file_dict.get(file)
-        if id is None:
-            id = len(self.file_list)
-            self.file_list.append(file)
-            self.file_dict[file] = id
-        return id
-
-    def _combine_source_files(self):
-        """It is possible that addr2line gives us different names for the same
-           file, like:
-            /usr/local/.../src/main/jni/sudo-game-jni.cpp
-            sudo-game-jni.cpp
-           We'd better combine these two files. We can do it by combining
-           source files with no conflicts in path.
-        """
-        # Collect files having the same filename.
-        filename_dict = dict()
-        for file in self.file_list:
-            index = max(file.rfind('/'), file.rfind(os.sep))
-            filename = file[index+1:]
-            entry = filename_dict.get(filename)
-            if entry is None:
-                filename_dict[filename] = entry = []
-            entry.append(file)
-
-        # Combine files having the same filename and having no conflicts in path.
-        for filename in filename_dict.keys():
-            files = filename_dict[filename]
-            if len(files) == 1:
-                continue
-            for file in files:
-                to_file = file
-                # Test if we can merge files[i] with another file having longer
-                # path.
-                for f in files:
-                    if len(f) > len(to_file) and f.find(file) != -1:
-                        to_file = f
-                if to_file != file:
-                    from_id = self.file_dict[file]
-                    to_id = self.file_dict[to_file]
-                    self.file_list[from_id] = self.file_list[to_id]
-
-
-    def get_sources(self, dso_name, addr):
-        dso = self.dso_dict.get(dso_name)
-        if dso is None:
+    def get_sources(self, dso_path, addr):
+        dso = self.addr2line.get_dso(dso_path)
+        if not dso:
             return []
-        item = dso.get(addr) or []
-        source_lines = []
-        for source in item:
-            source_lines.append(SourceLine(self.file_list[source.file],
-                                           source.function, source.line))
-        return source_lines
-
-
-    def _find_dso_path(self, dso):
-        if dso[0] != '/' or dso == '//anon':
-            return None
-        if self.symfs_dir:
-            dso_path = os.path.join(self.symfs_dir, dso[1:])
-            if os.path.isfile(dso_path):
-                return dso_path
-        if os.path.isfile(dso):
-            return dso
-        return None
+        source = self.addr2line.get_addr_source(dso, addr)
+        if not source:
+            return []
+        result = []
+        for (source_file, source_line, function_name) in source:
+            source_file_path = self.source_searcher.get_real_path(source_file)
+            if not source_file_path:
+                source_file_path = source_file
+            result.append(SourceLine(source_file_path, function_name, source_line))
+        return result
 
 
 class Period(object):
@@ -239,8 +109,8 @@
 
 class FilePeriod(object):
     """Period for each source file"""
-    def __init__(self, file):
-        self.file = file
+    def __init__(self, file_id):
+        self.file = file_id
         self.period = Period()
         # Period for each line in the file.
         self.line_dict = {}
@@ -283,12 +153,6 @@
         kallsyms = 'binary_cache/kallsyms'
         if not os.path.isfile(kallsyms):
             kallsyms = None
-        source_dirs = config['source_dirs']
-        for dir in source_dirs:
-            if not os.path.isdir(dir):
-                log_exit('[source_dirs] "%s" is not a dir' % dir)
-        if not config['source_dirs']:
-            log_exit('Please set source directories.')
 
         # init member variables
         self.config = config
@@ -311,7 +175,11 @@
             shutil.rmtree(output_dir)
         os.makedirs(output_dir)
 
-        self.addr2line = Addr2Line(self.config['ndk_path'], symfs_dir)
+
+        self.addr2line = Addr2Line(self.config['ndk_path'], symfs_dir, config.get('source_dirs'))
+        self.period = 0
+        self.dso_periods = {}
+        self.file_periods = {}
 
 
     def annotate(self):
@@ -319,7 +187,6 @@
         self._convert_addrs_to_lines()
         self._generate_periods()
         self._write_summary()
-        self._collect_source_files()
         self._annotate_files()
 
 
@@ -348,8 +215,10 @@
                     symbols.append(callchain.entries[i].symbol)
                 for symbol in symbols:
                     if self._filter_symbol(symbol):
-                        self.addr2line.add_addr(symbol.dso_name, symbol.vaddr_in_file)
-                        self.addr2line.add_addr(symbol.dso_name, symbol.symbol_addr)
+                        self.addr2line.add_addr(symbol.dso_name, symbol.symbol_addr,
+                                                symbol.vaddr_in_file)
+                        self.addr2line.add_addr(symbol.dso_name, symbol.symbol_addr,
+                                                symbol.symbol_addr)
 
 
     def _filter_sample(self, sample):
@@ -380,9 +249,6 @@
         """read perf.data, collect Period for all types:
             binaries, source files, functions, lines.
         """
-        self.period = 0
-        self.dso_periods = dict()
-        self.file_periods = dict()
         for perf_data in self.config['perf_data_list']:
             lib = ReportLib()
             lib.SetRecordFile(perf_data)
@@ -397,49 +263,52 @@
                     break
                 if not self._filter_sample(sample):
                     continue
-                symbols = []
-                symbols.append(lib.GetSymbolOfCurrentSample())
-                callchain = lib.GetCallChainOfCurrentSample()
-                for i in range(callchain.nr):
-                    symbols.append(callchain.entries[i].symbol)
-                # Each sample has a callchain, but its period is only used once
-                # to add period for each function/source_line/source_file/binary.
-                # For example, if more than one entry in the callchain hits a
-                # function, the event count of that function is only increased once.
-                # Otherwise, we may get periods > 100%.
-                is_sample_used = False
-                used_dso_dict = dict()
-                used_file_dict = dict()
-                used_function_dict = dict()
-                used_line_dict = dict()
-                period = Period(sample.period, sample.period)
-                for i in range(len(symbols)):
-                    symbol = symbols[i]
-                    if i == 1:
-                        period = Period(0, sample.period)
-                    if not self._filter_symbol(symbol):
-                        continue
-                    is_sample_used = True
-                    # Add period to dso.
-                    self._add_dso_period(symbol.dso_name, period, used_dso_dict)
-                    # Add period to source file.
-                    sources = self.addr2line.get_sources(symbol.dso_name, symbol.vaddr_in_file)
-                    for source in sources:
-                        if source.file:
-                            self._add_file_period(source, period, used_file_dict)
-                            # Add period to line.
-                            if source.line:
-                                self._add_line_period(source, period, used_line_dict)
-                    # Add period to function.
-                    sources = self.addr2line.get_sources(symbol.dso_name, symbol.symbol_addr)
-                    for source in sources:
-                        if source.file:
-                            self._add_file_period(source, period, used_file_dict)
-                            if source.function:
-                                self._add_function_period(source, period, used_function_dict)
+                self._generate_periods_for_sample(lib, sample)
 
-                if is_sample_used:
-                    self.period += sample.period
+
+    def _generate_periods_for_sample(self, lib, sample):
+        symbols = []
+        symbols.append(lib.GetSymbolOfCurrentSample())
+        callchain = lib.GetCallChainOfCurrentSample()
+        for i in range(callchain.nr):
+            symbols.append(callchain.entries[i].symbol)
+        # Each sample has a callchain, but its period is only used once
+        # to add period for each function/source_line/source_file/binary.
+        # For example, if more than one entry in the callchain hits a
+        # function, the event count of that function is only increased once.
+        # Otherwise, we may get periods > 100%.
+        is_sample_used = False
+        used_dso_dict = {}
+        used_file_dict = {}
+        used_function_dict = {}
+        used_line_dict = {}
+        period = Period(sample.period, sample.period)
+        for j, symbol in enumerate(symbols):
+            if j == 1:
+                period = Period(0, sample.period)
+            if not self._filter_symbol(symbol):
+                continue
+            is_sample_used = True
+            # Add period to dso.
+            self._add_dso_period(symbol.dso_name, period, used_dso_dict)
+            # Add period to source file.
+            sources = self.addr2line.get_sources(symbol.dso_name, symbol.vaddr_in_file)
+            for source in sources:
+                if source.file:
+                    self._add_file_period(source, period, used_file_dict)
+                    # Add period to line.
+                    if source.line:
+                        self._add_line_period(source, period, used_line_dict)
+            # Add period to function.
+            sources = self.addr2line.get_sources(symbol.dso_name, symbol.symbol_addr)
+            for source in sources:
+                if source.file:
+                    self._add_file_period(source, period, used_file_dict)
+                    if source.function:
+                        self._add_function_period(source, period, used_function_dict)
+
+        if is_sample_used:
+            self.period += sample.period
 
 
     def _add_dso_period(self, dso_name, period, used_dso_dict):
@@ -520,71 +389,24 @@
         return (acc_p, p)
 
 
-    def _collect_source_files(self):
-        self.source_file_dict = dict()
-        source_file_suffix = ['h', 'c', 'cpp', 'cc', 'java', 'kt']
-        for source_dir in self.config['source_dirs']:
-            for root, _, files in os.walk(source_dir):
-                for file in files:
-                    if file[file.rfind('.')+1:] in source_file_suffix:
-                        entry = self.source_file_dict.get(file)
-                        if entry is None:
-                            entry = self.source_file_dict[file] = []
-                        entry.append(os.path.join(root, file))
-
-
-    def _find_source_file(self, file):
-        filename = file[file.rfind(os.sep)+1:]
-        source_files = self.source_file_dict.get(filename)
-        if source_files is None:
-            return None
-        best_path_count = 0
-        best_path = None
-        best_suffix_len = 0
-        for path in source_files:
-            suffix_len = len(os.path.commonprefix((path[::-1], file[::-1])))
-            if suffix_len > best_suffix_len:
-                best_suffix_len = suffix_len
-                best_path = path
-                best_path_count = 1
-            elif suffix_len == best_suffix_len:
-                best_path_count += 1
-        if best_path_count > 1:
-            log_warning('multiple source for %s, select %s' % (file, best_path))
-        return best_path
-
-
     def _annotate_files(self):
         """Annotate Source files: add acc_period/period for each source file.
            1. Annotate java source files, which have $JAVA_SRC_ROOT prefix.
            2. Annotate c++ source files.
         """
         dest_dir = self.config['annotate_dest_dir']
-        for key in self.file_periods.keys():
-            is_java = False
-            if key.startswith('$JAVA_SRC_ROOT/'):
-                path = key[len('$JAVA_SRC_ROOT/'):]
-                items = path.split('/')
-                path = os.sep.join(items)
-                from_path = self._find_source_file(path)
-                to_path = os.path.join(dest_dir, 'java', path)
-                is_java = True
-            elif key.startswith('/') and os.path.isfile(key):
-                path = key
-                from_path = path
-                to_path = os.path.join(dest_dir, path[1:])
-            elif is_windows() and ':\\' in key and os.path.isfile(key):
-                from_path = key
-                to_path = os.path.join(dest_dir, key.replace(':\\', '\\'))
-            else:
-                path = key[1:] if key.startswith('/') else key
-                # Change path on device to path on host
-                path = os.sep.join(path.split('/'))
-                from_path = self._find_source_file(path)
-                to_path = os.path.join(dest_dir, path)
-            if from_path is None:
-                log_warning("can't find source file for path %s" % key)
+        for key in self.file_periods:
+            from_path = key
+            if not os.path.isfile(from_path):
+                log_warning("can't find source file for path %s" % from_path)
                 continue
+            if from_path.startswith('/'):
+                to_path = os.path.join(dest_dir, from_path[1:])
+            elif is_windows() and ':\\' in from_path:
+                to_path = os.path.join(dest_dir, from_path.replace(':\\', os.sep))
+            else:
+                to_path = os.path.join(dest_dir, from_path)
+            is_java = from_path.endswith('.java')
             self._annotate_file(from_path, to_path, self.file_periods[key], is_java)
 
 
@@ -601,7 +423,7 @@
         with open(from_path, 'r') as rf:
             lines = rf.readlines()
 
-        annotates = dict()
+        annotates = {}
         for line in file_period.line_dict.keys():
             annotates[line] = self._get_percentage_str(file_period.line_dict[line], True)
         for func_name in file_period.function_dict.keys():
@@ -613,7 +435,7 @@
         annotates[1] = '[file] ' + self._get_percentage_str(file_period.period, True)
 
         max_annotate_cols = 0
-        for key in annotates.keys():
+        for key in annotates:
             max_annotate_cols = max(max_annotate_cols, len(annotates[key]))
 
         empty_annotate = ' ' * (max_annotate_cols + 6)
@@ -636,22 +458,22 @@
                 wf.write(lines[line-1])
 
 def main():
-    parser = argparse.ArgumentParser(description=
-"""Annotate source files based on profiling data. It reads line information from
-binary_cache generated by app_profiler.py or binary_cache_builder.py, and
-generate annotated source files in annotated_files directory.""")
-    parser.add_argument('-i', '--perf_data_list', nargs='+', action='append', help=
-"""The paths of profiling data. Default is perf.data.""")
-    parser.add_argument('-s', '--source_dirs', nargs='+', action='append', help=
-"""Directories to find source files.""")
-    parser.add_argument('--comm', nargs='+', action='append', help=
-"""Use samples only in threads with selected names.""")
-    parser.add_argument('--pid', nargs='+', action='append', help=
-"""Use samples only in processes with selected process ids.""")
-    parser.add_argument('--tid', nargs='+', action='append', help=
-"""Use samples only in threads with selected thread ids.""")
-    parser.add_argument('--dso', nargs='+', action='append', help=
-"""Use samples only in selected binaries.""")
+    parser = argparse.ArgumentParser(description="""
+        Annotate source files based on profiling data. It reads line information from binary_cache
+        generated by app_profiler.py or binary_cache_builder.py, and generate annotated source
+        files in annotated_files directory.""")
+    parser.add_argument('-i', '--perf_data_list', nargs='+', action='append', help="""
+        The paths of profiling data. Default is perf.data.""")
+    parser.add_argument('-s', '--source_dirs', type=extant_dir, nargs='+', action='append', help="""
+        Directories to find source files.""")
+    parser.add_argument('--comm', nargs='+', action='append', help="""
+        Use samples only in threads with selected names.""")
+    parser.add_argument('--pid', nargs='+', action='append', help="""
+        Use samples only in processes with selected process ids.""")
+    parser.add_argument('--tid', nargs='+', action='append', help="""
+        Use samples only in threads with selected thread ids.""")
+    parser.add_argument('--dso', nargs='+', action='append', help="""
+        Use samples only in selected binaries.""")
     parser.add_argument('--ndk_path', type=extant_dir, help='Set the path of a ndk release.')
 
     args = parser.parse_args()
diff --git a/app_profiler.py b/app_profiler.py
old mode 100644
new mode 100755
index 4c4a188..b0e7a92
--- a/app_profiler.py
+++ b/app_profiler.py
@@ -241,7 +241,7 @@
             if self.args.disable_adb_root:
                 binary_cache_args += ['--disable_adb_root']
             if self.args.ndk_path:
-                binary_cache_args += ['--ndk-path', self.args.ndk_path]
+                binary_cache_args += ['--ndk_path', self.args.ndk_path]
             subprocess.check_call(binary_cache_args)
 
 
diff --git a/bin/android/arm/simpleperf b/bin/android/arm/simpleperf
index b41318b..e277334 100755
--- a/bin/android/arm/simpleperf
+++ b/bin/android/arm/simpleperf
Binary files differ
diff --git a/bin/android/arm64/simpleperf b/bin/android/arm64/simpleperf
index 4df1f31..3c8a913 100755
--- a/bin/android/arm64/simpleperf
+++ b/bin/android/arm64/simpleperf
Binary files differ
diff --git a/bin/android/x86/simpleperf b/bin/android/x86/simpleperf
index 2c6edab..db4b420 100755
--- a/bin/android/x86/simpleperf
+++ b/bin/android/x86/simpleperf
Binary files differ
diff --git a/bin/android/x86_64/simpleperf b/bin/android/x86_64/simpleperf
index bd24729..dca0e03 100755
--- a/bin/android/x86_64/simpleperf
+++ b/bin/android/x86_64/simpleperf
Binary files differ
diff --git a/bin/darwin/x86/libsimpleperf_report.dylib b/bin/darwin/x86/libsimpleperf_report.dylib
index bf28205..728a61b 100755
--- a/bin/darwin/x86/libsimpleperf_report.dylib
+++ b/bin/darwin/x86/libsimpleperf_report.dylib
Binary files differ
diff --git a/bin/darwin/x86/simpleperf b/bin/darwin/x86/simpleperf
index f0cdf21..d5da380 100755
--- a/bin/darwin/x86/simpleperf
+++ b/bin/darwin/x86/simpleperf
Binary files differ
diff --git a/bin/darwin/x86_64/libsimpleperf_report.dylib b/bin/darwin/x86_64/libsimpleperf_report.dylib
index 8625e0d..12d9a5f 100755
--- a/bin/darwin/x86_64/libsimpleperf_report.dylib
+++ b/bin/darwin/x86_64/libsimpleperf_report.dylib
Binary files differ
diff --git a/bin/darwin/x86_64/simpleperf b/bin/darwin/x86_64/simpleperf
index 1dad615..7879260 100755
--- a/bin/darwin/x86_64/simpleperf
+++ b/bin/darwin/x86_64/simpleperf
Binary files differ
diff --git a/bin/linux/x86/libsimpleperf_report.so b/bin/linux/x86/libsimpleperf_report.so
index ebf36b3..f15f841 100755
--- a/bin/linux/x86/libsimpleperf_report.so
+++ b/bin/linux/x86/libsimpleperf_report.so
Binary files differ
diff --git a/bin/linux/x86/simpleperf b/bin/linux/x86/simpleperf
index 3ce5fed..31f288e 100755
--- a/bin/linux/x86/simpleperf
+++ b/bin/linux/x86/simpleperf
Binary files differ
diff --git a/bin/linux/x86_64/libsimpleperf_report.so b/bin/linux/x86_64/libsimpleperf_report.so
index db141fb..4323d01 100755
--- a/bin/linux/x86_64/libsimpleperf_report.so
+++ b/bin/linux/x86_64/libsimpleperf_report.so
Binary files differ
diff --git a/bin/linux/x86_64/simpleperf b/bin/linux/x86_64/simpleperf
index 1f532c9..46e763e 100755
--- a/bin/linux/x86_64/simpleperf
+++ b/bin/linux/x86_64/simpleperf
Binary files differ
diff --git a/bin/windows/x86/libsimpleperf_report.dll b/bin/windows/x86/libsimpleperf_report.dll
index 7cf8ec9..eea692a 100755
--- a/bin/windows/x86/libsimpleperf_report.dll
+++ b/bin/windows/x86/libsimpleperf_report.dll
Binary files differ
diff --git a/bin/windows/x86/libwinpthread-1.dll b/bin/windows/x86/libwinpthread-1.dll
index 3aa88d7..5979688 100755
--- a/bin/windows/x86/libwinpthread-1.dll
+++ b/bin/windows/x86/libwinpthread-1.dll
Binary files differ
diff --git a/bin/windows/x86/simpleperf.exe b/bin/windows/x86/simpleperf.exe
index 9e1d378..47b84f0 100755
--- a/bin/windows/x86/simpleperf.exe
+++ b/bin/windows/x86/simpleperf.exe
Binary files differ
diff --git a/bin/windows/x86_64/libsimpleperf_report.dll b/bin/windows/x86_64/libsimpleperf_report.dll
index 23b289a..0c3371b 100755
--- a/bin/windows/x86_64/libsimpleperf_report.dll
+++ b/bin/windows/x86_64/libsimpleperf_report.dll
Binary files differ
diff --git a/bin/windows/x86_64/libwinpthread-1.dll b/bin/windows/x86_64/libwinpthread-1.dll
index b2fd346..755f7a8 100755
--- a/bin/windows/x86_64/libwinpthread-1.dll
+++ b/bin/windows/x86_64/libwinpthread-1.dll
Binary files differ
diff --git a/bin/windows/x86_64/simpleperf.exe b/bin/windows/x86_64/simpleperf.exe
index 9e1d378..47b84f0 100755
--- a/bin/windows/x86_64/simpleperf.exe
+++ b/bin/windows/x86_64/simpleperf.exe
Binary files differ
diff --git a/binary_cache_builder.py b/binary_cache_builder.py
old mode 100644
new mode 100755
index deb312a..b5a53da
--- a/binary_cache_builder.py
+++ b/binary_cache_builder.py
@@ -23,14 +23,10 @@
 import argparse
 import os
 import os.path
-import re
 import shutil
-import subprocess
-import sys
-import time
 
-from simpleperf_report_lib import *
-from utils import *
+from simpleperf_report_lib import ReportLib
+from utils import AdbHelper, flatten_arg_list, log_info, log_warning, log_exit, ReadElf
 
 
 class BinaryCacheBuilder(object):
@@ -53,6 +49,7 @@
         self.binary_cache_dir = 'binary_cache'
         if not os.path.isdir(self.binary_cache_dir):
             os.makedirs(self.binary_cache_dir)
+        self.binaries = {}
 
 
     def build_binary_cache(self):
@@ -65,7 +62,7 @@
     def _collect_used_binaries(self):
         """read perf.data, collect all used binaries and their build id (if available)."""
         # A dict mapping from binary name to build_id
-        binaries = dict()
+        binaries = {}
         lib = ReportLib()
         lib.SetRecordFile(self.perf_data_path)
         lib.SetLogSeverity('error')
@@ -99,7 +96,7 @@
         # and same build_id.
 
         # Map from filename to binary paths.
-        filename_dict = dict()
+        filename_dict = {}
         for binary in self.binaries:
             index = binary.rfind('/')
             filename = binary[index+1:]
@@ -111,17 +108,19 @@
         # Walk through all files in symfs_dirs, and copy matching files to build_cache.
         for symfs_dir in self.symfs_dirs:
             for root, _, files in os.walk(symfs_dir):
-                for file in files:
-                    paths = filename_dict.get(file)
-                    if paths is not None:
-                        build_id = self._read_build_id(os.path.join(root, file))
-                        if not build_id:
-                            continue
-                        for binary in paths:
-                            expected_build_id = self.binaries.get(binary)
-                            if expected_build_id == build_id:
-                                self._copy_to_binary_cache(os.path.join(root, file),
-                                                           expected_build_id, binary)
+                for filename in files:
+                    paths = filename_dict.get(filename)
+                    if not paths:
+                        continue
+                    build_id = self._read_build_id(os.path.join(root, filename))
+                    if not build_id:
+                        continue
+                    for binary in paths:
+                        expected_build_id = self.binaries.get(binary)
+                        if expected_build_id == build_id:
+                            self._copy_to_binary_cache(os.path.join(root, filename),
+                                                       expected_build_id, binary)
+                            break
 
 
     def _copy_to_binary_cache(self, from_path, expected_build_id, target_file):
@@ -129,10 +128,8 @@
             target_file = target_file[1:]
         target_file = target_file.replace('/', os.sep)
         target_file = os.path.join(self.binary_cache_dir, target_file)
-        if (os.path.isfile(target_file) and self._read_build_id(target_file) == expected_build_id
-            and self._file_has_symbol_table(target_file)):
-            # The existing file in binary_cache can provide more information, so no
-            # need to copy.
+        if not self._need_to_copy(target_file, expected_build_id):
+            # The existing file in binary_cache can provide more information, so no need to copy.
             return
         target_dir = os.path.dirname(target_file)
         if not os.path.isdir(target_dir):
@@ -141,6 +138,16 @@
         shutil.copy(from_path, target_file)
 
 
+    def _need_to_copy(self, target_file, expected_build_id):
+        if not os.path.isfile(target_file):
+            return True
+        if self._read_build_id(target_file) != expected_build_id:
+            return True
+        if not self._file_has_symbol_table(target_file):
+            return True
+        return False
+
+
     def _pull_binaries_from_device(self):
         """pull binaries needed in perf.data to binary_cache."""
         for binary in self.binaries:
@@ -176,14 +183,14 @@
             log_info('use current file in binary_cache: %s' % binary_cache_file)
 
 
-    def _read_build_id(self, file):
+    def _read_build_id(self, file_path):
         """read build id of a binary on host."""
-        return self.readelf.get_build_id(file)
+        return self.readelf.get_build_id(file_path)
 
 
-    def _file_has_symbol_table(self, file):
+    def _file_has_symbol_table(self, file_path):
         """Test if an elf file has symbol table section."""
-        return '.symtab' in self.readelf.get_sections(file)
+        return '.symtab' in self.readelf.get_sections(file_path)
 
 
     def _pull_file_from_device(self, device_path, host_path):
@@ -193,7 +200,7 @@
         # Instead, we can first copy the file to /data/local/tmp, then pull it.
         filename = device_path[device_path.rfind('/')+1:]
         if (self.adb.run(['shell', 'cp', device_path, '/data/local/tmp']) and
-            self.adb.run(['pull', '/data/local/tmp/' + filename, host_path])):
+                self.adb.run(['pull', '/data/local/tmp/' + filename, host_path])):
             self.adb.run(['shell', 'rm', '/data/local/tmp/' + filename])
             return True
         log_warning('failed to pull %s from device' % device_path)
@@ -201,24 +208,23 @@
 
 
     def _pull_kernel_symbols(self):
-        file = os.path.join(self.binary_cache_dir, 'kallsyms')
-        if os.path.isfile(file):
-            os.remove(file)
+        file_path = os.path.join(self.binary_cache_dir, 'kallsyms')
+        if os.path.isfile(file_path):
+            os.remove(file_path)
         if self.adb.switch_to_root():
             self.adb.run(['shell', '"echo 0 >/proc/sys/kernel/kptr_restrict"'])
-            self.adb.run(['pull', '/proc/kallsyms', file])
+            self.adb.run(['pull', '/proc/kallsyms', file_path])
 
 
 def main():
-    parser = argparse.ArgumentParser(description=
-"""Pull binaries needed by perf.data from device to binary_cache directory.""")
-    parser.add_argument('-i', '--perf_data_path', default='perf.data', help=
-"""The path of profiling data.""")
-    parser.add_argument('-lib', '--native_lib_dir', nargs='+', help=
-"""Path to find debug version of native shared libraries used in the app.""",
-                        action='append')
-    parser.add_argument('--disable_adb_root', action='store_true', help=
-"""Force adb to run in non root mode.""")
+    parser = argparse.ArgumentParser(description="""
+        Pull binaries needed by perf.data from device to binary_cache directory.""")
+    parser.add_argument('-i', '--perf_data_path', default='perf.data', help="""
+        The path of profiling data.""")
+    parser.add_argument('-lib', '--native_lib_dir', nargs='+', help="""
+        Path to find debug version of native shared libraries used in the app.""", action='append')
+    parser.add_argument('--disable_adb_root', action='store_true', help="""
+        Force adb to run in non root mode.""")
     parser.add_argument('--ndk_path', nargs=1, help='Find tools in the ndk path.')
     args = parser.parse_args()
     config = {}
@@ -232,4 +238,4 @@
 
 
 if __name__ == '__main__':
-    main()
\ No newline at end of file
+    main()
diff --git a/debug_unwind_reporter.py b/debug_unwind_reporter.py
old mode 100644
new mode 100755
index 56b5176..72853e9
--- a/debug_unwind_reporter.py
+++ b/debug_unwind_reporter.py
@@ -44,7 +44,8 @@
 import re
 import subprocess
 
-from utils import *
+from utils import log_exit, log_fatal
+from utils import get_host_binary_path
 
 
 class MapEntry(object):
@@ -122,7 +123,7 @@
 
 class UnwindingMemConsumption(object):
 
-    def __init___(self):
+    def __init__(self):
         self.before_unwinding = None
         self.after_unwinding = None
 
@@ -341,13 +342,14 @@
         elif items[0] == 'callchain:':
             in_callchain = True
         elif in_callchain:
-            # "dalvik-jit-code-cache (deleted)[+346c] (/dev/ashmem/dalvik-jit-code-cache (deleted)[+346c])"
+            # "dalvik-jit-code-cache (deleted)[+346c] (/dev/ashmem/dalvik-jit-code-cache
+            #  (deleted)[+346c])"
             if re.search(r'\)\[\+\w+\]\)$', line):
                 break_pos = line.rfind('(', 0, line.rfind('('))
             else:
                 break_pos = line.rfind('(')
             if break_pos > 0:
-                m = re.match('(.+)\[\+(\w+)\]\)', line[break_pos + 1:])
+                m = re.match(r'(.*)\[\+(\w+)\]\)', line[break_pos + 1:])
                 if m:
                     function_names.append(line[:break_pos].strip())
                     filenames.append(m.group(1))
@@ -387,7 +389,7 @@
     lines = stdoutdata.split('\n')
     i = 0
     while i < len(lines):
-        if lines[i].startswith('record mmap:'):
+        if lines[i].startswith('record mmap:') or lines[i].startswith('record mmap2:'):
             i += 1
             pid = None
             start = None
@@ -400,7 +402,7 @@
                         pid = int(m.group(1))
                         start = int(m.group(2), 16)
                         end = start + int(m.group(3), 16)
-                elif lines[i].startswith('  pgoff'):
+                elif 'filename' in lines[i]:
                     pos = lines[i].find('filename') + len('filename')
                     filename = lines[i][pos:].strip()
                 i += 1
diff --git a/doc/README.md b/doc/README.md
index a16bd92..791fd24 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -7,8 +7,6 @@
 
 Simpleperf is part of the Android Open Source Project. The source code is [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/).
 The latest document is [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/README.md).
-Bugs and feature requests can be submitted at http://github.com/android-ndk/ndk/issues.
-
 
 ## Table of Contents
 
@@ -63,6 +61,7 @@
     - [Why we suggest profiling on android >= N devices](#why-we-suggest-profiling-on-android-n-devices)
     - [Suggestions about recording call graphs](#suggestions-about-recording-call-graphs)
     - [How to solve missing symbols in report](#how-to-solve-missing-symbols-in-report)
+- [Bugs and contribution](#bugs-and-contribution)
 
 ## Introduction
 
@@ -171,8 +170,8 @@
 
 For the release build type, Android studio sets android::debuggable="false" in AndroidManifest.xml,
 disables JNI checks and optimizes C/C++ code. However, security restrictions mean that only apps
-with android::debuggable set to true can be profiled. So simpleperf can only profile release build
-in either of below two situations:
+with android::debuggable set to true can be profiled. So simpleperf can only profile a release
+build under these two circumstances:
 If you are on a rooted device, you can profile any app.
 
 If you are on Android >= O, we can use [wrap.sh](#https://developer.android.com/ndk/guides/wrap-script.html)
@@ -184,7 +183,7 @@
 ```
 
 Step 2: Add wrap.sh in lib/`arch` directories. wrap.sh runs the app without passing any debug flags
-to ART, so the app runs as a release app. wrap.sh can be done by adding below scripts in
+to ART, so the app runs as a release app. wrap.sh can be done by adding the script below in
 app/build.gradle.
 ```
 android {
@@ -226,7 +225,7 @@
 4. If you want to profile Java code:
 
 On Android >= P, simpleperf supports profiling Java code, no matter whether it is executed by
-interpreter, or JITed, or compiled into native instructions. So you don't need to do anything.
+the interpreter, or JITed, or compiled into native instructions. So you don't need to do anything.
 
 On Android O, simpleperf supports profiling Java code which is compiled into native instructions,
 and it also needs wrap.sh to use the compiled Java code. To compile Java code, we can pass
@@ -981,6 +980,9 @@
 # Record both on CPU time and off CPU time.
 $ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative \
     -r "-e task-clock -g -f 1000 --duration 10 --trace-offcpu"
+
+# Save profiling data in a custom file (like perf_custom.data) instead of perf.data.
+$ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative -o perf_custom.data
 ```
 
 #### Profile from launch of an application
@@ -998,8 +1000,8 @@
 # Start the app manually or using the `am` command.
 ```
 
-To make it convenient to use, app_profiler.py supports using -a option to start an Activity after
-starting recording.
+To make it convenient to use, app_profiler.py supports using the -a option to start an Activity
+after recording has started.
 
 ```sh
 $ python app_profiler.py -p com.example.simpleperf.simpleperfexamplewithnative -a .MainActivity
@@ -1032,7 +1034,7 @@
 By default, app_profiler.py builds the binary_cache directory after recording. But we can also
 build binary_cache for existing profiling data files using binary_cache_builder.py. It is useful
 when you record profiling data using `simpleperf record` directly, to do system wide profiling or
-record without USB cable connected.
+record without the USB cable connected.
 
 binary_cache_builder.py can either pull binaries from an Android device, or find binaries in
 directories on the host (via -lib).
@@ -1241,3 +1243,20 @@
 $ python report_html.py
 ```
 
+## Bugs and contribution
+
+Bugs and feature requests can be submitted at http://github.com/android-ndk/ndk/issues.
+Patches can be uploaded to android-review.googlesource.com as [here](https://source.android.com/setup/contribute/),
+or sent to email addresses listed [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/OWNERS).
+
+If you want to compile simpleperf C++ source code, follow below steps:
+1. Download AOSP master branch as [here](https://source.android.com/setup/build/requirements).
+2. Build simpleperf.
+```sh
+$ . build/envsetup.sh
+$ lunch aosp_arm64-userdebug
+$ mmma system/extras/simpleperf -j30
+```
+
+If built successfully, out/target/product/generic_arm64/system/xbin/simpleperf is for ARM64, and
+out/target/product/generic_arm64/system/xbin/simpleperf32 is for ARM.
diff --git a/inferno.bat b/inferno.bat
old mode 100644
new mode 100755
diff --git a/inferno/data_types.py b/inferno/data_types.py
index 17af700..deb9f51 100644
--- a/inferno/data_types.py
+++ b/inferno/data_types.py
@@ -15,14 +15,14 @@
 #
 
 
-class CallSite:
+class CallSite(object):
 
     def __init__(self, method, dso):
         self.method = method
         self.dso = dso
 
 
-class Thread:
+class Thread(object):
 
     def __init__(self, tid, pid):
         self.tid = tid
@@ -48,7 +48,7 @@
         self.flamegraph.add_callchain(chain, sample.period)
 
 
-class Process:
+class Process(object):
 
     def __init__(self, name, pid):
         self.name = name
@@ -77,7 +77,7 @@
         self.num_events += sample.period
 
 
-class FlameGraphCallSite:
+class FlameGraphCallSite(object):
 
     callsite_counter = 0
     @classmethod
@@ -85,7 +85,7 @@
         cls.callsite_counter += 1
         return cls.callsite_counter
 
-    def __init__(self, method, dso, id):
+    def __init__(self, method, dso, callsite_id):
         # map from (dso, method) to FlameGraphCallSite. Used to speed up add_callchain().
         self.child_dict = {}
         self.children = []
@@ -93,7 +93,7 @@
         self.dso = dso
         self.num_events = 0
         self.offset = 0  # Offset allows position nodes in different branches.
-        self.id = id
+        self.id = callsite_id
 
     def weight(self):
         return float(self.num_events)
@@ -102,15 +102,15 @@
         self.num_events += num_events
         current = self
         for callsite in chain:
-            current = current._get_child(callsite)
+            current = current.get_child(callsite)
             current.num_events += num_events
 
-    def _get_child(self, callsite):
+    def get_child(self, callsite):
         key = (callsite.dso, callsite.method)
         child = self.child_dict.get(key)
         if child is None:
             child = self.child_dict[key] = FlameGraphCallSite(callsite.method, callsite.dso,
-                                               self._get_next_callsite_id())
+                                                              self._get_next_callsite_id())
         return child
 
     def trim_callchain(self, min_num_events):
diff --git a/inferno/inferno.py b/inferno/inferno.py
old mode 100644
new mode 100755
index e0035d8..0bc0665
--- a/inferno/inferno.py
+++ b/inferno/inferno.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
@@ -35,18 +36,19 @@
 import subprocess
 import sys
 
-scripts_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-sys.path.append(scripts_path)
+# pylint: disable=wrong-import-position
+SCRIPTS_PATH = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+sys.path.append(SCRIPTS_PATH)
 from simpleperf_report_lib import ReportLib
 from utils import log_exit, log_info, AdbHelper, open_report_in_browser
 
-from data_types import *
-from svg_renderer import *
+from data_types import Process
+from svg_renderer import get_proper_scaled_time_string, render_svg
 
 
 def collect_data(args):
     """ Run app_profiler.py to generate record file. """
-    app_profiler_args = [sys.executable, os.path.join(scripts_path, "app_profiler.py"), "-nb"]
+    app_profiler_args = [sys.executable, os.path.join(SCRIPTS_PATH, "app_profiler.py"), "-nb"]
     if args.app:
         app_profiler_args += ["-p", args.app]
     elif args.native_program:
@@ -106,10 +108,10 @@
     process.cmd = lib.GetRecordCmd()
     product_props = lib.MetaInfo().get("product_props")
     if product_props:
-        tuple = product_props.split(':')
-        process.props['ro.product.manufacturer'] = tuple[0]
-        process.props['ro.product.model'] = tuple[1]
-        process.props['ro.product.name'] = tuple[2]
+        manufacturer, model, name = product_props.split(':')
+        process.props['ro.product.manufacturer'] = manufacturer
+        process.props['ro.product.model'] = model
+        process.props['ro.product.name'] = name
     if lib.MetaInfo().get('trace_offcpu') == 'true':
         process.props['trace_offcpu'] = True
         if args.one_flamegraph:
@@ -163,7 +165,7 @@
     if not args.embedded_flamegraph:
         f.write("<html><body>")
     f.write("<div id='flamegraph_id' style='font-family: Monospace; %s'>" % (
-            "display: none;" if args.embedded_flamegraph else ""))
+        "display: none;" if args.embedded_flamegraph else ""))
     f.write("""<style type="text/css"> .s { stroke:black; stroke-width:0.5; cursor:pointer;}
             </style>""")
     f.write('<style type="text/css"> .t:hover { cursor:pointer; } </style>')
@@ -171,29 +173,29 @@
     f.write(get_local_asset_content("inferno.b64"))
     f.write('"/>')
     process_entry = ("Process : %s (%d)<br/>" % (process.name, process.pid)) if process.pid else ""
+    thread_entry = '' if args.one_flamegraph else ('Threads: %d<br/>' % len(process.threads))
     if process.props['trace_offcpu']:
         event_entry = 'Total time: %s<br/>' % get_proper_scaled_time_string(process.num_events)
     else:
         event_entry = 'Event count: %s<br/>' % ("{:,}".format(process.num_events))
     # TODO: collect capture duration info from perf.data.
     duration_entry = ("Duration: %s seconds<br/>" % args.capture_duration
-                      ) if args.capture_duration else ""
+                     ) if args.capture_duration else ""
     f.write("""<div style='display:inline-block;'>
                   <font size='8'>
                   Inferno Flamegraph Report%s</font><br/><br/>
                   %s
                   Date&nbsp;&nbsp;&nbsp;&nbsp;: %s<br/>
-                  Threads : %d <br/>
+                  %s
                   Samples : %d<br/>
                   %s
-                  %s""" % (
-        (': ' + args.title) if args.title else '',
-        process_entry,
-        datetime.datetime.now().strftime("%Y-%m-%d (%A) %H:%M:%S"),
-        len(process.threads),
-        process.num_samples,
-        event_entry,
-        duration_entry))
+                  %s""" % ((': ' + args.title) if args.title else '',
+                           process_entry,
+                           datetime.datetime.now().strftime("%Y-%m-%d (%A) %H:%M:%S"),
+                           thread_entry,
+                           process.num_samples,
+                           event_entry,
+                           duration_entry))
     if 'ro.product.model' in process.props:
         f.write(
             "Machine : %s (%s) by %s<br/>" %
@@ -211,9 +213,11 @@
 
     # Sort threads by the event count in a thread.
     for thread in sorted(process.threads.values(), key=lambda x: x.num_events, reverse=True):
-        f.write("<br/><br/><b>Thread %d (%s) (%d samples):</b><br/>\n\n\n\n" % (
-                thread.tid, thread.name, thread.num_samples))
-        renderSVG(process, thread.flamegraph, f, args.color)
+        thread_name = 'One flamegraph' if args.one_flamegraph else ('Thread %d (%s)' %
+                                                                    (thread.tid, thread.name))
+        f.write("<br/><br/><b>%s (%d samples):</b><br/>\n\n\n\n" %
+                (thread_name, thread.num_samples))
+        render_svg(process, thread.flamegraph, f, args.color)
 
     f.write("</div>")
     if not args.embedded_flamegraph:
@@ -224,7 +228,7 @@
 
 def generate_threads_offsets(process):
     for thread in process.threads.values():
-       thread.flamegraph.generate_offset(0)
+        thread.flamegraph.generate_offset(0)
 
 
 def collect_machine_info(process):
@@ -305,7 +309,7 @@
         if result:
             try:
                 process.pid = int(output)
-            except:
+            except ValueError:
                 process.pid = 0
         collect_machine_info(process)
     else:
@@ -313,7 +317,7 @@
 
     sample_filter_fn = None
     if args.one_flamegraph:
-        def filter_fn(sample, symbol, callchain):
+        def filter_fn(sample, _symbol, _callchain):
             sample.pid = sample.tid = process.pid
             return True
         sample_filter_fn = filter_fn
diff --git a/inferno/svg_renderer.py b/inferno/svg_renderer.py
index fd0096d..0627bc6 100644
--- a/inferno/svg_renderer.py
+++ b/inferno/svg_renderer.py
@@ -34,21 +34,21 @@
     return hash(string) / float(sys.maxsize)
 
 
-def getLegacyColor(method):
+def get_legacy_color(method):
     r = 175 + int(50 * hash_to_float(reversed(method)))
     g = 60 + int(180 * hash_to_float(method))
     b = 60 + int(55 * hash_to_float(reversed(method)))
     return (r, g, b)
 
 
-def getDSOColor(method):
+def get_dso_color(method):
     r = 170 + int(80 * hash_to_float(reversed(method)))
     g = 180 + int(70 * hash_to_float((method)))
     b = 170 + int(80 * hash_to_float(reversed(method)))
     return (r, g, b)
 
 
-def getHeatColor(callsite, total_weight):
+def get_heat_color(callsite, total_weight):
     r = 245 + 10 * (1 - callsite.weight() / total_weight)
     g = 110 + 105 * (1 - callsite.weight() / total_weight)
     b = 100
@@ -63,7 +63,7 @@
         return '%.3f us' % (value / 1e3)
     return '%.0f ns' % value
 
-def createSVGNode(process, callsite, depth, f, total_weight, height, color_scheme, nav):
+def create_svg_node(process, callsite, depth, f, total_weight, height, color_scheme, nav):
     x = float(callsite.offset) / total_weight * 100
     y = height - (depth + 1) * SVG_NODE_HEIGHT
     width = callsite.weight() / total_weight * 100
@@ -73,11 +73,11 @@
         return
 
     if color_scheme == "dso":
-        r, g, b = getDSOColor(callsite.dso)
+        r, g, b = get_dso_color(callsite.dso)
     elif color_scheme == "legacy":
-        r, g, b = getLegacyColor(method)
+        r, g, b = get_legacy_color(method)
     else:
-        r, g, b = getHeatColor(callsite, total_weight)
+        r, g, b = get_heat_color(callsite, total_weight)
 
     r_border, g_border, b_border = [max(0, color - 50) for color in [r, g, b]]
 
@@ -119,7 +119,7 @@
          FONT_SIZE))
 
 
-def renderSVGNodes(process, flamegraph, depth, f, total_weight, height, color_scheme):
+def render_svg_nodes(process, flamegraph, depth, f, total_weight, height, color_scheme):
     for i, child in enumerate(flamegraph.children):
         # Prebuild navigation target for wasd
 
@@ -138,12 +138,12 @@
         # up, left, down, right
         nav = [up_index, left_index, flamegraph.id, right_index]
 
-        createSVGNode(process, child, depth, f, total_weight, height, color_scheme, nav)
+        create_svg_node(process, child, depth, f, total_weight, height, color_scheme, nav)
         # Recurse down
-        renderSVGNodes(process, child, depth + 1, f, total_weight, height, color_scheme)
+        render_svg_nodes(process, child, depth + 1, f, total_weight, height, color_scheme)
 
 
-def renderSearchNode(f):
+def render_search_node(f):
     f.write(
         """<rect id="search_rect"  style="stroke:rgb(0,0,0);" onclick="search(this);" class="t"
         rx="10" ry="10" x="%d" y="10" width="%d" height="30" fill="rgb(255,255,255)""/>
@@ -151,7 +151,7 @@
         """ % (SEARCH_NODE_ORIGIN_X, SEARCH_NODE_WIDTH, SEARCH_NODE_ORIGIN_X + RECT_TEXT_PADDING))
 
 
-def renderUnzoomNode(f):
+def render_unzoom_node(f):
     f.write(
         """<rect id="zoom_rect" style="display:none;stroke:rgb(0,0,0);" class="t"
         onclick="unzoom(this);" rx="10" ry="10" x="%d" y="10" width="%d" height="30"
@@ -161,7 +161,7 @@
         """ % (UNZOOM_NODE_ORIGIN_X, UNZOOM_NODE_WIDTH, UNZOOM_NODE_ORIGIN_X + RECT_TEXT_PADDING))
 
 
-def renderInfoNode(f):
+def render_info_node(f):
     f.write(
         """<clipPath id="info_clip_path"> <rect id="info_rect" style="stroke:rgb(0,0,0);"
         rx="10" ry="10" x="%d" y="10" width="%d" height="30" fill="rgb(255,255,255)"/>
@@ -173,7 +173,7 @@
                 INFO_NODE_ORIGIN_X + RECT_TEXT_PADDING))
 
 
-def renderPercentNode(f):
+def render_percent_node(f):
     f.write(
         """<rect id="percent_rect" style="stroke:rgb(0,0,0);"
         rx="10" ry="10" x="%d" y="10" width="%d" height="30" fill="rgb(255,255,255)"/>
@@ -182,7 +182,7 @@
                PERCENT_NODE_ORIGIN_X + PERCENT_NODE_WIDTH - RECT_TEXT_PADDING))
 
 
-def renderSVG(process, flamegraph, f, color_scheme):
+def render_svg(process, flamegraph, f, color_scheme):
     height = (flamegraph.get_max_depth() + 2) * SVG_NODE_HEIGHT
     f.write("""<div class="flamegraph_block" style="width:100%%; height:%dpx;">
             """ % height)
@@ -196,9 +196,9 @@
     </linearGradient> </defs>""")
     f.write("""<rect x="0.0" y="0" width="100%" height="100%" fill="url(#background_gradiant)" />
             """)
-    renderSVGNodes(process, flamegraph, 0, f, flamegraph.weight(), height, color_scheme)
-    renderSearchNode(f)
-    renderUnzoomNode(f)
-    renderInfoNode(f)
-    renderPercentNode(f)
+    render_svg_nodes(process, flamegraph, 0, f, flamegraph.weight(), height, color_scheme)
+    render_search_node(f)
+    render_unzoom_node(f)
+    render_info_node(f)
+    render_percent_node(f)
     f.write("</svg></div><br/>\n\n")
diff --git a/pprof_proto_generator.py b/pprof_proto_generator.py
old mode 100644
new mode 100755
index fcca090..b236c55
--- a/pprof_proto_generator.py
+++ b/pprof_proto_generator.py
@@ -28,22 +28,15 @@
 import argparse
 import os
 import os.path
-import re
-import shutil
-import sys
-import time
 
-from annotate import Addr2Line
-from simpleperf_report_lib import *
-from utils import *
-
+from simpleperf_report_lib import ReportLib
+from utils import log_info, log_exit
+from utils import Addr2Nearestline, extant_dir, find_tool_path, flatten_arg_list
 try:
-    import google.protobuf
-except:
+    import profile_pb2
+except ImportError:
     log_exit('google.protobuf module is missing. Please install it first.')
 
-import profile_pb2
-
 def load_pprof_profile(filename):
     profile = profile_pb2.Profile()
     with open(filename, "rb") as f:
@@ -165,15 +158,8 @@
         print('%sfilename: %s' % (space, self.string(function.filename)))
         print('%sstart_line: %d' % (space, function.start_line))
 
-    def show_label(self, label, space=''):
-        print('%sLabel(%s =', space, self.string(label.key), end='')
-        if label.HasField('str'):
-            print('%s)' % self.get_string(label.str))
-        else:
-            print('%d)' % label.num)
-
-    def string(self, id):
-        return self.string_table[id]
+    def string(self, string_id):
+        return self.string_table[string_id]
 
 
 class Sample(object):
@@ -185,13 +171,12 @@
     def add_location_id(self, location_id):
         self.location_ids.append(location_id)
 
-    def add_value(self, id, value):
-        self.values[id] = self.values.get(id, 0) + value
+    def add_value(self, sample_type_id, value):
+        self.values[sample_type_id] = self.values.get(sample_type_id, 0) + value
 
     def add_values(self, values):
-        for id in values.keys():
-            value = values[id]
-            self.add_value(id, value)
+        for sample_type_id, value in values.items():
+            self.add_value(sample_type_id, value)
 
     @property
     def key(self):
@@ -254,6 +239,7 @@
         return (self.name_id, self.dso_name_id)
 
 
+# pylint: disable=no-member
 class PprofProfileGenerator(object):
 
     def __init__(self, config):
@@ -280,8 +266,6 @@
         else:
             self.tid_filter = None
         self.dso_filter = set(config['dso_filters']) if config.get('dso_filters') else None
-
-    def gen(self):
         self.profile = profile_pb2.Profile()
         self.profile.string_table.append('')
         self.string_table = {}
@@ -295,6 +279,7 @@
         self.function_map = {}
         self.function_list = []
 
+    def gen(self):
         # 1. Process all samples in perf.data, aggregate samples.
         while True:
             report_sample = self.lib.GetNextSample()
@@ -356,33 +341,33 @@
             return True
         return False
 
-    def get_string_id(self, str):
-        if len(str) == 0:
+    def get_string_id(self, str_value):
+        if not str_value:
             return 0
-        id = self.string_table.get(str)
-        if id is not None:
-            return id
-        id = len(self.string_table) + 1
-        self.string_table[str] = id
-        self.profile.string_table.append(str)
-        return id
+        str_id = self.string_table.get(str_value)
+        if str_id is not None:
+            return str_id
+        str_id = len(self.string_table) + 1
+        self.string_table[str_value] = str_id
+        self.profile.string_table.append(str_value)
+        return str_id
 
-    def get_string(self, string_id):
-        return self.profile.string_table[string_id]
+    def get_string(self, str_id):
+        return self.profile.string_table[str_id]
 
     def get_sample_type_id(self, name):
-        id = self.sample_types.get(name)
-        if id is not None:
-            return id
-        id = len(self.profile.sample_type)
+        sample_type_id = self.sample_types.get(name)
+        if sample_type_id is not None:
+            return sample_type_id
+        sample_type_id = len(self.profile.sample_type)
         sample_type = self.profile.sample_type.add()
         sample_type.type = self.get_string_id('event_' + name + '_samples')
         sample_type.unit = self.get_string_id('count')
         sample_type = self.profile.sample_type.add()
         sample_type.type = self.get_string_id('event_' + name + '_count')
         sample_type.unit = self.get_string_id('count')
-        self.sample_types[name] = id
-        return id
+        self.sample_types[name] = sample_type_id
+        return sample_type_id
 
     def get_location_id(self, ip, symbol):
         mapping_id = self.get_mapping_id(symbol.mapping[0], symbol.dso_name)
@@ -457,53 +442,62 @@
         if not find_tool_path('addr2line', self.config['ndk_path']):
             log_info("Can't generate line information because can't find addr2line.")
             return
-        addr2line = Addr2Line(self.config['ndk_path'], self.config['binary_cache_dir'])
+        addr2line = Addr2Nearestline(self.config['ndk_path'], self.config['binary_cache_dir'], True)
 
         # 2. Put all needed addresses to it.
         for location in self.location_list:
             mapping = self.get_mapping(location.mapping_id)
             dso_name = self.get_string(mapping.filename_id)
-            addr2line.add_addr(dso_name, location.vaddr_in_dso)
+            if location.lines:
+                function = self.get_function(location.lines[0].function_id)
+                addr2line.add_addr(dso_name, function.vaddr_in_dso, location.vaddr_in_dso)
         for function in self.function_list:
             dso_name = self.get_string(function.dso_name_id)
-            addr2line.add_addr(dso_name, function.vaddr_in_dso)
+            addr2line.add_addr(dso_name, function.vaddr_in_dso, function.vaddr_in_dso)
 
         # 3. Generate source lines.
         addr2line.convert_addrs_to_lines()
 
         # 4. Annotate locations and functions.
         for location in self.location_list:
+            if not location.lines:
+                continue
             mapping = self.get_mapping(location.mapping_id)
             dso_name = self.get_string(mapping.filename_id)
-            sources = addr2line.get_sources(dso_name, location.vaddr_in_dso)
-            source_id = 0
-            for source in sources:
-                if source.file and source.function and source.line:
-                    function_id = self.get_function_id(source.function, dso_name, 0)
-                    if function_id == 0:
-                        continue
-                    if source_id == 0:
-                        # Clear default line info
-                        location.lines = []
-                    location.lines.append(self.add_line(source, dso_name, function_id))
-                    source_id += 1
+            dso = addr2line.get_dso(dso_name)
+            if not dso:
+                continue
+            sources = addr2line.get_addr_source(dso, location.vaddr_in_dso)
+            if not sources:
+                continue
+            for (source_id, source) in enumerate(sources):
+                source_file, source_line, function_name = source
+                function_id = self.get_function_id(function_name, dso_name, 0)
+                if function_id == 0:
+                    continue
+                if source_id == 0:
+                    # Clear default line info
+                    location.lines = []
+                location.lines.append(self.add_line(source_file, source_line, function_id))
 
         for function in self.function_list:
             dso_name = self.get_string(function.dso_name_id)
             if function.vaddr_in_dso:
-                sources = addr2line.get_sources(dso_name, function.vaddr_in_dso)
-                source = sources[0] if sources else None
-                if source and source.file:
-                    function.source_filename_id = self.get_string_id(source.file)
-                    if source.line:
-                        function.start_line = source.line
+                dso = addr2line.get_dso(dso_name)
+                if not dso:
+                    continue
+                sources = addr2line.get_addr_source(dso, function.vaddr_in_dso)
+                if sources:
+                    source_file, source_line, _ = sources[0]
+                    function.source_filename_id = self.get_string_id(source_file)
+                    function.start_line = source_line
 
-    def add_line(self, source, dso_name, function_id):
+    def add_line(self, source_file, source_line, function_id):
         line = Line()
         function = self.get_function(function_id)
-        function.source_filename_id = self.get_string_id(source.file)
+        function.source_filename_id = self.get_string_id(source_file)
         line.function_id = function_id
-        line.line = source.line
+        line.line = source_line
         return line
 
     def gen_profile_sample(self, sample):
@@ -511,8 +505,8 @@
         profile_sample.location_id.extend(sample.location_ids)
         sample_type_count = len(self.sample_types) * 2
         values = [0] * sample_type_count
-        for id in sample.values.keys():
-            values[id] = sample.values[id]
+        for sample_type_id in sample.values:
+            values[sample_type_id] = sample.values[sample_type_id]
         profile_sample.value.extend(values)
 
     def gen_profile_mapping(self, mapping):
@@ -554,18 +548,18 @@
 def main():
     parser = argparse.ArgumentParser(description='Generate pprof profile data in pprof.profile.')
     parser.add_argument('--show', nargs='?', action='append', help='print existing pprof.profile.')
-    parser.add_argument('-i', '--perf_data_path', default='perf.data', help=
-"""The path of profiling data.""")
-    parser.add_argument('-o', '--output_file', default='pprof.profile', help=
-"""The path of generated pprof profile data.""")
-    parser.add_argument('--comm', nargs='+', action='append', help=
-"""Use samples only in threads with selected names.""")
-    parser.add_argument('--pid', nargs='+', action='append', help=
-"""Use samples only in processes with selected process ids.""")
-    parser.add_argument('--tid', nargs='+', action='append', help=
-"""Use samples only in threads with selected thread ids.""")
-    parser.add_argument('--dso', nargs='+', action='append', help=
-"""Use samples only in selected binaries.""")
+    parser.add_argument('-i', '--perf_data_path', default='perf.data', help="""
+        The path of profiling data.""")
+    parser.add_argument('-o', '--output_file', default='pprof.profile', help="""
+        The path of generated pprof profile data.""")
+    parser.add_argument('--comm', nargs='+', action='append', help="""
+        Use samples only in threads with selected names.""")
+    parser.add_argument('--pid', nargs='+', action='append', help="""
+        Use samples only in processes with selected process ids.""")
+    parser.add_argument('--tid', nargs='+', action='append', help="""
+        Use samples only in threads with selected thread ids.""")
+    parser.add_argument('--dso', nargs='+', action='append', help="""
+        Use samples only in selected binaries.""")
     parser.add_argument('--ndk_path', type=extant_dir, help='Set the path of a ndk release.')
 
     args = parser.parse_args()
diff --git a/repo.prop b/repo.prop
index 34b0ecd..42fab72 100644
--- a/repo.prop
+++ b/repo.prop
@@ -1,285 +1,290 @@
-device/asus/fugu 7e05b83b82f4da3a2b5cdf2c729b72f9a8bc2886
-device/asus/fugu-kernel c64f934a0f09bb485a241710f6d3fdd86ceb6964
-device/common a62f435feab2011fe3c842bd6003ad727b45f836
+device/common 90cb78a769f20a8fba99e8a67d394038cdf35cbf
 device/generic/arm64 392eec3cb4c64721cd5b76fa36535954b5d45309
-device/generic/armv7-a-neon 9e96e041bf3c9d5b57b11f921c6339e076478a7a
+device/generic/armv7-a-neon 98c1add8d047381bc8d325d0069170600d2b3388
 device/generic/car 46db904d78e3f3517a2b7f5b41b408ba5d69ead6
-device/generic/common fb116ba50d1b77821337f37a97617e114ceabf0a
-device/generic/goldfish bb4e3e5f640fd1568320352dd09f40cf295e67ac
-device/generic/goldfish-opengl 032255d4c6cabe67a1f25f650f38c6e39d747d0c
-device/generic/mini-emulator-arm64 2ced84e28c3435b9bca882d5becc6c77912a3b98
-device/generic/mini-emulator-armv7-a-neon 078cbd03b2073b31530845bc0e48a1a166914ec8
-device/generic/mini-emulator-x86 152ea797fd07b76d87f83a343bdc58ad2c762028
-device/generic/mini-emulator-x86_64 47302a0e22c620b9f7b9d2215bd32d54f68e7bb6
-device/generic/qemu c7575805571f5b0d2b346d0bcfcff2e80b85f6f1
-device/generic/uml cdc20aa5e05005f67989d8f86b64c0526b87b76d
+device/generic/common 3a1b861e123023390628512256c01c3a0624b9a4
+device/generic/goldfish f8eaa9efac411481ba56efe2bb2e758f941a2597
+device/generic/goldfish-opengl 952c1ee50f8bb4735931e39fb3ad0081781b5cd4
+device/generic/mini-emulator-arm64 595ad51d88bfbd3e680b290b69bbb28f6ee41f27
+device/generic/mini-emulator-armv7-a-neon c65b46a1a0d89411e666244423555c860413e911
+device/generic/mini-emulator-x86 eed5690b365f4859bf718d48345902ac99484cc4
+device/generic/mini-emulator-x86_64 e7cbff5118aefe44a397bc793e944b0f1eb7c84c
+device/generic/qemu 0cc02265690c4b908c587b73d7ab0f28460c391b
+device/generic/uml 0d977b69ffa9205758cdf6b9fcb580040d07dca5
 device/generic/x86 645e6e711b32f94ecbd9b1cc77a51559dfce5ebe
 device/generic/x86_64 506952733fdebb5a2ef086b519ceebe86a95ae31
 device/google/accessory/arduino abc5159a3ca9dbb5c7e364a1eab99901a4440ac5
 device/google/accessory/demokit 7dfe7f89a3b174709c773fe319531006e46440d9
-device/google/atv 8508682879c3609445857fe43d58bd9ba91265b2
-device/google/contexthub 0de12d480ad03ab5f1146f5b20f708d5a7b69d94
-device/google/cuttlefish 2a1f8c1b2ed9c2c353a3d55cec323e307dd5c5c7
-device/google/cuttlefish_common a732f588b36c87551eca671b4a259fcc11f490a6
+device/google/atv 0be683f290141610b055a5b2a1edfebd8843885d
+device/google/contexthub 51c32d5b4cd2177e64fdbbac4f161026aab3aecc
+device/google/cuttlefish 301c0efcee67601c9e2d29cf921b2d7c968271b5
+device/google/cuttlefish_common ab40dceb5ae7d081946d4459635e80bf80f9c0ff
 device/google/cuttlefish_kernel 01ec44d12f853de3f4dc95dee2af2cd3dc4f6acc
 device/google/dragon d764a91064c27a1df6186641f99399efef24f465
 device/google/dragon-kernel f7f148d36fa47eb17b1bc7025ffc9f388af1e7cf
-device/google/marlin 082cd5dc8b09c63e257a4109e1cbae852b9eab51
+device/google/marlin eaa07e06c1ed82874a15b38d81901802a40cb920
 device/google/marlin-kernel 76ad80e315e1c41db73c17909ef95b3b738ea991
-device/google/muskie 2b111ebde66fa7229b0a5e3f2b29d5d7687b520b
-device/google/taimen 7a749145d99e3972567d427e3b724d88f2543053
-device/google/vrservices a0d1e67bccda895a5556e8a2fac8bd4e04985607
-device/google/wahoo a4ac62adff8ab51e2d9f6bbd125bf047c3e99bb8
+device/google/muskie 383f387a8771105e0ed7c85ea32f1403cd612380
+device/google/taimen 7e953a2f32aa51008769387f6ba70044d51c19ac
+device/google/vrservices bdc90104b414fd8caacf07eb960f1519de57f500
+device/google/wahoo e8e45d58ab76ff076938746997528a600b1ffa04
 device/google/wahoo-kernel a321d637755d40f43da8b0a3fcb846a83d817da6
-device/huawei/angler 5cf81900d1ba351339cbc3d36ddc0ae18b33a131
+device/huawei/angler cf92cd8e298cda8d0550f3e9f826d03d285a8d3c
 device/huawei/angler-kernel 1e67697ea59d01f495b31a0d44ec72979e35dd10
-device/lge/bullhead 00f1156f771566c0050209dbe6ab741971ee4fc2
+device/lge/bullhead 2994b6be2bf0191c73e5d4ceee1833119c99ab96
 device/lge/bullhead-kernel 279990fb4d0c47958929515f3aaf0d4939cfbf1d
-device/linaro/bootloader/OpenPlatformPkg 662f6b3adfcf742456efd5ef24106af71e0fab57
-device/linaro/bootloader/arm-trusted-firmware 8608ac5262e8b04e72cef118477baf445f5435aa
-device/linaro/bootloader/edk2 27fa02b09674915e1c0e053214b95a68a98e2cb9
-device/linaro/hikey 19336afbcf7d20b343bb00d72e296c35b705c1e7
-device/linaro/hikey-kernel d255b44692e3900dcaa073b787cace42b7b3eb2d
-device/sample c08631332a984033b323ccc61e1fc956e85179d6
-kernel/configs c4163554066d88d60874deba70303445bed94e3a
-kernel/tests ab297908759d64ca023597dfdeac6ebc57114e98
-platform/art b7f680727032a6fa82f166c2c268db8eb0f3b570
-platform/bionic a27cadfa5fd75cd54e1597a13ed517fdd6909fe3
-platform/bootable/recovery 2de12da142af4dc8386ea3b6838d3e10ea9bf20a
-platform/build 36bf0df48f916f4a64468ffaf6cb5f6b76dae2e5
-platform/build/blueprint b2e8a88c013496fdaa9a177eafbf0bf47e4721d2
-platform/build/kati ac70a73c816b3a51fe1541ef594f665853662e2f
-platform/build/soong 8959e149b713f6c7332f9eda80fe5dc47c9d0718
+device/linaro/bootloader/OpenPlatformPkg 7cb8f3dc8dbbeebb3cc5cde8c47821f9a463bd99
+device/linaro/bootloader/arm-trusted-firmware b23f5fb1ac2033077c7204f604b2c74649b0e9a3
+device/linaro/bootloader/edk2 08244e33149eacfbeee382aa02a17940fc5abfce
+device/linaro/hikey 2d8fb579d511429bbf3b2a2cdabaf9dd4361cba8
+device/linaro/hikey-kernel f9a8ca624e83d96ab15926f8b0cce7d48e872fd6
+device/linaro/poplar 741e61d94c0ffa06f2d971aed5b662607f72d0c9
+device/linaro/poplar-kernel 6412647c7ff0d2d412281082a574a8ebb0d8a2fa
+device/sample 41da2ed7a51fa50e3a2bc3fcbde3c3456012907e
+kernel/configs bd3b160eaa472da3f657e59593f51a622912ba5e
+kernel/tests 06e96b06bcbe9b3f8dc1ca1495dcccf4622bcad4
+platform/art 34be5e212b18f03a6cd3ec3b11cd05df49a4d32a
+platform/bionic fc97dafa86faa0b8639e7ace40dd48cdf5cd32dd
+platform/bootable/recovery dbe44203257917d2610a3fc3f0e9e84ec49e6b34
+platform/build 959701f1a821920a4b1ba31f40fea478ca637814
+platform/build/blueprint 254f2ae7b16c654531498f37de8e7568759af816
+platform/build/kati 40ad301ec0da74f820d0e06cada96e98273e33d2
+platform/build/soong 77a69ecb70765f09f40c249a1546b5de7b2ee7f8
 platform/compatibility/cdd 85a4240862bddec633e1a3a73c95212b95844ce4
-platform/cts ec2907be8379cc5a9873dfb28195c31b9e37aa1f
-platform/dalvik 2e9d6011fe4f4d2e8c065210d0118e7b9d9f305c
+platform/cts 9d6110e5d3a19f26c4cfc6a7b2fee418a0d5a569
+platform/dalvik 5428d7f79625d38353a1ca4322839e40ba8889c0
 platform/developers/build ae45e741a440e39cfd68e0efd1f048f5b7896e0f
 platform/developers/demos 03814c35b8ee0a1284c667556260124d97466b28
-platform/developers/samples/android 0b1ff9e0666e104f6f7d6401449f32ba9f9cbbaa
-platform/development 16d65bdb0c50814a97cd633d5ac17a8997bbf1dc
+platform/developers/samples/android cae19d9a712446822c72b4f624afdcd424fedc45
+platform/development 752ffe8a3b583b04fa750fe2ce6cba74dd1eef77
 platform/external/ImageMagick c9cef2822b2f87340f725dc87d3ac3cca797d176
-platform/external/Microsoft-GSL 771e49c447d5b6082482f42d11f0624d83cc357d
+platform/external/Microsoft-GSL 2ef7b8a9e35e409b175e190298cde5434155f54a
+platform/external/Reactive-Extensions/RxCpp ef3b4d50442673ca87176d9e7b02c046c0cbdd50
 platform/external/aac a3e0aa5f25908d92535e045bbde73c9a3d19adc7
-platform/external/abi-compliance-checker 4e8243ffe9cda940112cde32017a90a9e1bc6878
-platform/external/abi-dumper 179d46ad643e21523ecfb4f97108be8a53f24778
-platform/external/adt-infra 2d2ad8555a5f05af7e124cfad946a0560de1d848
-platform/external/android-clat f1c79f8dac61141a518b87361cfef0619e3ef49e
-platform/external/androidplot c66727ebf001607cee14521c35bc852b55fd9845
-platform/external/annotation-tools c7d7f5d919e7c4741fd863b070772d5afcfa1f3b
-platform/external/ant-glob b13276924c017addc59c0edb2c5b123502be26cc
-platform/external/antlr fd960efa159591a9a11a27aca191ebf7e5f7f4aa
-platform/external/apache-commons-math eeab42b14a95bc1972316325abdd995393a1745e
-platform/external/apache-harmony c7b2514d479c53b6965bfcfa40f14ec373549660
-platform/external/apache-http 6bf56852e720f77b9da28d048826ead78de166b7
-platform/external/apache-xml 6a1742c1aa8bab2adac66aa7820fe51b4b494312
-platform/external/archive-patcher b51cca584ba2f39c7659465063381c46bf95a6f4
+platform/external/adt-infra 2f907142600275ccd4eab0fa3015a3b0a174472b
+platform/external/android-clat ec4c31a080cd42e705222c34e8acb4f8478f0540
+platform/external/androidplot d8ab4d46ec81cb669261e826aee1c7c6b52c06e2
+platform/external/annotation-tools 4aaa13fa86ebd895d501584cdf7d4984799575ac
+platform/external/ant-glob 85522dde56ebac6e2c2b5453a506f3736d899126
+platform/external/antlr f27dca5549129b36ac3de86c253a769844c9dcc0
+platform/external/apache-commons-bcel 347ef0a0556a6ddf13b23a6c32d6378b1d77b5f6
+platform/external/apache-commons-math 0cb1dc642ca1fd29f9ff17f1c3a50c8ca43f54e1
+platform/external/apache-harmony 5992da371ec867b6bb42255f69ed9459fb3b4b78
+platform/external/apache-http fc79906645ed6f0c56d71d0914af2e32e5de5d73
+platform/external/apache-xml d129e1daf125e6ef2ea832ce39302879a375bbd7
+platform/external/archive-patcher 6e8fe1c5a159cd916729f67af01e4011c4461165
 platform/external/arm-neon-tests 7eef3ecdd1dda0bcd075e4035de17257edac832e
-platform/external/autotest 4d744a73aa7bc2bef8fd0390ce80e22e02facff9
-platform/external/avb fccd64ecaf416932a3f668eaff0a6af1eb9cba75
-platform/external/bart 8c0608b2d51fcaeb9a84d748499108fd8efe9fb3
+platform/external/arm-optimized-routines 70c38b33a1421dc1c48434a5902c33c196ef6d4f
+platform/external/autotest 7c735a267016822dba9d9ab88ba5a4e07caae41f
+platform/external/avb 076a0ebdd59526b80eeb328abb37c39434dc731a
+platform/external/bart 08808a75e6fd92995ad2be2730e9334ba8ce1c8a
 platform/external/blktrace d345431f16b8f76f30a58193ff2b26d5853e1109
-platform/external/boringssl c6ebb28b08232d4af3e99643b4e260b98381b4c6
-platform/external/bouncycastle 23e859dfd2e095bd71be0290b7c99f0016c6bf4e
-platform/external/brotli 5972a1cdb82a8417811bfc4c424fd1384160f573
-platform/external/bsdiff c82a7587b850ae43ba4bdb1d317e697642896215
-platform/external/bzip2 757884d3a8acac08a4d6da4bbbc8794278f7c8de
-platform/external/caliper 1c800fe10206aed2c6fab03fda8263841b585cc7
-platform/external/cblas a4218b0a022528469b2df7f5d2a71dcf1f6758a5
-platform/external/chromium-libpac 8c47eebd81c996f17198edb2fa504c04d48d567e
+platform/external/boringssl 6120b2b9dfbe5b19fe2d0f69a4b8418ad5f7fe08
+platform/external/bouncycastle 92e371bf810507957295981437c1fda6c3208fef
+platform/external/brotli a3b33dad0761d13f273719113a2824ad9d100106
+platform/external/bsdiff c35a3dd7996924b9925880f988993faec3449e26
+platform/external/bzip2 a5304fb28b079b3980ff094cbeb604aa217d21e9
+platform/external/caliper 9a6777b973f78370a8aeed5d2953320fc887732f
+platform/external/catch2 d405d66ed6d7bdf4376654ece193b4f0bd7821a4
+platform/external/cblas ddf5f49cb53866fbc503a6349bf44bac24a6963d
+platform/external/chromium-libpac 97a4dae9b92907e82e47fce8566e6743eb34630a
 platform/external/chromium-trace 3e601f2c29e63f5151aa982790deea52645bc6ea
 platform/external/chromium-webview 566a38bfcf260c9cf94af0a82f9c3551b9830926
-platform/external/clang 4083c7505ffbdec9338f19e1288043efa87b9694
-platform/external/cldr 6a2f6ad06b1a31ceec40f2283438a546d8f7af1d
+platform/external/clang 2fce575dfef7bd3760f5882448baa32af39f0f2c
+platform/external/cldr 40b6c069ee23f2f32609c64e27495be82915a48b
 platform/external/cmockery 9199c7bfafefea32d1884182fa655b6e4578c1c4
-platform/external/cn-cbor dd6fcf75d70cf6f9d06014813df325b3574032a7
-platform/external/compiler-rt 754fd3d334cf667e65c9642f175326cd07922983
-platform/external/conscrypt b8d709a8b00c4dbb2f44545fe387333a5345b550
-platform/external/crcalc 5ee5a5919298a037f859e0f14bc058eb29710eb3
-platform/external/cros/system_api acea362da79d44846981ca97b4d66e56bd49d77d
-platform/external/curl 0c6d81f80bf942993566564f6efb1de2ab1d836b
-platform/external/dagger2 aa6a6e2682aed0d5bb483baa37a04357af0ea918
-platform/external/deqp 9800dc30748dbfaccc32ab15e9012fc4e61d20a2
-platform/external/desugar 9e1602fcb030f33d2d263acd783a9e24fccb3e7d
+platform/external/cn-cbor 7fe9f01990a97be4df5e46d2decd894c06678072
+platform/external/compiler-rt dc737ff85f32510749ba507e289f59dec39b4a63
+platform/external/conscrypt 952ea709347bf1988595da1d002c57651ed0a7ff
+platform/external/crcalc c42b173b60027d1517cec618f3776fb55e941dd1
+platform/external/cros/system_api 205c6829e7c570e6d86c764be470f4bfb423140e
+platform/external/curl f7bbc49291518d581cfc7d771c300c157b944c38
+platform/external/dagger2 b1654fa03e7f43425c74644ca73d860198344d30
+platform/external/deqp e98bb687e85fb0e4b671f8e301a2f8ddee14f181
+platform/external/desugar 2b50d295f5acc8ddf8924cd6536dfbfe45965ade
 platform/external/devlib 5222d76c31640dbb9c2f319f7f09cb42074002f9
 platform/external/dexmaker dc8ca0a5de696b028e21f8ad7ebf4ee4936e128e
-platform/external/dhcpcd-6.8.2 bbe9a70a8592233a55b6af52a88f1758b9ba8d69
+platform/external/dhcpcd-6.8.2 a34a61b23d287c7d24a2d34c7f8c191641747316
 platform/external/dlmalloc 6661f3ca66b55d8f5a57b96fec97efaf8f3897a5
 platform/external/dng_sdk 28469c57712f985ba7acbf4ed7e2a8352bf33a9a
-platform/external/dnsmasq 1e03ec8dc8c0401318bdeaef0845709d733b8f38
-platform/external/doclava bfb4c0d806d0f4013a79f9393cef7b9f93e6a512
-platform/external/dokka 244f97fc9ec5745b377e594e54b2f465489fef8b
-platform/external/drm_hwcomposer b4ae4fab95bc638335309091084d5378ccabc7e3
+platform/external/dnsmasq 8a524e76a1cbafa7ee12afd22ba2747cf3fd9849
+platform/external/doclava 86de7f03745a4d9ce2cb3b8e1dd096449f063ef7
+platform/external/dokka 4361c0e608cd2076bce568016fa6926e7a13c2c3
+platform/external/drm_hwcomposer 7e75bce5e3b3e8617f8f513ea048d6e8a8b73d4d
 platform/external/droiddriver ef618d981947f6d8242755516269a0026c07126c
-platform/external/drrickorang 5ea28e9b46d5f0b805ba8ec84a5541d6a4869de6
-platform/external/dtc f0b88bfe14680e11e51c8637d08d8fd0fe53d4da
-platform/external/e2fsprogs 794489fc8b7e3ce263d5c7f2bd2e62202d857244
-platform/external/easymock 1c7aea9e23a6c155f08b08af709de1751476483f
-platform/external/eigen 1d5d34618a9c4d8340eb4f6d597fa84563371d46
-platform/external/elfutils bc54f04a343047328a0f268ef525a05e2bdea31e
-platform/external/emma 2488a22acfb93a472c4a7dbf1dce6a2698e5a4eb
-platform/external/error_prone ce8219b911658a99cdf4f46c2bb16d65ac6d75e7
+platform/external/drrickorang 57e41657aaec89ef910297c759946bd0f4831806
+platform/external/dtc 6c6634a26785d16b058ee3799f69cd3606a557ca
+platform/external/e2fsprogs 1697f664782ab693dd22595d213beb979ab0c480
+platform/external/easymock 045fe1a003805f21f4bd266ab202319486a90284
+platform/external/eigen 1b13c5e03d9005018784383e65e74089cbef47ce
+platform/external/elfutils c5f2c7e1c19acb791f3b58344929c6b5b3f9b9e1
+platform/external/emma 181b44b0422f2ca9a844a39f0b7e7f20c2712b72
+platform/external/epid-sdk 6e4aec865fcfaa48d2f8827dac35eb51d8ba9f9e
+platform/external/error_prone f6d44b6a684316132121f4e10c6c1f532ba957b1
 platform/external/esd 943c42b6f8e9afe821744aa4c039f4943ebf29f5
-platform/external/expat 9204094d45b6274f1673472427bdfb6c18538a09
+platform/external/expat 8fd2db7fafa14a7be63ef6e532fb6d39011bf7cb
 platform/external/eyes-free 16bd4c7a4d1bfe229068b637614dad7c48dd2ceb
-platform/external/f2fs-tools 8f3bc3f9bd26adcf39216386d61d3ae434e84aff
-platform/external/fdlibm 2cc2d9e683f82a9a3a2145118306adb2d2e91d4b
-platform/external/fec 14e8e0309c9bfca60c34b0bf4021b49dc72081aa
-platform/external/flac a16bad628f3492590f28389c4c56360e9efc8e87
-platform/external/flatbuffers 3ff8b19e2821f21937b1a1ea9ee24108a4c823e3
-platform/external/fonttools cc9a86e36194f2ef4456e14d98b810e41e4fa52d
-platform/external/freetype 99fee66607b5afe9153b7e8d455f258ecfb3089f
-platform/external/fsck_msdos 187d924304ffec8610cc692e51017b61c07949b4
-platform/external/gemmlowp 7d0d5a611e629e7c8946e6720baa6846ade9f015
-platform/external/gflags 3ca002cf3dfd31e74db686f27f749c5548029733
-platform/external/giflib a71d71db259cb315e497b33953e077e0b9d3532f
-platform/external/glide de595520977614ec63057cb8de1b3737f4d1ada7
-platform/external/golang-protobuf a8f9838e293dbdeafc5acd56805f8b7cd98af9a2
-platform/external/google-api-services-storage 29b36ffc866ec32ea01f92ab55100f16bb4b2157
-platform/external/google-benchmark e6467412a7da0f502cb3a51ad3f3a526edc7dbe7
-platform/external/google-breakpad fbb27dd33c98ac00b776a4880de763e4b5e79316
+platform/external/f2fs-tools 0598f3e0a71a8c1f9206bd2df8fca175c0e62c5b
+platform/external/fdlibm 59f7335e4dd8275a7dc2f8aeb4fd00758fde37ac
+platform/external/fec 696ab3cde225bbd6a24d7aa74a709eac696979f8
+platform/external/flac 2a2f59be31eda0c290bf1c6e17dda9a1fc1b25fb
+platform/external/flatbuffers b5c9b8e12b47d0597cd29b217e5765d325957d8a
+platform/external/fonttools 23e2a03920cfbd4758735b232bf64f26e6c63e7e
+platform/external/freetype c3f1fdb6e2f28e0c03402b91c8005f3be189f198
+platform/external/fsck_msdos 2edb366074e3c3ce4004f383ef20f598fd19769f
+platform/external/gemmlowp 0ed4f31d5ced2432473aa7063bc1e28d990ff3f2
+platform/external/gflags 992dca696ee6cfdcf536d1cbb2a2f1e950f28366
+platform/external/giflib 2655771fbcf80ef5cab8192b9409f697da9d8c0a
+platform/external/glide 0737ddc95f0f2d8f6527689c34116bc89d0847d8
+platform/external/golang-protobuf ec9867d202a6cb31bd5424887edf9f4a71f8262f
+platform/external/google-benchmark 3d1fb1b6d7995b770fba7c7d150f264563eb59b3
+platform/external/google-breakpad c668ec43bdb864a64e65ca40da932f8d7e2d077a
 platform/external/google-fonts/carrois-gothic-sc 0062a10458d4c357f3082d66bcb129d11913aaae
 platform/external/google-fonts/coming-soon 2c5cb418c690815545bbb0316eae5fd33b9fc859
 platform/external/google-fonts/cutive-mono bce2136662854076023066602526ba299e6556b2
 platform/external/google-fonts/dancing-script 7b6623bd54cee3e48ae8a4f477f616366643cc78
-platform/external/google-styleguide 333e3bc1315f1cdf215d51af343e24c7381e8cac
+platform/external/google-fruit 67a7fe692ecc0da7ea6e10b10bf8b1ca5662eeab
+platform/external/google-styleguide 749f7ae008c0d2dd9767fdfe202e6873e864a956
 platform/external/google-tv-pairing-protocol c731915e80d9e2ccb755b97e7fdd280bbea07f70
-platform/external/googletest 23f59843dbc16e86d0451123c55243c5f3fdbc33
-platform/external/gptfdisk 09d403257ec735131d9038603114eda58a3ac0e2
-platform/external/guava a0e05072adcf2028e8b6727e2c17974888a786ed
-platform/external/guice cd790698226ca626108c503b754ea2d3f60129d0
-platform/external/hamcrest 8ac829b3c618329d0c993c90080d5ac8aadb010e
-platform/external/harfbuzz_ng cc370d5a67a64ef776226770ec83b223fb8f85b5
+platform/external/googletest 921be817eec95af0f321362c02554bd02b502ac7
+platform/external/gptfdisk 2d88f8dcb70b3b9c1412e159c2356df34e4d839e
+platform/external/guava 2e52c68711b104ac2e225a9d298bd2de1eed92bb
+platform/external/guice 628ced18e817a96c11857a51536468a886c6e183
+platform/external/hamcrest 6668c743790380cb3e7889c2abb6cd53b720a211
+platform/external/harfbuzz_ng 0076322c98db97239436bfe4fb72cd6443da57b4
 platform/external/hyphenation-patterns fecb03d289ac347852ce63b8b37677066ab6746f
-platform/external/icu 9590ab58b95ec96cdb512a934ce4e156685b9ed8
-platform/external/ims c84ae57fd0564666a10ec8c975eb827b77706ebe
-platform/external/iproute2 2392dac63f04e7a575c8618d2187280aeba742c4
-platform/external/ipsec-tools 21ae2deacba0b38dd613d3912a1efdd7642785d0
-platform/external/iptables f316c86a2b40cc844e66dbccbb3bc2ba1ee454c3
-platform/external/iputils 113980460536e7fe25db5a3063ef435a08a326b4
+platform/external/icu 1a6c91e9ab221725ba1cbebb0449d3c2db758114
+platform/external/ims 2236205ce0d5861b327067985b5a25a66f5ff4fc
+platform/external/iproute2 20c0f1de5d57ebc3acddd35934f6f184a4fe1639
+platform/external/ipsec-tools bf16586a33b1432a11526754523fceb62944dc1c
+platform/external/iptables 622ce2570bb42685e8e6910c64ec28b3120d3f28
+platform/external/iputils 71c6d11e1fef4a52db0563ebca05dbd006467855
 platform/external/iw 63d8bad2a5a8a9b443595762233692c5f6a6d36b
-platform/external/jacoco 8922af5540d7e7587de647df3875b3fac244f0a4
-platform/external/jarjar 2db8c52efa6a0a706ec6e18ab09b09626212a857
-platform/external/javaparser b7e221621daa3502d52dcc087dd2c8e5a47b9f35
-platform/external/javasqlite 56dce0b1b743b5fff318bdfcd1c77929262c0c31
+platform/external/jacoco 730cc2668f7a8e9ef614b7bccf4b1586e77f9e90
+platform/external/jarjar 35912cf94b8213bbd6c06017684de671587b1b8a
+platform/external/javaparser 1afe9e0652b9b53edade5aa276162abe27b32a67
+platform/external/javasqlite 8ece4e2b6edb941592f72cc811d04425aad5c170
 platform/external/javassist f7c4b954072e563b75f6910c25bb689bbf38a3d1
-platform/external/jcommander c75d582b559ceb26bd75cc082c74fb7b52d937bd
-platform/external/jdiff d59157b919fa55c4d9679c5b43fe96548579a404
-platform/external/jemalloc b7be677fa4873914f1f3f913cb91ef3f097f23f2
-platform/external/jline 74812032f8d8eddbef387f18c96de9e5c38b8fdb
+platform/external/jcommander 288dbc7374714f68a01d97e2491f1ea6840e26df
+platform/external/jdiff fe19ba361e4a1230108378b4634784281dd9d250
+platform/external/jemalloc 3a6a856bba83bf55e30598596f46822a46a20805
+platform/external/jline 486ecfedc8a054176ffc95db49d59b01115da202
 platform/external/jmdns 0c71647deb7f7835c473fd3dfb45943083e47794
-platform/external/jsilver c1514dce6b718b3c2c97da89d727ba06f88ad2d6
-platform/external/jsmn 748637f95e2eef27125b3ae9890a3c24f16a366c
+platform/external/jsilver cbfad5619138abefd2a304dd537cc4ed4ad2b7a1
+platform/external/jsmn 0945105514d9e025c67a9cbc64b3e96e40ed999c
 platform/external/jsoncpp ba99b11181c0c9bad38d87c6404e1c8ce9591b9b
-platform/external/jsr305 e51e6c827e802c6261f39d64af0969b80f2f3274
-platform/external/jsr330 231a7a1e3a4e049aa1bf14ff69f947f05bbad7ec
-platform/external/junit b52b6b012d348d26d693657da3cb75ff525990b4
-platform/external/junit-params 98cf2d73469fb6d9aee3f453d17169770983a57b
-platform/external/kernel-headers 62240ee6662daa3f339206717125c59ff8143df0
-platform/external/kmod 3080778f3b915e75328d904887e72f6015a6960d
-platform/external/kotlinc 7e0c7b88c2b648a7f53dae88238c79fb9364890e
-platform/external/ksoap2 9fdc4a6bb2b882bd1d6c822d2ad0c66585d66ba9
-platform/external/libavc 6ecdf603b4f77c8785dd010bbaa71709db9bb4af
-platform/external/libbackup df2414e5a07f3935ae3a986c8c6e0b7f5951a6c0
-platform/external/libbrillo 5b67e34f822b7288a24723d090b0329cf12750aa
-platform/external/libcap a9bf1f0446b20683183e0070441f8fa142e67c71
-platform/external/libcap-ng 9268914ae163fbb60c63ab16aba6906c9c92e35c
-platform/external/libchrome 2e2446af20fcd5c2202fc40c5f50c66b983c706e
-platform/external/libconstrainedcrypto 19294173909b349d361ee592855425a5bc4196f7
+platform/external/jsr305 afbad758fae6b74b4a739935341c224575c58169
+platform/external/jsr330 8ef1f9dde8e3bbfbdac76d39265bbb0588c3330b
+platform/external/junit c67195927f579fb8a7fda44815b189e4c0ecf58f
+platform/external/junit-params d893a996dd257b9cc351235bf186700928c5852a
+platform/external/kernel-headers bc75e834cd9462108f170ae59e51076e9d2f2b17
+platform/external/kmod 7cb2f308403b177720e6f022d404ce1fc414e663
+platform/external/kotlinc e0b456261d9a3f661b289c1bf037c628c7a85f99
+platform/external/ksoap2 efef04a819dc9ca99b9c72ee9739d5006339f1ae
+platform/external/libavc 0f1e83047cae926777af64319862d21d987e388d
+platform/external/libbackup 48482a17ecc34a0a36dce78a73db9dd934d70a7a
+platform/external/libbrillo b5f20f52ab0885f1676b1c2024df541b3e6cc5f5
+platform/external/libcap 772976b9e70d9701afe1a1484bb9809c747d544a
+platform/external/libcap-ng b48d5ddc7b24b693f92d1d10275151544a22578e
+platform/external/libchrome b3e1d67d8f1744ec90473178b042be66230c6bb0
+platform/external/libconstrainedcrypto 3f7c2ead52dbdc19b5077032831496e386ce977e
 platform/external/libcups 9d428efdba9c929546f67ca87640697d447af544
-platform/external/libcxx 02ad8ad9214145f918020b17fb9a0f0d3f86dd46
-platform/external/libcxxabi 2eddfdfc541f691d49a425d037aa5d4f32ef22dd
-platform/external/libdaemon 81ec75b62513a93cb795d8e137f6813e3f1b2fd8
-platform/external/libdivsufsort bae827bf007422bbdf68eeb3d5072020c114c4b6
-platform/external/libdrm 2f9aea0661550a43c3d2ac33a5bc286870edd34e
+platform/external/libcxx 1c231ba2834d6924beaa0c3c70864abb5f7be1e5
+platform/external/libcxxabi 3ed0b9e36d20b43c2f208e155532ba4b8f90dc86
+platform/external/libdaemon 6992baa2b708e1f4fc12daa7b90530bc1b5aaf1e
+platform/external/libdivsufsort dac727f1e1747b60ad0f3aa9826276f5d192bfc4
+platform/external/libdrm 8b8d0170f74607aa322ec95dbaad759cb89448b6
 platform/external/libedit 67e14dfc833aafa400a3aad8cb329cbaec503445
 platform/external/libese 773135272c86740714ede852a32ae9baa7d73254
-platform/external/libevent 45b76b9ca13ddc664c69c10ceb5f68e1f8e0fb2e
-platform/external/libexif 5a09fe45615e3e2e400efe59e0b4d12cf34d6665
-platform/external/libgsm 322a1f7ce1fbc0273ee951b4af998d6328ccf68e
-platform/external/libhevc be2db551c531a3118ee64cdcdcdad2d9453ddb41
-platform/external/libjpeg-turbo bd7903e2a5584fe8d4c1103da25dff429e77c304
-platform/external/libldac 15922bc3b3052b0928ba3996daf72d711f13fcf4
-platform/external/libmicrohttpd 86d3bdb95516ffdde278bcea3ff9258832c4688e
+platform/external/libevent f5930082c9827e9c3185ba104efacb5cbbaa76df
+platform/external/libexif 8f688823fc5399ab0f42ba2c8986cdd127dbde9b
+platform/external/libgsm e775ffccfbedc4bb525cb2b5c9280a1dd0cfbe21
+platform/external/libhevc 662b247f656379ca3ed253ddaa96b234c1c49841
+platform/external/libjpeg-turbo f5c8ca7bb600d1e5488f65c6d5447b19b18d899c
+platform/external/libldac 82b6a1abee84787b8fa167efe20290073f60db2d
+platform/external/libmicrohttpd 355802f3c150ada62b489a4d4bf70cb9b51a1e97
 platform/external/libmpeg2 458d7f1d066fafd3da8dfa910ba1c08246ac3d41
-platform/external/libmtp bd454345fe0bb012515f2ab41eee479801eada01
-platform/external/libnetfilter_conntrack 0c28f13f32e490b4983f8aad240defd54e5ed7dc
-platform/external/libnfnetlink 816c77d8974e9a4a07fb6ca9107eb0714188b49c
-platform/external/libnl 8328856ab141d154f4e1ab4b04375faec7661246
-platform/external/libogg 31242e4f5c2ab1f9b3914a6381c5a848a475a8fb
-platform/external/libopus 791f9897b6699efb63392781b21d9486ce2387fd
-platform/external/libpcap ba51ee392e9b2d5dd04faaf695b1da82488d0323
+platform/external/libmtp f5dd4c21b33cdfc3e98c8b4b4406f78dd1e2bede
+platform/external/libnetfilter_conntrack 50bf5f2fd4d05c6c62b17a6179947d5cabaef166
+platform/external/libnfnetlink 92233c8e4f58f331a2fe72f4466b791238a0ff86
+platform/external/libnl 77d957f743d9892a6918d7c0c110c038faa0c63f
+platform/external/libogg 140e2eec9db797d2a22e6e9cc3d75d0401e2a31d
+platform/external/libopus cf168b63a5c4766bcb663d9ddb16dadbc080e103
+platform/external/libpcap 7b5dfa333cd42dd542b92a8beeb65e53a20565a4
 platform/external/libphonenumber bf10aa4b035a9cf93b711d91f3e5daf153b11cef
-platform/external/libpng c85cbbda3dc726783b046853ca847884750b2620
+platform/external/libpng 62f5255389a91db9332ca2a1946ce8ab2a594d44
 platform/external/libtextclassifier 0b3ea84b83c3fa8108f9879d7f141445644fcde8
-platform/external/libunwind 41ba0e90f33fe3b5d1467e4fab5e0a709685c0db
-platform/external/libunwind_llvm 34ed2ea33c5cbd8799e8658080a0017fc702e538
-platform/external/libusb 95affad99635ac7c02606d6ff7d94034963d49ac
+platform/external/libunwind 9ed960a358e4606d761779d772dad39cb08513dd
+platform/external/libunwind_llvm 5a384dca2703a5cfb9198e0554f588897ce9baf8
+platform/external/libusb 1664027674d057c8cc85e9f88d48a029a80fa73f
 platform/external/libusb-compat 759481ae400d02fe99488dcdcd653b4f8139a39c
-platform/external/libvncserver dc66d75266c02ec07b0a6524db363d697eadd5c1
-platform/external/libvorbis fd3e33525c0fa5abffbc634c3a5ab56904adc733
+platform/external/libvncserver f663790c00a39f17a529acb1bfddd062655676a6
 platform/external/libvpx 85b36b8dd4432c721989b0b0ccbdfb19fe20be8d
-platform/external/libvterm dcdd338ebaf6f9bccccd7a7adb88379088fcebdc
-platform/external/libxcam 507c4324d916ecd164bbf410af7df4191ec2b827
+platform/external/libvterm 5b7ca2e7bce00d2fe7b149203341d92ead575f76
+platform/external/libxcam ad8c3650d3db1b9c16d91793a844d1b52dd4d9bf
 platform/external/libxml2 0e97475b090aa7418dde06aa234fdf6ac1be46d7
-platform/external/libyuv b189caf1d7013dfa3154c36535f1dc1792f35609
-platform/external/linux-kselftest 50531906ebc0c00be9faeef47873bb0ff35eaac8
+platform/external/libyuv 237d98acc923bd02ed6d04b359e43d9d5971cbf4
+platform/external/linux-kselftest df3fb219bca4145d61b86c5dd946d9413b88be24
 platform/external/lisa c6bbd727505cdc697106c62c13cb48af92c6f5cd
-platform/external/llvm ebb0b5abae2699e8a2cc0f0251621fd9ee57e7a2
-platform/external/lmfit 574f31928be238949e2524418248e6f943a70140
-platform/external/ltp 87d8c0ad55716d0115e32683d5b5c8be5b3defa5
-platform/external/lz4 aef7cb17be9f0d817303fc095d0807fa0b08e264
-platform/external/lzma 9186d4157ac38b8c314ffe99ea031faff87de996
+platform/external/llvm 7d1da5dfad8440c6ccfbcc687e25c8fb79221067
+platform/external/lmfit bcde41fab768e29467ae0496755576c39bbd81db
+platform/external/ltp 0f529bd7fd770f2b1964eb2825ce534fe39bc410
+platform/external/lz4 db96f7cadd60d24ad028d50d82d7fd9ef1aa12ac
+platform/external/lzma 7851dce6f4ca17f5caa1c93a4e0a45686b1d56c3
 platform/external/markdown 06a8f8b914d477183f68b84424bce8ff4dae7e84
-platform/external/mdnsresponder 9c56330ccc56dfcc48b4a08c94dadd2a0c1ff86b
-platform/external/mesa3d 7347c4264052920b2eb02eee084809760ac7c9a2
-platform/external/minijail 542342125831c69eb9c80265cd865250579fa19d
-platform/external/mksh 827d6b8caff5c6c06fa3688ebce010df747b67b9
-platform/external/mmc-utils f105db5bd1934f0c3e5f74bbc9a5aaeac7a6bf2b
-platform/external/mockftpserver 48ddf9e8c10d2b2b956924f3e6da4b9d13787480
+platform/external/mdnsresponder 33e620a736939be478f009640acf1e1e9d709450
+platform/external/mesa3d 8ead3127aa0d1259aad39131d450f50f8a2af7e5
+platform/external/minigbm ff234ae4223e32e93a2c3012524e1129d1e185b2
+platform/external/minijail 89cbc32f005628664e7e5dcffd23efc974fa0a52
+platform/external/mksh 1b542a22f68163ee8acf14c227d70369e2117d42
+platform/external/mmc-utils ecb66312dd2b0d82d462c14af4acef00d93f2375
+platform/external/mockftpserver 799e0e134b61f160f69fbf84d5cbf24861e5e78e
 platform/external/mockito c3b72fb406d776a62e3c8f3b8ef26c008bfda071
-platform/external/mockwebserver 5ea6d395c59627f6d1c2aa091e77f9cd83d4e886
+platform/external/mockwebserver e441903a5f40ab00a49d76f5e9570a04ea80f74e
 platform/external/modp_b64 6f11b5c9b4ffb38dc9375f3cf344da4300a2741a
 platform/external/mp4parser 88bd0c6cfa7fcfbbebcc15c2c565f714cb36b065
-platform/external/mtpd da21b1ada7b96b997fbf334a9cc0c79013755f18
+platform/external/mtpd 123ea675fae1d24ce3f498609644efa6ab832701
 platform/external/nanohttpd c6783c32331b3fe78ffd5077e7f180995d0e268e
-platform/external/nanopb-c 3b20283285e6ae76ba17167d20eaeab0461ff0d9
+platform/external/nanopb-c 731c7139ab46dd0c22bcbda506069e5454e9b2c2
 platform/external/naver-fonts 91e6e9f94d1d769a8f742649674149ba98ce7d45
-platform/external/netcat aeb0df532cb8cc5a7de4801ec0d646642a5d1548
-platform/external/neven 0898d8987ec7214861c8b850ffd809f0f982dfc8
-platform/external/nfacct 197aa74ff48c75a84d7169e840f7d87c7b367579
-platform/external/nist-pkits 9b65b98ff582d7245cf21f15ecddf25067eaccfc
-platform/external/nist-sip 729ce800312d781464944ae7ab04a721861096c6
-platform/external/noto-fonts e0a19c7a9c478969f3c28c95bc2ccfc402ebeb86
+platform/external/neven a199086bfafc4503b9a3f27557ca1c554d364366
+platform/external/newfs_msdos 92fe5db069b92f149c545fbb4114cc5b26bc97b2
+platform/external/nfacct e1fa89c56893352381e6c59b38a3412b087c3f91
+platform/external/nist-pkits da9a84e43e1710b4512825ef31bffd9d6b61df59
+platform/external/nist-sip f8d1d27b97b57d6a4ea8f242230b5ee2f3522612
+platform/external/noto-fonts d20a289eaebf2e371064dacc306b57d37b733f7c
 platform/external/oauth 49f3624a6d3307b640a012f15b94d04174473501
 platform/external/objenesis ad9b610acfc6216656e1913a6561302dea5e3c8c
-platform/external/oj-libjdwp c53a7db69ec9c262fcc227403e1336a1f48bfd01
-platform/external/okhttp 77d960f3bba09b4a3c53c41bb7a8e3cad6dfc57b
-platform/external/one-true-awk a4bf73261a7cac2bdd32e915b53449e82f86f288
-platform/external/opencv b34c1e94c5e770c4e80bc621d2a84b35414d9dec
+platform/external/oj-libjdwp cffbb6caea6fb2413b92566d436b48c24b6a8fa9
+platform/external/okhttp bcc13b52d1653d1a59ec4dd8ba14c5706f879690
+platform/external/one-true-awk e3d2bd506a8bba84603878dfc57396f7d1a27d12
+platform/external/opencv 68d29651a7b1b7b81822d035d222202adcf5de5d
 platform/external/owasp/sanitizer bbfb25464ff30c5a62dce351d719a8c533afb2a3
-platform/external/parameter-framework d5944a530eb24f71ac50a3faddd1501f41b61bca
-platform/external/pcre d895c07ca06b0ab7bffc66b3bbcc084ce2fc80bc
+platform/external/parameter-framework 108db75a59dbea562ac4bcaf8c6cc862c4919af0
+platform/external/pcre 04fc5e3e1a393db61bd49e016ba30226dd0adc71
 platform/external/pdfium a0e7c81674a57d607c7cfb668c928d046d83fb1a
-platform/external/perf_data_converter 77162c97e59b1e6b3895a97b3b9dfbe45422ad3f
-platform/external/perfetto 4d0f6c810ab151be094113ac1d69eb3009f0208b
-platform/external/piex bb217acdca1cc0c16b704669dd6f91a1b509c406
-platform/external/ply 5b6a08a9e9d1916e83f7b7fc177f1e97271e80f6
-platform/external/ppp 90da8c8f15db2c82aece6e4085aaf9c116d481f2
+platform/external/perf_data_converter deddae1a418844bc53514000c40ef39239c5f022
+platform/external/perfetto 1ec93d0df7d1fc69c1901a7a417e50501df28105
+platform/external/piex 49b79bc8ec66538027e018ed655b27af5b77a39e
+platform/external/ply 8b4275169c7b4efb46d034d409edac37b9b6df89
+platform/external/ppp 5f356644965d9a36b283db1611df129474a09330
 platform/external/proguard af893cdc1242e4cfbeeb10e2323f9a689a825177
-platform/external/protobuf 998f3ff954fb657b373a26a891bb310d6e3db355
-platform/external/puffin 9dcf835a31ffdfb962c803a7f14e396eec86d49f
+platform/external/protobuf e9ab58d7c0a173e058cfd3e15f1a72a4c0c8be93
+platform/external/puffin 5997a125a477cc9b4b36348c698fe2ca7e8e5c01
 platform/external/python/Pillow d9c0fcc955e4fe0662cd0580a412a20cb328f039
+platform/external/python/apitools cac27a14e822c8d31e4cef2886899dfdb0a3168a
 platform/external/python/appdirs 0d9b51147cd55a9ee45382c534bcfa4df4a2e3fa
 platform/external/python/cachetools ee4588a2a8743a550de2a7e18d306d5f6a436702
-platform/external/python/cpython2 64a408d87a2343dcad691cb416014bcada2ceede
-platform/external/python/cpython3 ffb56c065b7dd052ee7677ae3d6b7e71654dcfd8
+platform/external/python/cpython2 297d98655286f56fe3789abdaaafc3489398db4e
+platform/external/python/cpython3 3078d6a7cda697a5180449d414a8a7d9794b4975
+platform/external/python/dateutil c0ec852fca382f255d75271f8bb0244fe21956f9
 platform/external/python/dill 01988c5fa063836e457bdb7134d4d300d1276e5e
 platform/external/python/enum 6706fcd5d4054a9dcbd517142f2be8c9498fcecc
 platform/external/python/enum34 ed56821ca06c8ba80bc083069c9f5672cf834e2a
 platform/external/python/future fa8e703fd259f8056cb817219041eb0e20c00ada
-platform/external/python/futures dc15686d2d289866234948689a338891068faef2
+platform/external/python/futures a48c6eb42244dc9070bce6670566f4823ff6c11d
 platform/external/python/gapic-google-cloud-pubsub-v1 44cde1e4892ce518b1927ab3f8f45a22f0f67a1d
-platform/external/python/google-api-python-client 6e0c0af3fa414387cd2ecc22ba6cb4eca47f171e
+platform/external/python/google-api-python-client c8dec010968fa2694508fed6a7d282d71fe5abd5
 platform/external/python/google-auth 51bbd38e9129785729a71abea8ae0bae1ac9784c
 platform/external/python/google-auth-httplib2 c4201f1bd596e5b17b7e461af2dd8b0d676834a6
 platform/external/python/google-cloud-core 1b76945730adf9762a680dde8facca6acd864df0
@@ -288,379 +293,369 @@
 platform/external/python/googleapis 67d384ef2d9bad424bce05c79899a1c6e6baee25
 platform/external/python/grpc-google-iam-v1 6ce59f644bd5e99279b020b6fa9b8851fccb7c50
 platform/external/python/grpcio c059a54af916df11ead84a19bb4e2faca19e3682
-platform/external/python/httplib2 90f880038c1d7bd50fa7b0538954b24fd12da231
+platform/external/python/httplib2 290292844ff09dccbaef7d60ff10aa75f119e6bb
 platform/external/python/matplotlib bbaf4f40479b90f583faf080bb472499439ee2fa
+platform/external/python/mock 55f7b251b443af200e47421dc993b4af96fcb6ad
 platform/external/python/numpy b12e87119e636214f5d41688ec4ea9ba4b4e58b5
-platform/external/python/oauth2client ea7213cdc8de40a4887a68aff85a1729884461d0
+platform/external/python/oauth2client 5bbe4a5bfc70651357a235e1a51494a059cbc179
 platform/external/python/olefile 1221d9b12619499cd798e73598a074b8cb6d7b31
 platform/external/python/packaging 28b23ad183cfe53c9f848e1cd55595dcd11b22f8
 platform/external/python/parse aebfe77d79bd5f84e82bbb23bc3f0e7ce817064e
 platform/external/python/ply 2b3c386a04f92d292ea289ea10d6508a3bae2c2d
 platform/external/python/proto-google-cloud-pubsub-v1 ea95753aa8bc0d8d7766a36917b488b331dbabee
 platform/external/python/protobuf 6e9cbd59578771e8005e4d3ca73c167521f4e4d5
-platform/external/python/pyasn1 060cf6916bdf4a137c20fab13ad81f84c99941e2
-platform/external/python/pyasn1-modules 52eef53175cb9e051125555c187fd7ac95464654
+platform/external/python/pyasn1 156b75dab22d623d4bf54e47fced17d829a536b4
+platform/external/python/pyasn1-modules 66fd96d6e96746dc30b03ff9ff2f7d954925c755
 platform/external/python/pyparsing 349ddcef3f7641faf4d11327c959da6568b84635
 platform/external/python/requests f51a28912b8571f0276a633a85578a8ded94d37c
-platform/external/python/rsa dbe61a7ce8c6b3a1ffbf632590ea05c61231b687
+platform/external/python/rsa bb1c2dedecd5abed776627728e9f7a02d0665727
 platform/external/python/scipy 626da5975f8d3d1ee9de56180b57b6dee8ede15d
-platform/external/python/setuptools e493fcf90483ade4b2a5ac331a4f89cb15ae3fd2
-platform/external/python/six f91473d8b8dabb218500dd05582fc2d5c5481143
-platform/external/python/uritemplates 4edb22282e4cfaa9250d3fdb2d2f7be9c0a34fb4
-platform/external/replicaisland 22d02e1a04ec54ee210cb3ce831581840c849ba4
-platform/external/rmi4utils 0a2c74f76f051b408675f6c97d7fd972e39ef8bd
+platform/external/python/setuptools 21d497cb588c40b2a891d9a862f56e108b427e4c
+platform/external/python/six 8cf04836b3cfa9194ca6e33ce19800b86f4e405f
+platform/external/python/uritemplates eaee30993c1cc09f5b69a197f5fd0b824b2bf009
+platform/external/replicaisland 25276df916d2339d2345af48625c913963965402
+platform/external/rmi4utils 2f429b74b8902488f5bbc0f2f44dc75f5ccb5a61
 platform/external/robolectric 18cb219ca73cf67e5cf3d29a820441fe330c226b
 platform/external/roboto-fonts a49bcc0972b3772872e36ca06817e950e26593bf
-platform/external/rootdev 83dfa81a44b8266f8f9280bd3a206dd8c2474407
-platform/external/safe-iop 4e20417f767dedadd684930ff1517b03751b3af8
-platform/external/scapy 34a73a2c760300aa79525e0b1ac42732e751b458
-platform/external/scrypt 46c951675db25fd880bb97ef191108dd0746d13c
-platform/external/seccomp-tests 7634acc3063de4cd33df43fe571e41f69eb416a9
-platform/external/selinux 638e19a88500511b77b18f2243f409625716e715
+platform/external/rootdev 4129a9fb971c629262bf8925b1f1a2fcdb6e0e7c
+platform/external/safe-iop b805514f31a231a0e78a18f296c0454fcadead1a
+platform/external/scapy d6b412cfde0442650d6a05884e822a396702edfd
+platform/external/scrypt c37a330865ad150a993d2c610867312bb2a91e35
+platform/external/seccomp-tests 910e9a11450f6b50463fc7a5fa4a77823d40a326
+platform/external/selinux c712b9cf0790e21ca2ba0a7da9613a6de04f9ae9
 platform/external/sfntly 0bad802dc17fc283171923479960d6dab6342d3c
-platform/external/shaderc/spirv-headers b252c470366118af0db2b4395217bedffb4c2e01
+platform/external/shaderc/spirv-headers 492abb45bbdfc0c5b9ffd00f16e53ab628305da9
 platform/external/shflags c4876e01829b8cf110ee33267bb1bad1f8ebb51d
 platform/external/skia 5000290e5f93ddb048681e6efee908f119e1dd90
-platform/external/sl4a 1b8a78713d5b132be8fde77d1fac4094a1d8d077
-platform/external/slf4j 8ed83fa0a47fa8dfd5cc6b1437109beda7182d3b
-platform/external/smali 9cd7a6e0ace477a79596aab75c0a52fba03087fb
-platform/external/snakeyaml 92d847d5a13f5ebb356706bfb3aa0126f8db436e
-platform/external/sonic 3a512d9bb9f68c451221e72e1e31008ebe38e712
+platform/external/sl4a f2d8f1e03c2dd78e301f59dd2a3dc1c895fdb11d
+platform/external/slf4j 22558c51ff8a9a9d8314a13febb665083bb5986a
+platform/external/smali 68270cc4df9d462b53e4dd2e9395f698b792b2c8
+platform/external/snakeyaml bccc0c9bc191e16560be107c78aa2d685e9077a4
+platform/external/sonic 0acda6c822aba430d93044bbf2ebb9680b8969c2
 platform/external/sonivox 6638d2f9006dbed5a6017ad997601052a12cffe2
-platform/external/speex f79eb68d06038b070b5d903100302167b3c07626
-platform/external/spirv-llvm 6e322f696ac5d2a1fdfaece2db39b1b088bdbae9
-platform/external/sqlite b8a64e4e268cda02f7aa32a2e248853401d0014c
-platform/external/squashfs-tools 1cdde05cc0eaa50b8bdc2377a12b49412a5c52a9
-platform/external/strace 5b3348ca5f0829c9149c01ade3904c8ff828bb62
-platform/external/stressapptest 00ddbf2caa811862fe0936d99eefd2f630a056e6
-platform/external/subsampling-scale-image-view a067d8ec0a84d54b550ece85f19e185e0bb456ec
+platform/external/speex 15bd660278835adb078df9860cedb3a8b2e74043
+platform/external/spirv-llvm 63555c7bb790516c89f54d661dc8ea400dd8d2d7
+platform/external/sqlite 9c66bf8830f54f2ffab3fbe0bac62571cd1dc668
+platform/external/squashfs-tools be62439693a63ea115c34dab1eb1ef63c55aaf1e
+platform/external/strace 85c0c9e38fb68e015d3747de58d1b7f981090e44
+platform/external/stressapptest 13eed3cb064308d4b534d921a30cad29e7479575
+platform/external/subsampling-scale-image-view 45ca08280baebd7342764bdbc9b520d7183d6ad7
 platform/external/swiftshader 2d425170efcd582c82372ed23bd5d7d1b3ec86a1
-platform/external/syslinux 460c252613d826547857230b1d689fcf76a01b0d
-platform/external/tagsoup b40d1a29dacfe6def3477f20c3b59f3d651b9508
-platform/external/tcpdump 05033025e46c1b8cb06a0128de7d56d0d2bced6a
-platform/external/tensorflow 55302b513b014bac50c6ffff381d06fab1c1a4d4
-platform/external/testng 09abf148e2c59df46e7954324a7f5f237a69e8b4
+platform/external/syslinux f867c52e6ee1fb0dde495f79352928e1cd1314e6
+platform/external/tagsoup 4145e0953a8de225031c100f72e2d463fe1a9f4e
+platform/external/tcpdump 163c36132e05362efd0b6da8e3bbd6fb70d33c81
+platform/external/tensorflow 33669739e0e7c5bf066c9ddba4528caf7753a853
+platform/external/testng 9ec8eb049efd3113c495607e92a10374e380f3aa
 platform/external/tinyalsa ce0616534e7fcfd9ba4857075a08e54e0c32424e
-platform/external/tinycompress 1926c2138d9825d29d07b1532c28dd85ab833c24
-platform/external/tinyxml 119ea69917f3e06692de2a4430c291487eb74eba
-platform/external/tinyxml2 c4a167426ff55cf782aca27dadfa7d5c4f872efc
-platform/external/toolchain-utils f1a93c225b92dc0059e7d7e2de7c7bd0a493e23d
-platform/external/toybox 94e30f6a1edecea393e27f1b1ac5423fa3676bd3
-platform/external/tpm2 f52a9aae46ca182174d61342791d0d519bc51629
+platform/external/tinycompress 67eecda8d99419fab4482154a5e6b572f33dc80b
+platform/external/tinyxml 7a1184d68cdd33245efc9875b237fcef2c9d2c54
+platform/external/tinyxml2 dd7a9809cfc623287da2d21d766203972b953649
+platform/external/toolchain-utils ec1dfabf71cfe08796ba80ae03f4857b2797f5f9
+platform/external/toybox 1ba78f42899c818e69625cc888bc5ae159391265
+platform/external/tpm2 77039dd71222236a5cc653a3953b0758039234d8
 platform/external/trappy 31ccc081929e27f780a87a94deb20e4333c6b323
 platform/external/tremolo aef7026aea714341249989c8323a9c62eddc481a
-platform/external/turbine 5081441ed92b73dacbbf1a33f95a1495f0e831ec
+platform/external/turbine bb281afdc41eda16a8277dcebdbf817c32086960
 platform/external/unicode 1d15b7695581bf8fca39b4486615b014e7b72ce6
-platform/external/universal-tween-engine 620f0c9b0697509e12c25eba00ffa00683f5f2ea
+platform/external/universal-tween-engine 0ee49cdeeaff478c8265ee7a81c1b235eb31c873
 platform/external/v4l2_codec2 a97ef533bde3fec64148b6fed0897a2101dd3ef5
-platform/external/v8 b2a709533213472c32e1c3928d0a285e64ef5dde
-platform/external/valgrind 4d492393733cfae4dd97fbc8971207905b37ff75
-platform/external/vboot_reference 0138d45436f0c3d99bb9602b1118c177b9195e5d
-platform/external/vixl 9b00771ae42e6ca9031ce5095ae08cc9a2feaa72
-platform/external/vogar 8833c43ac9e0c119567226f3f2955a215c3ae237
-platform/external/volley aee0d184daa41c0abb5bf88580ad6cf7fea63d72
+platform/external/v8 1387abbbac0739bf85336f4fa58d6b541e7667aa
+platform/external/vboot_reference a2516840c8b010ae5176680877dede3d34e135df
+platform/external/vixl 49166133cb911098ecaea6ab04ad48c8b3af5720
+platform/external/vogar 2a875cf2294f81cb0880a5fe5f7864a952fab39f
+platform/external/volley c409124cab8fa87dab8a3e42ce34844f5a4fa7ea
+platform/external/vulkan-headers 85aa4405c0ef761687f9c31519bd2030921ca64d
 platform/external/vulkan-validation-layers ba7c9df0abca79aa6498a2f8534ace0b3d7a059e
 platform/external/walt 37c75fea5674f9430f77b0e952a1969c7c14a59f
 platform/external/webp 4756f2b049f23e52664067b7a24666d13dec3895
-platform/external/webrtc dbf4e663744fae37318982fe5c394cf4f97e2a76
+platform/external/webrtc 7209da4601ac70bf53476144e6e4e77f1e3cda82
 platform/external/webview_support_interfaces 51b18905eadbbdf201d5e3864fe854da2c5d3719
-platform/external/wpa_supplicant_8 ac6b90bc7c79c4c9e9d70ed00345d608c4fbe4d1
-platform/external/wycheproof 0817256c4f6bdee8715a5706fe75ecf8249e3648
-platform/external/x264 c195f001050699430ca8b0d9c99b2dae9e10852c
+platform/external/wpa_supplicant_8 a0685c87c833bba74f77135c179b535f3779d724
+platform/external/wycheproof beb7117fd943b4b8be423e807b6ed8456fd4188f
+platform/external/x264 d2c9edd9c883dce2031be118737a09bd04b25908
 platform/external/xmlrpcpp c79d4eae757a81270915c2a7cf01c81b8d42785e
 platform/external/xmp_toolkit 42ea4dc6d1fc2206a7778029070ed9213e3b0fbf
-platform/external/xz-embedded 2d94df3b0ed749902696a1d4635a8597621624fa
-platform/external/zlib 0d8ec3aa938b73192a8fd69f9cd1b2568f12c4ed
-platform/external/zopfli 326ac1cbe4782894dd1be564bfcab361ddd4f8f3
+platform/external/xz-embedded 2ac7dc3167c394c90479b1cd48562b58570c651d
+platform/external/yapf 5a3466bc50051e3676ef9859fd97b0f85803e1ee
+platform/external/zlib 49d47ec37f95800213206f4514152db185494937
+platform/external/zopfli 4101d743cef9e1161bfc9ed4d3c8d4910c6346f7
 platform/external/zxing fedf8f2d8099bf7bb85dc3db8699343d56617deb
-platform/frameworks/av 2178e2c8103cb29cb77cf6b92dc216d6b9d538e7
-platform/frameworks/base 59e139a767b5552a0c42ce236ff59f843900e842
-platform/frameworks/compile/libbcc 3260678523ab0b68336b607905a748395685c6e9
-platform/frameworks/compile/mclinker b8036c7ba0fb200ba514c69add80b38cbba39cdd
-platform/frameworks/compile/slang 2faac495b3b8ba3ecb9dbbdc45a5c490d061a840
-platform/frameworks/data-binding a53fdf4a28693a8ebbc5d3fef1e44423a560dc90
+platform/frameworks/av 9d431ceffaad7334f61971abdee8e8436ffc2447
+platform/frameworks/base 10c593cf7f30ddea32889361c81ef06eabaeb6b3
+platform/frameworks/compile/libbcc c14da8b46cc800e444452e7c09365ad4d312aed4
+platform/frameworks/compile/mclinker 309a19c703fc09b024969fcc0f43d78e21ea6d07
+platform/frameworks/compile/slang 5f12f973dfc1baa98d484df269f8b251efcdccab
+platform/frameworks/data-binding 60f806584f0796fdfe51f263bc80b429146740b1
 platform/frameworks/ex 2fd9d9cb046b212889797eeb8aa92e25ace172e0
-platform/frameworks/hardware/interfaces 2f6f803ecfb198c3dc0a51f4d18db9593ad11159
-platform/frameworks/layoutlib 77b8db4414660f363974e3d04e3b26c66692b355
+platform/frameworks/hardware/interfaces 484ac5fb7f04ff491952207deb3cbec42754b445
+platform/frameworks/layoutlib d2bff74b243f47f81c0c8d39018f63c9664931d1
 platform/frameworks/minikin 281c26a92ffc8fdcea55941c0e7a02b90dca79f2
-platform/frameworks/ml a66bdf89ff6b3ce5d32576b83c1d579f7b008da4
-platform/frameworks/multidex 38be0f4882057d25c26a4aac93671a39a8cdff02
-platform/frameworks/native 93a0198e5f7a90150549f74fd2080c730f01b138
-platform/frameworks/opt/bitmap a0d4e3108663202564a6833b76770075b8e5b767
-platform/frameworks/opt/calendar 03dbebcdc8c9dfe18b51fe129ca03c846b3a6ff7
+platform/frameworks/ml 242b6166bed1dbe608fe2b4519f643f20d4ebd29
+platform/frameworks/multidex 0572c7e8a96d3b569813a57b3c5a256bc22e962f
+platform/frameworks/native 16b9f38d2245028256954490458971efe65af329
+platform/frameworks/opt/bitmap 05835ac05b95f614d43c9289dc070e13b7fbe4ca
+platform/frameworks/opt/calendar c0246198be80c6a8185aba01707e3c6ecba21222
 platform/frameworks/opt/car/services e22ee2007472cff08e324594e9954f01d7bc30d5
 platform/frameworks/opt/chips 4a0849290eef9f05b95a1a631011fbe27a03e1db
-platform/frameworks/opt/colorpicker 49c28fe5aa792b7ffecee2741991fd690ff6c78a
-platform/frameworks/opt/datetimepicker e2984e35e933f8b804a1007f0ab6031f9c707450
-platform/frameworks/opt/inputconnectioncommon 3baece9b20fa480da46d860acd7320fd9eee3386
+platform/frameworks/opt/colorpicker 01814056abd52eb9a39023eefe2799b49ea1b393
+platform/frameworks/opt/datetimepicker b82ce5be987fbae68ce3c52e772cbe0654db4fe3
 platform/frameworks/opt/inputmethodcommon 990fcb1a6dbb5d1204cc8ec86e4bc3f691f4aeeb
-platform/frameworks/opt/net/ethernet 36fd22bb9615a313c68c4f74427e7f8f357934b9
-platform/frameworks/opt/net/ims f3ee4138f61e2610c2728c1c63d3ebeff61ab8cb
-platform/frameworks/opt/net/lowpan 4010f14244d4cb42ebff80f91cdefaf1a4f1791a
-platform/frameworks/opt/net/voip 7c605681423b7f75c7fd512bd6b34f0bb49b2c90
-platform/frameworks/opt/net/wifi f24354dfb99eca9de897e258f3818045c9464eab
+platform/frameworks/opt/net/ethernet 5a4b8793d488c4e431051d489b59d636e41843dc
+platform/frameworks/opt/net/ims 3bb2a9743c0c8f806f406bd6fff7cb83c4d83ff7
+platform/frameworks/opt/net/lowpan dab952b5d0a08d1e82f60dc945a683a40bf50101
+platform/frameworks/opt/net/voip 9ca5c69262083971903029063647378847c98877
+platform/frameworks/opt/net/wifi ff93379e679c2fb292909cc177857cfa869166ad
 platform/frameworks/opt/photoviewer 569bcb26d0dcf2fa98d85a8c400ddc8fdf783fcb
-platform/frameworks/opt/setupwizard 9ac186c5e9fe85085946a034c518e234b9a20dde
-platform/frameworks/opt/telephony caa67c8a19d8f0e89bf893afe67e9bfaba426d56
+platform/frameworks/opt/setupwizard 878bae4e322d98a52cc1e44b0f17434607cab18a
+platform/frameworks/opt/telephony 1319e5b0a8648e89fdf1d54caa6614441dec7380
 platform/frameworks/opt/timezonepicker d8ecd96676e64af62c9c252ba274ade4b416ebc5
 platform/frameworks/opt/vcard 3adb98b7e9b604ca9cbca46c778abcdc2692f330
-platform/frameworks/rs 0b62366ac13908dd55a69c0b6be1078c367798c7
-platform/frameworks/support 57f7e35572a20b6ff4bd99fb714e2efcbf8023bb
+platform/frameworks/rs d0b9241f66b00798d5d39f049517074733479b19
+platform/frameworks/support 9791ac540f94c318f6602123d7000bfc55909b81
 platform/frameworks/webview 8ab2b2a747942e08cf8c3591bf2f8f783602316e
 platform/frameworks/wilhelm b117f433055c3c0a18d9dc32225ea7df98aca97f
-platform/hardware/akm 28bbf9b63a51d0830d3f64d16661d811bc481139
+platform/hardware/akm b8b06ce000306a7ecb418840d32e90b27dd76a71
 platform/hardware/broadcom/libbt 5763f5e96e589694f704ba6baf1db383866314ba
 platform/hardware/broadcom/wlan 06485a95d7b250cd6271baa8d70fbb9588c7091e
-platform/hardware/google/apf 17c1fbc9e98e380e508fa83d0f927bc742a5161b
+platform/hardware/google/apf 18d719278ed5d59ade64a814e846f71a71244714
 platform/hardware/google/easel 5edc9cd6a18ec1449d7de78e29829b2eba56917b
-platform/hardware/google/interfaces c4be1ab47cf5e5685451480d9ef394cee26a6a59
-platform/hardware/intel/audio_media 0e60b77af3fa70393ee9fa0e80811d1b39c5f6fc
-platform/hardware/intel/bootstub 206a2709df91f2640925dd199558a10a09701a84
-platform/hardware/intel/common/bd_prov 8af329f2d2b54dfcfa84051d3ce1fae95f79011a
-platform/hardware/intel/common/libmix 4dec89d47fde128280b9507409a0e3da0b7cf990
-platform/hardware/intel/common/libstagefrighthw bfc9bda74643eedd4414e996d2a089a36d9acb24
-platform/hardware/intel/common/libva a49a95403f6b214f91fb9c6251dd7440d5069d48
-platform/hardware/intel/common/libwsbm 43470eaa35b14e3c723caf5fcdeafc57e3670ffb
-platform/hardware/intel/common/omx-components d966e80c48c6e6269f42c362fcda1216d74634f7
-platform/hardware/intel/common/utils 9831abd60e70ef4cf50148068f25d3b78874113d
-platform/hardware/intel/common/wrs_omxil_core 689411deff0f97b7904e86a371a92dfd2605177c
-platform/hardware/intel/img/hwcomposer b2fd52839026be1ac1a2df1a4bc2bb76d0f95689
-platform/hardware/intel/img/psb_headers c29e8fbcf9ada93e122ca53f9c310a0fc6a7ad06
-platform/hardware/intel/img/psb_video fabbb665d0e0aa1c4286acd423cc1e572d10d684
-platform/hardware/intel/sensors 68dc9e70b79dacddc4e0bf00af0de7f764b04eed
-platform/hardware/interfaces 2e38b2c0f640b29620d1e6a74b83dc4978c827f6
-platform/hardware/invensense 93554468123313a9585281d6c7e2509854ae1142
-platform/hardware/libhardware 7904e9c2d777674adf3f71f438001824773ef323
-platform/hardware/libhardware_legacy a0f12356762398542c70ee4f5a840b66c6adfdfe
+platform/hardware/google/interfaces a818cc52f72912a6e97aef774c90964bcbd43192
+platform/hardware/interfaces 9d0f0635b993294d50c8b0341aecde274c57ccf8
+platform/hardware/invensense a8e01ac3a98bd2d5cfa2eaa60c50afd07decf3b4
+platform/hardware/libhardware f8248731abd60b96493f06ef7c7a6f1eea2b7793
+platform/hardware/libhardware_legacy 12e55bef5b4440e4f80e3da98185d2f46372840b
 platform/hardware/marvell/bt 3f33d194e8300816b94d1d7b68b1d48c8f903251
-platform/hardware/nxp/nfc ada6feefd3823cf5aa73f9bc7c58e5fe535fee4c
-platform/hardware/nxp/secure_element 16b9be106592c998c1ba385bfc8deba860dbd579
+platform/hardware/nxp/nfc 827d708109a69feb196e7b2116716c4692b72026
+platform/hardware/nxp/secure_element db1c279bbc9342ddffae8c131a6cb709279115d7
 platform/hardware/qcom/audio cd3ede20a188f30a7e6bfbf5f0ea056d19e744f4
 platform/hardware/qcom/bootctrl bb80b0f386d7babd8db5ede168ecf74c1a9a9a4f
 platform/hardware/qcom/bt 78628913ee1bd0261107591b59f46a94318c6bf0
-platform/hardware/qcom/camera 324ee0fd8c275febb65d5cb5112f836f3c9a67e7
+platform/hardware/qcom/camera bcb0f6a889bd59d971140a9e927417dc7af8101a
 platform/hardware/qcom/data/ipacfg-mgr 41ee806c7f63745fed424c93e60fe9d3b97155b9
 platform/hardware/qcom/display c24cbf382980e870c5cbec73a61b4922601a9242
 platform/hardware/qcom/gps dae62bdf70584595e7c971c3e3aa14da319278be
-platform/hardware/qcom/keymaster 7516becc84b834b8cf37821f46ef3c4136ed2b7b
+platform/hardware/qcom/keymaster 2c1559041d5d30bf4e1d622b2c6924facf2a4daf
 platform/hardware/qcom/media 3e4f4b4c6cc2db6c0479063d139b2f78c2e6d50a
 platform/hardware/qcom/msm8960 c25a431842a26b5756b58a9d4a42c776e0457ba2
-platform/hardware/qcom/msm8994 50139f3d2f12021038df9ea29c2610bf1394eab2
+platform/hardware/qcom/msm8994 8e0383f6f41a2c49461f381c8d066ea21b20c674
 platform/hardware/qcom/msm8996 4ac9a091c7b67be475fbf402f28ad680411c9752
-platform/hardware/qcom/msm8998 67efa21cb09fec65796808bf47b2234c22245059
+platform/hardware/qcom/msm8998 62a014cebe08e782bd43263ae9e8a53cb49d5f1e
 platform/hardware/qcom/msm8x09 9903d4e5f088248da507a42db251395fb6cc76b0
 platform/hardware/qcom/msm8x26 85c1a5282ae28663335e55ce96a4c0487de6c578
 platform/hardware/qcom/msm8x27 8ff5c0057cbdecfa09410c1710ba043e191a2862
 platform/hardware/qcom/msm8x84 582b414269d8472d17eef65d8a8965aa8105042f
-platform/hardware/qcom/neuralnetworks/hvxservice b887cf567748d89af62ec2aed5397748265f8758
-platform/hardware/qcom/power 251977ebbb5b2556d37d8bec381354692c9a00c6
+platform/hardware/qcom/neuralnetworks/hvxservice c6f8e32620de280b28e842d42ff0103da0e5ce2f
+platform/hardware/qcom/power c6b93442fa3e69b7991049835aa5051e7336d5b3
 platform/hardware/qcom/wlan d914ba9acacc625a01714eb135710aa62f0ac058
 platform/hardware/ril f6b6550f24f9b531b0050cf05d962de7dcbf8523
-platform/hardware/st/nfc 9459c897cee0ef18438401173d8f6623aa43499c
-platform/libcore 2399174cc54f122353cc06aa77e51fdb68917c95
-platform/libnativehelper b8b2e34518311e776dd61982897a35f0b520cb0f
-platform/manifest 1e9c194bd60a5136093b93aa7b1de77b0e8bcbe8
+platform/hardware/st/nfc 222568da39d40ce391ee8903bd0f7938ba6f6480
+platform/libcore 0c2c9e1b8cbeb4db50f8bfff402dce3933039670
+platform/libnativehelper a219963f97ae58de5529b58c3dbfd86bf8ca8167
+platform/manifest 83ce74a60ff378c5bb903626a87418a817357f82
 platform/packages/apps/BasicSmsReceiver 1de7ec51f2e65f76597688328764769e4c8aed42
-platform/packages/apps/Bluetooth d4ce2ea25f0f0b73026a4262dfe99a5c2da1e148
+platform/packages/apps/Bluetooth 24cb9704c2080267020c72ee4aaa999ca5322a50
 platform/packages/apps/Browser2 ba4deaa4ce223143e699bb530035c5d736ab161f
-platform/packages/apps/Calendar 956525423e51315015d6902e3b9487e72e9f4885
-platform/packages/apps/Camera2 6c904ced8128a591ea31044a7ab4b96d5a13dc5d
-platform/packages/apps/Car/Dialer f9b829821a62a7e9c0363a438ebbb57f6bc0b985
+platform/packages/apps/Calendar 7b051c6e9ef7922f08d9ebf664ac3087d5fa0552
+platform/packages/apps/Camera2 c7a2095139f6efc634b7063d43b3b3c26a9d6a99
+platform/packages/apps/Car/Dialer b04568fb1e3fff7959e68b238c7447e5d297469a
 platform/packages/apps/Car/Hvac 3613f0cff4106a56b84f2b060d990dad2f6e4493
-platform/packages/apps/Car/LatinIME 3d8c1a48e363e790173c2c32a42382f2b7ba8e89
+platform/packages/apps/Car/LatinIME b7dbefb8003082851c662337bb927eb1623ffa2c
 platform/packages/apps/Car/Launcher 232625d9ce8b964b374d289e4bc51a23552ad07f
-platform/packages/apps/Car/LensPicker 9279306a388ea2c94cae1b11af608d3b85f114fd
-platform/packages/apps/Car/LocalMediaPlayer 4b688e36da39f984f8873e760b79a9881438e095
-platform/packages/apps/Car/Media d1e33a6afdb4037c7f0903e7abee8ee93c5d3047
-platform/packages/apps/Car/Messenger 1417daee1688f36a8e9400455b253a498990601b
-platform/packages/apps/Car/Overview 09cc95ac949b1499e2122157ab1a21c30a6ae2e2
-platform/packages/apps/Car/Radio ed613183b407118ef866f119a84b4f116f9c117a
-platform/packages/apps/Car/Settings c9623a1ccfe504bdbe243de62bc7a40c5a00d82e
-platform/packages/apps/Car/Stream 0af16e0a196a9253d305bd56a5fbfad58ff72f74
+platform/packages/apps/Car/LensPicker 450f635b3a7477bb03ae2711b153fe3339dbe97a
+platform/packages/apps/Car/LocalMediaPlayer fe560313ebfd471d41cf332417dde55d3d0389d3
+platform/packages/apps/Car/Media 4ffad1cbe30bf0bb1a881366d9782ca363972cfa
+platform/packages/apps/Car/Messenger d56ef7d13feb6a3c43f9b2235dd64ebabc3d5d5d
+platform/packages/apps/Car/Notification f2c9b2ddee9757b12b86706f6d3391bb37857c7d
+platform/packages/apps/Car/Overview b8f6e8591f760073ebda60c2a6173c8ed0f9d688
+platform/packages/apps/Car/Radio 7d570a24e4a2aeae40e89d8264bf816ad5aa50db
+platform/packages/apps/Car/Settings 814debf090d4e40acc201bdf773ad4ee6155b2d5
+platform/packages/apps/Car/Stream f6dd2990e8d078c329a2fb7de2a0d4e5180ccc92
 platform/packages/apps/Car/SystemUpdater a5e37e235ba591bdf962460fee8c223c1a48d28b
-platform/packages/apps/Car/UserManagement 9391baedbf4ad645d7c125279a19d7a2b6b285f9
-platform/packages/apps/Car/libs a8b58bdcee5325034ba87d0f63bbe3d824535915
-platform/packages/apps/CarrierConfig 7e35430600824994e6c6b38470197d984c2ed991
-platform/packages/apps/CellBroadcastReceiver 5f5d086cd1edf2c62079a39023d6692f5d595289
+platform/packages/apps/Car/libs 76fe40120845f5d60f7ee6cc2eaf5a88ca9f3f3e
+platform/packages/apps/CarrierConfig 464effaaf6ba8ae3e66943f8722e30638ac10aaf
+platform/packages/apps/CellBroadcastReceiver 21551c4dd74ff0d17717ed20908f28ad0f188803
 platform/packages/apps/CertInstaller ae1a16bd415b4b9dcd2e4db3c87b159b204f0f0a
-platform/packages/apps/Contacts 7173594e8bff9249792b9a1698cf63420897f23e
-platform/packages/apps/DeskClock 254290d4ba92078b23e3e40a8619e3b6871c5c48
+platform/packages/apps/Contacts 497a4359c644120bb197cd898a14a534bfbbd9e3
+platform/packages/apps/DeskClock ebafd23efd1252fdc6fae9e3092675e352b0b8e7
 platform/packages/apps/DevCamera 90fdbf7460fbd255194455331c6a38a7b8f41dd2
-platform/packages/apps/Dialer eac73f3067acefffaa8eec7b7a9d8b3cc176e674
-platform/packages/apps/DocumentsUI 55dda5a69571730a1db2adfe948e7bcc28f10cbe
-platform/packages/apps/Email 6eb899d47c5e3bec8e5613782ac69a567f8e86de
-platform/packages/apps/EmergencyInfo 54d66862a03f63d9212a8e7d083bf3d6918907dd
-platform/packages/apps/ExactCalculator 78475cdcf69dd10b9a3579c465e103850a2216e2
+platform/packages/apps/Dialer 2789b8de52a65b1ec20d4e5c2ade1a16c3df26e0
+platform/packages/apps/DocumentsUI 1cefee6d1181cb15efd8b7686aa5bc68b7648e3c
+platform/packages/apps/Email c28e36dbaaff7d2b4641ab0d86f1309a418437e7
+platform/packages/apps/EmergencyInfo 34dc4863b9f201b9812366fd2285b5bae6733ca7
+platform/packages/apps/ExactCalculator 232eb2eca41c9da72f2073a90405e09db2e60e9f
 platform/packages/apps/Gallery 187c22b4cce776e597bceb326f08b688dcee366a
-platform/packages/apps/Gallery2 7115c7cbf95dea392ca9488d22369b5c3e91ed41
-platform/packages/apps/HTMLViewer 84f22d070f5181da041f9694328e7d6404f1a666
+platform/packages/apps/Gallery2 2813902e4efcfdbc8feec76ac3628eafdf9df374
+platform/packages/apps/HTMLViewer b59af56842e157cff48480a2db0d95aa2e072be8
 platform/packages/apps/KeyChain 2c7fb841f5e56490c221515013633273353f16a7
 platform/packages/apps/Launcher2 00d14bddacb5bcb19c1b31fc258d81820a497134
-platform/packages/apps/Launcher3 f51b706439ad7ed98f2f7de0a50d427e2a7fd632
+platform/packages/apps/Launcher3 74ebf7a282dde3731cb42f70ad46ccd489e47149
 platform/packages/apps/LegacyCamera 4e4414c8758020142f5d6635c73d644953a88eef
-platform/packages/apps/ManagedProvisioning ce32ecde38fdc7c9043c7cf6aa64e60cc5d5b109
-platform/packages/apps/Messaging 3dd036dc9973f3cf10768d66e892d152a58c6dee
-platform/packages/apps/Music f0d0a01dc247a4ab7880afcf05ca429848c56525
+platform/packages/apps/ManagedProvisioning 12122444dbf307b49b4b32bd1452d7946efe91ae
+platform/packages/apps/Messaging a75d7022fc6e17563bc71e843432ed37669a680b
+platform/packages/apps/Music 551d7c0529935d09441226920293e93169a4b602
 platform/packages/apps/MusicFX 8767c6cbcb736662a3412467af803af553e08180
-platform/packages/apps/Nfc ca54cd8c8e821c779457fe4e41019dbf9618be26
-platform/packages/apps/OneTimeInitializer 51e07426b1e031e5a1dd1ac0ee5a82fdeb78ce9d
+platform/packages/apps/Nfc 1a5eef2682805a24d10d141916044a65084ea8be
+platform/packages/apps/OneTimeInitializer 0cd160be02bd381e8d84800128c7092a946be95b
 platform/packages/apps/PackageInstaller 52ca98bf71fb58a85e9e7af4c7b77406955051e3
 platform/packages/apps/PhoneCommon 88ef9db0681e1129d0719c9d25c1748db88ecc25
 platform/packages/apps/Protips 1e5d8a500dce6530a99cb952681889042dcfb63a
-platform/packages/apps/Provision 01fab1d3e4e3329a7c14614930f18dbd7889d19d
-platform/packages/apps/QuickSearchBox cc3026c49771a19fc5252b83a496171530740fd9
+platform/packages/apps/Provision 41171be03f7a98e631cc6d5ecb97f38a23d31e7e
+platform/packages/apps/QuickSearchBox 40adfa9388338ffbfabd5e295387514b007eb21e
 platform/packages/apps/SafetyRegulatoryInfo 3e5bd08e3fe8de69c4efc70f84fc9363f62f4801
-platform/packages/apps/SecureElement 8c700e8a5b6c3d106613d7f0abbab1ee5d188bed
-platform/packages/apps/Settings ea74edc9c5506a061e17654050f38100396e199c
-platform/packages/apps/SoundRecorder 0c687bc4c4e375208f05e1cc2495b0b348093643
-platform/packages/apps/SpareParts 1b32dc47c66c6254d43ce576d4487347af8512dd
-platform/packages/apps/Stk 551134bbcd1e392e8764773646e645cd0708e2f4
+platform/packages/apps/SecureElement 023718c08c93dcf237a05b68e6eba36de0cdefad
+platform/packages/apps/Settings aa2ec5f23986df4ebc5254b06977e7e68e65c946
+platform/packages/apps/SoundRecorder 0f64cc38e2a2c4d7cc020a6821f4956b333ad62c
+platform/packages/apps/SpareParts 1067dc8f999729f3588cbdbb5ee01ec090297d09
+platform/packages/apps/Stk ce9d5d6555f4d0aebb22bd842e8e61e78e202af2
 platform/packages/apps/StorageManager 5eb32dc28f4d6e6173989b17135f3844269c3e86
-platform/packages/apps/TV 9d03eaf85ca79a871f59311168a5b1cba6725198
-platform/packages/apps/Tag 8be5463568b43e9fce7de3dd68ff7932be74acd7
+platform/packages/apps/TV 5fdc24aa6cc0acfd6a2d5939862e6edbe14f1ef6
+platform/packages/apps/Tag 9fd4a043a349af218ca90bfa821a647bc201792c
 platform/packages/apps/Terminal 9b98f743f2f23ea072c7c97d806b07fe9d49440f
 platform/packages/apps/Test/connectivity 0ed69e07ef9e23ad9a199460ff0cba4b1410d0b2
 platform/packages/apps/TimeZoneData 4774dc09fde94fbed863196d243aa28b1327ad7a
 platform/packages/apps/TimeZoneUpdater cb62ec134f8c779b17d13dd6c2dbe3b44219b460
-platform/packages/apps/Traceur a93ffff870a66379e6b6ddca4c0ad148bc677cc5
-platform/packages/apps/TvSettings 65dde5ffbcc173202c6ebbba6edcf8e6ad203838
-platform/packages/apps/UnifiedEmail 3ec87ee3a7c4a6f4a55f817ddf402ffb54cbd235
-platform/packages/apps/WallpaperPicker 63e34444e953173bd9b60561e63f4760916ae00d
-platform/packages/experimental 9fdd32e6758bcca54518b82a6876e7cd6934cd90
+platform/packages/apps/Traceur b95960071f5d66da7691d39eadaf4f0947202214
+platform/packages/apps/TvSettings 8ac38fe812f3c02fd07c319c1c0bdc7363f614da
+platform/packages/apps/UnifiedEmail c94fdd9ca75700a28527eef21f19832188fa5bf0
+platform/packages/apps/WallpaperPicker 84dfd5a4094d3a7024f5375d10f2b41289845dd8
+platform/packages/experimental 8f2b472ae1b1914335e3834242c33c26d1d76cad
 platform/packages/inputmethods/LatinIME 87c6fcf126672e1b8ea8b3a4581f96845537bcae
-platform/packages/inputmethods/OpenWnn c94a8c0458a0a189f88e3990bc23d5383954b634
+platform/packages/inputmethods/OpenWnn 7525835dc606501b19084c6fd09b2774317b60df
 platform/packages/providers/ApplicationsProvider 33d26f5eedb3d3011762ce5b2de66e931bf64b35
 platform/packages/providers/BlockedNumberProvider 11aa7e8bd1c574d61160d1fc445468c32db91031
-platform/packages/providers/BookmarkProvider 05bdfb1ef1792bb116e07b630d5eb9f773c80034
+platform/packages/providers/BookmarkProvider 3f85b115452ee79f327353d67d5381d5b409e76d
 platform/packages/providers/CalendarProvider ff030e888487b6d31f0837f8649924f762626a2e
 platform/packages/providers/CallLogProvider 08f71f5bb4dff48cdb94296c56c889f21568ad7b
 platform/packages/providers/ContactsProvider fb691a33d15ddeb7a427c48255fb287a99c1d29e
-platform/packages/providers/DownloadProvider 8515a33c49f8437ec21fcf28b297929b66897fa6
+platform/packages/providers/DownloadProvider 20dfa43eb73a6fca9564652c10bdcfa67bc740aa
 platform/packages/providers/MediaProvider b8a3eff2e5496c5262f60c16a4d2e40cea428240
 platform/packages/providers/PartnerBookmarksProvider 6a4100812002d597ce40cbeddb8be4e9a00f517d
-platform/packages/providers/TelephonyProvider 982324e066612949e89a8e172f1beaa010c11afe
+platform/packages/providers/TelephonyProvider 90feda37fe7226e9136c5afebc9fe8b53e4457cd
 platform/packages/providers/TvProvider 689838bb1c5a6e4a5edeab1a64bfe453dea1e682
 platform/packages/providers/UserDictionaryProvider a2fcb634fddaee9a02e6fc117a6898d498986587
 platform/packages/screensavers/Basic 6269ad665f7dc646791ca636f745a6f09906d2db
-platform/packages/screensavers/PhotoTable 97fd601978b04e23cad5a5f1647925e93a196f98
-platform/packages/screensavers/WebView a32aa0982f9d6b76754ca789344b778224e0fd2f
+platform/packages/screensavers/PhotoTable 997d8d211531f3ab8dba6439c2d32951c3fcfbb5
+platform/packages/screensavers/WebView a3efb7f8644fa676486016d93d113a821dd97776
 platform/packages/services/BuiltInPrintService 205144796388abcccfdeaf4bd71c11a9b654e281
-platform/packages/services/Car 4d1e3469cb2f285e7a4a864bd48a4c5177e7c83f
-platform/packages/services/Mms 1655b9673c1b2ca57cd63199f0094997ee66f5da
-platform/packages/services/NetworkRecommendation 85184fbd6abd298e8d24ba45863287bc982c39db
-platform/packages/services/Telecomm 560f8f30903ea15054e6bfa52775fadba2d1b2db
-platform/packages/services/Telephony 39f6d948d7855ad0ef70a741e7b1592141c7134a
+platform/packages/services/Car 683e37e879c703dbedbcd490f4fc51276d80ee98
+platform/packages/services/Mms 389e465876ac5f1fd3508a95ad34d70077c3cd27
+platform/packages/services/NetworkRecommendation 83c1d9346149bb1615328a13d15873a455754633
+platform/packages/services/Telecomm 79fe82a8d331d892e4ba818a34d24722b57dca52
+platform/packages/services/Telephony 9b6ee67985744117d6275bd85a649e15ea1ac5ac
 platform/packages/wallpapers/LivePicker 695ea134075b5947edbd9dc45f050ef5cfa6e812
 platform/pdk 31179761140edfba9234db19ab1ec64748033cc1
-platform/platform_testing fa7f37a8e19cba0cfa579da6a2697e00dc03f81a
-platform/prebuilts/abi-dumps/ndk 1544050ff3b6db6358d9e328baa1366fd63a8416
-platform/prebuilts/abi-dumps/vndk c79d08eee4127837135db56d0b03b3c44f1508d5
+platform/platform_testing 22cdc7e23685e2ab70b4046f24c2b4ee62a7fcb7
+platform/prebuilts/abi-dumps/ndk f47d391ecfe5e71a0e82db1990f4ff836f76f51a
+platform/prebuilts/abi-dumps/vndk c44364239f7efea4d63a782fe961ae2608b570cf
 platform/prebuilts/android-emulator a0b027c81bb70dca4f055b82db563b91c197d59d
-platform/prebuilts/build-tools 651f7a8136082c8e781b8051c2e2f3d9d9b9a429
-platform/prebuilts/checkcolor f6f661b424f646bc5ddf9e1b1e644abb492b7547
-platform/prebuilts/checkstyle c52010acac9b638f45f66747762cc0ad187c1e39
-platform/prebuilts/clang-tools 3a08d993fd11e6ebf87d5ebaa52ee9bc2ad85ee0
-platform/prebuilts/clang/host/darwin-x86 6c2eb897f90ebbf575032831833de8655c040be7
-platform/prebuilts/clang/host/linux-x86 aea8a0b2d8e85256b888882ce968cf409de6f316
+platform/prebuilts/build-tools 1d896cdc54a0237660fae2b31596544d1605c5bb
+platform/prebuilts/checkstyle 8ff3f0e77db2e7e3770a3e3699401570cc68e9c1
+platform/prebuilts/clang-tools d39538009de33b44a3f67ddb0ebe2722e1ef5f19
+platform/prebuilts/clang/host/darwin-x86 cea1308de6d32ef9453baaf280a35d32027506e1
+platform/prebuilts/clang/host/linux-x86 d42fce2273fe17e4ec0b85be8ec6a4931cf2c005
 platform/prebuilts/deqp c51deec2af21659a05960bdc48e4d9a49b507672
 platform/prebuilts/devtools b6d577870004cdb6b921118384d3b5a63c4a27d4
-platform/prebuilts/gcc/darwin-x86/aarch64/aarch64-linux-android-4.9 3c641780cd3faf03216017ca934f229b4bd5254e
-platform/prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9 5ee5d0b850ba2b10cfc5d92d2231ec29d955275b
+platform/prebuilts/gcc/darwin-x86/aarch64/aarch64-linux-android-4.9 5239d733a5b45781cf0078f70921e941cc7edf8d
+platform/prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9 ed1ffc5e65091dfaf792fe150296aebb9b32c090
 platform/prebuilts/gcc/darwin-x86/host/i686-apple-darwin-4.2.1 ec5aa66aaa4964c27564d0ec84dc1f18a2d72b7e
 platform/prebuilts/gcc/darwin-x86/mips/mips64el-linux-android-4.9 d942564c9331f07026a8f78502d8571206eb7be4
-platform/prebuilts/gcc/darwin-x86/x86/x86_64-linux-android-4.9 a0e9ccff3265f6356d8a09acedd784cc33fc7430
-platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9 63eb6ea699981390072db6b2f35770054e520c84
-platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9 2e8808b841953c8b90f36fc0b3bc77ccce321352
+platform/prebuilts/gcc/darwin-x86/x86/x86_64-linux-android-4.9 f80183acf73b6d724216d8ca5d0ced8bced3ab75
+platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9 77a9b08d6a44208afb100c9d210c712319f9d4b7
+platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9 91cb0e87deb2188c04aac97661ff93107a0843ec
 platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8 2ccb38af8c940f1feef62ff5986f4bbc5d899e66
-platform/prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8 1bde9c3b14f3a3b081ada6e32da9f2870671b46f
+platform/prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8 24285de70c617d2bd051e070598d8d4e2efb221a
 platform/prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9 388fdc4995d374d76a0c4b292afabac91638e134
-platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9 5ad707f7c85407a20f0c2ca9b7b1521ac6fbbb81
+platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9 f809d4e0f1e0ab5cdc9668fce809579cfabee516
 platform/prebuilts/gdb/darwin-x86 0a8c90248264a8b26970b4473770bcc3df8515fe
 platform/prebuilts/gdb/linux-x86 4adfde8bc82dd39f59e0445588c3e599ada477d2
-platform/prebuilts/go/darwin-x86 a3223285a951ddaebff74a59ea17039313eb9d16
-platform/prebuilts/go/linux-x86 e1b3b188ff03c41213dca621c46c39702d79f3ee
-platform/prebuilts/gradle-plugin 37e5c043969431a6e3616316dab6d4fc5b7cb794
-platform/prebuilts/jdk/jdk8 bb7dab15c8f2839694a4ced717ea2102e9a5b2a1
-platform/prebuilts/jdk/jdk9 da9ddf9c4de6bd6224639ac79ed735204d153c67
+platform/prebuilts/go/darwin-x86 ee04cf91e69ed1ea9cb2e00c5a4764f6b67c4175
+platform/prebuilts/go/linux-x86 a837bae4a621488cae19d70945e59b6334bc670d
+platform/prebuilts/gradle-plugin 9cbafb8ccc799c9a1de9e9a50003e6cbfa0a6d52
+platform/prebuilts/jdk/jdk8 13f5a7554ae77bf3cd959445646ecb485a670d3a
+platform/prebuilts/jdk/jdk9 1311527b3a3cc926a32eec58c83f89979c02c29b
 platform/prebuilts/libs/libedit d32685dba4011664b590b94ad156bc734c2c9bb5
 platform/prebuilts/maven_repo/android 3cfcfbbc81eb36123378dc1d3e7beeee0303a199
-platform/prebuilts/maven_repo/bumptech 6eb5834f46129376fafac9a456625ccd9406ba60
+platform/prebuilts/maven_repo/bumptech d166742cdd6eab44d07fdb7be1d271ceb0b73a17
 platform/prebuilts/maven_repo/google-play-service-client-libraries-3p 427e6e2267fd43142ddbd1a87268e0112ddb9b11
-platform/prebuilts/misc b2b163b841fa8fb36bc34eb060f8d4d77e779a5c
-platform/prebuilts/ndk 90ec4adf8b56b07fb216d0dddda9d57908f9b5c6
+platform/prebuilts/misc 13ac3452972b5d458ba0f93106d70eba541de4ca
+platform/prebuilts/ndk 15245c207711114830ed377727fc81227eaf9520
 platform/prebuilts/python/darwin-x86/2.7.5 0c5958b1636c47ed7c284f859c8e805fd06a0e63
-platform/prebuilts/python/linux-x86/2.7.5 02c50fa81b02a4246acac1d6eedcb28d77a4c2e1
+platform/prebuilts/python/linux-x86/2.7.5 5bcc5eb23dbb00d5e5dbf75835aa2fb79e8bafa2
 platform/prebuilts/qemu-kernel 1586476297c751b93bf520427d3f06d500fe6514
-platform/prebuilts/r8 feac83d01c4781d441dc9d819f5a6ff8475a51f2
-platform/prebuilts/sdk 3c86743ff0cf6a76eab6ee42039afb4463e6bbfd
-platform/prebuilts/tools 606ad49aa8d62e1ed64a86c1333bef7aac836701
-platform/sdk 1ce300881fb5a6d02c05e2fd103dbe18ccc165b5
-platform/system/bt 2a3b9b597ee07f8728bd9903b0bc104c4e24fd6c
-platform/system/ca-certificates d8f261e47d3d1401140e974f7d21ac0c13840085
-platform/system/chre fdb75beab709576e90db8116dff6ca43805f144d
+platform/prebuilts/r8 9e6f0108e9ce82d9b0ddd538f9a6ac49fd50ae43
+platform/prebuilts/sdk 5070312266aa13633de6856de9ceb9d2cf630725
+platform/prebuilts/tools 296d339ce5889fc795905ea4649fccbbae42c53d
+platform/sdk 21b5f6fef124e14b612b506620ea5c8addde0536
+platform/system/bt 897a734a47472e0c329aecfe46fd02748ca15c44
+platform/system/ca-certificates a5108d92122fb13a40b4c79ded0f29167bb7e2c2
+platform/system/chre 8d15a01972d7e56d8a931114a2467583c166a0ab
 platform/system/connectivity/wificond ed1f3ddbe13fed1d5fedd5dfcdb6912bc40ee68d
 platform/system/connectivity/wifilogd 6e5a94db8d7dc577a135cc81d80e0bdf39d42780
-platform/system/core 49b7f296b9e513b69fa60088e03a70cb354a3120
-platform/system/extras ec911c7426eb9b040f0fb4aefed6ef4f04351ef5
-platform/system/gatekeeper 1173d5841cc9008680c325686f4a1653a27aa048
-platform/system/hardware/interfaces 94832f94dc902b130bf3b2814e9addc2f27a2840
-platform/system/hwservicemanager 512903da9e6aa0f87284c4de16a01aacb7b8ff8f
-platform/system/iot/attestation 978bb9a973d63fbc28bcba3d5d79caee5b973431
-platform/system/keymaster 9480701e6924b0b4cfaae263aae9a5bf35dad206
-platform/system/libfmq 76d547d1be0fdcd099327c0d824aa173f25b9d65
-platform/system/libhidl 5f3a8cf804b87e9b0c9abebef85a57027555f1d9
-platform/system/libhwbinder 418856e083a27ef19744b12208c49c84b0bec1d0
-platform/system/libufdt 2626d8b9e4d8e8c6cc67ceb1dc4e05a47779785c
-platform/system/libvintf 1dd79d71f5adb520fe3ebfb4c6d0bb559307cdba
-platform/system/media 384bd00de2efb16eea962367734c2cdebe631e42
-platform/system/netd 76fb71603f101e4d0db6a1bcf52f122a6e7ff8ed
-platform/system/nfc 5d014a1c750cabf1d07ef9998ff6b0d11478bb80
-platform/system/nvram b393064c69320437f3ec56a51450db419ffc9806
-platform/system/security 6ec80146e52e74b34e4499a553553d9ea01a30a0
-platform/system/sepolicy 92b6793d115af07eec9a46e900b098882bddee3c
-platform/system/timezone d8b0d16fe5fb81eeb199f309b7320c7a26c91b4a
-platform/system/tools/aidl f1b50780ce96c916802e11de10334627f529a728
+platform/system/core 919ee8ab1cb05d34099ba30728cedbfad309ba40
+platform/system/extras 60dd6c456924fad32ef3da655df6cd3daa776309
+platform/system/gatekeeper c5bf61b35935cbfa75aa9d8d5c3d8fbb4d4953ee
+platform/system/hardware/interfaces 9720abcd5a4620a203a0260d5631ccd58cb34feb
+platform/system/hwservicemanager 0e70e649c97b38841e3567a87c2b93a690b5ce58
+platform/system/iorap 51ce7a80ca2cdc995b2de81996da1ee82add652f
+platform/system/iot/attestation 1a657783b3fc59e33cbfaeee8b4e1e97b0a4c1a2
+platform/system/keymaster 5f765b86ae9a83e32e3fced90fda583a5cfc2c3e
+platform/system/libfmq 066fa3f609b08c1855ac369ac81036164db7e6c2
+platform/system/libhidl 642a9a302472be128b0cda24b1f5724aa5dedcbf
+platform/system/libhwbinder 55d4107c06fb0dc4051a3039174fbba36968f108
+platform/system/libufdt b59dbe8eb4cdef68a676b7aa3cb416f94d5ce8ec
+platform/system/libvintf af0cefd66b94159da9a97d2ffafc23c98796a105
+platform/system/media 81ce87f0cc1b91e333dff5fc3dfb7f9ca0ccf2cb
+platform/system/netd b4d84687e83b86eb64ec0705f8452b4a2023569b
+platform/system/nfc 1a0df4d3e4feec34ac1398c8b0f7134233659501
+platform/system/nvram 6c246ace0160aedf62a7e699e1ccef51e1d12a01
+platform/system/security cc4bed2f3d9afee1f202651314f242a03b7dc5b8
+platform/system/sepolicy 4738b93db250175f0915cee2f08ab01aaf8d28f9
+platform/system/timezone 939d4ac65081f731df6d92d4e815cb71463be01e
+platform/system/tools/aidl beb98c78cced798170b1eaacb72efaec7fc8e7a7
 platform/system/tools/bpt e2c77aebf72d56beb50329e9ce873ea834e02164
-platform/system/tools/hidl 42a4aae5b21e532a6b11cb6d413f1f282a5534ba
-platform/system/tpm be1da4c5dbf43a6a16cdf6fcb55bd9ea0083a7c1
-platform/system/update_engine ac773deff2287f0f3a2da52919efa2eefd574970
-platform/system/vold f6dbba884632e4770011f389559da673b5529935
-platform/test/framework 707e49acd77bc3b6dcfb32963de5437f3ed91d86
+platform/system/tools/hidl 71ce008bf97864c3ed6f86715bc1e7ca44ef8435
+platform/system/update_engine 91f8d2aa3f8d7bd7bfb4c0dcaa30ef05b61de380
+platform/system/vold cb114c36d89edb4f3f45f517cafba51bcc2d730f
+platform/test/framework ab8dbf046ad5ebf551cd201f6f24f2a57f660e10
+platform/test/mlts/benchmark e0fe62caa793035db59d03fa811275c008ed40d1
+platform/test/mlts/models eccf58ad60577853ed831106a8ce893f69c8fbed
 platform/test/sts e50c6f1decfb63167eade6600450923ed1386840
 platform/test/vti/alert 2270d160e38f0623d708cce36754fe14168dce64
-platform/test/vti/dashboard af67870e99758b915a622f64d734c44fa53b8fc1
-platform/test/vti/fuzz_test_serving 4f804b528934be8723c71b422b3dda33efe891b4
-platform/test/vti/test_serving e3b4ea8cb51a722a0a52ece99edc3ec31ab74ff5
-platform/test/vts 5ba3ec58788d6b944eb91e6e6820dd84191210c6
-platform/test/vts-testcase/fuzz 70288fdeaa251a7b572b5855cec4bb205026b2ea
-platform/test/vts-testcase/hal 2b5e59286f975ef2c040d45a50592e91248d7e15
-platform/test/vts-testcase/hal-trace 2e44794cc0b48ccf7d769d09511f85a6b93c9e8b
-platform/test/vts-testcase/kernel db688508ef71c768489af3cf539c1696eb0fb8a5
-platform/test/vts-testcase/nbu cba91305e8eefdf34a2cd3643eeb8fffc895096d
-platform/test/vts-testcase/performance 1b5e4f621043ef93b2549123c9a1252daac13b42
-platform/test/vts-testcase/security 371d089c303353d35f0ef56baed81894c2b80d93
-platform/test/vts-testcase/vndk 3df48ddf36502717f862a318eb3de2ed9b917951
-platform/tools/acloud d29008907e4183c2fd3a9c9ac191ca36f75f0f16
-platform/tools/adt/idea c42a87ece8dbeb3ce1e0ee5d26576466a8127869
-platform/tools/apksig 2616b964950b591d92247b3e11f7a97e7396c936
-platform/tools/apkzlib d6148f97a5cef82fca960955f23f1d1917db3944
+platform/test/vti/dashboard 3cdb03d4461ef20cb60e1c790f521d992e1186af
+platform/test/vti/fuzz_test_serving e5bcc336b6b8bb7826e791cf608bf0efbb32a2ed
+platform/test/vti/test_serving c71a5ac41ba0b4c2c883cc8ccf72922c14170c8e
+platform/test/vts 2956dcdc9518541d9c0ef0354123e3eae700d248
+platform/test/vts-testcase/fuzz e8bb518fdd54f495c7a1a86e632fdfa6dfbb7111
+platform/test/vts-testcase/hal dcf5adeb6831b8d6574fe69985dfcb7a515767dd
+platform/test/vts-testcase/hal-trace 260dfffd16a180ac1d278b6bb43d8fefadbb7460
+platform/test/vts-testcase/kernel d2d944573bfff0b45a787c3379ea9d5dcb96f426
+platform/test/vts-testcase/nbu 70b9b838d7fa258ac1ffffbeb2cb606ad2218fd6
+platform/test/vts-testcase/performance 52b98fa38f75825ec4bb8b34581c064b64990994
+platform/test/vts-testcase/security cca8faaa2b0da17dbc4790b1e37955514f2d6a27
+platform/test/vts-testcase/vndk fd11d3c216aaaf2e019b9171e95a60f74aa77175
+platform/tools/acloud fdb32f061a74ee8a74bc3e2932abd0a7af01e973
+platform/tools/adt/idea dbaf0c96c3214cce5497259831469c05aacb3068
+platform/tools/apksig 15630cdebbd2731ed5e349be50dd28a01afd38c6
+platform/tools/apkzlib 65da1558097c007a847ed46ecd27a5a69ce6d96a
+platform/tools/asuite e4750089df9798d1e2deb1c2056bc481ae4b5431
 platform/tools/base 908b391a9c006af569dfaff08b37f8fdd6c4da89
 platform/tools/build 69c4b95102b4b9862bfba68b3eaf5b7537a705ee
-platform/tools/dexter dc7eb39f966ab890d274363c76929775ed03b6ac
+platform/tools/dexter d803fdf37ea8b313360283f6baa965be2d01c36c
 platform/tools/external/fat32lib 445d40e55cbb432b5682a0df54e42b3d5285d57f
-platform/tools/external/gradle 770d6ed9ff91fb085be6f9bd7fe1ee2a2199aebf
+platform/tools/external/gradle e9395a3fdf2e5afa3a21a921667d46ae8af4dc3f
+platform/tools/external_updater 70480f5af1e45843ce494175450f67f588b539c0
 platform/tools/idea 9b5d02ac8c92b1e71523cc15cb3d168d57fbd898
-platform/tools/loganalysis 9308120bc2e837170793af30388b6a21d5e47c07
-platform/tools/metalava 4d21ce4cddc8e85549ead7219e9d2c82d8a88ca3
+platform/tools/loganalysis 7c4aeb12ee1330313a1574e013afbdcb2b81f0f6
+platform/tools/metalava 8b19e4039d505a176690582b4c93cea041ff2162
 platform/tools/motodev 69989786cefbde82527960a1e100ec9afba46a98
-platform/tools/repohooks 989ccce479d8afdb5bdeab4ba2a6272357fd7720
+platform/tools/repohooks 2ee871c35817d3c1f7b8964d2e9c62c033cd8785
 platform/tools/security fdda0f4c20431c4b40372e2a0080017f50f55218
 platform/tools/studio/cloud 58f06e77e051fff3903adabca7acdaa9dd12ec2d
 platform/tools/swt 8996e71047a2bd11efee46ef14e02435ab5fa07a
-platform/tools/test/connectivity 908927a856da08abc837febfe9a416091eeaa10b
-platform/tools/test/graphicsbenchmark 102c44cf07cf338c811303747c2686c2c549383c
-platform/tools/tradefederation 7f3157d862eee50bc80d0d405322fd06b69eace5
-platform/tools/tradefederation/contrib e5aba25c43add3a91e2064ecc3d5403ce7e1f7bc
-toolchain/benchmark 2eebfdf9a165082e60d9ec0ac1be85dbde740d9a
-toolchain/binutils 2485f18b02e8a6d2173314daa570d81f580607c3
+platform/tools/test/connectivity 82c06467f4bfd2ddd02fd7d5a76ed911e08321f8
+platform/tools/test/graphicsbenchmark b8ab4030287273173016cbea5be3597acf5250a1
+platform/tools/tradefederation 1a0e8aad2e20f8289d0e639fd3d62a983d7caa19
+platform/tools/tradefederation/contrib 3009684b9460753df7dec129e26b15c66128fc9f
+toolchain/benchmark ce43588700ff84f20b6bf6a513d8932f693e33dd
+toolchain/binutils 44492f8e438f8ce42a254699ba8496eb76075e00
 toolchain/pgo-profiles 3d0d4c6e286e94be332662b64f26e0d189b37af4
diff --git a/report.py b/report.py
old mode 100644
new mode 100755
diff --git a/report_html.js b/report_html.js
index 5e86c16..720f72f 100644
--- a/report_html.js
+++ b/report_html.js
@@ -16,7 +16,94 @@
 'use strict';
 
 // Use IIFE to avoid leaking names to other scripts.
-$(document).ready(function() {
+(function () {
+
+function getTimeInMs() {
+    return new Date().getTime();
+}
+
+class TimeLog {
+    constructor() {
+        this.start = getTimeInMs();
+    }
+
+    log(name) {
+        let end = getTimeInMs();
+        console.log(name, end - this.start, 'ms');
+        this.start = end;
+    }
+}
+
+class ProgressBar {
+    constructor() {
+        let str = `
+            <div class="modal" tabindex="-1" role="dialog">
+                <div class="modal-dialog" role="document">
+                    <div class="modal-content">
+                        <div class="modal-header"><h5 class="modal-title">Loading page...</h5></div>
+                        <div class="modal-body">
+                            <div class="progress">
+                                <div class="progress-bar" role="progressbar"
+                                    style="width: 0%" aria-valuenow="0" aria-valuemin="0"
+                                    aria-valuemax="100">0%</div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        `;
+        this.modal = $(str).appendTo($('body'));
+        this.progress = 0;
+        this.shownCallback = null;
+        this.modal.on('shown.bs.modal', () => this._onShown());
+        // Shorten progress bar update time.
+        this.modal.find('.progress-bar').css('transition-duration', '0ms');
+        this.shown = false;
+    }
+
+    // progress is [0-100]. Return a Promise resolved when the update is shown.
+    updateAsync(text, progress) {
+        progress = parseInt(progress);  // Truncate float number to integer.
+        return this.showAsync().then(() => {
+            if (text) {
+                this.modal.find('.modal-title').text(text);
+            }
+            this.progress = progress;
+            this.modal.find('.progress-bar').css('width', progress + '%')
+                    .attr('aria-valuenow', progress).text(progress + '%');
+            // Leave 100ms for the progess bar to update.
+            return createPromise((resolve) => setTimeout(resolve, 100));
+        });
+    }
+
+    showAsync() {
+        if (this.shown) {
+            return createPromise();
+        }
+        return createPromise((resolve) => {
+            this.shownCallback = resolve;
+            this.modal.modal({
+                show: true,
+                keyboard: false,
+                backdrop: false,
+            });
+        });
+    }
+
+    _onShown() {
+        this.shown = true;
+        if (this.shownCallback) {
+            let callback = this.shownCallback;
+            this.shownCallback = null;
+            callback();
+        }
+    }
+
+    hide() {
+        this.shown = false;
+        this.modal.modal('hide');
+    }
+}
 
 function openHtml(name, attrs={}) {
     let s = `<${name} `;
@@ -54,10 +141,6 @@
     return s;
 }
 
-function toPercentageStr(percentage) {
-    return percentage.toFixed(2) + '%';
-}
-
 function getProcessName(pid) {
     let name = gProcesses[pid];
     return name ? `${pid} (${name})`: pid.toString();
@@ -106,57 +189,119 @@
             eventInfo.eventName.includes('cpu-clock');
 }
 
+let createId = function() {
+    let currentId = 0;
+    return () => `id${++currentId}`;
+}();
+
 class TabManager {
     constructor(divContainer) {
-        this.div = $('<div>', {id: 'tabs'});
-        this.div.appendTo(divContainer);
-        this.div.append(getHtml('ul'));
-        this.tabs = [];
-        this.isDrawCalled = false;
+        let id = createId();
+        divContainer.append(`<ul class="nav nav-pills mb-3 mt-3 ml-3" id="${id}" role="tablist">
+            </ul><hr/><div class="tab-content" id="${id}Content"></div>`);
+        this.ul = divContainer.find(`#${id}`);
+        this.content = divContainer.find(`#${id}Content`);
+        // Map from title to [tabObj, drawn=false|true].
+        this.tabs = new Map();
+        this.tabActiveCallback = null;
     }
 
     addTab(title, tabObj) {
-        let id = 'tab_' + this.div.children().length;
-        let tabDiv = $('<div>', {id: id});
-        tabDiv.appendTo(this.div);
-        this.div.children().first().append(
-            getHtml('li', {text: getHtml('a', {href: '#' + id, text: title})}));
-        tabObj.init(tabDiv);
-        this.tabs.push(tabObj);
-        if (this.isDrawCalled) {
-            this.div.tabs('refresh');
-        }
+        let id = createId();
+        this.content.append(`<div class="tab-pane" id="${id}" role="tabpanel"
+            aria-labelledby="${id}-tab"></div>`);
+        this.ul.append(`
+            <li class="nav-item">
+                <a class="nav-link" id="${id}-tab" data-toggle="pill" href="#${id}" role="tab"
+                    aria-controls="${id}" aria-selected="false">${title}</a>
+            </li>`);
+        tabObj.init(this.content.find(`#${id}`));
+        this.tabs.set(title, [tabObj, false]);
+        this.ul.find(`#${id}-tab`).on('shown.bs.tab', () => this.onTabActive(title));
         return tabObj;
     }
 
+    setActiveAsync(title) {
+        let tabObj = this.findTab(title);
+        return createPromise((resolve) => {
+            this.tabActiveCallback = resolve;
+            let id = tabObj.div.attr('id') + '-tab';
+            this.ul.find(`#${id}`).tab('show');
+        });
+    }
+
+    onTabActive(title) {
+        let array = this.tabs.get(title);
+        let tabObj = array[0];
+        let drawn = array[1];
+        if (!drawn) {
+            tabObj.draw();
+            array[1] = true;
+        }
+        if (this.tabActiveCallback) {
+            let callback = this.tabActiveCallback;
+            this.tabActiveCallback = null;
+            callback();
+        }
+    }
+
     findTab(title) {
-        let links = this.div.find('li a');
-        for (let i = 0; i < links.length; ++i) {
-            if (links.eq(i).text() == title) {
-                return this.tabs[i];
-            }
-        }
-        return null;
+        let array = this.tabs.get(title);
+        return array ? array[0] : null;
     }
+}
 
-    draw() {
-        this.div.tabs({
-            active: 0,
-        });
-        this.tabs.forEach(function(tab) {
-            tab.draw();
-        });
-        this.isDrawCalled = true;
+function createEventTabs(id) {
+    let ul = `<ul class="nav nav-pills mb-3 mt-3 ml-3" id="${id}" role="tablist">`;
+    let content = `<div class="tab-content" id="${id}Content">`;
+    for (let i = 0; i < gSampleInfo.length; ++i) {
+        let subId = id + '_' + i;
+        let title = gSampleInfo[i].eventName;
+        ul += `
+            <li class="nav-item">
+                <a class="nav-link" id="${subId}-tab" data-toggle="pill" href="#${subId}" role="tab"
+                aria-controls="${subId}" aria-selected="${i == 0 ? "true" : "false"}">${title}</a>
+            </li>`;
+        content += `
+            <div class="tab-pane" id="${subId}" role="tabpanel" aria-labelledby="${subId}-tab">
+            </div>`;
     }
+    ul += '</ul>';
+    content += '</div>';
+    return ul + content;
+}
 
-    setActive(tabObj) {
-        for (let i = 0; i < this.tabs.length; ++i) {
-            if (this.tabs[i] == tabObj) {
-                this.div.tabs('option', 'active', i);
-                break;
-            }
+function createViewsForEvents(div, createViewCallback) {
+    let views = [];
+    if (gSampleInfo.length == 1) {
+        views.push(createViewCallback(div, gSampleInfo[0]));
+    } else if (gSampleInfo.length > 1) {
+        // If more than one event, draw them in tabs.
+        let id = createId();
+        div.append(createEventTabs(id));
+        for (let i = 0; i < gSampleInfo.length; ++i) {
+            let subId = id + '_' + i;
+            views.push(createViewCallback(div.find(`#${subId}`), gSampleInfo[i]));
         }
+        div.find(`#${id}_0-tab`).tab('show');
     }
+    return views;
+}
+
+// Return a promise to draw views.
+function drawViewsAsync(views, totalProgress, drawViewCallback) {
+    if (views.length == 0) {
+        return createPromise();
+    }
+    let drawPos = 0;
+    let eachProgress = totalProgress / views.length;
+    function drawAsync() {
+        if (drawPos == views.length) {
+            return createPromise();
+        }
+        return drawViewCallback(views[drawPos++], eachProgress).then(drawAsync);
+    }
+    return drawAsync();
 }
 
 // Show global information retrieved from the record file, including:
@@ -215,9 +360,7 @@
 // Show pieChart of event count percentage of each process, thread, library and function.
 class ChartView {
     constructor(divContainer, eventInfo) {
-        this.id = divContainer.children().length;
-        this.div = $('<div>', {id: 'chartstat_' + this.id});
-        this.div.appendTo(divContainer);
+        this.div = $('<div>').appendTo(divContainer);
         this.eventInfo = eventInfo;
         this.processInfo = null;
         this.threadInfo = null;
@@ -231,7 +374,7 @@
         if (isClockEvent(this.eventInfo)) {
             this.getSampleWeight = function (eventCount) {
                 return (eventCount / 1000000.0).toFixed(3) + ' ms';
-            }
+            };
         } else {
             this.getSampleWeight = (eventCount) => '' + eventCount;
         }
@@ -323,9 +466,8 @@
             },
         });
         if (this._getState() != this.states.SHOW_EVENT_INFO) {
-            let button = $('<button>', {text: 'Back'});
-            button.appendTo(this.div);
-            button.button().click(() => this._goBack());
+            $('<button type="button" class="btn btn-primary">Back</button>').appendTo(this.div)
+                .click(() => this._goBack());
         }
     }
 
@@ -367,7 +509,7 @@
             title = 'Functions in library ' + getLibName(this.libInfo.libId);
             firstColumn = 'Function';
             for (let func of this.libInfo.functions) {
-                rows.push(getItem('Function: ' + getFuncName(func.g.f), func.g.e,
+                rows.push(getItem('Function: ' + getFuncName(func.f), func.c[1],
                                   this.libInfo.eventCount));
             }
         }
@@ -392,54 +534,39 @@
 
 
 class ChartStatTab {
-    constructor() {
-    }
-
     init(div) {
         this.div = div;
-        this.recordFileView = new RecordFileView(this.div);
-        this.chartViews = [];
-        for (let eventInfo of gSampleInfo) {
-            this.chartViews.push(new ChartView(this.div, eventInfo));
-        }
     }
 
     draw() {
-        this.recordFileView.draw();
-        for (let charView of this.chartViews) {
-            charView.draw();
+        new RecordFileView(this.div).draw();
+        let views = createViewsForEvents(this.div, (div, eventInfo) => {
+            return new ChartView(div, eventInfo);
+        });
+        for (let view of views) {
+            view.draw();
         }
     }
 }
 
 
 class SampleTableTab {
-    constructor() {
-    }
-
     init(div) {
         this.div = div;
-        this.selectorView = null;
-        this.sampleTableViews = [];
     }
 
     draw() {
-        this.selectorView = new SampleTableWeightSelectorView(this.div, gSampleInfo[0],
-                                                              () => this.onSampleWeightChange());
-        this.selectorView.draw();
-        for (let eventInfo of gSampleInfo) {
-            this.div.append(getHtml('hr'));
-            this.sampleTableViews.push(new SampleTableView(this.div, eventInfo));
-        }
-        this.onSampleWeightChange();
-    }
-
-    onSampleWeightChange() {
-        for (let i = 0; i < gSampleInfo.length; ++i) {
-            let sampleWeightFunction = this.selectorView.getSampleWeightFunction(gSampleInfo[i]);
-            let sampleWeightSuffix = this.selectorView.getSampleWeightSuffix(gSampleInfo[i]);
-            this.sampleTableViews[i].draw(sampleWeightFunction, sampleWeightSuffix);
-        }
+        let views = [];
+        createPromise()
+            .then(updateProgress('Draw SampleTable...', 0))
+            .then(wait(() => {
+                this.div.empty();
+                views = createViewsForEvents(this.div, (div, eventInfo) => {
+                    return new SampleTableView(div, eventInfo);
+                });
+            }))
+            .then(() => drawViewsAsync(views, 100, (view, progress) => view.drawAsync(progress)))
+            .then(hideProgress());
     }
 }
 
@@ -447,54 +574,57 @@
 // 1. Show percentage of event count.
 // 2. Show event count (For cpu-clock and task-clock events, it is time in ms).
 class SampleTableWeightSelectorView {
-    constructor(divContainer, firstEventInfo, onSelectChange) {
-        this.div = $('<div>');
-        this.div.appendTo(divContainer);
-        this.onSelectChange = onSelectChange;
-        this.options = {
-            SHOW_PERCENT: 0,
-            SHOW_EVENT_COUNT: 1,
-        };
-        if (isClockEvent(firstEventInfo)) {
-            this.curOption = this.options.SHOW_EVENT_COUNT;
-        } else {
-            this.curOption = this.options.SHOW_PERCENT;
+    constructor(divContainer, eventInfo, onSelectChange) {
+        let options = new Map();
+        options.set('percent', 'Show percentage of event count');
+        options.set('event_count', 'Show event count');
+        if (isClockEvent(eventInfo)) {
+            options.set('event_count_in_ms', 'Show event count in milliseconds');
         }
-    }
-
-    draw() {
-        let options = ['Show percentage of event count', 'Show event count'];
-        let optionStr = '';
-        for (let i = 0; i < options.length; ++i) {
-            optionStr += getHtml('option', {value: i, text: options[i]});
-        }
-        this.div.append(getHtml('select', {text: optionStr}));
-        let selectMenu = this.div.children().last();
-        selectMenu.children().eq(this.curOption).attr('selected', 'selected');
-        let thisObj = this;
-        selectMenu.selectmenu({
-            change: function() {
-                thisObj.curOption = this.value;
-                thisObj.onSelectChange();
-            },
-            width: '100%',
+        let buttons = [];
+        options.forEach((value, key) => {
+            buttons.push(`<button type="button" class="dropdown-item" key="${key}">${value}
+                          </button>`);
+        });
+        this.curOption = 'percent';
+        this.eventCount = eventInfo.eventCount;
+        let id = createId();
+        let str = `
+            <div class="dropdown">
+                <button type="button" class="btn btn-primary dropdown-toggle" id="${id}"
+                    data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
+                    >${options.get(this.curOption)}</button>
+                <div class="dropdown-menu" aria-labelledby="${id}">${buttons.join('')}</div>
+            </div>
+        `;
+        divContainer.append(str);
+        divContainer.children().last().on('hidden.bs.dropdown', (e) => {
+            if (e.clickEvent) {
+                let button = $(e.clickEvent.target);
+                let newOption = button.attr('key');
+                if (newOption && this.curOption != newOption) {
+                    this.curOption = newOption;
+                    divContainer.find(`#${id}`).text(options.get(this.curOption));
+                    onSelectChange();
+                }
+            }
         });
     }
 
-    getSampleWeightFunction(eventInfo) {
-        if (this.curOption == this.options.SHOW_PERCENT) {
-            return function(eventCount) {
-                return (eventCount * 100.0 / eventInfo.eventCount).toFixed(2) + '%';
-            }
+    getSampleWeightFunction() {
+        if (this.curOption == 'percent') {
+            return (eventCount) => (eventCount * 100.0 / this.eventCount).toFixed(2) + '%';
         }
-        if (isClockEvent(eventInfo)) {
+        if (this.curOption == 'event_count') {
+            return (eventCount) => '' + eventCount;
+        }
+        if (this.curOption == 'event_count_in_ms') {
             return (eventCount) => (eventCount / 1000000.0).toFixed(3);
         }
-        return (eventCount) => '' + eventCount;
     }
 
-    getSampleWeightSuffix(eventInfo) {
-        if (this.curOption == this.options.SHOW_EVENT_COUNT && isClockEvent(eventInfo)) {
+    getSampleWeightSuffix() {
+        if (this.curOption == 'event_count_in_ms') {
             return ' ms';
         }
         return '';
@@ -504,89 +634,227 @@
 
 class SampleTableView {
     constructor(divContainer, eventInfo) {
-        this.id = divContainer.children().length;
-        this.div = $('<div>');
-        this.div.appendTo(divContainer);
+        this.id = createId();
+        this.div = $('<div>', {id: this.id}).appendTo(divContainer);
         this.eventInfo = eventInfo;
+        this.selectorView = null;
+        this.tableDiv = null;
     }
 
-    draw(getSampleWeight, sampleWeightSuffix) {
-        // Draw a table of 'Total', 'Self', 'Samples', 'Process', 'Thread', 'Library', 'Function'.
-        this.div.empty();
+    drawAsync(totalProgress) {
+        return createPromise()
+            .then(wait(() => {
+                this.div.empty();
+                this.selectorView = new SampleTableWeightSelectorView(
+                    this.div, this.eventInfo, () => this.onSampleWeightChange());
+                this.tableDiv = $('<div>').appendTo(this.div);
+            }))
+            .then(() => this._drawSampleTable(totalProgress));
+    }
+
+    // Return a promise to draw SampleTable.
+    _drawSampleTable(totalProgress) {
         let eventInfo = this.eventInfo;
-        let sampleWeight = getSampleWeight(eventInfo.eventCount);
-        this.div.append(getHtml('p', {text: `Sample table for event ${eventInfo.eventName}, ` +
-                `total count ${sampleWeight}${sampleWeightSuffix}`}));
-        let tableId = 'sampleTable_' + this.id;
-        let valueSuffix = sampleWeightSuffix.length > 0 ? `(in${sampleWeightSuffix})` : '';
-        let titles = ['Total' + valueSuffix, 'Self' + valueSuffix, 'Samples',
-                      'Process', 'Thread', 'Library', 'Function'];
-        let tableStr = openHtml('table', {id: tableId, cellspacing: '0', width: '100%'}) +
-                        getHtml('thead', {text: getTableRow(titles, 'th')}) +
-                        getHtml('tfoot', {text: getTableRow(titles, 'th')}) +
-                        openHtml('tbody');
-        for (let i = 0; i < eventInfo.processes.length; ++i) {
-            let processInfo = eventInfo.processes[i];
-            let processName = getProcessName(processInfo.pid);
-            for (let j = 0; j < processInfo.threads.length; ++j) {
-                let threadInfo = processInfo.threads[j];
-                let threadName = getThreadName(threadInfo.tid);
-                for (let k = 0; k < threadInfo.libs.length; ++k) {
-                    let lib = threadInfo.libs[k];
-                    let libName = getLibName(lib.libId);
-                    for (let t = 0; t < lib.functions.length; ++t) {
-                        let func = lib.functions[t];
-                        let key = [i, j, k, t].join('_');
-                        let totalValue = getSampleWeight(func.g.s);
-                        let selfValue = getSampleWeight(func.g.e);
-                        tableStr += getTableRow([totalValue, selfValue, func.c,
-                                                 processName, threadName, libName,
-                                                 getFuncName(func.g.f)], 'td', {key: key});
+        let data = [];
+        return createPromise()
+            .then(wait(() => {
+                this.tableDiv.empty();
+                let getSampleWeight = this.selectorView.getSampleWeightFunction();
+                let sampleWeightSuffix = this.selectorView.getSampleWeightSuffix();
+                // Draw a table of 'Total', 'Self', 'Samples', 'Process', 'Thread', 'Library',
+                // 'Function'.
+                let valueSuffix = sampleWeightSuffix.length > 0 ? `(in${sampleWeightSuffix})` : '';
+                let titles = ['Total' + valueSuffix, 'Self' + valueSuffix, 'Samples', 'Process',
+                              'Thread', 'Library', 'Function', 'HideKey'];
+                this.tableDiv.append(`
+                    <table cellspacing="0" class="table table-striped table-bordered"
+                        style="width:100%">
+                        <thead>${getTableRow(titles, 'th')}</thead>
+                        <tbody></tbody>
+                        <tfoot>${getTableRow(titles, 'th')}</tfoot>
+                    </table>`);
+                for (let [i, process] of eventInfo.processes.entries()) {
+                    let processName = getProcessName(process.pid);
+                    for (let [j, thread] of process.threads.entries()) {
+                        let threadName = getThreadName(thread.tid);
+                        for (let [k, lib] of thread.libs.entries()) {
+                            let libName = getLibName(lib.libId);
+                            for (let [t, func] of lib.functions.entries()) {
+                                let totalValue = getSampleWeight(func.c[2]);
+                                let selfValue = getSampleWeight(func.c[1]);
+                                let key = [i, j, k, t].join('_');
+                                data.push([totalValue, selfValue, func.c[0], processName,
+                                           threadName, libName, getFuncName(func.f), key])
+                           }
+                        }
                     }
                 }
-            }
-        }
-        tableStr += closeHtml('tbody') + closeHtml('table');
-        this.div.append(tableStr);
-        let table = this.div.find(`table#${tableId}`).dataTable({
-            lengthMenu: [10, 20, 50, 100, -1],
-            processing: true,
-            order: [0, 'desc'],
-            responsive: true,
-        });
+            }))
+            .then(addProgress(totalProgress / 2))
+            .then(wait(() => {
+                let table = this.tableDiv.find('table');
+                let dataTable = table.DataTable({
+                    lengthMenu: [10, 20, 50, 100, -1],
+                    order: [0, 'desc'],
+                    data: data,
+                    responsive: true,
+                });
+                dataTable.column(7).visible(false);
 
-        table.find('tr').css('cursor', 'pointer');
-        table.on('click', 'tr', function() {
-            let key = this.getAttribute('key');
-            if (!key) {
-                return;
-            }
-            let indexes = key.split('_');
-            let processInfo = eventInfo.processes[indexes[0]];
-            let threadInfo = processInfo.threads[indexes[1]];
-            let lib = threadInfo.libs[indexes[2]];
-            let func = lib.functions[indexes[3]];
-            FunctionTab.showFunction(eventInfo, processInfo, threadInfo, lib, func);
-        });
+                table.find('tr').css('cursor', 'pointer');
+                table.on('click', 'tr', function() {
+                    let data = dataTable.row(this).data();
+                    if (!data) {
+                        // A row in header or footer.
+                        return;
+                    }
+                    let key = data[7];
+                    if (!key) {
+                        return;
+                    }
+                    let indexes = key.split('_');
+                    let processInfo = eventInfo.processes[indexes[0]];
+                    let threadInfo = processInfo.threads[indexes[1]];
+                    let lib = threadInfo.libs[indexes[2]];
+                    let func = lib.functions[indexes[3]];
+                    FunctionTab.showFunction(eventInfo, processInfo, threadInfo, lib, func);
+                });
+            }));
+    }
+
+    onSampleWeightChange() {
+        createPromise()
+            .then(updateProgress('Draw SampleTable...', 0))
+            .then(() => this._drawSampleTable(100))
+            .then(hideProgress());
     }
 }
 
 
 // Show embedded flamegraph generated by inferno.
 class FlameGraphTab {
-    constructor() {
-    }
-
     init(div) {
         this.div = div;
     }
 
     draw() {
-        $('div#flamegraph_id').appendTo(this.div).css('display', 'block');
-        flamegraphInit();
+        let views = [];
+        createPromise()
+            .then(updateProgress('Draw Flamegraph...', 0))
+            .then(wait(() => {
+                this.div.empty();
+                views = createViewsForEvents(this.div, (div, eventInfo) => {
+                    return new FlameGraphViewList(div, eventInfo);
+                });
+            }))
+            .then(() => drawViewsAsync(views, 100, (view, progress) => view.drawAsync(progress)))
+            .then(hideProgress());
     }
 }
 
+// Show FlameGraphs for samples in an event type, used in FlameGraphTab.
+// 1. Draw 10 FlameGraphs at one time, and use a "More" button to show more FlameGraphs.
+// 2. First draw background of Flamegraphs, then draw details in idle time.
+class FlameGraphViewList {
+    constructor(div, eventInfo) {
+        this.div = div;
+        this.eventInfo = eventInfo;
+        this.selectorView = null;
+        this.flamegraphDiv = null;
+        this.flamegraphs = [];
+        this.moreButton = null;
+    }
+
+    drawAsync(totalProgress) {
+        this.div.empty();
+        this.selectorView = new SampleWeightSelectorView(this.div, this.eventInfo,
+                                                         () => this.onSampleWeightChange());
+        this.flamegraphDiv = $('<div>').appendTo(this.div);
+        return this._drawMoreFlameGraphs(10, totalProgress);
+    }
+
+    // Return a promise to draw flamegraphs.
+    _drawMoreFlameGraphs(moreCount, progress) {
+        let initProgress = progress / (1 + moreCount);
+        let newFlamegraphs = [];
+        return createPromise()
+        .then(wait(() => {
+            if (this.moreButton) {
+                this.moreButton.hide();
+            }
+            let pId = 0;
+            let tId = 0;
+            let newCount = this.flamegraphs.length + moreCount;
+            for (let i = 0; i < newCount; ++i) {
+                if (pId == this.eventInfo.processes.length) {
+                    break;
+                }
+                let process = this.eventInfo.processes[pId];
+                let thread = process.threads[tId];
+                if (i >= this.flamegraphs.length) {
+                    let title = `Process ${getProcessName(process.pid)} ` +
+                                `Thread ${getThreadName(thread.tid)} ` +
+                                `(Samples: ${thread.sampleCount})`;
+                    let totalCount = {countForProcess: process.eventCount,
+                                      countForThread: thread.eventCount};
+                    let flamegraph = new FlameGraphView(this.flamegraphDiv, title, totalCount,
+                                                        thread.g.c, false);
+                    flamegraph.draw();
+                    newFlamegraphs.push(flamegraph);
+                }
+                tId++;
+                if (tId == process.threads.length) {
+                    pId++;
+                    tId = 0;
+                }
+            }
+            if (pId < this.eventInfo.processes.length) {
+                // Show "More" Button.
+                if (!this.moreButton) {
+                    this.div.append(`
+                        <div style="text-align:center">
+                            <button type="button" class="btn btn-primary">More</button>
+                        </div>`);
+                    this.moreButton = this.div.children().last().find('button');
+                    this.moreButton.click(() => {
+                        createPromise().then(updateProgress('Draw FlameGraph...', 0))
+                            .then(() => this._drawMoreFlameGraphs(10, 100))
+                            .then(hideProgress());
+                    });
+                    this.moreButton.hide();
+                }
+            } else if (this.moreButton) {
+                this.moreButton.remove();
+                this.moreButton = null;
+            }
+            for (let flamegraph of newFlamegraphs) {
+                this.flamegraphs.push(flamegraph);
+            }
+        }))
+        .then(addProgress(initProgress))
+        .then(() => this.drawDetails(newFlamegraphs, progress - initProgress));
+    }
+
+    drawDetails(flamegraphs, totalProgress) {
+        return createPromise()
+            .then(() => drawViewsAsync(flamegraphs, totalProgress, (view, progress) => {
+                return createPromise()
+                    .then(wait(() => view.drawDetails(this.selectorView.getSampleWeightFunction())))
+                    .then(addProgress(progress));
+            }))
+            .then(wait(() => {
+               if (this.moreButton) {
+                   this.moreButton.show();
+               }
+            }));
+    }
+
+    onSampleWeightChange() {
+        createPromise().then(updateProgress('Draw FlameGraph...', 0))
+            .then(() => this.drawDetails(this.flamegraphs, 100))
+            .then(hideProgress());
+    }
+}
 
 // FunctionTab: show information of a function.
 // 1. Show the callgrpah and reverse callgraph of a function as flamegraphs.
@@ -598,7 +866,8 @@
         if (!tab) {
             tab = gTabs.addTab(title, new FunctionTab());
         }
-        tab.setFunction(eventInfo, processInfo, threadInfo, lib, func);
+        gTabs.setActiveAsync(title)
+            .then(() => tab.setFunction(eventInfo, processInfo, threadInfo, lib, func));
     }
 
     constructor() {
@@ -617,57 +886,77 @@
         this.lib = lib;
         this.func = func;
         this.selectorView = null;
-        this.callgraphView = null;
-        this.reverseCallgraphView = null;
-        this.sourceCodeView = null;
-        this.disassemblyView = null;
-        this.draw();
-        gTabs.setActive(this);
+        this.views = [];
+        this.redraw();
     }
 
-    draw() {
+    redraw() {
         if (!this.func) {
             return;
         }
-        this.div.empty();
-        this._drawTitle();
+        createPromise()
+            .then(updateProgress("Draw Function...", 0))
+            .then(wait(() => {
+                this.div.empty();
+                this._drawTitle();
 
-        this.selectorView = new FunctionSampleWeightSelectorView(this.div, this.eventInfo,
-            this.processInfo, this.threadInfo, () => this.onSampleWeightChange());
-        this.selectorView.draw();
+                this.selectorView = new SampleWeightSelectorView(this.div, this.eventInfo,
+                                                                 () => this.onSampleWeightChange());
+                let funcId = this.func.f;
+                let funcName = getFuncName(funcId);
+                function getNodesMatchingFuncId(root) {
+                    let nodes = [];
+                    function recursiveFn(node) {
+                        if (node.f == funcId) {
+                            nodes.push(node);
+                        } else {
+                            for (let child of node.c) {
+                                recursiveFn(child);
+                            }
+                        }
+                    }
+                    recursiveFn(root);
+                    return nodes;
+                }
+                let totalCount = {countForProcess: this.processInfo.eventCount,
+                                  countForThread: this.threadInfo.eventCount};
+                let callgraphView = new FlameGraphView(
+                    this.div, `Functions called by ${funcName}`, totalCount,
+                    getNodesMatchingFuncId(this.threadInfo.g), false);
+                callgraphView.draw();
+                this.views.push(callgraphView);
+                let reverseCallgraphView = new FlameGraphView(
+                    this.div, `Functions calling ${funcName}`, totalCount,
+                    getNodesMatchingFuncId(this.threadInfo.rg), true);
+                reverseCallgraphView.draw();
+                this.views.push(reverseCallgraphView);
+                let sourceFiles = collectSourceFilesForFunction(this.func);
+                if (sourceFiles) {
+                    this.div.append(getHtml('hr'));
+                    this.div.append(getHtml('b', {text: 'SourceCode:'}) + '<br/>');
+                    this.views.push(new SourceCodeView(this.div, sourceFiles, totalCount));
+                }
 
-        this.div.append(getHtml('hr'));
-        let funcName = getFuncName(this.func.g.f);
-        this.div.append(getHtml('b', {text: `Functions called by ${funcName}`}) + '<br/>');
-        this.callgraphView = new FlameGraphView(this.div, this.func.g, false);
-
-        this.div.append(getHtml('hr'));
-        this.div.append(getHtml('b', {text: `Functions calling ${funcName}`}) + '<br/>');
-        this.reverseCallgraphView = new FlameGraphView(this.div, this.func.rg, true);
-
-        let sourceFiles = collectSourceFilesForFunction(this.func);
-        if (sourceFiles) {
-            this.div.append(getHtml('hr'));
-            this.div.append(getHtml('b', {text: 'SourceCode:'}) + '<br/>');
-            this.sourceCodeView = new SourceCodeView(this.div, sourceFiles);
-        }
-
-        let disassembly = collectDisassemblyForFunction(this.func);
-        if (disassembly) {
-            this.div.append(getHtml('hr'));
-            this.div.append(getHtml('b', {text: 'Disassembly:'}) + '<br/>');
-            this.disassemblyView = new DisassemblyView(this.div, disassembly);
-        }
-
-        this.onSampleWeightChange();  // Manually set sample weight function for the first time.
+                let disassembly = collectDisassemblyForFunction(this.func);
+                if (disassembly) {
+                    this.div.append(getHtml('hr'));
+                    this.div.append(getHtml('b', {text: 'Disassembly:'}) + '<br/>');
+                    this.views.push(new DisassemblyView(this.div, disassembly, totalCount));
+                }
+            }))
+            .then(addProgress(25))
+            .then(() => this.drawDetails(75))
+            .then(hideProgress());
     }
 
+    draw() {}
+
     _drawTitle() {
         let eventName = this.eventInfo.eventName;
         let processName = getProcessName(this.processInfo.pid);
         let threadName = getThreadName(this.threadInfo.tid);
         let libName = getLibName(this.lib.libId);
-        let funcName = getFuncName(this.func.g.f);
+        let funcName = getFuncName(this.func.f);
         // Draw a table of 'Name', 'Value'.
         let rows = [];
         rows.push(['Event Type', eventName]);
@@ -696,106 +985,98 @@
     }
 
     onSampleWeightChange() {
+        createPromise()
+            .then(updateProgress("Draw Function...", 0))
+            .then(() => this.drawDetails(100))
+            .then(hideProgress());
+    }
+
+    drawDetails(totalProgress) {
         let sampleWeightFunction = this.selectorView.getSampleWeightFunction();
-        if (this.callgraphView) {
-            this.callgraphView.draw(sampleWeightFunction);
-        }
-        if (this.reverseCallgraphView) {
-            this.reverseCallgraphView.draw(sampleWeightFunction);
-        }
-        if (this.sourceCodeView) {
-            this.sourceCodeView.draw(sampleWeightFunction);
-        }
-        if (this.disassemblyView) {
-            this.disassemblyView.draw(sampleWeightFunction);
-        }
+        return drawViewsAsync(this.views, totalProgress, (view, progress) => {
+            return createPromise()
+                .then(wait(() => view.drawDetails(sampleWeightFunction)))
+                .then(addProgress(progress));
+        });
     }
 }
 
 
-// Select the way to show sample weight in FunctionTab.
+// Select the way to show sample weight in FlamegraphTab and FunctionTab.
 // 1. Show percentage of event count relative to all processes.
 // 2. Show percentage of event count relative to the current process.
 // 3. Show percentage of event count relative to the current thread.
 // 4. Show absolute event count.
 // 5. Show event count in milliseconds, only possible for cpu-clock or task-clock events.
-class FunctionSampleWeightSelectorView {
-    constructor(divContainer, eventInfo, processInfo, threadInfo, onSelectChange) {
-        this.div = $('<div>');
-        this.div.appendTo(divContainer);
-        this.onSelectChange = onSelectChange;
-        this.eventCountForAllProcesses = eventInfo.eventCount;
-        this.eventCountForProcess = processInfo.eventCount;
-        this.eventCountForThread = threadInfo.eventCount;
-        this.options = {
-            PERCENT_TO_ALL_PROCESSES: 0,
-            PERCENT_TO_CUR_PROCESS: 1,
-            PERCENT_TO_CUR_THREAD: 2,
-            RAW_EVENT_COUNT: 3,
-            EVENT_COUNT_IN_TIME: 4,
-        };
-        let name = eventInfo.eventName;
-        this.supportEventCountInTime = isClockEvent(eventInfo);
-        if (this.supportEventCountInTime) {
-            this.curOption = this.options.EVENT_COUNT_IN_TIME;
-        } else {
-            this.curOption = this.options.PERCENT_TO_CUR_THREAD;
+class SampleWeightSelectorView {
+    constructor(divContainer, eventInfo, onSelectChange) {
+        let options = new Map();
+        options.set('percent_to_all', 'Show percentage of event count relative to all processes');
+        options.set('percent_to_process',
+                    'Show percentage of event count relative to the current process');
+        options.set('percent_to_thread',
+                    'Show percentage of event count relative to the current thread');
+        options.set('event_count', 'Show event count');
+        if (isClockEvent(eventInfo)) {
+            options.set('event_count_in_ms', 'Show event count in milliseconds');
         }
-    }
-
-    draw() {
-        let options = [];
-        options.push('Show percentage of event count relative to all processes.');
-        options.push('Show percentage of event count relative to the current process.');
-        options.push('Show percentage of event count relative to the current thread.');
-        options.push('Show event count.');
-        if (this.supportEventCountInTime) {
-            options.push('Show event count in milliseconds.');
-        }
-        let optionStr = '';
-        for (let i = 0; i < options.length; ++i) {
-            optionStr += getHtml('option', {value: i, text: options[i]});
-        }
-        this.div.append(getHtml('select', {text: optionStr}));
-        let selectMenu = this.div.children().last();
-        selectMenu.children().eq(this.curOption).attr('selected', 'selected');
-        let thisObj = this;
-        selectMenu.selectmenu({
-            change: function() {
-                thisObj.curOption = this.value;
-                thisObj.onSelectChange();
-            },
-            width: '100%',
+        let buttons = [];
+        options.forEach((value, key) => {
+            buttons.push(`<button type="button" class="dropdown-item" key="${key}">${value}
+                          </button>`);
         });
+        this.curOption = 'percent_to_all';
+        let id = createId();
+        let str = `
+            <div class="dropdown">
+                <button type="button" class="btn btn-primary dropdown-toggle" id="${id}"
+                    data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
+                    >${options.get(this.curOption)}</button>
+                <div class="dropdown-menu" aria-labelledby="${id}">${buttons.join('')}</div>
+            </div>
+        `;
+        divContainer.append(str);
+        divContainer.children().last().on('hidden.bs.dropdown', (e) => {
+            if (e.clickEvent) {
+                let button = $(e.clickEvent.target);
+                let newOption = button.attr('key');
+                if (newOption && this.curOption != newOption) {
+                    this.curOption = newOption;
+                    divContainer.find(`#${id}`).text(options.get(this.curOption));
+                    onSelectChange();
+                }
+            }
+        });
+        this.countForAllProcesses = eventInfo.eventCount;
     }
 
     getSampleWeightFunction() {
-        let thisObj = this;
-        if (this.curOption == this.options.PERCENT_TO_ALL_PROCESSES) {
-            return function(eventCount) {
-                let percent = eventCount * 100.0 / thisObj.eventCountForAllProcesses;
+        if (this.curOption == 'percent_to_all') {
+            let countForAllProcesses = this.countForAllProcesses;
+            return function(eventCount, _) {
+                let percent = eventCount * 100.0 / countForAllProcesses;
                 return percent.toFixed(2) + '%';
             };
         }
-        if (this.curOption == this.options.PERCENT_TO_CUR_PROCESS) {
-            return function(eventCount) {
-                let percent = eventCount * 100.0 / thisObj.eventCountForProcess;
+        if (this.curOption == 'percent_to_process') {
+            return function(eventCount, totalCount) {
+                let percent = eventCount * 100.0 / totalCount.countForProcess;
                 return percent.toFixed(2) + '%';
             };
         }
-        if (this.curOption == this.options.PERCENT_TO_CUR_THREAD) {
-            return function(eventCount) {
-                let percent = eventCount * 100.0 / thisObj.eventCountForThread;
+        if (this.curOption == 'percent_to_thread') {
+            return function(eventCount, totalCount) {
+                let percent = eventCount * 100.0 / totalCount.countForThread;
                 return percent.toFixed(2) + '%';
             };
         }
-        if (this.curOption == this.options.RAW_EVENT_COUNT) {
-            return function(eventCount) {
+        if (this.curOption == 'event_count') {
+            return function(eventCount, _) {
                 return '' + eventCount;
             };
         }
-        if (this.curOption == this.options.EVENT_COUNT_IN_TIME) {
-            return function(eventCount) {
+        if (this.curOption == 'event_count_in_ms') {
+            return function(eventCount, _) {
                 let timeInMs = eventCount / 1000000.0;
                 return timeInMs.toFixed(3) + ' ms';
             };
@@ -803,22 +1084,23 @@
     }
 }
 
-
 // Given a callgraph, show the flamegraph.
 class FlameGraphView {
-    // If reverseOrder is false, the root of the flamegraph is at the bottom,
-    // otherwise it is at the top.
-    constructor(divContainer, callgraph, reverseOrder) {
-        this.id = divContainer.children().length;
-        this.div = $('<div>', {id: 'fg_' + this.id});
+    constructor(divContainer, title, totalCount, initNodes, reverseOrder) {
+        this.id = createId();
+        this.div = $('<div>', {id: this.id,
+                               style: 'font-family: Monospace; font-size: 12px'});
         this.div.appendTo(divContainer);
-        this.callgraph = callgraph;
+        this.title = title;
+        this.totalCount = totalCount;
         this.reverseOrder = reverseOrder;
         this.sampleWeightFunction = null;
-        this.svgWidth = $(window).width();
         this.svgNodeHeight = 17;
-        this.fontSize = 12;
-
+        this.initNodes = initNodes;
+        this.sumCount = 0;
+        for (let node of initNodes) {
+            this.sumCount += node.s;
+        }
         function getMaxDepth(node) {
             let depth = 0;
             for (let child of node.c) {
@@ -826,42 +1108,60 @@
             }
             return depth + 1;
         }
-        this.maxDepth = getMaxDepth(this.callgraph);
+        this.maxDepth = 0;
+        for (let node of this.initNodes) {
+            this.maxDepth = Math.max(this.maxDepth, getMaxDepth(node));
+        }
         this.svgHeight = this.svgNodeHeight * (this.maxDepth + 3);
+        this.svgStr = null;
+        this.svgDiv = null;
+        this.svg = null;
     }
 
-    draw(sampleWeightFunction) {
-        this.sampleWeightFunction = sampleWeightFunction;
+    draw() {
+        // Only draw skeleton.
         this.div.empty();
-        this.div.css('width', '100%').css('height', this.svgHeight + 'px');
-        let svgStr = '<svg xmlns="http://www.w3.org/2000/svg" \
-        xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" \
-        width="100%" height="100%" style="border: 1px solid black; font-family: Monospace;"> \
-        </svg>';
-        this.div.append(svgStr);
-        this.svg = this.div.find('svg');
+        this.div.append(`<p><b>${this.title}</b></p>`);
+        this.svgStr = [];
         this._renderBackground();
-        this._renderSvgNodes(this.callgraph, 0, 0);
+        this.svgStr.push('</svg></div>');
+        this.div.append(this.svgStr.join(''));
+        this.svgDiv = this.div.children().last();
+        this.div.append('<br/><br/>');
+    }
+
+    drawDetails(sampleWeightFunction) {
+        this.sampleWeightFunction = sampleWeightFunction;
+        this.svgStr = [];
+        this._renderBackground();
+        this._renderSvgNodes();
         this._renderUnzoomNode();
         this._renderInfoNode();
         this._renderPercentNode();
-        // Make the added nodes in the svg visible.
-        this.div.html(this.div.html());
-        this.svg = this.div.find('svg');
+        this._renderSearchNode();
+        // It is much faster to add html content to svgStr than adding it directly to svgDiv.
+        this.svgDiv.html(this.svgStr.join(''));
+        this.svgStr = [];
+        this.svg = this.svgDiv.find('svg');
         this._adjustTextSize();
         this._enableZoom();
         this._enableInfo();
+        this._enableSearch();
         this._adjustTextSizeOnResize();
     }
 
     _renderBackground() {
-        this.svg.append(`<defs > <linearGradient id="background_gradient_${this.id}"
-                                  y1="0" y2="1" x1="0" x2="0" > \
-                                  <stop stop-color="#eeeeee" offset="5%" /> \
-                                  <stop stop-color="#efefb1" offset="90%" /> \
-                                  </linearGradient> \
-                         </defs> \
-                         <rect x="0" y="0" width="100%" height="100%" \
+        this.svgStr.push(`
+            <div style="width: 100%; height: ${this.svgHeight}px;">
+                <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+                    version="1.1" width="100%" height="100%" style="border: 1px solid black; ">
+                        <defs > <linearGradient id="background_gradient_${this.id}"
+                                  y1="0" y2="1" x1="0" x2="0" >
+                                  <stop stop-color="#eeeeee" offset="5%" />
+                                  <stop stop-color="#efefb1" offset="90%" />
+                                  </linearGradient>
+                         </defs>
+                         <rect x="0" y="0" width="100%" height="100%"
                            fill="url(#background_gradient_${this.id})" />`);
     }
 
@@ -873,7 +1173,7 @@
     }
 
     _getWidthPercentage(eventCount) {
-        return eventCount * 100.0 / this.callgraph.s;
+        return eventCount * 100.0 / this.sumCount;
     }
 
     _getHeatColor(widthPercentage) {
@@ -884,10 +1184,51 @@
         };
     }
 
-    _renderSvgNodes(callNode, depth, xOffset) {
+    _renderSvgNodes() {
+        let fakeNodes = [{c: this.initNodes}];
+        let children = this._splitChildrenForNodes(fakeNodes);
+        let xOffset = 0;
+        for (let child of children) {
+            xOffset = this._renderSvgNodesWithSameRoot(child, 0, xOffset);
+        }
+    }
+
+    // Return an array of children nodes, with children having the same functionId merged in a
+    // subarray.
+    _splitChildrenForNodes(nodes) {
+        let map = new Map();
+        for (let node of nodes) {
+            for (let child of node.c) {
+                let subNodes = map.get(child.f);
+                if (subNodes) {
+                    subNodes.push(child);
+                } else {
+                    map.set(child.f, [child]);
+                }
+            }
+        }
+        let res = [];
+        for (let subNodes of map.values()) {
+            res.push(subNodes.length == 1 ? subNodes[0] : subNodes);
+        }
+        return res;
+    }
+
+    // nodes can be a CallNode, or an array of CallNodes with the same functionId.
+    _renderSvgNodesWithSameRoot(nodes, depth, xOffset) {
         let x = xOffset;
         let y = this._getYForDepth(depth);
-        let width = this._getWidthPercentage(callNode.s);
+        let isArray = Array.isArray(nodes);
+        let funcId;
+        let sumCount;
+        if (isArray) {
+            funcId = nodes[0].f;
+            sumCount = nodes.reduce((acc, cur) => acc + cur.s, 0);
+        } else {
+            funcId = nodes.f;
+            sumCount = nodes.s;
+        }
+        let width = this._getWidthPercentage(sumCount);
         if (width < 0.1) {
             return xOffset;
         }
@@ -896,52 +1237,59 @@
         for (let key in color) {
             borderColor[key] = Math.max(0, color[key] - 50);
         }
-        let funcName = getFuncName(callNode.f);
-        let libName = getLibNameOfFunction(callNode.f);
-        let sampleWeight = this.sampleWeightFunction(callNode.s);
-        let title = funcName + ' | ' + libName + ' (' + callNode.s + ' events: ' +
+        let funcName = getFuncName(funcId);
+        let libName = getLibNameOfFunction(funcId);
+        let sampleWeight = this.sampleWeightFunction(sumCount, this.totalCount);
+        let title = funcName + ' | ' + libName + ' (' + sumCount + ' events: ' +
                     sampleWeight + ')';
-        this.svg.append(`<g> <title>${title}</title> <rect x="${x}%" y="${y}" ox="${x}" \
-                        depth="${depth}" width="${width}%" owidth="${width}" height="15.0" \
-                        ofill="rgb(${color.r},${color.g},${color.b})" \
-                        fill="rgb(${color.r},${color.g},${color.b})" \
-                        style="stroke:rgb(${borderColor.r},${borderColor.g},${borderColor.b})"/> \
-                        <text x="${x}%" y="${y + 12}" font-size="${this.fontSize}" \
-                        font-family="Monospace"></text></g>`);
+        this.svgStr.push(`<g><title>${title}</title> <rect x="${x}%" y="${y}" ox="${x}"
+                        depth="${depth}" width="${width}%" owidth="${width}" height="15.0"
+                        ofill="rgb(${color.r},${color.g},${color.b})"
+                        fill="rgb(${color.r},${color.g},${color.b})"
+                        style="stroke:rgb(${borderColor.r},${borderColor.g},${borderColor.b})"/>
+                        <text x="${x}%" y="${y + 12}"></text></g>`);
 
+        let children = isArray ? this._splitChildrenForNodes(nodes) : nodes.c;
         let childXOffset = xOffset;
-        for (let child of callNode.c) {
-            childXOffset = this._renderSvgNodes(child, depth + 1, childXOffset);
+        for (let child of children) {
+            childXOffset = this._renderSvgNodesWithSameRoot(child, depth + 1, childXOffset);
         }
         return xOffset + width;
     }
 
     _renderUnzoomNode() {
-        this.svg.append(`<rect id="zoom_rect_${this.id}" style="display:none;stroke:rgb(0,0,0);" \
-        rx="10" ry="10" x="10" y="10" width="80" height="30" \
-        fill="rgb(255,255,255)"/> \
+        this.svgStr.push(`<rect id="zoom_rect_${this.id}" style="display:none;stroke:rgb(0,0,0);"
+        rx="10" ry="10" x="10" y="10" width="80" height="30"
+        fill="rgb(255,255,255)"/>
          <text id="zoom_text_${this.id}" x="19" y="30" style="display:none">Zoom out</text>`);
     }
 
     _renderInfoNode() {
-        this.svg.append(`<clipPath id="info_clip_path_${this.id}"> \
-                         <rect style="stroke:rgb(0,0,0);" rx="10" ry="10" x="120" y="10" \
-                         width="789" height="30" fill="rgb(255,255,255)"/> \
-                         </clipPath> \
-                         <rect style="stroke:rgb(0,0,0);" rx="10" ry="10" x="120" y="10" \
-                         width="799" height="30" fill="rgb(255,255,255)"/> \
-                         <text clip-path="url(#info_clip_path_${this.id})" \
+        this.svgStr.push(`<clipPath id="info_clip_path_${this.id}">
+                         <rect style="stroke:rgb(0,0,0);" rx="10" ry="10" x="120" y="10"
+                         width="789" height="30" fill="rgb(255,255,255)"/>
+                         </clipPath>
+                         <rect style="stroke:rgb(0,0,0);" rx="10" ry="10" x="120" y="10"
+                         width="799" height="30" fill="rgb(255,255,255)"/>
+                         <text clip-path="url(#info_clip_path_${this.id})"
                          id="info_text_${this.id}" x="128" y="30"></text>`);
     }
 
     _renderPercentNode() {
-        this.svg.append(`<rect style="stroke:rgb(0,0,0);" rx="10" ry="10" \
-                         x="934" y="10" width="150" height="30" \
-                         fill="rgb(255,255,255)"/> \
-                         <text id="percent_text_${this.id}" text-anchor="end" \
+        this.svgStr.push(`<rect style="stroke:rgb(0,0,0);" rx="10" ry="10"
+                         x="934" y="10" width="150" height="30"
+                         fill="rgb(255,255,255)"/>
+                         <text id="percent_text_${this.id}" text-anchor="end"
                          x="1074" y="30"></text>`);
     }
 
+    _renderSearchNode() {
+        this.svgStr.push(`<rect style="stroke:rgb(0,0,0); rx="10" ry="10"
+                         x="1150" y="10" width="80" height="30"
+                         fill="rgb(255,255,255)" class="search"/>
+                         <text x="1160" y="30" class="search">Search</text>`);
+    }
+
     _adjustTextSizeForNode(g) {
         let text = g.find('text');
         let width = parseFloat(g.find('rect').attr('width')) * this.svgWidth * 0.01;
@@ -972,7 +1320,7 @@
     }
 
     _enableZoom() {
-        this.zoomStack = [this.svg.find('g').first().get(0)];
+        this.zoomStack = [null];
         this.svg.find('g').css('cursor', 'pointer').click(zoom);
         this.svg.find(`#zoom_rect_${this.id}`).css('cursor', 'pointer').click(unzoom);
         this.svg.find(`#zoom_text_${this.id}`).css('cursor', 'pointer').click(unzoom);
@@ -997,12 +1345,18 @@
         }
 
         function displayFromElement(g) {
-            g = $(g);
-            let clickedRect = g.find('rect');
-            let clickedOriginX = parseFloat(clickedRect.attr('ox'));
-            let clickedDepth = parseInt(clickedRect.attr('depth'));
-            let clickedOriginWidth = parseFloat(clickedRect.attr('owidth'));
-            let scaleFactor = 100.0 / clickedOriginWidth;
+            let clickedOriginX = 0;
+            let clickedDepth = 0;
+            let clickedOriginWidth = 100;
+            let scaleFactor = 1;
+            if (g) {
+                g = $(g);
+                let clickedRect = g.find('rect');
+                clickedOriginX = parseFloat(clickedRect.attr('ox'));
+                clickedDepth = parseInt(clickedRect.attr('depth'));
+                clickedOriginWidth = parseFloat(clickedRect.attr('owidth'));
+                scaleFactor = 100.0 / clickedOriginWidth;
+            }
             thisObj.svg.find('g').each(function(_, g) {
                 g = $(g);
                 let text = g.find('text');
@@ -1059,6 +1413,27 @@
         });
     }
 
+    _enableSearch() {
+        this.svg.find('.search').css('cursor', 'pointer').click(() => {
+            let term = prompt('Search for:', '');
+            if (!term) {
+                this.svg.find('g > rect').each(function() {
+                    this.attributes['fill'].value = this.attributes['ofill'].value;
+                });
+            } else {
+                this.svg.find('g').each(function() {
+                    let title = this.getElementsByTagName('title')[0];
+                    let rect = this.getElementsByTagName('rect')[0];
+                    if (title.textContent.indexOf(term) != -1) {
+                        rect.attributes['fill'].value = 'rgb(230,100,230)';
+                    } else {
+                        rect.attributes['fill'].value = rect.attributes['ofill'].value;
+                    }
+                });
+            }
+        });
+    }
+
     _adjustTextSizeOnResize() {
         function throttle(callback) {
             let running = false;
@@ -1122,7 +1497,7 @@
     }
 
     // Show lines for the function.
-    let funcRange = getFuncSourceRange(func.g.f);
+    let funcRange = getFuncSourceRange(func.f);
     if (funcRange) {
         let file = getFile(funcRange.fileId);
         file.addLineRange(funcRange.startLine);
@@ -1156,13 +1531,14 @@
 // Show annotated source code of a function.
 class SourceCodeView {
 
-    constructor(divContainer, sourceFiles) {
+    constructor(divContainer, sourceFiles, totalCount) {
         this.div = $('<div>');
         this.div.appendTo(divContainer);
         this.sourceFiles = sourceFiles;
+        this.totalCount = totalCount;
     }
 
-    draw(sampleWeightFunction) {
+    drawDetails(sampleWeightFunction) {
         google.charts.setOnLoadCallback(() => this.realDraw(sampleWeightFunction));
     }
 
@@ -1179,8 +1555,8 @@
                 let totalValue = '';
                 let selfValue = '';
                 if (countInfo.subtreeEventCount != 0) {
-                    totalValue = sampleWeightFunction(countInfo.subtreeEventCount);
-                    selfValue = sampleWeightFunction(countInfo.eventCount);
+                    totalValue = sampleWeightFunction(countInfo.subtreeEventCount, this.totalCount);
+                    selfValue = sampleWeightFunction(countInfo.eventCount, this.totalCount);
                 }
                 rows.push([lineNumber, totalValue, selfValue, code]);
             }
@@ -1217,7 +1593,7 @@
         return null;
     }
     let hitAddrs = func.a;
-    let rawCode = getFuncDisassembly(func.g.f);
+    let rawCode = getFuncDisassembly(func.f);
     if (!rawCode) {
         return null;
     }
@@ -1258,13 +1634,14 @@
 // Show annotated disassembly of a function.
 class DisassemblyView {
 
-    constructor(divContainer, disassembly) {
+    constructor(divContainer, disassembly, totalCount) {
         this.div = $('<div>');
         this.div.appendTo(divContainer);
         this.disassembly = disassembly;
+        this.totalCount = totalCount;
     }
 
-    draw(sampleWeightFunction) {
+    drawDetails(sampleWeightFunction) {
         google.charts.setOnLoadCallback(() => this.realDraw(sampleWeightFunction));
     }
 
@@ -1277,8 +1654,8 @@
             let totalValue = '';
             let selfValue = '';
             if (line.subtreeEventCount != 0) {
-                totalValue = sampleWeightFunction(line.subtreeEventCount);
-                selfValue = sampleWeightFunction(line.eventCount);
+                totalValue = sampleWeightFunction(line.subtreeEventCount, this.totalCount);
+                selfValue = sampleWeightFunction(line.eventCount, this.totalCount);
             }
             rows.push([totalValue, selfValue, code]);
         }
@@ -1306,7 +1683,6 @@
 
 
 function initGlobalObjects() {
-    gTabs = new TabManager($('div#report_content'));
     let recordData = $('#record_data').text();
     gRecordInfo = JSON.parse(recordData);
     gProcesses = gRecordInfo.processNames;
@@ -1318,13 +1694,17 @@
 }
 
 function createTabs() {
+    gTabs = new TabManager($('div#report_content'));
     gTabs.addTab('Chart Statistics', new ChartStatTab());
     gTabs.addTab('Sample Table', new SampleTableTab());
     gTabs.addTab('Flamegraph', new FlameGraphTab());
-    gTabs.draw();
 }
 
+// Global draw objects
 let gTabs;
+let gProgressBar = new ProgressBar();
+
+// Gobal Json Data
 let gRecordInfo;
 let gProcesses;
 let gThreads;
@@ -1333,7 +1713,45 @@
 let gSampleInfo;
 let gSourceFiles;
 
-initGlobalObjects();
-createTabs();
+function updateProgress(text, progress) {
+    return () => gProgressBar.updateAsync(text, progress);
+}
 
-});
\ No newline at end of file
+function addProgress(progress) {
+    return () => gProgressBar.updateAsync(null, gProgressBar.progress + progress);
+}
+
+function hideProgress() {
+    return () => gProgressBar.hide();
+}
+
+function createPromise(callback) {
+    if (callback) {
+        return new Promise((resolve, _) => callback(resolve));
+    }
+    return new Promise((resolve,_) => resolve());
+}
+
+function waitDocumentReady() {
+    return createPromise((resolve) => $(document).ready(resolve));
+}
+
+function wait(functionCall) {
+    return () => {
+        functionCall();
+        return createPromise();
+    };
+}
+
+createPromise()
+    .then(updateProgress('Load page...', 0))
+    .then(waitDocumentReady)
+    .then(updateProgress('Parse Json data...', 20))
+    .then(wait(initGlobalObjects))
+    .then(updateProgress('Create tabs...', 30))
+    .then(wait(createTabs))
+    .then(updateProgress('Draw ChartStat...', 40))
+    .then(() => gTabs.setActiveAsync('Chart Statistics'))
+    .then(updateProgress(null, 100))
+    .then(hideProgress());
+})();
diff --git a/report_html.py b/report_html.py
old mode 100644
new mode 100755
index b52a8d1..1a616fb
--- a/report_html.py
+++ b/report_html.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 #
 # Copyright (C) 2017 The Android Open Source Project
 #
@@ -16,16 +16,18 @@
 #
 
 import argparse
+import collections
 import datetime
 import json
 import os
-import subprocess
 import sys
-import tempfile
 
 from simpleperf_report_lib import ReportLib
-from utils import *
+from utils import log_info, log_exit
+from utils import Addr2Nearestline, get_script_dir, Objdump, open_report_in_browser
+from utils import SourceFileSearcher
 
+MAX_CALLSTACK_LENGTH = 750
 
 class HtmlWriter(object):
 
@@ -80,10 +82,24 @@
         result = {}
         result['eventName'] = self.name
         result['eventCount'] = self.event_count
+        processes = sorted(self.processes.values(), key=lambda a: a.event_count, reverse=True)
         result['processes'] = [process.get_sample_info(gen_addr_hit_map)
-                                  for process in self.processes.values()]
+                               for process in processes]
         return result
 
+    @property
+    def threads(self):
+        for process in self.processes.values():
+            for thread in process.threads.values():
+                yield thread
+
+    @property
+    def libraries(self):
+        for process in self.processes.values():
+            for thread in process.threads.values():
+                for lib in thread.libs.values():
+                    yield lib
+
 
 class ProcessScope(object):
 
@@ -106,8 +122,9 @@
         result = {}
         result['pid'] = self.pid
         result['eventCount'] = self.event_count
+        threads = sorted(self.threads.values(), key=lambda a: a.event_count, reverse=True)
         result['threads'] = [thread.get_sample_info(gen_addr_hit_map)
-                                for thread in self.threads.values()]
+                             for thread in threads]
         return result
 
 
@@ -117,14 +134,16 @@
         self.tid = tid
         self.name = ''
         self.event_count = 0
+        self.sample_count = 0
         self.libs = {}  # map from lib_id to LibScope
+        self.call_graph = CallNode(-1)
+        self.reverse_call_graph = CallNode(-1)
 
     def add_callstack(self, event_count, callstack, build_addr_hit_map):
         """ callstack is a list of tuple (lib_id, func_id, addr).
             For each i > 0, callstack[i] calls callstack[i-1]."""
         hit_func_ids = set()
-        for i in range(len(callstack)):
-            lib_id, func_id, addr = callstack[i]
+        for i, (lib_id, func_id, addr) in enumerate(callstack):
             # When a callstack contains recursive function, only add for each function once.
             if func_id in hit_func_ids:
                 continue
@@ -134,30 +153,51 @@
             if not lib:
                 lib = self.libs[lib_id] = LibScope(lib_id)
             function = lib.get_function(func_id)
+            function.subtree_event_count += event_count
             if i == 0:
                 lib.event_count += event_count
+                function.event_count += event_count
                 function.sample_count += 1
-            function.add_reverse_callchain(callstack, i + 1, len(callstack), event_count)
-
             if build_addr_hit_map:
                 function.build_addr_hit_map(addr, event_count if i == 0 else 0, event_count)
 
-        hit_func_ids.clear()
-        for i in range(len(callstack) - 1, -1, -1):
-            lib_id, func_id, _ = callstack[i]
-            # When a callstack contains recursive function, only add for each function once.
-            if func_id in hit_func_ids:
-                continue
-            hit_func_ids.add(func_id)
-            lib = self.libs.get(lib_id)
-            lib.get_function(func_id).add_callchain(callstack, i - 1, -1, event_count)
+        # build call graph and reverse call graph
+        node = self.call_graph
+        for item in reversed(callstack):
+            node = node.get_child(item[1])
+        node.event_count += event_count
+        node = self.reverse_call_graph
+        for item in callstack:
+            node = node.get_child(item[1])
+        node.event_count += event_count
+
+    def update_subtree_event_count(self):
+        self.call_graph.update_subtree_event_count()
+        self.reverse_call_graph.update_subtree_event_count()
+
+    def limit_percents(self, min_func_limit, min_callchain_percent, hit_func_ids):
+        for lib in self.libs.values():
+            to_del_funcs = []
+            for function in lib.functions.values():
+                if function.subtree_event_count < min_func_limit:
+                    to_del_funcs.append(function.func_id)
+                else:
+                    hit_func_ids.add(function.func_id)
+            for func_id in to_del_funcs:
+                del lib.functions[func_id]
+        min_limit = min_callchain_percent * 0.01 * self.call_graph.subtree_event_count
+        self.call_graph.cut_edge(min_limit, hit_func_ids)
+        self.reverse_call_graph.cut_edge(min_limit, hit_func_ids)
 
     def get_sample_info(self, gen_addr_hit_map):
         result = {}
         result['tid'] = self.tid
         result['eventCount'] = self.event_count
+        result['sampleCount'] = self.sample_count
         result['libs'] = [lib.gen_sample_info(gen_addr_hit_map)
-                            for lib in self.libs.values()]
+                          for lib in self.libs.values()]
+        result['g'] = self.call_graph.gen_sample_info()
+        result['rg'] = self.reverse_call_graph.gen_sample_info()
         return result
 
 
@@ -179,32 +219,21 @@
         result['libId'] = self.lib_id
         result['eventCount'] = self.event_count
         result['functions'] = [func.gen_sample_info(gen_addr_hit_map)
-                                  for func in self.functions.values()]
+                               for func in self.functions.values()]
         return result
 
 
 class FunctionScope(object):
 
     def __init__(self, func_id):
+        self.func_id = func_id
         self.sample_count = 0
-        self.call_graph = CallNode(func_id)
-        self.reverse_call_graph = CallNode(func_id)
+        self.event_count = 0
+        self.subtree_event_count = 0
         self.addr_hit_map = None  # map from addr to [event_count, subtree_event_count].
         # map from (source_file_id, line) to [event_count, subtree_event_count].
         self.line_hit_map = None
 
-    def add_callchain(self, callchain, start, end, event_count):
-        node = self.call_graph
-        for i in range(start, end, -1):
-            node = node.get_child(callchain[i][1])
-        node.event_count += event_count
-
-    def add_reverse_callchain(self, callchain, start, end, event_count):
-        node = self.reverse_call_graph
-        for i in range(start, end):
-            node = node.get_child(callchain[i][1])
-        node.event_count += event_count
-
     def build_addr_hit_map(self, addr, event_count, subtree_event_count):
         if self.addr_hit_map is None:
             self.addr_hit_map = {}
@@ -226,21 +255,10 @@
             count_info[0] += event_count
             count_info[1] += subtree_event_count
 
-    def update_subtree_event_count(self):
-        a = self.call_graph.update_subtree_event_count()
-        b = self.reverse_call_graph.update_subtree_event_count()
-        return max(a, b)
-
-    def limit_callchain_percent(self, min_callchain_percent, hit_func_ids):
-        min_limit = min_callchain_percent * 0.01 * self.call_graph.subtree_event_count
-        self.call_graph.cut_edge(min_limit, hit_func_ids)
-        self.reverse_call_graph.cut_edge(min_limit, hit_func_ids)
-
     def gen_sample_info(self, gen_addr_hit_map):
         result = {}
-        result['c'] = self.sample_count
-        result['g'] = self.call_graph.gen_sample_info()
-        result['rg'] = self.reverse_call_graph.gen_sample_info()
+        result['f'] = self.func_id
+        result['c'] = [self.sample_count, self.event_count, self.subtree_event_count]
         if self.line_hit_map:
             items = []
             for key in self.line_hit_map:
@@ -263,7 +281,7 @@
         self.event_count = 0
         self.subtree_event_count = 0
         self.func_id = func_id
-        self.children = {}  # map from func_id to CallNode
+        self.children = collections.OrderedDict()  # map from func_id to CallNode
 
     def get_child(self, func_id):
         child = self.children.get(func_id)
@@ -398,68 +416,6 @@
                 source_file.add_source_code(real_path)
 
 
-class SourceFileSearcher(object):
-
-    SOURCE_FILE_EXTS = {'.h', '.hh', '.H', '.hxx', '.hpp', '.h++',
-                        '.c', '.cc', '.C', '.cxx', '.cpp', '.c++',
-                        '.java', '.kt'}
-
-    @classmethod
-    def is_source_filename(cls, filename):
-        ext = os.path.splitext(filename)[1]
-        return ext in cls.SOURCE_FILE_EXTS
-
-    """" Find source file paths in the file system.
-        The file paths reported by addr2line are the paths stored in debug sections
-        of shared libraries. And we need to convert them to file paths in the file
-        system. It is done in below steps:
-        1. Collect all file paths under the provided source_dirs. The suffix of a
-           source file should contain one of below:
-            h: for C/C++ header files.
-            c: for C/C++ source files.
-            java: for Java source files.
-            kt: for Kotlin source files.
-        2. Given an abstract_path reported by addr2line, select the best real path
-           as below:
-           2.1 Find all real paths with the same file name as the abstract path.
-           2.2 Select the real path having the longest common suffix with the abstract path.
-    """
-    def __init__(self, source_dirs):
-        # Map from filename to a list of reversed directory path containing filename.
-        self.filename_to_rparents = {}
-        self._collect_paths(source_dirs)
-
-    def _collect_paths(self, source_dirs):
-        for source_dir in source_dirs:
-            for parent, _, file_names in os.walk(source_dir):
-                rparent = None
-                for file_name in file_names:
-                    if self.is_source_filename(file_name):
-                        rparents = self.filename_to_rparents.get(file_name)
-                        if rparents is None:
-                            rparents = self.filename_to_rparents[file_name] = []
-                        if rparent is None:
-                            rparent = parent[::-1]
-                        rparents.append(rparent)
-
-    def get_real_path(self, abstract_path):
-        abstract_path = abstract_path.replace('/', os.sep)
-        abstract_parent, file_name = os.path.split(abstract_path)
-        abstract_rparent = abstract_parent[::-1]
-        real_rparents = self.filename_to_rparents.get(file_name)
-        if real_rparents is None:
-            return None
-        best_matched_rparent = None
-        best_common_length = -1
-        for real_rparent in real_rparents:
-            length = len(os.path.commonprefix((real_rparent, abstract_rparent)))
-            if length > best_common_length:
-                best_common_length = length
-                best_matched_rparent = real_rparent
-        if best_matched_rparent is None:
-            return None
-        return os.path.join(best_matched_rparent[::-1], file_name)
-
 
 class RecordData(object):
 
@@ -495,7 +451,10 @@
                 threadInfo = {
                     tid
                     eventCount
+                    sampleCount
                     libs: [libInfo],
+                    g: callGraph,
+                    rg: reverseCallgraph
                 }
                 libInfo = {
                     libId,
@@ -503,9 +462,8 @@
                     functions: [funcInfo]
                 }
                 funcInfo = {
-                    c: sampleCount
-                    g: callGraph
-                    rg: reverseCallgraph
+                    f: functionId
+                    c: [sampleCount, eventCount, subTreeEventCount]
                     s: [sourceCodeInfo] [optional]
                     a: [addrInfo] (sorted by addrInfo.addr) [optional]
                 }
@@ -582,6 +540,7 @@
             process.event_count += raw_sample.period
             thread = process.get_thread(raw_sample.tid, raw_sample.thread_comm)
             thread.event_count += raw_sample.period
+            thread.sample_count += 1
 
             lib_id = self.libs.get_lib_id(symbol.dso_name)
             func_id = self.functions.get_func_id(lib_id, symbol)
@@ -591,33 +550,27 @@
                 lib_id = self.libs.get_lib_id(symbol.dso_name)
                 func_id = self.functions.get_func_id(lib_id, symbol)
                 callstack.append((lib_id, func_id, symbol.vaddr_in_file))
+            if len(callstack) > MAX_CALLSTACK_LENGTH:
+                callstack = callstack[:MAX_CALLSTACK_LENGTH]
             thread.add_callstack(raw_sample.period, callstack, self.build_addr_hit_map)
 
         for event in self.events.values():
-            for process in event.processes.values():
-                for thread in process.threads.values():
-                    for lib in thread.libs.values():
-                        for func_id in lib.functions:
-                            function = lib.functions[func_id]
-                            function.update_subtree_event_count()
+            for thread in event.threads:
+                thread.update_subtree_event_count()
 
     def limit_percents(self, min_func_percent, min_callchain_percent):
         hit_func_ids = set()
         for event in self.events.values():
             min_limit = event.event_count * min_func_percent * 0.01
             for process in event.processes.values():
+                to_del_threads = []
                 for thread in process.threads.values():
-                    for lib in thread.libs.values():
-                        to_del_func_ids = []
-                        for func_id in lib.functions:
-                            function = lib.functions[func_id]
-                            if function.call_graph.subtree_event_count < min_limit:
-                                to_del_func_ids.append(func_id)
-                            else:
-                                function.limit_callchain_percent(min_callchain_percent,
-                                                                 hit_func_ids)
-                        for func_id in to_del_func_ids:
-                            del lib.functions[func_id]
+                    if thread.call_graph.subtree_event_count < min_limit:
+                        to_del_threads.append(thread.tid)
+                    else:
+                        thread.limit_percents(min_limit, min_callchain_percent, hit_func_ids)
+                for thread in to_del_threads:
+                    del process.threads[thread]
         self.functions.trim_functions(hit_func_ids)
 
     def _get_event(self, event_name):
@@ -625,32 +578,31 @@
             self.events[event_name] = EventScope(event_name)
         return self.events[event_name]
 
-    def add_source_code(self, source_dirs):
+    def add_source_code(self, source_dirs, filter_lib):
         """ Collect source code information:
             1. Find line ranges for each function in FunctionSet.
             2. Find line for each addr in FunctionScope.addr_hit_map.
             3. Collect needed source code in SourceFileSet.
         """
-        addr2line = Addr2Nearestline(self.ndk_path, self.binary_cache_path)
+        addr2line = Addr2Nearestline(self.ndk_path, self.binary_cache_path, False)
         # Request line range for each function.
         for function in self.functions.id_to_func.values():
             if function.func_name == 'unknown':
                 continue
             lib_name = self.libs.get_lib_name(function.lib_id)
-            addr2line.add_addr(lib_name, function.start_addr, function.start_addr)
-            addr2line.add_addr(lib_name, function.start_addr,
-                               function.start_addr + function.addr_len - 1)
+            if filter_lib(lib_name):
+                addr2line.add_addr(lib_name, function.start_addr, function.start_addr)
+                addr2line.add_addr(lib_name, function.start_addr,
+                                   function.start_addr + function.addr_len - 1)
         # Request line for each addr in FunctionScope.addr_hit_map.
         for event in self.events.values():
-            for process in event.processes.values():
-                for thread in process.threads.values():
-                    for lib in thread.libs.values():
-                        lib_name = self.libs.get_lib_name(lib.lib_id)
-                        for function in lib.functions.values():
-                            func_addr = self.functions.id_to_func[
-                                            function.call_graph.func_id].start_addr
-                            for addr in function.addr_hit_map:
-                                addr2line.add_addr(lib_name, func_addr, addr)
+            for lib in event.libraries:
+                lib_name = self.libs.get_lib_name(lib.lib_id)
+                if filter_lib(lib_name):
+                    for function in lib.functions.values():
+                        func_addr = self.functions.id_to_func[function.func_id].start_addr
+                        for addr in function.addr_hit_map:
+                            addr2line.add_addr(lib_name, func_addr, addr)
         addr2line.convert_addrs_to_lines()
 
         # Set line range for each function.
@@ -658,9 +610,10 @@
             if function.func_name == 'unknown':
                 continue
             dso = addr2line.get_dso(self.libs.get_lib_name(function.lib_id))
+            if not dso:
+                continue
             start_source = addr2line.get_addr_source(dso, function.start_addr)
-            end_source = addr2line.get_addr_source(dso,
-                            function.start_addr + function.addr_len - 1)
+            end_source = addr2line.get_addr_source(dso, function.start_addr + function.addr_len - 1)
             if not start_source or not end_source:
                 continue
             start_file_path, start_line = start_source[-1]
@@ -673,38 +626,49 @@
 
         # Build FunctionScope.line_hit_map.
         for event in self.events.values():
-            for process in event.processes.values():
-                for thread in process.threads.values():
-                    for lib in thread.libs.values():
-                        dso = addr2line.get_dso(self.libs.get_lib_name(lib.lib_id))
-                        for function in lib.functions.values():
-                            for addr in function.addr_hit_map:
-                                source = addr2line.get_addr_source(dso, addr)
-                                if not source:
-                                    continue
-                                for file_path, line in source:
-                                    source_file = self.source_files.get_source_file(file_path)
-                                    # Show [line - 5, line + 5] of the line hit by a sample.
-                                    source_file.request_lines(line - 5, line + 5)
-                                    count_info = function.addr_hit_map[addr]
-                                    function.build_line_hit_map(source_file.file_id, line,
-                                                                count_info[0], count_info[1])
+            for lib in event.libraries:
+                dso = addr2line.get_dso(self.libs.get_lib_name(lib.lib_id))
+                if not dso:
+                    continue
+                for function in lib.functions.values():
+                    for addr in function.addr_hit_map:
+                        source = addr2line.get_addr_source(dso, addr)
+                        if not source:
+                            continue
+                        for file_path, line in source:
+                            source_file = self.source_files.get_source_file(file_path)
+                            # Show [line - 5, line + 5] of the line hit by a sample.
+                            source_file.request_lines(line - 5, line + 5)
+                            count_info = function.addr_hit_map[addr]
+                            function.build_line_hit_map(source_file.file_id, line, count_info[0],
+                                                        count_info[1])
 
         # Collect needed source code in SourceFileSet.
         self.source_files.load_source_code(source_dirs)
 
-    def add_disassembly(self):
+    def add_disassembly(self, filter_lib):
         """ Collect disassembly information:
             1. Use objdump to collect disassembly for each function in FunctionSet.
             2. Set flag to dump addr_hit_map when generating record info.
         """
         objdump = Objdump(self.ndk_path, self.binary_cache_path)
-        for function in self.functions.id_to_func.values():
+        cur_lib_name = None
+        dso_info = None
+        for function in sorted(self.functions.id_to_func.values(), key=lambda a: a.lib_id):
             if function.func_name == 'unknown':
                 continue
             lib_name = self.libs.get_lib_name(function.lib_id)
-            code = objdump.disassemble_code(lib_name, function.start_addr, function.addr_len)
-            function.disassembly = code
+            if lib_name != cur_lib_name:
+                cur_lib_name = lib_name
+                if filter_lib(lib_name):
+                    dso_info = objdump.get_dso_info(lib_name)
+                else:
+                    dso_info = None
+                if dso_info:
+                    log_info('Disassemble %s' % dso_info[0])
+            if dso_info:
+                code = objdump.disassemble_code(dso_info, function.start_addr, function.addr_len)
+                function.disassembly = code
 
         self.gen_addr_hit_map_in_record_info = True
 
@@ -771,7 +735,7 @@
 
     def _gen_sample_info(self):
         return [event.get_sample_info(self.gen_addr_hit_map_in_record_info)
-                    for event in self.events.values()]
+                for event in self.events.values()]
 
     def _gen_source_files(self):
         source_files = sorted(self.source_files.path_to_source_files.values(),
@@ -791,6 +755,17 @@
             file_list.append(file_data)
         return file_list
 
+URLS = {
+    'jquery': 'https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js',
+    'bootstrap4-css': 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.2/css/bootstrap.min.css',
+    'bootstrap4-popper':
+        'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js',
+    'bootstrap4': 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.2/js/bootstrap.min.js',
+    'dataTable': 'https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js',
+    'dataTable-bootstrap4': 'https://cdn.datatables.net/1.10.19/js/dataTables.bootstrap4.min.js',
+    'dataTable-css': 'https://cdn.datatables.net/1.10.19/css/dataTables.bootstrap4.min.css',
+    'gstatic-charts': 'https://www.gstatic.com/charts/loader.js',
+}
 
 class ReportGenerator(object):
 
@@ -798,23 +773,14 @@
         self.hw = HtmlWriter(html_path)
         self.hw.open_tag('html')
         self.hw.open_tag('head')
-        self.hw.open_tag('link', rel='stylesheet', type='text/css',
-            href='https://code.jquery.com/ui/1.12.0/themes/smoothness/jquery-ui.css'
-                         ).close_tag()
+        for css in ['bootstrap4-css', 'dataTable-css']:
+            self.hw.open_tag('link', rel='stylesheet', type='text/css', href=URLS[css]).close_tag()
+        for js in ['jquery', 'bootstrap4-popper', 'bootstrap4', 'dataTable', 'dataTable-bootstrap4',
+                   'gstatic-charts']:
+            self.hw.open_tag('script', src=URLS[js]).close_tag()
 
-        self.hw.open_tag('link', rel='stylesheet', type='text/css',
-             href='https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css'
-                         ).close_tag()
-        self.hw.open_tag('script', src='https://www.gstatic.com/charts/loader.js').close_tag()
         self.hw.open_tag('script').add(
             "google.charts.load('current', {'packages': ['corechart', 'table']});").close_tag()
-        self.hw.open_tag('script', src='https://code.jquery.com/jquery-3.2.1.js').close_tag()
-        self.hw.open_tag('script', src='https://code.jquery.com/ui/1.12.1/jquery-ui.js'
-                         ).close_tag()
-        self.hw.open_tag('script',
-            src='https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js').close_tag()
-        self.hw.open_tag('script',
-            src='https://cdn.datatables.net/1.10.16/js/dataTables.jqueryui.min.js').close_tag()
         self.hw.open_tag('style', type='text/css').add("""
             .colForLine { width: 50px; }
             .colForCount { width: 100px; }
@@ -833,9 +799,6 @@
         self.hw.add(json.dumps(record_data))
         self.hw.close_tag()
 
-    def write_flamegraph(self, flamegraph):
-        self.hw.add(flamegraph)
-
     def write_script(self):
         self.hw.open_tag('script').add_file('report_html.js').close_tag()
 
@@ -845,22 +808,8 @@
         self.hw.close()
 
 
-def gen_flamegraph(record_file, show_art_frames):
-    fd, flamegraph_path = tempfile.mkstemp()
-    os.close(fd)
-    inferno_script_path = os.path.join(get_script_dir(), 'inferno', 'inferno.py')
-    args = [sys.executable, inferno_script_path, '-sc', '-o', flamegraph_path,
-            '--record_file', record_file, '--embedded_flamegraph', '--no_browser']
-    if show_art_frames:
-        args.append('--show_art_frames')
-    subprocess.check_call(args)
-    with open(flamegraph_path, 'r') as fh:
-        data = fh.read()
-    remove(flamegraph_path)
-    return data
-
-
 def main():
+    sys.setrecursionlimit(MAX_CALLSTACK_LENGTH * 2 + 50)
     parser = argparse.ArgumentParser(description='report profiling data')
     parser.add_argument('-i', '--record_file', nargs='+', default=['perf.data'], help="""
                         Set profiling data file to report. Default is perf.data.""")
@@ -878,6 +827,8 @@
     parser.add_argument('--add_source_code', action='store_true', help='Add source code.')
     parser.add_argument('--source_dirs', nargs='+', help='Source code directories.')
     parser.add_argument('--add_disassembly', action='store_true', help='Add disassembled code.')
+    parser.add_argument('--binary_filter', nargs='+', help="""Annotate source code and disassembly
+                        only for selected binaries.""")
     parser.add_argument('--ndk_path', nargs=1, help='Find tools in the ndk path.')
     parser.add_argument('--no_browser', action='store_true', help="Don't open report in browser.")
     parser.add_argument('--show_art_frames', action='store_true',
@@ -904,21 +855,24 @@
     for record_file in args.record_file:
         record_data.load_record_file(record_file, args.show_art_frames)
     record_data.limit_percents(args.min_func_percent, args.min_callchain_percent)
+
+    def filter_lib(lib_name):
+        if not args.binary_filter:
+            return True
+        for binary in args.binary_filter:
+            if binary in lib_name:
+                return True
+        return False
     if args.add_source_code:
-        record_data.add_source_code(args.source_dirs)
+        record_data.add_source_code(args.source_dirs, filter_lib)
     if args.add_disassembly:
-        record_data.add_disassembly()
+        record_data.add_disassembly(filter_lib)
 
     # 3. Generate report html.
     report_generator = ReportGenerator(args.report_path)
+    report_generator.write_script()
     report_generator.write_content_div()
     report_generator.write_record_data(record_data.gen_record_info())
-    report_generator.write_script()
-    # TODO: support multiple perf.data in flamegraph.
-    if len(args.record_file) > 1:
-        log_warning('flamegraph will only be shown for %s' % args.record_file[0])
-    flamegraph = gen_flamegraph(args.record_file[0], args.show_art_frames)
-    report_generator.write_flamegraph(flamegraph)
     report_generator.finish()
 
     if not args.no_browser:
diff --git a/report_sample.py b/report_sample.py
old mode 100644
new mode 100755
index 45e884a..bbe9008
--- a/report_sample.py
+++ b/report_sample.py
@@ -20,11 +20,10 @@
 
 from __future__ import print_function
 import argparse
-import sys
-from simpleperf_report_lib import *
+from simpleperf_report_lib import ReportLib
 
 
-def report_sample(record_file, symfs_dir, kallsyms_file=None):
+def report_sample(record_file, symfs_dir, kallsyms_file, show_tracing_data):
     """ read record_file, and print each sample"""
     lib = ReportLib()
 
@@ -54,15 +53,26 @@
         for i in range(callchain.nr):
             entry = callchain.entries[i]
             print('%16x\t%s (%s)' % (entry.ip, entry.symbol.symbol_name, entry.symbol.dso_name))
+        if show_tracing_data:
+            data = lib.GetTracingDataOfCurrentSample()
+            if data:
+                print('\ttracing data:')
+                for key, value in data.items():
+                    print('\t\t%s : %s' % (key, value))
         print('')
 
 
-if __name__ == '__main__':
+def main():
     parser = argparse.ArgumentParser(description='Report samples in perf.data.')
     parser.add_argument('--symfs',
                         help='Set the path to find binaries with symbols and debug info.')
     parser.add_argument('--kallsyms', help='Set the path to find kernel symbols.')
     parser.add_argument('record_file', nargs='?', default='perf.data',
                         help='Default is perf.data.')
+    parser.add_argument('--show_tracing_data', action='store_true', help='print tracing data.')
     args = parser.parse_args()
-    report_sample(args.record_file, args.symfs, args.kallsyms)
+    report_sample(args.record_file, args.symfs, args.kallsyms, args.show_tracing_data)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/run_simpleperf_on_device.py b/run_simpleperf_on_device.py
old mode 100644
new mode 100755
index 37155bc..9732d6d
--- a/run_simpleperf_on_device.py
+++ b/run_simpleperf_on_device.py
@@ -21,7 +21,7 @@
 """
 import subprocess
 import sys
-from utils import *
+from utils import AdbHelper, disable_debug_log, get_target_binary_path
 
 def main():
     disable_debug_log()
@@ -34,4 +34,4 @@
     sys.exit(subprocess.call([adb.adb_path, 'shell', shell_cmd]))
 
 if __name__ == '__main__':
-    main()
\ No newline at end of file
+    main()
diff --git a/run_simpleperf_without_usb_connection.py b/run_simpleperf_without_usb_connection.py
old mode 100644
new mode 100755
diff --git a/simpleperf_report_lib.py b/simpleperf_report_lib.py
index 2eff83e..d2bc2d3 100644
--- a/simpleperf_report_lib.py
+++ b/simpleperf_report_lib.py
@@ -20,12 +20,10 @@
 
 """
 
+import collections
 import ctypes as ct
-import os
-import subprocess
-import sys
-import unittest
-from utils import *
+import struct
+from utils import bytes_to_str, get_host_binary_path, is_windows, str_to_bytes
 
 
 def _get_native_lib():
@@ -45,6 +43,10 @@
 def _char_pt_to_str(char_pt):
     return bytes_to_str(char_pt)
 
+def _check(cond, failmsg):
+    if not cond:
+        raise RuntimeError(failmsg)
+
 
 class SampleStruct(ct.Structure):
     """ Instance of a sample in perf.data.
@@ -63,16 +65,90 @@
     _fields_ = [('ip', ct.c_uint64),
                 ('pid', ct.c_uint32),
                 ('tid', ct.c_uint32),
-                ('thread_comm', ct.c_char_p),
+                ('_thread_comm', ct.c_char_p),
                 ('time', ct.c_uint64),
                 ('in_kernel', ct.c_uint32),
                 ('cpu', ct.c_uint32),
                 ('period', ct.c_uint64)]
 
+    @property
+    def thread_comm(self):
+        return _char_pt_to_str(self._thread_comm)
+
+
+class TracingFieldFormatStruct(ct.Structure):
+    """Format of a tracing field.
+       name: name of the field.
+       offset: offset of the field in tracing data.
+       elem_size: size of the element type.
+       elem_count: the number of elements in this field, more than one if the field is an array.
+       is_signed: whether the element type is signed or unsigned.
+    """
+    _fields_ = [('_name', ct.c_char_p),
+                ('offset', ct.c_uint32),
+                ('elem_size', ct.c_uint32),
+                ('elem_count', ct.c_uint32),
+                ('is_signed', ct.c_uint32)]
+
+    _unpack_key_dict = {1: 'b', 2: 'h', 4: 'i', 8: 'q'}
+
+    @property
+    def name(self):
+        return _char_pt_to_str(self._name)
+
+    def parse_value(self, data):
+        """ Parse value of a field in a tracepoint event.
+            The return value depends on the type of the field, and can be an int value, a string,
+            an array of int values, etc. If the type can't be parsed, return a byte array or an
+            array of byte arrays.
+        """
+        if self.elem_count > 1 and self.elem_size == 1 and self.is_signed == 0:
+            # The field is a string.
+            length = 0
+            while length < self.elem_count and data[self.offset + length] != '\x00':
+                length += 1
+            return bytes_to_str(data[self.offset : self.offset + length])
+        unpack_key = self._unpack_key_dict.get(self.elem_size)
+        if unpack_key:
+            if not self.is_signed:
+                unpack_key = unpack_key.upper()
+            value = struct.unpack('%d%s' % (self.elem_count, unpack_key),
+                                  data[self.offset:self.offset + self.elem_count * self.elem_size])
+        else:
+            # Since we don't know the element type, just return the bytes.
+            value = []
+            offset = self.offset
+            for _ in range(self.elem_count):
+                value.append(data[offset : offset + self.elem_size])
+                offset += self.elem_size
+        if self.elem_count == 1:
+            value = value[0]
+        return value
+
+
+class TracingDataFormatStruct(ct.Structure):
+    """Format of tracing data of a tracepoint event, like
+       https://www.kernel.org/doc/html/latest/trace/events.html#event-formats.
+       size: total size of all fields in the tracing data.
+       field_count: the number of fields.
+       fields: an array of fields.
+    """
+    _fields_ = [('size', ct.c_uint32),
+                ('field_count', ct.c_uint32),
+                ('fields', ct.POINTER(TracingFieldFormatStruct))]
+
 
 class EventStruct(ct.Structure):
-    """ Name of the event. """
-    _fields_ = [('name', ct.c_char_p)]
+    """Event type of a sample.
+       name: name of the event type.
+       tracing_data_format: only available when it is a tracepoint event.
+    """
+    _fields_ = [('_name', ct.c_char_p),
+                ('tracing_data_format', TracingDataFormatStruct)]
+
+    @property
+    def name(self):
+        return _char_pt_to_str(self._name)
 
 
 class MappingStruct(ct.Structure):
@@ -95,13 +171,21 @@
         symbol_len: length of the function in the shared library.
         mapping: the mapping area hit by the instruction.
     """
-    _fields_ = [('dso_name', ct.c_char_p),
+    _fields_ = [('_dso_name', ct.c_char_p),
                 ('vaddr_in_file', ct.c_uint64),
-                ('symbol_name', ct.c_char_p),
+                ('_symbol_name', ct.c_char_p),
                 ('symbol_addr', ct.c_uint64),
                 ('symbol_len', ct.c_uint64),
                 ('mapping', ct.POINTER(MappingStruct))]
 
+    @property
+    def dso_name(self):
+        return _char_pt_to_str(self._dso_name)
+
+    @property
+    def symbol_name(self):
+        return _char_pt_to_str(self._symbol_name)
+
 
 class CallChainEntryStructure(ct.Structure):
     """ A callchain entry of a sample.
@@ -134,51 +218,11 @@
                 ('data_size', ct.c_uint32)]
 
 
-# convert char_p to str for python3.
-class SampleStructUsingStr(object):
-    def __init__(self, sample):
-        self.ip = sample.ip
-        self.pid = sample.pid
-        self.tid = sample.tid
-        self.thread_comm = _char_pt_to_str(sample.thread_comm)
-        self.time = sample.time
-        self.in_kernel = sample.in_kernel
-        self.cpu = sample.cpu
-        self.period = sample.period
-
-
-class EventStructUsingStr(object):
-    def __init__(self, event):
-        self.name = _char_pt_to_str(event.name)
-
-
-class SymbolStructUsingStr(object):
-    def __init__(self, symbol):
-        self.dso_name = _char_pt_to_str(symbol.dso_name)
-        self.vaddr_in_file = symbol.vaddr_in_file
-        self.symbol_name = _char_pt_to_str(symbol.symbol_name)
-        self.symbol_addr = symbol.symbol_addr
-        self.mapping = symbol.mapping
-
-
-class CallChainEntryStructureUsingStr(object):
-    def __init__(self, entry):
-        self.ip = entry.ip
-        self.symbol = SymbolStructUsingStr(entry.symbol)
-
-
-class CallChainStructureUsingStr(object):
-    def __init__(self, callchain):
-        self.nr = callchain.nr
-        self.entries = []
-        for i in range(self.nr):
-            self.entries.append(CallChainEntryStructureUsingStr(callchain.entries[i]))
-
-
 class ReportLibStructure(ct.Structure):
     _fields_ = []
 
 
+# pylint: disable=invalid-name
 class ReportLib(object):
 
     def __init__(self, native_lib_path=None):
@@ -203,8 +247,9 @@
         self._GetSymbolOfCurrentSampleFunc = self._lib.GetSymbolOfCurrentSample
         self._GetSymbolOfCurrentSampleFunc.restype = ct.POINTER(SymbolStruct)
         self._GetCallChainOfCurrentSampleFunc = self._lib.GetCallChainOfCurrentSample
-        self._GetCallChainOfCurrentSampleFunc.restype = ct.POINTER(
-            CallChainStructure)
+        self._GetCallChainOfCurrentSampleFunc.restype = ct.POINTER(CallChainStructure)
+        self._GetTracingDataOfCurrentSampleFunc = self._lib.GetTracingDataOfCurrentSample
+        self._GetTracingDataOfCurrentSampleFunc.restype = ct.POINTER(ct.c_char)
         self._GetBuildIdForPathFunc = self._lib.GetBuildIdForPath
         self._GetBuildIdForPathFunc.restype = ct.c_char_p
         self._GetFeatureSection = self._lib.GetFeatureSection
@@ -212,7 +257,6 @@
         self._instance = self._CreateReportLibFunc()
         assert not _is_null(self._instance)
 
-        self.convert_to_str = (sys.version_info >= (3, 0))
         self.meta_info = None
         self.current_sample = None
         self.record_cmd = None
@@ -231,17 +275,17 @@
     def SetLogSeverity(self, log_level='info'):
         """ Set log severity of native lib, can be verbose,debug,info,error,fatal."""
         cond = self._SetLogSeverityFunc(self.getInstance(), _char_pt(log_level))
-        self._check(cond, 'Failed to set log level')
+        _check(cond, 'Failed to set log level')
 
     def SetSymfs(self, symfs_dir):
         """ Set directory used to find symbols."""
         cond = self._SetSymfsFunc(self.getInstance(), _char_pt(symfs_dir))
-        self._check(cond, 'Failed to set symbols directory')
+        _check(cond, 'Failed to set symbols directory')
 
     def SetRecordFile(self, record_file):
         """ Set the path of record file, like perf.data."""
         cond = self._SetRecordFileFunc(self.getInstance(), _char_pt(record_file))
-        self._check(cond, 'Failed to set record file')
+        _check(cond, 'Failed to set record file')
 
     def ShowIpForUnknownSymbol(self):
         self._ShowIpForUnknownSymbolFunc(self.getInstance())
@@ -253,15 +297,14 @@
     def SetKallsymsFile(self, kallsym_file):
         """ Set the file path to a copy of the /proc/kallsyms file (for off device decoding) """
         cond = self._SetKallsymsFileFunc(self.getInstance(), _char_pt(kallsym_file))
-        self._check(cond, 'Failed to set kallsyms file')
+        _check(cond, 'Failed to set kallsyms file')
 
     def GetNextSample(self):
         psample = self._GetNextSampleFunc(self.getInstance())
         if _is_null(psample):
             self.current_sample = None
         else:
-            sample = psample[0]
-            self.current_sample = SampleStructUsingStr(sample) if self.convert_to_str else sample
+            self.current_sample = psample[0]
         return self.current_sample
 
     def GetCurrentSample(self):
@@ -270,24 +313,29 @@
     def GetEventOfCurrentSample(self):
         event = self._GetEventOfCurrentSampleFunc(self.getInstance())
         assert not _is_null(event)
-        if self.convert_to_str:
-            return EventStructUsingStr(event[0])
         return event[0]
 
     def GetSymbolOfCurrentSample(self):
         symbol = self._GetSymbolOfCurrentSampleFunc(self.getInstance())
         assert not _is_null(symbol)
-        if self.convert_to_str:
-            return SymbolStructUsingStr(symbol[0])
         return symbol[0]
 
     def GetCallChainOfCurrentSample(self):
         callchain = self._GetCallChainOfCurrentSampleFunc(self.getInstance())
         assert not _is_null(callchain)
-        if self.convert_to_str:
-            return CallChainStructureUsingStr(callchain[0])
         return callchain[0]
 
+    def GetTracingDataOfCurrentSample(self):
+        data = self._GetTracingDataOfCurrentSampleFunc(self.getInstance())
+        if _is_null(data):
+            return None
+        event = self.GetEventOfCurrentSample()
+        result = collections.OrderedDict()
+        for i in range(event.tracing_data_format.field_count):
+            field = event.tracing_data_format.fields[i]
+            result[field.name] = field.parse_value(data)
+        return result
+
     def GetBuildIdForPath(self, path):
         build_id = self._GetBuildIdForPathFunc(self.getInstance(), _char_pt(path))
         assert not _is_null(build_id)
@@ -364,7 +412,3 @@
         if self._instance is None:
             raise Exception('Instance is Closed')
         return self._instance
-
-    def _check(self, cond, failmsg):
-        if not cond:
-            raise Exception(failmsg)
diff --git a/test.py b/test.py
old mode 100644
new mode 100755
index 1b80964..a0fb41f
--- a/test.py
+++ b/test.py
@@ -34,42 +34,49 @@
 Test using both `adb root` and `adb unroot`.
 
 """
-
+from __future__ import print_function
+import argparse
+import fnmatch
+import inspect
 import os
 import re
 import shutil
 import signal
+import subprocess
 import sys
-import tempfile
 import time
+import types
 import unittest
 
 from app_profiler import NativeLibDownloader
 from simpleperf_report_lib import ReportLib
-from utils import *
+from utils import log_exit, log_info, log_fatal
+from utils import AdbHelper, Addr2Nearestline, get_script_dir, is_windows, Objdump, ReadElf, remove
+from utils import SourceFileSearcher
 
-has_google_protobuf = True
 try:
+    # pylint: disable=unused-import
     import google.protobuf
-except:
-    has_google_protobuf = False
+    HAS_GOOGLE_PROTOBUF = True
+except ImportError:
+    HAS_GOOGLE_PROTOBUF = False
 
-inferno_script = os.path.join(get_script_dir(), "inferno.bat" if is_windows() else "./inferno.sh")
+INFERNO_SCRIPT = os.path.join(get_script_dir(), "inferno.bat" if is_windows() else "./inferno.sh")
 
-support_trace_offcpu = None
+def get_device_features():
+    adb = AdbHelper()
+    adb.check_run_and_return_output(['push',
+                                     'bin/android/%s/simpleperf' % adb.get_device_arch(),
+                                     '/data/local/tmp'])
+    adb.check_run_and_return_output(['shell', 'chmod', 'a+x', '/data/local/tmp/simpleperf'])
+    return adb.check_run_and_return_output(['shell', '/data/local/tmp/simpleperf', 'list',
+                                            '--show-features'])
 
 def is_trace_offcpu_supported():
-    global support_trace_offcpu
-    if support_trace_offcpu is None:
-        adb = AdbHelper()
-        adb.check_run_and_return_output(['push',
-                                         'bin/android/%s/simpleperf' % adb.get_device_arch(),
-                                         "/data/local/tmp"])
-        adb.check_run_and_return_output(['shell', 'chmod', 'a+x', '/data/local/tmp/simpleperf'])
-        output = adb.check_run_and_return_output(['shell', '/data/local/tmp/simpleperf', 'list',
-                                                  '--show-features'])
-        support_trace_offcpu = 'trace-offcpu' in output
-    return support_trace_offcpu
+    if not hasattr(is_trace_offcpu_supported, 'value'):
+        is_trace_offcpu_supported.value = 'trace-offcpu' in get_device_features()
+    return is_trace_offcpu_supported.value
+
 
 def build_testdata():
     """ Collect testdata from ../testdata and ../demo. """
@@ -77,7 +84,7 @@
     from_demo_path = os.path.join('..', 'demo')
     from_script_testdata_path = 'script_testdata'
     if (not os.path.isdir(from_testdata_path) or not os.path.isdir(from_demo_path) or
-        not from_script_testdata_path):
+            not from_script_testdata_path):
         return
     copy_testdata_list = ['perf_with_symbols.data', 'perf_with_trace_offcpu.data',
                           'perf_with_tracepoint_event.data', 'perf_with_interpreter_frames.data']
@@ -106,11 +113,12 @@
                 subproc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=use_shell)
                 (output_data, _) = subproc.communicate()
                 returncode = subproc.returncode
-        except:
+        except OSError:
             returncode = None
         self.assertEqual(returncode, 0, msg="failed to run cmd: %s" % args)
         if return_output:
             return output_data
+        return ''
 
 
 class TestExampleBase(TestBase):
@@ -193,28 +201,28 @@
         if not self.adb_root:
             args.append("--disable_adb_root")
         self.run_cmd(args)
-        self.check_exist(file="perf.data")
+        self.check_exist(filename="perf.data")
         if build_binary_cache:
-            self.check_exist(dir="binary_cache")
+            self.check_exist(dirname="binary_cache")
 
-    def check_exist(self, file=None, dir=None):
-        if file:
-            self.assertTrue(os.path.isfile(file), file)
-        if dir:
-            self.assertTrue(os.path.isdir(dir), dir)
+    def check_exist(self, filename=None, dirname=None):
+        if filename:
+            self.assertTrue(os.path.isfile(filename), filename)
+        if dirname:
+            self.assertTrue(os.path.isdir(dirname), dirname)
 
-    def check_file_under_dir(self, dir, file):
-        self.check_exist(dir=dir)
-        for _, _, files in os.walk(dir):
+    def check_file_under_dir(self, dirname, filename):
+        self.check_exist(dirname=dirname)
+        for _, _, files in os.walk(dirname):
             for f in files:
-                if f == file:
+                if f == filename:
                     return
-        self.fail("Failed to call check_file_under_dir(dir=%s, file=%s)" % (dir, file))
+        self.fail("Failed to call check_file_under_dir(dir=%s, file=%s)" % (dirname, filename))
 
 
-    def check_strings_in_file(self, file, strings):
-        self.check_exist(file=file)
-        with open(file, 'r') as fh:
+    def check_strings_in_file(self, filename, strings):
+        self.check_exist(filename=filename)
+        with open(filename, 'r') as fh:
             self.check_strings_in_content(fh.read(), strings)
 
     def check_strings_in_content(self, content, strings):
@@ -226,17 +234,15 @@
             This function checks for each entry, if the line containing [name]
             has at least required accumulated_period and period.
         """
-        self.check_exist(file=summary_file)
+        self.check_exist(filename=summary_file)
         with open(summary_file, 'r') as fh:
             summary = fh.read()
         fulfilled = [False for x in check_entries]
-        if not hasattr(self, "summary_check_re"):
-            self.summary_check_re = re.compile(r'accumulated_period:\s*([\d.]+)%.*period:\s*([\d.]+)%')
+        summary_check_re = re.compile(r'accumulated_period:\s*([\d.]+)%.*period:\s*([\d.]+)%')
         for line in summary.split('\n'):
-            for i in range(len(check_entries)):
-                (name, need_acc_period, need_period) = check_entries[i]
+            for i, (name, need_acc_period, need_period) in enumerate(check_entries):
                 if not fulfilled[i] and name in line:
-                    m = self.summary_check_re.search(line)
+                    m = summary_check_re.search(line)
                     if m:
                         acc_period = float(m.group(1))
                         period = float(m.group(2))
@@ -244,9 +250,9 @@
                             fulfilled[i] = True
         self.assertEqual(len(fulfilled), sum([int(x) for x in fulfilled]), fulfilled)
 
-    def check_inferno_report_html(self, check_entries, file="report.html"):
-        self.check_exist(file=file)
-        with open(file, 'r') as fh:
+    def check_inferno_report_html(self, check_entries, filename="report.html"):
+        self.check_exist(filename=filename)
+        with open(filename, 'r') as fh:
             data = fh.read()
         fulfilled = [False for _ in check_entries]
         for line in data.split('\n'):
@@ -258,7 +264,7 @@
                 if m and float(m.group(1)) >= entry[1]:
                     fulfilled[i] = True
                     break
-        self.assertEqual(fulfilled, [True for x in check_entries])
+        self.assertEqual(fulfilled, [True for _ in check_entries])
 
     def common_test_app_profiler(self):
         self.run_cmd(["app_profiler.py", "-h"])
@@ -269,7 +275,7 @@
         if not self.adb_root:
             args.append("--disable_adb_root")
         self.run_cmd(args)
-        self.check_exist(dir="binary_cache")
+        self.check_exist(dirname="binary_cache")
         remove("binary_cache")
         self.run_app_profiler(build_binary_cache=True)
         self.run_app_profiler()
@@ -280,13 +286,13 @@
         self.run_cmd(["report.py"])
         self.run_cmd(["report.py", "-i", "perf.data"])
         self.run_cmd(["report.py", "-g"])
-        self.run_cmd(["report.py", "--self-kill-for-testing",  "-g", "--gui"])
+        self.run_cmd(["report.py", "--self-kill-for-testing", "-g", "--gui"])
 
     def common_test_annotate(self):
         self.run_cmd(["annotate.py", "-h"])
         remove("annotated_files")
         self.run_cmd(["annotate.py", "-s", self.example_path])
-        self.check_exist(dir="annotated_files")
+        self.check_exist(dirname="annotated_files")
 
     def common_test_report_sample(self, check_strings):
         self.run_cmd(["report_sample.py", "-h"])
@@ -296,37 +302,36 @@
 
     def common_test_pprof_proto_generator(self, check_strings_with_lines,
                                           check_strings_without_lines):
-        if not has_google_protobuf:
+        if not HAS_GOOGLE_PROTOBUF:
             log_info('Skip test for pprof_proto_generator because google.protobuf is missing')
             return
         self.run_cmd(["pprof_proto_generator.py", "-h"])
         self.run_cmd(["pprof_proto_generator.py"])
         remove("pprof.profile")
         self.run_cmd(["pprof_proto_generator.py", "-i", "perf.data", "-o", "pprof.profile"])
-        self.check_exist(file="pprof.profile")
+        self.check_exist(filename="pprof.profile")
         self.run_cmd(["pprof_proto_generator.py", "--show"])
         output = self.run_cmd(["pprof_proto_generator.py", "--show", "pprof.profile"],
                               return_output=True)
-        self.check_strings_in_content(output, check_strings_with_lines +
-                                              ["has_line_numbers: True"])
+        self.check_strings_in_content(output, check_strings_with_lines + ["has_line_numbers: True"])
         remove("binary_cache")
         self.run_cmd(["pprof_proto_generator.py"])
         output = self.run_cmd(["pprof_proto_generator.py", "--show", "pprof.profile"],
                               return_output=True)
         self.check_strings_in_content(output, check_strings_without_lines +
-                                              ["has_line_numbers: False"])
+                                      ["has_line_numbers: False"])
 
     def common_test_inferno(self):
-        self.run_cmd([inferno_script, "-h"])
+        self.run_cmd([INFERNO_SCRIPT, "-h"])
         remove("perf.data")
         append_args = [] if self.adb_root else ["--disable_adb_root"]
-        self.run_cmd([inferno_script, "-p", self.package_name, "-t", "3"] + append_args)
-        self.check_exist(file="perf.data")
-        self.run_cmd([inferno_script, "-p", self.package_name, "-f", "1000", "-du", "-t", "1"] +
+        self.run_cmd([INFERNO_SCRIPT, "-p", self.package_name, "-t", "3"] + append_args)
+        self.check_exist(filename="perf.data")
+        self.run_cmd([INFERNO_SCRIPT, "-p", self.package_name, "-f", "1000", "-du", "-t", "1"] +
                      append_args)
-        self.run_cmd([inferno_script, "-p", self.package_name, "-e", "100000 cpu-cycles",
+        self.run_cmd([INFERNO_SCRIPT, "-p", self.package_name, "-e", "100000 cpu-cycles",
                       "-t", "1"] + append_args)
-        self.run_cmd([inferno_script, "-sc"])
+        self.run_cmd([INFERNO_SCRIPT, "-sc"])
 
     def common_test_report_html(self):
         self.run_cmd(['report_html.py', '-h'])
@@ -353,9 +358,9 @@
     def test_app_profiler_profile_from_launch(self):
         self.run_app_profiler(start_activity=True, build_binary_cache=False)
         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
-        self.check_strings_in_file("report.txt",
-            ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
-             "__start_thread"])
+        self.check_strings_in_file("report.txt", [
+            "com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run",
+            "__start_thread"])
 
     def test_app_profiler_multiprocesses(self):
         self.adb.check_run(['shell', 'am', 'force-stop', self.package_name])
@@ -393,23 +398,28 @@
         self.assertEqual(subproc.returncode, 0)
         self.run_cmd(["report.py"])
 
+    def test_app_profiler_with_ndk_path(self):
+        # Although we pass an invalid ndk path, it should be able to find tools in default ndk path.
+        self.run_cmd(['app_profiler.py', '--app', self.package_name, '-a', self.activity_name,
+                      '--ndk_path', '.'])
+
     def test_report(self):
         self.common_test_report()
         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
-        self.check_strings_in_file("report.txt",
-            ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
-             "__start_thread"])
+        self.check_strings_in_file("report.txt", [
+            "com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run",
+            "__start_thread"])
 
     def test_profile_with_process_id(self):
         self.adb.check_run(['shell', 'am', 'start', '-n', self.package_name + '/.MainActivity'])
         time.sleep(1)
-        pid = self.adb.check_run_and_return_output(['shell', 'pidof',
-                'com.example.simpleperf.simpleperfexamplepurejava']).strip()
+        pid = self.adb.check_run_and_return_output([
+            'shell', 'pidof', 'com.example.simpleperf.simpleperfexamplepurejava']).strip()
         self.run_app_profiler(start_activity=False, record_arg='-g --duration 10 -p ' + pid)
         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
-        self.check_strings_in_file("report.txt",
-            ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
-             "__start_thread"])
+        self.check_strings_in_file("report.txt", [
+            "com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run",
+            "__start_thread"])
 
     def test_annotate(self):
         self.common_test_annotate()
@@ -418,15 +428,15 @@
             return
         self.check_file_under_dir("annotated_files", "MainActivity.java")
         summary_file = os.path.join("annotated_files", "summary")
-        self.check_annotation_summary(summary_file,
-            [("MainActivity.java", 80, 80),
-             ("run", 80, 0),
-             ("callFunction", 0, 0),
-             ("line 23", 80, 0)])
+        self.check_annotation_summary(summary_file, [
+            ("MainActivity.java", 80, 80),
+            ("run", 80, 0),
+            ("callFunction", 0, 0),
+            ("line 23", 80, 0)])
 
     def test_report_sample(self):
         self.common_test_report_sample(
-            ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
+            ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run",
              "__start_thread"])
 
     def test_pprof_proto_generator(self):
@@ -437,18 +447,18 @@
                 "run"]
         self.common_test_pprof_proto_generator(
             check_strings_with_lines=check_strings_with_lines,
-            check_strings_without_lines=
-                ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()"])
+            check_strings_without_lines=[
+                "com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run"])
 
     def test_inferno(self):
         self.common_test_inferno()
         self.run_app_profiler()
-        self.run_cmd([inferno_script, "-sc"])
+        self.run_cmd([INFERNO_SCRIPT, "-sc"])
         self.check_inferno_report_html(
-            [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()', 80)])
-        self.run_cmd([inferno_script, "-sc", "-o", "report2.html"])
+            [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run', 80)])
+        self.run_cmd([INFERNO_SCRIPT, "-sc", "-o", "report2.html"])
         self.check_inferno_report_html(
-            [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()', 80)],
+            [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run', 80)],
             "report2.html")
         remove("report2.html")
 
@@ -460,8 +470,8 @@
         os.chdir(test_dir)
         self.run_cmd(['python', os.path.join(saved_dir, 'app_profiler.py'),
                       '--app', self.package_name, '-r', '-e task-clock:u -g --duration 3'])
-        self.check_exist(file="perf.data")
-        self.run_cmd([inferno_script, "-sc"])
+        self.check_exist(filename="perf.data")
+        self.run_cmd([INFERNO_SCRIPT, "-sc"])
         os.chdir(saved_dir)
         remove(test_dir)
 
@@ -475,7 +485,7 @@
         self.adb.check_run(['kill-server'])
         time.sleep(3)
         self.run_cmd(['run_simpleperf_without_usb_connection.py', 'stop'])
-        self.check_exist(file="perf.data")
+        self.check_exist(filename="perf.data")
         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
 
 
@@ -501,25 +511,25 @@
     def test_smoke(self):
         self.run_app_profiler(record_arg="-g -f 1000 --duration 10 -e cpu-cycles:u --trace-offcpu")
         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
-        self.check_strings_in_file("report.txt",
-            ["com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.run",
-             "com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.RunFunction",
-             "com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.SleepFunction"
-             ])
+        self.check_strings_in_file("report.txt", [
+            "com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.run",
+            "com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.RunFunction",
+            "com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.SleepFunction"
+            ])
         remove("annotated_files")
         self.run_cmd(["annotate.py", "-s", self.example_path])
-        self.check_exist(dir="annotated_files")
+        self.check_exist(dirname="annotated_files")
         if self.use_compiled_java_code:
             self.check_file_under_dir("annotated_files", "SleepActivity.java")
             summary_file = os.path.join("annotated_files", "summary")
-            self.check_annotation_summary(summary_file,
-               [("SleepActivity.java", 80, 20),
+            self.check_annotation_summary(summary_file, [
+                ("SleepActivity.java", 80, 20),
                 ("run", 80, 0),
                 ("RunFunction", 20, 20),
                 ("SleepFunction", 20, 0),
                 ("line 24", 20, 0),
                 ("line 32", 20, 0)])
-        self.run_cmd([inferno_script, "-sc"])
+        self.run_cmd([INFERNO_SCRIPT, "-sc"])
         self.check_inferno_report_html(
             [('com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.run', 80),
              ('com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.RunFunction',
@@ -541,25 +551,21 @@
     def test_app_profiler_profile_from_launch(self):
         self.run_app_profiler(start_activity=True, build_binary_cache=False)
         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
-        self.check_strings_in_file("report.txt",
-            ["BusyLoopThread",
-             "__start_thread"])
+        self.check_strings_in_file("report.txt", ["BusyLoopThread", "__start_thread"])
 
     def test_report(self):
         self.common_test_report()
         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
-        self.check_strings_in_file("report.txt",
-            ["BusyLoopThread",
-             "__start_thread"])
+        self.check_strings_in_file("report.txt", ["BusyLoopThread", "__start_thread"])
 
     def test_annotate(self):
         self.common_test_annotate()
         self.check_file_under_dir("annotated_files", "native-lib.cpp")
         summary_file = os.path.join("annotated_files", "summary")
-        self.check_annotation_summary(summary_file,
-            [("native-lib.cpp", 20, 0),
-             ("BusyLoopThread", 20, 0),
-             ("line 46", 20, 0)])
+        self.check_annotation_summary(summary_file, [
+            ("native-lib.cpp", 20, 0),
+            ("BusyLoopThread", 20, 0),
+            ("line 46", 20, 0)])
 
     def test_report_sample(self):
         self.common_test_report_sample(
@@ -568,20 +574,19 @@
 
     def test_pprof_proto_generator(self):
         self.common_test_pprof_proto_generator(
-            check_strings_with_lines=
-                ["native-lib.cpp",
-                 "BusyLoopThread"],
-            check_strings_without_lines=
-                ["BusyLoopThread"])
+            check_strings_with_lines=["native-lib.cpp", "BusyLoopThread"],
+            check_strings_without_lines=["BusyLoopThread"])
 
     def test_inferno(self):
         self.common_test_inferno()
         self.run_app_profiler()
-        self.run_cmd([inferno_script, "-sc"])
+        self.run_cmd([INFERNO_SCRIPT, "-sc"])
         self.check_inferno_report_html([('BusyLoopThread', 20)])
 
     def test_report_html(self):
         self.common_test_report_html()
+        self.run_cmd(['report_html.py', '--add_source_code', '--source_dirs', 'testdata',
+                      '--add_disassembly', '--binary_filter', "libnative-lib.so"])
 
 
 class TestExampleWithNativeRoot(TestExampleBase):
@@ -606,23 +611,23 @@
     def test_smoke(self):
         self.run_app_profiler(record_arg="-g -f 1000 --duration 10 -e cpu-cycles:u --trace-offcpu")
         self.run_cmd(["report.py", "-g", "--comms", "SleepThread", "-o", "report.txt"])
-        self.check_strings_in_file("report.txt",
-            ["SleepThread(void*)",
-             "RunFunction()",
-             "SleepFunction(unsigned long long)"])
+        self.check_strings_in_file("report.txt", [
+            "SleepThread(void*)",
+            "RunFunction()",
+            "SleepFunction(unsigned long long)"])
         remove("annotated_files")
         self.run_cmd(["annotate.py", "-s", self.example_path, "--comm", "SleepThread"])
-        self.check_exist(dir="annotated_files")
+        self.check_exist(dirname="annotated_files")
         self.check_file_under_dir("annotated_files", "native-lib.cpp")
         summary_file = os.path.join("annotated_files", "summary")
-        self.check_annotation_summary(summary_file,
-            [("native-lib.cpp", 80, 20),
-             ("SleepThread", 80, 0),
-             ("RunFunction", 20, 20),
-             ("SleepFunction", 20, 0),
-             ("line 73", 20, 0),
-             ("line 83", 20, 0)])
-        self.run_cmd([inferno_script, "-sc"])
+        self.check_annotation_summary(summary_file, [
+            ("native-lib.cpp", 80, 20),
+            ("SleepThread", 80, 0),
+            ("RunFunction", 20, 20),
+            ("SleepFunction", 20, 0),
+            ("line 73", 20, 0),
+            ("line 83", 20, 0)])
+        self.run_cmd([INFERNO_SCRIPT, "-sc"])
         self.check_inferno_report_html([('SleepThread', 80),
                                         ('RunFunction', 20),
                                         ('SleepFunction', 20)])
@@ -638,28 +643,26 @@
     def test_smoke(self):
         self.run_app_profiler()
         self.run_cmd(["report.py", "-g", "--comms", "BusyThread", "-o", "report.txt"])
-        self.check_strings_in_file("report.txt",
-            ["void com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run()",
-             "int com.example.simpleperf.simpleperfexamplewithnative.MixActivity.callFunction(int)",
-             "Java_com_example_simpleperf_simpleperfexamplewithnative_MixActivity_callFunction"])
+        self.check_strings_in_file("report.txt", [
+            "com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run",
+            "com.example.simpleperf.simpleperfexamplewithnative.MixActivity.callFunction",
+            "Java_com_example_simpleperf_simpleperfexamplewithnative_MixActivity_callFunction"])
         remove("annotated_files")
         self.run_cmd(["annotate.py", "-s", self.example_path, "--comm", "BusyThread"])
-        self.check_exist(dir="annotated_files")
+        self.check_exist(dirname="annotated_files")
         self.check_file_under_dir("annotated_files", "native-lib.cpp")
         summary_file = os.path.join("annotated_files", "summary")
-        self.check_annotation_summary(summary_file,
-            [("native-lib.cpp", 5, 0),
-             ("line 40", 5, 0)])
+        self.check_annotation_summary(summary_file, [("native-lib.cpp", 5, 0), ("line 40", 5, 0)])
         if self.use_compiled_java_code:
             self.check_file_under_dir("annotated_files", "MixActivity.java")
-            self.check_annotation_summary(summary_file,
-                [("MixActivity.java", 80, 0),
-                 ("run", 80, 0),
-                 ("line 26", 20, 0),
-                 ("native-lib.cpp", 5, 0),
-                 ("line 40", 5, 0)])
+            self.check_annotation_summary(summary_file, [
+                ("MixActivity.java", 80, 0),
+                ("run", 80, 0),
+                ("line 26", 20, 0),
+                ("native-lib.cpp", 5, 0),
+                ("line 40", 5, 0)])
 
-        self.run_cmd([inferno_script, "-sc"])
+        self.run_cmd([INFERNO_SCRIPT, "-sc"])
 
 
 class TestExampleWithNativeForceArm(TestExampleWithNative):
@@ -703,16 +706,16 @@
     def test_app_profiler_profile_from_launch(self):
         self.run_app_profiler(start_activity=True, build_binary_cache=False)
         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
-        self.check_strings_in_file("report.txt",
-            ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
-             "__start_thread"])
+        self.check_strings_in_file("report.txt", [
+            "com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1." +
+            "run", "__start_thread"])
 
     def test_report(self):
         self.common_test_report()
         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
-        self.check_strings_in_file("report.txt",
-            ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
-             "__start_thread"])
+        self.check_strings_in_file("report.txt", [
+            "com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1." +
+            "run", "__start_thread"])
 
     def test_annotate(self):
         if not self.use_compiled_java_code:
@@ -720,17 +723,17 @@
         self.common_test_annotate()
         self.check_file_under_dir("annotated_files", "MainActivity.kt")
         summary_file = os.path.join("annotated_files", "summary")
-        self.check_annotation_summary(summary_file,
-            [("MainActivity.kt", 80, 80),
-             ("run", 80, 0),
-             ("callFunction", 0, 0),
-             ("line 19", 80, 0),
-             ("line 25", 0, 0)])
+        self.check_annotation_summary(summary_file, [
+            ("MainActivity.kt", 80, 80),
+            ("run", 80, 0),
+            ("callFunction", 0, 0),
+            ("line 19", 80, 0),
+            ("line 25", 0, 0)])
 
     def test_report_sample(self):
-        self.common_test_report_sample(
-            ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
-             "__start_thread"])
+        self.common_test_report_sample([
+            "com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1." +
+            "run", "__start_thread"])
 
     def test_pprof_proto_generator(self):
         check_strings_with_lines = []
@@ -740,16 +743,15 @@
                 "run"]
         self.common_test_pprof_proto_generator(
             check_strings_with_lines=check_strings_with_lines,
-            check_strings_without_lines=
-                ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()"])
+            check_strings_without_lines=["com.example.simpleperf.simpleperfexampleofkotlin." +
+                                         "MainActivity$createBusyThread$1.run"])
 
     def test_inferno(self):
         self.common_test_inferno()
         self.run_app_profiler()
-        self.run_cmd([inferno_script, "-sc"])
-        self.check_inferno_report_html(
-            [('com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()',
-              80)])
+        self.run_cmd([INFERNO_SCRIPT, "-sc"])
+        self.check_inferno_report_html([('com.example.simpleperf.simpleperfexampleofkotlin.' +
+                                         'MainActivity$createBusyThread$1.run', 80)])
 
     def test_report_html(self):
         self.common_test_report_html()
@@ -777,33 +779,32 @@
     def test_smoke(self):
         self.run_app_profiler(record_arg="-g -f 1000 --duration 10 -e cpu-cycles:u --trace-offcpu")
         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
-        self.check_strings_in_file("report.txt",
-            ["com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.run",
-             "com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.RunFunction",
-             "com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.SleepFunction"
-             ])
+        function_prefix = "com.example.simpleperf.simpleperfexampleofkotlin." + \
+                          "SleepActivity$createRunSleepThread$1."
+        self.check_strings_in_file("report.txt", [
+            function_prefix + "run",
+            function_prefix + "RunFunction",
+            function_prefix + "SleepFunction"
+            ])
         if self.use_compiled_java_code:
             remove("annotated_files")
             self.run_cmd(["annotate.py", "-s", self.example_path])
-            self.check_exist(dir="annotated_files")
+            self.check_exist(dirname="annotated_files")
             self.check_file_under_dir("annotated_files", "SleepActivity.kt")
             summary_file = os.path.join("annotated_files", "summary")
-            self.check_annotation_summary(summary_file,
-                [("SleepActivity.kt", 80, 20),
-                 ("run", 80, 0),
-                 ("RunFunction", 20, 20),
-                 ("SleepFunction", 20, 0),
-                 ("line 24", 20, 0),
-                 ("line 32", 20, 0)])
+            self.check_annotation_summary(summary_file, [
+                ("SleepActivity.kt", 80, 20),
+                ("run", 80, 0),
+                ("RunFunction", 20, 20),
+                ("SleepFunction", 20, 0),
+                ("line 24", 20, 0),
+                ("line 32", 20, 0)])
 
-        self.run_cmd([inferno_script, "-sc"])
-        self.check_inferno_report_html(
-            [('com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.run',
-              80),
-             ('com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.RunFunction',
-              20),
-             ('com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.SleepFunction',
-              20)])
+        self.run_cmd([INFERNO_SCRIPT, "-sc"])
+        self.check_inferno_report_html([
+            (function_prefix + 'run', 80),
+            (function_prefix + 'RunFunction', 20),
+            (function_prefix + 'SleepFunction', 20)])
 
 
 class TestProfilingCmd(TestBase):
@@ -819,8 +820,8 @@
         if adb.switch_to_root():
             self.run_cmd(["app_profiler.py", "-np", "surfaceflinger"])
             self.run_cmd(["report.py", "-g", "-o", "report.txt"])
-            self.run_cmd([inferno_script, "-sc"])
-            self.run_cmd([inferno_script, "-np", "surfaceflinger"])
+            self.run_cmd([INFERNO_SCRIPT, "-sc"])
+            self.run_cmd([INFERNO_SCRIPT, "-np", "surfaceflinger"])
 
 
 class TestReportLib(unittest.TestCase):
@@ -838,7 +839,6 @@
     def test_symbol(self):
         found_func2 = False
         while self.report_lib.GetNextSample():
-            sample = self.report_lib.GetCurrentSample()
             symbol = self.report_lib.GetSymbolOfCurrentSample()
             if symbol.symbol_name == 'func2(int, int)':
                 found_func2 = True
@@ -883,7 +883,8 @@
     def test_record_cmd(self):
         self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
         self.assertEqual(self.report_lib.GetRecordCmd(),
-                         "/data/local/tmp/simpleperf record --trace-offcpu --duration 2 -g ./simpleperf_runtest_run_and_sleep64")
+                         "/data/local/tmp/simpleperf record --trace-offcpu --duration 2 -g " +
+                         "./simpleperf_runtest_run_and_sleep64")
 
     def test_offcpu(self):
         self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
@@ -927,6 +928,22 @@
         report_lib.ShowArtFrames(True)
         self.assertTrue(has_art_frame(report_lib))
 
+    def test_tracing_data(self):
+        self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_tracepoint_event.data'))
+        has_tracing_data = False
+        while self.report_lib.GetNextSample():
+            event = self.report_lib.GetEventOfCurrentSample()
+            tracing_data = self.report_lib.GetTracingDataOfCurrentSample()
+            if event.name == 'sched:sched_switch':
+                self.assertIsNotNone(tracing_data)
+                self.assertIn('prev_pid', tracing_data)
+                self.assertIn('next_comm', tracing_data)
+                if tracing_data['prev_pid'] == 9896 and tracing_data['next_comm'] == 'swapper/4':
+                    has_tracing_data = True
+            else:
+                self.assertIsNone(tracing_data)
+        self.assertTrue(has_tracing_data)
+
 
 class TestRunSimpleperfOnDevice(TestBase):
     def test_smoke(self):
@@ -935,6 +952,10 @@
 
 class TestTools(unittest.TestCase):
     def test_addr2nearestline(self):
+        self.run_addr2nearestline_test(True)
+        self.run_addr2nearestline_test(False)
+
+    def run_addr2nearestline_test(self, with_function_name):
         binary_cache_path = 'testdata'
         test_map = {
             '/simpleperf_runtest_two_functions_arm64': [
@@ -942,12 +963,15 @@
                     'func_addr': 0x668,
                     'addr': 0x668,
                     'source': 'system/extras/simpleperf/runtest/two_functions.cpp:20',
+                    'function': 'main',
                 },
                 {
                     'func_addr': 0x668,
                     'addr': 0x6a4,
                     'source': """system/extras/simpleperf/runtest/two_functions.cpp:7
                                  system/extras/simpleperf/runtest/two_functions.cpp:22""",
+                    'function': """Function1()
+                                   main""",
                 },
             ],
             '/simpleperf_runtest_two_functions_arm': [
@@ -956,12 +980,16 @@
                     'addr': 0x7b0,
                     'source': """system/extras/simpleperf/runtest/two_functions.cpp:14
                                  system/extras/simpleperf/runtest/two_functions.cpp:23""",
+                    'function': """Function2()
+                                   main""",
                 },
                 {
                     'func_addr': 0x784,
                     'addr': 0x7d0,
                     'source': """system/extras/simpleperf/runtest/two_functions.cpp:15
                                  system/extras/simpleperf/runtest/two_functions.cpp:23""",
+                    'function': """Function2()
+                                   main""",
                 }
             ],
             '/simpleperf_runtest_two_functions_x86_64': [
@@ -969,12 +997,15 @@
                     'func_addr': 0x840,
                     'addr': 0x840,
                     'source': 'system/extras/simpleperf/runtest/two_functions.cpp:7',
+                    'function': 'Function1()',
                 },
                 {
                     'func_addr': 0x920,
                     'addr': 0x94a,
                     'source': """system/extras/simpleperf/runtest/two_functions.cpp:7
                                  system/extras/simpleperf/runtest/two_functions.cpp:22""",
+                    'function': """Function1()
+                                   main""",
                 }
             ],
             '/simpleperf_runtest_two_functions_x86': [
@@ -982,16 +1013,19 @@
                     'func_addr': 0x6d0,
                     'addr': 0x6da,
                     'source': 'system/extras/simpleperf/runtest/two_functions.cpp:14',
+                    'function': 'Function2()',
                 },
                 {
                     'func_addr': 0x710,
                     'addr': 0x749,
                     'source': """system/extras/simpleperf/runtest/two_functions.cpp:8
                                  system/extras/simpleperf/runtest/two_functions.cpp:22""",
+                    'function': """Function1()
+                                   main""",
                 }
             ],
         }
-        addr2line = Addr2Nearestline(None, binary_cache_path)
+        addr2line = Addr2Nearestline(None, binary_cache_path, with_function_name)
         for dso_path in test_map:
             test_addrs = test_map[dso_path]
             for test_addr in test_addrs:
@@ -1002,18 +1036,26 @@
             self.assertTrue(dso is not None)
             test_addrs = test_map[dso_path]
             for test_addr in test_addrs:
-                source_str = test_addr['source']
-                expected_source = []
-                for line in source_str.split('\n'):
+                expected_files = []
+                expected_lines = []
+                expected_functions = []
+                for line in test_addr['source'].split('\n'):
                     items = line.split(':')
-                    expected_source.append((items[0].strip(), int(items[1])))
+                    expected_files.append(items[0].strip())
+                    expected_lines.append(int(items[1]))
+                for line in test_addr['function'].split('\n'):
+                    expected_functions.append(line.strip())
+                self.assertEquals(len(expected_files), len(expected_functions))
+
                 actual_source = addr2line.get_addr_source(dso, test_addr['addr'])
                 self.assertTrue(actual_source is not None)
-                self.assertEqual(len(actual_source), len(expected_source))
-                for i in range(len(expected_source)):
-                    actual_file_path, actual_line = actual_source[i]
-                    self.assertEqual(actual_file_path, expected_source[i][0])
-                    self.assertEqual(actual_line, expected_source[i][1])
+                self.assertEqual(len(actual_source), len(expected_files))
+                for i, source in enumerate(actual_source):
+                    self.assertEqual(len(source), 3 if with_function_name else 2)
+                    self.assertEqual(source[0], expected_files[i])
+                    self.assertEqual(source[1], expected_lines[i])
+                    if with_function_name:
+                        self.assertEqual(source[2], expected_functions[i])
 
     def test_objdump(self):
         binary_cache_path = 'testdata'
@@ -1057,37 +1099,38 @@
         }
         objdump = Objdump(None, binary_cache_path)
         for dso_path in test_map:
-            dso_info = test_map[dso_path]
-            disassemble_code = objdump.disassemble_code(dso_path, dso_info['start_addr'],
-                                                        dso_info['len'])
+            dso = test_map[dso_path]
+            dso_info = objdump.get_dso_info(dso_path)
+            self.assertIsNotNone(dso_info)
+            disassemble_code = objdump.disassemble_code(dso_info, dso['start_addr'], dso['len'])
             self.assertTrue(disassemble_code)
-            for item in dso_info['expected_items']:
+            for item in dso['expected_items']:
                 self.assertTrue(item in disassemble_code)
 
     def test_readelf(self):
         test_map = {
-           '/simpleperf_runtest_two_functions_arm64': {
-               'arch': 'arm64',
-               'build_id': '0xe8ecb3916d989dbdc068345c30f0c24300000000',
-               'sections': ['.interp', '.note.android.ident', '.note.gnu.build-id', '.dynsym',
-                            '.dynstr', '.gnu.hash', '.gnu.version', '.gnu.version_r', '.rela.dyn',
-                            '.rela.plt', '.plt', '.text', '.rodata', '.eh_frame', '.eh_frame_hdr',
-                            '.preinit_array', '.init_array', '.fini_array', '.dynamic', '.got',
-                            '.got.plt', '.data', '.bss', '.comment', '.debug_str', '.debug_loc',
-                            '.debug_abbrev', '.debug_info', '.debug_ranges', '.debug_macinfo',
-                            '.debug_pubnames', '.debug_pubtypes', '.debug_line',
-                            '.note.gnu.gold-version', '.symtab', '.strtab', '.shstrtab'],
-           },
-           '/simpleperf_runtest_two_functions_arm': {
-               'arch': 'arm',
-               'build_id': '0x718f5b36c4148ee1bd3f51af89ed2be600000000',
-           },
-           '/simpleperf_runtest_two_functions_x86_64': {
-               'arch': 'x86_64',
-           },
-           '/simpleperf_runtest_two_functions_x86': {
-               'arch': 'x86',
-           }
+            '/simpleperf_runtest_two_functions_arm64': {
+                'arch': 'arm64',
+                'build_id': '0xe8ecb3916d989dbdc068345c30f0c24300000000',
+                'sections': ['.interp', '.note.android.ident', '.note.gnu.build-id', '.dynsym',
+                             '.dynstr', '.gnu.hash', '.gnu.version', '.gnu.version_r', '.rela.dyn',
+                             '.rela.plt', '.plt', '.text', '.rodata', '.eh_frame', '.eh_frame_hdr',
+                             '.preinit_array', '.init_array', '.fini_array', '.dynamic', '.got',
+                             '.got.plt', '.data', '.bss', '.comment', '.debug_str', '.debug_loc',
+                             '.debug_abbrev', '.debug_info', '.debug_ranges', '.debug_macinfo',
+                             '.debug_pubnames', '.debug_pubtypes', '.debug_line',
+                             '.note.gnu.gold-version', '.symtab', '.strtab', '.shstrtab'],
+            },
+            '/simpleperf_runtest_two_functions_arm': {
+                'arch': 'arm',
+                'build_id': '0x718f5b36c4148ee1bd3f51af89ed2be600000000',
+            },
+            '/simpleperf_runtest_two_functions_x86_64': {
+                'arch': 'x86_64',
+            },
+            '/simpleperf_runtest_two_functions_x86': {
+                'arch': 'x86',
+            }
         }
         readelf = ReadElf(None)
         for dso_path in test_map:
@@ -1102,13 +1145,40 @@
         self.assertEqual(readelf.get_build_id('not_exist_file'), '')
         self.assertEqual(readelf.get_sections('not_exist_file'), [])
 
+    def test_source_file_searcher(self):
+        searcher = SourceFileSearcher(['testdata'])
+        def format_path(path):
+            return path.replace('/', os.sep)
+        # Find a C++ file with pure file name.
+        self.assertEquals(
+            format_path('testdata/SimpleperfExampleWithNative/app/src/main/cpp/native-lib.cpp'),
+            searcher.get_real_path('native-lib.cpp'))
+        # Find a C++ file with an absolute file path.
+        self.assertEquals(
+            format_path('testdata/SimpleperfExampleWithNative/app/src/main/cpp/native-lib.cpp'),
+            searcher.get_real_path('/data/native-lib.cpp'))
+        # Find a Java file.
+        self.assertEquals(
+            format_path('testdata/SimpleperfExampleWithNative/app/src/main/java/com/example/' +
+                        'simpleperf/simpleperfexamplewithnative/MainActivity.java'),
+            searcher.get_real_path('simpleperfexamplewithnative/MainActivity.java'))
+        # Find a Kotlin file.
+        self.assertEquals(
+            format_path('testdata/SimpleperfExampleOfKotlin/app/src/main/java/com/example/' +
+                        'simpleperf/simpleperfexampleofkotlin/MainActivity.kt'),
+            searcher.get_real_path('MainActivity.kt'))
+
 
 class TestNativeLibDownloader(unittest.TestCase):
     def test_smoke(self):
-        self.adb = AdbHelper()
+        adb = AdbHelper()
+
+        def is_lib_on_device(path):
+            return adb.run(['shell', 'ls', path])
+
         # Sync all native libs on device.
-        self.adb.run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs'])
-        downloader = NativeLibDownloader(None, 'arm64', self.adb)
+        adb.run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs'])
+        downloader = NativeLibDownloader(None, 'arm64', adb)
         downloader.collect_native_libs_on_host(os.path.join(
             'testdata', 'SimpleperfExampleWithNative', 'app', 'build', 'intermediates', 'cmake',
             'profiling'))
@@ -1127,36 +1197,73 @@
             downloader.sync_natives_libs_on_device()
             downloader.collect_native_libs_on_device()
             self.assertEqual(len(downloader.device_build_id_map), sync_count)
-            for i in range(len(lib_list)):
-                build_id = lib_list[i][0]
-                name = lib_list[i][1].name
+            for i, item in enumerate(lib_list):
+                build_id = item[0]
+                name = item[1].name
                 if i < sync_count:
                     self.assertTrue(build_id in downloader.device_build_id_map)
                     self.assertEqual(name, downloader.device_build_id_map[build_id])
-                    self.assertTrue(self._is_lib_on_device(downloader.dir_on_device + name))
+                    self.assertTrue(is_lib_on_device(downloader.dir_on_device + name))
                 else:
                     self.assertTrue(build_id not in downloader.device_build_id_map)
-                    self.assertFalse(self._is_lib_on_device(downloader.dir_on_device + name))
+                    self.assertFalse(is_lib_on_device(downloader.dir_on_device + name))
             if sync_count == 1:
-                self.adb.run(['pull', '/data/local/tmp/native_libs/build_id_list',
-                              'build_id_list'])
+                adb.run(['pull', '/data/local/tmp/native_libs/build_id_list', 'build_id_list'])
                 with open('build_id_list', 'rb') as fh:
                     self.assertEqual(fh.read(), '{}={}\n'.format(lib_list[0][0],
                                                                  lib_list[0][1].name))
                 remove('build_id_list')
-        self.adb.run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs'])
+        adb.run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs'])
 
-    def _is_lib_on_device(self, path):
-        return self.adb.run(['shell', 'ls', path])
 
+class TestReportHtml(TestBase):
+    def test_long_callchain(self):
+        self.run_cmd(['report_html.py', '-i', 'testdata/perf_with_long_callchain.data'])
+
+
+def get_all_tests():
+    tests = []
+    for name, value in globals().items():
+        if isinstance(value, type) and issubclass(value, unittest.TestCase):
+            test_methods = [x for x, y in inspect.getmembers(value)
+                            if isinstance(y, types.UnboundMethodType) and x.startswith('test')]
+            for method in test_methods:
+                tests.append(name + '.' + method)
+    return sorted(tests)
 
 def main():
+    parser = argparse.ArgumentParser(description='Test simpleperf scripts')
+    parser.add_argument('--list-tests', action='store_true', help='List all tests.')
+    parser.add_argument('--test-from', nargs=1, help='Run left tests from the selected test.')
+    parser.add_argument('pattern', nargs='*', help='Run tests matching the selected pattern.')
+    args = parser.parse_args()
+    tests = get_all_tests()
+    if args.list_tests:
+        print('\n'.join(tests))
+        return
+    if args.test_from:
+        start_pos = 0
+        while start_pos < len(tests) and tests[start_pos] != args.test_from[0]:
+            start_pos += 1
+        if start_pos == len(tests):
+            log_exit("Can't find test %s" % args.test_from[0])
+        tests = tests[start_pos:]
+    if args.pattern:
+        pattern = re.compile(fnmatch.translate(args.pattern[0]))
+        new_tests = []
+        for test in tests:
+            if pattern.match(test):
+                new_tests.append(test)
+        tests = new_tests
+
     os.chdir(get_script_dir())
     build_testdata()
     if AdbHelper().get_android_version() < 7:
         log_info("Skip tests on Android version < N.")
         sys.exit(0)
-    unittest.main(failfast=True)
+    log_info('Run tests %s' % ('\n'.join(tests)))
+    argv = [sys.argv[0]] + tests
+    unittest.main(argv=argv, failfast=True, verbosity=2)
 
 if __name__ == '__main__':
     main()
diff --git a/testdata/perf_with_long_callchain.data b/testdata/perf_with_long_callchain.data
new file mode 100644
index 0000000..87e59b7
--- /dev/null
+++ b/testdata/perf_with_long_callchain.data
Binary files differ
diff --git a/utils.py b/utils.py
index b224ce8..3abcfce 100644
--- a/utils.py
+++ b/utils.py
@@ -69,17 +69,17 @@
 def disable_debug_log():
     logging.getLogger().setLevel(logging.WARN)
 
-def str_to_bytes(str):
+def str_to_bytes(str_value):
     if not is_python3():
-        return str
+        return str_value
     # In python 3, str are wide strings whereas the C api expects 8 bit strings,
     # hence we have to convert. For now using utf-8 as the encoding.
-    return str.encode('utf-8')
+    return str_value.encode('utf-8')
 
-def bytes_to_str(bytes):
+def bytes_to_str(bytes_value):
     if not is_python3():
-        return bytes
-    return bytes.decode('utf-8')
+        return bytes_value
+    return bytes_value.decode('utf-8')
 
 def get_target_binary_path(arch, binary_name):
     if arch == 'aarch64':
@@ -94,21 +94,21 @@
 
 
 def get_host_binary_path(binary_name):
-    dir = os.path.join(get_script_dir(), 'bin')
+    dirname = os.path.join(get_script_dir(), 'bin')
     if is_windows():
         if binary_name.endswith('.so'):
             binary_name = binary_name[0:-3] + '.dll'
         elif '.' not in binary_name:
             binary_name += '.exe'
-        dir = os.path.join(dir, 'windows')
+        dirname = os.path.join(dirname, 'windows')
     elif sys.platform == 'darwin': # OSX
         if binary_name.endswith('.so'):
             binary_name = binary_name[0:-3] + '.dylib'
-        dir = os.path.join(dir, 'darwin')
+        dirname = os.path.join(dirname, 'darwin')
     else:
-        dir = os.path.join(dir, 'linux')
-    dir = os.path.join(dir, 'x86_64' if sys.maxsize > 2 ** 32 else 'x86')
-    binary_path = os.path.join(dir, binary_name)
+        dirname = os.path.join(dirname, 'linux')
+    dirname = os.path.join(dirname, 'x86_64' if sys.maxsize > 2 ** 32 else 'x86')
+    binary_path = os.path.join(dirname, binary_name)
     if not os.path.isfile(binary_path):
         log_fatal("can't find binary: %s" % binary_path)
     return binary_path
@@ -121,7 +121,7 @@
                                    stderr=subprocess.PIPE)
         subproc.communicate()
         return subproc.returncode == 0
-    except:
+    except OSError:
         return False
 
 DEFAULT_NDK_PATH = {
@@ -304,6 +304,7 @@
         if '86' in output:
             return 'x86'
         log_fatal('unsupported architecture: %s' % output.strip())
+        return ''
 
 
     def get_android_version(self):
@@ -342,21 +343,24 @@
         try:
             subprocess.check_call(['open', report_path])
             return
-        except:
+        except subprocess.CalledProcessError:
             pass
     import webbrowser
     try:
         # Try to open the report with Chrome
-        browser_key = ''
-        for key, _ in webbrowser._browsers.items():
-            if 'chrome' in key:
-                browser_key = key
-        browser = webbrowser.get(browser_key)
+        browser = webbrowser.get('google-chrome')
         browser.open(report_path, new=0, autoraise=True)
-    except:
+    except webbrowser.Error:
         # webbrowser.get() doesn't work well on darwin/windows.
         webbrowser.open_new_tab(report_path)
 
+def is_elf_file(path):
+    if os.path.isfile(path):
+        with open(path, 'rb') as fh:
+            data = fh.read(4)
+            if len(data) == 4 and bytes_to_str(data) == '\x7fELF':
+                return True
+    return False
 
 def find_real_dso_path(dso_path_in_record_file, binary_cache_path):
     """ Given the path of a shared library in perf.data, find its real path in the file system. """
@@ -364,9 +368,9 @@
         return None
     if binary_cache_path:
         tmp_path = os.path.join(binary_cache_path, dso_path_in_record_file[1:])
-        if os.path.isfile(tmp_path):
+        if is_elf_file(tmp_path):
             return tmp_path
-    if os.path.isfile(dso_path_in_record_file):
+    if is_elf_file(dso_path_in_record_file):
         return dso_path_in_record_file
     return None
 
@@ -415,17 +419,20 @@
             self.func_addr = func_addr
             self.source_lines = None
 
-    def __init__(self, ndk_path, binary_cache_path):
+    def __init__(self, ndk_path, binary_cache_path, with_function_name):
         self.addr2line_path = find_tool_path('addr2line', ndk_path)
         if not self.addr2line_path:
-            log_exit("Can't find addr2line. Please set ndk path with --ndk-path option.")
+            log_exit("Can't find addr2line. Please set ndk path with --ndk_path option.")
         self.readelf = ReadElf(ndk_path)
         self.dso_map = {}  # map from dso_path to Dso.
         self.binary_cache_path = binary_cache_path
+        self.with_function_name = with_function_name
         # Saving file names for each addr takes a lot of memory. So we store file ids in Addr,
         # and provide data structures connecting file id and file name here.
         self.file_name_to_id = {}
         self.file_id_to_name = []
+        self.func_name_to_id = {}
+        self.func_id_to_name = []
 
     def add_addr(self, dso_path, func_addr, addr):
         dso = self.dso_map.get(dso_path)
@@ -486,19 +493,26 @@
 
         # 2. Use addr2line to collect line info.
         try:
-            subproc = subprocess.Popen([self.addr2line_path, '-ai', '-e', real_path],
+            option = '-ai' + ('fC' if self.with_function_name else '')
+            subproc = subprocess.Popen([self.addr2line_path, option, '-e', real_path],
                                        stdin=subprocess.PIPE, stdout=subprocess.PIPE)
             (stdoutdata, _) = subproc.communicate(str_to_bytes(addr_request))
             stdoutdata = bytes_to_str(stdoutdata)
-        except:
+        except OSError:
             return
         addr_map = {}
         cur_line_list = None
+        need_function_name = self.with_function_name
+        cur_function_name = None
         for line in stdoutdata.strip().split('\n'):
             if line[:2] == '0x':
                 # a new address
                 cur_line_list = addr_map[int(line, 16)] = []
+            elif need_function_name:
+                cur_function_name = line.strip()
+                need_function_name = False
             else:
+                need_function_name = self.with_function_name
                 # a file:line.
                 if cur_line_list is None:
                     continue
@@ -521,7 +535,11 @@
                 except ValueError:
                     continue
                 file_id = self._get_file_id(file_path)
-                cur_line_list.append((file_id, line_number))
+                if self.with_function_name:
+                    func_id = self._get_func_id(cur_function_name)
+                    cur_line_list.append((file_id, line_number, func_id))
+                else:
+                    cur_line_list.append((file_id, line_number))
 
         # 3. Fill line info in dso.addrs.
         for addr in dso.addrs:
@@ -544,6 +562,13 @@
             self.file_id_to_name.append(file_path)
         return file_id
 
+    def _get_func_id(self, func_name):
+        func_id = self.func_name_to_id.get(func_name)
+        if func_id is None:
+            func_id = self.func_name_to_id[func_name] = len(self.func_id_to_name)
+            self.func_id_to_name.append(func_name)
+        return func_id
+
     def get_dso(self, dso_path):
         return self.dso_map.get(dso_path)
 
@@ -551,9 +576,75 @@
         source = dso.addrs[addr].source_lines
         if source is None:
             return None
+        if self.with_function_name:
+            return [(self.file_id_to_name[file_id], line, self.func_id_to_name[func_id])
+                    for (file_id, line, func_id) in source]
         return [(self.file_id_to_name[file_id], line) for (file_id, line) in source]
 
 
+class SourceFileSearcher(object):
+    """ Find source file paths in the file system.
+        The file paths reported by addr2line are the paths stored in debug sections
+        of shared libraries. And we need to convert them to file paths in the file
+        system. It is done in below steps:
+        1. Collect all file paths under the provided source_dirs. The suffix of a
+           source file should contain one of below:
+            h: for C/C++ header files.
+            c: for C/C++ source files.
+            java: for Java source files.
+            kt: for Kotlin source files.
+        2. Given an abstract_path reported by addr2line, select the best real path
+           as below:
+           2.1 Find all real paths with the same file name as the abstract path.
+           2.2 Select the real path having the longest common suffix with the abstract path.
+    """
+
+    SOURCE_FILE_EXTS = {'.h', '.hh', '.H', '.hxx', '.hpp', '.h++',
+                        '.c', '.cc', '.C', '.cxx', '.cpp', '.c++',
+                        '.java', '.kt'}
+
+    @classmethod
+    def is_source_filename(cls, filename):
+        ext = os.path.splitext(filename)[1]
+        return ext in cls.SOURCE_FILE_EXTS
+
+    def __init__(self, source_dirs):
+        # Map from filename to a list of reversed directory path containing filename.
+        self.filename_to_rparents = {}
+        self._collect_paths(source_dirs)
+
+    def _collect_paths(self, source_dirs):
+        for source_dir in source_dirs:
+            for parent, _, file_names in os.walk(source_dir):
+                rparent = None
+                for file_name in file_names:
+                    if self.is_source_filename(file_name):
+                        rparents = self.filename_to_rparents.get(file_name)
+                        if rparents is None:
+                            rparents = self.filename_to_rparents[file_name] = []
+                        if rparent is None:
+                            rparent = parent[::-1]
+                        rparents.append(rparent)
+
+    def get_real_path(self, abstract_path):
+        abstract_path = abstract_path.replace('/', os.sep)
+        abstract_parent, file_name = os.path.split(abstract_path)
+        abstract_rparent = abstract_parent[::-1]
+        real_rparents = self.filename_to_rparents.get(file_name)
+        if real_rparents is None:
+            return None
+        best_matched_rparent = None
+        best_common_length = -1
+        for real_rparent in real_rparents:
+            length = len(os.path.commonprefix((real_rparent, abstract_rparent)))
+            if length > best_common_length:
+                best_common_length = length
+                best_matched_rparent = real_rparent
+        if best_matched_rparent is None:
+            return None
+        return os.path.join(best_matched_rparent[::-1], file_name)
+
+
 class Objdump(object):
     """ A wrapper of objdump to disassemble code. """
     def __init__(self, ndk_path, binary_cache_path):
@@ -562,19 +653,20 @@
         self.readelf = ReadElf(ndk_path)
         self.objdump_paths = {}
 
-    def disassemble_code(self, dso_path, start_addr, addr_len):
-        """ Disassemble [start_addr, start_addr + addr_len] of dso_path.
-            Return a list of pair (disassemble_code_line, addr).
-        """
-        # 1. Find real path.
+    def get_dso_info(self, dso_path):
         real_path = find_real_dso_path(dso_path, self.binary_cache_path)
-        if real_path is None:
+        if not real_path:
             return None
-
-        # 2. Get path of objdump.
         arch = self.readelf.get_arch(real_path)
         if arch == 'unknown':
             return None
+        return (real_path, arch)
+
+    def disassemble_code(self, dso_info, start_addr, addr_len):
+        """ Disassemble [start_addr, start_addr + addr_len] of dso_path.
+            Return a list of pair (disassemble_code_line, addr).
+        """
+        real_path, arch = dso_info
         objdump_path = self.objdump_paths.get(arch)
         if not objdump_path:
             objdump_path = find_tool_path('objdump', self.ndk_path, arch)
@@ -591,7 +683,7 @@
             subproc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
             (stdoutdata, _) = subproc.communicate()
             stdoutdata = bytes_to_str(stdoutdata)
-        except:
+        except OSError:
             return None
 
         if not stdoutdata:
@@ -617,53 +709,57 @@
 
     def get_arch(self, elf_file_path):
         """ Get arch of an elf file. """
-        try:
-            output = subprocess.check_output([self.readelf_path, '-h', elf_file_path])
-            if output.find('AArch64') != -1:
-                return 'arm64'
-            if output.find('ARM') != -1:
-                return 'arm'
-            if output.find('X86-64') != -1:
-                return 'x86_64'
-            if output.find('80386') != -1:
-                return 'x86'
-        except subprocess.CalledProcessError:
-            pass
+        if is_elf_file(elf_file_path):
+            try:
+                output = subprocess.check_output([self.readelf_path, '-h', elf_file_path])
+                output = bytes_to_str(output)
+                if output.find('AArch64') != -1:
+                    return 'arm64'
+                if output.find('ARM') != -1:
+                    return 'arm'
+                if output.find('X86-64') != -1:
+                    return 'x86_64'
+                if output.find('80386') != -1:
+                    return 'x86'
+            except subprocess.CalledProcessError:
+                pass
         return 'unknown'
 
     def get_build_id(self, elf_file_path):
         """ Get build id of an elf file. """
-        try:
-            output = subprocess.check_output([self.readelf_path, '-n', elf_file_path])
-            output = bytes_to_str(output)
-            result = re.search(r'Build ID:\s*(\S+)', output)
-            if result:
-                build_id = result.group(1)
-                if len(build_id) < 40:
-                    build_id += '0' * (40 - len(build_id))
-                else:
-                    build_id = build_id[:40]
-                build_id = '0x' + build_id
-                return build_id
-        except subprocess.CalledProcessError:
-            pass
+        if is_elf_file(elf_file_path):
+            try:
+                output = subprocess.check_output([self.readelf_path, '-n', elf_file_path])
+                output = bytes_to_str(output)
+                result = re.search(r'Build ID:\s*(\S+)', output)
+                if result:
+                    build_id = result.group(1)
+                    if len(build_id) < 40:
+                        build_id += '0' * (40 - len(build_id))
+                    else:
+                        build_id = build_id[:40]
+                    build_id = '0x' + build_id
+                    return build_id
+            except subprocess.CalledProcessError:
+                pass
         return ""
 
     def get_sections(self, elf_file_path):
         """ Get sections of an elf file. """
         section_names = []
-        try:
-            output = subprocess.check_output([self.readelf_path, '-SW', elf_file_path])
-            output = bytes_to_str(output)
-            for line in output.split('\n'):
-                # Parse line like:" [ 1] .note.android.ident NOTE  0000000000400190 ...".
-                result = re.search(r'^\s+\[\s*\d+\]\s(.+?)\s', line)
-                if result:
-                    section_name = result.group(1).strip()
-                    if section_name:
-                        section_names.append(section_name)
-        except subprocess.CalledProcessError:
-            pass
+        if is_elf_file(elf_file_path):
+            try:
+                output = subprocess.check_output([self.readelf_path, '-SW', elf_file_path])
+                output = bytes_to_str(output)
+                for line in output.split('\n'):
+                    # Parse line like:" [ 1] .note.android.ident NOTE  0000000000400190 ...".
+                    result = re.search(r'^\s+\[\s*\d+\]\s(.+?)\s', line)
+                    if result:
+                        section_name = result.group(1).strip()
+                        if section_name:
+                            section_names.append(section_name)
+            except subprocess.CalledProcessError:
+                pass
         return section_names
 
 def extant_dir(arg):