source: rtems-tools/tester/rt/gdb.py

Last change on this file was dac7ef2, checked in by Chris Johns <chrisj@…>, on 09/14/21 at 07:18:47

tester/tftp: Fix recovery of timed out TFTP sessions

  • Add support to retry the tftp session if the target has not started
  • Add target handlers for the test directives to allow recovery on error
  • Property mode set to 100644
File size: 13.0 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2013, 2020 Chris Johns (chrisj@rtems.org)
4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-tools'.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions are met:
10#
11# 1. Redistributions of source code must retain the above copyright notice,
12# this list of conditions and the following disclaimer.
13#
14# 2. Redistributions in binary form must reproduce the above copyright notice,
15# this list of conditions and the following disclaimer in the documentation
16# and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.
29#
30
31#
32# RTEMS Testing GDB Interface
33#
34
35from __future__ import print_function
36
37import datetime
38import os
39try:
40    import Queue
41    queue = Queue
42except ImportError:
43    import queue
44import sys
45import threading
46import time
47
48from rtemstoolkit import error
49from rtemstoolkit import execute
50from rtemstoolkit import options
51from rtemstoolkit import path
52
53import tester.rt.pygdb
54
55class gdb(object):
56    '''RTEMS Testing GDB base.'''
57
58    def __init__(self, bsp_arch, bsp, trace = False, mi_trace = False):
59        self.session = tester.rt.pygdb.mi_parser.session()
60        self.trace = trace
61        self.mi_trace = mi_trace
62        self.lock_trace = False
63        self.lock_locked = None
64        self.lock = threading.RLock()
65        self.script = None
66        self.script_line = 0
67        self.bsp = bsp
68        self.bsp_arch = bsp_arch
69        self.output = None
70        self.output_length = 0
71        self.gdb_console = None
72        self.input = queue.Queue()
73        self.commands = queue.Queue()
74        self.process = None
75        self.ecode = None
76        self.state = {}
77        self.running = False
78        self.breakpoints = {}
79        self.output = None
80        self.output_buffer = ''
81        self.timeout = None
82        self.test_too_long = None
83        self.lc = 0
84
85    def _lock(self, msg):
86        if self.lock_trace:
87            print('|[   LOCK:%s ]|' % (msg))
88            self.lock_locked = datetime.datetime.now()
89        self.lock.acquire()
90
91    def _unlock(self, msg):
92        if self.lock_trace:
93            period = datetime.datetime.now() - self.lock_locked
94            print('|] UNLOCK:%s [| : %s' % (msg, period))
95        self.lock.release()
96
97    def _put(self, text):
98        if self.trace:
99            print(')))', text)
100        self.commands.put(text)
101
102    def _input_commands(self):
103        if self.commands.empty():
104            return False
105        try:
106            if self.trace:
107                print('... input empty ', self.input.empty())
108            if self.input.empty():
109                line = self.commands.get(block = False)
110                if self.trace:
111                    print('+++', line)
112                self.input.put(line)
113        except:
114            pass
115        return True
116
117    def _reader(self, line):
118        self._lock('_reader')
119        if self.trace:
120            print('<<<', line)
121        try:
122            self.lc += 1
123            if line.startswith('(gdb)'):
124                if self.trace:
125                    print('^^^ (gdb)')
126                if not self._input_commands():
127                    self.gdb_expect()
128                    self._input_commands()
129            else:
130                self.gdb_parse(line)
131        finally:
132            self._unlock('_reader')
133
134    def _writer(self):
135        try:
136            try:
137                self._lock('_writer')
138                try:
139                    if self.process is None:
140                        return None
141                finally:
142                    self._unlock('_writer')
143                line = self.input.get(timeout = 0.5)
144                if self.trace:
145                    print('>>> input: queue=%d' % (self.input.qsize()), line)
146            except queue.Empty:
147                return True
148            if line is None:
149                return None
150            return line + os.linesep
151        except:
152            if self.trace:
153                print('writer exception')
154            pass
155        if self.trace:
156            print('writer closing')
157        return False
158
159    def _timeout(self):
160        self._stop()
161        if self.timeout is not None:
162            self.timeout()
163
164    def _test_too_long(self):
165        self._stop()
166        if self.test_too_long is not None:
167            self.test_too_long()
168
169    def _cleanup(self, proc):
170        self._lock('_cleanup')
171        try:
172            self._put(None)
173        finally:
174            self._unlock('_cleanup')
175
176    def _gdb_quit(self, backtrace = False):
177        self._lock('_gdb_quit')
178        try:
179            self._put('-exec-interrupt')
180            if backtrace:
181                self._put('bt')
182            self._put('quit')
183            self._put('None')
184            if self.script:
185                self.script_line = len(self.script)
186        finally:
187            self._unlock('_gdb_quit')
188
189    def _stop(self):
190        self._gdb_quit(backtrace=True)
191        seconds = 5
192        step = 0.1
193        while self.process and seconds > 0:
194            if seconds > step:
195                seconds -= step
196            else:
197                seconds = 0
198            self._unlock('_stop')
199            time.sleep(step)
200            self._lock('_stop')
201        if self.process and seconds == 0:
202            self._kill()
203
204    def _kill(self):
205        if self.process:
206            self.process.kill()
207        self.process = None
208
209    def _execute_gdb(self, args):
210        '''Thread to execute GDB and to wait for it to finish.'''
211        cmds = args
212        self.gdb_console('gdb: %s' % (' '.join(cmds)))
213        ecode, proc = self.process.open(cmds)
214        if self.trace:
215            print('gdb done', ecode)
216        self._lock('_execute_gdb')
217        self.ecode = ecode
218        self.process = None
219        self.running = False
220        self._unlock('_execute_gdb')
221
222    def _monitor(self, timeout):
223        output_length = self.output_length
224        step = 0.25
225        period = timeout[0]
226        seconds = timeout[1]
227        while self.process and period > 0 and seconds > 0:
228            current_length = self.output_length
229            if output_length != current_length:
230                period = timeout[0]
231            output_length = current_length
232            if seconds < step:
233                seconds = 0
234            else:
235                seconds -= step
236            if period < step:
237                step = period
238                period = 0
239            else:
240                period -= step
241            self._unlock('_monitor')
242            time.sleep(step)
243            self._lock('_monitor')
244        if self.process is not None:
245            if period == 0:
246                self._timeout()
247            elif seconds == 0:
248                self._test_too_long()
249
250    def open(self, command, executable,
251             output, gdb_console, timeout,
252             script = None, tty = None):
253        self._lock('_open')
254        self.timeout = timeout[2]
255        self.test_too_long = timeout[3]
256        try:
257            cmds = execute.arg_list(command) + ['-i=mi',
258                                                '--nx',
259                                                '--quiet']
260            if tty:
261                cmds += ['--tty=%s' % tty]
262            if executable:
263                cmds += [executable]
264            self.output = output
265            self.gdb_console = gdb_console
266            self.script = script
267            self.process = execute.execute(output = self._reader,
268                                           input = self._writer,
269                                           cleanup = self._cleanup)
270            exec_thread = threading.Thread(target=self._execute_gdb,
271                                           args=[cmds])
272            exec_thread.start()
273            self._monitor(timeout)
274            if self.ecode is not None and self.ecode > 0:
275                raise error.general('gdb exec: %s: %s' % (cmds[0],
276                                                          os.strerror(self.ecode)))
277        finally:
278            self._unlock('_open')
279
280    def kill(self):
281        self._lock('_kill')
282        try:
283            self._kill()
284        finally:
285            self._unlock('_kill')
286
287    def target_restart(self, started):
288        pass
289
290    def target_reset(self, started):
291        pass
292
293    def target_start(self):
294        pass
295
296    def target_end(self):
297        pass
298
299    def gdb_expect(self):
300        if self.trace:
301            print('}}} gdb-expect')
302        if self.process and not self.running and self.script is not None:
303            if self.script_line == len(self.script):
304                self._put(None)
305            else:
306                if self.script_line == 0:
307                    self._put('-gdb-set confirm no')
308                line = self.script[self.script_line]
309                self.script_line += 1
310                self._put(line)
311
312    def gdb_parse(self, lines):
313        try:
314            if self.mi_trace:
315                print('mi-data:', lines)
316            rec = self.session.process(lines)
317            if self.mi_trace:
318                print('mi-rec:', rec)
319            if rec.record_type == 'result':
320                if rec.type == 'result':
321                    if rec.class_ == 'error':
322                        self._gdb_quit()
323                    elif 'register_names' in dir(rec.result):
324                        self.register_names = rec.result.register_names
325                    elif 'register_values' in dir(rec.result):
326                        self.register_values = rec.result.register_values
327                elif rec.type == 'exec':
328                    if rec.class_ == 'running':
329                        if self.trace:
330                            print('*** running')
331                        self._put('')
332                        self.running = True
333                    elif rec.class_ == 'stopped':
334                        if self.trace:
335                            print('*** stopped')
336                        self.running = False
337                        #self._put('-data-list-register-values')
338                elif rec.type == 'breakpoint':
339                    if rec.class_ == 'breakpoint-created':
340                        self.breakpoints[rec.result.bkpt.number] = rec.result.bkpt
341                    elif rec.class_ == 'breakpoint-modified':
342                        self.breakpoints[rec.result.bkpt.number] = rec.result.bkpt
343                    elif rec.class_ == 'breakpoint-deleted':
344                        if rec.result.id in self.breakpoints:
345                            del self.breakpoints[rec.result.id]
346            elif rec.record_type == 'error':
347                self._gdb_quit()
348            elif rec.record_type == 'stream':
349                if rec.type == 'console' or rec.type == 'log':
350                    for line in rec.value.splitlines():
351                        self.gdb_console(line)
352                if rec.type == 'target':
353                    self.output_buffer += rec.value
354                    last_lf = self.output_buffer.rfind('\n')
355                    if last_lf >= 0:
356                        lines = self.output_buffer[:last_lf]
357                        if self.trace:
358                            print('/// console output: ', len(lines))
359                        for line in lines.splitlines():
360                            self.output_length += len(line)
361                            self.output(line)
362                        self.output_buffer = self.output_buffer[last_lf + 1:]
363        except:
364            if self.trace:
365                print('/// exception: console output')
366            for line in lines.splitlines():
367                self.output(line)
368
369if __name__ == "__main__":
370    import tester.rt.console
371    stdtty = tester.rt.console.save()
372    try:
373        def output(text):
374            print(']', text)
375        def gdb_console(text):
376            print('>', text)
377        script = ['target sim']
378        if len(sys.argv) > 1:
379            executable = sys.argv[1]
380            script += ['load',
381                       'run',
382                       'info reg',
383                       '-stack-list-frames',
384                       '-stack-list-arguments --all-values']
385        else:
386            executable = None
387        script += ['quit']
388        g = gdb('sparc', 'sis', mi_trace = True)
389        g.open('sparc-rtems4.11-gdb', executable, output, gdb_console, script)
390    except:
391        tester.rt.console.restore(stdtty)
392        raise
393    finally:
394        tester.rt.console.restore(stdtty)
Note: See TracBrowser for help on using the repository browser.