xref: /haiku/src/system/kernel/debug/tracing.cpp (revision ed6250c95736c0b55da79d6e9dd01369532260c0)
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