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 = NULL; 342 PyObject *value = NULL; 343 PyObject *list; 344 int err; 345 346 err = unpack_string(self, &key); 347 if (err) 348 goto finally; 349 err = unpack_string(self, &value); 350 if (err) 351 goto finally; 352 list = PyDict_GetItem(self->info, key); 353 if (list == NULL) { 354 list = PyList_New(0); 355 if (list == NULL) { 356 err = ERR_EXCEPTION; 357 goto finally; 358 } 359 if (PyDict_SetItem(self->info, key, list)) { 360 Py_DECREF(list); 361 err = ERR_EXCEPTION; 362 goto finally; 363 } 364 Py_DECREF(list); 365 } 366 if (PyList_Append(list, value)) 367 err = ERR_EXCEPTION; 368 369 finally: 370 Py_XDECREF(key); 371 Py_XDECREF(value); 372 return err; 373 } 374 375 376 static void 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 if (len + PISIZE + self->index >= BUFFERSIZE) { 630 PyErr_SetString(PyExc_ValueError, "string too large for internal buffer"); 631 return -1; 632 } 633 } 634 assert(len < INT_MAX); 635 if (pack_packed_int(self, (int)len) < 0) 636 return -1; 637 memcpy(self->buffer + self->index, s, len); 638 self->index += len; 639 return 0; 640 } 641 642 static int 643 pack_add_info(ProfilerObject *self, const char *s1, const char *s2) 644 { 645 Py_ssize_t len1 = strlen(s1); 646 Py_ssize_t len2 = strlen(s2); 647 648 if (len1 + len2 + PISIZE*2 + 1 + self->index >= BUFFERSIZE) { 649 if (flush_data(self) < 0) 650 return -1; 651 } 652 self->buffer[self->index] = WHAT_ADD_INFO; 653 self->index++; 654 if (pack_string(self, s1, len1) < 0) 655 return -1; 656 return pack_string(self, s2, len2); 657 } 658 659 static int 660 pack_define_file(ProfilerObject *self, int fileno, const char *filename) 661 { 662 Py_ssize_t len = strlen(filename); 663 664 if (len + PISIZE*2 + 1 + self->index >= BUFFERSIZE) { 665 if (flush_data(self) < 0) 666 return -1; 667 } 668 self->buffer[self->index] = WHAT_DEFINE_FILE; 669 self->index++; 670 if (pack_packed_int(self, fileno) < 0) 671 return -1; 672 return pack_string(self, filename, len); 673 } 674 675 static int 676 pack_define_func(ProfilerObject *self, int fileno, int lineno, 677 const char *funcname) 678 { 679 Py_ssize_t len = strlen(funcname); 680 681 if (len + PISIZE*3 + 1 + self->index >= BUFFERSIZE) { 682 if (flush_data(self) < 0) 683 return -1; 684 } 685 self->buffer[self->index] = WHAT_DEFINE_FUNC; 686 self->index++; 687 if (pack_packed_int(self, fileno) < 0) 688 return -1; 689 if (pack_packed_int(self, lineno) < 0) 690 return -1; 691 return pack_string(self, funcname, len); 692 } 693 694 static int 695 pack_line_times(ProfilerObject *self) 696 { 697 if (2 + self->index >= BUFFERSIZE) { 698 if (flush_data(self) < 0) 699 return -1; 700 } 701 self->buffer[self->index] = WHAT_LINE_TIMES; 702 self->buffer[self->index + 1] = self->linetimings ? 1 : 0; 703 self->index += 2; 704 return 0; 705 } 706 707 static int 708 pack_frame_times(ProfilerObject *self) 709 { 710 if (2 + self->index >= BUFFERSIZE) { 711 if (flush_data(self) < 0) 712 return -1; 713 } 714 self->buffer[self->index] = WHAT_FRAME_TIMES; 715 self->buffer[self->index + 1] = self->frametimings ? 1 : 0; 716 self->index += 2; 717 return 0; 718 } 719 720 static inline int 721 pack_enter(ProfilerObject *self, int fileno, int tdelta, int lineno) 722 { 723 if (MPISIZE + PISIZE*2 + self->index >= BUFFERSIZE) { 724 if (flush_data(self) < 0) 725 return -1; 726 } 727 pack_modified_packed_int(self, fileno, 2, WHAT_ENTER); 728 pack_packed_int(self, lineno); 729 if (self->frametimings) 730 return pack_packed_int(self, tdelta); 731 else 732 return 0; 733 } 734 735 static inline int 736 pack_exit(ProfilerObject *self, int tdelta) 737 { 738 if (MPISIZE + self->index >= BUFFERSIZE) { 739 if (flush_data(self) < 0) 740 return -1; 741 } 742 if (self->frametimings) 743 return pack_modified_packed_int(self, tdelta, 2, WHAT_EXIT); 744 self->buffer[self->index] = WHAT_EXIT; 745 self->index++; 746 return 0; 747 } 748 749 static inline int 750 pack_lineno(ProfilerObject *self, int lineno) 751 { 752 if (MPISIZE + self->index >= BUFFERSIZE) { 753 if (flush_data(self) < 0) 754 return -1; 755 } 756 return pack_modified_packed_int(self, lineno, 2, WHAT_LINENO); 757 } 758 759 static inline int 760 pack_lineno_tdelta(ProfilerObject *self, int lineno, int tdelta) 761 { 762 if (MPISIZE + PISIZE + self->index >= BUFFERSIZE) { 763 if (flush_data(self) < 0) 764 return 0; 765 } 766 if (pack_modified_packed_int(self, lineno, 2, WHAT_LINENO) < 0) 767 return -1; 768 return pack_packed_int(self, tdelta); 769 } 770 771 static inline int 772 get_fileno(ProfilerObject *self, PyCodeObject *fcode) 773 { 774 /* This is only used for ENTER events. */ 775 776 PyObject *obj; 777 PyObject *dict; 778 int fileno; 779 780 obj = PyDict_GetItem(self->filemap, fcode->co_filename); 781 if (obj == NULL) { 782 /* first sighting of this file */ 783 dict = PyDict_New(); 784 if (dict == NULL) { 785 return -1; 786 } 787 fileno = self->next_fileno; 788 obj = Py_BuildValue("iN", fileno, dict); 789 if (obj == NULL) { 790 return -1; 791 } 792 if (PyDict_SetItem(self->filemap, fcode->co_filename, obj)) { 793 Py_DECREF(obj); 794 return -1; 795 } 796 self->next_fileno++; 797 Py_DECREF(obj); 798 if (pack_define_file(self, fileno, 799 PyString_AS_STRING(fcode->co_filename)) < 0) 800 return -1; 801 } 802 else { 803 /* already know this ID */ 804 fileno = PyInt_AS_LONG(PyTuple_GET_ITEM(obj, 0)); 805 dict = PyTuple_GET_ITEM(obj, 1); 806 } 807 /* make sure we save a function name for this (fileno, lineno) */ 808 obj = PyInt_FromLong(fcode->co_firstlineno); 809 if (obj == NULL) { 810 /* We just won't have it saved; too bad. */ 811 PyErr_Clear(); 812 } 813 else { 814 PyObject *name = PyDict_GetItem(dict, obj); 815 if (name == NULL) { 816 if (pack_define_func(self, fileno, fcode->co_firstlineno, 817 PyString_AS_STRING(fcode->co_name)) < 0) { 818 Py_DECREF(obj); 819 return -1; 820 } 821 if (PyDict_SetItem(dict, obj, fcode->co_name)) { 822 Py_DECREF(obj); 823 return -1; 824 } 825 } 826 Py_DECREF(obj); 827 } 828 return fileno; 829 } 830 831 static inline int 832 get_tdelta(ProfilerObject *self) 833 { 834 int tdelta; 835 #ifdef MS_WINDOWS 836 hs_time tv; 837 hs_time diff; 838 839 GETTIMEOFDAY(&tv); 840 diff = tv - self->prev_timeofday; 841 tdelta = (int)diff; 842 #else 843 struct timeval tv; 844 845 GETTIMEOFDAY(&tv); 846 847 tdelta = tv.tv_usec - self->prev_timeofday.tv_usec; 848 if (tv.tv_sec != self->prev_timeofday.tv_sec) 849 tdelta += (tv.tv_sec - self->prev_timeofday.tv_sec) * 1000000; 850 #endif 851 /* time can go backwards on some multiprocessor systems or by NTP */ 852 if (tdelta < 0) 853 return 0; 854 855 self->prev_timeofday = tv; 856 return tdelta; 857 } 858 859 860 /* The workhorse: the profiler callback function. */ 861 862 static int 863 tracer_callback(ProfilerObject *self, PyFrameObject *frame, int what, 864 PyObject *arg) 865 { 866 int fileno; 867 868 switch (what) { 869 case PyTrace_CALL: 870 fileno = get_fileno(self, frame->f_code); 871 if (fileno < 0) 872 return -1; 873 return pack_enter(self, fileno, 874 self->frametimings ? get_tdelta(self) : -1, 875 frame->f_code->co_firstlineno); 876 877 case PyTrace_RETURN: 878 return pack_exit(self, get_tdelta(self)); 879 880 case PyTrace_LINE: /* we only get these events if we asked for them */ 881 if (self->linetimings) 882 return pack_lineno_tdelta(self, frame->f_lineno, 883 get_tdelta(self)); 884 else 885 return pack_lineno(self, frame->f_lineno); 886 887 default: 888 /* ignore PyTrace_EXCEPTION */ 889 break; 890 } 891 return 0; 892 } 893 894 895 /* A couple of useful helper functions. */ 896 897 #ifdef MS_WINDOWS 898 static LARGE_INTEGER frequency = {0, 0}; 899 #endif 900 901 static unsigned long timeofday_diff = 0; 902 static unsigned long rusage_diff = 0; 903 904 static void 905 calibrate(void) 906 { 907 hs_time tv1, tv2; 908 909 #ifdef MS_WINDOWS 910 hs_time diff; 911 QueryPerformanceFrequency(&frequency); 912 #endif 913 914 GETTIMEOFDAY(&tv1); 915 while (1) { 916 GETTIMEOFDAY(&tv2); 917 #ifdef MS_WINDOWS 918 diff = tv2 - tv1; 919 if (diff != 0) { 920 timeofday_diff = (unsigned long)diff; 921 break; 922 } 923 #else 924 if (tv1.tv_sec != tv2.tv_sec || tv1.tv_usec != tv2.tv_usec) { 925 if (tv1.tv_sec == tv2.tv_sec) 926 timeofday_diff = tv2.tv_usec - tv1.tv_usec; 927 else 928 timeofday_diff = (1000000 - tv1.tv_usec) + tv2.tv_usec; 929 break; 930 } 931 #endif 932 } 933 #if defined(MS_WINDOWS) || defined(PYOS_OS2) || \ 934 defined(__VMS) || defined (__QNX__) 935 rusage_diff = -1; 936 #else 937 { 938 struct rusage ru1, ru2; 939 940 getrusage(RUSAGE_SELF, &ru1); 941 while (1) { 942 getrusage(RUSAGE_SELF, &ru2); 943 if (ru1.ru_utime.tv_sec != ru2.ru_utime.tv_sec) { 944 rusage_diff = ((1000000 - ru1.ru_utime.tv_usec) 945 + ru2.ru_utime.tv_usec); 946 break; 947 } 948 else if (ru1.ru_utime.tv_usec != ru2.ru_utime.tv_usec) { 949 rusage_diff = ru2.ru_utime.tv_usec - ru1.ru_utime.tv_usec; 950 break; 951 } 952 else if (ru1.ru_stime.tv_sec != ru2.ru_stime.tv_sec) { 953 rusage_diff = ((1000000 - ru1.ru_stime.tv_usec) 954 + ru2.ru_stime.tv_usec); 955 break; 956 } 957 else if (ru1.ru_stime.tv_usec != ru2.ru_stime.tv_usec) { 958 rusage_diff = ru2.ru_stime.tv_usec - ru1.ru_stime.tv_usec; 959 break; 960 } 961 } 962 } 963 #endif 964 } 965 966 static void 967 do_start(ProfilerObject *self) 968 { 969 self->active = 1; 970 GETTIMEOFDAY(&self->prev_timeofday); 971 if (self->lineevents) 972 PyEval_SetTrace((Py_tracefunc) tracer_callback, (PyObject *)self); 973 else 974 PyEval_SetProfile((Py_tracefunc) tracer_callback, (PyObject *)self); 975 } 976 977 static void 978 do_stop(ProfilerObject *self) 979 { 980 if (self->active) { 981 self->active = 0; 982 if (self->lineevents) 983 PyEval_SetTrace(NULL, NULL); 984 else 985 PyEval_SetProfile(NULL, NULL); 986 } 987 if (self->index > 0) { 988 /* Best effort to dump out any remaining data. */ 989 flush_data(self); 990 } 991 } 992 993 static int 994 is_available(ProfilerObject *self) 995 { 996 if (self->active) { 997 PyErr_SetString(ProfilerError, "profiler already active"); 998 return 0; 999 } 1000 if (self->logfp == NULL) { 1001 PyErr_SetString(ProfilerError, "profiler already closed"); 1002 return 0; 1003 } 1004 return 1; 1005 } 1006 1007 1008 /* Profiler object interface methods. */ 1009 1010 PyDoc_STRVAR(addinfo__doc__, 1011 "addinfo(key, value)\n" 1012 "Insert an ADD_INFO record into the log."); 1013 1014 static PyObject * 1015 profiler_addinfo(ProfilerObject *self, PyObject *args) 1016 { 1017 PyObject *result = NULL; 1018 char *key, *value; 1019 1020 if (PyArg_ParseTuple(args, "ss:addinfo", &key, &value)) { 1021 if (self->logfp == NULL) 1022 PyErr_SetString(ProfilerError, "profiler already closed"); 1023 else { 1024 if (pack_add_info(self, key, value) == 0) { 1025 result = Py_None; 1026 Py_INCREF(result); 1027 } 1028 } 1029 } 1030 return result; 1031 } 1032 1033 PyDoc_STRVAR(close__doc__, 1034 "close()\n" 1035 "Shut down this profiler and close the log files, even if its active."); 1036 1037 static PyObject * 1038 profiler_close(ProfilerObject *self) 1039 { 1040 do_stop(self); 1041 if (self->logfp != NULL) { 1042 fclose(self->logfp); 1043 self->logfp = NULL; 1044 } 1045 Py_INCREF(Py_None); 1046 return Py_None; 1047 } 1048 1049 #define fileno__doc__ logreader_fileno__doc__ 1050 1051 static PyObject * 1052 profiler_fileno(ProfilerObject *self) 1053 { 1054 if (self->logfp == NULL) { 1055 PyErr_SetString(PyExc_ValueError, 1056 "profiler's file object already closed"); 1057 return NULL; 1058 } 1059 return PyInt_FromLong(fileno(self->logfp)); 1060 } 1061 1062 PyDoc_STRVAR(runcall__doc__, 1063 "runcall(callable[, args[, kw]]) -> callable()\n" 1064 "Profile a specific function call, returning the result of that call."); 1065 1066 static PyObject * 1067 profiler_runcall(ProfilerObject *self, PyObject *args) 1068 { 1069 PyObject *result = NULL; 1070 PyObject *callargs = NULL; 1071 PyObject *callkw = NULL; 1072 PyObject *callable; 1073 1074 if (PyArg_UnpackTuple(args, "runcall", 1, 3, 1075 &callable, &callargs, &callkw)) { 1076 if (is_available(self)) { 1077 do_start(self); 1078 result = PyEval_CallObjectWithKeywords(callable, callargs, callkw); 1079 do_stop(self); 1080 } 1081 } 1082 return result; 1083 } 1084 1085 PyDoc_STRVAR(runcode__doc__, 1086 "runcode(code, globals[, locals])\n" 1087 "Execute a code object while collecting profile data. If locals is\n" 1088 "omitted, globals is used for the locals as well."); 1089 1090 static PyObject * 1091 profiler_runcode(ProfilerObject *self, PyObject *args) 1092 { 1093 PyObject *result = NULL; 1094 PyCodeObject *code; 1095 PyObject *globals; 1096 PyObject *locals = NULL; 1097 1098 if (PyArg_ParseTuple(args, "O!O!|O:runcode", 1099 &PyCode_Type, &code, 1100 &PyDict_Type, &globals, 1101 &locals)) { 1102 if (is_available(self)) { 1103 if (locals == NULL || locals == Py_None) 1104 locals = globals; 1105 else if (!PyDict_Check(locals)) { 1106 PyErr_SetString(PyExc_TypeError, 1107 "locals must be a dictionary or None"); 1108 return NULL; 1109 } 1110 do_start(self); 1111 result = PyEval_EvalCode(code, globals, locals); 1112 do_stop(self); 1113 #if 0 1114 if (!PyErr_Occurred()) { 1115 result = Py_None; 1116 Py_INCREF(result); 1117 } 1118 #endif 1119 } 1120 } 1121 return result; 1122 } 1123 1124 PyDoc_STRVAR(start__doc__, 1125 "start()\n" 1126 "Install this profiler for the current thread."); 1127 1128 static PyObject * 1129 profiler_start(ProfilerObject *self, PyObject *args) 1130 { 1131 PyObject *result = NULL; 1132 1133 if (is_available(self)) { 1134 do_start(self); 1135 result = Py_None; 1136 Py_INCREF(result); 1137 } 1138 return result; 1139 } 1140 1141 PyDoc_STRVAR(stop__doc__, 1142 "stop()\n" 1143 "Remove this profiler from the current thread."); 1144 1145 static PyObject * 1146 profiler_stop(ProfilerObject *self, PyObject *args) 1147 { 1148 PyObject *result = NULL; 1149 1150 if (!self->active) 1151 PyErr_SetString(ProfilerError, "profiler not active"); 1152 else { 1153 do_stop(self); 1154 result = Py_None; 1155 Py_INCREF(result); 1156 } 1157 return result; 1158 } 1159 1160 1161 /* Python API support. */ 1162 1163 static void 1164 profiler_dealloc(ProfilerObject *self) 1165 { 1166 do_stop(self); 1167 if (self->logfp != NULL) 1168 fclose(self->logfp); 1169 Py_XDECREF(self->filemap); 1170 Py_XDECREF(self->logfilename); 1171 PyObject_Del((PyObject *)self); 1172 } 1173 1174 static PyMethodDef profiler_methods[] = { 1175 {"addinfo", (PyCFunction)profiler_addinfo, METH_VARARGS, addinfo__doc__}, 1176 {"close", (PyCFunction)profiler_close, METH_NOARGS, close__doc__}, 1177 {"fileno", (PyCFunction)profiler_fileno, METH_NOARGS, fileno__doc__}, 1178 {"runcall", (PyCFunction)profiler_runcall, METH_VARARGS, runcall__doc__}, 1179 {"runcode", (PyCFunction)profiler_runcode, METH_VARARGS, runcode__doc__}, 1180 {"start", (PyCFunction)profiler_start, METH_NOARGS, start__doc__}, 1181 {"stop", (PyCFunction)profiler_stop, METH_NOARGS, stop__doc__}, 1182 {NULL, NULL} 1183 }; 1184 1185 static PyMemberDef profiler_members[] = { 1186 {"frametimings", T_LONG, offsetof(ProfilerObject, linetimings), READONLY}, 1187 {"lineevents", T_LONG, offsetof(ProfilerObject, lineevents), READONLY}, 1188 {"linetimings", T_LONG, offsetof(ProfilerObject, linetimings), READONLY}, 1189 {NULL} 1190 }; 1191 1192 static PyObject * 1193 profiler_get_closed(ProfilerObject *self, void *closure) 1194 { 1195 PyObject *result = (self->logfp == NULL) ? Py_True : Py_False; 1196 Py_INCREF(result); 1197 return result; 1198 } 1199 1200 static PyGetSetDef profiler_getsets[] = { 1201 {"closed", (getter)profiler_get_closed, NULL, 1202 PyDoc_STR("True if the profiler's output file has already been closed.")}, 1203 {NULL} 1204 }; 1205 1206 1207 PyDoc_STRVAR(profiler_object__doc__, 1208 "High-performance profiler object.\n" 1209 "\n" 1210 "Methods:\n" 1211 "\n" 1212 "close(): Stop the profiler and close the log files.\n" 1213 "fileno(): Returns the file descriptor of the log file.\n" 1214 "runcall(): Run a single function call with profiling enabled.\n" 1215 "runcode(): Execute a code object with profiling enabled.\n" 1216 "start(): Install the profiler and return.\n" 1217 "stop(): Remove the profiler.\n" 1218 "\n" 1219 "Attributes (read-only):\n" 1220 "\n" 1221 "closed: True if the profiler has already been closed.\n" 1222 "frametimings: True if ENTER/EXIT events collect timing information.\n" 1223 "lineevents: True if line events are reported to the profiler.\n" 1224 "linetimings: True if line events collect timing information."); 1225 1226 static PyTypeObject ProfilerType = { 1227 PyVarObject_HEAD_INIT(NULL, 0) 1228 "_hotshot.ProfilerType", /* tp_name */ 1229 (int) sizeof(ProfilerObject), /* tp_basicsize */ 1230 0, /* tp_itemsize */ 1231 (destructor)profiler_dealloc, /* tp_dealloc */ 1232 0, /* tp_print */ 1233 0, /* tp_getattr */ 1234 0, /* tp_setattr */ 1235 0, /* tp_compare */ 1236 0, /* tp_repr */ 1237 0, /* tp_as_number */ 1238 0, /* tp_as_sequence */ 1239 0, /* tp_as_mapping */ 1240 0, /* tp_hash */ 1241 0, /* tp_call */ 1242 0, /* tp_str */ 1243 PyObject_GenericGetAttr, /* tp_getattro */ 1244 0, /* tp_setattro */ 1245 0, /* tp_as_buffer */ 1246 Py_TPFLAGS_DEFAULT, /* tp_flags */ 1247 profiler_object__doc__, /* tp_doc */ 1248 0, /* tp_traverse */ 1249 0, /* tp_clear */ 1250 0, /* tp_richcompare */ 1251 0, /* tp_weaklistoffset */ 1252 0, /* tp_iter */ 1253 0, /* tp_iternext */ 1254 profiler_methods, /* tp_methods */ 1255 profiler_members, /* tp_members */ 1256 profiler_getsets, /* tp_getset */ 1257 0, /* tp_base */ 1258 0, /* tp_dict */ 1259 0, /* tp_descr_get */ 1260 0, /* tp_descr_set */ 1261 }; 1262 1263 1264 static PyMethodDef logreader_methods[] = { 1265 {"close", (PyCFunction)logreader_close, METH_NOARGS, 1266 logreader_close__doc__}, 1267 {"fileno", (PyCFunction)logreader_fileno, METH_NOARGS, 1268 logreader_fileno__doc__}, 1269 {NULL, NULL} 1270 }; 1271 1272 static PyMemberDef logreader_members[] = { 1273 {"info", T_OBJECT, offsetof(LogReaderObject, info), RO, 1274 PyDoc_STR("Dictionary mapping informational keys to lists of values.")}, 1275 {NULL} 1276 }; 1277 1278 1279 PyDoc_STRVAR(logreader__doc__, 1280 "logreader(filename) --> log-iterator\n\ 1281 Create a log-reader for the timing information file."); 1282 1283 static PySequenceMethods logreader_as_sequence = { 1284 0, /* sq_length */ 1285 0, /* sq_concat */ 1286 0, /* sq_repeat */ 1287 (ssizeargfunc)logreader_sq_item, /* sq_item */ 1288 0, /* sq_slice */ 1289 0, /* sq_ass_item */ 1290 0, /* sq_ass_slice */ 1291 0, /* sq_contains */ 1292 0, /* sq_inplace_concat */ 1293 0, /* sq_inplace_repeat */ 1294 }; 1295 1296 static PyObject * 1297 logreader_get_closed(LogReaderObject *self, void *closure) 1298 { 1299 PyObject *result = (self->logfp == NULL) ? Py_True : Py_False; 1300 Py_INCREF(result); 1301 return result; 1302 } 1303 1304 static PyGetSetDef logreader_getsets[] = { 1305 {"closed", (getter)logreader_get_closed, NULL, 1306 PyDoc_STR("True if the logreader's input file has already been closed.")}, 1307 {NULL} 1308 }; 1309 1310 static PyTypeObject LogReaderType = { 1311 PyVarObject_HEAD_INIT(NULL, 0) 1312 "_hotshot.LogReaderType", /* tp_name */ 1313 (int) sizeof(LogReaderObject), /* tp_basicsize */ 1314 0, /* tp_itemsize */ 1315 (destructor)logreader_dealloc, /* tp_dealloc */ 1316 0, /* tp_print */ 1317 0, /* tp_getattr */ 1318 0, /* tp_setattr */ 1319 0, /* tp_compare */ 1320 0, /* tp_repr */ 1321 0, /* tp_as_number */ 1322 &logreader_as_sequence, /* tp_as_sequence */ 1323 0, /* tp_as_mapping */ 1324 0, /* tp_hash */ 1325 0, /* tp_call */ 1326 0, /* tp_str */ 1327 PyObject_GenericGetAttr, /* tp_getattro */ 1328 0, /* tp_setattro */ 1329 0, /* tp_as_buffer */ 1330 Py_TPFLAGS_DEFAULT, /* tp_flags */ 1331 logreader__doc__, /* tp_doc */ 1332 0, /* tp_traverse */ 1333 0, /* tp_clear */ 1334 0, /* tp_richcompare */ 1335 0, /* tp_weaklistoffset */ 1336 PyObject_SelfIter, /* tp_iter */ 1337 (iternextfunc)logreader_tp_iternext,/* tp_iternext */ 1338 logreader_methods, /* tp_methods */ 1339 logreader_members, /* tp_members */ 1340 logreader_getsets, /* tp_getset */ 1341 0, /* tp_base */ 1342 0, /* tp_dict */ 1343 0, /* tp_descr_get */ 1344 0, /* tp_descr_set */ 1345 }; 1346 1347 static PyObject * 1348 hotshot_logreader(PyObject *unused, PyObject *args) 1349 { 1350 LogReaderObject *self = NULL; 1351 char *filename; 1352 int c; 1353 int err = 0; 1354 1355 if (PyArg_ParseTuple(args, "s:logreader", &filename)) { 1356 self = PyObject_New(LogReaderObject, &LogReaderType); 1357 if (self != NULL) { 1358 self->frametimings = 1; 1359 self->linetimings = 0; 1360 self->info = NULL; 1361 self->logfp = fopen(filename, "rb"); 1362 if (self->logfp == NULL) { 1363 PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename); 1364 goto error; 1365 } 1366 self->info = PyDict_New(); 1367 if (self->info == NULL) 1368 goto error; 1369 /* read initial info */ 1370 for (;;) { 1371 if ((c = fgetc(self->logfp)) == EOF) { 1372 eof_error(self); 1373 goto error; 1374 } 1375 if (c != WHAT_ADD_INFO) { 1376 ungetc(c, self->logfp); 1377 break; 1378 } 1379 err = unpack_add_info(self); 1380 if (err) { 1381 if (err == ERR_EOF) 1382 eof_error(self); 1383 else 1384 PyErr_SetString(PyExc_RuntimeError, 1385 "unexpected error"); 1386 goto error; 1387 } 1388 } 1389 } 1390 } 1391 return (PyObject *) self; 1392 error: 1393 Py_DECREF(self); 1394 return NULL; 1395 } 1396 1397 1398 /* Return a Python string that represents the version number without the 1399 * extra cruft added by revision control, even if the right options were 1400 * given to the "cvs export" command to make it not include the extra 1401 * cruft. 1402 */ 1403 static char * 1404 get_version_string(void) 1405 { 1406 static char *rcsid = "$Revision$"; 1407 char *rev = rcsid; 1408 char *buffer; 1409 int i = 0; 1410 1411 while (*rev && !isdigit(Py_CHARMASK(*rev))) 1412 ++rev; 1413 while (rev[i] != ' ' && rev[i] != '\0') 1414 ++i; 1415 buffer = (char *)malloc(i + 1); 1416 if (buffer != NULL) { 1417 memmove(buffer, rev, i); 1418 buffer[i] = '\0'; 1419 } 1420 return buffer; 1421 } 1422 1423 /* Write out a RFC 822-style header with various useful bits of 1424 * information to make the output easier to manage. 1425 */ 1426 static int 1427 write_header(ProfilerObject *self) 1428 { 1429 char *buffer; 1430 char cwdbuffer[PATH_MAX]; 1431 PyObject *temp; 1432 Py_ssize_t i, len; 1433 1434 buffer = get_version_string(); 1435 if (buffer == NULL) { 1436 PyErr_NoMemory(); 1437 return -1; 1438 } 1439 pack_add_info(self, "hotshot-version", buffer); 1440 pack_add_info(self, "requested-frame-timings", 1441 (self->frametimings ? "yes" : "no")); 1442 pack_add_info(self, "requested-line-events", 1443 (self->lineevents ? "yes" : "no")); 1444 pack_add_info(self, "requested-line-timings", 1445 (self->linetimings ? "yes" : "no")); 1446 pack_add_info(self, "platform", Py_GetPlatform()); 1447 pack_add_info(self, "executable", Py_GetProgramFullPath()); 1448 free(buffer); 1449 buffer = (char *) Py_GetVersion(); 1450 if (buffer == NULL) 1451 PyErr_Clear(); 1452 else 1453 pack_add_info(self, "executable-version", buffer); 1454 1455 #ifdef MS_WINDOWS 1456 PyOS_snprintf(cwdbuffer, sizeof(cwdbuffer), "%I64d", frequency.QuadPart); 1457 pack_add_info(self, "reported-performance-frequency", cwdbuffer); 1458 #else 1459 PyOS_snprintf(cwdbuffer, sizeof(cwdbuffer), "%lu", rusage_diff); 1460 pack_add_info(self, "observed-interval-getrusage", cwdbuffer); 1461 PyOS_snprintf(cwdbuffer, sizeof(cwdbuffer), "%lu", timeofday_diff); 1462 pack_add_info(self, "observed-interval-gettimeofday", cwdbuffer); 1463 #endif 1464 1465 pack_add_info(self, "current-directory", 1466 getcwd(cwdbuffer, sizeof cwdbuffer)); 1467 1468 temp = PySys_GetObject("path"); 1469 if (temp == NULL || !PyList_Check(temp)) { 1470 PyErr_SetString(PyExc_RuntimeError, "sys.path must be a list"); 1471 return -1; 1472 } 1473 len = PyList_GET_SIZE(temp); 1474 for (i = 0; i < len; ++i) { 1475 PyObject *item = PyList_GET_ITEM(temp, i); 1476 buffer = PyString_AsString(item); 1477 if (buffer == NULL) { 1478 pack_add_info(self, "sys-path-entry", "<non-string-path-entry>"); 1479 PyErr_Clear(); 1480 } 1481 else { 1482 pack_add_info(self, "sys-path-entry", buffer); 1483 } 1484 } 1485 pack_frame_times(self); 1486 pack_line_times(self); 1487 1488 return 0; 1489 } 1490 1491 PyDoc_STRVAR(profiler__doc__, 1492 "profiler(logfilename[, lineevents[, linetimes]]) -> profiler\n\ 1493 Create a new profiler object."); 1494 1495 static PyObject * 1496 hotshot_profiler(PyObject *unused, PyObject *args) 1497 { 1498 char *logfilename; 1499 ProfilerObject *self = NULL; 1500 int lineevents = 0; 1501 int linetimings = 1; 1502 1503 if (PyArg_ParseTuple(args, "s|ii:profiler", &logfilename, 1504 &lineevents, &linetimings)) { 1505 self = PyObject_New(ProfilerObject, &ProfilerType); 1506 if (self == NULL) 1507 return NULL; 1508 self->frametimings = 1; 1509 self->lineevents = lineevents ? 1 : 0; 1510 self->linetimings = (lineevents && linetimings) ? 1 : 0; 1511 self->index = 0; 1512 self->active = 0; 1513 self->next_fileno = 0; 1514 self->logfp = NULL; 1515 self->logfilename = PyTuple_GET_ITEM(args, 0); 1516 Py_INCREF(self->logfilename); 1517 self->filemap = PyDict_New(); 1518 if (self->filemap == NULL) { 1519 Py_DECREF(self); 1520 return NULL; 1521 } 1522 self->logfp = fopen(logfilename, "wb"); 1523 if (self->logfp == NULL) { 1524 Py_DECREF(self); 1525 PyErr_SetFromErrnoWithFilename(PyExc_IOError, logfilename); 1526 return NULL; 1527 } 1528 if (timeofday_diff == 0) { 1529 /* Run this several times since sometimes the first 1530 * doesn't give the lowest values, and we're really trying 1531 * to determine the lowest. 1532 */ 1533 calibrate(); 1534 calibrate(); 1535 calibrate(); 1536 } 1537 if (write_header(self)) { 1538 /* some error occurred, exception has been set */ 1539 Py_DECREF(self); 1540 self = NULL; 1541 } 1542 } 1543 return (PyObject *) self; 1544 } 1545 1546 PyDoc_STRVAR(coverage__doc__, 1547 "coverage(logfilename) -> profiler\n\ 1548 Returns a profiler that doesn't collect any timing information, which is\n\ 1549 useful in building a coverage analysis tool."); 1550 1551 static PyObject * 1552 hotshot_coverage(PyObject *unused, PyObject *args) 1553 { 1554 char *logfilename; 1555 PyObject *result = NULL; 1556 1557 if (PyArg_ParseTuple(args, "s:coverage", &logfilename)) { 1558 result = hotshot_profiler(unused, args); 1559 if (result != NULL) { 1560 ProfilerObject *self = (ProfilerObject *) result; 1561 self->frametimings = 0; 1562 self->linetimings = 0; 1563 self->lineevents = 1; 1564 } 1565 } 1566 return result; 1567 } 1568 1569 PyDoc_VAR(resolution__doc__) = 1570 #ifdef MS_WINDOWS 1571 PyDoc_STR( 1572 "resolution() -> (performance-counter-ticks, update-frequency)\n" 1573 "Return the resolution of the timer provided by the QueryPerformanceCounter()\n" 1574 "function. The first value is the smallest observed change, and the second\n" 1575 "is the result of QueryPerformanceFrequency()." 1576 ) 1577 #else 1578 PyDoc_STR( 1579 "resolution() -> (gettimeofday-usecs, getrusage-usecs)\n" 1580 "Return the resolution of the timers provided by the gettimeofday() and\n" 1581 "getrusage() system calls, or -1 if the call is not supported." 1582 ) 1583 #endif 1584 ; 1585 1586 static PyObject * 1587 hotshot_resolution(PyObject *self, PyObject *unused) 1588 { 1589 if (timeofday_diff == 0) { 1590 calibrate(); 1591 calibrate(); 1592 calibrate(); 1593 } 1594 #ifdef MS_WINDOWS 1595 return Py_BuildValue("ii", timeofday_diff, frequency.LowPart); 1596 #else 1597 return Py_BuildValue("ii", timeofday_diff, rusage_diff); 1598 #endif 1599 } 1600 1601 1602 static PyMethodDef functions[] = { 1603 {"coverage", hotshot_coverage, METH_VARARGS, coverage__doc__}, 1604 {"profiler", hotshot_profiler, METH_VARARGS, profiler__doc__}, 1605 {"logreader", hotshot_logreader, METH_VARARGS, logreader__doc__}, 1606 {"resolution", hotshot_resolution, METH_NOARGS, resolution__doc__}, 1607 {NULL, NULL} 1608 }; 1609 1610 1611 void 1612 init_hotshot(void) 1613 { 1614 PyObject *module; 1615 1616 Py_TYPE(&LogReaderType) = &PyType_Type; 1617 Py_TYPE(&ProfilerType) = &PyType_Type; 1618 module = Py_InitModule("_hotshot", functions); 1619 if (module != NULL) { 1620 char *s = get_version_string(); 1621 1622 PyModule_AddStringConstant(module, "__version__", s); 1623 free(s); 1624 Py_INCREF(&LogReaderType); 1625 PyModule_AddObject(module, "LogReaderType", 1626 (PyObject *)&LogReaderType); 1627 Py_INCREF(&ProfilerType); 1628 PyModule_AddObject(module, "ProfilerType", 1629 (PyObject *)&ProfilerType); 1630 1631 if (ProfilerError == NULL) 1632 ProfilerError = PyErr_NewException("hotshot.ProfilerError", 1633 NULL, NULL); 1634 if (ProfilerError != NULL) { 1635 Py_INCREF(ProfilerError); 1636 PyModule_AddObject(module, "ProfilerError", ProfilerError); 1637 } 1638 PyModule_AddIntConstant(module, "WHAT_ENTER", WHAT_ENTER); 1639 PyModule_AddIntConstant(module, "WHAT_EXIT", WHAT_EXIT); 1640 PyModule_AddIntConstant(module, "WHAT_LINENO", WHAT_LINENO); 1641 PyModule_AddIntConstant(module, "WHAT_OTHER", WHAT_OTHER); 1642 PyModule_AddIntConstant(module, "WHAT_ADD_INFO", WHAT_ADD_INFO); 1643 PyModule_AddIntConstant(module, "WHAT_DEFINE_FILE", WHAT_DEFINE_FILE); 1644 PyModule_AddIntConstant(module, "WHAT_DEFINE_FUNC", WHAT_DEFINE_FUNC); 1645 PyModule_AddIntConstant(module, "WHAT_LINE_TIMES", WHAT_LINE_TIMES); 1646 } 1647 } 1648