This patch adds Python debugger support. It may be contributed upstream at
some point, but the suitability (or lack thereof) has not yet been determined.
--- Python-2.7.12/Makefile.pre.in.~3~ 2016-07-07 14:07:55.545052752 -0700
+++ Python-2.7.12/Makefile.pre.in 2016-07-07 14:07:55.569639679 -0700
@@ -432,7 +432,7 @@
# Default target
all: build_all
-build_all: $(BUILDPYTHON) oldsharedmods sharedmods gdbhooks
+build_all: $(BUILDPYTHON) oldsharedmods sharedmods gdbhooks build-py_db
# Compile a binary with profile guided optimization.
profile-opt:
@@ -877,6 +877,15 @@
$(LIBRARY_OBJS) $(MODOBJS) Modules/python.o: $(PYTHON_HEADERS)
+SHLIB_FLAGS = -shared -fpic
+
+libpython2.7_db.so.1.0: $(srcdir)/py_db/libpython27_db.c
+ $(CC) -o $@ $(CFLAGS) $(CPPFLAGS) $(SHLIB_FLAGS) $<
+
+build-py_db: libpython2.7_db.so.1.0
+
+install-py_db: libpython2.7_db.so.1.0
+ $(INSTALL_SHARED) libpython2.7_db.so.1.0 $(DESTDIR)$(LIBDIR)
######################################################################
@@ -941,7 +950,7 @@
$(TESTPYTHON) $(TESTPROG) $(MEMTESTOPTS)
# Install everything
-install: @FRAMEWORKINSTALLFIRST@ commoninstall bininstall maninstall @FRAMEWORKINSTALLLAST@
+install: @FRAMEWORKINSTALLFIRST@ commoninstall bininstall maninstall @FRAMEWORKINSTALLLAST@ install-py_db
if test "x$(ENSUREPIP)" != "xno" ; then \
case $(ENSUREPIP) in \
upgrade) ensurepip="--upgrade" ;; \
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gelf.h>
+
+#include <Python.h>
+#include <frameobject.h>
+
+#include "libpython27_db.h"
+#if defined(_LP64)
+#include "libpython27_db_32.h"
+#endif /* _LP64 */
+
+/*
+ * Because MDB always runs the debugger in the same datamodel as the target,
+ * only functions that are used by the procfs part of this interface (or shared
+ * between the two) are written as 64->32 aware.
+ */
+typedef struct pydb_arch_ops {
+ ssize_t (*strobj_readdata)(pydb_agent_t *, uintptr_t, unsigned char *,
+ size_t);
+ int (*frameinfo)(pydb_agent_t *, uintptr_t, char *,
+ size_t, char *, size_t, int *);
+} pydb_arch_ops_t;
+
+struct pydb_agent {
+ struct ps_prochandle *pdb_ph;
+ int pdb_vers;
+ int pdb_is_64bit;
+ int pdb_datamodel;
+ const pydb_arch_ops_t *pdb_ops;
+};
+
+typedef uintptr_t (*pdi_next_cb_t)(pydb_iter_t *);
+
+struct pydb_iter {
+ struct ps_prochandle *pdi_ph;
+ uintptr_t pdi_current;
+ pdi_next_cb_t pdi_nextf;
+};
+
+#define LIBPYTHON "libpython2.7.so"
+
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+
+/* Generic interface to helper functions */
+static ssize_t pydb_strobj_readdata(pydb_agent_t *py, uintptr_t addr,
+ unsigned char *buf, size_t buf_len);
+static int pydb_getlno(pydb_agent_t *py, uintptr_t lnotab_addr, int firstline,
+ int lastinst);
+static int pydb_frameinfo(pydb_agent_t *py, uintptr_t addr, char *funcnm,
+ size_t funcnm_sz, char *filenm, size_t filenm_sz, int *lineno);
+
+/* datamodel specific implementation of helper functions */
+static ssize_t pydb_strobj_readdata_native(pydb_agent_t *py, uintptr_t addr,
+ unsigned char *buf, size_t buf_len);
+static int pydb_frameinfo_native(pydb_agent_t *py, uintptr_t addr, char *funcnm,
+ size_t funcnm_sz, char *filenm, size_t filenm_sz, int *lineno);
+
+#if defined (_LP64)
+static ssize_t pydb_strobj_readdata_32(pydb_agent_t *py, uintptr_t addr,
+ unsigned char *buf, size_t buf_len);
+static int pydb_frameinfo_32(pydb_agent_t *py, uintptr_t addr, char *funcnm,
+ size_t funcnm_sz, char *filenm, size_t filenm_sz, int *lineno);
+#endif /* _LP64 */
+
+static ssize_t pydb_strobj_readstr(pydb_agent_t *py, uintptr_t addr, char *buf,
+ size_t len);
+
+/* Iterator function next routines. Plugable, configured by iterator init */
+static uintptr_t pydb_frame_iter_next(pydb_iter_t *iter);
+static uintptr_t pydb_interp_iter_next(pydb_iter_t *iter);
+static uintptr_t pydb_thread_iter_next(pydb_iter_t *iter);
+
+static const char *strbasename(const char *s);
+
+static const pydb_arch_ops_t arch_ops_native = {
+ .frameinfo = pydb_frameinfo_native,
+ .strobj_readdata = pydb_strobj_readdata_native,
+};
+
+#if defined (_LP64)
+static const pydb_arch_ops_t arch_ops_32 = {
+ .frameinfo = pydb_frameinfo_32,
+ .strobj_readdata = pydb_strobj_readdata_32,
+};
+#endif /* _LP64 */
+
+static const char *
+strbasename(const char *s)
+{
+ const char *p = strrchr(s, '/');
+
+ if (p == NULL)
+ return (s);
+
+ return (++p);
+}
+
+/* Agent creation / destruction routines */
+
+pydb_agent_t *
+pydb_agent_create(struct ps_prochandle *P, int vers)
+{
+ pydb_agent_t *py;
+ int datamodel;
+
+ if (vers != PYDB_VERSION) {
+ errno = ENOTSUP;
+ return (NULL);
+ }
+
+ if (ps_pdmodel(P, &datamodel) != PS_OK) {
+ return (NULL);
+ }
+
+ py = (pydb_agent_t *)malloc(sizeof (pydb_agent_t));
+ if (py == NULL) {
+ return (NULL);
+ }
+
+ py->pdb_ph = P;
+ py->pdb_vers = vers;
+ py->pdb_datamodel = datamodel;
+ py->pdb_is_64bit = 0;
+ py->pdb_ops = &arch_ops_native;
+
+#if defined (_LP64)
+ py->pdb_is_64bit = (datamodel == PR_MODEL_LP64);
+ if (!py->pdb_is_64bit) {
+ py->pdb_ops = &arch_ops_32;
+ }
+#endif /* _LP64 */
+
+ return (py);
+}
+
+void
+pydb_agent_destroy(pydb_agent_t *py)
+{
+ if (py == NULL) {
+ return;
+ }
+
+ free(py);
+}
+
+/* Helper functions */
+static int
+pydb_getlno(pydb_agent_t *py, uintptr_t lnotab_addr, int firstline,
+ int lastinst)
+{
+ unsigned char lnotab[4096];
+ ssize_t lnotab_len;
+ int addr, line;
+ int i;
+
+ lnotab_len = pydb_strobj_readdata(py, lnotab_addr, lnotab,
+ sizeof (lnotab));
+ if (lnotab_len < 0) {
+ return (-1);
+ }
+
+ /*
+ * Python's line number algorithm is arcane. See here for details:
+ */
+
+ line = firstline;
+ for (addr = i = 0; i < lnotab_len; i += 2) {
+ if (addr + lnotab[i] > lastinst) {
+ break;
+ }
+ addr += lnotab[i];
+ line += lnotab[i + 1];
+ }
+
+ return (line);
+}
+
+static ssize_t
+pydb_strobj_readdata(pydb_agent_t *py, uintptr_t addr, unsigned char *buf,
+ size_t buf_len)
+{
+ return (py->pdb_ops->strobj_readdata(py, addr, buf, buf_len));
+}
+
+static ssize_t
+pydb_strobj_readdata_native(pydb_agent_t *py, uintptr_t addr,
+ unsigned char *buf, size_t buf_len)
+{
+ PyStringObject sobj;
+ ssize_t obj_sz;
+ ssize_t read_sz;
+ psaddr_t straddr;
+
+ /*
+ * PyStringObjects are variable size. The size of the PyStringObject
+ * struct is fixed, and known at compile time; however, the size of the
+ * associated buffer is variable. The char[1] element at the end of the
+ * structure contains the string, and the ob_size of the PyStringObject
+ * indicates how much extra space was allocated to contain the string
+ * buffer at the object's tail. Read in the fixed size portion of the
+ * object first, and then read the contents of the data buffer into the
+ * buffer passed by the caller.
+ */
+
+ if (ps_pread(py->pdb_ph, addr, &sobj, sizeof (PyStringObject))
+ != PS_OK) {
+ return (-1);
+ }
+
+ obj_sz = (ssize_t)PyString_GET_SIZE(&sobj);
+
+ read_sz = MIN(obj_sz, (ssize_t)buf_len);
+ straddr = (psaddr_t)(addr + offsetof(PyStringObject, ob_sval));
+
+ if (ps_pread(py->pdb_ph, straddr, buf, (size_t)read_sz) != PS_OK) {
+ return (-1);
+ }
+
+ return (read_sz);
+}
+
+#if defined(_LP64)
+static ssize_t
+pydb_strobj_readdata_32(pydb_agent_t *py, uintptr_t addr,
+ unsigned char *buf, size_t buf_len)
+{
+ PyStringObject32 sobj;
+ ssize_t obj_sz;
+ ssize_t read_sz;
+ psaddr_t straddr;
+
+ /*
+ * PyStringObjects are variable size. The size of the PyStringObject
+ * struct is fixed, and known at compile time; however, the size of the
+ * associated buffer is variable. The char[1] element at the end of the
+ * structure contains the string, and the ob_size of the PyStringObject
+ * indicates how much extra space was allocated to contain the string
+ * buffer at the object's tail. Read in the fixed size portion of the
+ * object first, and then read the contents of the data buffer into the
+ * buffer passed by the caller.
+ */
+
+ if (ps_pread(py->pdb_ph, addr, &sobj, sizeof (PyStringObject32))
+ != PS_OK) {
+ return (-1);
+ }
+
+ obj_sz = (ssize_t)PyString_GET_SIZE32(&sobj);
+
+ read_sz = MIN(obj_sz, (ssize_t)buf_len);
+ straddr = (psaddr_t)(addr + offsetof(PyStringObject32, ob_sval));
+
+ if (ps_pread(py->pdb_ph, straddr, buf, (size_t)read_sz) != PS_OK) {
+ return (-1);
+ }
+
+ return (read_sz);
+}
+#endif /* _LP64 */
+
+/*
+ * Most Python PyStringObjects contain strings, as one would expect. However,
+ * due to some sleazy hackery in parts of the Python code, some string objects
+ * are used as buffers for binary data. In the general case,
+ * pydb_strobj_readstr() should be used to read strings out of string objects.
+ * It wraps pydb_strobj_readdata(), which should be used by callers only when
+ * trying to retrieve binary data. (This routine does some string cleanup).
+ */
+static ssize_t
+pydb_strobj_readstr(pydb_agent_t *py, uintptr_t addr, char *buf,
+ size_t buf_len)
+{
+ ssize_t read_sz;
+
+ read_sz = pydb_strobj_readdata(py, addr, (unsigned char *)buf, buf_len);
+
+ if (read_sz >= 0) {
+ if (read_sz >= buf_len) {
+ read_sz = buf_len - 1;
+ }
+
+ buf[read_sz] = '\0';
+ }
+
+ return (read_sz);
+}
+
+
+static int
+pydb_frameinfo(pydb_agent_t *py, uintptr_t addr, char *funcnm,
+ size_t funcnm_sz, char *filenm, size_t filenm_sz, int *lineno)
+{
+ return (py->pdb_ops->frameinfo(py, addr, funcnm, funcnm_sz,
+ filenm, filenm_sz, lineno));
+}
+
+static int
+pydb_frameinfo_native(pydb_agent_t *py, uintptr_t addr, char *funcnm,
+ size_t funcnm_sz, char *filenm, size_t filenm_sz, int *lineno)
+{
+ PyFrameObject fo;
+ PyCodeObject co;
+ ssize_t rc;
+
+ if (ps_pread(py->pdb_ph, addr, &fo, sizeof (PyFrameObject))
+ != PS_OK) {
+ return (-1);
+ }
+
+ if (ps_pread(py->pdb_ph, (uintptr_t)fo.f_code, &co,
+ sizeof (PyCodeObject)) != PS_OK) {
+ return (-1);
+ }
+
+ rc = pydb_strobj_readstr(py, (uintptr_t)co.co_name, funcnm, funcnm_sz);
+ if (rc < 0) {
+ return (-1);
+ }
+
+ rc = pydb_strobj_readstr(py, (uintptr_t)co.co_filename, filenm,
+ filenm_sz);
+ if (rc < 0) {
+ return (-1);
+ }
+
+ *lineno = pydb_getlno(py, (uintptr_t)co.co_lnotab, co.co_firstlineno,
+ fo.f_lasti);
+ if (*lineno < 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+#if defined (_LP64)
+static int
+pydb_frameinfo_32(pydb_agent_t *py, uintptr_t addr, char *funcnm,
+ size_t funcnm_sz, char *filenm, size_t filenm_sz, int *lineno)
+{
+ PyFrameObject32 fo;
+ PyCodeObject32 co;
+ ssize_t rc;
+
+ if (ps_pread(py->pdb_ph, addr, &fo, sizeof (PyFrameObject32))
+ != PS_OK) {
+ return (-1);
+ }
+
+ if (ps_pread(py->pdb_ph, (uintptr_t)fo.f_code, &co,
+ sizeof (PyCodeObject32)) != PS_OK) {
+ return (-1);
+ }
+
+ rc = pydb_strobj_readstr(py, (uintptr_t)co.co_name, funcnm, funcnm_sz);
+ if (rc < 0) {
+ return (-1);
+ }
+
+ rc = pydb_strobj_readstr(py, (uintptr_t)co.co_filename, filenm,
+ filenm_sz);
+ if (rc < 0) {
+ return (-1);
+ }
+
+ *lineno = pydb_getlno(py, (uintptr_t)co.co_lnotab, co.co_firstlineno,
+ fo.f_lasti);
+ if (*lineno < 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+#endif /* _LP64 */
+
+/* Functions that are part of the library's interface */
+
+/*
+ * Given the address of a PyFrameObject, and a buffer of a known size,
+ * fill the buffer with a description of the frame.
+ */
+int
+pydb_get_frameinfo(pydb_agent_t *py, uintptr_t frame_addr, char *fbuf,
+ size_t bufsz, int verbose)
+{
+ char funcname[1024];
+ char filename[1024];
+ char *fn;
+ int lineno;
+ int length = (py->pdb_is_64bit ? 16 : 8);
+ int rc;
+
+ rc = pydb_frameinfo(py, frame_addr, funcname, sizeof (funcname),
+ filename, sizeof (filename), &lineno);
+ if (rc < 0) {
+ return (-1);
+ }
+
+ if (!verbose) {
+ fn = (char *)strbasename(filename);
+ } else {
+ fn = filename;
+ }
+
+ (void) snprintf(fbuf, bufsz, "%0.*lx %s:%d %s()\n", length,
+ frame_addr, fn, lineno, funcname);
+
+ return (0);
+}
+
+/*
+ * Return a description about a PyFrameObject, if the object is
+ * actually a PyFrameObject. In this case, the pc argument is checked
+ * to make sure that it came from a function that takes a PyFrameObject
+ * as its first (argv[0]) argument.
+ */
+int
+pydb_pc_frameinfo(pydb_agent_t *py, uintptr_t pc, uintptr_t frame_addr,
+ char *fbuf, size_t bufsz)
+{
+ char funcname[1024];
+ char filename[1024];
+ int lineno;
+ int rc;
+ ps_sym_t psym;
+
+ /*
+ * If PC doesn't match PyEval_EvalFrameEx in either libpython
+ * or the executable, don't decode it.
+ */
+ if (ps_pglobal_sym(py->pdb_ph, LIBPYTHON, "PyEval_EvalFrameEx", &psym)
+ != PS_OK) {
+ return (-1);
+ }
+
+ /* If symbol found, ensure that PC falls within PyEval_EvalFrameEx. */
+ return (-1);
+ }
+
+ rc = pydb_frameinfo(py, frame_addr, funcname, sizeof (funcname),
+ filename, sizeof (filename), &lineno);
+ if (rc < 0) {
+ return (-1);
+ }
+
+ (void) snprintf(fbuf, bufsz, "[ %s:%d (%s) ]\n", filename, lineno,
+ funcname);
+
+ return (0);
+}
+
+/*
+ * Walks the list of PyInterpreterState objects. If caller doesn't
+ * supply address of list, this method will look it up.
+ */
+pydb_iter_t *
+pydb_interp_iter_init(pydb_agent_t *py, uintptr_t addr)
+{
+ pydb_iter_t *itr;
+ uintptr_t i_addr;
+ int rc;
+
+ if (addr == 0) {
+ rc = ps_pglobal_lookup(py->pdb_ph, LIBPYTHON, "interp_head",
+ (psaddr_t *)&addr);
+ if (rc != PS_OK) {
+ return (NULL);
+ }
+ }
+
+ if (ps_pread(py->pdb_ph, (uintptr_t)addr, &i_addr, sizeof (uintptr_t))
+ != PS_OK) {
+ return (NULL);
+ }
+
+ itr = malloc(sizeof (pydb_iter_t));
+ if (itr == NULL) {
+ return (NULL);
+ }
+
+ itr->pdi_ph = py->pdb_ph;
+ itr->pdi_current = i_addr;
+ itr->pdi_nextf = pydb_interp_iter_next;
+
+ return (itr);
+}
+
+static uintptr_t
+pydb_interp_iter_next(pydb_iter_t *iter)
+{
+ PyInterpreterState st;
+ uintptr_t cur;
+
+ cur = iter->pdi_current;
+
+ if (cur == 0) {
+ return (cur);
+ }
+
+ if (ps_pread(iter->pdi_ph, cur, &st, sizeof (PyInterpreterState))
+ != PS_OK) {
+ iter->pdi_current = 0;
+ return (0);
+ }
+
+ iter->pdi_current = (uintptr_t)st.next;
+
+ return (cur);
+}
+
+/*
+ * Walk a list of Python PyFrameObjects. The addr argument must be
+ * the address of a valid PyThreadState object.
+ */
+pydb_iter_t *
+pydb_frame_iter_init(pydb_agent_t *py, uintptr_t addr)
+{
+ pydb_iter_t *itr;
+ PyThreadState ts;
+
+ if (ps_pread(py->pdb_ph, (uintptr_t)addr, &ts, sizeof (PyThreadState))
+ != PS_OK) {
+ return (NULL);
+ }
+
+ itr = malloc(sizeof (pydb_iter_t));
+ if (itr == NULL) {
+ return (NULL);
+ }
+
+ itr->pdi_ph = py->pdb_ph;
+ itr->pdi_current = (uintptr_t)ts.frame;
+ itr->pdi_nextf = pydb_frame_iter_next;
+
+ return (itr);
+}
+
+static uintptr_t
+pydb_frame_iter_next(pydb_iter_t *iter)
+{
+ PyFrameObject fo;
+ uintptr_t cur;
+
+ cur = iter->pdi_current;
+
+ if (cur == 0) {
+ return (cur);
+ }
+
+ if (ps_pread(iter->pdi_ph, cur, &fo, sizeof (PyFrameObject))
+ != PS_OK) {
+ iter->pdi_current = 0;
+ return (0);
+ }
+
+ iter->pdi_current = (uintptr_t)fo.f_back;
+
+ return (cur);
+}
+
+/*
+ * Walk a list of Python PyThreadState objects. The addr argument must be
+ * the address of a valid PyInterpreterState object.
+ */
+pydb_iter_t *
+pydb_thread_iter_init(pydb_agent_t *py, uintptr_t addr)
+{
+ pydb_iter_t *itr;
+ PyInterpreterState is;
+
+ if (ps_pread(py->pdb_ph, (uintptr_t)addr, &is,
+ sizeof (PyInterpreterState)) != PS_OK) {
+ return (NULL);
+ }
+
+ itr = malloc(sizeof (pydb_iter_t));
+ if (itr == NULL) {
+ return (NULL);
+ }
+
+ itr->pdi_ph = py->pdb_ph;
+ itr->pdi_current = (uintptr_t)is.tstate_head;
+ itr->pdi_nextf = pydb_thread_iter_next;
+
+ return (itr);
+}
+
+static uintptr_t
+pydb_thread_iter_next(pydb_iter_t *iter)
+{
+ PyThreadState ts;
+ uintptr_t cur;
+
+ cur = iter->pdi_current;
+
+ if (cur == 0) {
+ return (cur);
+ }
+
+ if (ps_pread(iter->pdi_ph, cur, &ts, sizeof (PyThreadState)) != PS_OK) {
+ iter->pdi_current = 0;
+ return (0);
+ }
+
+ iter->pdi_current = (uintptr_t)ts.next;
+
+ return (cur);
+}
+
+
+uintptr_t
+pydb_iter_next(pydb_iter_t *iter)
+{
+ return (iter->pdi_nextf(iter));
+}
+
+void
+pydb_iter_fini(pydb_iter_t *iter)
+{
+ if (iter == NULL) {
+ return;
+ }
+
+ free(iter);
+}
new file mode 100644
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _LIBPYTHON27_DB_H
+#define _LIBPYTHON27_DB_H
+
+#include <proc_service.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Agent is opaque to library's consumers. */
+typedef struct pydb_agent pydb_agent_t;
+
+/*
+ * Library's debug version is 1. Changes to interface should increase this
+ * number.
+ */
+#define PYDB_VERSION 1
+
+/* Agent creation/destruction routines */
+extern pydb_agent_t *pydb_agent_create(struct ps_prochandle *P, int vers);
+extern void pydb_agent_destroy(pydb_agent_t *py);
+
+/* Used by callers that know they are looking at a PyFrameObject */
+extern int pydb_get_frameinfo(pydb_agent_t *py, uintptr_t frame_addr,
+ char *fbuf, size_t bufsz, int verbose);
+
+/*
+ * Used by callers that don't know if they're looking at PyFrameObject.
+ * Checks PC for traceable functions.
+ */
+extern int pydb_pc_frameinfo(pydb_agent_t *py, uintptr_t pc,
+ uintptr_t frame_addr, char *fbuf, size_t bufsz);
+
+/* Iterator functions */
+typedef struct pydb_iter pydb_iter_t;
+
+extern pydb_iter_t *pydb_frame_iter_init(pydb_agent_t *py, uintptr_t addr);
+extern pydb_iter_t *pydb_interp_iter_init(pydb_agent_t *py,
+ uintptr_t addr);
+extern pydb_iter_t *pydb_thread_iter_init(pydb_agent_t *py,
+ uintptr_t addr);
+extern void pydb_iter_fini(pydb_iter_t *iter);
+extern uintptr_t pydb_iter_next(pydb_iter_t *iter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBPYTHON27_DB_H */
new file mode 100644
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _LIBPYTHON27_DB_32_H
+#define _LIBPYTHON27_DB_32_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/*
+ * Define 32-bit Python data structures for use by the 64-bit debugger. This
+ * is so that a 64-bit debugger may properly examine a 32-bit process.
+ *
+ * In many cases, the debug library is only concerned with a few fields in the
+ * Python structure. In that case, the other ancillary fields are elided.
+ */
+
+typedef uint32_t uintptr32_t;
+typedef int32_t Py_ssize32_t;
+
+typedef struct _is32 {
+ uintptr32_t next;
+ uintptr32_t tstate_head;
+} PyInterpreterState32;
+
+typedef struct _ts32 {
+ uintptr32_t next;
+ uintptr32_t interp;
+ uintptr32_t frame;
+} PyThreadState32;
+
+#define PyObject_HEAD32 \
+ Py_ssize32_t ob_refcnt; \
+ uintptr32_t ob_type;
+
+#define PyObject_VAR_HEAD32 \
+ PyObject_HEAD32 \
+ Py_ssize32_t ob_size;
+
+typedef struct {
+ PyObject_HEAD32
+} PyObject32;
+
+typedef struct {
+ PyObject_VAR_HEAD32
+} PyVarObject32;
+
+typedef struct {
+ PyObject_VAR_HEAD32
+ int32_t ob_shash;
+ int ob_sstate;
+ char ob_sval[1];
+} PyStringObject32;
+
+#define Py_SIZE32(ob) (((PyVarObject32*)(ob))->ob_size)
+#define PyString_GET_SIZE32(op) Py_SIZE32(op)
+#define PyString_AS_STRING32(op) (((PyStringObject32 *)(op))->ob_sval)
+
+typedef struct {
+ PyObject_VAR_HEAD32
+ uintptr32_t f_back;
+ uintptr32_t f_code;
+ uintptr32_t f_builtins;
+ uintptr32_t f_globals;
+ uintptr32_t f_locals;
+ uintptr32_t f_valuestack;
+ uintptr32_t f_stacktop;
+ uintptr32_t f_trace;
+ uintptr32_t f_exc_typpe, f_exc_value, f_exc_traceback;
+ uintptr32_t f_tstate;
+ int f_lasti;
+ int f_lineno;
+} PyFrameObject32;
+
+typedef struct {
+ PyObject_HEAD32
+ int co_argcount;
+ int co_nlocals;
+ int co_stacksize;
+ int co_flags;
+ uintptr32_t co_code;
+ uintptr32_t co_consts;
+ uintptr32_t co_names;
+ uintptr32_t co_varnames;
+ uintptr32_t co_freevars;
+ uintptr32_t co_cellvars;
+ uintptr32_t co_filename;
+ uintptr32_t co_name;
+ int co_firstlineno;
+ uintptr32_t co_lnotab;
+} PyCodeObject32;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBPYTHON27_DB_32_H */
new file mode 100644
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+#
+
+SUNWprivate_1.1 {
+ global:
+ pydb_agent_create;
+ pydb_agent_destroy;
+ pydb_frame_iter_init;
+ pydb_get_frameinfo;
+ pydb_pc_frameinfo;
+ pydb_interp_iter_init;
+ pydb_thread_iter_init;
+ pydb_iter_fini;
+ pydb_iter_next;
+ local:
+ *;
+};