diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/app_test.py | 13 | ||||
-rwxr-xr-x | test/do_test.py | 3 | ||||
-rw-r--r-- | test/pprof_proto_generator_test.py | 7 | ||||
-rw-r--r-- | test/report_html_test.py | 15 | ||||
-rw-r--r-- | test/report_lib_test.py | 65 | ||||
-rw-r--r-- | test/sample_filter_test.py | 42 | ||||
-rw-r--r-- | test/testdata/display_bitmaps.proto_data | bin | 0 -> 385461 bytes | |||
-rw-r--r-- | test/testdata/etm/etm_test_loop_small | bin | 0 -> 4008 bytes | |||
-rw-r--r-- | test/testdata/etm/perf_for_small_binary.data | bin | 0 -> 22680 bytes | |||
-rw-r--r-- | test/testdata/etm/perf_inject_small.data | 32 | ||||
-rw-r--r-- | test/testdata/lbr/inject_lbr.data | 14 | ||||
-rw-r--r-- | test/testdata/lbr/perf_lbr.data | bin | 0 -> 23855 bytes | |||
-rw-r--r-- | test/testdata/perf_test_vmlinux.data | bin | 0 -> 1529 bytes | |||
-rw-r--r-- | test/testdata/perf_with_interpreter_frames.gecko.json | 1 | ||||
-rw-r--r-- | test/testdata/perf_with_jit_symbol.gecko.json | 1 | ||||
-rw-r--r-- | test/testdata/perf_with_tracepoint_event.gecko.json | 1 | ||||
-rw-r--r-- | test/testdata/vmlinux | bin | 0 -> 2331 bytes | |||
-rw-r--r-- | test/tools_test.py | 67 |
18 files changed, 255 insertions, 6 deletions
diff --git a/test/app_test.py b/test/app_test.py index a13200a..2c6835d 100644 --- a/test/app_test.py +++ b/test/app_test.py @@ -23,6 +23,7 @@ import subprocess import time from typing import List, Tuple +from simpleperf_report_lib import ReportLib from simpleperf_utils import remove from . test_utils import TestBase, TestHelper, AdbHelper, INFERNO_SCRIPT @@ -268,7 +269,13 @@ class TestRecordingRealApps(TestBase): def test_recording_endless_tunnel(self): self.install_apk(TestHelper.testdata_path( 'EndlessTunnel.apk'), 'com.google.sample.tunnel') - self.start_app('shell am start -n com.google.sample.tunnel/android.app.NativeActivity -a ' + - 'android.intent.action.MAIN -c android.intent.category.LAUNCHER') - self.record_data('com.google.sample.tunnel', '-e cpu-clock -g --duration 10') + # Test using --launch to start the app. + self.run_cmd(['app_profiler.py', '--app', 'com.google.sample.tunnel', + '--launch', '-r', '-e cpu-clock -g --duration 10']) self.check_symbol_in_record_file('PlayScene::DoFrame') + + # Check app versioncode. + report = ReportLib() + meta_info = report.MetaInfo() + self.assertEqual(meta_info.get('app_versioncode'), '1') + report.Close() diff --git a/test/do_test.py b/test/do_test.py index 012dc62..d341c20 100755 --- a/test/do_test.py +++ b/test/do_test.py @@ -59,6 +59,7 @@ from . report_html_test import * from . report_lib_test import * from . report_sample_test import * from . run_simpleperf_on_device_test import * +from . sample_filter_test import * from . stackcollapse_test import * from . tools_test import * from . test_utils import TestHelper @@ -130,10 +131,12 @@ def get_test_type(test: str) -> Optional[str]: 'TestDebugUnwindReporter', 'TestInferno', 'TestPprofProtoGenerator', + 'TestProtoFileReportLib', 'TestPurgatorio', 'TestReportHtml', 'TestReportLib', 'TestReportSample', + 'TestSampleFilter', 'TestStackCollapse', 'TestTools', 'TestGeckoProfileGenerator'): diff --git a/test/pprof_proto_generator_test.py b/test/pprof_proto_generator_test.py index 297cf14..b8db48a 100644 --- a/test/pprof_proto_generator_test.py +++ b/test/pprof_proto_generator_test.py @@ -96,6 +96,10 @@ class TestPprofProtoGenerator(TestBase): """ Test the build ids generated are not padded with zeros. """ self.assertIn('build_id: e3e938cc9e40de2cfe1a5ac7595897de(', self.run_generator()) + def test_time_nanos(self): + """ Test the timestamp is adjusted to be nanoseconds. """ + self.assertIn('time_nanos: 1516268753000000000\n', self.run_generator()) + def test_build_id_with_binary_cache(self): """ Test the build ids for elf files in binary_cache are not padded with zero. """ # Test with binary_cache. @@ -291,3 +295,6 @@ class TestPprofProtoGenerator(TestBase): self.assertIn(31881, threads) self.assertNotIn(31850, threads) os.unlink(filter_file.name) + + def test_report_sample_proto_file(self): + self.run_generator('', testdata_file='display_bitmaps.proto_data') diff --git a/test/report_html_test.py b/test/report_html_test.py index 7241a5e..32e5821 100644 --- a/test/report_html_test.py +++ b/test/report_html_test.py @@ -276,3 +276,18 @@ class TestReportHtml(TestBase): self.assertEqual(thread_names['AsyncTask.*'], 19) self.assertNotIn('AsyncTask #3', thread_names) self.assertNotIn('AsyncTask #4', thread_names) + + def test_sort_call_graph_by_function_name(self): + record_data = self.get_record_data( + ['-i', TestHelper.testdata_path('perf_display_bitmaps.data'), + '--aggregate-threads', '.*']) + + def get_func_name(func_id: int) -> str: + return record_data['functionMap'][str(func_id)]['f'] + + # Test if the top functions are sorted by function names. + thread = record_data['sampleInfo'][0]['processes'][0]['threads'][0] + top_functions = [get_func_name(c['f']) for c in thread['g']['c']] + self.assertIn('__libc_init', top_functions) + self.assertIn('__start_thread', top_functions) + self.assertEqual(top_functions, sorted(top_functions)) diff --git a/test/report_lib_test.py b/test/report_lib_test.py index 8e212a2..1285448 100644 --- a/test/report_lib_test.py +++ b/test/report_lib_test.py @@ -15,10 +15,13 @@ # limitations under the License. import os +from pathlib import Path +import shutil import tempfile from typing import Dict, List, Optional, Set -from simpleperf_report_lib import ReportLib +from simpleperf_report_lib import ReportLib, ProtoFileReportLib +from simpleperf_utils import ReadElf from . test_utils import TestBase, TestHelper @@ -312,6 +315,25 @@ class TestReportLib(TestBase): self.assertNotIn(31850, threads) os.unlink(filter_file.name) + def test_set_sample_filter_for_cpu(self): + """ Test --cpu in ReportLib.SetSampleFilter(). """ + def get_cpus_for_filter(filters: List[str]) -> Set[int]: + self.report_lib.Close() + self.report_lib = ReportLib() + self.report_lib.SetRecordFile(TestHelper.testdata_path('perf_display_bitmaps.data')) + self.report_lib.SetSampleFilter(filters) + cpus = set() + while self.report_lib.GetNextSample(): + sample = self.report_lib.GetCurrentSample() + cpus.add(sample.cpu) + return cpus + + cpus = get_cpus_for_filter(['--cpu', '0,1-2']) + self.assertIn(0, cpus) + self.assertIn(1, cpus) + self.assertIn(2, cpus) + self.assertNotIn(3, cpus) + def test_aggregate_threads(self): """ Test using ReportLib.AggregateThreads(). """ def get_thread_names(aggregate_regex_list: Optional[List[str]]) -> Dict[str, int]: @@ -332,3 +354,44 @@ class TestReportLib(TestBase): self.assertEqual(thread_names['AsyncTask.*'], 19) self.assertNotIn('AsyncTask #3', thread_names) self.assertNotIn('AsyncTask #4', thread_names) + + def test_use_vmlinux(self): + """ Test if we can use vmlinux in symfs_dir. """ + record_file = TestHelper.testdata_path('perf_test_vmlinux.data') + # Create a symfs_dir. + symfs_dir = Path('symfs_dir') + symfs_dir.mkdir() + shutil.copy(TestHelper.testdata_path('vmlinux'), symfs_dir) + kernel_build_id = ReadElf(TestHelper.ndk_path).get_build_id(symfs_dir / 'vmlinux') + (symfs_dir / 'build_id_list').write_text('%s=vmlinux' % kernel_build_id) + + # Check if vmlinux in symfs_dir is used, when we set record file before setting symfs_dir. + self.report_lib.SetRecordFile(record_file) + self.report_lib.SetSymfs(str(symfs_dir)) + sample = self.report_lib.GetNextSample() + self.assertIsNotNone(sample) + symbol = self.report_lib.GetSymbolOfCurrentSample() + self.assertEqual(symbol.dso_name, "[kernel.kallsyms]") + # vaddr_in_file and symbol_addr are adjusted after using vmlinux. + self.assertEqual(symbol.vaddr_in_file, 0xffffffc008fb3e28) + self.assertEqual(symbol.symbol_name, "_raw_spin_unlock_irq") + self.assertEqual(symbol.symbol_addr, 0xffffffc008fb3e0c) + self.assertEqual(symbol.symbol_len, 0x4c) + + +class TestProtoFileReportLib(TestBase): + def test_smoke(self): + report_lib = ProtoFileReportLib() + report_lib.SetRecordFile(TestHelper.testdata_path('display_bitmaps.proto_data')) + sample_count = 0 + while True: + sample = report_lib.GetNextSample() + if sample is None: + report_lib.Close() + break + sample_count += 1 + event = report_lib.GetEventOfCurrentSample() + self.assertEqual(event.name, 'cpu-clock') + report_lib.GetSymbolOfCurrentSample() + report_lib.GetCallChainOfCurrentSample() + self.assertEqual(sample_count, 525) diff --git a/test/sample_filter_test.py b/test/sample_filter_test.py new file mode 100644 index 0000000..46d51d0 --- /dev/null +++ b/test/sample_filter_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import os +from pathlib import Path +import re +import tempfile +from typing import List, Optional, Set + +from . test_utils import TestBase, TestHelper + + +class TestSampleFilter(TestBase): + def test_show_time_range(self): + testdata_file = TestHelper.testdata_path('perf_display_bitmaps.data') + output = self.run_cmd(['sample_filter.py', '-i', testdata_file, + '--show-time-range'], return_output=True) + self.assertIn('0.134 s', output) + + def test_split_time_range(self): + testdata_file = TestHelper.testdata_path('perf_display_bitmaps.data') + self.run_cmd(['sample_filter.py', '-i', testdata_file, '--split-time-range', '2']) + part1_data = Path('sample_filter_part1').read_text() + self.assertIn('GLOBAL_BEGIN 684943449406175', part1_data) + self.assertIn('GLOBAL_END 684943516618526', part1_data) + part2_data = Path('sample_filter_part2').read_text() + self.assertIn('GLOBAL_BEGIN 684943516618526', part2_data) + self.assertIn('GLOBAL_END 684943583830876', part2_data) diff --git a/test/testdata/display_bitmaps.proto_data b/test/testdata/display_bitmaps.proto_data Binary files differnew file mode 100644 index 0000000..6b8b269 --- /dev/null +++ b/test/testdata/display_bitmaps.proto_data diff --git a/test/testdata/etm/etm_test_loop_small b/test/testdata/etm/etm_test_loop_small Binary files differnew file mode 100644 index 0000000..600bf9e --- /dev/null +++ b/test/testdata/etm/etm_test_loop_small diff --git a/test/testdata/etm/perf_for_small_binary.data b/test/testdata/etm/perf_for_small_binary.data Binary files differnew file mode 100644 index 0000000..9b012e5 --- /dev/null +++ b/test/testdata/etm/perf_for_small_binary.data diff --git a/test/testdata/etm/perf_inject_small.data b/test/testdata/etm/perf_inject_small.data new file mode 100644 index 0000000..560b492 --- /dev/null +++ b/test/testdata/etm/perf_inject_small.data @@ -0,0 +1,32 @@ +14 +14b4-14c4:1 +14c8-14fc:1 +150c-151c:1 +156c-1580:1 +1584-158c:1 +1590-15a4:10 +15a8-15b0:10 +15b4-15c4:2 +15c8-15dc:200 +15e0-15e0:2 +15e4-15f4:8 +15f8-160c:8000 +1610-1610:8 +1640-164c:1 +0 +12 +14c4->14c8:1 +14fc->150c:1 +151c->1640:1 +1580->15a8:1 +158c->0:1 +15a4->1584:1 +15b0->15e4:8 +15dc->15c8:198 +15e0->1590:2 +160c->15f8:7992 +1610->1590:8 +164c->0:1 +// build_id: 0xb9988f5e72de4b2580f98bef0ae8e4a000000000 +// /data/local/tmp/etm/etm_test_loop_small + diff --git a/test/testdata/lbr/inject_lbr.data b/test/testdata/lbr/inject_lbr.data new file mode 100644 index 0000000..6f14164 --- /dev/null +++ b/test/testdata/lbr/inject_lbr.data @@ -0,0 +1,14 @@ +2 +1910-191b:31 +1940-194d:341 +4 +1914:1 +1940:3 +1944:4 +1948:11 +2 +191b->1910:32 +194d->1940:353 +// build_id: 0x0000000000000000000000000000000000000000 +// /home/yabinc/lbr_test_loop + diff --git a/test/testdata/lbr/perf_lbr.data b/test/testdata/lbr/perf_lbr.data Binary files differnew file mode 100644 index 0000000..81fafe6 --- /dev/null +++ b/test/testdata/lbr/perf_lbr.data diff --git a/test/testdata/perf_test_vmlinux.data b/test/testdata/perf_test_vmlinux.data Binary files differnew file mode 100644 index 0000000..08de199 --- /dev/null +++ b/test/testdata/perf_test_vmlinux.data diff --git a/test/testdata/perf_with_interpreter_frames.gecko.json b/test/testdata/perf_with_interpreter_frames.gecko.json index 9959265..264dc44 100644 --- a/test/testdata/perf_with_interpreter_frames.gecko.json +++ b/test/testdata/perf_with_interpreter_frames.gecko.json @@ -2,6 +2,7 @@ "libs": [], "meta": { "abi": "aarch64", + "appBuildID": null, "asyncstack": 1, "categories": [ { diff --git a/test/testdata/perf_with_jit_symbol.gecko.json b/test/testdata/perf_with_jit_symbol.gecko.json index bb8940a..3988fef 100644 --- a/test/testdata/perf_with_jit_symbol.gecko.json +++ b/test/testdata/perf_with_jit_symbol.gecko.json @@ -2,6 +2,7 @@ "libs": [], "meta": { "abi": "aarch64", + "appBuildID": null, "asyncstack": 1, "categories": [ { diff --git a/test/testdata/perf_with_tracepoint_event.gecko.json b/test/testdata/perf_with_tracepoint_event.gecko.json index 2f5a926..2072b53 100644 --- a/test/testdata/perf_with_tracepoint_event.gecko.json +++ b/test/testdata/perf_with_tracepoint_event.gecko.json @@ -2,6 +2,7 @@ "libs": [], "meta": { "abi": "aarch64", + "appBuildID": null, "asyncstack": 1, "categories": [ { diff --git a/test/testdata/vmlinux b/test/testdata/vmlinux Binary files differnew file mode 100644 index 0000000..1239e5e --- /dev/null +++ b/test/testdata/vmlinux diff --git a/test/tools_test.py b/test/tools_test.py index 5cdcbe0..2d57230 100644 --- a/test/tools_test.py +++ b/test/tools_test.py @@ -14,12 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +import io import os from pathlib import Path from binary_cache_builder import BinaryCacheBuilder -from simpleperf_utils import (Addr2Nearestline, AddrRange, BinaryFinder, Disassembly, Objdump, ReadElf, - SourceFileSearcher, is_windows, remove) +from simpleperf_utils import (Addr2Nearestline, AddrRange, BinaryFinder, Disassembly, Objdump, + ReadElf, SourceFileSearcher, is_windows, remove) from . test_utils import TestBase, TestHelper @@ -271,6 +272,68 @@ system/extras/simpleperf/runtest/two_functions.cpp:21:3 self.fail('for %s, %s:0x%x not found in disassemble code:\n%s' % (dso_path, expected_line, expected_addr, s)) + def test_objdump_parse_disassembly_for_functions(self): + # Parse kernel disassembly. + s = """ +ffffffc008000000 <_text>: +; _text(): +; arch/arm64/kernel/head.S:60 +ffffffc008000000: ccmp x18, #0x0, #0xd, pl +ffffffc008000004: b 0xffffffc009b2a37c <primary_entry> + +ffffffc008000008 <$d.1>: +ffffffc008000008: 00 00 00 00 .word 0x00000000 +ffffffc0089bbb30 <readl>: +; readl(): +; include/asm-generic/io.h:218 +ffffffc0089bbb30: paciasp +ffffffc0089bbb34: stp x29, x30, [sp, #-0x30]! + """ + addr_ranges = [AddrRange(0xffffffc008000000, 8), + AddrRange(0xffffffc008000010, 10), + AddrRange(0xffffffc0089bbb30, 20)] + binary_finder = BinaryFinder(TestHelper.testdata_dir, ReadElf(TestHelper.ndk_path)) + objdump = Objdump(TestHelper.ndk_path, binary_finder) + result = objdump._parse_disassembly_for_functions(io.StringIO(s), addr_ranges) + self.assertEqual(len(result), 3) + self.assertEqual( + result[0].lines, + [('ffffffc008000000 <_text>:', 0xffffffc008000000), + ('; _text():', 0), + ('; arch/arm64/kernel/head.S:60', 0), + ('ffffffc008000000: ccmp x18, #0x0, #0xd, pl', 0xffffffc008000000), + ('ffffffc008000004: b 0xffffffc009b2a37c <primary_entry>', + 0xffffffc008000004), + ('', 0)]) + self.assertEqual(len(result[1].lines), 0) + self.assertEqual(result[2].lines, [ + ('ffffffc0089bbb30 <readl>:', 0xffffffc0089bbb30), + ('; readl():', 0), + ('; include/asm-generic/io.h:218', 0), + ('ffffffc0089bbb30: paciasp', 0xffffffc0089bbb30), + ('ffffffc0089bbb34: stp x29, x30, [sp, #-0x30]!', 0xffffffc0089bbb34), + ('', 0)]) + + # Parse user space library disassembly. + s = """ +0000000000200000 <art::gc::collector::ConcurrentCopying::ProcessMarkStack()>: +; art::gc::collector::ConcurrentCopying::ProcessMarkStack(): +; art/runtime/gc/collector/concurrent_copying.cc:2121 + 200000: stp x29, x30, [sp, #-0x20]! + 200004: stp x20, x19, [sp, #0x10] + """ + addr_ranges = [AddrRange(0x200000, 8)] + result = objdump._parse_disassembly_for_functions(io.StringIO(s), addr_ranges) + self.assertEqual(len(result), 1) + self.assertEqual(result[0].lines, [ + ('0000000000200000 <art::gc::collector::ConcurrentCopying::ProcessMarkStack()>:', + 0x200000), + ('; art::gc::collector::ConcurrentCopying::ProcessMarkStack():', 0), + ('; art/runtime/gc/collector/concurrent_copying.cc:2121', 0), + (' 200000: stp x29, x30, [sp, #-0x20]!', 0x200000), + (' 200004: stp x20, x19, [sp, #0x10]', 0x200004), + ('', 0)]) + def test_readelf(self): test_map = { 'simpleperf_runtest_two_functions_arm64': { |