1 /*
2 * This is the High Performance Python Profiler portion of HotShot.
3 */
4
5 #include "Python.h"
6 #include "code.h"
7 #include "eval.h"
8 #include "frameobject.h"
9 #include "structmember.h"
10
11 /*
12 * Which timer to use should be made more configurable, but that should not
13 * be difficult. This will do for now.
14 */
15 #ifdef MS_WINDOWS
16 #include <windows.h>
17
18 #ifdef HAVE_DIRECT_H
19 #include <direct.h> /* for getcwd() */
20 #endif
21
22 typedef __int64 hs_time;
23 #define GETTIMEOFDAY(P_HS_TIME) \
24 { LARGE_INTEGER _temp; \
25 QueryPerformanceCounter(&_temp); \
26 *(P_HS_TIME) = _temp.QuadPart; }
27
28
29 #else
30 #ifndef HAVE_GETTIMEOFDAY
31 #error "This module requires gettimeofday() on non-Windows platforms!"
32 #endif
33 #if (defined(PYOS_OS2) && defined(PYCC_GCC)) || defined(__QNX__)
34 #include <sys/time.h>
35 #else
36 #include <sys/resource.h>
37 #include <sys/times.h>
38 #endif
39 typedef struct timeval hs_time;
40 #endif
41
42 #if !defined(__cplusplus) && !defined(inline)
43 #ifdef __GNUC__
44 #define inline __inline
45 #endif
46 #endif
47
48 #ifndef inline
49 #define inline
50 #endif
51
52 #define BUFFERSIZE 10240
53
54 #if defined(PYOS_OS2) && defined(PYCC_GCC)
55 #define PATH_MAX 260
56 #endif
57
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) */
61 #define PATH_MAX 1024
62 #endif
63
64 #ifndef PATH_MAX
65 # ifdef MAX_PATH
66 # define PATH_MAX MAX_PATH
67 # elif defined (_POSIX_PATH_MAX)
68 # define PATH_MAX _POSIX_PATH_MAX
69 # else
70 # error "Need a defn. for PATH_MAX in _hotshot.c"
71 # endif
72 #endif
73
74 typedef struct {
75 PyObject_HEAD
76 PyObject *filemap;
77 PyObject *logfilename;
78 Py_ssize_t index;
79 unsigned char buffer[BUFFERSIZE];
80 FILE *logfp;
81 int lineevents;
82 int linetimings;
83 int frametimings;
84 /* size_t filled; */
85 int active;
86 int next_fileno;
87 hs_time prev_timeofday;
88 } ProfilerObject;
89
90 typedef struct {
91 PyObject_HEAD
92 PyObject *info;
93 FILE *logfp;
94 int linetimings;
95 int frametimings;
96 } LogReaderObject;
97
98 static PyObject * ProfilerError = NULL;
99
100
101 #ifndef MS_WINDOWS
102 #ifdef GETTIMEOFDAY_NO_TZ
103 #define GETTIMEOFDAY(ptv) gettimeofday((ptv))
104 #else
105 #define GETTIMEOFDAY(ptv) gettimeofday((ptv), (struct timezone *)NULL)
106 #endif
107 #endif
108
109
110 /* The log reader... */
111
112 PyDoc_STRVAR(logreader_close__doc__,
113 "close()\n"
114 "Close the log file, preventing additional records from being read.");
115
116 static PyObject *
logreader_close(LogReaderObject * self,PyObject * args)117 logreader_close(LogReaderObject *self, PyObject *args)
118 {
119 if (self->logfp != NULL) {
120 fclose(self->logfp);
121 self->logfp = NULL;
122 }
123 Py_INCREF(Py_None);
124
125 return Py_None;
126 }
127
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.");
132
133 static PyObject *
logreader_fileno(LogReaderObject * self)134 logreader_fileno(LogReaderObject *self)
135 {
136 if (self->logfp == NULL) {
137 PyErr_SetString(PyExc_ValueError,
138 "logreader's file object already closed");
139 return NULL;
140 }
141 return PyInt_FromLong(fileno(self->logfp));
142 }
143
144
145 /* Log File Format
146 * ---------------
147 *
148 * The log file consists of a sequence of variable-length records.
149 * Each record is identified with a record type identifier in two
150 * bits of the first byte. The two bits are the "least significant"
151 * bits of the byte.
152 *
153 * Low bits: Opcode: Meaning:
154 * 0x00 ENTER enter a frame
155 * 0x01 EXIT exit a frame
156 * 0x02 LINENO execution moved onto a different line
157 * 0x03 OTHER more bits are needed to deecode
158 *
159 * If the type is OTHER, the record is not packed so tightly, and the
160 * remaining bits are used to disambiguate the record type. These
161 * records are not used as frequently so compaction is not an issue.
162 * Each of the first three record types has a highly tailored
163 * structure that allows it to be packed tightly.
164 *
165 * The OTHER records have the following identifiers:
166 *
167 * First byte: Opcode: Meaning:
168 * 0x13 ADD_INFO define a key/value pair
169 * 0x23 DEFINE_FILE define an int->filename mapping
170 * 0x33 LINE_TIMES indicates if LINENO events have tdeltas
171 * 0x43 DEFINE_FUNC define a (fileno,lineno)->funcname mapping
172 * 0x53 FRAME_TIMES indicates if ENTER/EXIT events have tdeltas
173 *
174 * Packed Integers
175 *
176 * "Packed integers" are non-negative integer values encoded as a
177 * sequence of bytes. Each byte is encoded such that the most
178 * significant bit is set if the next byte is also part of the
179 * integer. Each byte provides bits to the least-significant end of
180 * the result; the accumulated value must be shifted up to place the
181 * new bits into the result.
182 *
183 * "Modified packed integers" are packed integers where only a portion
184 * of the first byte is used. In the rest of the specification, these
185 * are referred to as "MPI(n,name)", where "n" is the number of bits
186 * discarded from the least-signicant positions of the byte, and
187 * "name" is a name being given to those "discarded" bits, since they
188 * are a field themselves.
189 *
190 * ENTER records:
191 *
192 * MPI(2,type) fileno -- type is 0x00
193 * PI lineno
194 * PI tdelta -- iff frame times are enabled
195 *
196 * EXIT records
197 *
198 * MPI(2,type) tdelta -- type is 0x01; tdelta will be 0
199 * if frame times are disabled
200 *
201 * LINENO records
202 *
203 * MPI(2,type) lineno -- type is 0x02
204 * PI tdelta -- iff LINENO includes it
205 *
206 * ADD_INFO records
207 *
208 * BYTE type -- always 0x13
209 * PI len1 -- length of first string
210 * BYTE string1[len1] -- len1 bytes of string data
211 * PI len2 -- length of second string
212 * BYTE string2[len2] -- len2 bytes of string data
213 *
214 * DEFINE_FILE records
215 *
216 * BYTE type -- always 0x23
217 * PI fileno
218 * PI len -- length of filename
219 * BYTE filename[len] -- len bytes of string data
220 *
221 * DEFINE_FUNC records
222 *
223 * BYTE type -- always 0x43
224 * PI fileno
225 * PI lineno
226 * PI len -- length of funcname
227 * BYTE funcname[len] -- len bytes of string data
228 *
229 * LINE_TIMES records
230 *
231 * This record can be used only before the start of ENTER/EXIT/LINENO
232 * records. If have_tdelta is true, LINENO records will include the
233 * tdelta field, otherwise it will be omitted. If this record is not
234 * given, LINENO records will not contain the tdelta field.
235 *
236 * BYTE type -- always 0x33
237 * BYTE have_tdelta -- 0 if LINENO does *not* have
238 * timing information
239 * FRAME_TIMES records
240 *
241 * This record can be used only before the start of ENTER/EXIT/LINENO
242 * records. If have_tdelta is true, ENTER and EXIT records will
243 * include the tdelta field, otherwise it will be omitted. If this
244 * record is not given, ENTER and EXIT records will contain the tdelta
245 * field.
246 *
247 * BYTE type -- always 0x53
248 * BYTE have_tdelta -- 0 if ENTER/EXIT do *not* have
249 * timing information
250 */
251
252 #define WHAT_ENTER 0x00
253 #define WHAT_EXIT 0x01
254 #define WHAT_LINENO 0x02
255 #define WHAT_OTHER 0x03 /* only used in decoding */
256 #define WHAT_ADD_INFO 0x13
257 #define WHAT_DEFINE_FILE 0x23
258 #define WHAT_LINE_TIMES 0x33
259 #define WHAT_DEFINE_FUNC 0x43
260 #define WHAT_FRAME_TIMES 0x53
261
262 #define ERR_NONE 0
263 #define ERR_EOF -1
264 #define ERR_EXCEPTION -2
265 #define ERR_BAD_RECTYPE -3
266
267 #define PISIZE (sizeof(int) + 1)
268 #define MPISIZE (PISIZE + 1)
269
270 /* Maximum size of "normal" events -- nothing that contains string data */
271 #define MAXEVENTSIZE (MPISIZE + PISIZE*2)
272
273
274 /* Unpack a packed integer; if "discard" is non-zero, unpack a modified
275 * packed integer with "discard" discarded bits.
276 */
277 static int
unpack_packed_int(LogReaderObject * self,int * pvalue,int discard)278 unpack_packed_int(LogReaderObject *self, int *pvalue, int discard)
279 {
280 int c;
281 int accum = 0;
282 int bits = 0;
283 int cont;
284
285 do {
286 /* read byte */
287 if ((c = fgetc(self->logfp)) == EOF)
288 return ERR_EOF;
289 accum |= ((c & 0x7F) >> discard) << bits;
290 bits += (7 - discard);
291 cont = c & 0x80;
292 discard = 0;
293 } while (cont);
294
295 *pvalue = accum;
296
297 return 0;
298 }
299
300 /* Unpack a string, which is encoded as a packed integer giving the
301 * length of the string, followed by the string data.
302 */
303 static int
unpack_string(LogReaderObject * self,PyObject ** pvalue)304 unpack_string(LogReaderObject *self, PyObject **pvalue)
305 {
306 int i;
307 int len;
308 int err;
309 int ch;
310 char *buf;
311
312 if ((err = unpack_packed_int(self, &len, 0)))
313 return err;
314
315 buf = (char *)malloc(len);
316 if (!buf) {
317 PyErr_NoMemory();
318 return ERR_EXCEPTION;
319 }
320
321 for (i=0; i < len; i++) {
322 ch = fgetc(self->logfp);
323 buf[i] = ch;
324 if (ch == EOF) {
325 free(buf);
326 return ERR_EOF;
327 }
328 }
329 *pvalue = PyString_FromStringAndSize(buf, len);
330 free(buf);
331 if (*pvalue == NULL) {
332 return ERR_EXCEPTION;
333 }
334 return 0;
335 }
336
337
338 static int
unpack_add_info(LogReaderObject * self)339 unpack_add_info(LogReaderObject *self)
340 {
341 PyObject *key = NULL;
342 PyObject *value = NULL;
343 PyObject *list;
344 int err;
345
346 err = unpack_string(self, &key);
347 if (err)
348 goto finally;
349 err = unpack_string(self, &value);
350 if (err)
351 goto finally;
352 list = PyDict_GetItem(self->info, key);
353 if (list == NULL) {
354 list = PyList_New(0);
355 if (list == NULL) {
356 err = ERR_EXCEPTION;
357 goto finally;
358 }
359 if (PyDict_SetItem(self->info, key, list)) {
360 Py_DECREF(list);
361 err = ERR_EXCEPTION;
362 goto finally;
363 }
364 Py_DECREF(list);
365 }
366 if (PyList_Append(list, value))
367 err = ERR_EXCEPTION;
368
369 finally:
370 Py_XDECREF(key);
371 Py_XDECREF(value);
372 return err;
373 }
374
375
376 static void
eof_error(LogReaderObject * self)377 eof_error(LogReaderObject *self)
378 {
379 fclose(self->logfp);
380 self->logfp = NULL;
381 PyErr_SetString(PyExc_EOFError,
382 "end of file with incomplete profile record");
383 }
384
385 static PyObject *
logreader_tp_iternext(LogReaderObject * self)386 logreader_tp_iternext(LogReaderObject *self)
387 {
388 int c;
389 int what;
390 int err = ERR_NONE;
391 int lineno = -1;
392 int fileno = -1;
393 int tdelta = -1;
394 PyObject *s1 = NULL, *s2 = NULL;
395 PyObject *result = NULL;
396 #if 0
397 unsigned char b0, b1;
398 #endif
399
400 if (self->logfp == NULL) {
401 PyErr_SetString(ProfilerError,
402 "cannot iterate over closed LogReader object");
403 return NULL;
404 }
405
406 restart:
407 /* decode the record type */
408 if ((c = fgetc(self->logfp)) == EOF) {
409 fclose(self->logfp);
410 self->logfp = NULL;
411 return NULL;
412 }
413 what = c & WHAT_OTHER;
414 if (what == WHAT_OTHER)
415 what = c; /* need all the bits for type */
416 else
417 ungetc(c, self->logfp); /* type byte includes packed int */
418
419 switch (what) {
420 case WHAT_ENTER:
421 err = unpack_packed_int(self, &fileno, 2);
422 if (!err) {
423 err = unpack_packed_int(self, &lineno, 0);
424 if (self->frametimings && !err)
425 err = unpack_packed_int(self, &tdelta, 0);
426 }
427 break;
428 case WHAT_EXIT:
429 err = unpack_packed_int(self, &tdelta, 2);
430 break;
431 case WHAT_LINENO:
432 err = unpack_packed_int(self, &lineno, 2);
433 if (self->linetimings && !err)
434 err = unpack_packed_int(self, &tdelta, 0);
435 break;
436 case WHAT_ADD_INFO:
437 err = unpack_add_info(self);
438 break;
439 case WHAT_DEFINE_FILE:
440 err = unpack_packed_int(self, &fileno, 0);
441 if (!err) {
442 err = unpack_string(self, &s1);
443 if (!err) {
444 Py_INCREF(Py_None);
445 s2 = Py_None;
446 }
447 }
448 break;
449 case WHAT_DEFINE_FUNC:
450 err = unpack_packed_int(self, &fileno, 0);
451 if (!err) {
452 err = unpack_packed_int(self, &lineno, 0);
453 if (!err)
454 err = unpack_string(self, &s1);
455 }
456 break;
457 case WHAT_LINE_TIMES:
458 if ((c = fgetc(self->logfp)) == EOF)
459 err = ERR_EOF;
460 else {
461 self->linetimings = c ? 1 : 0;
462 goto restart;
463 }
464 break;
465 case WHAT_FRAME_TIMES:
466 if ((c = fgetc(self->logfp)) == EOF)
467 err = ERR_EOF;
468 else {
469 self->frametimings = c ? 1 : 0;
470 goto restart;
471 }
472 break;
473 default:
474 err = ERR_BAD_RECTYPE;
475 }
476 if (err == ERR_BAD_RECTYPE) {
477 PyErr_SetString(PyExc_ValueError,
478 "unknown record type in log file");
479 }
480 else if (err == ERR_EOF) {
481 eof_error(self);
482 }
483 else if (!err) {
484 result = PyTuple_New(4);
485 if (result == NULL)
486 return NULL;
487 PyTuple_SET_ITEM(result, 0, PyInt_FromLong(what));
488 PyTuple_SET_ITEM(result, 2, PyInt_FromLong(fileno));
489 if (s1 == NULL)
490 PyTuple_SET_ITEM(result, 1, PyInt_FromLong(tdelta));
491 else
492 PyTuple_SET_ITEM(result, 1, s1);
493 if (s2 == NULL)
494 PyTuple_SET_ITEM(result, 3, PyInt_FromLong(lineno));
495 else
496 PyTuple_SET_ITEM(result, 3, s2);
497 }
498 /* The only other case is err == ERR_EXCEPTION, in which case the
499 * exception is already set.
500 */
501 #if 0
502 b0 = self->buffer[self->index];
503 b1 = self->buffer[self->index + 1];
504 if (b0 & 1) {
505 /* This is a line-number event. */
506 what = PyTrace_LINE;
507 lineno = ((b0 & ~1) << 7) + b1;
508 self->index += 2;
509 }
510 else {
511 what = (b0 & 0x0E) >> 1;
512 tdelta = ((b0 & 0xF0) << 4) + b1;
513 if (what == PyTrace_CALL) {
514 /* we know there's a 2-byte file ID & 2-byte line number */
515 fileno = ((self->buffer[self->index + 2] << 8)
516 + self->buffer[self->index + 3]);
517 lineno = ((self->buffer[self->index + 4] << 8)
518 + self->buffer[self->index + 5]);
519 self->index += 6;
520 }
521 else
522 self->index += 2;
523 }
524 #endif
525 return result;
526 }
527
528 static void
logreader_dealloc(LogReaderObject * self)529 logreader_dealloc(LogReaderObject *self)
530 {
531 if (self->logfp != NULL) {
532 fclose(self->logfp);
533 self->logfp = NULL;
534 }
535 Py_XDECREF(self->info);
536 PyObject_Del(self);
537 }
538
539 static PyObject *
logreader_sq_item(LogReaderObject * self,Py_ssize_t index)540 logreader_sq_item(LogReaderObject *self, Py_ssize_t index)
541 {
542 PyObject *result = logreader_tp_iternext(self);
543 if (result == NULL && !PyErr_Occurred()) {
544 PyErr_SetString(PyExc_IndexError, "no more events in log");
545 return NULL;
546 }
547 return result;
548 }
549
550 static void
551 do_stop(ProfilerObject *self);
552
553 static int
flush_data(ProfilerObject * self)554 flush_data(ProfilerObject *self)
555 {
556 /* Need to dump data to the log file... */
557 size_t written = fwrite(self->buffer, 1, self->index, self->logfp);
558 if (written == (size_t)self->index)
559 self->index = 0;
560 else {
561 memmove(self->buffer, &self->buffer[written],
562 self->index - written);
563 self->index -= written;
564 if (written == 0) {
565 char *s = PyString_AsString(self->logfilename);
566 PyErr_SetFromErrnoWithFilename(PyExc_IOError, s);
567 do_stop(self);
568 return -1;
569 }
570 }
571 if (written > 0) {
572 if (fflush(self->logfp)) {
573 char *s = PyString_AsString(self->logfilename);
574 PyErr_SetFromErrnoWithFilename(PyExc_IOError, s);
575 do_stop(self);
576 return -1;
577 }
578 }
579 return 0;
580 }
581
582 static inline int
pack_packed_int(ProfilerObject * self,int value)583 pack_packed_int(ProfilerObject *self, int value)
584 {
585 unsigned char partial;
586
587 do {
588 partial = value & 0x7F;
589 value >>= 7;
590 if (value)
591 partial |= 0x80;
592 self->buffer[self->index] = partial;
593 self->index++;
594 } while (value);
595 return 0;
596 }
597
598 /* Encode a modified packed integer, with a subfield of modsize bits
599 * containing the value "subfield". The value of subfield is not
600 * checked to ensure it actually fits in modsize bits.
601 */
602 static inline int
pack_modified_packed_int(ProfilerObject * self,int value,int modsize,int subfield)603 pack_modified_packed_int(ProfilerObject *self, int value,
604 int modsize, int subfield)
605 {
606 const int maxvalues[] = {-1, 1, 3, 7, 15, 31, 63, 127};
607
608 int bits = 7 - modsize;
609 int partial = value & maxvalues[bits];
610 unsigned char b = subfield | (partial << modsize);
611
612 if (partial != value) {
613 b |= 0x80;
614 self->buffer[self->index] = b;
615 self->index++;
616 return pack_packed_int(self, value >> bits);
617 }
618 self->buffer[self->index] = b;
619 self->index++;
620 return 0;
621 }
622
623 static int
pack_string(ProfilerObject * self,const char * s,Py_ssize_t len)624 pack_string(ProfilerObject *self, const char *s, Py_ssize_t len)
625 {
626 if (len + PISIZE + self->index >= BUFFERSIZE) {
627 if (flush_data(self) < 0)
628 return -1;
629 if (len + PISIZE + self->index >= BUFFERSIZE) {
630 PyErr_SetString(PyExc_ValueError, "string too large for internal buffer");
631 return -1;
632 }
633 }
634 assert(len < INT_MAX);
635 if (pack_packed_int(self, (int)len) < 0)
636 return -1;
637 memcpy(self->buffer + self->index, s, len);
638 self->index += len;
639 return 0;
640 }
641
642 static int
pack_add_info(ProfilerObject * self,const char * s1,const char * s2)643 pack_add_info(ProfilerObject *self, const char *s1, const char *s2)
644 {
645 Py_ssize_t len1 = strlen(s1);
646 Py_ssize_t len2 = strlen(s2);
647
648 if (len1 + len2 + PISIZE*2 + 1 + self->index >= BUFFERSIZE) {
649 if (flush_data(self) < 0)
650 return -1;
651 }
652 self->buffer[self->index] = WHAT_ADD_INFO;
653 self->index++;
654 if (pack_string(self, s1, len1) < 0)
655 return -1;
656 return pack_string(self, s2, len2);
657 }
658
659 static int
pack_define_file(ProfilerObject * self,int fileno,const char * filename)660 pack_define_file(ProfilerObject *self, int fileno, const char *filename)
661 {
662 Py_ssize_t len = strlen(filename);
663
664 if (len + PISIZE*2 + 1 + self->index >= BUFFERSIZE) {
665 if (flush_data(self) < 0)
666 return -1;
667 }
668 self->buffer[self->index] = WHAT_DEFINE_FILE;
669 self->index++;
670 if (pack_packed_int(self, fileno) < 0)
671 return -1;
672 return pack_string(self, filename, len);
673 }
674
675 static int
pack_define_func(ProfilerObject * self,int fileno,int lineno,const char * funcname)676 pack_define_func(ProfilerObject *self, int fileno, int lineno,
677 const char *funcname)
678 {
679 Py_ssize_t len = strlen(funcname);
680
681 if (len + PISIZE*3 + 1 + self->index >= BUFFERSIZE) {
682 if (flush_data(self) < 0)
683 return -1;
684 }
685 self->buffer[self->index] = WHAT_DEFINE_FUNC;
686 self->index++;
687 if (pack_packed_int(self, fileno) < 0)
688 return -1;
689 if (pack_packed_int(self, lineno) < 0)
690 return -1;
691 return pack_string(self, funcname, len);
692 }
693
694 static int
pack_line_times(ProfilerObject * self)695 pack_line_times(ProfilerObject *self)
696 {
697 if (2 + self->index >= BUFFERSIZE) {
698 if (flush_data(self) < 0)
699 return -1;
700 }
701 self->buffer[self->index] = WHAT_LINE_TIMES;
702 self->buffer[self->index + 1] = self->linetimings ? 1 : 0;
703 self->index += 2;
704 return 0;
705 }
706
707 static int
pack_frame_times(ProfilerObject * self)708 pack_frame_times(ProfilerObject *self)
709 {
710 if (2 + self->index >= BUFFERSIZE) {
711 if (flush_data(self) < 0)
712 return -1;
713 }
714 self->buffer[self->index] = WHAT_FRAME_TIMES;
715 self->buffer[self->index + 1] = self->frametimings ? 1 : 0;
716 self->index += 2;
717 return 0;
718 }
719
720 static inline int
pack_enter(ProfilerObject * self,int fileno,int tdelta,int lineno)721 pack_enter(ProfilerObject *self, int fileno, int tdelta, int lineno)
722 {
723 if (MPISIZE + PISIZE*2 + self->index >= BUFFERSIZE) {
724 if (flush_data(self) < 0)
725 return -1;
726 }
727 pack_modified_packed_int(self, fileno, 2, WHAT_ENTER);
728 pack_packed_int(self, lineno);
729 if (self->frametimings)
730 return pack_packed_int(self, tdelta);
731 else
732 return 0;
733 }
734
735 static inline int
pack_exit(ProfilerObject * self,int tdelta)736 pack_exit(ProfilerObject *self, int tdelta)
737 {
738 if (MPISIZE + self->index >= BUFFERSIZE) {
739 if (flush_data(self) < 0)
740 return -1;
741 }
742 if (self->frametimings)
743 return pack_modified_packed_int(self, tdelta, 2, WHAT_EXIT);
744 self->buffer[self->index] = WHAT_EXIT;
745 self->index++;
746 return 0;
747 }
748
749 static inline int
pack_lineno(ProfilerObject * self,int lineno)750 pack_lineno(ProfilerObject *self, int lineno)
751 {
752 if (MPISIZE + self->index >= BUFFERSIZE) {
753 if (flush_data(self) < 0)
754 return -1;
755 }
756 return pack_modified_packed_int(self, lineno, 2, WHAT_LINENO);
757 }
758
759 static inline int
pack_lineno_tdelta(ProfilerObject * self,int lineno,int tdelta)760 pack_lineno_tdelta(ProfilerObject *self, int lineno, int tdelta)
761 {
762 if (MPISIZE + PISIZE + self->index >= BUFFERSIZE) {
763 if (flush_data(self) < 0)
764 return 0;
765 }
766 if (pack_modified_packed_int(self, lineno, 2, WHAT_LINENO) < 0)
767 return -1;
768 return pack_packed_int(self, tdelta);
769 }
770
771 static inline int
get_fileno(ProfilerObject * self,PyCodeObject * fcode)772 get_fileno(ProfilerObject *self, PyCodeObject *fcode)
773 {
774 /* This is only used for ENTER events. */
775
776 PyObject *obj;
777 PyObject *dict;
778 int fileno;
779
780 obj = PyDict_GetItem(self->filemap, fcode->co_filename);
781 if (obj == NULL) {
782 /* first sighting of this file */
783 dict = PyDict_New();
784 if (dict == NULL) {
785 return -1;
786 }
787 fileno = self->next_fileno;
788 obj = Py_BuildValue("iN", fileno, dict);
789 if (obj == NULL) {
790 return -1;
791 }
792 if (PyDict_SetItem(self->filemap, fcode->co_filename, obj)) {
793 Py_DECREF(obj);
794 return -1;
795 }
796 self->next_fileno++;
797 Py_DECREF(obj);
798 if (pack_define_file(self, fileno,
799 PyString_AS_STRING(fcode->co_filename)) < 0)
800 return -1;
801 }
802 else {
803 /* already know this ID */
804 fileno = PyInt_AS_LONG(PyTuple_GET_ITEM(obj, 0));
805 dict = PyTuple_GET_ITEM(obj, 1);
806 }
807 /* make sure we save a function name for this (fileno, lineno) */
808 obj = PyInt_FromLong(fcode->co_firstlineno);
809 if (obj == NULL) {
810 /* We just won't have it saved; too bad. */
811 PyErr_Clear();
812 }
813 else {
814 PyObject *name = PyDict_GetItem(dict, obj);
815 if (name == NULL) {
816 if (pack_define_func(self, fileno, fcode->co_firstlineno,
817 PyString_AS_STRING(fcode->co_name)) < 0) {
818 Py_DECREF(obj);
819 return -1;
820 }
821 if (PyDict_SetItem(dict, obj, fcode->co_name)) {
822 Py_DECREF(obj);
823 return -1;
824 }
825 }
826 Py_DECREF(obj);
827 }
828 return fileno;
829 }
830
831 static inline int
get_tdelta(ProfilerObject * self)832 get_tdelta(ProfilerObject *self)
833 {
834 int tdelta;
835 #ifdef MS_WINDOWS
836 hs_time tv;
837 hs_time diff;
838
839 GETTIMEOFDAY(&tv);
840 diff = tv - self->prev_timeofday;
841 tdelta = (int)diff;
842 #else
843 struct timeval tv;
844
845 GETTIMEOFDAY(&tv);
846
847 tdelta = tv.tv_usec - self->prev_timeofday.tv_usec;
848 if (tv.tv_sec != self->prev_timeofday.tv_sec)
849 tdelta += (tv.tv_sec - self->prev_timeofday.tv_sec) * 1000000;
850 #endif
851 /* time can go backwards on some multiprocessor systems or by NTP */
852 if (tdelta < 0)
853 return 0;
854
855 self->prev_timeofday = tv;
856 return tdelta;
857 }
858
859
860 /* The workhorse: the profiler callback function. */
861
862 static int
tracer_callback(ProfilerObject * self,PyFrameObject * frame,int what,PyObject * arg)863 tracer_callback(ProfilerObject *self, PyFrameObject *frame, int what,
864 PyObject *arg)
865 {
866 int fileno;
867
868 switch (what) {
869 case PyTrace_CALL:
870 fileno = get_fileno(self, frame->f_code);
871 if (fileno < 0)
872 return -1;
873 return pack_enter(self, fileno,
874 self->frametimings ? get_tdelta(self) : -1,
875 frame->f_code->co_firstlineno);
876
877 case PyTrace_RETURN:
878 return pack_exit(self, get_tdelta(self));
879
880 case PyTrace_LINE: /* we only get these events if we asked for them */
881 if (self->linetimings)
882 return pack_lineno_tdelta(self, frame->f_lineno,
883 get_tdelta(self));
884 else
885 return pack_lineno(self, frame->f_lineno);
886
887 default:
888 /* ignore PyTrace_EXCEPTION */
889 break;
890 }
891 return 0;
892 }
893
894
895 /* A couple of useful helper functions. */
896
897 #ifdef MS_WINDOWS
898 static LARGE_INTEGER frequency = {0, 0};
899 #endif
900
901 static unsigned long timeofday_diff = 0;
902 static unsigned long rusage_diff = 0;
903
904 static void
calibrate(void)905 calibrate(void)
906 {
907 hs_time tv1, tv2;
908
909 #ifdef MS_WINDOWS
910 hs_time diff;
911 QueryPerformanceFrequency(&frequency);
912 #endif
913
914 GETTIMEOFDAY(&tv1);
915 while (1) {
916 GETTIMEOFDAY(&tv2);
917 #ifdef MS_WINDOWS
918 diff = tv2 - tv1;
919 if (diff != 0) {
920 timeofday_diff = (unsigned long)diff;
921 break;
922 }
923 #else
924 if (tv1.tv_sec != tv2.tv_sec || tv1.tv_usec != tv2.tv_usec) {
925 if (tv1.tv_sec == tv2.tv_sec)
926 timeofday_diff = tv2.tv_usec - tv1.tv_usec;
927 else
928 timeofday_diff = (1000000 - tv1.tv_usec) + tv2.tv_usec;
929 break;
930 }
931 #endif
932 }
933 #if defined(MS_WINDOWS) || defined(PYOS_OS2) || \
934 defined(__VMS) || defined (__QNX__)
935 rusage_diff = -1;
936 #else
937 {
938 struct rusage ru1, ru2;
939
940 getrusage(RUSAGE_SELF, &ru1);
941 while (1) {
942 getrusage(RUSAGE_SELF, &ru2);
943 if (ru1.ru_utime.tv_sec != ru2.ru_utime.tv_sec) {
944 rusage_diff = ((1000000 - ru1.ru_utime.tv_usec)
945 + ru2.ru_utime.tv_usec);
946 break;
947 }
948 else if (ru1.ru_utime.tv_usec != ru2.ru_utime.tv_usec) {
949 rusage_diff = ru2.ru_utime.tv_usec - ru1.ru_utime.tv_usec;
950 break;
951 }
952 else if (ru1.ru_stime.tv_sec != ru2.ru_stime.tv_sec) {
953 rusage_diff = ((1000000 - ru1.ru_stime.tv_usec)
954 + ru2.ru_stime.tv_usec);
955 break;
956 }
957 else if (ru1.ru_stime.tv_usec != ru2.ru_stime.tv_usec) {
958 rusage_diff = ru2.ru_stime.tv_usec - ru1.ru_stime.tv_usec;
959 break;
960 }
961 }
962 }
963 #endif
964 }
965
966 static void
do_start(ProfilerObject * self)967 do_start(ProfilerObject *self)
968 {
969 self->active = 1;
970 GETTIMEOFDAY(&self->prev_timeofday);
971 if (self->lineevents)
972 PyEval_SetTrace((Py_tracefunc) tracer_callback, (PyObject *)self);
973 else
974 PyEval_SetProfile((Py_tracefunc) tracer_callback, (PyObject *)self);
975 }
976
977 static void
do_stop(ProfilerObject * self)978 do_stop(ProfilerObject *self)
979 {
980 if (self->active) {
981 self->active = 0;
982 if (self->lineevents)
983 PyEval_SetTrace(NULL, NULL);
984 else
985 PyEval_SetProfile(NULL, NULL);
986 }
987 if (self->index > 0) {
988 /* Best effort to dump out any remaining data. */
989 flush_data(self);
990 }
991 }
992
993 static int
is_available(ProfilerObject * self)994 is_available(ProfilerObject *self)
995 {
996 if (self->active) {
997 PyErr_SetString(ProfilerError, "profiler already active");
998 return 0;
999 }
1000 if (self->logfp == NULL) {
1001 PyErr_SetString(ProfilerError, "profiler already closed");
1002 return 0;
1003 }
1004 return 1;
1005 }
1006
1007
1008 /* Profiler object interface methods. */
1009
1010 PyDoc_STRVAR(addinfo__doc__,
1011 "addinfo(key, value)\n"
1012 "Insert an ADD_INFO record into the log.");
1013
1014 static PyObject *
profiler_addinfo(ProfilerObject * self,PyObject * args)1015 profiler_addinfo(ProfilerObject *self, PyObject *args)
1016 {
1017 PyObject *result = NULL;
1018 char *key, *value;
1019
1020 if (PyArg_ParseTuple(args, "ss:addinfo", &key, &value)) {
1021 if (self->logfp == NULL)
1022 PyErr_SetString(ProfilerError, "profiler already closed");
1023 else {
1024 if (pack_add_info(self, key, value) == 0) {
1025 result = Py_None;
1026 Py_INCREF(result);
1027 }
1028 }
1029 }
1030 return result;
1031 }
1032
1033 PyDoc_STRVAR(close__doc__,
1034 "close()\n"
1035 "Shut down this profiler and close the log files, even if its active.");
1036
1037 static PyObject *
profiler_close(ProfilerObject * self)1038 profiler_close(ProfilerObject *self)
1039 {
1040 do_stop(self);
1041 if (self->logfp != NULL) {
1042 fclose(self->logfp);
1043 self->logfp = NULL;
1044 }
1045 Py_INCREF(Py_None);
1046 return Py_None;
1047 }
1048
1049 #define fileno__doc__ logreader_fileno__doc__
1050
1051 static PyObject *
profiler_fileno(ProfilerObject * self)1052 profiler_fileno(ProfilerObject *self)
1053 {
1054 if (self->logfp == NULL) {
1055 PyErr_SetString(PyExc_ValueError,
1056 "profiler's file object already closed");
1057 return NULL;
1058 }
1059 return PyInt_FromLong(fileno(self->logfp));
1060 }
1061
1062 PyDoc_STRVAR(runcall__doc__,
1063 "runcall(callable[, args[, kw]]) -> callable()\n"
1064 "Profile a specific function call, returning the result of that call.");
1065
1066 static PyObject *
profiler_runcall(ProfilerObject * self,PyObject * args)1067 profiler_runcall(ProfilerObject *self, PyObject *args)
1068 {
1069 PyObject *result = NULL;
1070 PyObject *callargs = NULL;
1071 PyObject *callkw = NULL;
1072 PyObject *callable;
1073
1074 if (PyArg_UnpackTuple(args, "runcall", 1, 3,
1075 &callable, &callargs, &callkw)) {
1076 if (is_available(self)) {
1077 do_start(self);
1078 result = PyEval_CallObjectWithKeywords(callable, callargs, callkw);
1079 do_stop(self);
1080 }
1081 }
1082 return result;
1083 }
1084
1085 PyDoc_STRVAR(runcode__doc__,
1086 "runcode(code, globals[, locals])\n"
1087 "Execute a code object while collecting profile data. If locals is\n"
1088 "omitted, globals is used for the locals as well.");
1089
1090 static PyObject *
profiler_runcode(ProfilerObject * self,PyObject * args)1091 profiler_runcode(ProfilerObject *self, PyObject *args)
1092 {
1093 PyObject *result = NULL;
1094 PyCodeObject *code;
1095 PyObject *globals;
1096 PyObject *locals = NULL;
1097
1098 if (PyArg_ParseTuple(args, "O!O!|O:runcode",
1099 &PyCode_Type, &code,
1100 &PyDict_Type, &globals,
1101 &locals)) {
1102 if (is_available(self)) {
1103 if (locals == NULL || locals == Py_None)
1104 locals = globals;
1105 else if (!PyDict_Check(locals)) {
1106 PyErr_SetString(PyExc_TypeError,
1107 "locals must be a dictionary or None");
1108 return NULL;
1109 }
1110 do_start(self);
1111 result = PyEval_EvalCode(code, globals, locals);
1112 do_stop(self);
1113 #if 0
1114 if (!PyErr_Occurred()) {
1115 result = Py_None;
1116 Py_INCREF(result);
1117 }
1118 #endif
1119 }
1120 }
1121 return result;
1122 }
1123
1124 PyDoc_STRVAR(start__doc__,
1125 "start()\n"
1126 "Install this profiler for the current thread.");
1127
1128 static PyObject *
profiler_start(ProfilerObject * self,PyObject * args)1129 profiler_start(ProfilerObject *self, PyObject *args)
1130 {
1131 PyObject *result = NULL;
1132
1133 if (is_available(self)) {
1134 do_start(self);
1135 result = Py_None;
1136 Py_INCREF(result);
1137 }
1138 return result;
1139 }
1140
1141 PyDoc_STRVAR(stop__doc__,
1142 "stop()\n"
1143 "Remove this profiler from the current thread.");
1144
1145 static PyObject *
profiler_stop(ProfilerObject * self,PyObject * args)1146 profiler_stop(ProfilerObject *self, PyObject *args)
1147 {
1148 PyObject *result = NULL;
1149
1150 if (!self->active)
1151 PyErr_SetString(ProfilerError, "profiler not active");
1152 else {
1153 do_stop(self);
1154 result = Py_None;
1155 Py_INCREF(result);
1156 }
1157 return result;
1158 }
1159
1160
1161 /* Python API support. */
1162
1163 static void
profiler_dealloc(ProfilerObject * self)1164 profiler_dealloc(ProfilerObject *self)
1165 {
1166 do_stop(self);
1167 if (self->logfp != NULL)
1168 fclose(self->logfp);
1169 Py_XDECREF(self->filemap);
1170 Py_XDECREF(self->logfilename);
1171 PyObject_Del((PyObject *)self);
1172 }
1173
1174 static PyMethodDef profiler_methods[] = {
1175 {"addinfo", (PyCFunction)profiler_addinfo, METH_VARARGS, addinfo__doc__},
1176 {"close", (PyCFunction)profiler_close, METH_NOARGS, close__doc__},
1177 {"fileno", (PyCFunction)profiler_fileno, METH_NOARGS, fileno__doc__},
1178 {"runcall", (PyCFunction)profiler_runcall, METH_VARARGS, runcall__doc__},
1179 {"runcode", (PyCFunction)profiler_runcode, METH_VARARGS, runcode__doc__},
1180 {"start", (PyCFunction)profiler_start, METH_NOARGS, start__doc__},
1181 {"stop", (PyCFunction)profiler_stop, METH_NOARGS, stop__doc__},
1182 {NULL, NULL}
1183 };
1184
1185 static PyMemberDef profiler_members[] = {
1186 {"frametimings", T_LONG, offsetof(ProfilerObject, linetimings), READONLY},
1187 {"lineevents", T_LONG, offsetof(ProfilerObject, lineevents), READONLY},
1188 {"linetimings", T_LONG, offsetof(ProfilerObject, linetimings), READONLY},
1189 {NULL}
1190 };
1191
1192 static PyObject *
profiler_get_closed(ProfilerObject * self,void * closure)1193 profiler_get_closed(ProfilerObject *self, void *closure)
1194 {
1195 PyObject *result = (self->logfp == NULL) ? Py_True : Py_False;
1196 Py_INCREF(result);
1197 return result;
1198 }
1199
1200 static PyGetSetDef profiler_getsets[] = {
1201 {"closed", (getter)profiler_get_closed, NULL,
1202 PyDoc_STR("True if the profiler's output file has already been closed.")},
1203 {NULL}
1204 };
1205
1206
1207 PyDoc_STRVAR(profiler_object__doc__,
1208 "High-performance profiler object.\n"
1209 "\n"
1210 "Methods:\n"
1211 "\n"
1212 "close(): Stop the profiler and close the log files.\n"
1213 "fileno(): Returns the file descriptor of the log file.\n"
1214 "runcall(): Run a single function call with profiling enabled.\n"
1215 "runcode(): Execute a code object with profiling enabled.\n"
1216 "start(): Install the profiler and return.\n"
1217 "stop(): Remove the profiler.\n"
1218 "\n"
1219 "Attributes (read-only):\n"
1220 "\n"
1221 "closed: True if the profiler has already been closed.\n"
1222 "frametimings: True if ENTER/EXIT events collect timing information.\n"
1223 "lineevents: True if line events are reported to the profiler.\n"
1224 "linetimings: True if line events collect timing information.");
1225
1226 static PyTypeObject ProfilerType = {
1227 PyVarObject_HEAD_INIT(NULL, 0)
1228 "_hotshot.ProfilerType", /* tp_name */
1229 (int) sizeof(ProfilerObject), /* tp_basicsize */
1230 0, /* tp_itemsize */
1231 (destructor)profiler_dealloc, /* tp_dealloc */
1232 0, /* tp_print */
1233 0, /* tp_getattr */
1234 0, /* tp_setattr */
1235 0, /* tp_compare */
1236 0, /* tp_repr */
1237 0, /* tp_as_number */
1238 0, /* tp_as_sequence */
1239 0, /* tp_as_mapping */
1240 0, /* tp_hash */
1241 0, /* tp_call */
1242 0, /* tp_str */
1243 PyObject_GenericGetAttr, /* tp_getattro */
1244 0, /* tp_setattro */
1245 0, /* tp_as_buffer */
1246 Py_TPFLAGS_DEFAULT, /* tp_flags */
1247 profiler_object__doc__, /* tp_doc */
1248 0, /* tp_traverse */
1249 0, /* tp_clear */
1250 0, /* tp_richcompare */
1251 0, /* tp_weaklistoffset */
1252 0, /* tp_iter */
1253 0, /* tp_iternext */
1254 profiler_methods, /* tp_methods */
1255 profiler_members, /* tp_members */
1256 profiler_getsets, /* tp_getset */
1257 0, /* tp_base */
1258 0, /* tp_dict */
1259 0, /* tp_descr_get */
1260 0, /* tp_descr_set */
1261 };
1262
1263
1264 static PyMethodDef logreader_methods[] = {
1265 {"close", (PyCFunction)logreader_close, METH_NOARGS,
1266 logreader_close__doc__},
1267 {"fileno", (PyCFunction)logreader_fileno, METH_NOARGS,
1268 logreader_fileno__doc__},
1269 {NULL, NULL}
1270 };
1271
1272 static PyMemberDef logreader_members[] = {
1273 {"info", T_OBJECT, offsetof(LogReaderObject, info), RO,
1274 PyDoc_STR("Dictionary mapping informational keys to lists of values.")},
1275 {NULL}
1276 };
1277
1278
1279 PyDoc_STRVAR(logreader__doc__,
1280 "logreader(filename) --> log-iterator\n\
1281 Create a log-reader for the timing information file.");
1282
1283 static PySequenceMethods logreader_as_sequence = {
1284 0, /* sq_length */
1285 0, /* sq_concat */
1286 0, /* sq_repeat */
1287 (ssizeargfunc)logreader_sq_item, /* sq_item */
1288 0, /* sq_slice */
1289 0, /* sq_ass_item */
1290 0, /* sq_ass_slice */
1291 0, /* sq_contains */
1292 0, /* sq_inplace_concat */
1293 0, /* sq_inplace_repeat */
1294 };
1295
1296 static PyObject *
logreader_get_closed(LogReaderObject * self,void * closure)1297 logreader_get_closed(LogReaderObject *self, void *closure)
1298 {
1299 PyObject *result = (self->logfp == NULL) ? Py_True : Py_False;
1300 Py_INCREF(result);
1301 return result;
1302 }
1303
1304 static PyGetSetDef logreader_getsets[] = {
1305 {"closed", (getter)logreader_get_closed, NULL,
1306 PyDoc_STR("True if the logreader's input file has already been closed.")},
1307 {NULL}
1308 };
1309
1310 static PyTypeObject LogReaderType = {
1311 PyVarObject_HEAD_INIT(NULL, 0)
1312 "_hotshot.LogReaderType", /* tp_name */
1313 (int) sizeof(LogReaderObject), /* tp_basicsize */
1314 0, /* tp_itemsize */
1315 (destructor)logreader_dealloc, /* tp_dealloc */
1316 0, /* tp_print */
1317 0, /* tp_getattr */
1318 0, /* tp_setattr */
1319 0, /* tp_compare */
1320 0, /* tp_repr */
1321 0, /* tp_as_number */
1322 &logreader_as_sequence, /* tp_as_sequence */
1323 0, /* tp_as_mapping */
1324 0, /* tp_hash */
1325 0, /* tp_call */
1326 0, /* tp_str */
1327 PyObject_GenericGetAttr, /* tp_getattro */
1328 0, /* tp_setattro */
1329 0, /* tp_as_buffer */
1330 Py_TPFLAGS_DEFAULT, /* tp_flags */
1331 logreader__doc__, /* tp_doc */
1332 0, /* tp_traverse */
1333 0, /* tp_clear */
1334 0, /* tp_richcompare */
1335 0, /* tp_weaklistoffset */
1336 PyObject_SelfIter, /* tp_iter */
1337 (iternextfunc)logreader_tp_iternext,/* tp_iternext */
1338 logreader_methods, /* tp_methods */
1339 logreader_members, /* tp_members */
1340 logreader_getsets, /* tp_getset */
1341 0, /* tp_base */
1342 0, /* tp_dict */
1343 0, /* tp_descr_get */
1344 0, /* tp_descr_set */
1345 };
1346
1347 static PyObject *
hotshot_logreader(PyObject * unused,PyObject * args)1348 hotshot_logreader(PyObject *unused, PyObject *args)
1349 {
1350 LogReaderObject *self = NULL;
1351 char *filename;
1352 int c;
1353 int err = 0;
1354
1355 if (PyArg_ParseTuple(args, "s:logreader", &filename)) {
1356 self = PyObject_New(LogReaderObject, &LogReaderType);
1357 if (self != NULL) {
1358 self->frametimings = 1;
1359 self->linetimings = 0;
1360 self->info = NULL;
1361 self->logfp = fopen(filename, "rb");
1362 if (self->logfp == NULL) {
1363 PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
1364 goto error;
1365 }
1366 self->info = PyDict_New();
1367 if (self->info == NULL)
1368 goto error;
1369 /* read initial info */
1370 for (;;) {
1371 if ((c = fgetc(self->logfp)) == EOF) {
1372 eof_error(self);
1373 goto error;
1374 }
1375 if (c != WHAT_ADD_INFO) {
1376 ungetc(c, self->logfp);
1377 break;
1378 }
1379 err = unpack_add_info(self);
1380 if (err) {
1381 if (err == ERR_EOF)
1382 eof_error(self);
1383 else
1384 PyErr_SetString(PyExc_RuntimeError,
1385 "unexpected error");
1386 goto error;
1387 }
1388 }
1389 }
1390 }
1391 return (PyObject *) self;
1392 error:
1393 Py_DECREF(self);
1394 return NULL;
1395 }
1396
1397
1398 /* Return a Python string that represents the version number without the
1399 * extra cruft added by revision control, even if the right options were
1400 * given to the "cvs export" command to make it not include the extra
1401 * cruft.
1402 */
1403 static char *
get_version_string(void)1404 get_version_string(void)
1405 {
1406 static char *rcsid = "$Revision$";
1407 char *rev = rcsid;
1408 char *buffer;
1409 int i = 0;
1410
1411 while (*rev && !isdigit(Py_CHARMASK(*rev)))
1412 ++rev;
1413 while (rev[i] != ' ' && rev[i] != '\0')
1414 ++i;
1415 buffer = (char *)malloc(i + 1);
1416 if (buffer != NULL) {
1417 memmove(buffer, rev, i);
1418 buffer[i] = '\0';
1419 }
1420 return buffer;
1421 }
1422
1423 /* Write out a RFC 822-style header with various useful bits of
1424 * information to make the output easier to manage.
1425 */
1426 static int
write_header(ProfilerObject * self)1427 write_header(ProfilerObject *self)
1428 {
1429 char *buffer;
1430 char cwdbuffer[PATH_MAX];
1431 PyObject *temp;
1432 Py_ssize_t i, len;
1433
1434 buffer = get_version_string();
1435 if (buffer == NULL) {
1436 PyErr_NoMemory();
1437 return -1;
1438 }
1439 pack_add_info(self, "hotshot-version", buffer);
1440 pack_add_info(self, "requested-frame-timings",
1441 (self->frametimings ? "yes" : "no"));
1442 pack_add_info(self, "requested-line-events",
1443 (self->lineevents ? "yes" : "no"));
1444 pack_add_info(self, "requested-line-timings",
1445 (self->linetimings ? "yes" : "no"));
1446 pack_add_info(self, "platform", Py_GetPlatform());
1447 pack_add_info(self, "executable", Py_GetProgramFullPath());
1448 free(buffer);
1449 buffer = (char *) Py_GetVersion();
1450 if (buffer == NULL)
1451 PyErr_Clear();
1452 else
1453 pack_add_info(self, "executable-version", buffer);
1454
1455 #ifdef MS_WINDOWS
1456 PyOS_snprintf(cwdbuffer, sizeof(cwdbuffer), "%I64d", frequency.QuadPart);
1457 pack_add_info(self, "reported-performance-frequency", cwdbuffer);
1458 #else
1459 PyOS_snprintf(cwdbuffer, sizeof(cwdbuffer), "%lu", rusage_diff);
1460 pack_add_info(self, "observed-interval-getrusage", cwdbuffer);
1461 PyOS_snprintf(cwdbuffer, sizeof(cwdbuffer), "%lu", timeofday_diff);
1462 pack_add_info(self, "observed-interval-gettimeofday", cwdbuffer);
1463 #endif
1464
1465 pack_add_info(self, "current-directory",
1466 getcwd(cwdbuffer, sizeof cwdbuffer));
1467
1468 temp = PySys_GetObject("path");
1469 if (temp == NULL || !PyList_Check(temp)) {
1470 PyErr_SetString(PyExc_RuntimeError, "sys.path must be a list");
1471 return -1;
1472 }
1473 len = PyList_GET_SIZE(temp);
1474 for (i = 0; i < len; ++i) {
1475 PyObject *item = PyList_GET_ITEM(temp, i);
1476 buffer = PyString_AsString(item);
1477 if (buffer == NULL) {
1478 pack_add_info(self, "sys-path-entry", "<non-string-path-entry>");
1479 PyErr_Clear();
1480 }
1481 else {
1482 pack_add_info(self, "sys-path-entry", buffer);
1483 }
1484 }
1485 pack_frame_times(self);
1486 pack_line_times(self);
1487
1488 return 0;
1489 }
1490
1491 PyDoc_STRVAR(profiler__doc__,
1492 "profiler(logfilename[, lineevents[, linetimes]]) -> profiler\n\
1493 Create a new profiler object.");
1494
1495 static PyObject *
hotshot_profiler(PyObject * unused,PyObject * args)1496 hotshot_profiler(PyObject *unused, PyObject *args)
1497 {
1498 char *logfilename;
1499 ProfilerObject *self = NULL;
1500 int lineevents = 0;
1501 int linetimings = 1;
1502
1503 if (PyArg_ParseTuple(args, "s|ii:profiler", &logfilename,
1504 &lineevents, &linetimings)) {
1505 self = PyObject_New(ProfilerObject, &ProfilerType);
1506 if (self == NULL)
1507 return NULL;
1508 self->frametimings = 1;
1509 self->lineevents = lineevents ? 1 : 0;
1510 self->linetimings = (lineevents && linetimings) ? 1 : 0;
1511 self->index = 0;
1512 self->active = 0;
1513 self->next_fileno = 0;
1514 self->logfp = NULL;
1515 self->logfilename = PyTuple_GET_ITEM(args, 0);
1516 Py_INCREF(self->logfilename);
1517 self->filemap = PyDict_New();
1518 if (self->filemap == NULL) {
1519 Py_DECREF(self);
1520 return NULL;
1521 }
1522 self->logfp = fopen(logfilename, "wb");
1523 if (self->logfp == NULL) {
1524 Py_DECREF(self);
1525 PyErr_SetFromErrnoWithFilename(PyExc_IOError, logfilename);
1526 return NULL;
1527 }
1528 if (timeofday_diff == 0) {
1529 /* Run this several times since sometimes the first
1530 * doesn't give the lowest values, and we're really trying
1531 * to determine the lowest.
1532 */
1533 calibrate();
1534 calibrate();
1535 calibrate();
1536 }
1537 if (write_header(self)) {
1538 /* some error occurred, exception has been set */
1539 Py_DECREF(self);
1540 self = NULL;
1541 }
1542 }
1543 return (PyObject *) self;
1544 }
1545
1546 PyDoc_STRVAR(coverage__doc__,
1547 "coverage(logfilename) -> profiler\n\
1548 Returns a profiler that doesn't collect any timing information, which is\n\
1549 useful in building a coverage analysis tool.");
1550
1551 static PyObject *
hotshot_coverage(PyObject * unused,PyObject * args)1552 hotshot_coverage(PyObject *unused, PyObject *args)
1553 {
1554 char *logfilename;
1555 PyObject *result = NULL;
1556
1557 if (PyArg_ParseTuple(args, "s:coverage", &logfilename)) {
1558 result = hotshot_profiler(unused, args);
1559 if (result != NULL) {
1560 ProfilerObject *self = (ProfilerObject *) result;
1561 self->frametimings = 0;
1562 self->linetimings = 0;
1563 self->lineevents = 1;
1564 }
1565 }
1566 return result;
1567 }
1568
1569 PyDoc_VAR(resolution__doc__) =
1570 #ifdef MS_WINDOWS
1571 PyDoc_STR(
1572 "resolution() -> (performance-counter-ticks, update-frequency)\n"
1573 "Return the resolution of the timer provided by the QueryPerformanceCounter()\n"
1574 "function. The first value is the smallest observed change, and the second\n"
1575 "is the result of QueryPerformanceFrequency()."
1576 )
1577 #else
1578 PyDoc_STR(
1579 "resolution() -> (gettimeofday-usecs, getrusage-usecs)\n"
1580 "Return the resolution of the timers provided by the gettimeofday() and\n"
1581 "getrusage() system calls, or -1 if the call is not supported."
1582 )
1583 #endif
1584 ;
1585
1586 static PyObject *
hotshot_resolution(PyObject * self,PyObject * unused)1587 hotshot_resolution(PyObject *self, PyObject *unused)
1588 {
1589 if (timeofday_diff == 0) {
1590 calibrate();
1591 calibrate();
1592 calibrate();
1593 }
1594 #ifdef MS_WINDOWS
1595 return Py_BuildValue("ii", timeofday_diff, frequency.LowPart);
1596 #else
1597 return Py_BuildValue("ii", timeofday_diff, rusage_diff);
1598 #endif
1599 }
1600
1601
1602 static PyMethodDef functions[] = {
1603 {"coverage", hotshot_coverage, METH_VARARGS, coverage__doc__},
1604 {"profiler", hotshot_profiler, METH_VARARGS, profiler__doc__},
1605 {"logreader", hotshot_logreader, METH_VARARGS, logreader__doc__},
1606 {"resolution", hotshot_resolution, METH_NOARGS, resolution__doc__},
1607 {NULL, NULL}
1608 };
1609
1610
1611 void
init_hotshot(void)1612 init_hotshot(void)
1613 {
1614 PyObject *module;
1615
1616 Py_TYPE(&LogReaderType) = &PyType_Type;
1617 Py_TYPE(&ProfilerType) = &PyType_Type;
1618 module = Py_InitModule("_hotshot", functions);
1619 if (module != NULL) {
1620 char *s = get_version_string();
1621
1622 PyModule_AddStringConstant(module, "__version__", s);
1623 free(s);
1624 Py_INCREF(&LogReaderType);
1625 PyModule_AddObject(module, "LogReaderType",
1626 (PyObject *)&LogReaderType);
1627 Py_INCREF(&ProfilerType);
1628 PyModule_AddObject(module, "ProfilerType",
1629 (PyObject *)&ProfilerType);
1630
1631 if (ProfilerError == NULL)
1632 ProfilerError = PyErr_NewException("hotshot.ProfilerError",
1633 NULL, NULL);
1634 if (ProfilerError != NULL) {
1635 Py_INCREF(ProfilerError);
1636 PyModule_AddObject(module, "ProfilerError", ProfilerError);
1637 }
1638 PyModule_AddIntConstant(module, "WHAT_ENTER", WHAT_ENTER);
1639 PyModule_AddIntConstant(module, "WHAT_EXIT", WHAT_EXIT);
1640 PyModule_AddIntConstant(module, "WHAT_LINENO", WHAT_LINENO);
1641 PyModule_AddIntConstant(module, "WHAT_OTHER", WHAT_OTHER);
1642 PyModule_AddIntConstant(module, "WHAT_ADD_INFO", WHAT_ADD_INFO);
1643 PyModule_AddIntConstant(module, "WHAT_DEFINE_FILE", WHAT_DEFINE_FILE);
1644 PyModule_AddIntConstant(module, "WHAT_DEFINE_FUNC", WHAT_DEFINE_FUNC);
1645 PyModule_AddIntConstant(module, "WHAT_LINE_TIMES", WHAT_LINE_TIMES);
1646 }
1647 }
1648