diff options
Diffstat (limited to 'Modules/_hotshot.c')
-rw-r--r-- | Modules/_hotshot.c | 228 |
1 files changed, 177 insertions, 51 deletions
diff --git a/Modules/_hotshot.c b/Modules/_hotshot.c index 4a6d7fc..16af56f 100644 --- a/Modules/_hotshot.c +++ b/Modules/_hotshot.c @@ -65,10 +65,10 @@ typedef struct { FILE *logfp; int lineevents; int linetimings; + int frametimings; /* size_t filled; */ int active; int next_fileno; - long callcount; hs_time prev_timeofday; } ProfilerObject; @@ -78,6 +78,7 @@ typedef struct { int filled; int index; int linetimings; + int frametimings; unsigned char buffer[BUFFERSIZE]; } LogReaderObject; @@ -154,6 +155,8 @@ logreader_tp_iter(LogReaderObject *self) * 0x13 ADD_INFO define a key/value pair * 0x23 DEFINE_FILE define an int->filename mapping * 0x33 LINE_TIMES indicates if LINENO events have tdeltas + * 0x43 DEFINE_FUNC define a (fileno,lineno)->funcname mapping + * 0x53 FRAME_TIMES indicates if ENTER/EXIT events have tdeltas * * Packed Integers * @@ -174,12 +177,13 @@ logreader_tp_iter(LogReaderObject *self) * ENTER records: * * MPI(2,type) fileno -- type is 0x00 - * PI tdelta * PI lineno + * PI tdelta -- iff frame times are enabled * * EXIT records * - * MPI(2,type) tdelta -- type is 0x01 + * MPI(2,type) tdelta -- type is 0x01; tdelta will be 0 + * if frame times are disabled * * LINENO records * @@ -188,7 +192,7 @@ logreader_tp_iter(LogReaderObject *self) * * ADD_INFO records * - * BYTE type -- always 0x03 + * BYTE type -- always 0x13 * PI len1 -- length of first string * BYTE string1[len1] -- len1 bytes of string data * PI len2 -- length of second string @@ -196,15 +200,40 @@ logreader_tp_iter(LogReaderObject *self) * * DEFINE_FILE records * - * BYTE type -- always 0x13 + * BYTE type -- always 0x23 * PI fileno * PI len -- length of filename * BYTE filename[len] -- len bytes of string data * + * DEFINE_FUNC records + * + * BYTE type -- always 0x43 + * PI fileno + * PI lineno + * PI len -- length of funcname + * BYTE funcname[len] -- len bytes of string data + * * LINE_TIMES records - * BYTE type -- always 0x23 + * + * This record can be used only before the start of ENTER/EXIT/LINENO + * records. If have_tdelta is true, LINENO records will include the + * tdelta field, otherwise it will be omitted. If this record is not + * given, LINENO records will not contain the tdelta field. + * + * BYTE type -- always 0x33 * BYTE have_tdelta -- 0 if LINENO does *not* have * timing information + * FRAME_TIMES records + * + * This record can be used only before the start of ENTER/EXIT/LINENO + * records. If have_tdelta is true, ENTER and EXIT records will + * include the tdelta field, otherwise it will be omitted. If this + * record is not given, ENTER and EXIT records will contain the tdelta + * field. + * + * BYTE type -- always 0x53 + * BYTE have_tdelta -- 0 if ENTER/EXIT do *not* have + * timing information */ #define WHAT_ENTER 0x00 @@ -214,6 +243,8 @@ logreader_tp_iter(LogReaderObject *self) #define WHAT_ADD_INFO 0x13 #define WHAT_DEFINE_FILE 0x23 #define WHAT_LINE_TIMES 0x33 +#define WHAT_DEFINE_FUNC 0x43 +#define WHAT_FRAME_TIMES 0x53 #define ERR_NONE 0 #define ERR_EOF -1 @@ -338,9 +369,9 @@ logreader_tp_iternext(LogReaderObject *self) case WHAT_ENTER: err = unpack_packed_int(self, &fileno, 2); if (!err) { - err = unpack_packed_int(self, &tdelta, 0); - if (!err) - err = unpack_packed_int(self, &lineno, 0); + err = unpack_packed_int(self, &lineno, 0); + if (self->frametimings && !err) + err = unpack_packed_int(self, &tdelta, 0); } break; case WHAT_EXIT: @@ -371,6 +402,14 @@ logreader_tp_iternext(LogReaderObject *self) } } break; + case WHAT_DEFINE_FUNC: + err = unpack_packed_int(self, &fileno, 0); + if (!err) { + err = unpack_packed_int(self, &lineno, 0); + if (!err) + err = unpack_string(self, &s1); + } + break; case WHAT_LINE_TIMES: if (self->index >= self->filled) err = ERR_EOF; @@ -379,6 +418,14 @@ logreader_tp_iternext(LogReaderObject *self) self->index++; goto restart; } + case WHAT_FRAME_TIMES: + if (self->index >= self->filled) + err = ERR_EOF; + else { + self->frametimings = self->buffer[self->index] ? 1 : 0; + self->index++; + goto restart; + } default: ; } @@ -399,14 +446,14 @@ logreader_tp_iternext(LogReaderObject *self) result = PyTuple_New(4); PyTuple_SET_ITEM(result, 0, PyInt_FromLong(what)); PyTuple_SET_ITEM(result, 2, PyInt_FromLong(fileno)); - if (s1 == NULL) { + if (s1 == NULL) PyTuple_SET_ITEM(result, 1, PyInt_FromLong(tdelta)); - PyTuple_SET_ITEM(result, 3, PyInt_FromLong(lineno)); - } - else { + else PyTuple_SET_ITEM(result, 1, s1); + if (s2 == NULL) + PyTuple_SET_ITEM(result, 3, PyInt_FromLong(lineno)); + else PyTuple_SET_ITEM(result, 3, s2); - } } /* The only other case is err == ERR_EXCEPTION, in which case the * exception is already set. @@ -550,13 +597,11 @@ pack_modified_packed_int(ProfilerObject *self, int value, } static void -pack_string(ProfilerObject *self, const char *s) +pack_string(ProfilerObject *self, const char *s, int len) { - int len = strlen(s); - - pack_packed_int(self, len); - if (len + self->index >= BUFFERSIZE) + if (len + PISIZE + self->index >= BUFFERSIZE) (void) flush_data(self); + pack_packed_int(self, len); memcpy(self->buffer + self->index, s, len); self->index += len; } @@ -571,8 +616,8 @@ pack_add_info(ProfilerObject *self, const char *s1, const char *s2) (void) flush_data(self); self->buffer[self->index] = WHAT_ADD_INFO; self->index++; - pack_string(self, s1); - pack_string(self, s2); + pack_string(self, s1, len1); + pack_string(self, s2, len2); } static void @@ -585,7 +630,22 @@ pack_define_file(ProfilerObject *self, int fileno, const char *filename) self->buffer[self->index] = WHAT_DEFINE_FILE; self->index++; pack_packed_int(self, fileno); - pack_string(self, filename); + pack_string(self, filename, len); +} + +static void +pack_define_func(ProfilerObject *self, int fileno, int lineno, + const char *funcname) +{ + int len = strlen(funcname); + + if (len + PISIZE*3 + 1 + self->index >= BUFFERSIZE) + (void) flush_data(self); + self->buffer[self->index] = WHAT_DEFINE_FUNC; + self->index++; + pack_packed_int(self, fileno); + pack_packed_int(self, lineno); + pack_string(self, funcname, len); } static void @@ -598,14 +658,25 @@ pack_line_times(ProfilerObject *self) self->index += 2; } +static void +pack_frame_times(ProfilerObject *self) +{ + if (2 + self->index >= BUFFERSIZE) + (void) flush_data(self); + self->buffer[self->index] = WHAT_FRAME_TIMES; + self->buffer[self->index + 1] = self->frametimings ? 1 : 0; + self->index += 2; +} + static inline void pack_enter(ProfilerObject *self, int fileno, int tdelta, int lineno) { if (MPISIZE + PISIZE*2 + self->index >= BUFFERSIZE) (void) flush_data(self); pack_modified_packed_int(self, fileno, 2, WHAT_ENTER); - pack_packed_int(self, tdelta); pack_packed_int(self, lineno); + if (self->frametimings) + pack_packed_int(self, tdelta); } static inline void @@ -613,7 +684,12 @@ pack_exit(ProfilerObject *self, int tdelta) { if (MPISIZE + self->index >= BUFFERSIZE) (void) flush_data(self); - pack_modified_packed_int(self, tdelta, 2, WHAT_EXIT); + if (self->frametimings) + pack_modified_packed_int(self, tdelta, 2, WHAT_EXIT); + else { + self->buffer[self->index] = WHAT_EXIT; + self->index++; + } } static inline void @@ -636,28 +712,51 @@ pack_lineno_tdelta(ProfilerObject *self, int lineno, int tdelta) static inline int get_fileno(ProfilerObject *self, PyCodeObject *fcode) { - PyObject *idobj; + /* This is only used for ENTER events. */ + + PyObject *obj; + PyObject *dict; int fileno; - idobj = PyDict_GetItem(self->filemap, fcode->co_filename); - if (idobj == NULL) { + obj = PyDict_GetItem(self->filemap, fcode->co_filename); + if (obj == NULL) { /* first sighting of this file */ + dict = PyDict_New(); + if (dict == NULL) { + return -1; + } fileno = self->next_fileno; - idobj = PyInt_FromLong(fileno); - if (idobj == NULL) { + obj = Py_BuildValue("iN", fileno, dict); + if (obj == NULL) { return -1; } - if (PyDict_SetItem(self->filemap, fcode->co_filename, idobj)) { - Py_DECREF(idobj); + if (PyDict_SetItem(self->filemap, fcode->co_filename, obj)) { + Py_DECREF(obj); return -1; } self->next_fileno++; - Py_DECREF(idobj); + Py_DECREF(obj); pack_define_file(self, fileno, PyString_AS_STRING(fcode->co_filename)); } else { /* already know this ID */ - fileno = PyInt_AS_LONG(idobj); + fileno = PyInt_AS_LONG(PyTuple_GET_ITEM(obj, 0)); + dict = PyTuple_GET_ITEM(obj, 1); + } + /* make sure we save a function name for this (fileno, lineno) */ + obj = PyInt_FromLong(fcode->co_firstlineno); + if (obj == NULL) { + /* We just won't have it saved; too bad. */ + PyErr_Clear(); + } + else { + PyObject *name = PyDict_GetItem(dict, obj); + if (name == NULL) { + pack_define_func(self, fileno, fcode->co_firstlineno, + PyString_AS_STRING(fcode->co_name)); + if (PyDict_SetItem(dict, obj, fcode->co_name)) + return -1; + } } return fileno; } @@ -695,10 +794,11 @@ static int profiler_callback(ProfilerObject *self, PyFrameObject *frame, int what, PyObject *arg) { - int tdelta = get_tdelta(self); + int tdelta = -1; int fileno; - self->callcount++; + if (self->frametimings) + tdelta = get_tdelta(self); switch (what) { case PyTrace_CALL: fileno = get_fileno(self, frame->f_code); @@ -726,13 +826,12 @@ tracer_callback(ProfilerObject *self, PyFrameObject *frame, int what, { int fileno; - self->callcount++; switch (what) { case PyTrace_CALL: fileno = get_fileno(self, frame->f_code); if (fileno < 0) return -1; - pack_enter(self, fileno, get_tdelta(self), + pack_enter(self, fileno, self->frametimings ? get_tdelta(self) : -1, frame->f_code->co_firstlineno); break; case PyTrace_RETURN: @@ -1014,10 +1113,10 @@ static PyMethodDef profiler_methods[] = { * __members__ and therefore dir() to work. */ static struct memberlist profiler_members[] = { - {"callcount", T_LONG, offsetof(ProfilerObject, callcount), READONLY}, - {"closed", T_INT, -1, READONLY}, - {"lineevents", T_LONG, offsetof(ProfilerObject, lineevents), READONLY}, - {"linetimings", T_LONG, offsetof(ProfilerObject, linetimings), READONLY}, + {"closed", T_INT, -1, READONLY}, + {"frametimings", T_LONG, offsetof(ProfilerObject, linetimings), READONLY}, + {"lineevents", T_LONG, offsetof(ProfilerObject, lineevents), READONLY}, + {"linetimings", T_LONG, offsetof(ProfilerObject, linetimings), READONLY}, {NULL} }; @@ -1045,18 +1144,18 @@ static char profiler_object__doc__[] = "\n" "Methods:\n" "\n" -"close(): Stop the profiler and close the log files.\n" -"runcall(): Run a single function call with profiling enabled.\n" -"runcode(): Execute a code object with profiling enabled.\n" -"start(): Install the profiler and return.\n" -"stop(): Remove the profiler.\n" +"close(): Stop the profiler and close the log files.\n" +"runcall(): Run a single function call with profiling enabled.\n" +"runcode(): Execute a code object with profiling enabled.\n" +"start(): Install the profiler and return.\n" +"stop(): Remove the profiler.\n" "\n" "Attributes (read-only):\n" "\n" -"callcount: The number of low-level calls to the profiler.\n" -"closed: True if the profiler has already been closed.\n" -"lineevents: True if SET_LINENO events are reported to the profiler.\n" -"linetimings: True if SET_LINENO events collect timing information."; +"closed: True if the profiler has already been closed.\n" +"frametimings: True if ENTER/EXIT events collect timing information.\n" +"lineevents: True if SET_LINENO events are reported to the profiler.\n" +"linetimings: True if SET_LINENO events collect timing information."; static PyTypeObject ProfilerType = { PyObject_HEAD_INIT(NULL) @@ -1160,6 +1259,7 @@ hotshot_logreader(PyObject *unused, PyObject *args) if (self != NULL) { self->filled = 0; self->index = 0; + self->frametimings = 1; self->linetimings = 0; self->logfp = fopen(filename, "rb"); if (self->logfp == NULL) { @@ -1234,6 +1334,7 @@ write_header(ProfilerObject *self) sprintf(cwdbuffer, "%lu", timeofday_diff); pack_add_info(self, "Observed-Interval-gettimeofday", cwdbuffer); #endif + pack_frame_times(self); pack_line_times(self); pack_add_info(self, "Current-Directory", @@ -1268,7 +1369,7 @@ hotshot_profiler(PyObject *unused, PyObject *args) self = PyObject_New(ProfilerObject, &ProfilerType); if (self == NULL) return NULL; - self->callcount = 0; + self->frametimings = 1; self->lineevents = lineevents ? 1 : 0; self->linetimings = (lineevents && linetimings) ? 1 : 0; self->index = 0; @@ -1304,6 +1405,29 @@ hotshot_profiler(PyObject *unused, PyObject *args) return (PyObject *) self; } +static char coverage__doc__[] = "\ +coverage(logfilename) -> profiler\n\ +Returns a profiler that doesn't collect any timing information, which is\n\ +useful in building a coverage analysis tool."; + +static PyObject * +hotshot_coverage(PyObject *unused, PyObject *args) +{ + char *logfilename; + PyObject *result = NULL; + + if (PyArg_ParseTuple(args, "s:coverage", &logfilename)) { + result = hotshot_profiler(unused, args); + if (result != NULL) { + ProfilerObject *self = (ProfilerObject *) result; + self->frametimings = 0; + self->linetimings = 0; + self->lineevents = 1; + } + } + return result; +} + static char resolution__doc__[] = #ifdef MS_WIN32 "resolution() -> (performance-counter-ticks, update-frequency)\n" @@ -1338,6 +1462,7 @@ hotshot_resolution(PyObject *unused, PyObject *args) static PyMethodDef functions[] = { + {"coverage", hotshot_coverage, METH_VARARGS, coverage__doc__}, {"profiler", hotshot_profiler, METH_VARARGS, profiler__doc__}, {"logreader", hotshot_logreader, METH_VARARGS, logreader__doc__}, {"resolution", hotshot_resolution, METH_VARARGS, resolution__doc__}, @@ -1378,6 +1503,7 @@ init_hotshot(void) PyModule_AddIntConstant(module, "WHAT_OTHER", WHAT_OTHER); PyModule_AddIntConstant(module, "WHAT_ADD_INFO", WHAT_ADD_INFO); PyModule_AddIntConstant(module, "WHAT_DEFINE_FILE", WHAT_DEFINE_FILE); + PyModule_AddIntConstant(module, "WHAT_DEFINE_FUNC", WHAT_DEFINE_FUNC); PyModule_AddIntConstant(module, "WHAT_LINE_TIMES", WHAT_LINE_TIMES); } } |