blob: 930783d60268dc880cef0d310f344682e5208298 [file] [log] [blame]
Mate Toth-Pala8797e12020-06-05 21:14:26 +02001#-------------------------------------------------------------------------------
2# Copyright (c) 2020, Arm Limited. All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6#-------------------------------------------------------------------------------
7
8""" This module implements the debugger control interface for the GDB debugger
9"""
10
11import traceback
12import logging
13import re
14# pylint: disable=import-error
15import gdb
16# pylint: enable=import-error
17
18from irq_test_abstract_debugger import AbstractDebugger
19from irq_test_executor import TestExecutor
20
21class Breakpoint:
22 def __init__(self, number, hit_count):
23 self.number = number
24 self.hit_count = hit_count
25
26 def __str__(self):
27 return "(#" + str(self.number) + ": hit_count=" + str(self.hit_count) + ")"
28
29class GDBDebugger(AbstractDebugger):
30 """This class is the implementation for the debugger control interface for GDB
31 """
32 def __init__(self, use_sw_breakpoints):
33 super(GDBDebugger, self).__init__()
34 self.last_breakpoint_list = []
35 self.breakpoint_names = {}
36 self.use_sw_breakpoints = use_sw_breakpoints
37 self.last_breakpoint_num = 0
38 self.execute_output = None
39
40 def parse_breakpoint_line(self, lines):
41
42 number = 0
43 hit_count = 0
44
45 # look for a first line: starts with a number
46 m = re.match(r'^\s*(\d+)\s+', lines[0])
47 if m:
48 number = int(m.group(1))
49 lines.pop(0)
50
51 else:
52 logging.error('unexpected line in "info breakpoints": %s', lines[0])
53 exit (0)
54
55 # look for additional lines
56 if lines:
57 m = re.match(r'^\s*stop only', lines[0])
58 if m:
59 lines.pop(0)
60
61 if lines:
62 m = re.match(r'^\s*breakpoint already hit (\d+) time', lines[0])
63 if m:
64 hit_count = int(m.group(1))
65 lines.pop(0)
66
67 return Breakpoint(number, hit_count)
68
69 #TODO: remove this as it is unnecessary
70 def get_list_of_breakpoints(self):
71 breakpoints_output = gdb.execute('info breakpoints', to_string=True)
72
73 if breakpoints_output.find("No breakpoints or watchpoints.") == 0:
74 return []
75
76 breakpoints = []
77
78 m = re.match(r'^Num\s+Type\s+Disp\s+Enb\s+Address\s+What', breakpoints_output)
79 if m:
80 lines = breakpoints_output.splitlines()
81 lines.pop(0) # skip the header
82
83 while lines:
84 breakpoints.append(self.parse_breakpoint_line(lines))
85
86 return breakpoints
87
88 logging.error('unexpected output from "info breakpoints"')
89 exit(0)
90
91 def set_breakpoint(self, name, location):
92 # Using the gdb.Breakpoint class available in the gdb scripting
93 # environment is not used here, as it looks like the python API has no
94 # knowledge of the Hardware breakpoints.
95 # This means that the Breakpoint.__init__() be called with
96 # gdb.BP_HARDWARE_BREAKPOINT as nothing similar exists. Also the
97 # gdb.breakpoints() function seems to be returning the list of software
98 # breakpoints only (i.e. only breakpoints created with the 'break'
99 # command are returned, while breakpoints created with 'hbreak' command
100 # are not)
101 # So instead of using the python API for manipulating breakpoints,
102 # 'gdb.execute' is used to issue 'break', 'hbreak' and
103 # 'info breakpoints' commands, and the output is parsed.
104 logging.info("Add breakpoint for location '%s'", str(location))
105
106 if logging.getLogger().isEnabledFor(logging.DEBUG):
107 logging.debug("List the breakpoints BEFORE adding")
108 #gdb.execute("info breakpoints")
109 for b in self.get_list_of_breakpoints():
110 logging.debug(" " + str(b))
111 logging.debug("End of breakpoint list")
112
113 if self.use_sw_breakpoints:
114 keyword = "break"
115 else:
116 keyword = "hbreak"
117
118 if location.symbol:
119 if (location.offset != 0):
120 argument = '*(((unsigned char*)' + location.symbol + ') + ' + location.offset + ')'
121 else:
122 argument = location.symbol
123 else:
124 argument = location.filename + ":" + str(location.line)
125
126 command = keyword + " " + argument
127 logging.debug("Setting breakpoint with command '" + command + "'")
128
129 add_breakpoint_output = gdb.execute(command, to_string=True)
130
131 print add_breakpoint_output
132 m = re.search(r'reakpoint\s+(\d+)\s+at', str(add_breakpoint_output))
133 if (m):
134 self.last_breakpoint_num = int(m.group(1))
135 else:
136 logging.error("matching breakpoint command's output failed")
137 exit(1)
138
139 if logging.getLogger().isEnabledFor(logging.DEBUG):
140 logging.debug("List the breakpoints AFTER adding")
141 #gdb.execute("info breakpoints")
142 for b in self.get_list_of_breakpoints():
143 logging.debug(" " + str(b))
144 logging.debug("End of breakpoint list")
145
146 self.breakpoint_names[self.last_breakpoint_num] = name
147
148 def __triger_interrupt_using_STIR_address(self, line):
149 logging.debug("writing to STIR address %s", hex(line))
150
151 command = "set *((unsigned int *) " + hex(0xE000EF00) + ") = " + str(line)
152 logging.debug("calling '%s'", command)
153 gdb.execute(command)
154
155 def __triger_interrupt_using_NVIC_ISPR_address(self, line):
156 # write ISPR register directly
157 register_id = line//32
158
159 # straight
160 #register_offset = line%32
161 # reversed endianness
162 register_offset = ((3-(line%32)/8)*8)+(line%8)
163
164 # TODO: remove magic numbers
165 NVIC_ISPR_address = 0xE000E200
166 NVIC_ISPR_n_address = NVIC_ISPR_address + register_id * 4
167
168 value = 1 << register_offset # 0 bits are ignored on write
169
170 logging.debug("Writing to address 0x{:08x} register 0x{:08x}".
171 format(NVIC_ISPR_n_address, value))
172
173 command = "set *((unsigned int *) " + hex(NVIC_ISPR_n_address) + ") = " + hex(value)
174 logging.debug("calling '%s'", command)
175 gdb.execute(command)
176
177 def trigger_interrupt(self, interrupt_line):
178 logging.info("triggering interrupt for line %s", str(interrupt_line))
179
180 line = int(interrupt_line)
181
182 # self.__triger_interrupt_using_NVIC_ISPR_address(line)
183 self.__triger_interrupt_using_STIR_address(line)
184
185 def continue_execution(self):
186 logging.info("Continuing execution ")
187 # save the list of breakpoints before continuing
188 # self.last_breakpoint_list = gdb.breakpoints()
189 self.execute_output = gdb.execute("continue", to_string=True)
190
191 def clear_breakpoints(self):
192 logging.info("Remove all breakpoints")
193 gdb.execute("delete breakpoint")
194
195 def get_triggered_breakpoint(self):
196 logging.info("getting the triggered breakpoints")
197
198 if not self.execute_output:
199 logging.error("Execute was not called yet.")
200 exit(1)
201
202 m = re.search(r'Breakpoint\s+(\d+)\s*,', self.execute_output)
203 if m:
204 print "self.breakpoint_names: " + str(self.breakpoint_names)
205 return self.breakpoint_names[int(m.group(1))]
206 else:
207 logging.error("Unexpected output from execution.")
208 exit(1)
209
210class TestIRQsCommand(gdb.Command):
211 """This class represents the new command to be registered in GDB
212 """
213 def __init__(self, arg_parser):
214 # This registers our class as "test_irqs"
215 super(TestIRQsCommand, self).__init__("test_irqs", gdb.COMMAND_DATA)
216 self.arg_parser = arg_parser
217
218 def print_usage(self):
219 """Print usage of the custom command
220 """
221 print self.arg_parser.print_help()
222
223 def invoke(self, arg, from_tty):
224 """This is the entry point of the command
225
226 This function is called by GDB when the command is called from the debugger
227 """
228 args = self.arg_parser.parse_args(arg.split())
229
230 debugger = GDBDebugger(args.sw_break)
231 test_executor = TestExecutor(debugger)
232
233 try:
234 test_executor.execute(args.irqs, args.breakpoints, args.testcase)
235 except:
236 pass
237
238 traceback.print_exc()