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