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)
--- 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 { }