Make the low-level log-reader object export a dictionary mapping keys

to lists of values, giving the contents of all the ADD_INFO records
seen so far.  This is initialized agressively when the log file is
opened, so that whoever is looking at the log reader can always see
the initial data loaded into the data stream.  ADD_INFO events later
in the log file continue to be reported to the application layer as
before.

Add a new method, addinfo(), to the profiler.  This can be used to
insert additional ADD_INFO records into the profiler log.

Fix the tp_flags and tp_name slots on the type objects.
This commit is contained in:
Fred Drake 2001-10-29 20:45:57 +00:00
parent 1117d930fa
commit 4c2e1af890
1 changed files with 163 additions and 45 deletions

View File

@ -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;
}