• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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