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