~ubuntu-branches/ubuntu/natty/python2.6/natty-security

« back to all changes in this revision

Viewing changes to debian/patches/issue8032.dpatch

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2010-03-31 18:52:32 UTC
  • Revision ID: james.westby@ubuntu.com-20100331185232-lqvp3f7b7zj9vkns
Tags: 2.6.5-1ubuntu3
* debian/patches/issue8140.dpatch: Incomplete patch; regenerate.
* debian/patches/issue8032.dpatch: Update to v4:
  - Add support for PySetObject (set/frozenset).
  - Add support for PyBaseExceptionObject (BaseException).
  - Fix a signed vs unsigned char issue that led to exceptions
    in gdb for PyStringObject instances.
  - Handle the case of loops in the object reference graph.
  - Unit tests for all of the above.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#! /bin/sh -e
2
2
 
3
 
# DP: Apply proposed patch for issue #8032, gdb7 hooks for debugging.
 
3
# DP: Apply proposed patch for issue #8032, gdb7 hooks for debugging (v4).
4
4
 
5
5
dir=
6
6
if [ $# -eq 3 -a "$2" = '-d' ]; then
25
25
 
26
26
Index: Misc/ACKS
27
27
===================================================================
28
 
--- Misc/ACKS   (revision 78520)
 
28
--- Misc/ACKS   (revision 79422)
29
29
+++ Misc/ACKS   (working copy)
30
 
@@ -480,6 +480,7 @@
 
30
@@ -481,6 +481,7 @@
31
31
 Nick Maclaren
32
32
 Steve Majewski
33
33
 Grzegorz Makarewicz
39
39
===================================================================
40
40
--- Tools/gdb/libpython.py      (revision 0)
41
41
+++ Tools/gdb/libpython.py      (revision 0)
42
 
@@ -0,0 +1,623 @@
 
42
@@ -0,0 +1,941 @@
43
43
+#!/usr/bin/python
44
 
+# -*- coding: utf-8 -*-
45
 
+#
46
 
+# Copyright © 2010 David Hugh Malcolm
47
 
+#
48
 
+# This software is licensed to you under the same terms as Python itself
49
 
+#
50
 
+# Author: Dave Malcolm <dmalcolm@redhat.com>
51
44
+'''
52
45
+From gdb 7 onwards, gdb's build can be configured --with-python, allowing gdb
53
46
+to be extended with Python code e.g. for library-specific data visualizations,
54
 
+such as for the C++ STL types.
 
47
+such as for the C++ STL types.  Documentation on this API can be seen at:
 
48
+http://sourceware.org/gdb/current/onlinedocs/gdb/Python-API.html
 
49
+
55
50
+
56
51
+This python module deals with the case when the process being debugged (the
57
52
+"inferior process" in gdb parlance) is itself python, or more specifically,
70
65
+generate a proxy value within the gdb process that is a list of strings:
71
66
+  ["foo", "bar", "baz"]
72
67
+
73
 
+We try to defer all gdb.lookup_type() invocations until as late as possible:
74
 
+when the /usr/bin/python process starts in the debugger, the libpython.so
75
 
+hasn't been dynamically loaded yet, so none of the type names are known to
76
 
+the debugger
 
68
+We try to defer gdb.lookup_type() invocations for python types until as late as
 
69
+possible: for a dynamically linked python binary, when the process starts in
 
70
+the debugger, the libpython.so hasn't been dynamically loaded yet, so none of
 
71
+the type names are known to the debugger
77
72
+
78
 
+Tested with both libpython2.6 and libpython3.1
 
73
+The module also extends gdb with some python-specific commands.
79
74
+'''
80
75
+
81
76
+import gdb
82
77
+
83
78
+# Look up the gdb.Type for some standard types:
84
79
+_type_char_ptr = gdb.lookup_type('char').pointer() # char*
 
80
+_type_unsigned_char_ptr = gdb.lookup_type('unsigned char').pointer() # unsigned char*
85
81
+_type_void_ptr = gdb.lookup_type('void').pointer() # void*
86
82
+_type_size_t = gdb.lookup_type('size_t')
87
83
+
187
183
+            # Can't even read the object at all?
188
184
+            return 'unknown'
189
185
+
190
 
+    def proxyval(self):
 
186
+    def proxyval(self, visited):
191
187
+        '''
192
188
+        Scrape a value from the inferior process, and try to represent it
193
189
+        within the gdb process, whilst (hopefully) avoiding crashes when
197
193
+
198
194
+        For example, a PyIntObject* with ob_ival 42 in the inferior process
199
195
+        should result in an int(42) in this process.
 
196
+
 
197
+        visited: a set of all gdb.Value pyobject pointers already visited
 
198
+        whilst generating this value (to guard against infinite recursion when
 
199
+        visiting object graphs with loops).  Analogous to Py_ReprEnter and
 
200
+        Py_ReprLeave
200
201
+        '''
201
202
+
202
203
+        class FakeRepr(object):
211
212
+                self.address = address
212
213
+
213
214
+            def __repr__(self):
 
215
+                # For the NULL pointer, we have no way of knowing a type, so
 
216
+                # special-case it as per
 
217
+                # http://bugs.python.org/issue8032#msg100882
 
218
+                if self.address == 0:
 
219
+                    return '0x0'
214
220
+                return '<%s at remote 0x%x>' % (self.tp_name, self.address)
215
221
+
216
222
+        return FakeRepr(self.safe_tp_name(),
251
257
+                    'instance': PyInstanceObjectPtr,
252
258
+                    'NoneType': PyNoneStructPtr,
253
259
+                    'frame': PyFrameObjectPtr,
 
260
+                    'set' : PySetObjectPtr,
 
261
+                    'frozenset' : PySetObjectPtr,
254
262
+                    }
255
263
+        if tp_name in name_map:
256
264
+            return name_map[tp_name]
272
280
+            return PyUnicodeObjectPtr
273
281
+        if tp_flags & Py_TPFLAGS_DICT_SUBCLASS:
274
282
+            return PyDictObjectPtr
275
 
+        #if tp_flags & Py_TPFLAGS_BASE_EXC_SUBCLASS:
276
 
+        #    return something
 
283
+        if tp_flags & Py_TPFLAGS_BASE_EXC_SUBCLASS:
 
284
+            return PyBaseExceptionObjectPtr
277
285
+        #if tp_flags & Py_TPFLAGS_TYPE_SUBCLASS:
278
286
+        #    return PyTypeObjectPtr
279
287
+
300
308
+    def get_gdb_type(cls):
301
309
+        return gdb.lookup_type(cls._typename).pointer()
302
310
+
 
311
+    def as_address(self):
 
312
+        return long(self._gdbval)
 
313
+
 
314
+
 
315
+class ProxyAlreadyVisited(object):
 
316
+    '''
 
317
+    Placeholder proxy to use when protecting against infinite recursion due to
 
318
+    loops in the object graph.
 
319
+
 
320
+    Analogous to the values emitted by the users of Py_ReprEnter and Py_ReprLeave
 
321
+    '''
 
322
+    def __init__(self, rep):
 
323
+        self._rep = rep
 
324
+    
 
325
+    def __repr__(self):
 
326
+        return self._rep
303
327
+
304
328
+class InstanceProxy(object):
305
329
+
309
333
+        self.address = address
310
334
+
311
335
+    def __repr__(self):
312
 
+        kwargs = ', '.join(["%s=%r" % (arg, val)
313
 
+                            for arg, val in self.attrdict.iteritems()])
314
 
+        return '<%s(%s) at remote 0x%x>' % (self.cl_name,
315
 
+                                            kwargs, self.address)
 
336
+        if isinstance(self.attrdict, dict):
 
337
+            kwargs = ', '.join(["%s=%r" % (arg, val)
 
338
+                                for arg, val in self.attrdict.iteritems()])
 
339
+            return '<%s(%s) at remote 0x%x>' % (self.cl_name,
 
340
+                                                kwargs, self.address)
 
341
+        else:
 
342
+            return '<%s at remote 0x%x>' % (self.cl_name,
 
343
+                                            self.address)
316
344
+        
317
345
+
318
346
+def _PyObject_VAR_SIZE(typeobj, nitems):
325
353
+class HeapTypeObjectPtr(PyObjectPtr):
326
354
+    _typename = 'PyObject'
327
355
+
328
 
+    def proxyval(self):
 
356
+    def proxyval(self, visited):
329
357
+        '''
330
358
+        Support for new-style classes.
331
359
+
332
 
+        Currently we just locate the dictionary using _PyObject_GetDictPtr,
333
 
+        ignoring descriptors
 
360
+        Currently we just locate the dictionary using a transliteration to
 
361
+        python of _PyObject_GetDictPtr, ignoring descriptors
334
362
+        '''
 
363
+        # Guard against infinite loops:
 
364
+        if self.as_address() in visited:
 
365
+            return ProxyAlreadyVisited('<...>')
 
366
+        visited.add(self.as_address())
 
367
+
335
368
+        attr_dict = {}
336
 
+
337
369
+        try:
338
370
+            typeobj = self.type()
339
371
+            dictoffset = int_from_int(typeobj.field('tp_dictoffset'))
351
383
+                dictptr = self._gdbval.cast(_type_char_ptr) + dictoffset
352
384
+                PyObjectPtrPtr = PyObjectPtr.get_gdb_type().pointer()
353
385
+                dictptr = dictptr.cast(PyObjectPtrPtr)
354
 
+                attr_dict = PyObjectPtr.from_pyobject_ptr(dictptr.dereference()).proxyval()
 
386
+                attr_dict = PyObjectPtr.from_pyobject_ptr(dictptr.dereference()).proxyval(visited)
355
387
+        except RuntimeError:
356
388
+            # Corrupt data somewhere; fail safe
357
 
+            pass    
 
389
+            pass
358
390
+
359
391
+        tp_name = self.safe_tp_name()
360
392
+
361
393
+        # New-style class:
362
394
+        return InstanceProxy(tp_name, attr_dict, long(self._gdbval))
363
395
+
 
396
+class ProxyException(Exception):
 
397
+    def __init__(self, tp_name, args):
 
398
+        self.tp_name = tp_name
 
399
+        self.args = args
 
400
+
 
401
+    def __repr__(self):
 
402
+        return '%s%r' % (self.tp_name, self.args)
 
403
+
 
404
+class PyBaseExceptionObjectPtr(PyObjectPtr):
 
405
+    """
 
406
+    Class wrapping a gdb.Value that's a PyBaseExceptionObject* i.e. an exception
 
407
+    within the process being debugged.
 
408
+    """
 
409
+    _typename = 'PyBaseExceptionObject'
 
410
+
 
411
+    def proxyval(self, visited):
 
412
+        # Guard against infinite loops:
 
413
+        if self.as_address() in visited:
 
414
+            return ProxyAlreadyVisited('(...)')
 
415
+        visited.add(self.as_address())
 
416
+        arg_proxy = PyObjectPtr.from_pyobject_ptr(self.field('args')).proxyval(visited)
 
417
+        return ProxyException(self.safe_tp_name(),
 
418
+                              arg_proxy)
364
419
+
365
420
+class PyBoolObjectPtr(PyObjectPtr):
366
421
+    """
369
424
+    """
370
425
+    _typename = 'PyBoolObject'
371
426
+
372
 
+    def proxyval(self):
 
427
+    def proxyval(self, visited):
373
428
+        if int_from_int(self.field('ob_ival')):
374
429
+            return True
375
430
+        else:
391
446
+    """
392
447
+    _typename = 'PyCodeObject'
393
448
+
 
449
+    def addr2line(self, addrq):
 
450
+        '''
 
451
+        Get the line number for a given bytecode offset
 
452
+
 
453
+        Analogous to PyCode_Addr2Line; translated from pseudocode in
 
454
+        Objects/lnotab_notes.txt
 
455
+        '''
 
456
+        co_lnotab = PyObjectPtr.from_pyobject_ptr(self.field('co_lnotab')).proxyval(set())
 
457
+
 
458
+        # Initialize lineno to co_firstlineno as per PyCode_Addr2Line
 
459
+        # not 0, as lnotab_notes.txt has it:
 
460
+       lineno = int_from_int(self.field('co_firstlineno'))
 
461
+
 
462
+        addr = 0
 
463
+        for addr_incr, line_incr in zip(co_lnotab[::2], co_lnotab[1::2]):
 
464
+            addr += ord(addr_incr)
 
465
+            if addr > addrq:
 
466
+                return lineno
 
467
+            lineno += ord(line_incr)
 
468
+        return lineno
394
469
+
395
470
+class PyDictObjectPtr(PyObjectPtr):
396
471
+    """
399
474
+    """
400
475
+    _typename = 'PyDictObject'
401
476
+
402
 
+    def proxyval(self):
 
477
+    def proxyval(self, visited):
 
478
+        # Guard against infinite loops:
 
479
+        if self.as_address() in visited:
 
480
+            return ProxyAlreadyVisited('{...}')
 
481
+        visited.add(self.as_address())
 
482
+
403
483
+        result = {}
404
484
+        for i in safe_range(self.field('ma_mask') + 1):
405
485
+            ep = self.field('ma_table') + i
406
486
+            pvalue = PyObjectPtr.from_pyobject_ptr(ep['me_value'])
407
487
+            if not pvalue.is_null():
408
488
+                pkey = PyObjectPtr.from_pyobject_ptr(ep['me_key'])
409
 
+                result[pkey.proxyval()] = pvalue.proxyval()
 
489
+                result[pkey.proxyval(visited)] = pvalue.proxyval(visited)
410
490
+        return result
411
491
+
412
492
+
413
493
+class PyInstanceObjectPtr(PyObjectPtr):
414
494
+    _typename = 'PyInstanceObject'
415
495
+
416
 
+    def proxyval(self):
 
496
+    def proxyval(self, visited):
 
497
+        # Guard against infinite loops:
 
498
+        if self.as_address() in visited:
 
499
+            return ProxyAlreadyVisited('<...>')
 
500
+        visited.add(self.as_address())
 
501
+
417
502
+        # Get name of class:
418
503
+        in_class = PyObjectPtr.from_pyobject_ptr(self.field('in_class'))
419
 
+        cl_name = PyObjectPtr.from_pyobject_ptr(in_class.field('cl_name')).proxyval()
 
504
+        cl_name = PyObjectPtr.from_pyobject_ptr(in_class.field('cl_name')).proxyval(visited)
420
505
+
421
506
+        # Get dictionary of instance attributes:
422
 
+        in_dict = PyObjectPtr.from_pyobject_ptr(self.field('in_dict')).proxyval()
 
507
+        in_dict = PyObjectPtr.from_pyobject_ptr(self.field('in_dict')).proxyval(visited)
423
508
+
424
509
+        # Old-style class:
425
510
+        return InstanceProxy(cl_name, in_dict, long(self._gdbval))
428
513
+class PyIntObjectPtr(PyObjectPtr):
429
514
+    _typename = 'PyIntObject'
430
515
+
431
 
+    def proxyval(self):
 
516
+    def proxyval(self, visited):
432
517
+        result = int_from_int(self.field('ob_ival'))
433
518
+        return result
434
519
+
435
 
+
436
520
+class PyListObjectPtr(PyObjectPtr):
437
521
+    _typename = 'PyListObject'
438
522
+
441
525
+        field_ob_item = self.field('ob_item')
442
526
+        return field_ob_item[i]
443
527
+
444
 
+    def proxyval(self):
445
 
+        result = [PyObjectPtr.from_pyobject_ptr(self[i]).proxyval()
 
528
+    def proxyval(self, visited):
 
529
+        # Guard against infinite loops:
 
530
+        if self.as_address() in visited:
 
531
+            return ProxyAlreadyVisited('[...]')
 
532
+        visited.add(self.as_address())
 
533
+        
 
534
+        result = [PyObjectPtr.from_pyobject_ptr(self[i]).proxyval(visited)
446
535
+                  for i in safe_range(int_from_int(self.field('ob_size')))]
447
536
+        return result
448
537
+
450
539
+class PyLongObjectPtr(PyObjectPtr):
451
540
+    _typename = 'PyLongObject'
452
541
+
453
 
+    def proxyval(self):
 
542
+    def proxyval(self, visited):
454
543
+        '''
455
544
+        Python's Include/longobjrep.h has this declaration:
456
545
+           struct _longobject {
495
584
+    """
496
585
+    _typename = 'PyObject'
497
586
+
498
 
+    def proxyval(self):
 
587
+    def proxyval(self, visited):
499
588
+        return None
500
589
+
501
590
+
507
596
+        return str(fi)
508
597
+
509
598
+
 
599
+class PySetObjectPtr(PyObjectPtr):
 
600
+    _typename = 'PySetObject'
 
601
+
 
602
+    def proxyval(self, visited):
 
603
+        # Guard against infinite loops:
 
604
+        if self.as_address() in visited:
 
605
+            return ProxyAlreadyVisited('%s(...)' % self.safe_tp_name())
 
606
+        visited.add(self.as_address())
 
607
+
 
608
+        members = []
 
609
+        table = self.field('table')
 
610
+        for i in safe_range(self.field('mask')+1):
 
611
+            setentry = table[i]
 
612
+            key = setentry['key']
 
613
+            if key != 0:
 
614
+                key_proxy = PyObjectPtr.from_pyobject_ptr(key).proxyval(visited)
 
615
+                if key_proxy != '<dummy key>':
 
616
+                    members.append(key_proxy)
 
617
+        if self.safe_tp_name() == 'frozenset':
 
618
+            return frozenset(members)
 
619
+        else:
 
620
+            return set(members)
 
621
+
510
622
+class PyStringObjectPtr(PyObjectPtr):
511
623
+    _typename = 'PyStringObject'
512
624
+
513
625
+    def __str__(self):
 
626
+        field_ob_size = self.field('ob_size')
514
627
+        field_ob_sval = self.field('ob_sval')
515
 
+        char_ptr = field_ob_sval.address.cast(_type_char_ptr)
516
 
+        return char_ptr.string()
 
628
+        char_ptr = field_ob_sval.address.cast(_type_unsigned_char_ptr)
 
629
+        return ''.join([chr(char_ptr[i]) for i in safe_range(field_ob_size)])
517
630
+
518
 
+    def proxyval(self):
 
631
+    def proxyval(self, visited):
519
632
+        return str(self)
520
633
+
521
634
+
527
640
+        field_ob_item = self.field('ob_item')
528
641
+        return field_ob_item[i]
529
642
+
530
 
+    def proxyval(self):
531
 
+        result = tuple([PyObjectPtr.from_pyobject_ptr(self[i]).proxyval()
 
643
+    def proxyval(self, visited):
 
644
+        # Guard against infinite loops:
 
645
+        if self.as_address() in visited:
 
646
+            return ProxyAlreadyVisited('(...)')
 
647
+        visited.add(self.as_address())
 
648
+
 
649
+        result = tuple([PyObjectPtr.from_pyobject_ptr(self[i]).proxyval(visited)
532
650
+                        for i in safe_range(int_from_int(self.field('ob_size')))])
533
651
+        return result
534
652
+
540
658
+class PyUnicodeObjectPtr(PyObjectPtr):
541
659
+    _typename = 'PyUnicodeObject'
542
660
+
543
 
+    def proxyval(self):
 
661
+    def proxyval(self, visited):
544
662
+        # From unicodeobject.h:
545
663
+        #     Py_ssize_t length;  /* Length of raw Unicode data in buffer */
546
664
+        #     Py_UNICODE *str;    /* Raw Unicode buffer */
582
700
+        self.co_name = PyObjectPtr.from_pyobject_ptr(self.co.field('co_name'))
583
701
+        self.co_filename = PyObjectPtr.from_pyobject_ptr(self.co.field('co_filename'))
584
702
+        self.f_lineno = int_from_int(fval.field('f_lineno'))
 
703
+        self.f_lasti = int_from_int(fval.field('f_lasti'))
585
704
+        self.co_nlocals = int_from_int(self.co.field('co_nlocals'))
586
705
+        self.co_varnames = PyTupleObjectPtr.from_pyobject_ptr(self.co.field('co_varnames'))
587
706
+        self.locals = [] # list of kv pairs
592
711
+            if not value.is_null():
593
712
+                name = PyObjectPtr.from_pyobject_ptr(self.co_varnames[i])
594
713
+                #print 'name=%s' % name
595
 
+                value = value.proxyval()
 
714
+                value = value.proxyval(set())
596
715
+                #print 'value=%s' % value
597
716
+                self.locals.append((str(name), value))
598
717
+
 
718
+    def filename(self):
 
719
+        '''Get the path of the current Python source file, as a string'''
 
720
+        return self.co_filename.proxyval(set())
 
721
+
 
722
+    def current_line_num(self):
 
723
+        '''Get current line number as an integer (1-based)
 
724
+        
 
725
+        Translated from PyFrame_GetLineNumber and PyCode_Addr2Line
 
726
+        
 
727
+        See Objects/lnotab_notes.txt
 
728
+        '''
 
729
+        f_trace = self.fval.field('f_trace')
 
730
+        if long(f_trace) != 0:
 
731
+            # we have a non-NULL f_trace:
 
732
+            return self.f_lineno
 
733
+        else:
 
734
+            #try:
 
735
+            return self.co.addr2line(self.f_lasti)
 
736
+            #except ValueError:
 
737
+            #    return self.f_lineno
 
738
+
 
739
+    def current_line(self):
 
740
+        '''Get the text of the current source line as a string, with a trailing
 
741
+        newline character'''
 
742
+        with open(self.filename(), 'r') as f:
 
743
+            all_lines = f.readlines()
 
744
+            # Convert from 1-based current_line_num to 0-based list offset:
 
745
+            return all_lines[self.current_line_num()-1]
 
746
+
599
747
+    def __str__(self):
600
 
+        return ('File %s, line %i, in %s (%s)'
601
 
+                % (self.co_filename,
602
 
+                   self.f_lineno,
 
748
+        return ('Frame 0x%x, for file %s, line %i, in %s (%s)'
 
749
+                % (long(self.fval._gdbval),
 
750
+                   self.co_filename,
 
751
+                   self.current_line_num(),
603
752
+                   self.co_name,
604
753
+                   ', '.join(['%s=%s' % (k, stringify(v)) for k, v in self.locals]))
605
754
+                )
612
761
+        self.gdbval = gdbval
613
762
+
614
763
+    def to_string (self):
615
 
+        proxyval = PyObjectPtr.from_pyobject_ptr(self.gdbval).proxyval()
 
764
+        proxyval = PyObjectPtr.from_pyobject_ptr(self.gdbval).proxyval(set())
616
765
+        return stringify(proxyval)
617
766
+
618
767
+
663
812
+    obj.pretty_printers.append(pretty_printer_lookup)
664
813
+
665
814
+register (gdb.current_objfile ())
 
815
+
 
816
+def get_python_frame(gdb_frame):
 
817
+    try:
 
818
+        f = gdb_frame.read_var('f')
 
819
+        return PyFrameObjectPtr.from_pyobject_ptr(f)
 
820
+    except ValueError:
 
821
+        return None
 
822
+
 
823
+def get_selected_python_frame():
 
824
+    '''Try to obtain a (gdbframe, PyFrameObjectPtr) pair for the
 
825
+    currently-running python code, or (None, None)'''
 
826
+    gdb_frame = gdb.selected_frame()
 
827
+    while gdb_frame:
 
828
+        if (gdb_frame.function() is None or
 
829
+            gdb_frame.function().name != 'PyEval_EvalFrameEx'):
 
830
+            gdb_frame = gdb_frame.older()
 
831
+            continue
 
832
+
 
833
+        try:
 
834
+            f = gdb_frame.read_var('f')
 
835
+            return gdb_frame, PyFrameObjectPtr.from_pyobject_ptr(f)
 
836
+        except ValueError:
 
837
+            gdb_frame = gdb_frame.older()
 
838
+    return None, None
 
839
+
 
840
+class PyList(gdb.Command):
 
841
+    '''List the current Python source code, if any
 
842
+
 
843
+    Use
 
844
+       py-list START
 
845
+    to list at a different line number within the python source.
 
846
+    
 
847
+    Use
 
848
+       py-list START, END
 
849
+    to list a specific range of lines within the python source.
 
850
+    '''
 
851
+
 
852
+    def __init__(self):
 
853
+        gdb.Command.__init__ (self,
 
854
+                              "py-list",
 
855
+                              gdb.COMMAND_FILES,
 
856
+                              gdb.COMPLETE_NONE)
 
857
+
 
858
+
 
859
+    def invoke(self, args, from_tty):
 
860
+        import re
 
861
+
 
862
+        start = None
 
863
+        end = None
 
864
+
 
865
+        m = re.match(r'\s*(\d+)\s*', args)
 
866
+        if m:
 
867
+            start = int(m.group(0))
 
868
+            end = start + 10
 
869
+
 
870
+        m = re.match(r'\s*(\d+)\s*,\s*(\d+)\s*', args)
 
871
+        if m:
 
872
+            start, end = map(int, m.groups())
 
873
+
 
874
+        gdb_frame, py_frame = get_selected_python_frame()
 
875
+        if not py_frame:
 
876
+            print 'Unable to locate python frame'
 
877
+            return
 
878
+
 
879
+        fi = FrameInfo(py_frame)
 
880
+        filename = fi.filename()
 
881
+        lineno = fi.current_line_num()
 
882
+
 
883
+        if start is None:
 
884
+            start = lineno - 5
 
885
+            end = lineno + 5
 
886
+
 
887
+        if start<1:
 
888
+            start = 1
 
889
+
 
890
+        with open(filename, 'r') as f:
 
891
+            all_lines = f.readlines()
 
892
+            # start and end are 1-based, all_lines is 0-based;
 
893
+            # so [start-1:end] as a python slice gives us [start, end] as a
 
894
+            # closed interval
 
895
+            for i, line in enumerate(all_lines[start-1:end]):
 
896
+                sys.stdout.write('%4s    %s' % (i+start, line))
 
897
+            
 
898
+        
 
899
+# ...and register the command:
 
900
+PyList()
 
901
+
 
902
+def move_in_stack(move_up):
 
903
+    '''Move up or down the stack (for the py-up/py-down command)'''
 
904
+    gdb_frame, py_frame = get_selected_python_frame()
 
905
+    while gdb_frame:
 
906
+        if move_up:
 
907
+            iter_frame = gdb_frame.older()
 
908
+        else:
 
909
+            iter_frame = gdb_frame.newer()
 
910
+
 
911
+        if not iter_frame:
 
912
+            break
 
913
+
 
914
+        if (iter_frame.function() and 
 
915
+            iter_frame.function().name == 'PyEval_EvalFrameEx'):
 
916
+            # Result:
 
917
+            iter_frame.select()
 
918
+            py_frame = get_python_frame(iter_frame)
 
919
+            fi = FrameInfo(py_frame)
 
920
+            print fi
 
921
+            sys.stdout.write(fi.current_line())
 
922
+            return
 
923
+
 
924
+        gdb_frame = iter_frame
 
925
+
 
926
+    if move_up:
 
927
+        print 'Unable to find an older python frame'
 
928
+    else:
 
929
+        print 'Unable to find a newer python frame'
 
930
+
 
931
+class PyUp(gdb.Command):
 
932
+    'Select and print the python stack frame that called this one (if any)'
 
933
+    def __init__(self):
 
934
+        gdb.Command.__init__ (self,
 
935
+                              "py-up",
 
936
+                              gdb.COMMAND_STACK,
 
937
+                              gdb.COMPLETE_NONE)
 
938
+
 
939
+
 
940
+    def invoke(self, args, from_tty):
 
941
+        move_in_stack(move_up=True)
 
942
+
 
943
+PyUp()
 
944
+
 
945
+class PyDown(gdb.Command):
 
946
+    'Select and print the python stack frame called by this one (if any)'
 
947
+    def __init__(self):
 
948
+        gdb.Command.__init__ (self,
 
949
+                              "py-down",
 
950
+                              gdb.COMMAND_STACK,
 
951
+                              gdb.COMPLETE_NONE)
 
952
+
 
953
+
 
954
+    def invoke(self, args, from_tty):
 
955
+        move_in_stack(move_up=False)
 
956
+
 
957
+PyDown()
 
958
+
 
959
+class PyBacktrace(gdb.Command):
 
960
+    'Display the current python frame and all the frames within its call stack (if any)'
 
961
+    def __init__(self):
 
962
+        gdb.Command.__init__ (self,
 
963
+                              "py-bt",
 
964
+                              gdb.COMMAND_STACK,
 
965
+                              gdb.COMPLETE_NONE)
 
966
+
 
967
+
 
968
+    def invoke(self, args, from_tty):
 
969
+        gdb_frame, py_frame = get_selected_python_frame()
 
970
+        while gdb_frame:
 
971
+            gdb_frame = gdb_frame.older()
 
972
+
 
973
+            if not gdb_frame:
 
974
+                break
 
975
+
 
976
+            if (gdb_frame.function() and 
 
977
+                gdb_frame.function().name == 'PyEval_EvalFrameEx'):
 
978
+                py_frame = get_python_frame(gdb_frame)
 
979
+                fi = FrameInfo(py_frame)
 
980
+                print '  ', fi
 
981
+                sys.stdout.write(fi.current_line())
 
982
+
 
983
+PyBacktrace()
666
984
Index: Tools/README
667
985
===================================================================
668
 
--- Tools/README        (revision 78520)
 
986
--- Tools/README        (revision 79422)
669
987
+++ Tools/README        (working copy)
670
988
@@ -20,6 +20,9 @@
671
989
 
677
995
 i18n           Tools for internationalization. pygettext.py 
678
996
                parses Python source code and generates .pot files,
679
997
                and msgfmt.py generates a binary message catalog 
 
998
Index: Lib/test/test_gdb_sample.py
 
999
===================================================================
 
1000
--- Lib/test/test_gdb_sample.py (revision 0)
 
1001
+++ Lib/test/test_gdb_sample.py (revision 0)
 
1002
@@ -0,0 +1,12 @@
 
1003
+# Sample script for use by test_gdb.py
 
1004
+
 
1005
+def foo(a, b, c):
 
1006
+    bar(a, b, c)
 
1007
+
 
1008
+def bar(a, b, c):
 
1009
+    baz(a, b, c)
 
1010
+
 
1011
+def baz(*args):
 
1012
+    print(42)
 
1013
+
 
1014
+foo(1, 2, 3)
680
1015
Index: Lib/test/test_gdb.py
681
1016
===================================================================
682
1017
--- Lib/test/test_gdb.py        (revision 0)
683
1018
+++ Lib/test/test_gdb.py        (revision 0)
684
 
@@ -0,0 +1,259 @@
 
1019
@@ -0,0 +1,536 @@
685
1020
+# Verify that gdb can pretty-print the various PyObject* types
686
1021
+#
687
1022
+# The code for testing gdb was adapted from similar work in Unladen Swallow's
730
1065
+            ).communicate()
731
1066
+        return out, err
732
1067
+
733
 
+    def get_stack_trace(self, source, breakpoint='PyObject_Print',
734
 
+                        commands_after_breakpoint=None):
 
1068
+    def get_stack_trace(self, source=None, script=None,
 
1069
+                        breakpoint='PyObject_Print',
 
1070
+                        cmds_after_breakpoint=None,
 
1071
+                        import_site=False):
735
1072
+        '''
736
1073
+        Run 'python -c SOURCE' under gdb with a breakpoint.
737
1074
+
739
1076
+
740
1077
+        Returns the stdout from gdb
741
1078
+
742
 
+        commands_after_breakpoint: if provided, a list of strings: gdb commands
 
1079
+        cmds_after_breakpoint: if provided, a list of strings: gdb commands
743
1080
+        '''
744
1081
+        # We use "set breakpoint pending yes" to avoid blocking with a:
745
1082
+        #   Function "foo" not defined.
758
1095
+        commands = ['set breakpoint pending yes',
759
1096
+                    'break %s' % breakpoint,
760
1097
+                    'run']
761
 
+        if commands_after_breakpoint:
762
 
+            commands += commands_after_breakpoint
763
 
+        commands += ['backtrace']
 
1098
+        if cmds_after_breakpoint:
 
1099
+            commands += cmds_after_breakpoint
 
1100
+        else:
 
1101
+            commands += ['backtrace']
764
1102
+
765
1103
+        # print commands
766
1104
+
768
1106
+        args = ["gdb", "--batch"]
769
1107
+        args += ['--eval-command=%s' % cmd for cmd in commands]
770
1108
+        args += ["--args",
771
 
+                 sys.executable, "-S", "-c", source]
772
 
+        # -S suppresses the default 'import site'
 
1109
+                 sys.executable]
 
1110
+
 
1111
+        if not import_site:
 
1112
+            # -S suppresses the default 'import site'
 
1113
+            args += ["-S"]
 
1114
+            
 
1115
+        if source:
 
1116
+            args += ["-c", source]
 
1117
+        elif script:
 
1118
+            args += [script]
773
1119
+
774
1120
+        # print args
775
 
+
 
1121
+        # print ' '.join(args)
 
1122
+            
776
1123
+        # Use "args" to invoke gdb, capturing stdout, stderr:
777
1124
+        out, err = self.run_gdb(*args)
778
1125
+
785
1132
+        return out
786
1133
+
787
1134
+    def get_gdb_repr(self, source,
788
 
+                     commands_after_breakpoint=None):
 
1135
+                     cmds_after_breakpoint=None,
 
1136
+                     import_site=False):
789
1137
+        # Given an input python source representation of data,
790
1138
+        # run "python -c'print DATA'" under gdb with a breakpoint on
791
1139
+        # PyObject_Print and scrape out gdb's representation of the "op"
793
1141
+        #
794
1142
+        # For a nested structure, the first time we hit the breakpoint will
795
1143
+        # give us the top-level structure
796
 
+        gdb_output = self.get_stack_trace(source, 'PyObject_Print',
797
 
+                                          commands_after_breakpoint)
 
1144
+        gdb_output = self.get_stack_trace(source, breakpoint='PyObject_Print',
 
1145
+                                          cmds_after_breakpoint=cmds_after_breakpoint,
 
1146
+                                          import_site=import_site)
798
1147
+        m = re.match('.*#0  PyObject_Print \(op\=(.*?), fp=.*\).*',
799
1148
+                     gdb_output, re.DOTALL)
800
1149
+        #print m.groups()
801
1150
+        return m.group(1), gdb_output
802
1151
+
 
1152
+    def assertEndsWith(self, actual, exp_end):
 
1153
+        '''Ensure that the given "actual" string ends with "exp_end"'''
 
1154
+        self.assert_(actual.endswith(exp_end),
 
1155
+                     msg='%r did not end with %r' % (actual, exp_end))
 
1156
+
 
1157
+    def assertMultilineMatches(self, actual, pattern):
 
1158
+        m = re.match(pattern, actual, re.DOTALL)
 
1159
+        self.assert_(m,
 
1160
+                     msg='%r did not match %r' % (actual, pattern))
 
1161
+
 
1162
+class PrettyPrintTests(DebuggerTests):
803
1163
+    def test_getting_backtrace(self):
804
1164
+        gdb_output = self.get_stack_trace('print 42')
805
1165
+        self.assertTrue('PyObject_Print' in gdb_output)
806
1166
+
807
 
+    def assertGdbRepr(self, val, commands_after_breakpoint=None):
 
1167
+    def assertGdbRepr(self, val, cmds_after_breakpoint=None):
808
1168
+        # Ensure that gdb's rendering of the value in a debugged process
809
1169
+        # matches repr(value) in this process:
810
1170
+        gdb_repr, gdb_output = self.get_gdb_repr('print ' + repr(val),
811
 
+                                                 commands_after_breakpoint)
 
1171
+                                                 cmds_after_breakpoint)
812
1172
+        self.assertEquals(gdb_repr, repr(val), gdb_output)
813
1173
+
814
1174
+    def test_int(self):
 
1175
+        'Verify the pretty-printing of various "int" values'
815
1176
+        self.assertGdbRepr(42)
816
1177
+        self.assertGdbRepr(0)
817
1178
+        self.assertGdbRepr(-7)
819
1180
+        self.assertGdbRepr(-sys.maxint)
820
1181
+
821
1182
+    def test_long(self):
 
1183
+        'Verify the pretty-printing of various "long" values'
822
1184
+        self.assertGdbRepr(0L)
823
1185
+        self.assertGdbRepr(1000000000000L)
824
1186
+        self.assertGdbRepr(-1L)
825
1187
+        self.assertGdbRepr(-1000000000000000L)
826
1188
+
827
1189
+    def test_singletons(self):
 
1190
+        'Verify the pretty-printing of True, False and None'
828
1191
+        self.assertGdbRepr(True)
829
1192
+        self.assertGdbRepr(False)
830
1193
+        self.assertGdbRepr(None)
831
1194
+
832
1195
+    def test_dicts(self):
 
1196
+        'Verify the pretty-printing of dictionaries'
833
1197
+        self.assertGdbRepr({})
834
1198
+        self.assertGdbRepr({'foo': 'bar'})
835
1199
+
836
1200
+    def test_lists(self):
 
1201
+        'Verify the pretty-printing of lists'
837
1202
+        self.assertGdbRepr([])
838
1203
+        self.assertGdbRepr(range(5))
839
1204
+
840
1205
+    def test_strings(self):
 
1206
+        'Verify the pretty-printing of strings'
841
1207
+        self.assertGdbRepr('')
842
1208
+        self.assertGdbRepr('And now for something hopefully the same')
 
1209
+        self.assertGdbRepr('string with embedded NUL here \0 and then some more text')
 
1210
+        self.assertGdbRepr('this is byte 255:\xff and byte 128:\x80')
843
1211
+
844
1212
+    def test_tuples(self):
 
1213
+        'Verify the pretty-printing of tuples'
845
1214
+        self.assertGdbRepr(tuple())
846
1215
+        self.assertGdbRepr((1,))
847
1216
+
848
1217
+    def test_unicode(self):
 
1218
+        'Verify the pretty-printing of unicode values'
849
1219
+        self.assertGdbRepr(u'hello world')
850
1220
+        self.assertGdbRepr(u'\u2620')
851
1221
+
 
1222
+    def test_sets(self):
 
1223
+        'Verify the pretty-printing of sets'
 
1224
+        self.assertGdbRepr(set())
 
1225
+        self.assertGdbRepr(set(['a','b','c']))
 
1226
+        self.assertGdbRepr(set([4, 5, 6]))
 
1227
+
 
1228
+        # Ensure that we handled sets containing the "dummy" key value,
 
1229
+        # which happens on deletion:
 
1230
+        gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b'])
 
1231
+s.pop()
 
1232
+print s''')
 
1233
+        self.assertEquals(gdb_repr, "set(['b'])")
 
1234
+
 
1235
+    def test_frozensets(self):
 
1236
+        'Verify the pretty-printing of frozensets'
 
1237
+        self.assertGdbRepr(frozenset())
 
1238
+        self.assertGdbRepr(frozenset(['a','b','c']))
 
1239
+        self.assertGdbRepr(frozenset([4, 5, 6]))
 
1240
+
 
1241
+    def test_exceptions(self):
 
1242
+        # Test a RuntimeError
 
1243
+        gdb_repr, gdb_output = self.get_gdb_repr('''
 
1244
+try:
 
1245
+    raise RuntimeError("I am an error")
 
1246
+except RuntimeError, e:
 
1247
+    print e
 
1248
+''')
 
1249
+        self.assertEquals(gdb_repr,
 
1250
+                          "exceptions.RuntimeError('I am an error',)")
 
1251
+
 
1252
+
 
1253
+        # Test division by zero:
 
1254
+        gdb_repr, gdb_output = self.get_gdb_repr('''
 
1255
+try:
 
1256
+    a = 1 / 0
 
1257
+except ZeroDivisionError, e:
 
1258
+    print e
 
1259
+''')
 
1260
+        self.assertEquals(gdb_repr,
 
1261
+                          "exceptions.ZeroDivisionError('integer division or modulo by zero',)")
 
1262
+
852
1263
+    def test_classic_class(self):
 
1264
+        'Verify the pretty-printing of classic class instances'
853
1265
+        gdb_repr, gdb_output = self.get_gdb_repr('''
854
1266
+class Foo:
855
1267
+    pass
861
1273
+                        msg='Unexpected classic-class rendering %r' % gdb_repr)
862
1274
+
863
1275
+    def test_modern_class(self):
 
1276
+        'Verify the pretty-printing of new-style class instances'
864
1277
+        gdb_repr, gdb_output = self.get_gdb_repr('''
865
1278
+class Foo(object):
866
1279
+    pass
872
1285
+                        msg='Unexpected new-style class rendering %r' % gdb_repr)
873
1286
+
874
1287
+    def test_subclassing_list(self):
 
1288
+        'Verify the pretty-printing of an instance of a list subclass'
875
1289
+        gdb_repr, gdb_output = self.get_gdb_repr('''
876
1290
+class Foo(list):
877
1291
+    pass
884
1298
+                        msg='Unexpected new-style class rendering %r' % gdb_repr)
885
1299
+
886
1300
+    def test_subclassing_tuple(self):
887
 
+        '''This should exercise the negative tp_dictoffset code in the
888
 
+        new-style class support'''
 
1301
+        'Verify the pretty-printing of an instance of a tuple subclass'
 
1302
+        # This should exercise the negative tp_dictoffset code in the
 
1303
+        # new-style class support
889
1304
+        gdb_repr, gdb_output = self.get_gdb_repr('''
890
1305
+class Foo(tuple):
891
1306
+    pass
902
1317
+
903
1318
+        Verify that the variable's representation is the expected failsafe
904
1319
+        representation'''
 
1320
+        if corruption:
 
1321
+            cmds_after_breakpoint=[corruption, 'backtrace']
 
1322
+        else:
 
1323
+            cmds_after_breakpoint=['backtrace']
 
1324
+
905
1325
+        gdb_repr, gdb_output = \
906
1326
+            self.get_gdb_repr(source,
907
 
+                              commands_after_breakpoint=[corruption])
 
1327
+                              cmds_after_breakpoint=cmds_after_breakpoint)
908
1328
+        self.assertTrue(re.match('<%s at remote 0x[0-9a-f]+>' % exp_type,
909
1329
+                                 gdb_repr),
910
1330
+                        'Unexpected gdb representation: %r\n%s' % \
912
1332
+
913
1333
+    def test_NULL_ptr(self):
914
1334
+        'Ensure that a NULL PyObject* is handled gracefully'
915
 
+        self.assertSane('print 42',
916
 
+                        'set variable op=0')
 
1335
+        gdb_repr, gdb_output = (
 
1336
+            self.get_gdb_repr('print 42',
 
1337
+                              cmds_after_breakpoint=['set variable op=0',
 
1338
+                                                         'backtrace'])
 
1339
+            )
 
1340
+    
 
1341
+        self.assertEquals(gdb_repr, '0x0')
917
1342
+
918
1343
+    def test_NULL_ob_type(self):
 
1344
+        'Ensure that a PyObject* with NULL ob_type is handled gracefully'
919
1345
+        self.assertSane('print 42',
920
1346
+                        'set op->ob_type=0')
921
1347
+
922
1348
+    def test_corrupt_ob_type(self):
 
1349
+        'Ensure that a PyObject* with a corrupt ob_type is handled gracefully'
923
1350
+        self.assertSane('print 42',
924
1351
+                        'set op->ob_type=0xDEADBEEF')
925
1352
+
926
1353
+    def test_corrupt_tp_flags(self):
 
1354
+        'Ensure that a PyObject* with a type with corrupt tp_flags is handled'
927
1355
+        self.assertSane('print 42',
928
1356
+                        'set op->ob_type->tp_flags=0x0',
929
1357
+                        exp_type='int')
930
1358
+
931
1359
+    def test_corrupt_tp_name(self):
 
1360
+        'Ensure that a PyObject* with a type with corrupt tp_name is handled'
932
1361
+        self.assertSane('print 42',
933
1362
+                        'set op->ob_type->tp_name=0xDEADBEEF')
934
1363
+
 
1364
+    def test_NULL_instance_dict(self):
 
1365
+        'Ensure that a PyInstanceObject with with a NULL in_dict is handled'
 
1366
+        self.assertSane('''
 
1367
+class Foo:
 
1368
+    pass
 
1369
+foo = Foo()
 
1370
+foo.an_int = 42
 
1371
+print foo''',
 
1372
+                        'set ((PyInstanceObject*)op)->in_dict = 0',
 
1373
+                        exp_type='Foo')
 
1374
+
 
1375
+    def test_builtins_help(self):
 
1376
+        'Ensure that the new-style class _Helper in site.py can be handled'
 
1377
+        # (this was the issue causing tracebacks in
 
1378
+        #  http://bugs.python.org/issue8032#msg100537 )
 
1379
+
 
1380
+        gdb_repr, gdb_output = self.get_gdb_repr('print __builtins__.help', import_site=True)
 
1381
+        m = re.match(r'<_Helper at remote 0x[0-9a-f]+>', gdb_repr)
 
1382
+        self.assertTrue(m,
 
1383
+                        msg='Unexpected rendering %r' % gdb_repr)
 
1384
+
 
1385
+    def test_selfreferential_list(self):
 
1386
+        '''Ensure that a reference loop involving a list doesn't lead proxyval
 
1387
+        into an infinite loop:'''
 
1388
+        gdb_repr, gdb_output = \
 
1389
+            self.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; print a")
 
1390
+            
 
1391
+        self.assertEquals(gdb_repr, '[3, 4, 5, [...]]')
 
1392
+
 
1393
+        gdb_repr, gdb_output = \
 
1394
+            self.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; print a")
 
1395
+            
 
1396
+        self.assertEquals(gdb_repr, '[3, 4, 5, [[...]]]')
 
1397
+
 
1398
+    def test_selfreferential_dict(self):
 
1399
+        '''Ensure that a reference loop involving a dict doesn't lead proxyval
 
1400
+        into an infinite loop:'''
 
1401
+        gdb_repr, gdb_output = \
 
1402
+            self.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; print a")
 
1403
+            
 
1404
+        self.assertEquals(gdb_repr, "{'foo': {'bar': {...}}}")
 
1405
+
 
1406
+    def test_selfreferential_old_style_instance(self):
 
1407
+        gdb_repr, gdb_output = \
 
1408
+            self.get_gdb_repr('''
 
1409
+class Foo:
 
1410
+    pass
 
1411
+foo = Foo()
 
1412
+foo.an_attr = foo
 
1413
+print foo''')
 
1414
+        self.assertTrue(re.match('<Foo\(an_attr=<\.\.\.>\) at remote 0x[0-9a-f]+>',
 
1415
+                                 gdb_repr),
 
1416
+                        'Unexpected gdb representation: %r\n%s' % \
 
1417
+                            (gdb_repr, gdb_output))
 
1418
+
 
1419
+    def test_selfreferential_new_style_instance(self):
 
1420
+        gdb_repr, gdb_output = \
 
1421
+            self.get_gdb_repr('''
 
1422
+class Foo(object):
 
1423
+    pass
 
1424
+foo = Foo()
 
1425
+foo.an_attr = foo
 
1426
+print foo''')
 
1427
+        self.assertTrue(re.match('<Foo\(an_attr=<\.\.\.>\) at remote 0x[0-9a-f]+>',
 
1428
+                                 gdb_repr),
 
1429
+                        'Unexpected gdb representation: %r\n%s' % \
 
1430
+                            (gdb_repr, gdb_output))
 
1431
+
 
1432
+        gdb_repr, gdb_output = \
 
1433
+            self.get_gdb_repr('''
 
1434
+class Foo(object):
 
1435
+    pass
 
1436
+a = Foo()
 
1437
+b = Foo()
 
1438
+a.an_attr = b
 
1439
+b.an_attr = a
 
1440
+print a''')
 
1441
+        self.assertTrue(re.match('<Foo\(an_attr=<Foo\(an_attr=<\.\.\.>\) at remote 0x[0-9a-f]+>\) at remote 0x[0-9a-f]+>',
 
1442
+                                 gdb_repr),
 
1443
+                        'Unexpected gdb representation: %r\n%s' % \
 
1444
+                            (gdb_repr, gdb_output))
 
1445
+
935
1446
+    # TODO:
936
1447
+    #   frames
937
1448
+
938
1449
+
 
1450
+class PyListTests(DebuggerTests):
 
1451
+    def assertListing(self, expected, actual):
 
1452
+        self.assertEndsWith(actual, expected)
 
1453
+
 
1454
+    def test_basic_command(self):
 
1455
+        'Verify that the "py-list" command works'
 
1456
+        bt = self.get_stack_trace(script='Lib/test/test_gdb_sample.py',
 
1457
+                                  cmds_after_breakpoint=['py-list'])
 
1458
+
 
1459
+        self.assertListing('''
 
1460
+   5    
 
1461
+   6    def bar(a, b, c):
 
1462
+   7        baz(a, b, c)
 
1463
+   8    
 
1464
+   9    def baz(*args):
 
1465
+  10        print(42)
 
1466
+  11    
 
1467
+  12    foo(1, 2, 3)
 
1468
+''',
 
1469
+                      bt)
 
1470
+
 
1471
+    def test_one_abs_arg(self):
 
1472
+        'Verify the "py-list" command with one absolute argument'
 
1473
+        bt = self.get_stack_trace(script='Lib/test/test_gdb_sample.py',
 
1474
+                                  cmds_after_breakpoint=['py-list 9'])
 
1475
+
 
1476
+        self.assertListing('''
 
1477
+   9    def baz(*args):
 
1478
+  10        print(42)
 
1479
+  11    
 
1480
+  12    foo(1, 2, 3)
 
1481
+''',
 
1482
+                      bt)
 
1483
+
 
1484
+    def test_two_abs_args(self):
 
1485
+        'Verify the "py-list" command with two absolute arguments'
 
1486
+        bt = self.get_stack_trace(script='Lib/test/test_gdb_sample.py',
 
1487
+                                  cmds_after_breakpoint=['py-list 1,3'])
 
1488
+
 
1489
+        self.assertListing('''
 
1490
+   1    # Sample script for use by test_gdb.py
 
1491
+   2    
 
1492
+   3    def foo(a, b, c):
 
1493
+''',
 
1494
+                      bt)
 
1495
+
 
1496
+class StackNavigationTests(DebuggerTests):
 
1497
+    def test_pyup_command(self):
 
1498
+        'Verify that the "py-up" command works'
 
1499
+        bt = self.get_stack_trace(script='Lib/test/test_gdb_sample.py',
 
1500
+                                  cmds_after_breakpoint=['py-up'])
 
1501
+        self.assertMultilineMatches(bt, 
 
1502
+                                    r'''^.*
 
1503
+Frame 0x[0-9a-f]+, for file Lib/test/test_gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
 
1504
+    baz\(a, b, c\)
 
1505
+$''')
 
1506
+
 
1507
+    def test_down_at_bottom(self):
 
1508
+        'Verify handling of "py-down" at the bottom of the stack'
 
1509
+        bt = self.get_stack_trace(script='Lib/test/test_gdb_sample.py',
 
1510
+                                  cmds_after_breakpoint=['py-down'])
 
1511
+        self.assertEndsWith(bt, 
 
1512
+                            'Unable to find a newer python frame\n')
 
1513
+
 
1514
+    def test_up_at_top(self):
 
1515
+        'Verify handling of "py-up" at the top of the stack'
 
1516
+        bt = self.get_stack_trace(script='Lib/test/test_gdb_sample.py',
 
1517
+                                  cmds_after_breakpoint=['py-up'] * 4)
 
1518
+        self.assertEndsWith(bt,
 
1519
+                            'Unable to find an older python frame\n')
 
1520
+
 
1521
+    def test_up_then_down(self):
 
1522
+        'Verify "py-up" followed by "py-down"'
 
1523
+        bt = self.get_stack_trace(script='Lib/test/test_gdb_sample.py',
 
1524
+                                  cmds_after_breakpoint=['py-up', 'py-down'])
 
1525
+        self.assertMultilineMatches(bt,
 
1526
+                                    r'''^.*
 
1527
+Frame 0x[0-9a-f]+, for file Lib/test/test_gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
 
1528
+    baz\(a, b, c\)
 
1529
+Frame 0x[0-9a-f]+, for file Lib/test/test_gdb_sample.py, line 10, in baz \(args=\(1, 2, 3\)\)
 
1530
+    print\(42\)
 
1531
+$''')
 
1532
+
 
1533
+class PyBtTests(DebuggerTests):
 
1534
+    def test_basic_command(self):
 
1535
+        'Verify that the "py-bt" command works'
 
1536
+        bt = self.get_stack_trace(script='Lib/test/test_gdb_sample.py',
 
1537
+                                  cmds_after_breakpoint=['py-bt'])
 
1538
+        self.assertMultilineMatches(bt,
 
1539
+                                    r'''^.*
 
1540
+   Frame 0x[0-9a-f]+, for file Lib/test/test_gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
 
1541
+    baz\(a, b, c\)
 
1542
+   Frame 0x[0-9a-f]+, for file Lib/test/test_gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
 
1543
+    bar\(a, b, c\)
 
1544
+   Frame 0x[0-9a-f]+, for file Lib/test/test_gdb_sample.py, line 12, in <module> \(\)
 
1545
+foo\(1, 2, 3\)
 
1546
+''')
 
1547
+
939
1548
+def test_main():
940
 
+    run_unittest(DebuggerTests)
 
1549
+    run_unittest(PrettyPrintTests,
 
1550
+                 PyListTests,
 
1551
+                 StackNavigationTests,
 
1552
+                 PyBtTests)
941
1553
+
942
1554
+if __name__ == "__main__":
943
1555
+    test_main()
944
1556
Index: Makefile.pre.in
945
1557
===================================================================
946
 
--- Makefile.pre.in     (revision 78520)
 
1558
--- Makefile.pre.in     (revision 79422)
947
1559
+++ Makefile.pre.in     (working copy)
948
 
@@ -359,7 +359,7 @@
 
1560
@@ -360,7 +360,7 @@
949
1561
 
950
1562
 # Default target
951
1563
 all:           build_all
954
1566
 
955
1567
 # Compile a binary with gcc profile guided optimization.
956
1568
 profile-opt:
957
 
@@ -432,6 +432,16 @@
 
1569
@@ -433,6 +433,16 @@
958
1570
 libpython$(VERSION).sl: $(LIBRARY_OBJS)
959
1571
        $(LDSHARED) $(LDFLAGS) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST)
960
1572
 
971
1583
 # This rule is here for OPENSTEP/Rhapsody/MacOSX. It builds a temporary
972
1584
 # minimal framework (not including the Lib directory and such) in the current
973
1585
 # directory.
974
 
@@ -1233,5 +1243,6 @@
 
1586
@@ -1238,5 +1248,6 @@
975
1587
 .PHONY: frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools
976
1588
 .PHONY: frameworkaltinstallunixtools recheck autoconf clean clobber distclean 
977
1589
 .PHONY: smelly funny patchcheck