Added string dumping for location expressions, with tests
authoreliben <devnull@localhost>
Sun, 6 Nov 2011 13:27:57 +0000 (15:27 +0200)
committereliben <devnull@localhost>
Sun, 6 Nov 2011 13:27:57 +0000 (15:27 +0200)
.hgignore
elftools/dwarf/location_expr.py
tests/test_dwarf_location_expr.py

index bb6d5898e79c054d2694770dac8536b337bf2fde..f1dca2eae9821c4518262a820af6f76aee532ad3 100644 (file)
--- a/.hgignore
+++ b/.hgignore
@@ -1,3 +1,6 @@
 syntax: glob
 
 *.pyc
+.coverage
+htmlcov
+
index da1a81985af17a3ca3f9ded0fcf9c368ea4aa99a..0040559c9392d9c2961e3ea79c87fb5d0e8bccb5 100644 (file)
@@ -11,8 +11,7 @@ from cStringIO import StringIO
 from ..common.utils import struct_parse, bytelist2string
 
 
-# Location expression opcodes. 
-#
+# Location expression opcodes. name -> opcode mapping
 DW_OP_name2opcode = dict(
     DW_OP_addr=0x03,
     DW_OP_deref=0x06,
@@ -86,20 +85,20 @@ _generate_dynamic_values(DW_OP_name2opcode, 'DW_OP_lit', 0, 31, 0x30)
 _generate_dynamic_values(DW_OP_name2opcode, 'DW_OP_reg', 0, 31, 0x50)
 _generate_dynamic_values(DW_OP_name2opcode, 'DW_OP_breg', 0, 31, 0x70)
 
+# opcode -> name mapping
 DW_OP_opcode2name = dict((v, k) for k, v in DW_OP_name2opcode.iteritems())
 
 
 class GenericLocationExprVisitor(object):
     def __init__(self, structs):
         self.structs = structs
-        self.stream = None
         self._init_dispatch_table()
-
+        self.stream = None
         self._cur_opcode = None
         self._cur_opcode_name = None
         self._cur_args = []
 
-    def process_expr(self, expr):
+    def process_expr(self, loc_expr):
         """ Process (visit) a location expression. Currently two possible
             types are supported for expr:
 
@@ -107,11 +106,11 @@ class GenericLocationExprVisitor(object):
             2. List of byte values (the result of parsed DW_FORM_block*
                attributes).
         """
-        if hasattr(expr, 'read') and hasattr(expr, 'seek'):
+        if hasattr(loc_expr, 'read') and hasattr(loc_expr, 'seek'):
             # looks like a stream
-            self.stream = expr
+            self.stream = loc_expr
         else:
-            self.stream = StringIO(bytelist2string(expr))
+            self.stream = StringIO(bytelist2string(loc_expr))
 
         while True:
             # Get the next opcode from the stream. If nothing is left in the
@@ -122,7 +121,8 @@ class GenericLocationExprVisitor(object):
 
             # Decode the opcode and its name
             self._cur_opcode = ord(byte)
-            self._cur_opcode_name = DW_OP_opcode2name[self._cur_opcode]
+            self._cur_opcode_name = DW_OP_opcode2name.get(
+                self._cur_opcode, 'OP:0x%x' % self._cur_opcode)
             # Will be filled in by visitors
             self._cur_args = [] 
 
@@ -136,11 +136,11 @@ class GenericLocationExprVisitor(object):
             self._after_visit(
                     self._cur_opcode, self._cur_opcode_name, self._cur_args)
 
-    def _after_visit(self, opcode, opcode_name, *args):
-        raise NotImplementedError()
+    def _after_visit(self, opcode, opcode_name, args):
+        pass
         
     def _default_visitor(self, opcode, opcode_name):
-        raise NotImplementedError()
+        pass
         
     def _visit_OP_with_no_args(self, opcode, opcode_name):
         self._cur_args = []
@@ -153,7 +153,7 @@ class GenericLocationExprVisitor(object):
         """ Create a visitor method for an opcode that that accepts a single
             argument, specified by a struct.
         """
-        def visitor(self, opcode, opcode_name):
+        def visitor(opcode, opcode_name):
             self._cur_args = [struct_parse(struct_arg, self.stream)]
         return visitor
 
@@ -161,7 +161,7 @@ class GenericLocationExprVisitor(object):
         """ Create a visitor method for an opcode that that accepts two
             arguments, specified by structs.
         """
-        def visitor(self, opcode, opcode_name):
+        def visitor(opcode, opcode_name):
             self._cur_args = [
                 struct_parse(struct_arg1, self.stream),
                 struct_parse(struct_arg2, self.stream)]
@@ -186,9 +186,13 @@ class GenericLocationExprVisitor(object):
         add('DW_OP_const4s', 
             self._make_visitor_arg_struct(self.structs.Dwarf_int32('')))
         add('DW_OP_const8u', 
-            self._make_visitor_arg_struct(self.structs.Dwarf_uint64('')))
+            self._make_visitor_arg_struct2(
+                self.structs.Dwarf_uint32(''),
+                self.structs.Dwarf_uint32('')))
         add('DW_OP_const8s', 
-            self._make_visitor_arg_struct(self.structs.Dwarf_int64('')))
+            self._make_visitor_arg_struct2(
+                self.structs.Dwarf_int32(''),
+                self.structs.Dwarf_int32('')))
         add('DW_OP_constu',
             self._make_visitor_arg_struct(self.structs.Dwarf_uleb128('')))
         add('DW_OP_consts',
@@ -210,7 +214,7 @@ class GenericLocationExprVisitor(object):
                         'DW_OP_xor', 'DW_OP_eq', 'DW_OP_ge', 'DW_OP_gt',
                         'DW_OP_le', 'DW_OP_lt', 'DW_OP_ne', 'DW_OP_nop',
                         'DW_OP_push_object_address', 'DW_OP_form_tls_address',
-                        'DW_OP_call_frame_cfa', 'DW_OP_stack_value',]:
+                        'DW_OP_call_frame_cfa']:
             add(opname, self._visit_OP_with_no_args)
 
         for n in range(0, 32):
@@ -222,13 +226,13 @@ class GenericLocationExprVisitor(object):
         add('DW_OP_regx',
             self._make_visitor_arg_struct(self.structs.Dwarf_uleb128('')))
         add('DW_OP_bregx',
-            self._make_visitor_arg_struct(
+            self._make_visitor_arg_struct2(
                 self.structs.Dwarf_uleb128(''),
                 self.structs.Dwarf_sleb128('')))
         add('DW_OP_piece',
             self._make_visitor_arg_struct(self.structs.Dwarf_uleb128('')))
         add('DW_OP_bit_piece',
-            self._make_visitor_arg_struct(
+            self._make_visitor_arg_struct2(
                 self.structs.Dwarf_uleb128(''),
                 self.structs.Dwarf_uleb128('')))
         add('DW_OP_deref_size',
@@ -243,3 +247,46 @@ class GenericLocationExprVisitor(object):
             self._make_visitor_arg_struct(self.structs.Dwarf_offset('')))
 
 
+class LocationExpressionDumper(GenericLocationExprVisitor):
+    def __init__(self, structs):
+        super(LocationExpressionDumper, self).__init__(structs)
+        self._init_lookups()
+        self._str_parts = []
+    
+    def get_str(self):
+        return '; '.join(self._str_parts)
+
+    def _init_lookups(self):        
+        self._ops_with_decimal_arg = set([
+            'DW_OP_const1u', 'DW_OP_const1s', 'DW_OP_const2u', 'DW_OP_const2s',
+            'DW_OP_const4u', 'DW_OP_const4s', 'DW_OP_constu', 'DW_OP_consts',
+            'DW_OP_pick', 'DW_OP_plus_uconst', 'DW_OP_bra', 'DW_OP_skip',
+            'DW_OP_fbreg', 'DW_OP_piece', 'DW_OP_deref_size',
+            'DW_OP_xderef_size', 'DW_OP_regx', 'DW_OP_fbreg', ])
+        
+        for n in range(0, 32):
+            self._ops_with_decimal_arg.add('DW_OP_breg%s' % n)
+        
+        self._ops_with_two_decimal_args = set([
+            'DW_OP_const8u', 'DW_OP_const8s', 'DW_OP_bregx', 'DW_OP_bit_piece'])
+
+        self._ops_with_hex_arg = set(
+            ['DW_OP_addr', 'DW_OP_call2', 'DW_OP_call4', 'DW_OP_call_ref'])
+
+    def _after_visit(self, opcode, opcode_name, args):
+        self._str_parts.append(self._dump_to_string(opcode, opcode_name, args))
+
+    def _dump_to_string(self, opcode, opcode_name, args):
+        if len(args) == 0:
+            return opcode_name
+        elif opcode_name in self._ops_with_decimal_arg:
+            return '%s: %s' % (opcode_name, args[0])
+        elif opcode_name in self._ops_with_hex_arg:
+            return '%s: %x' % (opcode_name, args[0])
+        elif opcode_name in self._ops_with_two_decimal_args:
+            return '%s: %s %s' % (opcode_name, args[0], args[1])
+        else:
+            return '<unknown %s>' % opcode_name
+
+
+
index 7949a07d4defb3cf651e9dea7e14b9de96656c72..b6c219815f003abe7e7201d692ee9c978855499c 100644 (file)
@@ -1,30 +1,54 @@
 import sys, unittest
+from cStringIO import StringIO
 
 sys.path.extend(('..', '.'))
-from elftools.dwarf.location_expr import (
-        GenericLocationExprVisitor, DW_OP_opcode2name)
+from elftools.dwarf.location_expr import LocationExpressionDumper
 from elftools.dwarf.structs import DWARFStructs
 
 
-class MyTestVisitor(GenericLocationExprVisitor):
-    def __init__(self, structs):
-        super(MyTestVisitor, self).__init__(structs)
-        self.results = []
-        
-    def _after_visit(self, opcode, opcode_name, *args):
-        self.results.append((opcode_name, args))
-        
-
-class TestGenericLocationExprVisitor(unittest.TestCase):
+class TestLocationExpressionDumper(unittest.TestCase):
     structs32 = DWARFStructs(
             little_endian=True,
             dwarf_format=32,
             address_size=4)
 
-    def test_basic(self):
-        visitor = MyTestVisitor(self.structs32)
-        visitor.process_expr([0x03, 0x01, 0x02, 0, 0, 0x06, 0x06])
-        print visitor.results
+    def setUp(self):
+        self.visitor = LocationExpressionDumper(self.structs32)
+
+    def test_basic_single(self):
+        self.visitor.process_expr([0x1b])
+        self.assertEqual(self.visitor.get_str(),
+            'DW_OP_div')
+        
+        self.setUp()
+        self.visitor.process_expr([0x74, 0x82, 0x01])
+        self.assertEqual(self.visitor.get_str(),
+            'DW_OP_breg4: 130')
+
+        self.setUp()
+        self.visitor.process_expr([0x9d, 0x8f, 0x0A, 0x90, 0x01])
+        self.assertEqual(self.visitor.get_str(),
+            'DW_OP_bit_piece: 1295 144')
+
+    def test_basic_sequence(self):
+        self.visitor.process_expr([0x03, 0x01, 0x02, 0, 0, 0x06, 0x06])
+        self.assertEqual(self.visitor.get_str(),
+            'DW_OP_addr: 201; DW_OP_deref; DW_OP_deref')
+
+        self.setUp()
+        self.visitor.process_expr([0x15, 0xFF, 0x0b, 0xf1, 0xff])
+        self.assertEqual(self.visitor.get_str(),
+            'DW_OP_pick: 255; DW_OP_const2s: -15')
+
+        self.setUp()
+        self.visitor.process_expr([0x1d, 0x1e, 0x1d, 0x1e, 0x1d, 0x1e])
+        self.assertEqual(self.visitor.get_str(),
+            'DW_OP_mod; DW_OP_mul; DW_OP_mod; DW_OP_mul; DW_OP_mod; DW_OP_mul')
+
+    def test_stream_input(self):
+        self.visitor.process_expr(StringIO('\x1b'))
+        self.assertEqual(self.visitor.get_str(),
+            'DW_OP_div')
 
 
 if __name__ == '__main__':