2 * This is the High Performance Python Profiler portion of HotShot.
8 #include "frameobject.h"
9 #include "structmember.h"
12 * Which timer to use should be made more configurable, but that should not
13 * be difficult. This will do for now.
17 #include <direct.h> /* for getcwd() */
18 typedef __int64 hs_time
;
19 #define GETTIMEOFDAY(P_HS_TIME) \
20 { LARGE_INTEGER _temp; \
21 QueryPerformanceCounter(&_temp); \
22 *(P_HS_TIME) = _temp.QuadPart; }
26 #ifndef HAVE_GETTIMEOFDAY
27 #error "This module requires gettimeofday() on non-Windows platforms!"
29 #if defined(macintosh) || (defined(PYOS_OS2) && defined(PYCC_GCC))
32 #include <sys/resource.h>
33 #include <sys/times.h>
35 typedef struct timeval hs_time
;
38 #if !defined(__cplusplus) && !defined(inline)
40 #define inline __inline
48 #define BUFFERSIZE 10240
54 #if defined(PYOS_OS2) && defined(PYCC_GCC)
58 #if defined(__sgi) && _COMPILER_VERSION>700 && !defined(PATH_MAX)
59 /* fix PATH_MAX not being defined with MIPSPro 7.x
60 if mode is ANSI C (default) */
66 # define PATH_MAX MAX_PATH
67 # elif defined (_POSIX_PATH_MAX)
68 # define PATH_MAX _POSIX_PATH_MAX
70 # error "Need a defn. for PATH_MAX in _hotshot.c"
77 PyObject
*logfilename
;
79 unsigned char buffer
[BUFFERSIZE
];
87 hs_time prev_timeofday
;
98 static PyObject
* ProfilerError
= NULL
;
102 #ifdef GETTIMEOFDAY_NO_TZ
103 #define GETTIMEOFDAY(ptv) gettimeofday((ptv))
105 #define GETTIMEOFDAY(ptv) gettimeofday((ptv), (struct timezone *)NULL)
110 /* The log reader... */
112 PyDoc_STRVAR(logreader_close__doc__
,
114 "Close the log file, preventing additional records from being read.");
117 logreader_close(LogReaderObject
*self
, PyObject
*args
)
119 if (self
->logfp
!= NULL
) {
128 PyDoc_STRVAR(logreader_fileno__doc__
,
129 "fileno() -> file descriptor\n"
130 "Returns the file descriptor for the log file, if open.\n"
131 "Raises ValueError if the log file is closed.");
134 logreader_fileno(LogReaderObject
*self
)
136 if (self
->logfp
== NULL
) {
137 PyErr_SetString(PyExc_ValueError
,
138 "logreader's file object already closed");
141 return PyInt_FromLong(fileno(self
->logfp
));
145 logreader_tp_iter(LogReaderObject
*self
)
148 return (PyObject
*) self
;
155 * The log file consists of a sequence of variable-length records.
156 * Each record is identified with a record type identifier in two
157 * bits of the first byte. The two bits are the "least significant"
160 * Low bits: Opcode: Meaning:
161 * 0x00 ENTER enter a frame
162 * 0x01 EXIT exit a frame
163 * 0x02 LINENO execution moved onto a different line
164 * 0x03 OTHER more bits are needed to deecode
166 * If the type is OTHER, the record is not packed so tightly, and the
167 * remaining bits are used to disambiguate the record type. These
168 * records are not used as frequently so compaction is not an issue.
169 * Each of the first three record types has a highly tailored
170 * structure that allows it to be packed tightly.
172 * The OTHER records have the following identifiers:
174 * First byte: Opcode: Meaning:
175 * 0x13 ADD_INFO define a key/value pair
176 * 0x23 DEFINE_FILE define an int->filename mapping
177 * 0x33 LINE_TIMES indicates if LINENO events have tdeltas
178 * 0x43 DEFINE_FUNC define a (fileno,lineno)->funcname mapping
179 * 0x53 FRAME_TIMES indicates if ENTER/EXIT events have tdeltas
183 * "Packed integers" are non-negative integer values encoded as a
184 * sequence of bytes. Each byte is encoded such that the most
185 * significant bit is set if the next byte is also part of the
186 * integer. Each byte provides bits to the least-significant end of
187 * the result; the accumulated value must be shifted up to place the
188 * new bits into the result.
190 * "Modified packed integers" are packed integers where only a portion
191 * of the first byte is used. In the rest of the specification, these
192 * are referred to as "MPI(n,name)", where "n" is the number of bits
193 * discarded from the least-signicant positions of the byte, and
194 * "name" is a name being given to those "discarded" bits, since they
195 * are a field themselves.
199 * MPI(2,type) fileno -- type is 0x00
201 * PI tdelta -- iff frame times are enabled
205 * MPI(2,type) tdelta -- type is 0x01; tdelta will be 0
206 * if frame times are disabled
210 * MPI(2,type) lineno -- type is 0x02
211 * PI tdelta -- iff LINENO includes it
215 * BYTE type -- always 0x13
216 * PI len1 -- length of first string
217 * BYTE string1[len1] -- len1 bytes of string data
218 * PI len2 -- length of second string
219 * BYTE string2[len2] -- len2 bytes of string data
221 * DEFINE_FILE records
223 * BYTE type -- always 0x23
225 * PI len -- length of filename
226 * BYTE filename[len] -- len bytes of string data
228 * DEFINE_FUNC records
230 * BYTE type -- always 0x43
233 * PI len -- length of funcname
234 * BYTE funcname[len] -- len bytes of string data
238 * This record can be used only before the start of ENTER/EXIT/LINENO
239 * records. If have_tdelta is true, LINENO records will include the
240 * tdelta field, otherwise it will be omitted. If this record is not
241 * given, LINENO records will not contain the tdelta field.
243 * BYTE type -- always 0x33
244 * BYTE have_tdelta -- 0 if LINENO does *not* have
246 * FRAME_TIMES records
248 * This record can be used only before the start of ENTER/EXIT/LINENO
249 * records. If have_tdelta is true, ENTER and EXIT records will
250 * include the tdelta field, otherwise it will be omitted. If this
251 * record is not given, ENTER and EXIT records will contain the tdelta
254 * BYTE type -- always 0x53
255 * BYTE have_tdelta -- 0 if ENTER/EXIT do *not* have
259 #define WHAT_ENTER 0x00
260 #define WHAT_EXIT 0x01
261 #define WHAT_LINENO 0x02
262 #define WHAT_OTHER 0x03 /* only used in decoding */
263 #define WHAT_ADD_INFO 0x13
264 #define WHAT_DEFINE_FILE 0x23
265 #define WHAT_LINE_TIMES 0x33
266 #define WHAT_DEFINE_FUNC 0x43
267 #define WHAT_FRAME_TIMES 0x53
271 #define ERR_EXCEPTION -2
272 #define ERR_BAD_RECTYPE -3
274 #define PISIZE (sizeof(int) + 1)
275 #define MPISIZE (PISIZE + 1)
277 /* Maximum size of "normal" events -- nothing that contains string data */
278 #define MAXEVENTSIZE (MPISIZE + PISIZE*2)
281 /* Unpack a packed integer; if "discard" is non-zero, unpack a modified
282 * packed integer with "discard" discarded bits.
285 unpack_packed_int(LogReaderObject
*self
, int *pvalue
, int discard
)
294 if ((c
= fgetc(self
->logfp
)) == EOF
)
296 accum
|= ((c
& 0x7F) >> discard
) << bits
;
297 bits
+= (7 - discard
);
307 /* Unpack a string, which is encoded as a packed integer giving the
308 * length of the string, followed by the string data.
311 unpack_string(LogReaderObject
*self
, PyObject
**pvalue
)
319 if ((err
= unpack_packed_int(self
, &len
, 0)))
323 for (i
=0; i
< len
; i
++) {
324 ch
= fgetc(self
->logfp
);
331 *pvalue
= PyString_FromStringAndSize(buf
, len
);
333 if (*pvalue
== NULL
) {
334 return ERR_EXCEPTION
;
341 unpack_add_info(LogReaderObject
*self
)
344 PyObject
*value
= NULL
;
347 err
= unpack_string(self
, &key
);
349 err
= unpack_string(self
, &value
);
353 PyObject
*list
= PyDict_GetItem(self
->info
, key
);
355 list
= PyList_New(0);
360 if (PyDict_SetItem(self
->info
, key
, list
)) {
365 if (PyList_Append(list
, value
))
377 eof_error(LogReaderObject
*self
)
381 PyErr_SetString(PyExc_EOFError
,
382 "end of file with incomplete profile record");
386 logreader_tp_iternext(LogReaderObject
*self
)
394 PyObject
*s1
= NULL
, *s2
= NULL
;
395 PyObject
*result
= NULL
;
397 unsigned char b0
, b1
;
400 if (self
->logfp
== NULL
) {
401 PyErr_SetString(ProfilerError
,
402 "cannot iterate over closed LogReader object");
407 /* decode the record type */
408 if ((c
= fgetc(self
->logfp
)) == EOF
) {
413 what
= c
& WHAT_OTHER
;
414 if (what
== WHAT_OTHER
)
415 what
= c
; /* need all the bits for type */
417 ungetc(c
, self
->logfp
); /* type byte includes packed int */
421 err
= unpack_packed_int(self
, &fileno
, 2);
423 err
= unpack_packed_int(self
, &lineno
, 0);
424 if (self
->frametimings
&& !err
)
425 err
= unpack_packed_int(self
, &tdelta
, 0);
429 err
= unpack_packed_int(self
, &tdelta
, 2);
432 err
= unpack_packed_int(self
, &lineno
, 2);
433 if (self
->linetimings
&& !err
)
434 err
= unpack_packed_int(self
, &tdelta
, 0);
437 err
= unpack_add_info(self
);
439 case WHAT_DEFINE_FILE
:
440 err
= unpack_packed_int(self
, &fileno
, 0);
442 err
= unpack_string(self
, &s1
);
449 case WHAT_DEFINE_FUNC
:
450 err
= unpack_packed_int(self
, &fileno
, 0);
452 err
= unpack_packed_int(self
, &lineno
, 0);
454 err
= unpack_string(self
, &s1
);
457 case WHAT_LINE_TIMES
:
458 if ((c
= fgetc(self
->logfp
)) == EOF
)
461 self
->linetimings
= c
? 1 : 0;
465 case WHAT_FRAME_TIMES
:
466 if ((c
= fgetc(self
->logfp
)) == EOF
)
469 self
->frametimings
= c
? 1 : 0;
474 err
= ERR_BAD_RECTYPE
;
476 if (err
== ERR_BAD_RECTYPE
) {
477 PyErr_SetString(PyExc_ValueError
,
478 "unknown record type in log file");
480 else if (err
== ERR_EOF
) {
484 result
= PyTuple_New(4);
485 PyTuple_SET_ITEM(result
, 0, PyInt_FromLong(what
));
486 PyTuple_SET_ITEM(result
, 2, PyInt_FromLong(fileno
));
488 PyTuple_SET_ITEM(result
, 1, PyInt_FromLong(tdelta
));
490 PyTuple_SET_ITEM(result
, 1, s1
);
492 PyTuple_SET_ITEM(result
, 3, PyInt_FromLong(lineno
));
494 PyTuple_SET_ITEM(result
, 3, s2
);
496 /* The only other case is err == ERR_EXCEPTION, in which case the
497 * exception is already set.
500 b0
= self
->buffer
[self
->index
];
501 b1
= self
->buffer
[self
->index
+ 1];
503 /* This is a line-number event. */
505 lineno
= ((b0
& ~1) << 7) + b1
;
509 what
= (b0
& 0x0E) >> 1;
510 tdelta
= ((b0
& 0xF0) << 4) + b1
;
511 if (what
== PyTrace_CALL
) {
512 /* we know there's a 2-byte file ID & 2-byte line number */
513 fileno
= ((self
->buffer
[self
->index
+ 2] << 8)
514 + self
->buffer
[self
->index
+ 3]);
515 lineno
= ((self
->buffer
[self
->index
+ 4] << 8)
516 + self
->buffer
[self
->index
+ 5]);
527 logreader_dealloc(LogReaderObject
*self
)
529 if (self
->logfp
!= NULL
) {
537 logreader_sq_item(LogReaderObject
*self
, int index
)
539 PyObject
*result
= logreader_tp_iternext(self
);
540 if (result
== NULL
&& !PyErr_Occurred()) {
541 PyErr_SetString(PyExc_IndexError
, "no more events in log");
548 do_stop(ProfilerObject
*self
);
551 flush_data(ProfilerObject
*self
)
553 /* Need to dump data to the log file... */
554 size_t written
= fwrite(self
->buffer
, 1, self
->index
, self
->logfp
);
555 if (written
== (size_t)self
->index
)
558 memmove(self
->buffer
, &self
->buffer
[written
],
559 self
->index
- written
);
560 self
->index
-= written
;
562 char *s
= PyString_AsString(self
->logfilename
);
563 PyErr_SetFromErrnoWithFilename(PyExc_IOError
, s
);
569 if (fflush(self
->logfp
)) {
570 char *s
= PyString_AsString(self
->logfilename
);
571 PyErr_SetFromErrnoWithFilename(PyExc_IOError
, s
);
580 pack_packed_int(ProfilerObject
*self
, int value
)
582 unsigned char partial
;
585 partial
= value
& 0x7F;
589 self
->buffer
[self
->index
] = partial
;
595 /* Encode a modified packed integer, with a subfield of modsize bits
596 * containing the value "subfield". The value of subfield is not
597 * checked to ensure it actually fits in modsize bits.
600 pack_modified_packed_int(ProfilerObject
*self
, int value
,
601 int modsize
, int subfield
)
603 const int maxvalues
[] = {-1, 1, 3, 7, 15, 31, 63, 127};
605 int bits
= 7 - modsize
;
606 int partial
= value
& maxvalues
[bits
];
607 unsigned char b
= subfield
| (partial
<< modsize
);
609 if (partial
!= value
) {
611 self
->buffer
[self
->index
] = b
;
613 return pack_packed_int(self
, value
>> bits
);
615 self
->buffer
[self
->index
] = b
;
621 pack_string(ProfilerObject
*self
, const char *s
, int len
)
623 if (len
+ PISIZE
+ self
->index
>= BUFFERSIZE
) {
624 if (flush_data(self
) < 0)
627 if (pack_packed_int(self
, len
) < 0)
629 memcpy(self
->buffer
+ self
->index
, s
, len
);
635 pack_add_info(ProfilerObject
*self
, const char *s1
, const char *s2
)
637 int len1
= strlen(s1
);
638 int len2
= strlen(s2
);
640 if (len1
+ len2
+ PISIZE
*2 + 1 + self
->index
>= BUFFERSIZE
) {
641 if (flush_data(self
) < 0)
644 self
->buffer
[self
->index
] = WHAT_ADD_INFO
;
646 if (pack_string(self
, s1
, len1
) < 0)
648 return pack_string(self
, s2
, len2
);
652 pack_define_file(ProfilerObject
*self
, int fileno
, const char *filename
)
654 int len
= strlen(filename
);
656 if (len
+ PISIZE
*2 + 1 + self
->index
>= BUFFERSIZE
) {
657 if (flush_data(self
) < 0)
660 self
->buffer
[self
->index
] = WHAT_DEFINE_FILE
;
662 if (pack_packed_int(self
, fileno
) < 0)
664 return pack_string(self
, filename
, len
);
668 pack_define_func(ProfilerObject
*self
, int fileno
, int lineno
,
669 const char *funcname
)
671 int len
= strlen(funcname
);
673 if (len
+ PISIZE
*3 + 1 + self
->index
>= BUFFERSIZE
) {
674 if (flush_data(self
) < 0)
677 self
->buffer
[self
->index
] = WHAT_DEFINE_FUNC
;
679 if (pack_packed_int(self
, fileno
) < 0)
681 if (pack_packed_int(self
, lineno
) < 0)
683 return pack_string(self
, funcname
, len
);
687 pack_line_times(ProfilerObject
*self
)
689 if (2 + self
->index
>= BUFFERSIZE
) {
690 if (flush_data(self
) < 0)
693 self
->buffer
[self
->index
] = WHAT_LINE_TIMES
;
694 self
->buffer
[self
->index
+ 1] = self
->linetimings
? 1 : 0;
700 pack_frame_times(ProfilerObject
*self
)
702 if (2 + self
->index
>= BUFFERSIZE
) {
703 if (flush_data(self
) < 0)
706 self
->buffer
[self
->index
] = WHAT_FRAME_TIMES
;
707 self
->buffer
[self
->index
+ 1] = self
->frametimings
? 1 : 0;
713 pack_enter(ProfilerObject
*self
, int fileno
, int tdelta
, int lineno
)
715 if (MPISIZE
+ PISIZE
*2 + self
->index
>= BUFFERSIZE
) {
716 if (flush_data(self
) < 0)
719 pack_modified_packed_int(self
, fileno
, 2, WHAT_ENTER
);
720 pack_packed_int(self
, lineno
);
721 if (self
->frametimings
)
722 return pack_packed_int(self
, tdelta
);
728 pack_exit(ProfilerObject
*self
, int tdelta
)
730 if (MPISIZE
+ self
->index
>= BUFFERSIZE
) {
731 if (flush_data(self
) < 0)
734 if (self
->frametimings
)
735 return pack_modified_packed_int(self
, tdelta
, 2, WHAT_EXIT
);
736 self
->buffer
[self
->index
] = WHAT_EXIT
;
742 pack_lineno(ProfilerObject
*self
, int lineno
)
744 if (MPISIZE
+ self
->index
>= BUFFERSIZE
) {
745 if (flush_data(self
) < 0)
748 return pack_modified_packed_int(self
, lineno
, 2, WHAT_LINENO
);
752 pack_lineno_tdelta(ProfilerObject
*self
, int lineno
, int tdelta
)
754 if (MPISIZE
+ PISIZE
+ self
->index
>= BUFFERSIZE
) {
755 if (flush_data(self
) < 0)
758 if (pack_modified_packed_int(self
, lineno
, 2, WHAT_LINENO
) < 0)
760 return pack_packed_int(self
, tdelta
);
764 get_fileno(ProfilerObject
*self
, PyCodeObject
*fcode
)
766 /* This is only used for ENTER events. */
772 obj
= PyDict_GetItem(self
->filemap
, fcode
->co_filename
);
774 /* first sighting of this file */
779 fileno
= self
->next_fileno
;
780 obj
= Py_BuildValue("iN", fileno
, dict
);
784 if (PyDict_SetItem(self
->filemap
, fcode
->co_filename
, obj
)) {
790 if (pack_define_file(self
, fileno
,
791 PyString_AS_STRING(fcode
->co_filename
)) < 0)
795 /* already know this ID */
796 fileno
= PyInt_AS_LONG(PyTuple_GET_ITEM(obj
, 0));
797 dict
= PyTuple_GET_ITEM(obj
, 1);
799 /* make sure we save a function name for this (fileno, lineno) */
800 obj
= PyInt_FromLong(fcode
->co_firstlineno
);
802 /* We just won't have it saved; too bad. */
806 PyObject
*name
= PyDict_GetItem(dict
, obj
);
808 if (pack_define_func(self
, fileno
, fcode
->co_firstlineno
,
809 PyString_AS_STRING(fcode
->co_name
)) < 0)
811 if (PyDict_SetItem(dict
, obj
, fcode
->co_name
))
819 get_tdelta(ProfilerObject
*self
)
827 diff
= tv
- self
->prev_timeofday
;
834 if (tv
.tv_sec
== self
->prev_timeofday
.tv_sec
)
835 tdelta
= tv
.tv_usec
- self
->prev_timeofday
.tv_usec
;
837 tdelta
= ((tv
.tv_sec
- self
->prev_timeofday
.tv_sec
) * 1000000
840 self
->prev_timeofday
= tv
;
845 /* The workhorse: the profiler callback function. */
848 profiler_callback(ProfilerObject
*self
, PyFrameObject
*frame
, int what
,
854 if (self
->frametimings
)
855 tdelta
= get_tdelta(self
);
858 fileno
= get_fileno(self
, frame
->f_code
);
861 if (pack_enter(self
, fileno
, tdelta
,
862 frame
->f_code
->co_firstlineno
) < 0)
866 if (pack_exit(self
, tdelta
) < 0)
870 /* should never get here */
877 /* Alternate callback when we want PyTrace_LINE events */
880 tracer_callback(ProfilerObject
*self
, PyFrameObject
*frame
, int what
,
887 fileno
= get_fileno(self
, frame
->f_code
);
890 return pack_enter(self
, fileno
,
891 self
->frametimings
? get_tdelta(self
) : -1,
892 frame
->f_code
->co_firstlineno
);
895 return pack_exit(self
, get_tdelta(self
));
898 if (self
->linetimings
)
899 return pack_lineno_tdelta(self
, frame
->f_lineno
,
902 return pack_lineno(self
, frame
->f_lineno
);
905 /* ignore PyTrace_EXCEPTION */
912 /* A couple of useful helper functions. */
915 static LARGE_INTEGER frequency
= {0, 0};
918 static unsigned long timeofday_diff
= 0;
919 static unsigned long rusage_diff
= 0;
928 QueryPerformanceFrequency(&frequency
);
937 timeofday_diff
= (unsigned long)diff
;
941 if (tv1
.tv_sec
!= tv2
.tv_sec
|| tv1
.tv_usec
!= tv2
.tv_usec
) {
942 if (tv1
.tv_sec
== tv2
.tv_sec
)
943 timeofday_diff
= tv2
.tv_usec
- tv1
.tv_usec
;
945 timeofday_diff
= (1000000 - tv1
.tv_usec
) + tv2
.tv_usec
;
950 #if defined(MS_WINDOWS) || defined(macintosh) || defined(PYOS_OS2) || \
955 struct rusage ru1
, ru2
;
957 getrusage(RUSAGE_SELF
, &ru1
);
959 getrusage(RUSAGE_SELF
, &ru2
);
960 if (ru1
.ru_utime
.tv_sec
!= ru2
.ru_utime
.tv_sec
) {
961 rusage_diff
= ((1000000 - ru1
.ru_utime
.tv_usec
)
962 + ru2
.ru_utime
.tv_usec
);
965 else if (ru1
.ru_utime
.tv_usec
!= ru2
.ru_utime
.tv_usec
) {
966 rusage_diff
= ru2
.ru_utime
.tv_usec
- ru1
.ru_utime
.tv_usec
;
969 else if (ru1
.ru_stime
.tv_sec
!= ru2
.ru_stime
.tv_sec
) {
970 rusage_diff
= ((1000000 - ru1
.ru_stime
.tv_usec
)
971 + ru2
.ru_stime
.tv_usec
);
974 else if (ru1
.ru_stime
.tv_usec
!= ru2
.ru_stime
.tv_usec
) {
975 rusage_diff
= ru2
.ru_stime
.tv_usec
- ru1
.ru_stime
.tv_usec
;
984 do_start(ProfilerObject
*self
)
987 GETTIMEOFDAY(&self
->prev_timeofday
);
988 if (self
->lineevents
)
989 PyEval_SetTrace((Py_tracefunc
) tracer_callback
, (PyObject
*)self
);
991 PyEval_SetProfile((Py_tracefunc
) profiler_callback
, (PyObject
*)self
);
995 do_stop(ProfilerObject
*self
)
999 if (self
->lineevents
)
1000 PyEval_SetTrace(NULL
, NULL
);
1002 PyEval_SetProfile(NULL
, NULL
);
1004 if (self
->index
> 0) {
1005 /* Best effort to dump out any remaining data. */
1011 is_available(ProfilerObject
*self
)
1014 PyErr_SetString(ProfilerError
, "profiler already active");
1017 if (self
->logfp
== NULL
) {
1018 PyErr_SetString(ProfilerError
, "profiler already closed");
1025 /* Profiler object interface methods. */
1027 PyDoc_STRVAR(addinfo__doc__
,
1028 "addinfo(key, value)\n"
1029 "Insert an ADD_INFO record into the log.");
1032 profiler_addinfo(ProfilerObject
*self
, PyObject
*args
)
1034 PyObject
*result
= NULL
;
1037 if (PyArg_ParseTuple(args
, "ss:addinfo", &key
, &value
)) {
1038 if (self
->logfp
== NULL
)
1039 PyErr_SetString(ProfilerError
, "profiler already closed");
1041 if (pack_add_info(self
, key
, value
) == 0) {
1050 PyDoc_STRVAR(close__doc__
,
1052 "Shut down this profiler and close the log files, even if its active.");
1055 profiler_close(ProfilerObject
*self
)
1058 if (self
->logfp
!= NULL
) {
1059 fclose(self
->logfp
);
1066 #define fileno__doc__ logreader_fileno__doc__
1069 profiler_fileno(ProfilerObject
*self
)
1071 if (self
->logfp
== NULL
) {
1072 PyErr_SetString(PyExc_ValueError
,
1073 "profiler's file object already closed");
1076 return PyInt_FromLong(fileno(self
->logfp
));
1079 PyDoc_STRVAR(runcall__doc__
,
1080 "runcall(callable[, args[, kw]]) -> callable()\n"
1081 "Profile a specific function call, returning the result of that call.");
1084 profiler_runcall(ProfilerObject
*self
, PyObject
*args
)
1086 PyObject
*result
= NULL
;
1087 PyObject
*callargs
= NULL
;
1088 PyObject
*callkw
= NULL
;
1091 if (PyArg_ParseTuple(args
, "O|OO:runcall",
1092 &callable
, &callargs
, &callkw
)) {
1093 if (is_available(self
)) {
1095 result
= PyEval_CallObjectWithKeywords(callable
, callargs
, callkw
);
1102 PyDoc_STRVAR(runcode__doc__
,
1103 "runcode(code, globals[, locals])\n"
1104 "Execute a code object while collecting profile data. If locals is\n"
1105 "omitted, globals is used for the locals as well.");
1108 profiler_runcode(ProfilerObject
*self
, PyObject
*args
)
1110 PyObject
*result
= NULL
;
1113 PyObject
*locals
= NULL
;
1115 if (PyArg_ParseTuple(args
, "O!O!|O:runcode",
1116 &PyCode_Type
, &code
,
1117 &PyDict_Type
, &globals
,
1119 if (is_available(self
)) {
1120 if (locals
== NULL
|| locals
== Py_None
)
1122 else if (!PyDict_Check(locals
)) {
1123 PyErr_SetString(PyExc_TypeError
,
1124 "locals must be a dictionary or None");
1128 result
= PyEval_EvalCode(code
, globals
, locals
);
1131 if (!PyErr_Occurred()) {
1141 PyDoc_STRVAR(start__doc__
,
1143 "Install this profiler for the current thread.");
1146 profiler_start(ProfilerObject
*self
, PyObject
*args
)
1148 PyObject
*result
= NULL
;
1150 if (is_available(self
)) {
1158 PyDoc_STRVAR(stop__doc__
,
1160 "Remove this profiler from the current thread.");
1163 profiler_stop(ProfilerObject
*self
, PyObject
*args
)
1165 PyObject
*result
= NULL
;
1168 PyErr_SetString(ProfilerError
, "profiler not active");
1178 /* Python API support. */
1181 profiler_dealloc(ProfilerObject
*self
)
1184 if (self
->logfp
!= NULL
)
1185 fclose(self
->logfp
);
1186 Py_XDECREF(self
->filemap
);
1187 Py_XDECREF(self
->logfilename
);
1188 PyObject_Del((PyObject
*)self
);
1191 static PyMethodDef profiler_methods
[] = {
1192 {"addinfo", (PyCFunction
)profiler_addinfo
, METH_VARARGS
, addinfo__doc__
},
1193 {"close", (PyCFunction
)profiler_close
, METH_NOARGS
, close__doc__
},
1194 {"fileno", (PyCFunction
)profiler_fileno
, METH_NOARGS
, fileno__doc__
},
1195 {"runcall", (PyCFunction
)profiler_runcall
, METH_VARARGS
, runcall__doc__
},
1196 {"runcode", (PyCFunction
)profiler_runcode
, METH_VARARGS
, runcode__doc__
},
1197 {"start", (PyCFunction
)profiler_start
, METH_NOARGS
, start__doc__
},
1198 {"stop", (PyCFunction
)profiler_stop
, METH_NOARGS
, stop__doc__
},
1202 static PyMemberDef profiler_members
[] = {
1203 {"frametimings", T_LONG
, offsetof(ProfilerObject
, linetimings
), READONLY
},
1204 {"lineevents", T_LONG
, offsetof(ProfilerObject
, lineevents
), READONLY
},
1205 {"linetimings", T_LONG
, offsetof(ProfilerObject
, linetimings
), READONLY
},
1210 profiler_get_closed(ProfilerObject
*self
, void *closure
)
1212 PyObject
*result
= (self
->logfp
== NULL
) ? Py_True
: Py_False
;
1217 static PyGetSetDef profiler_getsets
[] = {
1218 {"closed", (getter
)profiler_get_closed
, NULL
,
1219 PyDoc_STR("True if the profiler's output file has already been closed.")},
1224 PyDoc_STRVAR(profiler_object__doc__
,
1225 "High-performance profiler object.\n"
1229 "close(): Stop the profiler and close the log files.\n"
1230 "fileno(): Returns the file descriptor of the log file.\n"
1231 "runcall(): Run a single function call with profiling enabled.\n"
1232 "runcode(): Execute a code object with profiling enabled.\n"
1233 "start(): Install the profiler and return.\n"
1234 "stop(): Remove the profiler.\n"
1236 "Attributes (read-only):\n"
1238 "closed: True if the profiler has already been closed.\n"
1239 "frametimings: True if ENTER/EXIT events collect timing information.\n"
1240 "lineevents: True if line events are reported to the profiler.\n"
1241 "linetimings: True if line events collect timing information.");
1243 static PyTypeObject ProfilerType
= {
1244 PyObject_HEAD_INIT(NULL
)
1246 "_hotshot.ProfilerType", /* tp_name */
1247 (int) sizeof(ProfilerObject
), /* tp_basicsize */
1248 0, /* tp_itemsize */
1249 (destructor
)profiler_dealloc
, /* tp_dealloc */
1255 0, /* tp_as_number */
1256 0, /* tp_as_sequence */
1257 0, /* tp_as_mapping */
1261 0, /* tp_getattro */
1262 0, /* tp_setattro */
1263 0, /* tp_as_buffer */
1264 Py_TPFLAGS_DEFAULT
, /* tp_flags */
1265 profiler_object__doc__
, /* tp_doc */
1266 0, /* tp_traverse */
1268 0, /* tp_richcompare */
1269 0, /* tp_weaklistoffset */
1271 0, /* tp_iternext */
1272 profiler_methods
, /* tp_methods */
1273 profiler_members
, /* tp_members */
1274 profiler_getsets
, /* tp_getset */
1277 0, /* tp_descr_get */
1278 0, /* tp_descr_set */
1282 static PyMethodDef logreader_methods
[] = {
1283 {"close", (PyCFunction
)logreader_close
, METH_NOARGS
,
1284 logreader_close__doc__
},
1285 {"fileno", (PyCFunction
)logreader_fileno
, METH_NOARGS
,
1286 logreader_fileno__doc__
},
1290 static PyMemberDef logreader_members
[] = {
1291 {"info", T_OBJECT
, offsetof(LogReaderObject
, info
), RO
,
1292 PyDoc_STR("Dictionary mapping informational keys to lists of values.")},
1297 PyDoc_STRVAR(logreader__doc__
,
1298 "logreader(filename) --> log-iterator\n\
1299 Create a log-reader for the timing information file.");
1301 static PySequenceMethods logreader_as_sequence
= {
1305 (intargfunc
)logreader_sq_item
, /* sq_item */
1307 0, /* sq_ass_item */
1308 0, /* sq_ass_slice */
1309 0, /* sq_contains */
1310 0, /* sq_inplace_concat */
1311 0, /* sq_inplace_repeat */
1315 logreader_get_closed(LogReaderObject
*self
, void *closure
)
1317 PyObject
*result
= (self
->logfp
== NULL
) ? Py_True
: Py_False
;
1322 static PyGetSetDef logreader_getsets
[] = {
1323 {"closed", (getter
)logreader_get_closed
, NULL
,
1324 PyDoc_STR("True if the logreader's input file has already been closed.")},
1328 static PyTypeObject LogReaderType
= {
1329 PyObject_HEAD_INIT(NULL
)
1331 "_hotshot.LogReaderType", /* tp_name */
1332 (int) sizeof(LogReaderObject
), /* tp_basicsize */
1333 0, /* tp_itemsize */
1334 (destructor
)logreader_dealloc
, /* tp_dealloc */
1340 0, /* tp_as_number */
1341 &logreader_as_sequence
, /* tp_as_sequence */
1342 0, /* tp_as_mapping */
1346 0, /* tp_getattro */
1347 0, /* tp_setattro */
1348 0, /* tp_as_buffer */
1349 Py_TPFLAGS_DEFAULT
, /* tp_flags */
1350 logreader__doc__
, /* tp_doc */
1351 0, /* tp_traverse */
1353 0, /* tp_richcompare */
1354 0, /* tp_weaklistoffset */
1355 (getiterfunc
)logreader_tp_iter
, /* tp_iter */
1356 (iternextfunc
)logreader_tp_iternext
,/* tp_iternext */
1357 logreader_methods
, /* tp_methods */
1358 logreader_members
, /* tp_members */
1359 logreader_getsets
, /* tp_getset */
1362 0, /* tp_descr_get */
1363 0, /* tp_descr_set */
1367 hotshot_logreader(PyObject
*unused
, PyObject
*args
)
1369 LogReaderObject
*self
= NULL
;
1374 if (PyArg_ParseTuple(args
, "s:logreader", &filename
)) {
1375 self
= PyObject_New(LogReaderObject
, &LogReaderType
);
1377 self
->frametimings
= 1;
1378 self
->linetimings
= 0;
1380 self
->logfp
= fopen(filename
, "rb");
1381 if (self
->logfp
== NULL
) {
1382 PyErr_SetFromErrnoWithFilename(PyExc_IOError
, filename
);
1387 self
->info
= PyDict_New();
1388 if (self
->info
== NULL
) {
1392 /* read initial info */
1394 if ((c
= fgetc(self
->logfp
)) == EOF
) {
1398 if (c
!= WHAT_ADD_INFO
) {
1399 ungetc(c
, self
->logfp
);
1402 err
= unpack_add_info(self
);
1407 PyErr_SetString(PyExc_RuntimeError
,
1408 "unexpected error");
1415 return (PyObject
*) self
;
1419 /* Return a Python string that represents the version number without the
1420 * extra cruft added by revision control, even if the right options were
1421 * given to the "cvs export" command to make it not include the extra
1425 get_version_string(void)
1427 static char *rcsid
= "$Revision$";
1432 while (*rev
&& !isdigit((int)*rev
))
1434 while (rev
[i
] != ' ' && rev
[i
] != '\0')
1436 buffer
= malloc(i
+ 1);
1437 if (buffer
!= NULL
) {
1438 memmove(buffer
, rev
, i
);
1444 /* Write out a RFC 822-style header with various useful bits of
1445 * information to make the output easier to manage.
1448 write_header(ProfilerObject
*self
)
1451 char cwdbuffer
[PATH_MAX
];
1455 buffer
= get_version_string();
1456 if (buffer
== NULL
) {
1460 pack_add_info(self
, "hotshot-version", buffer
);
1461 pack_add_info(self
, "requested-frame-timings",
1462 (self
->frametimings
? "yes" : "no"));
1463 pack_add_info(self
, "requested-line-events",
1464 (self
->lineevents
? "yes" : "no"));
1465 pack_add_info(self
, "requested-line-timings",
1466 (self
->linetimings
? "yes" : "no"));
1467 pack_add_info(self
, "platform", Py_GetPlatform());
1468 pack_add_info(self
, "executable", Py_GetProgramFullPath());
1470 buffer
= (char *) Py_GetVersion();
1474 pack_add_info(self
, "executable-version", buffer
);
1477 PyOS_snprintf(cwdbuffer
, sizeof(cwdbuffer
), "%I64d", frequency
.QuadPart
);
1478 pack_add_info(self
, "reported-performance-frequency", cwdbuffer
);
1480 PyOS_snprintf(cwdbuffer
, sizeof(cwdbuffer
), "%lu", rusage_diff
);
1481 pack_add_info(self
, "observed-interval-getrusage", cwdbuffer
);
1482 PyOS_snprintf(cwdbuffer
, sizeof(cwdbuffer
), "%lu", timeofday_diff
);
1483 pack_add_info(self
, "observed-interval-gettimeofday", cwdbuffer
);
1486 pack_add_info(self
, "current-directory",
1487 getcwd(cwdbuffer
, sizeof cwdbuffer
));
1489 temp
= PySys_GetObject("path");
1490 len
= PyList_GET_SIZE(temp
);
1491 for (i
= 0; i
< len
; ++i
) {
1492 PyObject
*item
= PyList_GET_ITEM(temp
, i
);
1493 buffer
= PyString_AsString(item
);
1494 if (buffer
== NULL
) {
1495 pack_add_info(self
, "sys-path-entry", "<non-string-path-entry>");
1499 pack_add_info(self
, "sys-path-entry", buffer
);
1502 pack_frame_times(self
);
1503 pack_line_times(self
);
1508 PyDoc_STRVAR(profiler__doc__
,
1509 "profiler(logfilename[, lineevents[, linetimes]]) -> profiler\n\
1510 Create a new profiler object.");
1513 hotshot_profiler(PyObject
*unused
, PyObject
*args
)
1516 ProfilerObject
*self
= NULL
;
1518 int linetimings
= 1;
1520 if (PyArg_ParseTuple(args
, "s|ii:profiler", &logfilename
,
1521 &lineevents
, &linetimings
)) {
1522 self
= PyObject_New(ProfilerObject
, &ProfilerType
);
1525 self
->frametimings
= 1;
1526 self
->lineevents
= lineevents
? 1 : 0;
1527 self
->linetimings
= (lineevents
&& linetimings
) ? 1 : 0;
1530 self
->next_fileno
= 0;
1532 self
->logfilename
= PyTuple_GET_ITEM(args
, 0);
1533 Py_INCREF(self
->logfilename
);
1534 self
->filemap
= PyDict_New();
1535 if (self
->filemap
== NULL
) {
1539 self
->logfp
= fopen(logfilename
, "wb");
1540 if (self
->logfp
== NULL
) {
1542 PyErr_SetFromErrnoWithFilename(PyExc_IOError
, logfilename
);
1545 if (timeofday_diff
== 0) {
1546 /* Run this several times since sometimes the first
1547 * doesn't give the lowest values, and we're really trying
1548 * to determine the lowest.
1554 if (write_header(self
))
1555 /* some error occurred, exception has been set */
1558 return (PyObject
*) self
;
1561 PyDoc_STRVAR(coverage__doc__
,
1562 "coverage(logfilename) -> profiler\n\
1563 Returns a profiler that doesn't collect any timing information, which is\n\
1564 useful in building a coverage analysis tool.");
1567 hotshot_coverage(PyObject
*unused
, PyObject
*args
)
1570 PyObject
*result
= NULL
;
1572 if (PyArg_ParseTuple(args
, "s:coverage", &logfilename
)) {
1573 result
= hotshot_profiler(unused
, args
);
1574 if (result
!= NULL
) {
1575 ProfilerObject
*self
= (ProfilerObject
*) result
;
1576 self
->frametimings
= 0;
1577 self
->linetimings
= 0;
1578 self
->lineevents
= 1;
1584 PyDoc_VAR(resolution__doc__
) =
1587 "resolution() -> (performance-counter-ticks, update-frequency)\n"
1588 "Return the resolution of the timer provided by the QueryPerformanceCounter()\n"
1589 "function. The first value is the smallest observed change, and the second\n"
1590 "is the result of QueryPerformanceFrequency()."
1594 "resolution() -> (gettimeofday-usecs, getrusage-usecs)\n"
1595 "Return the resolution of the timers provided by the gettimeofday() and\n"
1596 "getrusage() system calls, or -1 if the call is not supported."
1602 hotshot_resolution(PyObject
*unused
, PyObject
*args
)
1604 PyObject
*result
= NULL
;
1606 if (PyArg_ParseTuple(args
, ":resolution")) {
1607 if (timeofday_diff
== 0) {
1613 result
= Py_BuildValue("ii", timeofday_diff
, frequency
.LowPart
);
1615 result
= Py_BuildValue("ii", timeofday_diff
, rusage_diff
);
1622 static PyMethodDef functions
[] = {
1623 {"coverage", hotshot_coverage
, METH_VARARGS
, coverage__doc__
},
1624 {"profiler", hotshot_profiler
, METH_VARARGS
, profiler__doc__
},
1625 {"logreader", hotshot_logreader
, METH_VARARGS
, logreader__doc__
},
1626 {"resolution", hotshot_resolution
, METH_VARARGS
, resolution__doc__
},
1636 LogReaderType
.ob_type
= &PyType_Type
;
1637 LogReaderType
.tp_getattro
= PyObject_GenericGetAttr
;
1638 ProfilerType
.ob_type
= &PyType_Type
;
1639 ProfilerType
.tp_getattro
= PyObject_GenericGetAttr
;
1640 module
= Py_InitModule("_hotshot", functions
);
1641 if (module
!= NULL
) {
1642 char *s
= get_version_string();
1644 PyModule_AddStringConstant(module
, "__version__", s
);
1646 Py_INCREF(&LogReaderType
);
1647 PyModule_AddObject(module
, "LogReaderType",
1648 (PyObject
*)&LogReaderType
);
1649 Py_INCREF(&ProfilerType
);
1650 PyModule_AddObject(module
, "ProfilerType",
1651 (PyObject
*)&ProfilerType
);
1653 if (ProfilerError
== NULL
)
1654 ProfilerError
= PyErr_NewException("hotshot.ProfilerError",
1656 if (ProfilerError
!= NULL
) {
1657 Py_INCREF(ProfilerError
);
1658 PyModule_AddObject(module
, "ProfilerError", ProfilerError
);
1660 PyModule_AddIntConstant(module
, "WHAT_ENTER", WHAT_ENTER
);
1661 PyModule_AddIntConstant(module
, "WHAT_EXIT", WHAT_EXIT
);
1662 PyModule_AddIntConstant(module
, "WHAT_LINENO", WHAT_LINENO
);
1663 PyModule_AddIntConstant(module
, "WHAT_OTHER", WHAT_OTHER
);
1664 PyModule_AddIntConstant(module
, "WHAT_ADD_INFO", WHAT_ADD_INFO
);
1665 PyModule_AddIntConstant(module
, "WHAT_DEFINE_FILE", WHAT_DEFINE_FILE
);
1666 PyModule_AddIntConstant(module
, "WHAT_DEFINE_FUNC", WHAT_DEFINE_FUNC
);
1667 PyModule_AddIntConstant(module
, "WHAT_LINE_TIMES", WHAT_LINE_TIMES
);