[pypy-svn] r13615 - in pypy/dist/pypy/translator/c: . test

arigo at codespeak.net arigo at codespeak.net
Mon Jun 20 14:06:26 CEST 2005


Author: arigo
Date: Mon Jun 20 14:06:21 2005
New Revision: 13615

Added:
   pypy/dist/pypy/translator/c/g_debuginfo.h   (contents, props changed)
   pypy/dist/pypy/translator/c/symboltable.py   (contents, props changed)
   pypy/dist/pypy/translator/c/test/test_symboltable.py   (contents, props changed)
Modified:
   pypy/dist/pypy/translator/c/g_module.h
   pypy/dist/pypy/translator/c/genc.py
   pypy/dist/pypy/translator/c/node.py
Log:
Enable insepction of the global variable's state in a GenC-generated C module.
The SymbolTable class provides a way to access each global by C name, and
presents a lltype-compatible read-only interface on the pointers.

Implemented by generating all struct offsets in the C source so that the
SymbolTable can find out where each structure field actually is in memory.  
Then it uses the C function debuginfo_peek() to read raw data, and the
'struct' module to convert it to Python integers.



Added: pypy/dist/pypy/translator/c/g_debuginfo.h
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/c/g_debuginfo.h	Mon Jun 20 14:06:21 2005
@@ -0,0 +1,42 @@
+
+/************************************************************/
+ /***  C header subsection: debugging info                 ***/
+
+/* NOTE: this is not included by <g_include.h>.
+   The #include is generated manually if needed. */
+
+#undef METHODDEF_DEBUGINFO
+#define METHODDEF_DEBUGINFO						\
+		{ "debuginfo_offset", debuginfo_offset, METH_VARARGS },	\
+		{ "debuginfo_global", debuginfo_global, METH_VARARGS },	\
+		{ "debuginfo_peek",   debuginfo_peek,   METH_VARARGS },
+
+
+static PyObject *debuginfo_offset(PyObject *self, PyObject *args)
+{
+	int index;
+	if (!PyArg_ParseTuple(args, "i", &index))
+		return NULL;
+	return PyInt_FromLong(debuginfo_offsets[index]);
+}
+
+static PyObject *debuginfo_global(PyObject *self, PyObject *args)
+{
+	int index;
+	if (!PyArg_ParseTuple(args, "i", &index))
+		return NULL;
+	return PyLong_FromVoidPtr(debuginfo_globals[index]);
+}
+
+static PyObject *debuginfo_peek(PyObject *self, PyObject *args)
+{
+	PyObject *o;
+	int size;
+	void *start;
+	if (!PyArg_ParseTuple(args, "Oi", &o, &size))
+		return NULL;
+	start = PyLong_AsVoidPtr(o);
+	if (PyErr_Occurred())
+		return NULL;
+	return PyString_FromStringAndSize((char *)start, size);
+}

Modified: pypy/dist/pypy/translator/c/g_module.h
==============================================================================
--- pypy/dist/pypy/translator/c/g_module.h	(original)
+++ pypy/dist/pypy/translator/c/g_module.h	Mon Jun 20 14:06:21 2005
@@ -2,19 +2,22 @@
 /************************************************************/
  /***  C header subsection: CPython-extension-module-ness  ***/
 
-
-#ifndef COUNT_OP_MALLOCS
-# define MODULE_INITFUNC(modname) \
-	static PyMethodDef my_methods[] = { (char *)NULL, (PyCFunction)NULL }; \
-	PyMODINIT_FUNC init##modname(void)
+#ifdef COUNT_OP_MALLOCS
+# define METHODDEF_MALLOC_COUNTERS	\
+		{ "malloc_counters", malloc_counters, METH_VARARGS },
 #else
-# define MODULE_INITFUNC(modname) \
-	static PyMethodDef my_methods[] = { \
-		{ "malloc_counters", malloc_counters }, \
-		{ (char *)NULL, (PyCFunction)NULL } }; \
-	PyMODINIT_FUNC init##modname(void)
+# define METHODDEF_MALLOC_COUNTERS	/* nothing */
 #endif
 
+#define METHODDEF_DEBUGINFO    /* nothing, unless overridden by g_debuginfo.h */
+
+#define MODULE_INITFUNC(modname)                        \
+	static PyMethodDef my_methods[] = {             \
+		METHODDEF_MALLOC_COUNTERS               \
+		METHODDEF_DEBUGINFO                     \
+		{ (char *)NULL, (PyCFunction)NULL } };  \
+	PyMODINIT_FUNC init##modname(void)
+
 #define SETUP_MODULE(modname)	\
 	PyObject *m = Py_InitModule(#modname, my_methods); \
 	PyModule_AddStringConstant(m, "__sourcefile__", __FILE__); \
@@ -26,7 +29,7 @@
 	if (RPythonError == NULL) \
 		return; \
 	PyModule_AddObject(m, "RPythonError", RPythonError); \
-	if (setup_globalfunctions(globalfunctiondefs) < 0) \
+	if (setup_globalfunctions(globalfunctiondefs, #modname) < 0) \
 		return;	\
 	if (setup_initcode(frozen_initcode, FROZEN_INITCODE_SIZE) < 0) \
 		return;	\
@@ -67,12 +70,15 @@
 	return 0;
 }
 
-static int setup_globalfunctions(globalfunctiondef_t* def)
+static int setup_globalfunctions(globalfunctiondef_t* def, char* modname)
 {
 	PyObject* fn;
+	PyObject* modname_o = PyString_FromString(modname);
+	if (modname_o == NULL)
+		return -1;
 
 	for (; def->p != NULL; def++) {
-		fn = PyCFunction_New(&def->ml, NULL);
+		fn = PyCFunction_NewEx(&def->ml, NULL, modname_o);
 		if (fn == NULL)
 			return -1;
 		fn->ob_type = &PyGenCFunction_Type;

Modified: pypy/dist/pypy/translator/c/genc.py
==============================================================================
--- pypy/dist/pypy/translator/c/genc.py	(original)
+++ pypy/dist/pypy/translator/c/genc.py	Mon Jun 20 14:06:21 2005
@@ -16,7 +16,8 @@
     return db, pf
 
 
-def genc(translator, targetdir=None, modulename=None, compile=True):
+def genc(translator, targetdir=None, modulename=None, compile=True,
+                                                      symtable=True):
     """Generate C code starting at the translator's entry point.
     The files are written to the targetdir if specified.
     If 'compile' is True, compile and return the new module.
@@ -31,12 +32,19 @@
     elif isinstance(targetdir, str):
         targetdir = py.path.local(targetdir)
     targetdir.ensure(dir=1)
+    if symtable:
+        from pypy.translator.c.symboltable import SymbolTable
+        symboltable = SymbolTable()
+    else:
+        symboltable = None
     cfile = gen_source(db, modulename, targetdir,
                        # defines={'COUNT_OP_MALLOCS': 1},
-                       exports = {translator.entrypoint.func_name: pf})
+                       exports = {translator.entrypoint.func_name: pf},
+                       symboltable = symboltable)
     if not compile:
         return cfile
     m = make_module_from_c(cfile, include_dirs = [autopath.this_dir])
+    symboltable.attach(m)   # hopefully temporary hack
     return m
 
 
@@ -87,7 +95,8 @@
             blank = True
 
 
-def gen_source(database, modulename, targetdir, defines={}, exports={}):
+def gen_source(database, modulename, targetdir, defines={}, exports={},
+                                                symboltable=None):
     if isinstance(targetdir, str):
         targetdir = py.path.local(targetdir)
     filename = targetdir.join(modulename + '.c')
@@ -107,6 +116,27 @@
     gen_readable_parts_of_main_c_file(f, database)
 
     #
+    # Debugging info
+    #
+    if symboltable:
+        print >> f
+        print >> f, '/*******************************************************/'
+        print >> f, '/***  Debugging info                                 ***/'
+        print >> f
+        print >> f, 'static int debuginfo_offsets[] = {'
+        for node in database.structdeflist:
+            for expr in symboltable.generate_type_info(database, node):
+                print >> f, '\t%s,' % expr
+        print >> f, '\t0 };'
+        print >> f, 'static void *debuginfo_globals[] = {'
+        for node in database.globalcontainers():
+            if not isinstance(node, PyObjectNode):
+                result = symboltable.generate_global_info(database, node)
+                print >> f, '\t%s,' % (result,)
+        print >> f, '\tNULL };'
+        print >> f, '#include "g_debuginfo.h"'
+
+    #
     # PyObject support (strange) code
     #
     pyobjmaker = database.pyobjmaker

Modified: pypy/dist/pypy/translator/c/node.py
==============================================================================
--- pypy/dist/pypy/translator/c/node.py	(original)
+++ pypy/dist/pypy/translator/c/node.py	Mon Jun 20 14:06:21 2005
@@ -26,6 +26,7 @@
     def __init__(self, db, STRUCT, varlength=1):
         self.db = db
         self.STRUCT = STRUCT
+        self.LLTYPE = STRUCT
         self.varlength = varlength
 
     def setup(self):
@@ -148,6 +149,11 @@
                                         FIELD_T):
                 yield line
 
+    def debug_offsets(self):
+        # generate number exprs giving the offset of the elements in the struct
+        for name, typename in self.fields:
+            yield 'offsetof(struct %s, %s)' % (self.name, name)
+
 
 class ArrayDefNode:
     refcount = None
@@ -156,6 +162,7 @@
     def __init__(self, db, ARRAY, varlength=1):
         self.db = db
         self.ARRAY = ARRAY
+        self.LLTYPE = ARRAY
         self.varlength = varlength
 
     def setup(self):
@@ -225,6 +232,16 @@
             yield '\t}'
             yield '}'
 
+    def debug_offsets(self):
+        # generate three offsets for debugging inspection
+        yield 'offsetof(struct %s, length)' % (self.name,)
+        if self.ARRAY.OF != Void:
+            yield 'offsetof(struct %s, items[0])' % (self.name,)
+            yield 'offsetof(struct %s, items[1])' % (self.name,)
+        else:
+            yield '-1'
+            yield '-1'
+
 
 def generic_dealloc(db, expr, T):
     if isinstance(T, Ptr) and T._needsgc():
@@ -446,6 +463,7 @@
         assert T == RuntimeTypeInfo
         assert isinstance(obj.about, GcStruct)
         self.db = db
+        self.T = T
         self.obj = obj
         defnode = db.gettypedefnode(obj.about)
         self.implementationtypename = 'void (@)(struct %s *)' % (

Added: pypy/dist/pypy/translator/c/symboltable.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/c/symboltable.py	Mon Jun 20 14:06:21 2005
@@ -0,0 +1,143 @@
+from pypy.rpython.lltype import *
+
+
+class SymbolTable:
+    """For debugging purposes only.  This collects the information
+    needed to know the byte-layout of the data structures generated in
+    a C source.
+    """
+
+    def __init__(self):
+        self.next_number = 0
+        self.next_pointer = 0
+        self.lltypes = {}
+        self.globals = {}
+        self.module = None
+
+    def attach(self, module):
+        self.module = module
+        module.__symboltable__ = self
+
+    def generate_type_info(self, db, defnode):
+        self.lltypes[defnode.LLTYPE] = self.next_number
+        for number_expr in defnode.debug_offsets():
+            self.next_number += 1
+            yield number_expr
+
+    def generate_global_info(self, db, node):
+        self.globals[node.name] = self.next_pointer, node.T
+        self.next_pointer += 1
+        return node.ptrname
+
+    # __________ public interface (mapping-like) __________
+
+    def keys(self):
+        return self.globals.keys()
+
+    def __getitem__(self, globalname):
+        ptrindex, T = self.globals[globalname]
+        address = self.module.debuginfo_global(ptrindex)
+        return debugptr(Ptr(T), address, self)
+
+    def __iter__(self):
+        return self.globals.iterkeys()
+
+def getsymboltable(module):
+    if isinstance(module, str):
+        module = __import__(module)
+    return module.__symboltable__
+
+# ____________________________________________________________
+
+import struct
+
+PrimitiveTag = {
+    Signed:    'l',
+    Unsigned:  'L',
+    Float:     'd',
+    Char:      'c',
+    Bool:      'b',
+    }
+ptr_size = struct.calcsize('P')
+
+
+class debugptr:
+
+    def __init__(self, PTRTYPE, address, symtable):
+        self._TYPE = PTRTYPE
+        self._address = address
+        self._symtable = symtable
+
+    def __eq__(self, other):
+        return self._address == other._address
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    def __hash__(self):
+        raise TypeError("pointer objects are not hashable")
+
+    def __repr__(self):
+        addr = self._address
+        if addr < 0:
+            addr += 256 ** ptr_size
+        return '<debugptr %s to 0x%x>' % (self._TYPE.TO, addr)
+
+    def __nonzero__(self):
+        return self._address != 0
+
+    def _nth_offset(self, n):
+        index = self._symtable.lltypes[self._TYPE.TO]
+        return self._symtable.module.debuginfo_offset(index + n)
+
+    def _read(self, FIELD_TYPE, offset):
+        if not self:   # NULL
+            raise ValueError, 'dereferencing NULL pointer'
+        module = self._symtable.module
+        address = self._address + offset
+        if isinstance(FIELD_TYPE, ContainerType):
+            return debugptr(Ptr(FIELD_TYPE), address, self._symtable)
+        elif isinstance(FIELD_TYPE, Primitive):
+            if FIELD_TYPE == Void:
+                return None
+            tag = PrimitiveTag[FIELD_TYPE]
+            size = struct.calcsize(tag)
+            data = module.debuginfo_peek(address, size)
+            result, = struct.unpack(tag, data)
+            return result
+        elif isinstance(FIELD_TYPE, Ptr):
+            data = module.debuginfo_peek(address, ptr_size)
+            result, = struct.unpack('P', data)
+            return debugptr(FIELD_TYPE, result, self._symtable)
+        else:
+            raise TypeError("unknown type %r" % (FIELD_TYPE,))
+
+    def __getattr__(self, name):
+        STRUCT = self._TYPE.TO
+        if not name.startswith('_') and isinstance(STRUCT, Struct):
+            try:
+                field_index = list(STRUCT._names).index(name)
+            except ValueError:
+                raise AttributeError, name
+            FIELD_TYPE = STRUCT._flds[name]
+            offset = self._nth_offset(field_index)
+            return self._read(FIELD_TYPE, offset)
+        raise AttributeError, name
+
+    def __len__(self):
+        ARRAY = self._TYPE.TO
+        if isinstance(ARRAY, Array):
+            length_offset = self._nth_offset(0)
+            return self._read(Signed, length_offset)
+        raise TypeError, "not an array: %r" % (ARRAY,)
+
+    def __getitem__(self, index):
+        ARRAY = self._TYPE.TO
+        if isinstance(ARRAY, Array):
+            if not (0 <= index < len(self)):
+                raise IndexError("array index out of bounds")
+            item0_offset = self._nth_offset(1)
+            item1_offset = self._nth_offset(2)
+            offset = item0_offset + (item1_offset-item0_offset) * index
+            return self._read(ARRAY.OF, offset)
+        raise TypeError, "not an array: %r" % (ARRAY,)

Added: pypy/dist/pypy/translator/c/test/test_symboltable.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/c/test/test_symboltable.py	Mon Jun 20 14:06:21 2005
@@ -0,0 +1,21 @@
+from pypy.translator.translator import Translator
+from pypy.translator.c.symboltable import getsymboltable
+
+def test_simple():
+    glist = [4, 5, 6]
+    def f(x):
+        return glist[x]
+    t = Translator(f)
+    t.annotate([int])
+    t.specialize()
+
+    f = t.ccompile()
+    assert f(1) == 5
+    assert f(2) == 6
+
+    symtable = getsymboltable(f.__module__)
+    debug_list = symtable['g_list']    # XXX find a way to find this name
+    assert len(debug_list.items) == 3
+    assert debug_list.items[0] == 4
+    assert debug_list.items[1] == 5
+    assert debug_list.items[2] == 6



More information about the Pypy-commit mailing list