1647 lines · 45.3 KB · C
RawDownload
/* * This is the High Performance Python Profiler portion of HotShot. */ #include "Python.h"#include "code.h"#include "eval.h"#include "frameobject.h"#include "structmember.h" /* * Which timer to use should be made more configurable, but that should not * be difficult.  This will do for now. */#ifdef MS_WINDOWS#include <windows.h> #ifdef HAVE_DIRECT_H#include <direct.h>    /* for getcwd() */#endif typedef __int64 hs_time;#define GETTIMEOFDAY(P_HS_TIME) \	{ LARGE_INTEGER _temp; \	  QueryPerformanceCounter(&_temp); \	  *(P_HS_TIME) = _temp.QuadPart; }	   #else#ifndef HAVE_GETTIMEOFDAY#error "This module requires gettimeofday() on non-Windows platforms!"#endif#if (defined(PYOS_OS2) && defined(PYCC_GCC)) || defined(__QNX__)#include <sys/time.h>#else#include <sys/resource.h>#include <sys/times.h>#endiftypedef struct timeval hs_time;#endif #if !defined(__cplusplus) && !defined(inline)#ifdef __GNUC__#define inline __inline#endif#endif #ifndef inline#define inline#endif #define BUFFERSIZE 10240 #if defined(PYOS_OS2) && defined(PYCC_GCC)#define PATH_MAX 260#endif #if defined(__sgi) && _COMPILER_VERSION>700 && !defined(PATH_MAX)/* fix PATH_MAX not being defined with MIPSPro 7.x   if mode is ANSI C (default) */#define PATH_MAX 1024#endif #ifndef PATH_MAX#   ifdef MAX_PATH#       define PATH_MAX MAX_PATH#   elif defined (_POSIX_PATH_MAX)#       define PATH_MAX _POSIX_PATH_MAX#   else#       error "Need a defn. for PATH_MAX in _hotshot.c"#   endif#endif typedef struct {    PyObject_HEAD    PyObject *filemap;    PyObject *logfilename;    Py_ssize_t index;    unsigned char buffer[BUFFERSIZE];    FILE *logfp;    int lineevents;    int linetimings;    int frametimings;    /* size_t filled; */    int active;    int next_fileno;    hs_time prev_timeofday;} ProfilerObject; typedef struct {    PyObject_HEAD    PyObject *info;    FILE *logfp;    int linetimings;    int frametimings;} LogReaderObject; static PyObject * ProfilerError = NULL;  #ifndef MS_WINDOWS#ifdef GETTIMEOFDAY_NO_TZ#define GETTIMEOFDAY(ptv) gettimeofday((ptv))#else#define GETTIMEOFDAY(ptv) gettimeofday((ptv), (struct timezone *)NULL)#endif#endif  /* The log reader... */ PyDoc_STRVAR(logreader_close__doc__,"close()\n""Close the log file, preventing additional records from being read."); static PyObject *logreader_close(LogReaderObject *self, PyObject *args){    if (self->logfp != NULL) {        fclose(self->logfp);        self->logfp = NULL;    }    Py_INCREF(Py_None);     return Py_None;} PyDoc_STRVAR(logreader_fileno__doc__,"fileno() -> file descriptor\n""Returns the file descriptor for the log file, if open.\n""Raises ValueError if the log file is closed."); static PyObject *logreader_fileno(LogReaderObject *self){    if (self->logfp == NULL) {        PyErr_SetString(PyExc_ValueError,                        "logreader's file object already closed");        return NULL;    }    return PyInt_FromLong(fileno(self->logfp));}  /* Log File Format * --------------- * * The log file consists of a sequence of variable-length records. * Each record is identified with a record type identifier in two * bits of the first byte.  The two bits are the "least significant" * bits of the byte. * * Low bits:    Opcode:        Meaning: *       0x00         ENTER     enter a frame *       0x01          EXIT     exit a frame *       0x02        LINENO     execution moved onto a different line *       0x03         OTHER     more bits are needed to deecode * * If the type is OTHER, the record is not packed so tightly, and the * remaining bits are used to disambiguate the record type.  These * records are not used as frequently so compaction is not an issue. * Each of the first three record types has a highly tailored * structure that allows it to be packed tightly. * * The OTHER records have the following identifiers: * * First byte:  Opcode:        Meaning: *       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 * * "Packed integers" are non-negative integer values encoded as a * sequence of bytes.  Each byte is encoded such that the most * significant bit is set if the next byte is also part of the * integer.  Each byte provides bits to the least-significant end of * the result; the accumulated value must be shifted up to place the * new bits into the result. * * "Modified packed integers" are packed integers where only a portion * of the first byte is used.  In the rest of the specification, these * are referred to as "MPI(n,name)", where "n" is the number of bits * discarded from the least-signicant positions of the byte, and * "name" is a name being given to those "discarded" bits, since they * are a field themselves. * * ENTER records: * *      MPI(2,type)  fileno          -- type is 0x00 *      PI           lineno *      PI           tdelta          -- iff frame times are enabled * * EXIT records * *      MPI(2,type)  tdelta          -- type is 0x01; tdelta will be 0 *                                      if frame times are disabled * * LINENO records * *      MPI(2,type)  lineno          -- type is 0x02 *      PI           tdelta          -- iff LINENO includes it * * ADD_INFO records * *      BYTE         type            -- always 0x13 *      PI           len1            -- length of first string *      BYTE         string1[len1]   -- len1 bytes of string data *      PI           len2            -- length of second string *      BYTE         string2[len2]   -- len2 bytes of string data * * DEFINE_FILE records * *      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 * * 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#define WHAT_EXIT         0x01#define WHAT_LINENO       0x02#define WHAT_OTHER        0x03  /* only used in decoding */#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#define ERR_EXCEPTION    -2#define ERR_BAD_RECTYPE  -3 #define PISIZE            (sizeof(int) + 1)#define MPISIZE           (PISIZE + 1) /* Maximum size of "normal" events -- nothing that contains string data */#define MAXEVENTSIZE      (MPISIZE + PISIZE*2)  /* Unpack a packed integer; if "discard" is non-zero, unpack a modified * packed integer with "discard" discarded bits. */static intunpack_packed_int(LogReaderObject *self, int *pvalue, int discard){    int c;    int accum = 0;    int bits = 0;    int cont;     do {        /* read byte */	if ((c = fgetc(self->logfp)) == EOF)            return ERR_EOF;        accum |= ((c & 0x7F) >> discard) << bits;        bits += (7 - discard);        cont = c & 0x80;        discard = 0;    } while (cont);     *pvalue = accum;     return 0;} /* Unpack a string, which is encoded as a packed integer giving the * length of the string, followed by the string data. */static intunpack_string(LogReaderObject *self, PyObject **pvalue){    int i;    int len;    int err;    int ch;    char *buf;        if ((err = unpack_packed_int(self, &len, 0)))        return err;     buf = (char *)malloc(len);    if (!buf) {	PyErr_NoMemory();	return ERR_EXCEPTION;    }     for (i=0; i < len; i++) {        ch = fgetc(self->logfp);	buf[i] = ch;        if (ch == EOF) {            free(buf);            return ERR_EOF;        }    }    *pvalue = PyString_FromStringAndSize(buf, len);    free(buf);    if (*pvalue == NULL) {        return ERR_EXCEPTION;    }    return 0;}  static intunpack_add_info(LogReaderObject *self){    PyObject *key;    PyObject *value = NULL;    int err;     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)) {                    Py_DECREF(list);                    err = ERR_EXCEPTION;                    goto finally;                }                Py_DECREF(list);            }            if (PyList_Append(list, value))                err = ERR_EXCEPTION;        }    } finally:    Py_XDECREF(key);    Py_XDECREF(value);    return err;}  static voideof_error(LogReaderObject *self){    fclose(self->logfp);    self->logfp = NULL;    PyErr_SetString(PyExc_EOFError,                    "end of file with incomplete profile record");} static PyObject *logreader_tp_iternext(LogReaderObject *self){    int c;    int what;    int err = ERR_NONE;    int lineno = -1;    int fileno = -1;    int tdelta = -1;    PyObject *s1 = NULL, *s2 = NULL;    PyObject *result = NULL;#if 0    unsigned char b0, b1;#endif     if (self->logfp == NULL) {        PyErr_SetString(ProfilerError,                        "cannot iterate over closed LogReader object");        return NULL;    } restart:    /* decode the record type */    if ((c = fgetc(self->logfp)) == EOF) {        fclose(self->logfp);        self->logfp = NULL;        return NULL;    }    what = c & WHAT_OTHER;    if (what == WHAT_OTHER)        what = c; /* need all the bits for type */    else        ungetc(c, self->logfp); /* type byte includes packed int */     switch (what) {    case WHAT_ENTER:        err = unpack_packed_int(self, &fileno, 2);        if (!err) {            err = unpack_packed_int(self, &lineno, 0);            if (self->frametimings && !err)                err = unpack_packed_int(self, &tdelta, 0);        }        break;    case WHAT_EXIT:        err = unpack_packed_int(self, &tdelta, 2);        break;    case WHAT_LINENO:        err = unpack_packed_int(self, &lineno, 2);        if (self->linetimings && !err)            err = unpack_packed_int(self, &tdelta, 0);        break;    case WHAT_ADD_INFO:        err = unpack_add_info(self);        break;    case WHAT_DEFINE_FILE:        err = unpack_packed_int(self, &fileno, 0);        if (!err) {            err = unpack_string(self, &s1);            if (!err) {                Py_INCREF(Py_None);                s2 = Py_None;            }        }        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 ((c = fgetc(self->logfp)) == EOF)            err = ERR_EOF;        else {            self->linetimings = c ? 1 : 0;	    goto restart;	}        break;    case WHAT_FRAME_TIMES:        if ((c = fgetc(self->logfp)) == EOF)            err = ERR_EOF;        else {            self->frametimings = c ? 1 : 0;	    goto restart;	}        break;    default:        err = ERR_BAD_RECTYPE;    }    if (err == ERR_BAD_RECTYPE) {        PyErr_SetString(PyExc_ValueError,                        "unknown record type in log file");    }    else if (err == ERR_EOF) {        eof_error(self);    }    else if (!err) {        result = PyTuple_New(4);        if (result == NULL)            return NULL;        PyTuple_SET_ITEM(result, 0, PyInt_FromLong(what));        PyTuple_SET_ITEM(result, 2, PyInt_FromLong(fileno));        if (s1 == NULL)            PyTuple_SET_ITEM(result, 1, PyInt_FromLong(tdelta));        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.     */

Lines 1–500 of 1647.