[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