| # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import re |
| import sys |
| import xml.etree.ElementTree |
| |
| import gdb_rsp |
| |
| |
| def AssertRaises(exc_class, func): |
| try: |
| func() |
| except exc_class: |
| pass |
| else: |
| raise AssertionError('Function did not raise %r' % exc_class) |
| |
| |
| def GetTargetArch(connection): |
| """Get the CPU architecture of the NaCl application.""" |
| reply = connection.RspRequest('qXfer:features:read:target.xml:0,fff') |
| assert reply[0] == 'l', reply |
| tree = xml.etree.ElementTree.fromstring(reply[1:]) |
| arch_tag = tree.find('architecture') |
| assert arch_tag is not None, reply |
| return arch_tag.text.strip() |
| |
| |
| def ReverseBytes(byte_string): |
| """Reverse bytes in the hex string: '09ab' -> 'ab09'. This converts |
| little-endian number in the hex string to its normal string representation. |
| """ |
| assert len(byte_string) % 2 == 0, byte_string |
| return ''.join([byte_string[i - 2 : i] |
| for i in xrange(len(byte_string), 0, -2)]) |
| |
| |
| def GetProgCtrString(connection, arch): |
| """Get current execution point.""" |
| registers = connection.RspRequest('g') |
| # PC register indices can be found in |
| # native_client/src/trusted/debug_stub/abi.cc in AbiInit function. |
| if arch == 'i386': |
| # eip index is 8 |
| return ReverseBytes(registers[8 * 8 : 8 * 8 + 8]) |
| if arch == 'i386:x86-64': |
| # rip index is 16 |
| return ReverseBytes(registers[16 * 16 : 16 * 16 + 8]) |
| if arch == 'iwmmxt': |
| # pc index is 15 |
| return ReverseBytes(registers[15 * 8 : 15 * 8 + 8]) |
| raise AssertionError('Unknown architecture: %s' % arch) |
| |
| |
| def TestContinue(connection): |
| # Once the NaCl test module reports that the test passed, the NaCl <embed> |
| # element is removed from the page. The NaCl module will be killed by the |
| # browser which will appear as EOF (end-of-file) on the debug stub socket. |
| AssertRaises(gdb_rsp.EofOnReplyException, |
| lambda: connection.RspRequest('vCont;c')) |
| |
| |
| def TestBreakpoint(connection): |
| # Breakpoints and single-stepping might interfere with Chrome sandbox. So we |
| # check that they work properly in this test. |
| arch = GetTargetArch(connection) |
| registers = connection.RspRequest('g') |
| pc = GetProgCtrString(connection, arch) |
| # Set breakpoint |
| result = connection.RspRequest('Z0,%s,1' % pc) |
| assert result == 'OK', result |
| # Check that we stopped at breakpoint |
| result = connection.RspRequest('vCont;c') |
| stop_reply = re.compile(r'T05thread:(\d+);') |
| assert stop_reply.match(result), result |
| thread = stop_reply.match(result).group(1) |
| # Check that registers haven't changed |
| result = connection.RspRequest('g') |
| assert result == registers, (result, registers) |
| # Remove breakpoint |
| result = connection.RspRequest('z0,%s,1' % pc) |
| assert result == 'OK', result |
| # Check single stepping |
| result = connection.RspRequest('vCont;s:%s' % thread) |
| assert result == 'T05thread:%s;' % thread, result |
| assert pc != GetProgCtrString(connection, arch) |
| # Check that we terminate normally |
| AssertRaises(gdb_rsp.EofOnReplyException, |
| lambda: connection.RspRequest('vCont;c')) |
| |
| |
| def Main(args): |
| port = int(args[0]) |
| name = args[1] |
| connection = gdb_rsp.GdbRspConnection(('localhost', port)) |
| if name == 'continue': |
| TestContinue(connection) |
| elif name == 'breakpoint': |
| TestBreakpoint(connection) |
| else: |
| raise AssertionError('Unknown test name: %r' % name) |
| |
| |
| if __name__ == '__main__': |
| Main(sys.argv[1:]) |