diff --git a/Modules/_hotshot.c b/Modules/_hotshot.c index 2dbabccaaa5..b17be9fa57c 100644 --- a/Modules/_hotshot.c +++ b/Modules/_hotshot.c @@ -82,6 +82,7 @@ typedef struct { typedef struct { PyObject_HEAD + PyObject *info; FILE *logfp; int filled; int index; @@ -257,6 +258,7 @@ logreader_tp_iter(LogReaderObject *self) #define ERR_NONE 0 #define ERR_EOF -1 #define ERR_EXCEPTION -2 +#define ERR_BAD_RECTYPE -3 #define PISIZE (sizeof(int) + 1) #define MPISIZE (PISIZE + 1) @@ -326,6 +328,73 @@ unpack_string(LogReaderObject *self, PyObject **pvalue) } +static int +unpack_add_info(LogReaderObject *self, int skip_opcode) +{ + PyObject *key; + PyObject *value = NULL; + int err; + + if (skip_opcode) { + if (self->buffer[self->index] != WHAT_ADD_INFO) + return ERR_BAD_RECTYPE; + self->index++; + } + err = unpack_string(self, &key); + if (!err) { + err = unpack_string(self, &value); + if (err) + Py_DECREF(key); + else { + PyObject *list = PyDict_GetItem(self->info, key); + if (list == NULL) { + list = PyList_New(0); + if (list == NULL) { + err = ERR_EXCEPTION; + goto finally; + } + if (PyDict_SetItem(self->info, key, list)) { + err = ERR_EXCEPTION; + goto finally; + } + } + if (PyList_Append(list, value)) + err = ERR_EXCEPTION; + } + } + finally: + Py_XDECREF(key); + Py_XDECREF(value); + return err; +} + + +static void +logreader_refill(LogReaderObject *self) +{ + int needed; + size_t res; + + if (self->index) { + memmove(self->buffer, &self->buffer[self->index], + self->filled - self->index); + self->filled = self->filled - self->index; + self->index = 0; + } + needed = BUFFERSIZE - self->filled; + if (needed > 0) { + res = fread(&self->buffer[self->filled], 1, needed, self->logfp); + self->filled += res; + } +} + +static void +eof_error(void) +{ + PyErr_SetString(PyExc_EOFError, + "end of file with incomplete profile record"); +} + static PyObject * logreader_tp_iternext(LogReaderObject *self) { @@ -346,21 +415,9 @@ logreader_tp_iternext(LogReaderObject *self) return NULL; } restart: - if ((self->filled - self->index) < MAXEVENTSIZE) { - /* add a little to the buffer */ - int needed; - size_t res; - refill: - if (self->index) { - memmove(self->buffer, &self->buffer[self->index], - self->filled - self->index); - self->filled = self->filled - self->index; - self->index = 0; - } - needed = BUFFERSIZE - self->filled; - res = fread(&self->buffer[self->filled], 1, needed, self->logfp); - self->filled += res; - } + if ((self->filled - self->index) < MAXEVENTSIZE) + logreader_refill(self); + /* end of input */ if (self->filled == 0) return NULL; @@ -391,14 +448,7 @@ logreader_tp_iternext(LogReaderObject *self) err = unpack_packed_int(self, &tdelta, 0); break; case WHAT_ADD_INFO: - err = unpack_string(self, &s1); - if (!err) { - err = unpack_string(self, &s2); - if (err) { - Py_DECREF(s1); - s1 = NULL; - } - } + err = unpack_add_info(self, 0); break; case WHAT_DEFINE_FILE: err = unpack_packed_int(self, &fileno, 0); @@ -426,6 +476,7 @@ logreader_tp_iternext(LogReaderObject *self) self->index++; goto restart; } + break; case WHAT_FRAME_TIMES: if (self->index >= self->filled) err = ERR_EOF; @@ -434,8 +485,9 @@ logreader_tp_iternext(LogReaderObject *self) self->index++; goto restart; } + break; default: - ; + err = ERR_BAD_RECTYPE; } if (err == ERR_EOF && oldindex != 0) { /* It looks like we ran out of data before we had it all; this @@ -443,12 +495,15 @@ logreader_tp_iternext(LogReaderObject *self) * data. Try forcing the buffer to be re-filled before failing. */ err = ERR_NONE; - goto refill; + logreader_refill(self); } - if (err == ERR_EOF) { + if (err == ERR_BAD_RECTYPE) { + PyErr_SetString(PyExc_ValueError, + "unknown record type in log file"); + } + else if (err == ERR_EOF) { /* Could not avoid end-of-buffer error. */ - PyErr_SetString(PyExc_EOFError, - "end of file with incomplete profile record"); + eof_error(); } else if (!err) { result = PyTuple_New(4); @@ -973,6 +1028,28 @@ is_available(ProfilerObject *self) /* Profiler object interface methods. */ +static char addinfo__doc__[] = +"addinfo(key, value)\n" +"Insert an ADD_INFO record into the log."; + +static PyObject * +profiler_addinfo(ProfilerObject *self, PyObject *args) +{ + PyObject *result = NULL; + char *key, *value; + + if (PyArg_ParseTuple(args, "ss:addinfo", &key, &value)) { + if (self->logfp == NULL) + PyErr_SetString(ProfilerError, "profiler already closed"); + else { + pack_add_info(self, key, value); + result = Py_None; + Py_INCREF(result); + } + } + return result; +} + static char close__doc__[] = "close()\n" "Shut down this profiler and close the log files, even if its active."; @@ -1109,6 +1186,7 @@ profiler_dealloc(ProfilerObject *self) * more easily, requiring only the changes to the dispatcher to be made. */ static PyMethodDef profiler_methods[] = { + {"addinfo", (PyCFunction)profiler_addinfo, METH_VARARGS, addinfo__doc__}, {"close", (PyCFunction)profiler_close, METH_VARARGS, close__doc__}, {"runcall", (PyCFunction)profiler_runcall, METH_VARARGS, runcall__doc__}, {"runcode", (PyCFunction)profiler_runcode, METH_VARARGS, runcode__doc__}, @@ -1168,7 +1246,7 @@ static char profiler_object__doc__[] = static PyTypeObject ProfilerType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ - "HotShot-profiler", /* tp_name */ + "_hotshot.ProfilerType", /* tp_name */ (int) sizeof(ProfilerObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)profiler_dealloc, /* tp_dealloc */ @@ -1186,7 +1264,7 @@ static PyTypeObject ProfilerType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - 0, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ profiler_object__doc__, /* tp_doc */ }; @@ -1200,8 +1278,12 @@ static PyMethodDef logreader_methods[] = { }; static PyObject * -logreader_getattr(ProfilerObject *self, char *name) +logreader_getattr(LogReaderObject *self, char *name) { + if (strcmp(name, "info") == 0) { + Py_INCREF(self->info); + return self->info; + } return Py_FindMethod(logreader_methods, (PyObject *)self, name); } @@ -1226,7 +1308,7 @@ static PySequenceMethods logreader_as_sequence = { static PyTypeObject LogReaderType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ - "HotShot-logreader", /* tp_name */ + "_hotshot.LogReaderType", /* tp_name */ (int) sizeof(LogReaderObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)logreader_dealloc, /* tp_dealloc */ @@ -1244,7 +1326,7 @@ static PyTypeObject LogReaderType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - 0, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ logreader__doc__, /* tp_doc */ #if Py_TPFLAGS_HAVE_ITER 0, /* tp_traverse */ @@ -1269,14 +1351,45 @@ hotshot_logreader(PyObject *unused, PyObject *args) self->index = 0; self->frametimings = 1; self->linetimings = 0; + self->info = NULL; self->logfp = fopen(filename, "rb"); if (self->logfp == NULL) { PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename); Py_DECREF(self); self = NULL; + goto finally; + } + self->info = PyDict_New(); + if (self->info == NULL) { + Py_DECREF(self); + goto finally; + } + /* Aggressively attempt to load all preliminary ADD_INFO + * records from the log so the info records are available + * from a fresh logreader object. + */ + logreader_refill(self); + while (self->filled > self->index + && self->buffer[self->index] == WHAT_ADD_INFO) { + int err = unpack_add_info(self, 1); + if (err) { + if (err == ERR_EOF) + eof_error(); + else + PyErr_SetString(PyExc_RuntimeError, + "unexpected error"); + break; + } + /* Refill agressively so we can avoid EOF during + * initialization unless there's a real EOF condition + * (the tp_iternext handler loops attempts to refill + * and try again). + */ + logreader_refill(self); } } } + finally: return (PyObject *) self; } @@ -1322,30 +1435,32 @@ write_header(ProfilerObject *self) PyErr_NoMemory(); return -1; } - pack_add_info(self, "HotShot-Version", buffer); - pack_add_info(self, "Requested-Line-Events", + pack_add_info(self, "hotshot-version", buffer); + pack_add_info(self, "requested-frame-timings", + (self->frametimings ? "yes" : "no")); + pack_add_info(self, "requested-line-events", (self->lineevents ? "yes" : "no")); - pack_add_info(self, "Platform", Py_GetPlatform()); - pack_add_info(self, "Executable", Py_GetProgramFullPath()); + pack_add_info(self, "requested-line-timings", + (self->linetimings ? "yes" : "no")); + pack_add_info(self, "platform", Py_GetPlatform()); + pack_add_info(self, "executable", Py_GetProgramFullPath()); buffer = (char *) Py_GetVersion(); if (buffer == NULL) PyErr_Clear(); else - pack_add_info(self, "Executable-Version", buffer); + pack_add_info(self, "executable-version", buffer); #ifdef MS_WIN32 sprintf(cwdbuffer, "%I64d", frequency.QuadPart); - pack_add_info(self, "Reported-Performance-Frequency", cwdbuffer); + pack_add_info(self, "reported-performance-frequency", cwdbuffer); #else sprintf(cwdbuffer, "%lu", rusage_diff); - pack_add_info(self, "Observed-Interval-getrusage", cwdbuffer); + pack_add_info(self, "observed-interval-getrusage", cwdbuffer); sprintf(cwdbuffer, "%lu", timeofday_diff); - pack_add_info(self, "Observed-Interval-gettimeofday", cwdbuffer); + pack_add_info(self, "observed-interval-gettimeofday", cwdbuffer); #endif - pack_frame_times(self); - pack_line_times(self); - pack_add_info(self, "Current-Directory", + pack_add_info(self, "current-directory", getcwd(cwdbuffer, sizeof cwdbuffer)); temp = PySys_GetObject("path"); @@ -1355,8 +1470,11 @@ write_header(ProfilerObject *self) buffer = PyString_AsString(item); if (buffer == NULL) return -1; - pack_add_info(self, "Sys-Path-Entry", buffer); + pack_add_info(self, "sys-path-entry", buffer); } + pack_frame_times(self); + pack_line_times(self); + return 0; }