Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/env python
      2 #
      3 # Copyright (C) 2017 The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #      http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 #
     17 """test.py: Tests for simpleperf python scripts.
     18 
     19 These are smoke tests Using examples to run python scripts.
     20 For each example, we go through the steps of running each python script.
     21 Examples are collected from simpleperf/demo, which includes:
     22   SimpleperfExamplePureJava
     23   SimpleperfExampleWithNative
     24   SimpleperfExampleOfKotlin
     25 
     26 Tested python scripts include:
     27   app_profiler.py
     28   report.py
     29   annotate.py
     30   report_sample.py
     31   pprof_proto_generator.py
     32   report_html.py
     33 
     34 Test using both `adb root` and `adb unroot`.
     35 
     36 """
     37 
     38 import os
     39 import re
     40 import shutil
     41 import signal
     42 import sys
     43 import tempfile
     44 import time
     45 import unittest
     46 
     47 from simpleperf_report_lib import ReportLib
     48 from utils import *
     49 
     50 has_google_protobuf = True
     51 try:
     52     import google.protobuf
     53 except:
     54     has_google_protobuf = False
     55 
     56 inferno_script = os.path.join(get_script_dir(), "inferno.bat" if is_windows() else "./inferno.sh")
     57 
     58 support_trace_offcpu = None
     59 
     60 def is_trace_offcpu_supported():
     61     global support_trace_offcpu
     62     if support_trace_offcpu is None:
     63         adb = AdbHelper()
     64         adb.check_run_and_return_output(['push',
     65                                          'bin/android/%s/simpleperf' % adb.get_device_arch(),
     66                                          "/data/local/tmp"])
     67         adb.check_run_and_return_output(['shell', 'chmod', 'a+x', '/data/local/tmp/simpleperf'])
     68         output = adb.check_run_and_return_output(['shell', '/data/local/tmp/simpleperf', 'list',
     69                                                   '--show-features'])
     70         support_trace_offcpu = 'trace-offcpu' in output
     71     return support_trace_offcpu
     72 
     73 def build_testdata():
     74     """ Collect testdata from ../testdata and ../demo. """
     75     from_testdata_path = os.path.join('..', 'testdata')
     76     from_demo_path = os.path.join('..', 'demo')
     77     from_script_testdata_path = 'script_testdata'
     78     if (not os.path.isdir(from_testdata_path) or not os.path.isdir(from_demo_path) or
     79         not from_script_testdata_path):
     80         return
     81     copy_testdata_list = ['perf_with_symbols.data', 'perf_with_trace_offcpu.data',
     82                           'perf_with_tracepoint_event.data']
     83     copy_demo_list = ['SimpleperfExamplePureJava', 'SimpleperfExampleWithNative',
     84                       'SimpleperfExampleOfKotlin']
     85 
     86     testdata_path = "testdata"
     87     remove(testdata_path)
     88     os.mkdir(testdata_path)
     89     for testdata in copy_testdata_list:
     90         shutil.copy(os.path.join(from_testdata_path, testdata), testdata_path)
     91     for demo in copy_demo_list:
     92         shutil.copytree(os.path.join(from_demo_path, demo), os.path.join(testdata_path, demo))
     93     for f in os.listdir(from_script_testdata_path):
     94         shutil.copy(os.path.join(from_script_testdata_path, f), testdata_path)
     95 
     96 class TestBase(unittest.TestCase):
     97     def run_cmd(self, args, return_output=False):
     98         if args[0].endswith('.py'):
     99             args = [sys.executable] + args
    100         use_shell = args[0].endswith('.bat')
    101         try:
    102             if not return_output:
    103                 returncode = subprocess.call(args, shell=use_shell)
    104             else:
    105                 subproc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=use_shell)
    106                 (output_data, _) = subproc.communicate()
    107                 returncode = subproc.returncode
    108         except:
    109             returncode = None
    110         self.assertEqual(returncode, 0, msg="failed to run cmd: %s" % args)
    111         if return_output:
    112             return output_data
    113 
    114 
    115 class TestExampleBase(TestBase):
    116     @classmethod
    117     def prepare(cls, example_name, package_name, activity_name, abi=None, adb_root=False):
    118         cls.adb = AdbHelper(enable_switch_to_root=adb_root)
    119         cls.example_path = os.path.join("testdata", example_name)
    120         if not os.path.isdir(cls.example_path):
    121             log_fatal("can't find " + cls.example_path)
    122         for root, _, files in os.walk(cls.example_path):
    123             if 'app-profiling.apk' in files:
    124                 cls.apk_path = os.path.join(root, 'app-profiling.apk')
    125                 break
    126         if not hasattr(cls, 'apk_path'):
    127             log_fatal("can't find app-profiling.apk under " + cls.example_path)
    128         cls.package_name = package_name
    129         cls.activity_name = activity_name
    130         cls.abi = "arm64"
    131         if abi and abi != "arm64" and abi.find("arm") != -1:
    132             cls.abi = "arm"
    133         args = ["install", "-r"]
    134         if abi:
    135             args += ["--abi", abi]
    136         args.append(cls.apk_path)
    137         cls.adb.check_run(args)
    138         cls.adb_root = adb_root
    139         cls.compiled = False
    140 
    141     def setUp(self):
    142         if self.id().find('TraceOffCpu') != -1 and not is_trace_offcpu_supported():
    143             self.skipTest('trace-offcpu is not supported on device')
    144 
    145     @classmethod
    146     def tearDownClass(cls):
    147         if hasattr(cls, 'test_result') and cls.test_result and not cls.test_result.wasSuccessful():
    148             return
    149         if hasattr(cls, 'package_name'):
    150             cls.adb.check_run(["uninstall", cls.package_name])
    151         remove("binary_cache")
    152         remove("annotated_files")
    153         remove("perf.data")
    154         remove("report.txt")
    155         remove("pprof.profile")
    156 
    157     def run(self, result=None):
    158         self.__class__.test_result = result
    159         super(TestBase, self).run(result)
    160 
    161     def run_app_profiler(self, record_arg = "-g -f 1000 --duration 3 -e cpu-cycles:u",
    162                          build_binary_cache=True, skip_compile=False, start_activity=True,
    163                          native_lib_dir=None, profile_from_launch=False, add_arch=False):
    164         args = ["app_profiler.py", "--app", self.package_name, "--apk", self.apk_path,
    165                 "-r", record_arg, "-o", "perf.data"]
    166         if not build_binary_cache:
    167             args.append("-nb")
    168         if skip_compile or self.__class__.compiled:
    169             args.append("-nc")
    170         if start_activity:
    171             args += ["-a", self.activity_name]
    172         if native_lib_dir:
    173             args += ["-lib", native_lib_dir]
    174         if profile_from_launch:
    175             args.append("--profile_from_launch")
    176         if add_arch:
    177             args += ["--arch", self.abi]
    178         if not self.adb_root:
    179             args.append("--disable_adb_root")
    180         self.run_cmd(args)
    181         self.check_exist(file="perf.data")
    182         if build_binary_cache:
    183             self.check_exist(dir="binary_cache")
    184         if not skip_compile:
    185             self.__class__.compiled = True
    186 
    187     def check_exist(self, file=None, dir=None):
    188         if file:
    189             self.assertTrue(os.path.isfile(file), file)
    190         if dir:
    191             self.assertTrue(os.path.isdir(dir), dir)
    192 
    193     def check_file_under_dir(self, dir, file):
    194         self.check_exist(dir=dir)
    195         for _, _, files in os.walk(dir):
    196             for f in files:
    197                 if f == file:
    198                     return
    199         self.fail("Failed to call check_file_under_dir(dir=%s, file=%s)" % (dir, file))
    200 
    201 
    202     def check_strings_in_file(self, file, strings):
    203         self.check_exist(file=file)
    204         with open(file, 'r') as fh:
    205             self.check_strings_in_content(fh.read(), strings)
    206 
    207     def check_strings_in_content(self, content, strings):
    208         for s in strings:
    209             self.assertNotEqual(content.find(s), -1, "s: %s, content: %s" % (s, content))
    210 
    211     def check_annotation_summary(self, summary_file, check_entries):
    212         """ check_entries is a list of (name, accumulated_period, period).
    213             This function checks for each entry, if the line containing [name]
    214             has at least required accumulated_period and period.
    215         """
    216         self.check_exist(file=summary_file)
    217         with open(summary_file, 'r') as fh:
    218             summary = fh.read()
    219         fulfilled = [False for x in check_entries]
    220         if not hasattr(self, "summary_check_re"):
    221             self.summary_check_re = re.compile(r'accumulated_period:\s*([\d.]+)%.*period:\s*([\d.]+)%')
    222         for line in summary.split('\n'):
    223             for i in range(len(check_entries)):
    224                 (name, need_acc_period, need_period) = check_entries[i]
    225                 if not fulfilled[i] and name in line:
    226                     m = self.summary_check_re.search(line)
    227                     if m:
    228                         acc_period = float(m.group(1))
    229                         period = float(m.group(2))
    230                         if acc_period >= need_acc_period and period >= need_period:
    231                             fulfilled[i] = True
    232         self.assertEqual(len(fulfilled), sum([int(x) for x in fulfilled]), fulfilled)
    233 
    234     def check_inferno_report_html(self, check_entries, file="report.html"):
    235         self.check_exist(file=file)
    236         with open(file, 'r') as fh:
    237             data = fh.read()
    238         fulfilled = [False for _ in check_entries]
    239         for line in data.split('\n'):
    240             # each entry is a (function_name, min_percentage) pair.
    241             for i, entry in enumerate(check_entries):
    242                 if fulfilled[i] or line.find(entry[0]) == -1:
    243                     continue
    244                 m = re.search(r'(\d+\.\d+)%', line)
    245                 if m and float(m.group(1)) >= entry[1]:
    246                     fulfilled[i] = True
    247                     break
    248         self.assertEqual(fulfilled, [True for x in check_entries])
    249 
    250     def common_test_app_profiler(self):
    251         self.run_cmd(["app_profiler.py", "-h"])
    252         remove("binary_cache")
    253         self.run_app_profiler(build_binary_cache=False)
    254         self.assertFalse(os.path.isdir("binary_cache"))
    255         args = ["binary_cache_builder.py"]
    256         if not self.adb_root:
    257             args.append("--disable_adb_root")
    258         self.run_cmd(args)
    259         self.check_exist(dir="binary_cache")
    260         remove("binary_cache")
    261         self.run_app_profiler(build_binary_cache=True)
    262         self.run_app_profiler(skip_compile=True)
    263         self.run_app_profiler(start_activity=False)
    264 
    265     def common_test_report(self):
    266         self.run_cmd(["report.py", "-h"])
    267         self.run_app_profiler(build_binary_cache=False)
    268         self.run_cmd(["report.py"])
    269         self.run_cmd(["report.py", "-i", "perf.data"])
    270         self.run_cmd(["report.py", "-g"])
    271         self.run_cmd(["report.py", "--self-kill-for-testing",  "-g", "--gui"])
    272 
    273     def common_test_annotate(self):
    274         self.run_cmd(["annotate.py", "-h"])
    275         self.run_app_profiler()
    276         remove("annotated_files")
    277         self.run_cmd(["annotate.py", "-s", self.example_path])
    278         self.check_exist(dir="annotated_files")
    279 
    280     def common_test_report_sample(self, check_strings):
    281         self.run_cmd(["report_sample.py", "-h"])
    282         remove("binary_cache")
    283         self.run_app_profiler(build_binary_cache=False)
    284         self.run_cmd(["report_sample.py"])
    285         output = self.run_cmd(["report_sample.py", "perf.data"], return_output=True)
    286         self.check_strings_in_content(output, check_strings)
    287         self.run_app_profiler(record_arg="-g -f 1000 --duration 3 -e cpu-cycles:u --no-dump-symbols")
    288         output = self.run_cmd(["report_sample.py", "--symfs", "binary_cache"], return_output=True)
    289         self.check_strings_in_content(output, check_strings)
    290 
    291     def common_test_pprof_proto_generator(self, check_strings_with_lines,
    292                                           check_strings_without_lines):
    293         if not has_google_protobuf:
    294             log_info('Skip test for pprof_proto_generator because google.protobuf is missing')
    295             return
    296         self.run_cmd(["pprof_proto_generator.py", "-h"])
    297         self.run_app_profiler()
    298         self.run_cmd(["pprof_proto_generator.py"])
    299         remove("pprof.profile")
    300         self.run_cmd(["pprof_proto_generator.py", "-i", "perf.data", "-o", "pprof.profile"])
    301         self.check_exist(file="pprof.profile")
    302         self.run_cmd(["pprof_proto_generator.py", "--show"])
    303         output = self.run_cmd(["pprof_proto_generator.py", "--show", "pprof.profile"],
    304                               return_output=True)
    305         self.check_strings_in_content(output, check_strings_with_lines +
    306                                               ["has_line_numbers: True"])
    307         remove("binary_cache")
    308         self.run_cmd(["pprof_proto_generator.py"])
    309         output = self.run_cmd(["pprof_proto_generator.py", "--show", "pprof.profile"],
    310                               return_output=True)
    311         self.check_strings_in_content(output, check_strings_without_lines +
    312                                               ["has_line_numbers: False"])
    313 
    314     def common_test_inferno(self):
    315         self.run_cmd([inferno_script, "-h"])
    316         remove("perf.data")
    317         append_args = [] if self.adb_root else ["--disable_adb_root"]
    318         self.run_cmd([inferno_script, "-p", self.package_name, "-t", "3"] + append_args)
    319         self.check_exist(file="perf.data")
    320         self.run_cmd([inferno_script, "-p", self.package_name, "-f", "1000", "-du", "-t", "1",
    321                       "-nc"] + append_args)
    322         self.run_cmd([inferno_script, "-p", self.package_name, "-e", "100000 cpu-cycles",
    323                       "-t", "1", "-nc"] + append_args)
    324         self.run_cmd([inferno_script, "-sc"])
    325 
    326     def common_test_report_html(self):
    327         self.run_cmd(['report_html.py', '-h'])
    328         self.run_app_profiler(record_arg='-g -f 1000 --duration 3 -e task-clock:u')
    329         self.run_cmd(['report_html.py'])
    330         self.run_cmd(['report_html.py', '--add_source_code', '--source_dirs', 'testdata'])
    331         self.run_cmd(['report_html.py', '--add_disassembly'])
    332         # Test with multiple perf.data.
    333         shutil.move('perf.data', 'perf2.data')
    334         self.run_app_profiler()
    335         self.run_cmd(['report_html.py', '-i', 'perf.data', 'perf2.data'])
    336         remove('perf2.data')
    337 
    338 
    339 class TestExamplePureJava(TestExampleBase):
    340     @classmethod
    341     def setUpClass(cls):
    342         cls.prepare("SimpleperfExamplePureJava",
    343                     "com.example.simpleperf.simpleperfexamplepurejava",
    344                     ".MainActivity")
    345 
    346     def test_app_profiler(self):
    347         self.common_test_app_profiler()
    348 
    349     def test_app_profiler_profile_from_launch(self):
    350         self.run_app_profiler(profile_from_launch=True, add_arch=True, build_binary_cache=False)
    351         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    352         self.check_strings_in_file("report.txt",
    353             ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
    354              "__start_thread"])
    355 
    356     def test_app_profiler_multiprocesses(self):
    357         self.adb.check_run(['shell', 'am', 'force-stop', self.package_name])
    358         self.adb.check_run(['shell', 'am', 'start', '-n',
    359                             self.package_name + '/.MultiProcessActivity'])
    360         # Wait until both MultiProcessActivity and MultiProcessService set up.
    361         time.sleep(3)
    362         self.run_app_profiler(skip_compile=True, start_activity=False)
    363         self.run_cmd(["report.py", "-o", "report.txt"])
    364         self.check_strings_in_file("report.txt", ["BusyService", "BusyThread"])
    365 
    366     def test_app_profiler_with_ctrl_c(self):
    367         if is_windows():
    368             return
    369         self.adb.check_run(['shell', 'am', 'start', '-n', self.package_name + '/.MainActivity'])
    370         time.sleep(1)
    371         args = [sys.executable, "app_profiler.py", "--app", self.package_name,
    372                 "-r", "--duration 10000", "-nc", "--disable_adb_root"]
    373         subproc = subprocess.Popen(args)
    374         time.sleep(3)
    375 
    376         subproc.send_signal(signal.SIGINT)
    377         subproc.wait()
    378         self.assertEqual(subproc.returncode, 0)
    379         self.run_cmd(["report.py"])
    380 
    381     def test_app_profiler_stop_after_app_exit(self):
    382         self.adb.check_run(['shell', 'am', 'start', '-n', self.package_name + '/.MainActivity'])
    383         time.sleep(1)
    384         subproc = subprocess.Popen([sys.executable, 'app_profiler.py', '--app', self.package_name,
    385                                     '-r', '--duration 10000', '-nc', '--disable_adb_root'])
    386         time.sleep(3)
    387         self.adb.check_run(['shell', 'am', 'force-stop', self.package_name])
    388         subproc.wait()
    389         self.assertEqual(subproc.returncode, 0)
    390         self.run_cmd(["report.py"])
    391 
    392     def test_report(self):
    393         self.common_test_report()
    394         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    395         self.check_strings_in_file("report.txt",
    396             ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
    397              "__start_thread"])
    398 
    399     def test_annotate(self):
    400         self.common_test_annotate()
    401         self.check_file_under_dir("annotated_files", "MainActivity.java")
    402         summary_file = os.path.join("annotated_files", "summary")
    403         self.check_annotation_summary(summary_file,
    404             [("MainActivity.java", 80, 80),
    405              ("run", 80, 0),
    406              ("callFunction", 0, 0),
    407              ("line 23", 80, 0)])
    408 
    409     def test_report_sample(self):
    410         self.common_test_report_sample(
    411             ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
    412              "__start_thread"])
    413 
    414     def test_pprof_proto_generator(self):
    415         self.common_test_pprof_proto_generator(
    416             check_strings_with_lines=
    417                 ["com/example/simpleperf/simpleperfexamplepurejava/MainActivity.java",
    418                  "run"],
    419             check_strings_without_lines=
    420                 ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()"])
    421 
    422     def test_inferno(self):
    423         self.common_test_inferno()
    424         self.run_app_profiler()
    425         self.run_cmd([inferno_script, "-sc"])
    426         self.check_inferno_report_html(
    427             [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()', 80)])
    428         self.run_cmd([inferno_script, "-sc", "-o", "report2.html"])
    429         self.check_inferno_report_html(
    430             [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()', 80)],
    431             "report2.html")
    432         remove("report2.html")
    433 
    434     def test_inferno_in_another_dir(self):
    435         test_dir = 'inferno_testdir'
    436         saved_dir = os.getcwd()
    437         remove(test_dir)
    438         os.mkdir(test_dir)
    439         os.chdir(test_dir)
    440         self.run_cmd(['python', os.path.join(saved_dir, 'app_profiler.py'),
    441                       '--app', self.package_name, '-r', '-e task-clock:u -g --duration 3'])
    442         self.check_exist(file="perf.data")
    443         self.run_cmd([inferno_script, "-sc"])
    444         os.chdir(saved_dir)
    445         remove(test_dir)
    446 
    447     def test_report_html(self):
    448         self.common_test_report_html()
    449 
    450 
    451 class TestExamplePureJavaRoot(TestExampleBase):
    452     @classmethod
    453     def setUpClass(cls):
    454         cls.prepare("SimpleperfExamplePureJava",
    455                     "com.example.simpleperf.simpleperfexamplepurejava",
    456                     ".MainActivity",
    457                     adb_root=True)
    458 
    459     def test_app_profiler(self):
    460         self.common_test_app_profiler()
    461 
    462 
    463 class TestExamplePureJavaTraceOffCpu(TestExampleBase):
    464     @classmethod
    465     def setUpClass(cls):
    466         cls.prepare("SimpleperfExamplePureJava",
    467                     "com.example.simpleperf.simpleperfexamplepurejava",
    468                     ".SleepActivity")
    469 
    470     def test_smoke(self):
    471         self.run_app_profiler(record_arg="-g -f 1000 --duration 3 -e cpu-cycles:u --trace-offcpu")
    472         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    473         self.check_strings_in_file("report.txt",
    474             ["com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.run()",
    475              "long com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.RunFunction()",
    476              "long com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.SleepFunction(long)"
    477              ])
    478         remove("annotated_files")
    479         self.run_cmd(["annotate.py", "-s", self.example_path])
    480         self.check_exist(dir="annotated_files")
    481         self.check_file_under_dir("annotated_files", "SleepActivity.java")
    482         summary_file = os.path.join("annotated_files", "summary")
    483         self.check_annotation_summary(summary_file,
    484             [("SleepActivity.java", 80, 20),
    485              ("run", 80, 0),
    486              ("RunFunction", 20, 20),
    487              ("SleepFunction", 20, 0),
    488              ("line 24", 20, 0),
    489              ("line 32", 20, 0)])
    490         self.run_cmd([inferno_script, "-sc"])
    491         self.check_inferno_report_html(
    492             [('com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.run() ', 80),
    493              ('com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.RunFunction()',
    494               20),
    495              ('com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.SleepFunction(long)',
    496               20)])
    497 
    498 
    499 class TestExampleWithNative(TestExampleBase):
    500     @classmethod
    501     def setUpClass(cls):
    502         cls.prepare("SimpleperfExampleWithNative",
    503                     "com.example.simpleperf.simpleperfexamplewithnative",
    504                     ".MainActivity")
    505 
    506     def test_app_profiler(self):
    507         self.common_test_app_profiler()
    508         remove("binary_cache")
    509         self.run_app_profiler(native_lib_dir=self.example_path)
    510 
    511     def test_app_profiler_profile_from_launch(self):
    512         self.run_app_profiler(profile_from_launch=True, add_arch=True, build_binary_cache=False)
    513         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    514         self.check_strings_in_file("report.txt",
    515             ["BusyLoopThread",
    516              "__start_thread"])
    517 
    518     def test_report(self):
    519         self.common_test_report()
    520         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    521         self.check_strings_in_file("report.txt",
    522             ["BusyLoopThread",
    523              "__start_thread"])
    524 
    525     def test_annotate(self):
    526         self.common_test_annotate()
    527         self.check_file_under_dir("annotated_files", "native-lib.cpp")
    528         summary_file = os.path.join("annotated_files", "summary")
    529         self.check_annotation_summary(summary_file,
    530             [("native-lib.cpp", 20, 0),
    531              ("BusyLoopThread", 20, 0),
    532              ("line 46", 20, 0)])
    533 
    534     def test_report_sample(self):
    535         self.common_test_report_sample(
    536             ["BusyLoopThread",
    537              "__start_thread"])
    538 
    539     def test_pprof_proto_generator(self):
    540         self.common_test_pprof_proto_generator(
    541             check_strings_with_lines=
    542                 ["native-lib.cpp",
    543                  "BusyLoopThread"],
    544             check_strings_without_lines=
    545                 ["BusyLoopThread"])
    546 
    547     def test_inferno(self):
    548         self.common_test_inferno()
    549         self.run_app_profiler()
    550         self.run_cmd([inferno_script, "-sc"])
    551         self.check_inferno_report_html([('BusyLoopThread', 20)])
    552 
    553     def test_report_html(self):
    554         self.common_test_report_html()
    555 
    556 
    557 class TestExampleWithNativeRoot(TestExampleBase):
    558     @classmethod
    559     def setUpClass(cls):
    560         cls.prepare("SimpleperfExampleWithNative",
    561                     "com.example.simpleperf.simpleperfexamplewithnative",
    562                     ".MainActivity",
    563                     adb_root=True)
    564 
    565     def test_app_profiler(self):
    566         self.common_test_app_profiler()
    567         remove("binary_cache")
    568         self.run_app_profiler(native_lib_dir=self.example_path)
    569 
    570 
    571 class TestExampleWithNativeTraceOffCpu(TestExampleBase):
    572     @classmethod
    573     def setUpClass(cls):
    574         cls.prepare("SimpleperfExampleWithNative",
    575                     "com.example.simpleperf.simpleperfexamplewithnative",
    576                     ".SleepActivity")
    577 
    578     def test_smoke(self):
    579         self.run_app_profiler(record_arg="-g -f 1000 --duration 3 -e cpu-cycles:u --trace-offcpu")
    580         self.run_cmd(["report.py", "-g", "--comms", "SleepThread", "-o", "report.txt"])
    581         self.check_strings_in_file("report.txt",
    582             ["SleepThread(void*)",
    583              "RunFunction()",
    584              "SleepFunction(unsigned long long)"])
    585         remove("annotated_files")
    586         self.run_cmd(["annotate.py", "-s", self.example_path, "--comm", "SleepThread"])
    587         self.check_exist(dir="annotated_files")
    588         self.check_file_under_dir("annotated_files", "native-lib.cpp")
    589         summary_file = os.path.join("annotated_files", "summary")
    590         self.check_annotation_summary(summary_file,
    591             [("native-lib.cpp", 80, 20),
    592              ("SleepThread", 80, 0),
    593              ("RunFunction", 20, 20),
    594              ("SleepFunction", 20, 0),
    595              ("line 73", 20, 0),
    596              ("line 83", 20, 0)])
    597         self.run_cmd([inferno_script, "-sc"])
    598         self.check_inferno_report_html([('SleepThread', 80),
    599                                         ('RunFunction', 20),
    600                                         ('SleepFunction', 20)])
    601 
    602 
    603 class TestExampleWithNativeJniCall(TestExampleBase):
    604     @classmethod
    605     def setUpClass(cls):
    606         cls.prepare("SimpleperfExampleWithNative",
    607                     "com.example.simpleperf.simpleperfexamplewithnative",
    608                     ".MixActivity")
    609 
    610     def test_smoke(self):
    611         self.run_app_profiler()
    612         self.run_cmd(["report.py", "-g", "--comms", "BusyThread", "-o", "report.txt"])
    613         self.check_strings_in_file("report.txt",
    614             ["void com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run()",
    615              "int com.example.simpleperf.simpleperfexamplewithnative.MixActivity.callFunction(int)",
    616              "Java_com_example_simpleperf_simpleperfexamplewithnative_MixActivity_callFunction"])
    617         remove("annotated_files")
    618         self.run_cmd(["annotate.py", "-s", self.example_path, "--comm", "BusyThread"])
    619         self.check_exist(dir="annotated_files")
    620         self.check_file_under_dir("annotated_files", "native-lib.cpp")
    621         self.check_file_under_dir("annotated_files", "MixActivity.java")
    622         summary_file = os.path.join("annotated_files", "summary")
    623         self.check_annotation_summary(summary_file,
    624             [("MixActivity.java", 80, 0),
    625              ("run", 80, 0),
    626              ("line 26", 20, 0),
    627              ("native-lib.cpp", 5, 0),
    628              ("line 40", 5, 0)])
    629         self.run_cmd([inferno_script, "-sc"])
    630 
    631 
    632 class TestExampleWithNativeForceArm(TestExampleWithNative):
    633     @classmethod
    634     def setUpClass(cls):
    635         cls.prepare("SimpleperfExampleWithNative",
    636                     "com.example.simpleperf.simpleperfexamplewithnative",
    637                     ".MainActivity",
    638                     abi="armeabi-v7a")
    639 
    640 
    641 class TestExampleWithNativeForceArmRoot(TestExampleWithNativeRoot):
    642     @classmethod
    643     def setUpClass(cls):
    644         cls.prepare("SimpleperfExampleWithNative",
    645                     "com.example.simpleperf.simpleperfexamplewithnative",
    646                     ".MainActivity",
    647                     abi="armeabi-v7a",
    648                     adb_root=False)
    649 
    650 
    651 class TestExampleWithNativeTraceOffCpuForceArm(TestExampleWithNativeTraceOffCpu):
    652     @classmethod
    653     def setUpClass(cls):
    654         cls.prepare("SimpleperfExampleWithNative",
    655                     "com.example.simpleperf.simpleperfexamplewithnative",
    656                     ".SleepActivity",
    657                     abi="armeabi-v7a")
    658 
    659 
    660 class TestExampleOfKotlin(TestExampleBase):
    661     @classmethod
    662     def setUpClass(cls):
    663         cls.prepare("SimpleperfExampleOfKotlin",
    664                     "com.example.simpleperf.simpleperfexampleofkotlin",
    665                     ".MainActivity")
    666 
    667     def test_app_profiler(self):
    668         self.common_test_app_profiler()
    669 
    670     def test_app_profiler_profile_from_launch(self):
    671         self.run_app_profiler(profile_from_launch=True, add_arch=True, build_binary_cache=False)
    672         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    673         self.check_strings_in_file("report.txt",
    674             ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
    675              "__start_thread"])
    676 
    677     def test_report(self):
    678         self.common_test_report()
    679         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    680         self.check_strings_in_file("report.txt",
    681             ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
    682              "__start_thread"])
    683 
    684     def test_annotate(self):
    685         self.common_test_annotate()
    686         self.check_file_under_dir("annotated_files", "MainActivity.kt")
    687         summary_file = os.path.join("annotated_files", "summary")
    688         self.check_annotation_summary(summary_file,
    689             [("MainActivity.kt", 80, 80),
    690              ("run", 80, 0),
    691              ("callFunction", 0, 0),
    692              ("line 19", 80, 0),
    693              ("line 25", 0, 0)])
    694 
    695     def test_report_sample(self):
    696         self.common_test_report_sample(
    697             ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
    698              "__start_thread"])
    699 
    700     def test_pprof_proto_generator(self):
    701         self.common_test_pprof_proto_generator(
    702             check_strings_with_lines=
    703                 ["com/example/simpleperf/simpleperfexampleofkotlin/MainActivity.kt",
    704                  "run"],
    705             check_strings_without_lines=
    706                 ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()"])
    707 
    708     def test_inferno(self):
    709         self.common_test_inferno()
    710         self.run_app_profiler()
    711         self.run_cmd([inferno_script, "-sc"])
    712         self.check_inferno_report_html(
    713             [('com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()',
    714               80)])
    715 
    716     def test_report_html(self):
    717         self.common_test_report_html()
    718 
    719 
    720 class TestExampleOfKotlinRoot(TestExampleBase):
    721     @classmethod
    722     def setUpClass(cls):
    723         cls.prepare("SimpleperfExampleOfKotlin",
    724                     "com.example.simpleperf.simpleperfexampleofkotlin",
    725                     ".MainActivity",
    726                     adb_root=True)
    727 
    728     def test_app_profiler(self):
    729         self.common_test_app_profiler()
    730 
    731 
    732 class TestExampleOfKotlinTraceOffCpu(TestExampleBase):
    733     @classmethod
    734     def setUpClass(cls):
    735         cls.prepare("SimpleperfExampleOfKotlin",
    736                     "com.example.simpleperf.simpleperfexampleofkotlin",
    737                     ".SleepActivity")
    738 
    739     def test_smoke(self):
    740         self.run_app_profiler(record_arg="-g -f 1000 --duration 3 -e cpu-cycles:u --trace-offcpu")
    741         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    742         self.check_strings_in_file("report.txt",
    743             ["void com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.run()",
    744              "long com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.RunFunction()",
    745              "long com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.SleepFunction(long)"
    746              ])
    747         remove("annotated_files")
    748         self.run_cmd(["annotate.py", "-s", self.example_path])
    749         self.check_exist(dir="annotated_files")
    750         self.check_file_under_dir("annotated_files", "SleepActivity.kt")
    751         summary_file = os.path.join("annotated_files", "summary")
    752         self.check_annotation_summary(summary_file,
    753             [("SleepActivity.kt", 80, 20),
    754              ("run", 80, 0),
    755              ("RunFunction", 20, 20),
    756              ("SleepFunction", 20, 0),
    757              ("line 24", 20, 0),
    758              ("line 32", 20, 0)])
    759         self.run_cmd([inferno_script, "-sc"])
    760         self.check_inferno_report_html(
    761             [('void com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.run()',
    762               80),
    763              ('long com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.RunFunction()',
    764               20),
    765              ('long com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.SleepFunction(long)',
    766               20)])
    767 
    768 
    769 class TestProfilingNativeProgram(TestExampleBase):
    770     def test_smoke(self):
    771         if not AdbHelper().switch_to_root():
    772             log_info('skip TestProfilingNativeProgram on non-rooted devices.')
    773             return
    774         remove("perf.data")
    775         self.run_cmd(["app_profiler.py", "-np", "surfaceflinger",
    776                       "-r", "-g -f 1000 --duration 3 -e cpu-cycles:u"])
    777         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    778 
    779 
    780 class TestProfilingCmd(TestExampleBase):
    781     def test_smoke(self):
    782         remove("perf.data")
    783         self.run_cmd(["app_profiler.py", "-cmd", "pm -l", "--disable_adb_root"])
    784         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    785 
    786     def test_set_arch(self):
    787         arch = AdbHelper().get_device_arch()
    788         remove("perf.data")
    789         self.run_cmd(["app_profiler.py", "-cmd", "pm -l", "--arch", arch])
    790         self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    791 
    792 
    793 class TestProfilingNativeProgram(TestExampleBase):
    794     def test_smoke(self):
    795         adb = AdbHelper()
    796         if adb.switch_to_root():
    797             self.run_cmd(["app_profiler.py", "-np", "surfaceflinger"])
    798             self.run_cmd(["report.py", "-g", "-o", "report.txt"])
    799             self.run_cmd([inferno_script, "-sc"])
    800             self.run_cmd([inferno_script, "-np", "surfaceflinger"])
    801 
    802 
    803 class TestReportLib(unittest.TestCase):
    804     def setUp(self):
    805         self.report_lib = ReportLib()
    806         self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_symbols.data'))
    807 
    808     def tearDown(self):
    809         self.report_lib.Close()
    810 
    811     def test_build_id(self):
    812         build_id = self.report_lib.GetBuildIdForPath('/data/t2')
    813         self.assertEqual(build_id, '0x70f1fe24500fc8b0d9eb477199ca1ca21acca4de')
    814 
    815     def test_symbol(self):
    816         found_func2 = False
    817         while self.report_lib.GetNextSample():
    818             sample = self.report_lib.GetCurrentSample()
    819             symbol = self.report_lib.GetSymbolOfCurrentSample()
    820             if symbol.symbol_name == 'func2(int, int)':
    821                 found_func2 = True
    822                 self.assertEqual(symbol.symbol_addr, 0x4004ed)
    823                 self.assertEqual(symbol.symbol_len, 0x14)
    824         self.assertTrue(found_func2)
    825 
    826     def test_sample(self):
    827         found_sample = False
    828         while self.report_lib.GetNextSample():
    829             sample = self.report_lib.GetCurrentSample()
    830             if sample.ip == 0x4004ff and sample.time == 7637889424953:
    831                 found_sample = True
    832                 self.assertEqual(sample.pid, 15926)
    833                 self.assertEqual(sample.tid, 15926)
    834                 self.assertEqual(sample.thread_comm, 't2')
    835                 self.assertEqual(sample.cpu, 5)
    836                 self.assertEqual(sample.period, 694614)
    837                 event = self.report_lib.GetEventOfCurrentSample()
    838                 self.assertEqual(event.name, 'cpu-cycles')
    839                 callchain = self.report_lib.GetCallChainOfCurrentSample()
    840                 self.assertEqual(callchain.nr, 0)
    841         self.assertTrue(found_sample)
    842 
    843     def test_meta_info(self):
    844         self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
    845         meta_info = self.report_lib.MetaInfo()
    846         self.assertTrue("simpleperf_version" in meta_info)
    847         self.assertEqual(meta_info["system_wide_collection"], "false")
    848         self.assertEqual(meta_info["trace_offcpu"], "true")
    849         self.assertEqual(meta_info["event_type_info"], "cpu-cycles,0,0\nsched:sched_switch,2,47")
    850         self.assertTrue("product_props" in meta_info)
    851 
    852     def test_event_name_from_meta_info(self):
    853         self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_tracepoint_event.data'))
    854         event_names = set()
    855         while self.report_lib.GetNextSample():
    856             event_names.add(self.report_lib.GetEventOfCurrentSample().name)
    857         self.assertTrue('sched:sched_switch' in event_names)
    858         self.assertTrue('cpu-cycles' in event_names)
    859 
    860     def test_record_cmd(self):
    861         self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
    862         self.assertEqual(self.report_lib.GetRecordCmd(),
    863                          "/data/local/tmp/simpleperf record --trace-offcpu --duration 2 -g ./simpleperf_runtest_run_and_sleep64")
    864 
    865     def test_offcpu(self):
    866         self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
    867         total_period = 0
    868         sleep_function_period = 0
    869         sleep_function_name = "SleepFunction(unsigned long long)"
    870         while self.report_lib.GetNextSample():
    871             sample = self.report_lib.GetCurrentSample()
    872             total_period += sample.period
    873             if self.report_lib.GetSymbolOfCurrentSample().symbol_name == sleep_function_name:
    874                 sleep_function_period += sample.period
    875                 continue
    876             callchain = self.report_lib.GetCallChainOfCurrentSample()
    877             for i in range(callchain.nr):
    878                 if callchain.entries[i].symbol.symbol_name == sleep_function_name:
    879                     sleep_function_period += sample.period
    880                     break
    881             self.assertEqual(self.report_lib.GetEventOfCurrentSample().name, 'cpu-cycles')
    882         sleep_percentage = float(sleep_function_period) / total_period
    883         self.assertGreater(sleep_percentage, 0.30)
    884 
    885 
    886 class TestRunSimpleperfOnDevice(TestBase):
    887     def test_smoke(self):
    888         self.run_cmd(['run_simpleperf_on_device.py', 'list', '--show-features'])
    889 
    890 
    891 class TestTools(unittest.TestCase):
    892     def test_addr2nearestline(self):
    893         binary_cache_path = 'testdata'
    894         test_map = {
    895             '/simpleperf_runtest_two_functions_arm64': [
    896                 {
    897                     'func_addr': 0x668,
    898                     'addr': 0x668,
    899                     'source': 'system/extras/simpleperf/runtest/two_functions.cpp:20',
    900                 },
    901                 {
    902                     'func_addr': 0x668,
    903                     'addr': 0x6a4,
    904                     'source': """system/extras/simpleperf/runtest/two_functions.cpp:7
    905                                  system/extras/simpleperf/runtest/two_functions.cpp:22""",
    906                 },
    907             ],
    908             '/simpleperf_runtest_two_functions_arm': [
    909                 {
    910                     'func_addr': 0x784,
    911                     'addr': 0x7b0,
    912                     'source': """system/extras/simpleperf/runtest/two_functions.cpp:14
    913                                  system/extras/simpleperf/runtest/two_functions.cpp:23""",
    914                 },
    915                 {
    916                     'func_addr': 0x784,
    917                     'addr': 0x7d0,
    918                     'source': """system/extras/simpleperf/runtest/two_functions.cpp:15
    919                                  system/extras/simpleperf/runtest/two_functions.cpp:23""",
    920                 }
    921             ],
    922             '/simpleperf_runtest_two_functions_x86_64': [
    923                 {
    924                     'func_addr': 0x840,
    925                     'addr': 0x840,
    926                     'source': 'system/extras/simpleperf/runtest/two_functions.cpp:7',
    927                 },
    928                 {
    929                     'func_addr': 0x920,
    930                     'addr': 0x94a,
    931                     'source': """system/extras/simpleperf/runtest/two_functions.cpp:7
    932                                  system/extras/simpleperf/runtest/two_functions.cpp:22""",
    933                 }
    934             ],
    935             '/simpleperf_runtest_two_functions_x86': [
    936                 {
    937                     'func_addr': 0x6d0,
    938                     'addr': 0x6da,
    939                     'source': 'system/extras/simpleperf/runtest/two_functions.cpp:14',
    940                 },
    941                 {
    942                     'func_addr': 0x710,
    943                     'addr': 0x749,
    944                     'source': """system/extras/simpleperf/runtest/two_functions.cpp:8
    945                                  system/extras/simpleperf/runtest/two_functions.cpp:22""",
    946                 }
    947             ],
    948         }
    949         addr2line = Addr2Nearestline(None, binary_cache_path)
    950         for dso_path in test_map:
    951             test_addrs = test_map[dso_path]
    952             for test_addr in test_addrs:
    953                 addr2line.add_addr(dso_path, test_addr['func_addr'], test_addr['addr'])
    954         addr2line.convert_addrs_to_lines()
    955         for dso_path in test_map:
    956             dso = addr2line.get_dso(dso_path)
    957             self.assertTrue(dso is not None)
    958             test_addrs = test_map[dso_path]
    959             for test_addr in test_addrs:
    960                 source_str = test_addr['source']
    961                 expected_source = []
    962                 for line in source_str.split('\n'):
    963                     items = line.split(':')
    964                     expected_source.append((items[0].strip(), int(items[1])))
    965                 actual_source = addr2line.get_addr_source(dso, test_addr['addr'])
    966                 self.assertTrue(actual_source is not None)
    967                 self.assertEqual(len(actual_source), len(expected_source))
    968                 for i in range(len(expected_source)):
    969                     actual_file_path, actual_line = actual_source[i]
    970                     self.assertEqual(actual_file_path, expected_source[i][0])
    971                     self.assertEqual(actual_line, expected_source[i][1])
    972 
    973     def test_objdump(self):
    974         binary_cache_path = 'testdata'
    975         test_map = {
    976             '/simpleperf_runtest_two_functions_arm64': {
    977                 'start_addr': 0x668,
    978                 'len': 116,
    979                 'expected_items': [
    980                     ('main():', 0),
    981                     ('system/extras/simpleperf/runtest/two_functions.cpp:20', 0),
    982                     (' 694:	add	x20, x20, #0x6de', 0x694),
    983                 ],
    984             },
    985             '/simpleperf_runtest_two_functions_arm': {
    986                 'start_addr': 0x784,
    987                 'len': 80,
    988                 'expected_items': [
    989                     ('main():', 0),
    990                     ('system/extras/simpleperf/runtest/two_functions.cpp:20', 0),
    991                     ('     7ae:	bne.n	7a6 <main+0x22>', 0x7ae),
    992                 ],
    993             },
    994             '/simpleperf_runtest_two_functions_x86_64': {
    995                 'start_addr': 0x920,
    996                 'len': 201,
    997                 'expected_items': [
    998                     ('main():', 0),
    999                     ('system/extras/simpleperf/runtest/two_functions.cpp:20', 0),
   1000                     (' 96e:	mov    %edx,(%rbx,%rax,4)', 0x96e),
   1001                 ],
   1002             },
   1003             '/simpleperf_runtest_two_functions_x86': {
   1004                 'start_addr': 0x710,
   1005                 'len': 98,
   1006                 'expected_items': [
   1007                     ('main():', 0),
   1008                     ('system/extras/simpleperf/runtest/two_functions.cpp:20', 0),
   1009                     (' 748:	cmp    $0x5f5e100,%ebp', 0x748),
   1010                 ],
   1011             },
   1012         }
   1013         objdump = Objdump(None, binary_cache_path)
   1014         for dso_path in test_map:
   1015             dso_info = test_map[dso_path]
   1016             disassemble_code = objdump.disassemble_code(dso_path, dso_info['start_addr'],
   1017                                                         dso_info['len'])
   1018             self.assertTrue(disassemble_code)
   1019             for item in dso_info['expected_items']:
   1020                 self.assertTrue(item in disassemble_code)
   1021 
   1022 
   1023 def main():
   1024     os.chdir(get_script_dir())
   1025     build_testdata()
   1026     if AdbHelper().get_android_version() < 7:
   1027         log_info("Skip tests on Android version < N.")
   1028         sys.exit(0)
   1029     unittest.main(failfast=True)
   1030 
   1031 if __name__ == '__main__':
   1032     main()
   1033