1 /* 2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include <tracing.h> 9 10 #include <stdarg.h> 11 #include <stdlib.h> 12 13 #include <debug.h> 14 #include <kernel.h> 15 #include <team.h> 16 #include <thread.h> 17 #include <util/AutoLock.h> 18 19 20 #if ENABLE_TRACING 21 22 //#define TRACE_TRACING 23 #ifdef TRACE_TRACING 24 # define TRACE(x) dprintf_no_syslog x 25 #else 26 # define TRACE(x) ; 27 #endif 28 29 30 enum { 31 WRAP_ENTRY = 0x01, 32 ENTRY_INITIALIZED = 0x02, 33 BUFFER_ENTRY = 0x04, 34 FILTER_MATCH = 0x08 35 }; 36 37 static const size_t kBufferSize = MAX_TRACE_SIZE / 4; 38 39 static trace_entry* sBuffer; 40 static trace_entry* sFirstEntry; 41 static trace_entry* sAfterLastEntry; 42 static uint32 sEntries; 43 static uint32 sWritten; 44 static spinlock sLock; 45 46 47 static trace_entry* 48 next_entry(trace_entry* entry) 49 { 50 entry += entry->size; 51 if ((entry->flags & WRAP_ENTRY) != 0) 52 entry = sBuffer; 53 54 if (entry == sAfterLastEntry) 55 return NULL; 56 57 return entry; 58 } 59 60 61 static trace_entry* 62 previous_entry(trace_entry* entry) 63 { 64 if (entry == sFirstEntry) 65 return NULL; 66 67 if (entry == sBuffer) { 68 // beginning of buffer -- previous entry is a wrap entry 69 entry = sBuffer + kBufferSize - entry->previous_size; 70 } 71 72 return entry - entry->previous_size; 73 } 74 75 76 static bool 77 free_first_entry() 78 { 79 TRACE((" skip start %p, %lu*4 bytes\n", sFirstEntry, sFirstEntry->size)); 80 81 trace_entry* newFirst = next_entry(sFirstEntry); 82 83 if (sFirstEntry->flags & BUFFER_ENTRY) { 84 // a buffer entry -- just skip it 85 } else if (sFirstEntry->flags & ENTRY_INITIALIZED) { 86 // fully initialized TraceEntry -- destroy it 87 ((TraceEntry*)sFirstEntry)->~TraceEntry(); 88 sEntries--; 89 } else { 90 // Not fully initialized TraceEntry. We can't free it, since 91 // then it's constructor might still write into the memory and 92 // overwrite data of the entry we're going to allocate. 93 // We can't do anything until this entry can be discarded. 94 return false; 95 } 96 97 if (newFirst == NULL) { 98 // everything is freed -- that practically this can't happen, if 99 // the buffer is large enough to hold three max-sized entries 100 sFirstEntry = sAfterLastEntry = sBuffer; 101 TRACE(("free_first_entry(): all entries freed!\n")); 102 } else 103 sFirstEntry = newFirst; 104 105 return true; 106 } 107 108 109 /*! Makes sure we have needed * 4 bytes of memory at sAfterLastEntry. 110 Returns \c false, if unable to free that much. 111 */ 112 static bool 113 make_space(size_t needed) 114 { 115 // we need space for sAfterLastEntry, too (in case we need to wrap around 116 // later) 117 needed++; 118 119 // If there's not enough space (free or occupied) after sAfterLastEntry, 120 // we free all entries in that region and wrap around. 121 if (sAfterLastEntry + needed > sBuffer + kBufferSize) { 122 TRACE(("make_space(%lu), wrapping around: after last: %p\n", needed, 123 sAfterLastEntry)); 124 125 // Free all entries after sAfterLastEntry and one more at the beginning 126 // of the buffer. 127 while (sFirstEntry > sAfterLastEntry) { 128 if (!free_first_entry()) 129 return false; 130 } 131 if (sAfterLastEntry != sBuffer && !free_first_entry()) 132 return false; 133 134 // just in case free_first_entry() freed the very last existing entry 135 if (sAfterLastEntry == sBuffer) 136 return true; 137 138 // mark as wrap entry and actually wrap around 139 trace_entry* wrapEntry = sAfterLastEntry; 140 wrapEntry->size = 0; 141 wrapEntry->flags = WRAP_ENTRY; 142 sAfterLastEntry = sBuffer; 143 sAfterLastEntry->previous_size = sBuffer + kBufferSize - wrapEntry; 144 } 145 146 if (sFirstEntry <= sAfterLastEntry) { 147 // buffer is empty or the space after sAfterLastEntry is unoccupied 148 return true; 149 } 150 151 // free the first entries, until there's enough space 152 size_t space = sFirstEntry - sAfterLastEntry; 153 154 if (space < needed) { 155 TRACE(("make_space(%lu), left %ld\n", needed, space)); 156 } 157 158 while (space < needed) { 159 space += sFirstEntry->size; 160 161 if (!free_first_entry()) 162 return false; 163 } 164 165 TRACE((" out: start %p, entries %ld\n", sFirstEntry, sEntries)); 166 167 return true; 168 } 169 170 171 static trace_entry* 172 allocate_entry(size_t size, uint16 flags) 173 { 174 if (sAfterLastEntry == NULL || size == 0 || size >= 65532) 175 return NULL; 176 177 InterruptsSpinLocker _(sLock); 178 179 size = (size + 3) >> 2; 180 // 4 byte aligned, don't store the lower 2 bits 181 182 TRACE(("allocate_entry(%lu), start %p, end %p, buffer %p\n", size * 4, 183 sFirstEntry, sAfterLastEntry, sBuffer)); 184 185 if (!make_space(size)) 186 return NULL; 187 188 trace_entry* entry = sAfterLastEntry; 189 entry->size = size; 190 entry->flags = flags; 191 sAfterLastEntry += size; 192 sAfterLastEntry->previous_size = size; 193 194 if (!(flags & BUFFER_ENTRY)) 195 sEntries++; 196 197 TRACE((" entry: %p, end %p, start %p, entries %ld\n", entry, 198 sAfterLastEntry, sFirstEntry, sEntries)); 199 200 return entry; 201 } 202 203 204 #endif // ENABLE_TRACING 205 206 207 // #pragma mark - 208 209 210 TraceOutput::TraceOutput(char* buffer, size_t bufferSize, uint32 flags) 211 : fBuffer(buffer), 212 fCapacity(bufferSize), 213 fFlags(flags) 214 { 215 Clear(); 216 } 217 218 219 void 220 TraceOutput::Clear() 221 { 222 if (fCapacity > 0) 223 fBuffer[0] = '\0'; 224 fSize = 0; 225 } 226 227 228 void 229 TraceOutput::Print(const char* format,...) 230 { 231 if (IsFull()) 232 return; 233 234 va_list args; 235 va_start(args, format); 236 fSize += vsnprintf(fBuffer + fSize, fCapacity - fSize, format, args); 237 va_end(args); 238 } 239 240 241 void 242 TraceOutput::SetLastEntryTime(bigtime_t time) 243 { 244 fLastEntryTime = time; 245 } 246 247 248 bigtime_t 249 TraceOutput::LastEntryTime() const 250 { 251 return fLastEntryTime; 252 } 253 254 255 // #pragma mark - 256 257 258 TraceEntry::TraceEntry() 259 { 260 } 261 262 263 TraceEntry::~TraceEntry() 264 { 265 } 266 267 268 void 269 TraceEntry::Dump(TraceOutput& out) 270 { 271 #if ENABLE_TRACING 272 // to be overridden by subclasses 273 out.Print("ENTRY %p", this); 274 #endif 275 } 276 277 278 void 279 TraceEntry::Initialized() 280 { 281 #if ENABLE_TRACING 282 flags |= ENTRY_INITIALIZED; 283 sWritten++; 284 #endif 285 } 286 287 288 void* 289 TraceEntry::operator new(size_t size, const std::nothrow_t&) throw() 290 { 291 #if ENABLE_TRACING 292 return allocate_entry(size, 0); 293 #endif 294 return NULL; 295 } 296 297 298 // #pragma mark - 299 300 301 AbstractTraceEntry::AbstractTraceEntry() 302 { 303 struct thread* thread = thread_get_current_thread(); 304 if (thread != NULL) { 305 fThread = thread->id; 306 if (thread->team) 307 fTeam = thread->team->id; 308 } 309 fTime = system_time(); 310 } 311 312 AbstractTraceEntry::~AbstractTraceEntry() 313 { 314 } 315 316 317 void 318 AbstractTraceEntry::Dump(TraceOutput& out) 319 { 320 bigtime_t time = (out.Flags() & TRACE_OUTPUT_DIFF_TIME) 321 ? fTime - out.LastEntryTime() 322 : fTime; 323 324 if (out.Flags() & TRACE_OUTPUT_TEAM_ID) 325 out.Print("[%6ld:%6ld] %10Ld: ", fThread, fTeam, time); 326 else 327 out.Print("[%6ld] %10Ld: ", fThread, time); 328 329 AddDump(out); 330 331 out.SetLastEntryTime(fTime); 332 } 333 334 335 void 336 AbstractTraceEntry::AddDump(TraceOutput& out) 337 { 338 } 339 340 341 // #pragma mark - 342 343 344 #if ENABLE_TRACING 345 346 class KernelTraceEntry : public AbstractTraceEntry { 347 public: 348 KernelTraceEntry(const char* message) 349 { 350 fMessage = alloc_tracing_buffer_strcpy(message, 256, false); 351 352 Initialized(); 353 } 354 355 virtual void AddDump(TraceOutput& out) 356 { 357 out.Print("kern: %s", fMessage); 358 } 359 360 private: 361 char* fMessage; 362 }; 363 364 365 class UserTraceEntry : public AbstractTraceEntry { 366 public: 367 UserTraceEntry(const char* message) 368 { 369 fMessage = alloc_tracing_buffer_strcpy(message, 256, true); 370 371 Initialized(); 372 } 373 374 virtual void AddDump(TraceOutput& out) 375 { 376 out.Print("user: %s", fMessage); 377 } 378 379 private: 380 char* fMessage; 381 }; 382 383 #endif // ENABLE_TRACING 384 385 386 // #pragma mark - trace filters 387 388 389 class LazyTraceOutput : public TraceOutput { 390 public: 391 LazyTraceOutput(char* buffer, size_t bufferSize, uint32 flags) 392 : TraceOutput(buffer, bufferSize, flags) 393 { 394 } 395 396 const char* DumpEntry(const TraceEntry* entry) 397 { 398 if (Size() == 0) { 399 const_cast<TraceEntry*>(entry)->Dump(*this); 400 // Dump() should probably be const 401 } 402 403 return Buffer(); 404 } 405 }; 406 407 408 class TraceFilter { 409 public: 410 virtual ~TraceFilter() 411 { 412 } 413 414 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 415 { 416 return false; 417 } 418 419 public: 420 union { 421 thread_id fThread; 422 team_id fTeam; 423 const char* fString; 424 uint64 fValue; 425 struct { 426 TraceFilter* first; 427 TraceFilter* second; 428 } fSubFilters; 429 }; 430 }; 431 432 433 class ThreadTraceFilter : public TraceFilter { 434 public: 435 virtual bool Filter(const TraceEntry* _entry, LazyTraceOutput& out) 436 { 437 const AbstractTraceEntry* entry 438 = dynamic_cast<const AbstractTraceEntry*>(_entry); 439 return (entry != NULL && entry->Thread() == fThread); 440 } 441 }; 442 443 444 class TeamTraceFilter : public TraceFilter { 445 public: 446 virtual bool Filter(const TraceEntry* _entry, LazyTraceOutput& out) 447 { 448 const AbstractTraceEntry* entry 449 = dynamic_cast<const AbstractTraceEntry*>(_entry); 450 return (entry != NULL && entry->Team() == fTeam); 451 } 452 }; 453 454 455 class PatternTraceFilter : public TraceFilter { 456 public: 457 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 458 { 459 return strstr(out.DumpEntry(entry), fString) != NULL; 460 } 461 }; 462 463 464 class DecimalPatternTraceFilter : public TraceFilter { 465 public: 466 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 467 { 468 // TODO: this is *very* slow 469 char buffer[64]; 470 snprintf(buffer, sizeof(buffer), "%Ld", fValue); 471 return strstr(out.DumpEntry(entry), buffer) != NULL; 472 } 473 }; 474 475 class HexPatternTraceFilter : public TraceFilter { 476 public: 477 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 478 { 479 // TODO: this is *very* slow 480 char buffer[64]; 481 snprintf(buffer, sizeof(buffer), "%Lx", fValue); 482 return strstr(out.DumpEntry(entry), buffer) != NULL; 483 } 484 }; 485 486 class StringPatternTraceFilter : public TraceFilter { 487 public: 488 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 489 { 490 if (IS_KERNEL_ADDRESS(fValue)) 491 return strstr(out.DumpEntry(entry), (const char*)fValue) != NULL; 492 493 // TODO: this is *very* slow 494 char buffer[64]; 495 user_strlcpy(buffer, (const char*)fValue, sizeof(buffer)); 496 return strstr(out.DumpEntry(entry), buffer) != NULL; 497 } 498 }; 499 500 class NotTraceFilter : public TraceFilter { 501 public: 502 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 503 { 504 return !fSubFilters.first->Filter(entry, out); 505 } 506 }; 507 508 509 class AndTraceFilter : public TraceFilter { 510 public: 511 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 512 { 513 return fSubFilters.first->Filter(entry, out) 514 && fSubFilters.second->Filter(entry, out); 515 } 516 }; 517 518 519 class OrTraceFilter : public TraceFilter { 520 public: 521 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 522 { 523 return fSubFilters.first->Filter(entry, out) 524 || fSubFilters.second->Filter(entry, out); 525 } 526 }; 527 528 529 class TraceFilterParser { 530 public: 531 static TraceFilterParser* Default() 532 { 533 return &sParser; 534 } 535 536 bool Parse(int argc, const char* const* argv) 537 { 538 fTokens = argv; 539 fTokenCount = argc; 540 fTokenIndex = 0; 541 fFilterCount = 0; 542 543 TraceFilter* filter = _ParseExpression(); 544 return fTokenIndex == fTokenCount && filter != NULL; 545 } 546 547 bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 548 { 549 return fFilters[0].Filter(entry, out); 550 } 551 552 private: 553 TraceFilter* _ParseExpression() 554 { 555 const char* token = _NextToken(); 556 if (!token) { 557 // unexpected end of expression 558 return NULL; 559 } 560 561 if (fFilterCount == MAX_FILTERS) { 562 // too many filters 563 return NULL; 564 } 565 566 if (token[0] == '#') { 567 TraceFilter* filter = new(&fFilters[fFilterCount++]) 568 PatternTraceFilter; 569 filter->fString = token + 1; 570 return filter; 571 } else if (token[0] == 'd' && token[1] == '#') { 572 TraceFilter* filter = new(&fFilters[fFilterCount++]) 573 DecimalPatternTraceFilter; 574 filter->fValue = parse_expression(token + 2); 575 return filter; 576 } else if (token[0] == 'x' && token[1] == '#') { 577 TraceFilter* filter = new(&fFilters[fFilterCount++]) 578 HexPatternTraceFilter; 579 filter->fValue = parse_expression(token + 2); 580 return filter; 581 } else if (token[0] == 's' && token[1] == '#') { 582 TraceFilter* filter = new(&fFilters[fFilterCount++]) 583 StringPatternTraceFilter; 584 filter->fValue = parse_expression(token + 2); 585 return filter; 586 } else if (strcmp(token, "not") == 0) { 587 TraceFilter* filter = new(&fFilters[fFilterCount++]) NotTraceFilter; 588 if ((filter->fSubFilters.first = _ParseExpression()) != NULL) 589 return filter; 590 return NULL; 591 } else if (strcmp(token, "and") == 0) { 592 TraceFilter* filter = new(&fFilters[fFilterCount++]) AndTraceFilter; 593 if ((filter->fSubFilters.first = _ParseExpression()) != NULL 594 && (filter->fSubFilters.second = _ParseExpression()) != NULL) { 595 return filter; 596 } 597 return NULL; 598 } else if (strcmp(token, "or") == 0) { 599 TraceFilter* filter = new(&fFilters[fFilterCount++]) OrTraceFilter; 600 if ((filter->fSubFilters.first = _ParseExpression()) != NULL 601 && (filter->fSubFilters.second = _ParseExpression()) != NULL) { 602 return filter; 603 } 604 return NULL; 605 } else if (strcmp(token, "thread") == 0) { 606 const char* arg = _NextToken(); 607 if (arg == NULL) { 608 // unexpected end of expression 609 return NULL; 610 } 611 612 TraceFilter* filter = new(&fFilters[fFilterCount++]) 613 ThreadTraceFilter; 614 filter->fThread = strtol(arg, NULL, 0); 615 return filter; 616 } else if (strcmp(token, "team") == 0) { 617 const char* arg = _NextToken(); 618 if (arg == NULL) { 619 // unexpected end of expression 620 return NULL; 621 } 622 623 TraceFilter* filter = new(&fFilters[fFilterCount++]) 624 TeamTraceFilter; 625 filter->fTeam = strtol(arg, NULL, 0); 626 return filter; 627 } else { 628 // invalid token 629 return NULL; 630 } 631 } 632 633 const char* _CurrentToken() const 634 { 635 if (fTokenIndex >= 1 && fTokenIndex <= fTokenCount) 636 return fTokens[fTokenIndex - 1]; 637 return NULL; 638 } 639 640 const char* _NextToken() 641 { 642 if (fTokenIndex >= fTokenCount) 643 return NULL; 644 return fTokens[fTokenIndex++]; 645 } 646 647 private: 648 enum { MAX_FILTERS = 32 }; 649 650 const char* const* fTokens; 651 int fTokenCount; 652 int fTokenIndex; 653 TraceFilter fFilters[MAX_FILTERS]; 654 int fFilterCount; 655 656 static TraceFilterParser sParser; 657 }; 658 659 660 TraceFilterParser TraceFilterParser::sParser; 661 662 663 // #pragma mark - 664 665 666 #if ENABLE_TRACING 667 668 669 class TraceEntryIterator { 670 public: 671 TraceEntryIterator() 672 : 673 fEntry(NULL), 674 fIndex(0) 675 { 676 } 677 678 void Reset() 679 { 680 fEntry = NULL; 681 fIndex = 0; 682 } 683 684 int32 Index() const 685 { 686 return fIndex; 687 } 688 689 TraceEntry* Current() const 690 { 691 return (TraceEntry*)fEntry; 692 } 693 694 TraceEntry* Next() 695 { 696 if (fIndex == 0) { 697 fEntry = _NextNonBufferEntry(sFirstEntry); 698 fIndex = 1; 699 } else if (fEntry != NULL) { 700 fEntry = _NextNonBufferEntry(next_entry(fEntry)); 701 fIndex++; 702 } 703 704 return Current(); 705 } 706 707 TraceEntry* Previous() 708 { 709 if (fIndex == (int32)sEntries + 1) 710 fEntry = sAfterLastEntry; 711 712 if (fEntry != NULL) { 713 fEntry = _PreviousNonBufferEntry(previous_entry(fEntry)); 714 fIndex--; 715 } 716 717 return Current(); 718 } 719 720 TraceEntry* MoveTo(int32 index) 721 { 722 if (index == fIndex) 723 return Current(); 724 725 if (index <= 0 || index > (int32)sEntries) { 726 fIndex = (index <= 0 ? 0 : sEntries + 1); 727 fEntry = NULL; 728 return NULL; 729 } 730 731 // get the shortest iteration path 732 int32 distance = index - fIndex; 733 int32 direction = distance < 0 ? -1 : 1; 734 distance *= direction; 735 736 if (index < distance) { 737 distance = index; 738 direction = 1; 739 fEntry = NULL; 740 fIndex = 0; 741 } 742 if ((int32)sEntries + 1 - fIndex < distance) { 743 distance = sEntries + 1 - fIndex; 744 direction = -1; 745 fEntry = NULL; 746 fIndex = sEntries + 1; 747 } 748 749 // iterate to the index 750 if (direction < 0) { 751 while (fIndex != index) 752 Previous(); 753 } else { 754 while (fIndex != index) 755 Next(); 756 } 757 758 return Current(); 759 } 760 761 private: 762 trace_entry* _NextNonBufferEntry(trace_entry* entry) 763 { 764 while (entry != NULL && (entry->flags & BUFFER_ENTRY) != 0) 765 entry = next_entry(entry); 766 767 return entry; 768 } 769 770 trace_entry* _PreviousNonBufferEntry(trace_entry* entry) 771 { 772 while (entry != NULL && (entry->flags & BUFFER_ENTRY) != 0) 773 entry = previous_entry(entry); 774 775 return entry; 776 } 777 778 private: 779 trace_entry* fEntry; 780 int32 fIndex; 781 }; 782 783 784 int 785 dump_tracing(int argc, char** argv) 786 { 787 int argi = 1; 788 789 // variables in which we store our state to be continuable 790 static int32 _previousCount = 0; 791 static bool _previousHasFilter = false; 792 static int32 _previousMaxToCheck = 0; 793 static int32 _previousFirstChecked = 1; 794 static int32 _previousLastChecked = -1; 795 static int32 _previousDirection = 1; 796 static uint32 _previousWritten = 0; 797 static uint32 _previousEntries = 0; 798 static uint32 _previousOutputFlags = 0; 799 static TraceEntryIterator iterator; 800 801 802 // Note: start and index are Pascal-like indices (i.e. in [1, sEntries]). 803 int32 start = 0; // special index: print the last count entries 804 int32 count = 0; 805 int32 maxToCheck = 0; 806 int32 cont = 0; 807 808 bool hasFilter = false; 809 810 uint32 outputFlags = 0; 811 while (argi < argc) { 812 if (strcmp(argv[argi], "--printteam") == 0) { 813 outputFlags |= TRACE_OUTPUT_TEAM_ID; 814 argi++; 815 } else if (strcmp(argv[argi], "--difftime") == 0) { 816 outputFlags |= TRACE_OUTPUT_DIFF_TIME; 817 argi++; 818 } else 819 break; 820 } 821 822 if (argi < argc) { 823 if (strcmp(argv[argi], "forward") == 0) { 824 cont = 1; 825 argi++; 826 } else if (strcmp(argv[argi], "backward") == 0) { 827 cont = -1; 828 argi++; 829 } 830 } else 831 cont = _previousDirection; 832 833 if (cont != 0) { 834 if (argi < argc) { 835 print_debugger_command_usage(argv[0]); 836 return 0; 837 } 838 if (sWritten == 0 || sWritten != _previousWritten 839 || sEntries != _previousEntries) { 840 kprintf("Can't continue iteration. \"%s\" has not been invoked " 841 "before, or there were new entries written since the last " 842 "invocation.\n", argv[0]); 843 return 0; 844 } 845 } 846 847 // get start, count, maxToCheck 848 int32* params[3] = { &start, &count, &maxToCheck }; 849 for (int i = 0; i < 3 && !hasFilter && argi < argc; i++) { 850 if (strcmp(argv[argi], "filter") == 0) { 851 hasFilter = true; 852 argi++; 853 } else if (argv[argi][0] == '#') { 854 hasFilter = true; 855 } else { 856 *params[i] = parse_expression(argv[argi]); 857 argi++; 858 } 859 } 860 861 // filter specification 862 if (argi < argc) { 863 hasFilter = true; 864 if (strcmp(argv[argi], "filter") == 0) 865 argi++; 866 867 if (!TraceFilterParser::Default()->Parse(argc - argi, argv + argi)) { 868 print_debugger_command_usage(argv[0]); 869 return 0; 870 } 871 } 872 873 int32 direction; 874 int32 firstToCheck; 875 int32 lastToCheck; 876 877 if (cont != 0) { 878 // get values from the previous iteration 879 direction = cont; 880 count = _previousCount; 881 maxToCheck = _previousMaxToCheck; 882 hasFilter = _previousHasFilter; 883 outputFlags = _previousOutputFlags; 884 885 if (direction < 0) 886 start = _previousFirstChecked - 1; 887 else 888 start = _previousLastChecked + 1; 889 } else { 890 // defaults for count and maxToCheck 891 if (count == 0) 892 count = 30; 893 if (maxToCheck == 0 || !hasFilter) 894 maxToCheck = count; 895 else if (maxToCheck < 0) 896 maxToCheck = sEntries; 897 898 // determine iteration direction 899 direction = (start <= 0 || count < 0 ? -1 : 1); 900 901 // validate count and maxToCheck 902 if (count < 0) 903 count = -count; 904 if (maxToCheck < 0) 905 maxToCheck = -maxToCheck; 906 if (maxToCheck > (int32)sEntries) 907 maxToCheck = sEntries; 908 if (count > maxToCheck) 909 count = maxToCheck; 910 911 // validate start 912 if (start <= 0 || start > (int32)sEntries) 913 start = max_c(1, sEntries); 914 } 915 916 if (direction < 0) { 917 firstToCheck = max_c(1, start - maxToCheck + 1); 918 lastToCheck = start; 919 } else { 920 firstToCheck = start; 921 lastToCheck = min_c((int32)sEntries, start + maxToCheck - 1); 922 } 923 924 // reset the iterator, if something changed in the meantime 925 if (sWritten == 0 || sWritten != _previousWritten 926 || sEntries != _previousEntries) { 927 iterator.Reset(); 928 } 929 930 char buffer[256]; 931 LazyTraceOutput out(buffer, sizeof(buffer), outputFlags); 932 933 bool markedMatching = false; 934 int32 firstToDump = firstToCheck; 935 int32 lastToDump = lastToCheck; 936 937 if (direction < 0 && hasFilter && lastToCheck - firstToCheck >= count) { 938 // iteration direction is backwards 939 markedMatching = true; 940 941 // From the last entry to check iterate backwards to check filter 942 // matches. 943 int32 matching = 0; 944 945 // move to the entry after the last entry to check 946 iterator.MoveTo(lastToCheck + 1); 947 948 // iterate backwards 949 firstToDump = -1; 950 lastToDump = -1; 951 while (iterator.Index() > firstToCheck) { 952 TraceEntry* entry = iterator.Previous(); 953 if ((entry->flags & ENTRY_INITIALIZED) != 0) { 954 out.Clear(); 955 if (TraceFilterParser::Default()->Filter(entry, out)) { 956 entry->flags |= FILTER_MATCH; 957 if (lastToDump == -1) 958 lastToDump = iterator.Index(); 959 firstToDump = iterator.Index(); 960 961 matching++; 962 if (matching >= count) 963 break; 964 } else 965 entry->flags &= ~FILTER_MATCH; 966 } 967 } 968 969 firstToCheck = iterator.Index(); 970 971 // iterate to the previous entry, so that the next loop starts at the 972 // right one 973 iterator.Previous(); 974 } 975 976 out.SetLastEntryTime(0); 977 978 // set the iterator to the entry before the first one to dump 979 iterator.MoveTo(firstToDump - 1); 980 981 // dump the entries matching the filter in the range 982 // [firstToDump, lastToDump] 983 int32 dumped = 0; 984 985 while (TraceEntry* entry = iterator.Next()) { 986 int32 index = iterator.Index(); 987 if (index < firstToDump) 988 continue; 989 if (index > lastToDump || dumped >= count) { 990 if (direction > 0) 991 lastToCheck = index - 1; 992 break; 993 } 994 995 if ((entry->flags & ENTRY_INITIALIZED) != 0) { 996 out.Clear(); 997 if (hasFilter && (markedMatching 998 ? (entry->flags & FILTER_MATCH) == 0 999 : !TraceFilterParser::Default()->Filter(entry, out))) { 1000 continue; 1001 } 1002 1003 kprintf("%5ld. %s\n", index, out.DumpEntry(entry)); 1004 } else if (!hasFilter) 1005 kprintf("%5ld. ** uninitialized entry **\n", index); 1006 1007 dumped++; 1008 } 1009 1010 kprintf("printed %ld entries within range %ld to %ld (%ld of %ld total, " 1011 "%ld ever)\n", dumped, firstToCheck, lastToCheck, 1012 lastToCheck - firstToCheck + 1, sEntries, sWritten); 1013 1014 // store iteration state 1015 _previousCount = count; 1016 _previousMaxToCheck = maxToCheck; 1017 _previousHasFilter = hasFilter; 1018 _previousFirstChecked = firstToCheck; 1019 _previousLastChecked = lastToCheck; 1020 _previousDirection = direction; 1021 _previousWritten = sWritten; 1022 _previousEntries = sEntries; 1023 _previousOutputFlags = outputFlags; 1024 1025 return cont != 0 ? B_KDEBUG_CONT : 0; 1026 } 1027 1028 1029 #endif // ENABLE_TRACING 1030 1031 1032 extern "C" uint8* 1033 alloc_tracing_buffer(size_t size) 1034 { 1035 #if ENABLE_TRACING 1036 trace_entry* entry = allocate_entry(size + sizeof(trace_entry), 1037 BUFFER_ENTRY); 1038 if (entry == NULL) 1039 return NULL; 1040 1041 return (uint8*)(entry + 1); 1042 #else 1043 return NULL; 1044 #endif 1045 } 1046 1047 1048 uint8* 1049 alloc_tracing_buffer_memcpy(const void* source, size_t size, bool user) 1050 { 1051 if (user && !IS_USER_ADDRESS(source)) 1052 return NULL; 1053 1054 uint8* buffer = alloc_tracing_buffer(size); 1055 if (buffer == NULL) 1056 return NULL; 1057 1058 if (user) { 1059 if (user_memcpy(buffer, source, size) != B_OK) 1060 return NULL; 1061 } else 1062 memcpy(buffer, source, size); 1063 1064 return buffer; 1065 } 1066 1067 1068 char* 1069 alloc_tracing_buffer_strcpy(const char* source, size_t maxSize, bool user) 1070 { 1071 if (source == NULL || maxSize == 0) 1072 return NULL; 1073 1074 if (user && !IS_USER_ADDRESS(source)) 1075 return NULL; 1076 1077 // limit maxSize to the actual source string len 1078 if (user) { 1079 ssize_t size = user_strlcpy(NULL, source, 0); 1080 // there's no user_strnlen() 1081 if (size < 0) 1082 return 0; 1083 maxSize = min_c(maxSize, (size_t)size + 1); 1084 } else 1085 maxSize = strnlen(source, maxSize - 1) + 1; 1086 1087 char* buffer = (char*)alloc_tracing_buffer(maxSize); 1088 if (buffer == NULL) 1089 return NULL; 1090 1091 if (user) { 1092 if (user_strlcpy(buffer, source, maxSize) < B_OK) 1093 return NULL; 1094 } else 1095 strlcpy(buffer, source, maxSize); 1096 1097 return buffer; 1098 } 1099 1100 1101 extern "C" status_t 1102 tracing_init(void) 1103 { 1104 #if ENABLE_TRACING 1105 area_id area = create_area("tracing log", (void**)&sBuffer, 1106 B_ANY_KERNEL_ADDRESS, MAX_TRACE_SIZE, B_FULL_LOCK, 1107 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 1108 if (area < B_OK) 1109 return area; 1110 1111 sFirstEntry = sBuffer; 1112 sAfterLastEntry = sBuffer; 1113 1114 add_debugger_command_etc("traced", &dump_tracing, 1115 "Dump recorded trace entries", 1116 "[ \"--printteam\" ] [ \"--difftime\" ] (\"forward\" | \"backward\") " 1117 "| ([ <start> [ <count> [ <range> ] ] ] " 1118 "[ #<pattern> | (\"filter\" <filter>) ])\n" 1119 "Prints recorded trace entries. If \"backward\" or \"forward\" is\n" 1120 "specified, the command continues where the previous invocation left\n" 1121 "off, i.e. printing the previous respectively next entries (as many\n" 1122 "as printed before). In this case the command is continuable, that is\n" 1123 "afterwards entering an empty line in the debugger will reinvoke it.\n" 1124 "If no arguments are given, the command continues in the direction\n" 1125 "of the last invocation.\n" 1126 "\"--printteam\" enables printing the entries' team IDs.\n" 1127 "\"--difftime\" print difference times for all but the first entry.\n" 1128 " <start> - The base index of the entries to print. Depending on\n" 1129 " whether the iteration direction is forward or\n" 1130 " backward this will be the first or last entry printed\n" 1131 " (potentially, if a filter is specified). The index of\n" 1132 " the first entry in the trace buffer is 1. If 0 is\n" 1133 " specified, the last <count> recorded entries are\n" 1134 " printed (iteration direction is backward). Defaults \n" 1135 " to 0.\n" 1136 " <count> - The number of entries to be printed. Defaults to 30.\n" 1137 " If negative, the -<count> entries before and\n" 1138 " including <start> will be printed.\n" 1139 " <range> - Only relevant if a filter is specified. Specifies the\n" 1140 " number of entries to be filtered -- depending on the\n" 1141 " iteration direction the entries before or after\n" 1142 " <start>. If more than <count> entries match the\n" 1143 " filter, only the first (forward) or last (backward)\n" 1144 " <count> matching entries will be printed. If 0 is\n" 1145 " specified <range> will be set to <count>. If -1,\n" 1146 " <range> will be set to the number of recorded\n" 1147 " entries.\n" 1148 " <pattern> - If specified only entries containing this string are\n" 1149 " printed.\n" 1150 " <filter> - If specified only entries matching this filter\n" 1151 " expression are printed. The expression can consist of\n" 1152 " prefix operators \"not\", \"and\", \"or\", and\n" 1153 " filters \"'thread' <thread>\" (matching entries\n" 1154 " with the given thread ID), \"'team' <team>\"\n" 1155 "(matching entries with the given team ID), and\n" 1156 " \"#<pattern>\" (matching entries containing the given\n" 1157 " string).\n", 0); 1158 #endif // ENABLE_TRACING 1159 return B_OK; 1160 } 1161 1162 1163 void 1164 ktrace_printf(const char *format, ...) 1165 { 1166 #if ENABLE_TRACING 1167 va_list list; 1168 va_start(list, format); 1169 1170 char buffer[256]; 1171 vsnprintf(buffer, sizeof(buffer), format, list); 1172 1173 va_end(list); 1174 1175 new(nothrow) KernelTraceEntry(buffer); 1176 #endif // ENABLE_TRACING 1177 } 1178 1179 1180 void 1181 _user_ktrace_output(const char *message) 1182 { 1183 #if ENABLE_TRACING 1184 new(nothrow) UserTraceEntry(message); 1185 #endif // ENABLE_TRACING 1186 } 1187 1188