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 (sBuffer == 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 #else 294 return NULL; 295 #endif 296 } 297 298 299 // #pragma mark - 300 301 302 AbstractTraceEntry::AbstractTraceEntry() 303 : 304 fThread(thread_get_current_thread_id()), 305 fTeam(team_get_current_team_id()), 306 fTime(system_time()) 307 { 308 } 309 310 AbstractTraceEntry::~AbstractTraceEntry() 311 { 312 } 313 314 315 void 316 AbstractTraceEntry::Dump(TraceOutput& out) 317 { 318 bigtime_t time = (out.Flags() & TRACE_OUTPUT_DIFF_TIME) 319 ? fTime - out.LastEntryTime() 320 : fTime; 321 322 if (out.Flags() & TRACE_OUTPUT_TEAM_ID) 323 out.Print("[%6ld:%6ld] %10Ld: ", fThread, fTeam, time); 324 else 325 out.Print("[%6ld] %10Ld: ", fThread, time); 326 327 AddDump(out); 328 329 out.SetLastEntryTime(fTime); 330 } 331 332 333 void 334 AbstractTraceEntry::AddDump(TraceOutput& out) 335 { 336 } 337 338 339 // #pragma mark - 340 341 342 #if ENABLE_TRACING 343 344 class KernelTraceEntry : public AbstractTraceEntry { 345 public: 346 KernelTraceEntry(const char* message) 347 { 348 fMessage = alloc_tracing_buffer_strcpy(message, 256, false); 349 350 Initialized(); 351 } 352 353 virtual void AddDump(TraceOutput& out) 354 { 355 out.Print("kern: %s", fMessage); 356 } 357 358 private: 359 char* fMessage; 360 }; 361 362 363 class UserTraceEntry : public AbstractTraceEntry { 364 public: 365 UserTraceEntry(const char* message) 366 { 367 fMessage = alloc_tracing_buffer_strcpy(message, 256, true); 368 369 Initialized(); 370 } 371 372 virtual void AddDump(TraceOutput& out) 373 { 374 out.Print("user: %s", fMessage); 375 } 376 377 private: 378 char* fMessage; 379 }; 380 381 #endif // ENABLE_TRACING 382 383 384 // #pragma mark - trace filters 385 386 387 class LazyTraceOutput : public TraceOutput { 388 public: 389 LazyTraceOutput(char* buffer, size_t bufferSize, uint32 flags) 390 : TraceOutput(buffer, bufferSize, flags) 391 { 392 } 393 394 const char* DumpEntry(const TraceEntry* entry) 395 { 396 if (Size() == 0) { 397 const_cast<TraceEntry*>(entry)->Dump(*this); 398 // Dump() should probably be const 399 } 400 401 return Buffer(); 402 } 403 }; 404 405 406 class TraceFilter { 407 public: 408 virtual ~TraceFilter() 409 { 410 } 411 412 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 413 { 414 return false; 415 } 416 417 public: 418 union { 419 thread_id fThread; 420 team_id fTeam; 421 const char* fString; 422 struct { 423 TraceFilter* first; 424 TraceFilter* second; 425 } fSubFilters; 426 }; 427 }; 428 429 430 class ThreadTraceFilter : public TraceFilter { 431 public: 432 virtual bool Filter(const TraceEntry* _entry, LazyTraceOutput& out) 433 { 434 const AbstractTraceEntry* entry 435 = dynamic_cast<const AbstractTraceEntry*>(_entry); 436 return (entry != NULL && entry->Thread() == fThread); 437 } 438 }; 439 440 441 class TeamTraceFilter : public TraceFilter { 442 public: 443 virtual bool Filter(const TraceEntry* _entry, LazyTraceOutput& out) 444 { 445 const AbstractTraceEntry* entry 446 = dynamic_cast<const AbstractTraceEntry*>(_entry); 447 return (entry != NULL && entry->Team() == fTeam); 448 } 449 }; 450 451 452 class PatternTraceFilter : public TraceFilter { 453 public: 454 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 455 { 456 return strstr(out.DumpEntry(entry), fString) != NULL; 457 } 458 }; 459 460 461 class NotTraceFilter : public TraceFilter { 462 public: 463 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 464 { 465 return !fSubFilters.first->Filter(entry, out); 466 } 467 }; 468 469 470 class AndTraceFilter : public TraceFilter { 471 public: 472 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 473 { 474 return fSubFilters.first->Filter(entry, out) 475 && fSubFilters.second->Filter(entry, out); 476 } 477 }; 478 479 480 class OrTraceFilter : public TraceFilter { 481 public: 482 virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 483 { 484 return fSubFilters.first->Filter(entry, out) 485 || fSubFilters.second->Filter(entry, out); 486 } 487 }; 488 489 490 class TraceFilterParser { 491 public: 492 static TraceFilterParser* Default() 493 { 494 return &sParser; 495 } 496 497 bool Parse(int argc, const char* const* argv) 498 { 499 fTokens = argv; 500 fTokenCount = argc; 501 fTokenIndex = 0; 502 fFilterCount = 0; 503 504 TraceFilter* filter = _ParseExpression(); 505 return fTokenIndex == fTokenCount && filter != NULL; 506 } 507 508 bool Filter(const TraceEntry* entry, LazyTraceOutput& out) 509 { 510 return fFilters[0].Filter(entry, out); 511 } 512 513 private: 514 TraceFilter* _ParseExpression() 515 { 516 const char* token = _NextToken(); 517 if (!token) { 518 // unexpected end of expression 519 return NULL; 520 } 521 522 if (fFilterCount == MAX_FILTERS) { 523 // too many filters 524 return NULL; 525 } 526 527 if (token[0] == '#') { 528 TraceFilter* filter = new(&fFilters[fFilterCount++]) 529 PatternTraceFilter; 530 filter->fString = token + 1; 531 return filter; 532 } else if (strcmp(token, "not") == 0) { 533 TraceFilter* filter = new(&fFilters[fFilterCount++]) NotTraceFilter; 534 if ((filter->fSubFilters.first = _ParseExpression()) != NULL) 535 return filter; 536 return NULL; 537 } else if (strcmp(token, "and") == 0) { 538 TraceFilter* filter = new(&fFilters[fFilterCount++]) AndTraceFilter; 539 if ((filter->fSubFilters.first = _ParseExpression()) != NULL 540 && (filter->fSubFilters.second = _ParseExpression()) != NULL) { 541 return filter; 542 } 543 return NULL; 544 } else if (strcmp(token, "or") == 0) { 545 TraceFilter* filter = new(&fFilters[fFilterCount++]) OrTraceFilter; 546 if ((filter->fSubFilters.first = _ParseExpression()) != NULL 547 && (filter->fSubFilters.second = _ParseExpression()) != NULL) { 548 return filter; 549 } 550 return NULL; 551 } else if (strcmp(token, "thread") == 0) { 552 const char* arg = _NextToken(); 553 if (arg == NULL) { 554 // unexpected end of expression 555 return NULL; 556 } 557 558 TraceFilter* filter = new(&fFilters[fFilterCount++]) 559 ThreadTraceFilter; 560 filter->fThread = strtol(arg, NULL, 0); 561 return filter; 562 } else if (strcmp(token, "team") == 0) { 563 const char* arg = _NextToken(); 564 if (arg == NULL) { 565 // unexpected end of expression 566 return NULL; 567 } 568 569 TraceFilter* filter = new(&fFilters[fFilterCount++]) 570 TeamTraceFilter; 571 filter->fTeam = strtol(arg, NULL, 0); 572 return filter; 573 } else { 574 // invalid token 575 return NULL; 576 } 577 } 578 579 const char* _CurrentToken() const 580 { 581 if (fTokenIndex >= 1 && fTokenIndex <= fTokenCount) 582 return fTokens[fTokenIndex - 1]; 583 return NULL; 584 } 585 586 const char* _NextToken() 587 { 588 if (fTokenIndex >= fTokenCount) 589 return NULL; 590 return fTokens[fTokenIndex++]; 591 } 592 593 private: 594 enum { MAX_FILTERS = 32 }; 595 596 const char* const* fTokens; 597 int fTokenCount; 598 int fTokenIndex; 599 TraceFilter fFilters[MAX_FILTERS]; 600 int fFilterCount; 601 602 static TraceFilterParser sParser; 603 }; 604 605 606 TraceFilterParser TraceFilterParser::sParser; 607 608 609 // #pragma mark - 610 611 612 #if ENABLE_TRACING 613 614 615 class TraceEntryIterator { 616 public: 617 TraceEntryIterator() 618 : 619 fEntry(NULL), 620 fIndex(0) 621 { 622 } 623 624 void Reset() 625 { 626 fEntry = NULL; 627 fIndex = 0; 628 } 629 630 int32 Index() const 631 { 632 return fIndex; 633 } 634 635 TraceEntry* Current() const 636 { 637 return (TraceEntry*)fEntry; 638 } 639 640 TraceEntry* Next() 641 { 642 if (fIndex == 0) { 643 fEntry = _NextNonBufferEntry(sFirstEntry); 644 fIndex = 1; 645 } else if (fEntry != NULL) { 646 fEntry = _NextNonBufferEntry(next_entry(fEntry)); 647 fIndex++; 648 } 649 650 return Current(); 651 } 652 653 TraceEntry* Previous() 654 { 655 if (fIndex == (int32)sEntries + 1) 656 fEntry = sAfterLastEntry; 657 658 if (fEntry != NULL) { 659 fEntry = _PreviousNonBufferEntry(previous_entry(fEntry)); 660 fIndex--; 661 } 662 663 return Current(); 664 } 665 666 TraceEntry* MoveTo(int32 index) 667 { 668 if (index == fIndex) 669 return Current(); 670 671 if (index <= 0 || index > (int32)sEntries) { 672 fIndex = (index <= 0 ? 0 : sEntries + 1); 673 fEntry = NULL; 674 return NULL; 675 } 676 677 // get the shortest iteration path 678 int32 distance = index - fIndex; 679 int32 direction = distance < 0 ? -1 : 1; 680 distance *= direction; 681 682 if (index < distance) { 683 distance = index; 684 direction = 1; 685 fEntry = NULL; 686 fIndex = 0; 687 } 688 if ((int32)sEntries + 1 - fIndex < distance) { 689 distance = sEntries + 1 - fIndex; 690 direction = -1; 691 fEntry = NULL; 692 fIndex = sEntries + 1; 693 } 694 695 // iterate to the index 696 if (direction < 0) { 697 while (fIndex != index) 698 Previous(); 699 } else { 700 while (fIndex != index) 701 Next(); 702 } 703 704 return Current(); 705 } 706 707 private: 708 trace_entry* _NextNonBufferEntry(trace_entry* entry) 709 { 710 while (entry != NULL && (entry->flags & BUFFER_ENTRY) != 0) 711 entry = next_entry(entry); 712 713 return entry; 714 } 715 716 trace_entry* _PreviousNonBufferEntry(trace_entry* entry) 717 { 718 while (entry != NULL && (entry->flags & BUFFER_ENTRY) != 0) 719 entry = previous_entry(entry); 720 721 return entry; 722 } 723 724 private: 725 trace_entry* fEntry; 726 int32 fIndex; 727 }; 728 729 730 int 731 dump_tracing(int argc, char** argv) 732 { 733 int argi = 1; 734 735 // variables in which we store our state to be continuable 736 static int32 _previousCount = 0; 737 static bool _previousHasFilter = false; 738 static int32 _previousMaxToCheck = 0; 739 static int32 _previousFirstChecked = 1; 740 static int32 _previousLastChecked = -1; 741 static int32 _previousDirection = 1; 742 static uint32 _previousWritten = 0; 743 static uint32 _previousEntries = 0; 744 static uint32 _previousOutputFlags = 0; 745 static TraceEntryIterator iterator; 746 747 748 // Note: start and index are Pascal-like indices (i.e. in [1, sEntries]). 749 int32 start = 0; // special index: print the last count entries 750 int32 count = 0; 751 int32 maxToCheck = 0; 752 int32 cont = 0; 753 754 bool hasFilter = false; 755 756 uint32 outputFlags = 0; 757 while (argi < argc) { 758 if (strcmp(argv[argi], "--printteam") == 0) { 759 outputFlags |= TRACE_OUTPUT_TEAM_ID; 760 argi++; 761 } else if (strcmp(argv[argi], "--difftime") == 0) { 762 outputFlags |= TRACE_OUTPUT_DIFF_TIME; 763 argi++; 764 } else 765 break; 766 } 767 768 if (argi < argc) { 769 if (strcmp(argv[argi], "forward") == 0) { 770 cont = 1; 771 argi++; 772 } else if (strcmp(argv[argi], "backward") == 0) { 773 cont = -1; 774 argi++; 775 } 776 } else 777 cont = _previousDirection; 778 779 if (cont != 0) { 780 if (argi < argc) { 781 print_debugger_command_usage(argv[0]); 782 return 0; 783 } 784 if (sWritten == 0 || sWritten != _previousWritten 785 || sEntries != _previousEntries) { 786 kprintf("Can't continue iteration. \"%s\" has not been invoked " 787 "before, or there were new entries written since the last " 788 "invocation.\n", argv[0]); 789 return 0; 790 } 791 } 792 793 // get start, count, maxToCheck 794 int32* params[3] = { &start, &count, &maxToCheck }; 795 for (int i = 0; i < 3 && !hasFilter && argi < argc; i++) { 796 if (strcmp(argv[argi], "filter") == 0) { 797 hasFilter = true; 798 argi++; 799 } else if (argv[argi][0] == '#') { 800 hasFilter = true; 801 } else { 802 *params[i] = parse_expression(argv[argi]); 803 argi++; 804 } 805 } 806 807 // filter specification 808 if (argi < argc) { 809 hasFilter = true; 810 if (strcmp(argv[argi], "filter") == 0) 811 argi++; 812 813 if (!TraceFilterParser::Default()->Parse(argc - argi, argv + argi)) { 814 print_debugger_command_usage(argv[0]); 815 return 0; 816 } 817 } 818 819 int32 direction; 820 int32 firstToCheck; 821 int32 lastToCheck; 822 823 if (cont != 0) { 824 // get values from the previous iteration 825 direction = cont; 826 count = _previousCount; 827 maxToCheck = _previousMaxToCheck; 828 hasFilter = _previousHasFilter; 829 outputFlags = _previousOutputFlags; 830 831 if (direction < 0) 832 start = _previousFirstChecked - 1; 833 else 834 start = _previousLastChecked + 1; 835 } else { 836 // defaults for count and maxToCheck 837 if (count == 0) 838 count = 30; 839 if (maxToCheck == 0 || !hasFilter) 840 maxToCheck = count; 841 else if (maxToCheck < 0) 842 maxToCheck = sEntries; 843 844 // determine iteration direction 845 direction = (start <= 0 || count < 0 ? -1 : 1); 846 847 // validate count and maxToCheck 848 if (count < 0) 849 count = -count; 850 if (maxToCheck < 0) 851 maxToCheck = -maxToCheck; 852 if (maxToCheck > (int32)sEntries) 853 maxToCheck = sEntries; 854 if (count > maxToCheck) 855 count = maxToCheck; 856 857 // validate start 858 if (start <= 0 || start > (int32)sEntries) 859 start = max_c(1, sEntries); 860 } 861 862 if (direction < 0) { 863 firstToCheck = max_c(1, start - maxToCheck + 1); 864 lastToCheck = start; 865 } else { 866 firstToCheck = start; 867 lastToCheck = min_c((int32)sEntries, start + maxToCheck - 1); 868 } 869 870 // reset the iterator, if something changed in the meantime 871 if (sWritten == 0 || sWritten != _previousWritten 872 || sEntries != _previousEntries) { 873 iterator.Reset(); 874 } 875 876 char buffer[256]; 877 LazyTraceOutput out(buffer, sizeof(buffer), outputFlags); 878 879 bool markedMatching = false; 880 int32 firstToDump = firstToCheck; 881 int32 lastToDump = lastToCheck; 882 883 if (direction < 0 && hasFilter && lastToCheck - firstToCheck >= count) { 884 // iteration direction is backwards 885 markedMatching = true; 886 887 // From the last entry to check iterate backwards to check filter 888 // matches. 889 int32 matching = 0; 890 891 // move to the entry after the last entry to check 892 iterator.MoveTo(lastToCheck + 1); 893 894 // iterate backwards 895 firstToDump = -1; 896 lastToDump = -1; 897 while (iterator.Index() > firstToCheck) { 898 TraceEntry* entry = iterator.Previous(); 899 if ((entry->flags & ENTRY_INITIALIZED) != 0) { 900 out.Clear(); 901 if (TraceFilterParser::Default()->Filter(entry, out)) { 902 entry->flags |= FILTER_MATCH; 903 if (lastToDump == -1) 904 lastToDump = iterator.Index(); 905 firstToDump = iterator.Index(); 906 907 matching++; 908 if (matching >= count) 909 break; 910 } else 911 entry->flags &= ~FILTER_MATCH; 912 } 913 } 914 915 firstToCheck = iterator.Index(); 916 917 // iterate to the previous entry, so that the next loop starts at the 918 // right one 919 iterator.Previous(); 920 } 921 922 out.SetLastEntryTime(0); 923 924 // set the iterator to the entry before the first one to dump 925 iterator.MoveTo(firstToDump - 1); 926 927 // dump the entries matching the filter in the range 928 // [firstToDump, lastToDump] 929 int32 dumped = 0; 930 931 while (TraceEntry* entry = iterator.Next()) { 932 int32 index = iterator.Index(); 933 if (index < firstToDump) 934 continue; 935 if (index > lastToDump || dumped >= count) { 936 if (direction > 0) 937 lastToCheck = index - 1; 938 break; 939 } 940 941 if ((entry->flags & ENTRY_INITIALIZED) != 0) { 942 out.Clear(); 943 if (hasFilter && (markedMatching 944 ? (entry->flags & FILTER_MATCH) == 0 945 : !TraceFilterParser::Default()->Filter(entry, out))) { 946 continue; 947 } 948 949 kprintf("%5ld. %s\n", index, out.DumpEntry(entry)); 950 } else if (!hasFilter) 951 kprintf("%5ld. ** uninitialized entry **\n", index); 952 953 dumped++; 954 } 955 956 kprintf("printed %ld entries within range %ld to %ld (%ld of %ld total, " 957 "%ld ever)\n", dumped, firstToCheck, lastToCheck, 958 lastToCheck - firstToCheck + 1, sEntries, sWritten); 959 960 // store iteration state 961 _previousCount = count; 962 _previousMaxToCheck = maxToCheck; 963 _previousHasFilter = hasFilter; 964 _previousFirstChecked = firstToCheck; 965 _previousLastChecked = lastToCheck; 966 _previousDirection = direction; 967 _previousWritten = sWritten; 968 _previousEntries = sEntries; 969 _previousOutputFlags = outputFlags; 970 971 return cont != 0 ? B_KDEBUG_CONT : 0; 972 } 973 974 975 #endif // ENABLE_TRACING 976 977 978 extern "C" uint8* 979 alloc_tracing_buffer(size_t size) 980 { 981 #if ENABLE_TRACING 982 trace_entry* entry = allocate_entry(size + sizeof(trace_entry), 983 BUFFER_ENTRY); 984 if (entry == NULL) 985 return NULL; 986 987 return (uint8*)(entry + 1); 988 #else 989 return NULL; 990 #endif 991 } 992 993 994 uint8* 995 alloc_tracing_buffer_memcpy(const void* source, size_t size, bool user) 996 { 997 if (user && !IS_USER_ADDRESS(source)) 998 return NULL; 999 1000 uint8* buffer = alloc_tracing_buffer(size); 1001 if (buffer == NULL) 1002 return NULL; 1003 1004 if (user) { 1005 if (user_memcpy(buffer, source, size) != B_OK) 1006 return NULL; 1007 } else 1008 memcpy(buffer, source, size); 1009 1010 return buffer; 1011 } 1012 1013 1014 char* 1015 alloc_tracing_buffer_strcpy(const char* source, size_t maxSize, bool user) 1016 { 1017 if (source == NULL || maxSize == 0) 1018 return NULL; 1019 1020 if (user && !IS_USER_ADDRESS(source)) 1021 return NULL; 1022 1023 // limit maxSize to the actual source string len 1024 if (user) { 1025 ssize_t size = user_strlcpy(NULL, source, 0); 1026 // there's no user_strnlen() 1027 if (size < 0) 1028 return 0; 1029 maxSize = min_c(maxSize, (size_t)size + 1); 1030 } else 1031 maxSize = strnlen(source, maxSize - 1) + 1; 1032 1033 char* buffer = (char*)alloc_tracing_buffer(maxSize); 1034 if (buffer == NULL) 1035 return NULL; 1036 1037 if (user) { 1038 if (user_strlcpy(buffer, source, maxSize) < B_OK) 1039 return NULL; 1040 } else 1041 strlcpy(buffer, source, maxSize); 1042 1043 return buffer; 1044 } 1045 1046 1047 extern "C" status_t 1048 tracing_init(void) 1049 { 1050 #if ENABLE_TRACING 1051 area_id area = create_area("tracing log", (void**)&sBuffer, 1052 B_ANY_KERNEL_ADDRESS, MAX_TRACE_SIZE, B_FULL_LOCK, 1053 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 1054 if (area < B_OK) 1055 return area; 1056 1057 sFirstEntry = sBuffer; 1058 sAfterLastEntry = sBuffer; 1059 1060 add_debugger_command_etc("traced", &dump_tracing, 1061 "Dump recorded trace entries", 1062 "[ \"--printteam\" ] [ \"--difftime\" ] (\"forward\" | \"backward\") " 1063 "| ([ <start> [ <count> [ <range> ] ] ] " 1064 "[ #<pattern> | (\"filter\" <filter>) ])\n" 1065 "Prints recorded trace entries. If \"backward\" or \"forward\" is\n" 1066 "specified, the command continues where the previous invocation left\n" 1067 "off, i.e. printing the previous respectively next entries (as many\n" 1068 "as printed before). In this case the command is continuable, that is\n" 1069 "afterwards entering an empty line in the debugger will reinvoke it.\n" 1070 "If no arguments are given, the command continues in the direction\n" 1071 "of the last invocation.\n" 1072 "\"--printteam\" enables printing the entries' team IDs.\n" 1073 "\"--difftime\" print difference times for all but the first entry.\n" 1074 " <start> - The base index of the entries to print. Depending on\n" 1075 " whether the iteration direction is forward or\n" 1076 " backward this will be the first or last entry printed\n" 1077 " (potentially, if a filter is specified). The index of\n" 1078 " the first entry in the trace buffer is 1. If 0 is\n" 1079 " specified, the last <count> recorded entries are\n" 1080 " printed (iteration direction is backward). Defaults \n" 1081 " to 0.\n" 1082 " <count> - The number of entries to be printed. Defaults to 30.\n" 1083 " If negative, the -<count> entries before and\n" 1084 " including <start> will be printed.\n" 1085 " <range> - Only relevant if a filter is specified. Specifies the\n" 1086 " number of entries to be filtered -- depending on the\n" 1087 " iteration direction the entries before or after\n" 1088 " <start>. If more than <count> entries match the\n" 1089 " filter, only the first (forward) or last (backward)\n" 1090 " <count> matching entries will be printed. If 0 is\n" 1091 " specified <range> will be set to <count>. If -1,\n" 1092 " <range> will be set to the number of recorded\n" 1093 " entries.\n" 1094 " <pattern> - If specified only entries containing this string are\n" 1095 " printed.\n" 1096 " <filter> - If specified only entries matching this filter\n" 1097 " expression are printed. The expression can consist of\n" 1098 " prefix operators \"not\", \"and\", \"or\", and\n" 1099 " filters \"'thread' <thread>\" (matching entries\n" 1100 " with the given thread ID), \"'team' <team>\"\n" 1101 "(matching entries with the given team ID), and\n" 1102 " \"#<pattern>\" (matching entries containing the given\n" 1103 " string).\n", 0); 1104 #endif // ENABLE_TRACING 1105 return B_OK; 1106 } 1107 1108 1109 void 1110 ktrace_printf(const char *format, ...) 1111 { 1112 #if ENABLE_TRACING 1113 va_list list; 1114 va_start(list, format); 1115 1116 char buffer[256]; 1117 vsnprintf(buffer, sizeof(buffer), format, list); 1118 1119 va_end(list); 1120 1121 new(nothrow) KernelTraceEntry(buffer); 1122 #endif // ENABLE_TRACING 1123 } 1124 1125 1126 void 1127 _user_ktrace_output(const char *message) 1128 { 1129 #if ENABLE_TRACING 1130 new(nothrow) UserTraceEntry(message); 1131 #endif // ENABLE_TRACING 1132 } 1133 1134