Improved command `btx` (backtrace)
authorJan Vrany <jan.vrany@fit.cvut.cz>
Sat, 30 Nov 2013 22:31:53 +0000
changeset 2 ef575a931434
parent 1 406b61c83976
child 3 b3e318335eed
Improved command `btx` (backtrace) Detects stc-compiled method and show their frames Class >> selector instead of showing stx-mangled C function name. Also catches an error and prints backtrace in case there's problem in the code (unhandled exception etc)
stx/gdb/__init__.py
stx/gdb/commands.py
stx/gdb/load.py
stx/gdb/objects/__init__.py
stx/gdb/objects/stx_libbasic.py
stx/support.py
stx/util.py
tapscripts/stx-jvm-stats-simple.stp
tapscripts/stx-jvm-stats.stp
tapset/stx_librun.stp
--- a/stx/gdb/__init__.py	Thu Nov 21 00:55:50 2013 +0000
+++ b/stx/gdb/__init__.py	Sat Nov 30 22:31:53 2013 +0000
@@ -1,1 +1,27 @@
-import stx.gdb.commands
\ No newline at end of file
+import stx.gdb.commands
+import stx.gdb.objects
+import gdb.types
+
+def printer_lookup(val):
+    try:
+        v = val
+        t = val.type.strip_typedefs();
+        if (t.code == gdb.TYPE_CODE_PTR):                
+            t = t.target()
+        else: 
+            return None
+        if (t.code == gdb.TYPE_CODE_STRUCT):
+            name = str(t)
+            if (name == 'struct __instance'):
+                return stx.gdb.objects.create(val)
+    except:
+        return None
+
+
+def printer_register(objfile):
+    objfile.pretty_printers.append(printer_lookup)
+
+
+
+
+
--- a/stx/gdb/commands.py	Thu Nov 21 00:55:50 2013 +0000
+++ b/stx/gdb/commands.py	Sat Nov 30 22:31:53 2013 +0000
@@ -1,5 +1,7 @@
 import gdb
 import stx.support
+import stx.util
+
 
 class CFrameDecorator ( stx.support.Decorator ):
     """An abstract gdb.Frame decorator"""
@@ -9,6 +11,10 @@
         if frame.function().name == '__jInterpret':
             return CFrameDecorator_jInterpret(frame)
         else:
+            c , s = stx.util.demangle(frame.name())
+            if (c != None):
+                if (s != None):
+                    return CFrameDecorator_stcMethod(frame, c, s) 
             return CFrameDecorator(frame)
     
     def __str__(self):
@@ -63,6 +69,23 @@
 
     def kind(self):
         return "J-I"
+
+class CFrameDecorator_stcMethod ( CFrameDecorator ):
+    def __init__(self, frame, className = None, selectorValue = None):
+        CFrameDecorator.__init__(self, frame)
+        c = className
+        s = selectorValue
+        if (c == None or s == None):
+            c, s = stx.util.demangle(frame.name())
+        self.klassName = c
+        self.selectorValue = s 
+
+    def name(self):
+        return "%s >> #%s" % ( self.klassName, self.selectorValue )
+
+
+    def kind(self):
+        return "S-S"
         
 class Backtrace ( gdb.Command ):
     '''
@@ -73,23 +96,28 @@
     '''
     
     def __init__ ( self ):
-        super (Backtrace, self).__init__("bt", gdb.COMMAND_STACK)
+        super (Backtrace, self).__init__("btx", gdb.COMMAND_STACK)
     
     def invoke ( self , args , from_tty ):
-        argv = gdb.string_to_argv(args)
-        limit = None
-        if len(argv) == 1:
-            limit = int(argv[0])
-        fno = 0        
-        frame = CFrameDecorator.decorate(gdb.newest_frame())
-        framesel = gdb.selected_frame()
-        while ( frame != None ) and ( limit == None or fno < limit ):            
-            if frame.target() == framesel:
-                star = '*'
-            else:
-                star = ' '
-            print "%s%3d %s" % (star, fno , frame )
-            frame = frame.older()
-            fno = fno +1
+        try:
+            argv = gdb.string_to_argv(args)
+            limit = None
+            if len(argv) == 1:
+                limit = int(argv[0])
+            fno = 0        
+            frame = CFrameDecorator.decorate(gdb.newest_frame())
+            framesel = gdb.selected_frame()
+            while ( frame != None ) and ( limit == None or fno < limit ):            
+                if frame.target() == framesel:
+                    star = '*'
+                else:
+                    star = ' '
+                print "%s%3d %s" % (star, fno , frame )
+                frame = frame.older()
+                fno = fno +1
+        except:
+            import traceback
+            import sys
+            traceback.print_exc(file=sys.stdout)
 
 Backtrace()
\ No newline at end of file
--- a/stx/gdb/load.py	Thu Nov 21 00:55:50 2013 +0000
+++ b/stx/gdb/load.py	Sat Nov 30 22:31:53 2013 +0000
@@ -6,4 +6,6 @@
 
     sys.path.append(inc_dir)
 
-    import stx.gdb
\ No newline at end of file
+    import stx.gdb
+
+    stx.gdb.printer_register(gdb)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stx/gdb/objects/__init__.py	Sat Nov 30 22:31:53 2013 +0000
@@ -0,0 +1,160 @@
+import gdb
+import stx.support
+
+class __instance ( object ):
+    """
+    Generic object representation. Instance of this class
+    also implements gdb pretty-printing API and could therefore
+    used as a pretty printer
+    """
+
+    def __init__(self, val):
+        #stx.support.Decorator.__init__(self, val)
+        #object.__setattr__(self, "_klass", None)
+        self._obj = val
+        self._klass = None
+
+    def display_hint(self):
+        '''
+        Pretty printer API
+        '''
+        return None
+
+    def to_string(self):
+        '''
+        Pretty printer API: Returns a human-readable represenation of the object.
+        '''
+        return self.display_string()
+
+    def __repr__(self):
+        return self.display_string()
+    
+    def display_string(self):
+        try:
+            value = self.display_value()
+            klassName = '???'
+            
+            klass = self.klass
+            if klass == None:
+                klassName = '?None?'
+            else:
+                klassName = klass.name
+            if (value == None):
+                return "0x%08x (%s)" % ( self.address , klassName )            
+            else:
+                return "0x%08x (%s %s)" % ( self.address , klassName , value )
+        except:
+            return "0x%08x (** err **)" % self.address
+
+    def display_value(self):
+        '''
+        Return a human-readable represenation of object's value, if sensible.
+        FOr instance, for integer or string it may be its respective value
+        '''
+        return None
+
+    @property
+    def address(self):
+        '''Return the address of the object in memory'''
+        return long(self._obj);
+
+    @property
+    def klass(self):
+        '''Return this object's class as an __instance value'''
+        if (self._klass == None):
+            self._klass = create(self._obj['o_class'])
+        return self._klass
+
+    @property
+    def size(self):
+        '''Return size of the objects in slots (excluding header)'''
+        # Not really 64bit safe...
+        return (self.size_b() - 12) / 4
+    
+    def size_b(self):
+        '''Return size of the object in bytes, including header'''
+        return self._obj['o_size']                
+
+    @property
+    def flags(self):
+        return self._obj['o_flags']
+
+
+# Some flags defined in stc.h. 
+# !!! MAKE SURE they are in sync !!!
+
+BYTEARRAY       = 1
+WORDARRAY       = 2
+LONGARRAY       = 3
+POINTERARRAY    = 4
+WKPOINTERARRAY  = 5
+FLOATARRAY      = 6
+DOUBLEARRAY     = 7
+SWORDARRAY      = 8
+SLONGARRAY      = 9
+LONGLONGARRAY   = 10
+SLONGLONGARRAY  = 11
+#
+# reserved:       13-15
+# 
+
+ARRAYMASK       = 0x0F
+
+BEHAVIOR_INSTS  = 0x0010
+FLOAT_INSTS     = 0x0020  
+BLOCK_INSTS     = 0x0040  
+METHOD_INSTS    = 0x0080  
+CONTEXT_INSTS   = 0x0100  
+BCONTEXT_INSTS  = 0x0200  
+SYMBOL_INSTS    = 0x0400  
+
+NONOBJECT_INSTS = 0x0800  
+EXTERNALBYTES_INSTS = 0x1000 
+EXTFUNC_INSTS   = 0x010000
+
+
+
+def create(val):
+    import stx.gdb.objects.stx_libbasic
+    addr = long(val)
+    
+    # Here we should care for free/non objects
+    # by comparing address agains newspace
+    # boundaries. 
+    # Well, later :-)
+
+    if (addr == 0):
+        return stx.gdb.objects.stx_libbasic.nil
+
+    if ((addr & 1) == 1):
+        return stx.gdb.objects.stx_libbasic.SmallInteger(val)    
+    flags = long(val['o_class']['i_instvars'][1])
+    if ((flags & 1) == 1):
+        flags = flags >> 1
+    else:
+        raise Exception("Memory corruption? Class flags are not SmallInteger.")
+    
+    if ((flags & BEHAVIOR_INSTS) != 0):
+        # Care for anonymous classes    
+        size = (val['o_size'] - 12) / 4 # Not 64bit safe        
+        if (size == 5):
+            return stx.gdb.objects.stx_libbasic.Behavior(val)
+        if (size == 6):
+            return stx.gdb.objects.stx_libbasic.ClassDescription(val)
+        if (size >= 17):
+            return stx.gdb.objects.stx_libbasic.Class(val)            
+        if (size >= 7):
+            return stx.gdb.objects.stx_libbasic.Metaclass(val)            
+
+        raise Exception("Funny behaviour-like size (%s)" % size)
+
+    if ((flags & SYMBOL_INSTS) != 0):
+        return stx.gdb.objects.stx_libbasic.Symbol(val)
+
+    return __instance(val)
+
+
+
+
+
+ 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stx/gdb/objects/stx_libbasic.py	Sat Nov 30 22:31:53 2013 +0000
@@ -0,0 +1,123 @@
+# Deinitions for some stx:libbasic classes
+#
+
+import gdb
+import stx.gdb.objects
+
+
+class Object( stx.gdb.objects.__instance ):
+    None
+
+class UndefinedObject ( stx.gdb.objects.__instance ):
+    def __init__(self, val):
+        #stx.support.Decorator.__init__(self, val)
+        #object.__setattr__(self, "_klass", None)
+        self._obj = None
+        self._klass = None
+
+    def display_value(self):
+        '''
+        Return a human-readable represenation of object's value, if sensible.
+        FOr instance, for integer or string it may be its respective value
+        '''
+        return 'nil'
+
+    def display_string(self):
+        return 'nil'
+
+    @property
+    def address(self):
+        return 0
+    
+    @property
+    def klass(self):
+        '''Return this object's class as an __instance value'''
+        if (self._klass == None):
+            self._klass = stx.gdb.objects.create(gdb.parse_and_eval('UndefinedObject'))
+        return self._klass
+
+    @property
+    def size(self):
+        return 0
+
+    @property
+    def size_b(self):
+        return 0
+
+nil = UndefinedObject(None)
+
+
+class SmallInteger ( stx.gdb.objects.__instance ):
+    def display_value(self):
+        '''
+        Return a human-readable represenation of object's value, if sensible.
+        FOr instance, for integer or string it may be its respective value
+        '''
+        return self.address >> 1
+
+    @property
+    def klass(self):
+        '''Return this object's class as an __instance value'''
+        if (self._klass == None):
+            self._klass = Class(gdb.parse_and_eval('__GLOBAL_GET_BY_NAME("SmallInteger")'))
+        return self._klass
+
+    @property
+    def size(self):
+        return 0
+
+    @property
+    def size_b(self):
+        return 0
+
+    def display_string(self):
+        value = self.display_value()
+        return "0x%08x (SmallInteger %s)" % ( self.address , value )
+
+
+
+
+class Behavior ( Object ):
+    @property
+    def superclass(self):
+        if (self._superclass == None):
+            self._superclass = stx.gdb.objects.create(self._obj['i_instvars'][0])
+        return self._superclass
+
+
+class ClassDescription ( Behavior ):
+    @property 
+    def instvars(self):
+        return []
+
+        
+class Class ( ClassDescription ):
+    @property 
+    def name(self):
+        try:
+            return self._obj.cast(gdb.lookup_type('struct __instance').pointer())['i_instvars'][6].cast(gdb.lookup_type('struct __Symbol').pointer())['s_element'].cast(gdb.lookup_type('char').pointer()).string()
+        except:
+            return "** invalid class name **"
+
+class Metaclass ( ClassDescription ):
+    @property 
+    def name(self):
+        try:
+            return '%s class' % self._obj.cast(gdb.lookup_type('struct __instance').pointer())['i_instvars'][6].cast(gdb.lookup_type('struct __instance').pointer())['i_instvars'][6].cast(gdb.lookup_type('struct __Symbol').pointer())['s_element'].cast(gdb.lookup_type('char').pointer()).string()
+        except:
+            return "** invalid class x.name ** class"
+
+
+class Symbol ( Object ):
+    def __init__(self, val):
+        self._obj = val.cast(gdb.lookup_type('struct __Symbol').pointer())
+        self._klass = None
+
+
+    def display_value(self):
+        '''
+        Return a human-readable represenation of object's value, if sensible.
+        For instance, for integer or string it may be its respective value
+        '''
+        return '#' + self._obj['s_element'].cast(gdb.lookup_type('char').pointer()).string()
+
--- a/stx/support.py	Thu Nov 21 00:55:50 2013 +0000
+++ b/stx/support.py	Sat Nov 30 22:31:53 2013 +0000
@@ -1,3 +1,5 @@
+import types
+
 class Decorator ( object ):
     '''
     Generic decorator class
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stx/util.py	Sat Nov 30 22:31:53 2013 +0000
@@ -0,0 +1,48 @@
+import re
+
+STC_METHOD_FUNC_MATCHER1 = re.compile("__x___M_\d+_([A-Za-z_]\w*)_(_.*)")
+STC_METHOD_FUNC_MATCHER2 = re.compile("__M_\d+_([A-Za-z_]\w*)_(_.*)")
+STC_BLOCK_FUNC_MATCHER  = re.compile("([A-Za-z_]\w*)__B_\d+")
+
+STC_DEMANGLE_TABLE_1 = {
+    'pl'  : '+', 'mi'  : '-',
+    'mu'  : '*', 'di'  : '/',
+    'co'  : ',', 'at'  : '@', 
+    'le'  : '<', 'gr'  : '>',
+    'eq'  : '=', 'ne'  : '~',
+    'pi'  : '|', 'mo'  : '\\',
+    'am'  : '&'
+}
+
+def demangle(fname):
+    """
+    Given a C function name mangled by STX, returns tupe (classname, selector). 
+    For block functions, return tuple (classname, None) as stc does not mangle
+    method name into block C function name. If given fname is not mangled 
+    method name nor block function name, return tupe (None, None).
+    """
+
+    def selector(cselector):
+        """
+        Demangle given selector into it's Smalltalk form. 
+        See http://live.exept.de/doc/online/english/programming/primitive.html#ACCESS_SYMBOLS
+        """
+
+
+        sselector = cselector[1:]
+        return re.sub("_", ":", sselector)
+
+
+    match = STC_METHOD_FUNC_MATCHER1.match(fname)
+    if match != None:
+        return ( match.group(1) , selector(match.group(2)) )
+    match = STC_METHOD_FUNC_MATCHER2.match(fname)
+    if match != None:
+        return ( match.group(1) , selector(match.group(2)) )
+ 
+    match = STC_BLOCK_FUNC_MATCHER.match(fname)
+    if match != None:
+        return ( match.group(1) , None )
+    return ( None, None )
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tapscripts/stx-jvm-stats-simple.stp	Sat Nov 30 22:31:53 2013 +0000
@@ -0,0 +1,18 @@
+global stats;
+
+probe process("librun.so").function("__monitorEnter") {
+    if ( pid() != target() ) next;
+    stats["MONITOR ENTER"] <<< 1;
+}
+
+probe process("librun.so").function("__monitorExit") {
+    if ( pid() != target() ) next;
+    stats["MONITOR EXIT"] <<< 1;
+}
+
+probe end {
+    printf("=== STX-JVM Performance Statistics ===\n");
+    printf("\n");
+    printf("MONITOR ENTER: %10d\n",   @count(stats["MONITOR ENTER"]));
+    printf("MONITOR EXIT:  %10d\n",  @count(stats["MONITOR EXIT"]));
+}
\ No newline at end of file
--- a/tapscripts/stx-jvm-stats.stp	Thu Nov 21 00:55:50 2013 +0000
+++ b/tapscripts/stx-jvm-stats.stp	Sat Nov 30 22:31:53 2013 +0000
@@ -1,6 +1,5 @@
 global stats;
-
-
+global methods;
 
 probe stx.jvm.monitorenter {
     if ( pid() != target() ) next;
@@ -30,6 +29,13 @@
 probe stx.jvm.method.interpret {
     if ( pid() != target() ) next;
     stats["Methods interpreted"] <<< 1;
+
+#   if (! methods[$__selector] ) {
+#        methods[$__selector] = 1;
+#        stats["Methods interpreted (distinct)"] <<< 1;
+#
+#    }
+
 }
 
 probe stx.jvm.class.loaded {
@@ -37,10 +43,23 @@
     stats["Classes loaded"] <<< 1;
 }
 
+
+
+probe stx.gc.scavenge.end {
+    if ( pid() != target() ) next;
+
+    freed = @cast(@var("P__newEndPtr@globals.c"), "int") - @cast(@var("P__newNextPtr@globals.c"), "int")
+    stats["Scavenge"] <<< freed;
+}
+
+
 function print_counter(name) {    
     printf("  %-35s : %10d\n", name, @count(stats[name]));
 }
 
+function print_avg(name) {    
+    printf("  %-29s (avg) : %10d\n", name, @avg(stats[name]));
+}
 
 probe end {
     
@@ -49,6 +68,7 @@
 
     printf(" -- Execution --\n");
     print_counter("Methods interpreted");
+    print_counter("Methods interpreted( distinct)");
     printf("\n");
 
     printf(" -- Classes --\n");
@@ -64,6 +84,17 @@
     print_counter("MONITOR ENTER (fat)");
     print_counter("MONITOR EXIT");
     print_counter("MONITOR EXIT (fat)");
+    printf("\n");
+
+    printf(" -- GC --\n");
+    print_counter("Scavenge");
+    print_avg("Scavenge");
+    print("Scavenge Histogram");
+    print(@hist_linear(stats["Scavenge"], 0, 2076672, 32448));
+    
+
+    
+
 
 
 }
\ No newline at end of file
--- a/tapset/stx_librun.stp	Thu Nov 21 00:55:50 2013 +0000
+++ b/tapset/stx_librun.stp	Sat Nov 30 22:31:53 2013 +0000
@@ -9,5 +9,9 @@
 probe stx.jvm.monitorenter = stx.librun.function("__monitorEnter") { }
 probe stx.jvm.monitorexit  = stx.librun.function("__monitorExit") { }
 
+probe stx.jvm.method.interpret = stx.librun.function("__jInterpret") { }
 
-probe stx.jvm.method.interpret = stx.librun.function("__jInterpret") { }
+
+probe stx.gc.scavenge.begin = stx.librun.function("scavenge") { }
+probe stx.gc.scavenge.end   = stx.librun.function("scavenge@new.c:13829") { }
+probe stx.gc.scavenge       = stx.gc.scavenge.begin { }