]>
Commit | Line | Data |
---|---|---|
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 | |
22 | typedef __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 | |
39 | typedef 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 | |
74 | typedef 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 | |
90 | typedef 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 | |
98 | static 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 | |
112 | PyDoc_STRVAR(logreader_close__doc__,\r | |
113 | "close()\n"\r | |
114 | "Close the log file, preventing additional records from being read.");\r | |
115 | \r | |
116 | static PyObject *\r | |
117 | logreader_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 | |
128 | PyDoc_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 | |
133 | static PyObject *\r | |
134 | logreader_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 | |
277 | static int\r | |
278 | unpack_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 | |
303 | static int\r | |
304 | unpack_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 | |
338 | static int\r | |
339 | unpack_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 | |
376 | static void\r | |
377 | eof_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 | |
385 | static PyObject *\r | |
386 | logreader_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 | |
406 | restart:\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 | |
528 | static void\r | |
529 | logreader_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 | |
539 | static PyObject *\r | |
540 | logreader_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 | |
550 | static void\r | |
551 | do_stop(ProfilerObject *self);\r | |
552 | \r | |
553 | static int\r | |
554 | flush_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 | |
582 | static inline int\r | |
583 | pack_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 | |
602 | static inline int\r | |
603 | pack_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 | |
623 | static int\r | |
624 | pack_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 | |
638 | static int\r | |
639 | pack_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 | |
655 | static int\r | |
656 | pack_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 | |
671 | static int\r | |
672 | pack_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 | |
690 | static int\r | |
691 | pack_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 | |
703 | static int\r | |
704 | pack_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 | |
716 | static inline int\r | |
717 | pack_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 | |
731 | static inline int\r | |
732 | pack_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 | |
745 | static inline int\r | |
746 | pack_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 | |
755 | static inline int\r | |
756 | pack_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 | |
767 | static inline int\r | |
768 | get_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 | |
827 | static inline int\r | |
828 | get_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 | |
858 | static int\r | |
859 | tracer_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 | |
894 | static LARGE_INTEGER frequency = {0, 0};\r | |
895 | #endif\r | |
896 | \r | |
897 | static unsigned long timeofday_diff = 0;\r | |
898 | static unsigned long rusage_diff = 0;\r | |
899 | \r | |
900 | static void\r | |
901 | calibrate(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 | |
962 | static void\r | |
963 | do_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 | |
973 | static void\r | |
974 | do_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 | |
989 | static int\r | |
990 | is_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 | |
1006 | PyDoc_STRVAR(addinfo__doc__,\r | |
1007 | "addinfo(key, value)\n"\r | |
1008 | "Insert an ADD_INFO record into the log.");\r | |
1009 | \r | |
1010 | static PyObject *\r | |
1011 | profiler_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 | |
1029 | PyDoc_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 | |
1033 | static PyObject *\r | |
1034 | profiler_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 | |
1047 | static PyObject *\r | |
1048 | profiler_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 | |
1058 | PyDoc_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 | |
1062 | static PyObject *\r | |
1063 | profiler_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 | |
1081 | PyDoc_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 | |
1086 | static PyObject *\r | |
1087 | profiler_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 | |
1120 | PyDoc_STRVAR(start__doc__,\r | |
1121 | "start()\n"\r | |
1122 | "Install this profiler for the current thread.");\r | |
1123 | \r | |
1124 | static PyObject *\r | |
1125 | profiler_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 | |
1137 | PyDoc_STRVAR(stop__doc__,\r | |
1138 | "stop()\n"\r | |
1139 | "Remove this profiler from the current thread.");\r | |
1140 | \r | |
1141 | static PyObject *\r | |
1142 | profiler_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 | |
1159 | static void\r | |
1160 | profiler_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 | |
1170 | static 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 | |
1181 | static 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 | |
1188 | static PyObject *\r | |
1189 | profiler_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 | |
1196 | static 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 | |
1203 | PyDoc_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 | |
1222 | static 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 | |
1260 | static 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 | |
1268 | static 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 | |
1275 | PyDoc_STRVAR(logreader__doc__,\r | |
1276 | "logreader(filename) --> log-iterator\n\\r | |
1277 | Create a log-reader for the timing information file.");\r | |
1278 | \r | |
1279 | static 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 | |
1292 | static PyObject *\r | |
1293 | logreader_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 | |
1300 | static 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 | |
1306 | static 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 | |
1343 | static PyObject *\r | |
1344 | hotshot_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 | |
1399 | static char *\r | |
1400 | get_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 | |
1422 | static int\r | |
1423 | write_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 | |
1487 | PyDoc_STRVAR(profiler__doc__,\r | |
1488 | "profiler(logfilename[, lineevents[, linetimes]]) -> profiler\n\\r | |
1489 | Create a new profiler object.");\r | |
1490 | \r | |
1491 | static PyObject *\r | |
1492 | hotshot_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 | |
1542 | PyDoc_STRVAR(coverage__doc__,\r | |
1543 | "coverage(logfilename) -> profiler\n\\r | |
1544 | Returns a profiler that doesn't collect any timing information, which is\n\\r | |
1545 | useful in building a coverage analysis tool.");\r | |
1546 | \r | |
1547 | static PyObject *\r | |
1548 | hotshot_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 | |
1565 | PyDoc_VAR(resolution__doc__) =\r | |
1566 | #ifdef MS_WINDOWS\r | |
1567 | PyDoc_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 | |
1574 | PyDoc_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 | |
1582 | static PyObject *\r | |
1583 | hotshot_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 | |
1598 | static 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 | |
1607 | void\r | |
1608 | init_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 |