small fixes to lineprogram + unit tests + new unittest runner
authorEli Bendersky <eliben@gmail.com>
Sun, 4 Dec 2011 04:22:24 +0000 (06:22 +0200)
committerEli Bendersky <eliben@gmail.com>
Sun, 4 Dec 2011 04:22:24 +0000 (06:22 +0200)
elftools/dwarf/dwarfinfo.py
elftools/dwarf/lineprogram.py
tests/run_all_unittests.py [new file with mode: 0755]
tests/test_dwarf_lineprogram.py [new file with mode: 0644]
tests/test_dwarf_structs.py
z.py

index bb073e0d48c9cdfafe945fe39d36fa4c5674a926..d805d555e49ae4d22ed59e8d31000e51eff77cfe 100644 (file)
@@ -196,7 +196,7 @@ class DWARFInfo(object):
 
         return LineProgram(
             header=lineprog_header,
-            dwarfinfo=self,
+            stream=self.debug_line_sec.stream,
             structs=structs,
             program_start_offset=self.debug_line_sec.stream.tell(),
             program_end_offset=end_offset)
index 11cd57f30a5c1287ef0f23013f09e4b8a51a3db2..2dc4b3f552dce7748e5559644cc3dc1f63766cfb 100644 (file)
@@ -38,13 +38,14 @@ class LineState(object):
             a.append('  %s = %s' % (attr, getattr(self, attr)))
         return '\n'.join(a) + '>\n'
 
+
 class LineProgram(object):
     """ Builds a "line table", which is essentially the matrix described
         in section 6.2 of DWARFv3. It's a list of LineState objects,
         sorted by increasing address, so it can be used to obtain the
         state information for each address.
     """
-    def __init__(self, header, dwarfinfo, structs,
+    def __init__(self, header, stream, structs,
                  program_start_offset, program_end_offset):
         """ 
             header:
@@ -52,19 +53,19 @@ class LineProgram(object):
                 its header by appending file entries if DW_LNE_define_file
                 instructions are encountered.
 
-            dwarfinfo:
-                The DWARFInfo context object which created this one
+            stream:
+                The stream this program can be read from.
 
             structs:
                 A DWARFStructs instance suitable for this line program
 
             program_{start|end}_offset:
                 Offset in the debug_line section stream where this program
-                starts, and where it ends. The actual range includes start
-                but not end: [start, end - 1]
+                starts (the actual program, after the header), and where it
+                ends.
+                The actual range includes start but not end: [start, end - 1]
         """
-        self.dwarfinfo = dwarfinfo
-        self.stream = self.dwarfinfo.debug_line_sec.stream
+        self.stream = stream
         self.header = header
         self.structs = structs
         self.program_start_offset = program_start_offset
@@ -170,7 +171,7 @@ class LineProgram(object):
                     state.address += ((adjusted_opcode / self['line_range']) *
                                       self['minimum_instruction_length'])
                 elif opcode == DW_LNS_fixed_advance_pc:
-                    operand = struct_parse(self.structs.Dwarf_uint16,
+                    operand = struct_parse(self.structs.Dwarf_uint16(''),
                                            self.stream)
                     state.address += operand
                 elif opcode == DW_LNS_set_prologue_end:
diff --git a/tests/run_all_unittests.py b/tests/run_all_unittests.py
new file mode 100755 (executable)
index 0000000..b3a3871
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+#-------------------------------------------------------------------------------
+# tests/run_all_unittests.py
+#
+# Run all unit tests
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from unittest import TestLoader, TextTestRunner
+
+
+if __name__ == '__main__':
+    try:
+        tests = TestLoader().discover('tests', 'test*.py', 'tests')
+        TextTestRunner().run(tests)
+    except ImportError as err:
+        print err
+        print '!! Please execute from the root directory of pyelfutils'
+
diff --git a/tests/test_dwarf_lineprogram.py b/tests/test_dwarf_lineprogram.py
new file mode 100644 (file)
index 0000000..50d8af3
--- /dev/null
@@ -0,0 +1,88 @@
+import sys, unittest
+from cStringIO import StringIO
+
+sys.path.extend(['.', '..'])
+from elftools.dwarf.lineprogram import LineProgram, LineState
+from elftools.dwarf.structs import DWARFStructs
+
+
+class TestLineProgram(unittest.TestCase):
+    def _make_program_in_stream(self, stream):
+        """ Create a LineProgram from the given program encoded in a stream
+        """
+        ds = DWARFStructs(little_endian=True, dwarf_format=32, address_size=4)
+        header = ds.Dwarf_lineprog_header.parse(
+            '\x04\x10\x00\x00' +    # initial lenght
+            '\x03\x00' +            # version
+            '\x20\x00\x00\x00' +    # header length
+            '\x01\x01\x01\x0F' +    # flags
+            '\x0A' +                # opcode_base
+            '\x00\x01\x04\x08\x0C\x01\x01\x01\x00' + # standard_opcode_lengths
+            # 2 dir names followed by a NULL
+            '\x61\x62\x00\x70\x00\x00' + 
+            # a file entry
+            '\x61\x72\x00\x0C\x0D\x0F' + 
+            # and another entry
+            '\x45\x50\x51\x00\x86\x12\x07\x08' +
+            # followed by NULL
+            '\x00')
+
+        lp = LineProgram(header, stream, ds, 0, len(stream.getvalue()))
+        return lp
+        
+    def assertLineState(self, state, **kwargs):
+        """ Assert that the state attributes specified in kwargs have the given
+            values (the rest are default).
+        """
+        for k, v in kwargs.iteritems():
+            self.assertEqual(getattr(state, k), v)
+        
+    def test_spec_sample_59(self):
+        # Sample in figure 59 of DWARFv3
+        s = StringIO()
+        s.write(
+            '\x02\xb9\x04' +
+            '\x0b' +
+            '\x38' +
+            '\x82' +
+            '\x73' +
+            '\x02\x02' +
+            '\x00\x01\x01')
+
+        lp = self._make_program_in_stream(s)
+        linetable = lp.get_line_table()
+
+        self.assertLineState(linetable[0], address=0x239, line=3)
+        self.assertLineState(linetable[1], address=0x23c, line=5)
+        self.assertLineState(linetable[2], address=0x244, line=6)
+        self.assertLineState(linetable[3], address=0x24b, line=7, end_sequence=False)
+        self.assertLineState(linetable[4], address=0x24d, line=7, end_sequence=True)
+
+    def test_spec_sample_60(self):
+        # Sample in figure 60 of DWARFv3
+        s = StringIO()
+        s.write(
+            '\x09\x39\x02' +
+            '\x0b' +
+            '\x09\x03\x00' +
+            '\x0b' +
+            '\x09\x08\x00' +
+            '\x0a' +
+            '\x09\x07\x00' +
+            '\x0a' +
+            '\x09\x02\x00' +
+            '\x00\x01\x01')
+
+        lp = self._make_program_in_stream(s)
+        linetable = lp.get_line_table()
+
+        self.assertLineState(linetable[0], address=0x239, line=3)
+        self.assertLineState(linetable[1], address=0x23c, line=5)
+        self.assertLineState(linetable[2], address=0x244, line=6)
+        self.assertLineState(linetable[3], address=0x24b, line=7, end_sequence=False)
+        self.assertLineState(linetable[4], address=0x24d, line=7, end_sequence=True)
+
+
+if __name__ == '__main__':
+    unittest.main()
+
index 632aa663c48a8bfa390bb0c283ee50ff2deb68fc..a1e30d75a60083d6cd392733765328037b58e546 100644 (file)
@@ -9,20 +9,20 @@ class TestDWARFStructs(unittest.TestCase):
         ds = DWARFStructs(little_endian=True, dwarf_format=32, address_size=4)
 
         c = ds.Dwarf_lineprog_header.parse(
-                '\x04\x10\x00\x00' +    # initial lenght
-                '\x05\x02' +            # version
-                '\x20\x00\x00\x00' +    # header length
-                '\x05\x10\x40\x50' +    # until and including line_range
-                '\x06' +                # opcode_base
-                '\x00\x01\x04\x08\x0C' + # standard_opcode_lengths
-                # 2 dir names followed by a NULL
-                '\x61\x62\x00\x70\x00\x00' + 
-                # a file entry
-                '\x61\x72\x00\x0C\x0D\x0F' + 
-                # and another entry
-                '\x45\x50\x51\x00\x86\x12\x07\x08' +
-                # followed by NULL
-                '\x00')
+            '\x04\x10\x00\x00' +    # initial lenght
+            '\x05\x02' +            # version
+            '\x20\x00\x00\x00' +    # header length
+            '\x05\x10\x40\x50' +    # until and including line_range
+            '\x06' +                # opcode_base
+            '\x00\x01\x04\x08\x0C' + # standard_opcode_lengths
+            # 2 dir names followed by a NULL
+            '\x61\x62\x00\x70\x00\x00' + 
+            # a file entry
+            '\x61\x72\x00\x0C\x0D\x0F' + 
+            # and another entry
+            '\x45\x50\x51\x00\x86\x12\x07\x08' +
+            # followed by NULL
+            '\x00')
 
         self.assertEqual(c.version, 0x205)
         self.assertEqual(c.opcode_base, 6)
diff --git a/z.py b/z.py
index fed0ab7b933e681ec7a87a81b8c01ba4ebe45769..632f37605d7ade9144c3158009243c0c9c16cc75 100644 (file)
--- a/z.py
+++ b/z.py
@@ -24,9 +24,9 @@ print '===> %s sections!' % efile.num_sections()
 dwarfinfo = efile.get_dwarf_info()
 CUs = list(dwarfinfo.iter_CUs())
 print 'num CUs:', len(CUs)
-print 'first CU:', CUs[0]
+print 'CU:', CUs[2]
 
-lp = dwarfinfo.line_program_for_CU(CUs[0])
+lp = dwarfinfo.line_program_for_CU(CUs[2])
 print 'lp:', lp, lp.header
 print 'linetable:', lp.get_line_table()
 #for lp in dwarfinfo.iter_line_programs():