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