Home | History | Annotate | Download | only in harness
      1 # Copyright (C) 2016 The Android Open Source Project
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #      http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 
     15 '''Module that contains the base class TestBaseRemote'''
     16 
     17 from __future__ import absolute_import
     18 
     19 import os
     20 import re
     21 
     22 from .test_base import TestBase
     23 from . import util_log
     24 
     25 
     26 class TestBaseRemote(TestBase):
     27     '''Base class for all tests that connect to a remote device.
     28 
     29     Provides common functionality to set up the connection and tear it down.
     30     '''
     31 
     32     def __init__(self, device_port, device, timer, *args, **kwargs):
     33         super(TestBaseRemote, self).__init__(device_port, device, timer, *args, **kwargs)
     34         # port used by lldb-server on the device.
     35         self._device_port = device_port
     36         self._platform = None
     37         # id of the device that adb will communicate with.
     38         self._device = device
     39 
     40     def set_src_map(self, file_name, new_src_path):
     41         '''Call lldb to set the source mapping of a given file.
     42 
     43         Set lldb's source mapping of a given file to a given path. This can be
     44         used to make the test suite independent of where an APK was compiled.
     45 
     46         Args:
     47             file_name: String, which is the name of the file whose mapping is
     48                 to be changed
     49             new_src_path: String which is the new absolute path to the source
     50                 file.
     51         '''
     52         line_table = self.do_command('target modules dump line-table '
     53                                      + file_name)
     54 
     55         lines = line_table.split('\n')
     56         if 'Line table for' not in lines[0]:
     57             raise self.TestFail('Could not determine source path of '
     58                                 + file_name)
     59 
     60         # Expecting output like:
     61         # (lldb) target modules dump line-table scalars.rs
     62         # Line table for /home/jenkins/workspace/grd-aosp-parameterised-build/
     63         # merge_151216/frameworks/rs/tests/lldb/java/BranchingFunCalls/src/rs/
     64         # frameworks/rs/tests/lldb/java/BranchingFunCalls/src/rs/scalars.rs in
     65         # `librs.scalars.so
     66         # 0xb30f2374: /home/jenkins/workspace/grd-aosp-parameterised-build/
     67         # merge_151216/frameworks/rs/tests/lldb/java/BranchingFunCalls/src/rs/
     68         # scalars.rs:46
     69         # ...
     70         # For some reason the first line contains a mangled path?
     71         old_path = re.findall(r"[^ :]+", lines[1])[1]
     72         old_dir = os.path.dirname(old_path)
     73 
     74         self.try_command('settings set target.source-map %s %s'
     75                          % (old_dir, new_src_path), [''])
     76 
     77     def post_run(self):
     78         '''Clean up after execution.'''
     79         if self._platform:
     80             self._platform.DisconnectRemote()
     81 
     82     def _connect_to_platform(self, lldb_module, dbg, remote_pid):
     83         '''Connect to an lldb platform that has been started elsewhere.
     84 
     85         Args:
     86             lldb_module: A handle to the lldb module.
     87             dbg: The instance of the SBDebugger that should connect to the
     88                  server.
     89             remote_pid: The integer that is the process id of the binary that
     90                         the debugger should attach to.
     91 
     92         Returns:
     93             True if the debugger successfully attached to the server and
     94             process.
     95         '''
     96         # pylint: disable=too-many-return-statements
     97         remote_pid = str(remote_pid)
     98 
     99         log = util_log.get_logger()
    100 
    101         err1 = dbg.SetCurrentPlatform('remote-android')
    102         if err1.Fail():
    103             log.fatal(err1.GetCString())
    104             return False
    105 
    106         self._platform = dbg.GetSelectedPlatform()
    107         if not self._platform:
    108             return False
    109 
    110         connect_string = \
    111             'adb://{0}:{1}'.format(self._device, self._device_port)
    112         opts = lldb_module.SBPlatformConnectOptions(connect_string)
    113 
    114         for _ in range(2):
    115             err2 = self._platform.ConnectRemote(opts)
    116             if err2.Fail():
    117                 log.error(err2.GetCString())
    118 
    119                 if 'Connection refused' in err2.GetCString():
    120                     log.warning('Connection to lldb server was refused. '
    121                                 'Trying again.')
    122                 else:
    123                     # Unknown error. Don't try again.
    124                     return False
    125             else:
    126                 # Success
    127                 break
    128         else:
    129             log.fatal('Not trying again, maximum retries exceeded.')
    130             return False
    131 
    132         target = dbg.CreateTarget(None)
    133         if not target:
    134             return False
    135 
    136         dbg.SetSelectedTarget(target)
    137         listener = lldb_module.SBListener()
    138         err3 = lldb_module.SBError()
    139         process = target.AttachToProcessWithID(listener, int(remote_pid), err3)
    140         if err3.Fail() or not process:
    141             log.fatal(err3.GetCString())
    142             return False
    143 
    144         return True
    145 
    146     def run(self, dbg, remote_pid, lldb):
    147         '''Execute the actual testsuite.
    148 
    149         Args:
    150             dbg: The instance of the SBDebugger that is used to test commands.
    151             remote_pid: The integer that is the process id of the binary that
    152                         the debugger is attached to.
    153             lldb: A handle to the lldb module.
    154 
    155         Returns: list of (test, failure) tuples.
    156 
    157         '''
    158         assert dbg
    159         assert remote_pid
    160         assert lldb
    161 
    162         self._lldb = lldb
    163 
    164         self.assert_true(self._connect_to_platform(lldb, dbg, remote_pid))
    165         self._ci = dbg.GetCommandInterpreter()
    166         assert self._ci
    167 
    168         self.assert_true(self._ci.IsValid())
    169         self.assert_true(self._ci.HasCommands())
    170 
    171         return super(TestBaseRemote, self).run(dbg, remote_pid, lldb)
    172 
    173