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 17from __future__ import absolute_import 18 19import os 20import re 21 22from .test_base import TestBase 23from . import util_log 24 25 26class 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