1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import os
6import pickle
7import re
8import shutil
9import tempfile
10import unittest
11
12from telemetry import benchmark
13from telemetry.core import util
14from telemetry.core.platform.profiler import android_profiling_helper
15from telemetry.unittest import simple_mock
16from telemetry.unittest import tab_test_case
17
18
19def _GetLibrariesMappedIntoProcesses(device, pids):
20  libs = set()
21  for pid in pids:
22    maps_file = '/proc/%d/maps' % pid
23    maps = device.ReadFile(maps_file, as_root=True)
24    for map_line in maps:
25      lib = re.match(r'.*\s(/.*[.]so)$', map_line)
26      if lib:
27        libs.add(lib.group(1))
28  return libs
29
30
31class TestAndroidProfilingHelper(unittest.TestCase):
32
33  def testGetRequiredLibrariesForPerfProfile(self):
34    perf_output = os.path.join(
35        util.GetUnittestDataDir(), 'sample_perf_report_output.txt')
36    with open(perf_output) as f:
37      perf_output = f.read()
38
39    mock_popen = simple_mock.MockObject()
40    mock_popen.ExpectCall('communicate').WillReturn([None, perf_output])
41
42    mock_subprocess = simple_mock.MockObject()
43    mock_subprocess.ExpectCall(
44        'Popen').WithArgs(simple_mock.DONT_CARE).WillReturn(mock_popen)
45    mock_subprocess.SetAttribute('PIPE', simple_mock.MockObject())
46
47    real_subprocess = android_profiling_helper.subprocess
48    android_profiling_helper.subprocess = mock_subprocess
49    try:
50      libs = android_profiling_helper.GetRequiredLibrariesForPerfProfile('foo')
51      self.assertEqual(libs, set([
52          '/data/app-lib/com.google.android.apps.chrome-2/libchrome.2016.0.so',
53          '/system/lib/libart.so',
54          '/system/lib/libc.so',
55          '/system/lib/libm.so']))
56    finally:
57      android_profiling_helper.subprocess = real_subprocess
58
59  @benchmark.Enabled('android')
60  def testGetRequiredLibrariesForVTuneProfile(self):
61    vtune_db_output = os.path.join(
62        util.GetUnittestDataDir(), 'sample_vtune_db_output')
63    with open(vtune_db_output, 'rb') as f:
64      vtune_db_output = pickle.load(f)
65
66    mock_cursor = simple_mock.MockObject()
67    mock_cursor.ExpectCall(
68        'execute').WithArgs(simple_mock.DONT_CARE).WillReturn(vtune_db_output)
69
70    mock_conn = simple_mock.MockObject()
71    mock_conn.ExpectCall('cursor').WillReturn(mock_cursor)
72    mock_conn.ExpectCall('close')
73
74    mock_sqlite3 = simple_mock.MockObject()
75    mock_sqlite3.ExpectCall(
76        'connect').WithArgs(simple_mock.DONT_CARE).WillReturn(mock_conn)
77
78    real_sqlite3 = android_profiling_helper.sqlite3
79    android_profiling_helper.sqlite3 = mock_sqlite3
80    try:
81      libs = android_profiling_helper.GetRequiredLibrariesForVTuneProfile('foo')
82      self.assertEqual(libs, set([
83          '/data/app-lib/com.google.android.apps.chrome-1/libchrome.2019.0.so',
84          '/system/lib/libdvm.so',
85          '/system/lib/libc.so',
86          '/system/lib/libm.so']))
87    finally:
88      android_profiling_helper.sqlite3 = real_sqlite3
89
90
91class TestAndroidProfilingHelperTabTestCase(tab_test_case.TabTestCase):
92
93  def setUp(self):
94    super(TestAndroidProfilingHelperTabTestCase, self).setUp()
95    # pylint: disable=W0212
96    browser_backend = self._browser._browser_backend
97    self._device = browser_backend._adb.device()
98
99  @benchmark.Enabled('android')
100  def testCreateSymFs(self):
101    # pylint: disable=W0212
102    browser_pid = self._browser._browser_backend.pid
103    pids = ([browser_pid] +
104        self._browser._platform_backend.GetChildPids(browser_pid))
105    libs = _GetLibrariesMappedIntoProcesses(self._device, pids)
106    assert libs
107
108    symfs_dir = tempfile.mkdtemp()
109    try:
110      kallsyms = android_profiling_helper.CreateSymFs(self._device, symfs_dir,
111                                                      libs)
112
113      # Check that we have kernel symbols.
114      assert os.path.exists(kallsyms)
115
116      is_unstripped = re.compile('^/data/app/.*\.so$')
117      has_unstripped = False
118
119      # Check that all requested libraries are present.
120      for lib in libs:
121        has_unstripped = has_unstripped or is_unstripped.match(lib)
122        assert os.path.exists(os.path.join(symfs_dir, lib[1:])), \
123            '%s not found in symfs' % lib
124
125      # Make sure we found at least one unstripped library.
126      assert has_unstripped
127    finally:
128      shutil.rmtree(symfs_dir)
129
130  @benchmark.Enabled('android')
131  def testGetToolchainBinaryPath(self):
132    with tempfile.NamedTemporaryFile() as libc:
133      self._device.PullFile('/system/lib/libc.so', libc.name)
134      path = android_profiling_helper.GetToolchainBinaryPath(libc.name,
135                                                             'objdump')
136      assert os.path.exists(path)
137