-
-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the debug mode #142
Add the debug mode #142
Changes from all commits
1cd887a
4551d10
97803d8
2d868ec
c379b34
c3c97c1
0f939ee
3141873
ba33924
18c4a34
85ae3ae
dd0c650
fa5849f
d8a1736
d40d85d
2caaa3b
cd0f0ea
d6fcfe7
b6fffec
099bedb
ee51500
616d2c2
34bb3d0
cc439cf
95770ee
dca923a
a0a2302
f58cda4
f7270fc
d0f7fb7
59184b2
484e28d
92216b1
51bb8de
12b6311
eadef67
d1818ff
d770971
6a481ea
2213191
53b78ba
1a44f3d
f731b95
00db14d
3799046
651aaaf
2e50a13
f94104c
24e7588
a186e66
a670b73
0a660c3
11974b8
5b8d102
abe4cc4
b1df2ae
cd8cd6e
77fdaf8
f5d415d
64f2e36
4f1f406
239885a
c336f02
cb13b9e
c6174be
ae25d49
bbd00ac
ebf7c77
d18c016
beb2d07
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .leakdetector import HPyError, HPyLeak, LeakDetector |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from hpy.universal import _debug | ||
|
||
class HPyError(Exception): | ||
pass | ||
|
||
class HPyLeak(HPyError): | ||
def __init__(self, leaks): | ||
super().__init__() | ||
self.leaks = leaks | ||
|
||
def __str__(self): | ||
lines = [] | ||
lines.append('%s handles have not been closed properly:' % len(self.leaks)) | ||
for dh in self.leaks: | ||
lines.append(' %r' % dh) | ||
return '\n'.join(lines) | ||
|
||
|
||
class LeakDetector: | ||
|
||
def __init__(self): | ||
self.generation = None | ||
|
||
def start(self): | ||
if self.generation is not None: | ||
raise ValueError('LeakDetector already started') | ||
self.generation = _debug.new_generation() | ||
|
||
def stop(self): | ||
if self.generation is None: | ||
raise ValueError('LeakDetector not started yet') | ||
leaks = _debug.get_open_handles(self.generation) | ||
if leaks: | ||
raise HPyLeak(leaks) | ||
|
||
def __enter__(self): | ||
self.start() | ||
return self | ||
|
||
def __exit__(self, etype, evalue, tb): | ||
self.stop() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
// Python-level interface for the _debug module. Written in HPy itself, the | ||
// idea is that it should be reusable by other implementations | ||
|
||
// NOTE: hpy.debug._debug is loaded using the UNIVERSAL ctx. To make it | ||
// clearer, we will use "uctx" and "dctx" to distinguish them. | ||
|
||
#include "hpy.h" | ||
#include "debug_internal.h" | ||
|
||
static UHPy new_DebugHandleObj(HPyContext uctx, UHPy u_DebugHandleType, | ||
DebugHandle *handle); | ||
|
||
|
||
HPyDef_METH(new_generation, "new_generation", new_generation_impl, HPyFunc_NOARGS) | ||
static UHPy new_generation_impl(HPyContext uctx, UHPy self) | ||
{ | ||
HPyContext dctx = hpy_debug_get_ctx(uctx); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Obtaining a new debug ctx from the I'm not quite sure how to remove this constraint right now -- the ctx is fairly invisible from Python code, and that likely needs to stay that way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
HPyDebugInfo *info = get_info(dctx); | ||
info->current_generation++; | ||
return HPyLong_FromLong(uctx, info->current_generation); | ||
} | ||
|
||
|
||
HPyDef_METH(get_open_handles, "get_open_handles", get_open_handles_impl, HPyFunc_O, .doc= | ||
"Return a list containing all the open handles whose generation is >= " | ||
"of the given arg") | ||
static UHPy get_open_handles_impl(HPyContext uctx, UHPy u_self, UHPy u_gen) | ||
{ | ||
HPyContext dctx = hpy_debug_get_ctx(uctx); | ||
HPyDebugInfo *info = get_info(dctx); | ||
|
||
UHPy u_DebugHandleType = HPy_GetAttr_s(uctx, u_self, "DebugHandle"); | ||
if (HPy_IsNull(u_DebugHandleType)) | ||
return HPy_NULL; | ||
|
||
long gen = HPyLong_AsLong(uctx, u_gen); | ||
if (HPyErr_Occurred(uctx)) | ||
return HPy_NULL; | ||
|
||
UHPy u_result = HPyList_New(uctx, 0); | ||
if (HPy_IsNull(u_result)) | ||
return HPy_NULL; | ||
|
||
DebugHandle *dh = info->open_handles; | ||
while(dh != NULL) { | ||
if (dh->generation >= gen) { | ||
UHPy u_item = new_DebugHandleObj(uctx, u_DebugHandleType, dh); | ||
if (HPy_IsNull(u_item)) | ||
return HPy_NULL; | ||
if (HPyList_Append(uctx, u_result, u_item) == -1) { | ||
HPy_Close(uctx, u_item); | ||
HPy_Close(uctx, u_result); | ||
return HPy_NULL; | ||
} | ||
HPy_Close(uctx, u_item); | ||
} | ||
dh = dh->next; | ||
} | ||
return u_result; | ||
} | ||
|
||
/* ~~~~~~ DebugHandleType and DebugHandleObject ~~~~~~~~ | ||
|
||
This is the applevel view of a DebugHandle/DHPy. | ||
|
||
Note that there are two different ways to expose DebugHandle to applevel: | ||
|
||
1. make DebugHandle itself a Python object: this is simple but means that | ||
you have to pay the PyObject_HEAD overhead (16 bytes) for all of them | ||
|
||
2. make DebugHandle a plain C struct, and expose them through a | ||
Python-level wrapper. | ||
|
||
We choose to implement solution 2 because we expect to have many | ||
DebugHandle around, but to expose only few of them to applevel, when you | ||
call get_open_handles. This way, we save 16 bytes per DebugHandle. | ||
|
||
This means that you can have different DebugHandleObjects wrapping the same | ||
DebugHandle. To make it easier to compare them, they expose the .id | ||
attribute, which is the address of the wrapped DebugHandle. Also, | ||
DebugHandleObjects compare equal if their .id is equal. | ||
*/ | ||
|
||
typedef struct { | ||
HPyObject_HEAD | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we make this a pure type once the cast support branch lands? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. HPyObject_HEAD is already gone in the cast support branch. |
||
DebugHandle *handle; | ||
} DebugHandleObject; | ||
|
||
HPyDef_GET(DebugHandle_obj, "obj", DebugHandle_obj_get, | ||
.doc="The object which the handle points to") | ||
static UHPy DebugHandle_obj_get(HPyContext uctx, UHPy self, void *closure) | ||
{ | ||
DebugHandleObject *dh = HPy_CAST(uctx, DebugHandleObject, self); | ||
return HPy_Dup(uctx, dh->handle->uh); | ||
} | ||
|
||
HPyDef_GET(DebugHandle_id, "id", DebugHandle_id_get, | ||
.doc="A numeric identifier representing the underlying universal handle") | ||
static UHPy DebugHandle_id_get(HPyContext uctx, UHPy self, void *closure) | ||
{ | ||
DebugHandleObject *dh = HPy_CAST(uctx, DebugHandleObject, self); | ||
return HPyLong_FromSsize_t(uctx, (HPy_ssize_t)dh->handle); | ||
} | ||
|
||
HPyDef_SLOT(DebugHandle_cmp, DebugHandle_cmp_impl, HPy_tp_richcompare) | ||
static UHPy DebugHandle_cmp_impl(HPyContext uctx, UHPy self, UHPy o, HPy_RichCmpOp op) | ||
{ | ||
UHPy T = HPy_Type(uctx, self); | ||
if (!HPy_TypeCheck(uctx, o, T)) | ||
return HPy_Dup(uctx, uctx->h_NotImplemented); | ||
DebugHandleObject *dh_self = HPy_CAST(uctx, DebugHandleObject, self); | ||
DebugHandleObject *dh_o = HPy_CAST(uctx, DebugHandleObject, o); | ||
|
||
switch(op) { | ||
case HPy_EQ: | ||
return HPyBool_FromLong(uctx, dh_self->handle == dh_o->handle); | ||
case HPy_NE: | ||
return HPyBool_FromLong(uctx, dh_self->handle != dh_o->handle); | ||
default: | ||
return HPy_Dup(uctx, uctx->h_NotImplemented); | ||
} | ||
} | ||
|
||
HPyDef_SLOT(DebugHandle_repr, DebugHandle_repr_impl, HPy_tp_repr) | ||
static UHPy DebugHandle_repr_impl(HPyContext uctx, UHPy self) | ||
{ | ||
DebugHandleObject *dh = HPy_CAST(uctx, DebugHandleObject, self); | ||
UHPy uh_fmt = HPy_NULL; | ||
UHPy uh_id = HPy_NULL; | ||
UHPy uh_args = HPy_NULL; | ||
UHPy uh_result = HPy_NULL; | ||
|
||
uh_fmt = HPyUnicode_FromString(uctx, "<DebugHandle 0x%x for %r>"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could do with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good point, although we don't have |
||
if (HPy_IsNull(uh_fmt)) | ||
goto exit; | ||
|
||
uh_id = HPyLong_FromSsize_t(uctx, (HPy_ssize_t)dh->handle); | ||
if (HPy_IsNull(uh_id)) | ||
goto exit; | ||
|
||
uh_args = HPyTuple_FromArray(uctx, (UHPy[]){uh_id, dh->handle->uh}, 2); | ||
if (HPy_IsNull(uh_args)) | ||
goto exit; | ||
|
||
uh_result = HPy_Remainder(uctx, uh_fmt, uh_args); | ||
|
||
exit: | ||
HPy_Close(uctx, uh_fmt); | ||
HPy_Close(uctx, uh_id); | ||
HPy_Close(uctx, uh_args); | ||
return uh_result; | ||
} | ||
|
||
|
||
|
||
static HPyDef *DebugHandleType_defs[] = { | ||
&DebugHandle_obj, | ||
&DebugHandle_id, | ||
&DebugHandle_cmp, | ||
&DebugHandle_repr, | ||
NULL | ||
}; | ||
|
||
static HPyType_Spec DebugHandleType_spec = { | ||
.name = "hpy.debug._debug.DebugHandle", | ||
.basicsize = sizeof(DebugHandleObject), | ||
.flags = HPy_TPFLAGS_DEFAULT, | ||
.defines = DebugHandleType_defs, | ||
}; | ||
|
||
|
||
static UHPy new_DebugHandleObj(HPyContext uctx, UHPy u_DebugHandleType, | ||
DebugHandle *handle) | ||
{ | ||
DebugHandleObject *dhobj; | ||
UHPy u_result = HPy_New(uctx, u_DebugHandleType, &dhobj); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to put the DebugHandle type on the context like we do for other build-in types like List? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this is also a good illustration of the slight hopping one has to do at the moment since global type objects are gone -- and the hopping is actually not too bad in simple cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
no, because it's not a builtin. Why should it be different than, say,
exactly, that's the point. We need a way to have per-module "globals" at the C level, which we still don't have :( |
||
dhobj->handle = handle; | ||
return u_result; | ||
} | ||
|
||
|
||
/* ~~~~~~ definition of the module hpy.debug._debug ~~~~~~~ */ | ||
|
||
static HPyDef *module_defines[] = { | ||
&new_generation, | ||
&get_open_handles, | ||
NULL | ||
}; | ||
|
||
static HPyModuleDef moduledef = { | ||
HPyModuleDef_HEAD_INIT, | ||
.m_name = "hpy.debug._debug", | ||
.m_doc = "HPy debug mode", | ||
.m_size = -1, | ||
.defines = module_defines | ||
}; | ||
|
||
|
||
HPy_MODINIT(_debug) | ||
static UHPy init__debug_impl(HPyContext uctx) | ||
{ | ||
UHPy m = HPyModule_Create(uctx, &moduledef); | ||
if (HPy_IsNull(m)) | ||
return HPy_NULL; | ||
|
||
UHPy h_DebugHandleType = HPyType_FromSpec(uctx, &DebugHandleType_spec, NULL); | ||
if (HPy_IsNull(h_DebugHandleType)) | ||
return HPy_NULL; | ||
HPy_SetAttr_s(uctx, m, "DebugHandle", h_DebugHandleType); | ||
HPy_Close(uctx, h_DebugHandleType); | ||
return m; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we perhaps leave a comment on why
debug_ctx.c
had to be blacklisted?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good idea. The full explanation is in the log message of cd8cd6e, I added a comment which redirects to it