Files
haiku-beta6/src/system/kernel/debug/tracing.cpp
T
Axel Dörfler 00e8c084e6 Extremely important cleanup.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30103 a95241bf-73f2-0310-859d-f6bbb57e9c96
2009-04-10 09:53:50 +00:00

1304 lines
29 KiB
C++

/*
* Copyright 2008, Ingo Weinhold, [email protected].
* Copyright 2008-2009, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*/
#include <tracing.h>
#include <stdarg.h>
#include <stdlib.h>
#include <arch/debug.h>
#include <debug.h>
#include <elf.h>
#include <int.h>
#include <kernel.h>
#include <team.h>
#include <thread.h>
#include <util/AutoLock.h>
struct tracing_stack_trace {
int32 depth;
addr_t return_addresses[0];
};
#if ENABLE_TRACING
//#define TRACE_TRACING
#ifdef TRACE_TRACING
# define TRACE(x) dprintf_no_syslog x
#else
# define TRACE(x) ;
#endif
enum {
WRAP_ENTRY = 0x01,
ENTRY_INITIALIZED = 0x02,
BUFFER_ENTRY = 0x04,
FILTER_MATCH = 0x08
};
static const size_t kTraceOutputBufferSize = 10240;
static const size_t kBufferSize = MAX_TRACE_SIZE / 4;
static trace_entry* sBuffer;
static trace_entry* sFirstEntry;
static trace_entry* sAfterLastEntry;
static uint32 sEntries;
static uint32 sWritten;
static spinlock sLock;
static char* sTraceOutputBuffer;
static trace_entry*
next_entry(trace_entry* entry)
{
entry += entry->size;
if ((entry->flags & WRAP_ENTRY) != 0)
entry = sBuffer;
if (entry == sAfterLastEntry)
return NULL;
return entry;
}
static trace_entry*
previous_entry(trace_entry* entry)
{
if (entry == sFirstEntry)
return NULL;
if (entry == sBuffer) {
// beginning of buffer -- previous entry is a wrap entry
entry = sBuffer + kBufferSize - entry->previous_size;
}
return entry - entry->previous_size;
}
static bool
free_first_entry()
{
TRACE((" skip start %p, %lu*4 bytes\n", sFirstEntry, sFirstEntry->size));
trace_entry* newFirst = next_entry(sFirstEntry);
if (sFirstEntry->flags & BUFFER_ENTRY) {
// a buffer entry -- just skip it
} else if (sFirstEntry->flags & ENTRY_INITIALIZED) {
// fully initialized TraceEntry -- destroy it
TraceEntry::FromTraceEntry(sFirstEntry)->~TraceEntry();
sEntries--;
} else {
// Not fully initialized TraceEntry. We can't free it, since
// then it's constructor might still write into the memory and
// overwrite data of the entry we're going to allocate.
// We can't do anything until this entry can be discarded.
return false;
}
if (newFirst == NULL) {
// everything is freed -- that practically this can't happen, if
// the buffer is large enough to hold three max-sized entries
sFirstEntry = sAfterLastEntry = sBuffer;
TRACE(("free_first_entry(): all entries freed!\n"));
} else
sFirstEntry = newFirst;
return true;
}
/*! Makes sure we have needed * 4 bytes of memory at sAfterLastEntry.
Returns \c false, if unable to free that much.
*/
static bool
make_space(size_t needed)
{
// we need space for sAfterLastEntry, too (in case we need to wrap around
// later)
needed++;
// If there's not enough space (free or occupied) after sAfterLastEntry,
// we free all entries in that region and wrap around.
if (sAfterLastEntry + needed > sBuffer + kBufferSize) {
TRACE(("make_space(%lu), wrapping around: after last: %p\n", needed,
sAfterLastEntry));
// Free all entries after sAfterLastEntry and one more at the beginning
// of the buffer.
while (sFirstEntry > sAfterLastEntry) {
if (!free_first_entry())
return false;
}
if (sAfterLastEntry != sBuffer && !free_first_entry())
return false;
// just in case free_first_entry() freed the very last existing entry
if (sAfterLastEntry == sBuffer)
return true;
// mark as wrap entry and actually wrap around
trace_entry* wrapEntry = sAfterLastEntry;
wrapEntry->size = 0;
wrapEntry->flags = WRAP_ENTRY;
sAfterLastEntry = sBuffer;
sAfterLastEntry->previous_size = sBuffer + kBufferSize - wrapEntry;
}
if (sFirstEntry <= sAfterLastEntry) {
// buffer is empty or the space after sAfterLastEntry is unoccupied
return true;
}
// free the first entries, until there's enough space
size_t space = sFirstEntry - sAfterLastEntry;
if (space < needed) {
TRACE(("make_space(%lu), left %ld\n", needed, space));
}
while (space < needed) {
space += sFirstEntry->size;
if (!free_first_entry())
return false;
}
TRACE((" out: start %p, entries %ld\n", sFirstEntry, sEntries));
return true;
}
static trace_entry*
allocate_entry(size_t size, uint16 flags)
{
if (sAfterLastEntry == NULL || size == 0 || size >= 65532)
return NULL;
InterruptsSpinLocker _(sLock);
size = (size + 3) >> 2;
// 4 byte aligned, don't store the lower 2 bits
TRACE(("allocate_entry(%lu), start %p, end %p, buffer %p\n", size * 4,
sFirstEntry, sAfterLastEntry, sBuffer));
if (!make_space(size))
return NULL;
trace_entry* entry = sAfterLastEntry;
entry->size = size;
entry->flags = flags;
sAfterLastEntry += size;
sAfterLastEntry->previous_size = size;
if (!(flags & BUFFER_ENTRY))
sEntries++;
TRACE((" entry: %p, end %p, start %p, entries %ld\n", entry,
sAfterLastEntry, sFirstEntry, sEntries));
return entry;
}
#endif // ENABLE_TRACING
// #pragma mark -
TraceOutput::TraceOutput(char* buffer, size_t bufferSize, uint32 flags)
: fBuffer(buffer),
fCapacity(bufferSize),
fFlags(flags)
{
Clear();
}
void
TraceOutput::Clear()
{
if (fCapacity > 0)
fBuffer[0] = '\0';
fSize = 0;
}
void
TraceOutput::Print(const char* format,...)
{
#if ENABLE_TRACING
if (IsFull())
return;
va_list args;
va_start(args, format);
fSize += vsnprintf(fBuffer + fSize, fCapacity - fSize, format, args);
va_end(args);
#endif
}
void
TraceOutput::PrintStackTrace(tracing_stack_trace* stackTrace)
{
#if ENABLE_TRACING
if (stackTrace == NULL || stackTrace->depth <= 0)
return;
for (int32 i = 0; i < stackTrace->depth; i++) {
addr_t address = stackTrace->return_addresses[i];
const char* symbol;
const char* imageName;
bool exactMatch;
addr_t baseAddress;
if (elf_debug_lookup_symbol_address(address, &baseAddress, &symbol,
&imageName, &exactMatch) == B_OK) {
Print(" %p %s + 0x%lx (%s)%s\n", (void*)address, symbol,
address - baseAddress, imageName,
exactMatch ? "" : " (nearest)");
} else
Print(" %p\n", (void*)address);
}
#endif
}
void
TraceOutput::SetLastEntryTime(bigtime_t time)
{
fLastEntryTime = time;
}
bigtime_t
TraceOutput::LastEntryTime() const
{
return fLastEntryTime;
}
// #pragma mark -
TraceEntry::TraceEntry()
{
}
TraceEntry::~TraceEntry()
{
}
void
TraceEntry::Dump(TraceOutput& out)
{
#if ENABLE_TRACING
// to be overridden by subclasses
out.Print("ENTRY %p", this);
#endif
}
void
TraceEntry::DumpStackTrace(TraceOutput& out)
{
}
void
TraceEntry::Initialized()
{
#if ENABLE_TRACING
ToTraceEntry()->flags |= ENTRY_INITIALIZED;
sWritten++;
#endif
}
void*
TraceEntry::operator new(size_t size, const std::nothrow_t&) throw()
{
#if ENABLE_TRACING
trace_entry* entry = allocate_entry(size + sizeof(trace_entry), 0);
return entry != NULL ? entry + 1 : NULL;
#endif
return NULL;
}
// #pragma mark -
AbstractTraceEntry::AbstractTraceEntry()
{
struct thread* thread = thread_get_current_thread();
if (thread != NULL) {
fThread = thread->id;
if (thread->team)
fTeam = thread->team->id;
}
fTime = system_time();
}
AbstractTraceEntry::~AbstractTraceEntry()
{
}
void
AbstractTraceEntry::Dump(TraceOutput& out)
{
bigtime_t time = (out.Flags() & TRACE_OUTPUT_DIFF_TIME)
? fTime - out.LastEntryTime()
: fTime;
if (out.Flags() & TRACE_OUTPUT_TEAM_ID)
out.Print("[%6ld:%6ld] %10Ld: ", fThread, fTeam, time);
else
out.Print("[%6ld] %10Ld: ", fThread, time);
AddDump(out);
out.SetLastEntryTime(fTime);
}
void
AbstractTraceEntry::AddDump(TraceOutput& out)
{
}
// #pragma mark -
#if ENABLE_TRACING
class KernelTraceEntry : public AbstractTraceEntry {
public:
KernelTraceEntry(const char* message)
{
fMessage = alloc_tracing_buffer_strcpy(message, 256, false);
#if KTRACE_PRINTF_STACK_TRACE
fStackTrace = capture_tracing_stack_trace(
KTRACE_PRINTF_STACK_TRACE, 1, false);
#endif
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("kern: %s", fMessage);
}
#if KTRACE_PRINTF_STACK_TRACE
virtual void DumpStackTrace(TraceOutput& out)
{
out.PrintStackTrace(fStackTrace);
}
#endif
private:
char* fMessage;
#if KTRACE_PRINTF_STACK_TRACE
tracing_stack_trace* fStackTrace;
#endif
};
class UserTraceEntry : public AbstractTraceEntry {
public:
UserTraceEntry(const char* message)
{
fMessage = alloc_tracing_buffer_strcpy(message, 256, true);
#if KTRACE_PRINTF_STACK_TRACE
fStackTrace = capture_tracing_stack_trace(
KTRACE_PRINTF_STACK_TRACE, 1, false);
#endif
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("user: %s", fMessage);
}
#if KTRACE_PRINTF_STACK_TRACE
virtual void DumpStackTrace(TraceOutput& out)
{
out.PrintStackTrace(fStackTrace);
}
#endif
private:
char* fMessage;
#if KTRACE_PRINTF_STACK_TRACE
tracing_stack_trace* fStackTrace;
#endif
};
#endif // ENABLE_TRACING
// #pragma mark - trace filters
TraceFilter::~TraceFilter()
{
}
bool
TraceFilter::Filter(const TraceEntry* entry, LazyTraceOutput& out)
{
return false;
}
class ThreadTraceFilter : public TraceFilter {
public:
virtual bool Filter(const TraceEntry* _entry, LazyTraceOutput& out)
{
const AbstractTraceEntry* entry
= dynamic_cast<const AbstractTraceEntry*>(_entry);
return (entry != NULL && entry->Thread() == fThread);
}
};
class TeamTraceFilter : public TraceFilter {
public:
virtual bool Filter(const TraceEntry* _entry, LazyTraceOutput& out)
{
const AbstractTraceEntry* entry
= dynamic_cast<const AbstractTraceEntry*>(_entry);
return (entry != NULL && entry->Team() == fTeam);
}
};
class PatternTraceFilter : public TraceFilter {
public:
virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out)
{
return strstr(out.DumpEntry(entry), fString) != NULL;
}
};
class DecimalPatternTraceFilter : public TraceFilter {
public:
virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out)
{
// TODO: this is *very* slow
char buffer[64];
snprintf(buffer, sizeof(buffer), "%Ld", fValue);
return strstr(out.DumpEntry(entry), buffer) != NULL;
}
};
class HexPatternTraceFilter : public TraceFilter {
public:
virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out)
{
// TODO: this is *very* slow
char buffer[64];
snprintf(buffer, sizeof(buffer), "%Lx", fValue);
return strstr(out.DumpEntry(entry), buffer) != NULL;
}
};
class StringPatternTraceFilter : public TraceFilter {
public:
virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out)
{
if (IS_KERNEL_ADDRESS(fValue))
return strstr(out.DumpEntry(entry), (const char*)fValue) != NULL;
// TODO: this is *very* slow
char buffer[64];
user_strlcpy(buffer, (const char*)fValue, sizeof(buffer));
return strstr(out.DumpEntry(entry), buffer) != NULL;
}
};
class NotTraceFilter : public TraceFilter {
public:
virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out)
{
return !fSubFilters.first->Filter(entry, out);
}
};
class AndTraceFilter : public TraceFilter {
public:
virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out)
{
return fSubFilters.first->Filter(entry, out)
&& fSubFilters.second->Filter(entry, out);
}
};
class OrTraceFilter : public TraceFilter {
public:
virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out)
{
return fSubFilters.first->Filter(entry, out)
|| fSubFilters.second->Filter(entry, out);
}
};
class TraceFilterParser {
public:
static TraceFilterParser* Default()
{
return &sParser;
}
bool Parse(int argc, const char* const* argv)
{
fTokens = argv;
fTokenCount = argc;
fTokenIndex = 0;
fFilterCount = 0;
TraceFilter* filter = _ParseExpression();
return fTokenIndex == fTokenCount && filter != NULL;
}
TraceFilter* Filter()
{
return &fFilters[0];
}
private:
TraceFilter* _ParseExpression()
{
const char* token = _NextToken();
if (!token) {
// unexpected end of expression
return NULL;
}
if (fFilterCount == MAX_FILTERS) {
// too many filters
return NULL;
}
if (token[0] == '#') {
TraceFilter* filter = new(&fFilters[fFilterCount++])
PatternTraceFilter;
filter->fString = token + 1;
return filter;
} else if (token[0] == 'd' && token[1] == '#') {
TraceFilter* filter = new(&fFilters[fFilterCount++])
DecimalPatternTraceFilter;
filter->fValue = parse_expression(token + 2);
return filter;
} else if (token[0] == 'x' && token[1] == '#') {
TraceFilter* filter = new(&fFilters[fFilterCount++])
HexPatternTraceFilter;
filter->fValue = parse_expression(token + 2);
return filter;
} else if (token[0] == 's' && token[1] == '#') {
TraceFilter* filter = new(&fFilters[fFilterCount++])
StringPatternTraceFilter;
filter->fValue = parse_expression(token + 2);
return filter;
} else if (strcmp(token, "not") == 0) {
TraceFilter* filter = new(&fFilters[fFilterCount++]) NotTraceFilter;
if ((filter->fSubFilters.first = _ParseExpression()) != NULL)
return filter;
return NULL;
} else if (strcmp(token, "and") == 0) {
TraceFilter* filter = new(&fFilters[fFilterCount++]) AndTraceFilter;
if ((filter->fSubFilters.first = _ParseExpression()) != NULL
&& (filter->fSubFilters.second = _ParseExpression()) != NULL) {
return filter;
}
return NULL;
} else if (strcmp(token, "or") == 0) {
TraceFilter* filter = new(&fFilters[fFilterCount++]) OrTraceFilter;
if ((filter->fSubFilters.first = _ParseExpression()) != NULL
&& (filter->fSubFilters.second = _ParseExpression()) != NULL) {
return filter;
}
return NULL;
} else if (strcmp(token, "thread") == 0) {
const char* arg = _NextToken();
if (arg == NULL) {
// unexpected end of expression
return NULL;
}
TraceFilter* filter = new(&fFilters[fFilterCount++])
ThreadTraceFilter;
filter->fThread = strtol(arg, NULL, 0);
return filter;
} else if (strcmp(token, "team") == 0) {
const char* arg = _NextToken();
if (arg == NULL) {
// unexpected end of expression
return NULL;
}
TraceFilter* filter = new(&fFilters[fFilterCount++])
TeamTraceFilter;
filter->fTeam = strtol(arg, NULL, 0);
return filter;
} else {
// invalid token
return NULL;
}
}
const char* _CurrentToken() const
{
if (fTokenIndex >= 1 && fTokenIndex <= fTokenCount)
return fTokens[fTokenIndex - 1];
return NULL;
}
const char* _NextToken()
{
if (fTokenIndex >= fTokenCount)
return NULL;
return fTokens[fTokenIndex++];
}
private:
enum { MAX_FILTERS = 32 };
const char* const* fTokens;
int fTokenCount;
int fTokenIndex;
TraceFilter fFilters[MAX_FILTERS];
int fFilterCount;
static TraceFilterParser sParser;
};
TraceFilterParser TraceFilterParser::sParser;
// #pragma mark -
#if ENABLE_TRACING
TraceEntry*
TraceEntryIterator::Next()
{
if (fIndex == 0) {
fEntry = _NextNonBufferEntry(sFirstEntry);
fIndex = 1;
} else if (fEntry != NULL) {
fEntry = _NextNonBufferEntry(next_entry(fEntry));
fIndex++;
}
return Current();
}
TraceEntry*
TraceEntryIterator::Previous()
{
if (fIndex == (int32)sEntries + 1)
fEntry = sAfterLastEntry;
if (fEntry != NULL) {
fEntry = _PreviousNonBufferEntry(previous_entry(fEntry));
fIndex--;
}
return Current();
}
TraceEntry*
TraceEntryIterator::MoveTo(int32 index)
{
if (index == fIndex)
return Current();
if (index <= 0 || index > (int32)sEntries) {
fIndex = (index <= 0 ? 0 : sEntries + 1);
fEntry = NULL;
return NULL;
}
// get the shortest iteration path
int32 distance = index - fIndex;
int32 direction = distance < 0 ? -1 : 1;
distance *= direction;
if (index < distance) {
distance = index;
direction = 1;
fEntry = NULL;
fIndex = 0;
}
if ((int32)sEntries + 1 - fIndex < distance) {
distance = sEntries + 1 - fIndex;
direction = -1;
fEntry = NULL;
fIndex = sEntries + 1;
}
// iterate to the index
if (direction < 0) {
while (fIndex != index)
Previous();
} else {
while (fIndex != index)
Next();
}
return Current();
}
trace_entry*
TraceEntryIterator::_NextNonBufferEntry(trace_entry* entry)
{
while (entry != NULL && (entry->flags & BUFFER_ENTRY) != 0)
entry = next_entry(entry);
return entry;
}
trace_entry*
TraceEntryIterator::_PreviousNonBufferEntry(trace_entry* entry)
{
while (entry != NULL && (entry->flags & BUFFER_ENTRY) != 0)
entry = previous_entry(entry);
return entry;
}
int
dump_tracing_internal(int argc, char** argv, WrapperTraceFilter* wrapperFilter)
{
int argi = 1;
// variables in which we store our state to be continuable
static int32 _previousCount = 0;
static bool _previousHasFilter = false;
static bool _previousPrintStackTrace = false;
static int32 _previousMaxToCheck = 0;
static int32 _previousFirstChecked = 1;
static int32 _previousLastChecked = -1;
static int32 _previousDirection = 1;
static uint32 _previousWritten = 0;
static uint32 _previousEntries = 0;
static uint32 _previousOutputFlags = 0;
static TraceEntryIterator iterator;
// Note: start and index are Pascal-like indices (i.e. in [1, sEntries]).
int32 start = 0; // special index: print the last count entries
int32 count = 0;
int32 maxToCheck = 0;
int32 cont = 0;
bool hasFilter = false;
bool printStackTrace = false;
uint32 outputFlags = 0;
while (argi < argc) {
if (strcmp(argv[argi], "--difftime") == 0) {
outputFlags |= TRACE_OUTPUT_DIFF_TIME;
argi++;
} else if (strcmp(argv[argi], "--printteam") == 0) {
outputFlags |= TRACE_OUTPUT_TEAM_ID;
argi++;
} else if (strcmp(argv[argi], "--stacktrace") == 0) {
printStackTrace = true;
argi++;
} else
break;
}
if (argi < argc) {
if (strcmp(argv[argi], "forward") == 0) {
cont = 1;
argi++;
} else if (strcmp(argv[argi], "backward") == 0) {
cont = -1;
argi++;
}
} else
cont = _previousDirection;
if (cont != 0) {
if (argi < argc) {
print_debugger_command_usage(argv[0]);
return 0;
}
if (sWritten == 0 || sWritten != _previousWritten
|| sEntries != _previousEntries) {
kprintf("Can't continue iteration. \"%s\" has not been invoked "
"before, or there were new entries written since the last "
"invocation.\n", argv[0]);
return 0;
}
}
// get start, count, maxToCheck
int32* params[3] = { &start, &count, &maxToCheck };
for (int i = 0; i < 3 && !hasFilter && argi < argc; i++) {
if (strcmp(argv[argi], "filter") == 0) {
hasFilter = true;
argi++;
} else if (argv[argi][0] == '#') {
hasFilter = true;
} else {
*params[i] = parse_expression(argv[argi]);
argi++;
}
}
// filter specification
if (argi < argc) {
hasFilter = true;
if (strcmp(argv[argi], "filter") == 0)
argi++;
if (!TraceFilterParser::Default()->Parse(argc - argi, argv + argi)) {
print_debugger_command_usage(argv[0]);
return 0;
}
}
int32 direction;
int32 firstToCheck;
int32 lastToCheck;
if (cont != 0) {
// get values from the previous iteration
direction = cont;
count = _previousCount;
maxToCheck = _previousMaxToCheck;
hasFilter = _previousHasFilter;
outputFlags = _previousOutputFlags;
printStackTrace = _previousPrintStackTrace;
if (direction < 0)
start = _previousFirstChecked - 1;
else
start = _previousLastChecked + 1;
} else {
// defaults for count and maxToCheck
if (count == 0)
count = 30;
if (maxToCheck == 0 || !hasFilter)
maxToCheck = count;
else if (maxToCheck < 0)
maxToCheck = sEntries;
// determine iteration direction
direction = (start <= 0 || count < 0 ? -1 : 1);
// validate count and maxToCheck
if (count < 0)
count = -count;
if (maxToCheck < 0)
maxToCheck = -maxToCheck;
if (maxToCheck > (int32)sEntries)
maxToCheck = sEntries;
if (count > maxToCheck)
count = maxToCheck;
// validate start
if (start <= 0 || start > (int32)sEntries)
start = max_c(1, sEntries);
}
if (direction < 0) {
firstToCheck = max_c(1, start - maxToCheck + 1);
lastToCheck = start;
} else {
firstToCheck = start;
lastToCheck = min_c((int32)sEntries, start + maxToCheck - 1);
}
// reset the iterator, if something changed in the meantime
if (sWritten == 0 || sWritten != _previousWritten
|| sEntries != _previousEntries) {
iterator.Reset();
}
LazyTraceOutput out(sTraceOutputBuffer, kTraceOutputBufferSize,
outputFlags);
bool markedMatching = false;
int32 firstToDump = firstToCheck;
int32 lastToDump = lastToCheck;
TraceFilter* filter = NULL;
if (hasFilter)
filter = TraceFilterParser::Default()->Filter();
if (wrapperFilter != NULL) {
wrapperFilter->Init(filter, direction, cont != 0);
filter = wrapperFilter;
}
if (direction < 0 && filter && lastToCheck - firstToCheck >= count) {
// iteration direction is backwards
markedMatching = true;
// From the last entry to check iterate backwards to check filter
// matches.
int32 matching = 0;
// move to the entry after the last entry to check
iterator.MoveTo(lastToCheck + 1);
// iterate backwards
firstToDump = -1;
lastToDump = -1;
while (iterator.Index() > firstToCheck) {
TraceEntry* entry = iterator.Previous();
if ((entry->Flags() & ENTRY_INITIALIZED) != 0) {
out.Clear();
if (filter->Filter(entry, out)) {
entry->ToTraceEntry()->flags |= FILTER_MATCH;
if (lastToDump == -1)
lastToDump = iterator.Index();
firstToDump = iterator.Index();
matching++;
if (matching >= count)
break;
} else
entry->ToTraceEntry()->flags &= ~FILTER_MATCH;
}
}
firstToCheck = iterator.Index();
// iterate to the previous entry, so that the next loop starts at the
// right one
iterator.Previous();
}
out.SetLastEntryTime(0);
// set the iterator to the entry before the first one to dump
iterator.MoveTo(firstToDump - 1);
// dump the entries matching the filter in the range
// [firstToDump, lastToDump]
int32 dumped = 0;
while (TraceEntry* entry = iterator.Next()) {
int32 index = iterator.Index();
if (index < firstToDump)
continue;
if (index > lastToDump || dumped >= count) {
if (direction > 0)
lastToCheck = index - 1;
break;
}
if ((entry->Flags() & ENTRY_INITIALIZED) != 0) {
out.Clear();
if (filter && (markedMatching
? (entry->Flags() & FILTER_MATCH) == 0
: !filter->Filter(entry, out))) {
continue;
}
// don't print trailing new line
const char* dump = out.DumpEntry(entry);
int len = strlen(dump);
if (len > 0 && dump[len - 1] == '\n')
len--;
kprintf("%5ld. %.*s\n", index, len, dump);
if (printStackTrace) {
out.Clear();
entry->DumpStackTrace(out);
if (out.Size() > 0)
kputs(out.Buffer());
}
} else if (!filter)
kprintf("%5ld. ** uninitialized entry **\n", index);
dumped++;
}
kprintf("printed %ld entries within range %ld to %ld (%ld of %ld total, "
"%ld ever)\n", dumped, firstToCheck, lastToCheck,
lastToCheck - firstToCheck + 1, sEntries, sWritten);
// store iteration state
_previousCount = count;
_previousMaxToCheck = maxToCheck;
_previousHasFilter = hasFilter;
_previousPrintStackTrace = printStackTrace;
_previousFirstChecked = firstToCheck;
_previousLastChecked = lastToCheck;
_previousDirection = direction;
_previousWritten = sWritten;
_previousEntries = sEntries;
_previousOutputFlags = outputFlags;
return cont != 0 ? B_KDEBUG_CONT : 0;
}
static int
dump_tracing_command(int argc, char** argv)
{
return dump_tracing_internal(argc, argv, NULL);
}
#endif // ENABLE_TRACING
extern "C" uint8*
alloc_tracing_buffer(size_t size)
{
#if ENABLE_TRACING
trace_entry* entry = allocate_entry(size + sizeof(trace_entry),
BUFFER_ENTRY);
if (entry == NULL)
return NULL;
return (uint8*)(entry + 1);
#else
return NULL;
#endif
}
uint8*
alloc_tracing_buffer_memcpy(const void* source, size_t size, bool user)
{
if (user && !IS_USER_ADDRESS(source))
return NULL;
uint8* buffer = alloc_tracing_buffer(size);
if (buffer == NULL)
return NULL;
if (user) {
if (user_memcpy(buffer, source, size) != B_OK)
return NULL;
} else
memcpy(buffer, source, size);
return buffer;
}
char*
alloc_tracing_buffer_strcpy(const char* source, size_t maxSize, bool user)
{
if (source == NULL || maxSize == 0)
return NULL;
if (user && !IS_USER_ADDRESS(source))
return NULL;
// limit maxSize to the actual source string len
if (user) {
ssize_t size = user_strlcpy(NULL, source, 0);
// there's no user_strnlen()
if (size < 0)
return 0;
maxSize = min_c(maxSize, (size_t)size + 1);
} else
maxSize = strnlen(source, maxSize - 1) + 1;
char* buffer = (char*)alloc_tracing_buffer(maxSize);
if (buffer == NULL)
return NULL;
if (user) {
if (user_strlcpy(buffer, source, maxSize) < B_OK)
return NULL;
} else
strlcpy(buffer, source, maxSize);
return buffer;
}
tracing_stack_trace*
capture_tracing_stack_trace(int32 maxCount, int32 skipFrames, bool userOnly)
{
#if ENABLE_TRACING
// TODO: page_fault_exception() doesn't allow us to gracefully handle
// a bad address in the stack trace, if interrupts are disabled.
if (!are_interrupts_enabled())
return NULL;
tracing_stack_trace* stackTrace
= (tracing_stack_trace*)alloc_tracing_buffer(
sizeof(tracing_stack_trace) + maxCount * sizeof(addr_t));
if (stackTrace != NULL) {
stackTrace->depth = arch_debug_get_stack_trace(
stackTrace->return_addresses, maxCount, 0, skipFrames + 1,
userOnly);
}
return stackTrace;
#else
return NULL;
#endif
}
int
dump_tracing(int argc, char** argv, WrapperTraceFilter* wrapperFilter)
{
#if ENABLE_TRACING
return dump_tracing_internal(argc, argv, wrapperFilter);
#else
return 0;
#endif
}
void
lock_tracing_buffer()
{
#if ENABLE_TRACING
acquire_spinlock(&sLock);
#endif
}
void
unlock_tracing_buffer()
{
#if ENABLE_TRACING
release_spinlock(&sLock);
#endif
}
extern "C" status_t
tracing_init(void)
{
#if ENABLE_TRACING
area_id area = create_area("tracing log", (void**)&sTraceOutputBuffer,
B_ANY_KERNEL_ADDRESS, MAX_TRACE_SIZE + kTraceOutputBufferSize,
B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (area < B_OK)
return area;
sBuffer = (trace_entry*)(sTraceOutputBuffer + kTraceOutputBufferSize);
sFirstEntry = sBuffer;
sAfterLastEntry = sBuffer;
add_debugger_command_etc("traced", &dump_tracing_command,
"Dump recorded trace entries",
"[ --printteam ] [ --difftime ] [ --stacktrace ] "
"(\"forward\" | \"backward\") "
"| ([ <start> [ <count> [ <range> ] ] ] "
"[ #<pattern> | (\"filter\" <filter>) ])\n"
"Prints recorded trace entries. If \"backward\" or \"forward\" is\n"
"specified, the command continues where the previous invocation left\n"
"off, i.e. printing the previous respectively next entries (as many\n"
"as printed before). In this case the command is continuable, that is\n"
"afterwards entering an empty line in the debugger will reinvoke it.\n"
"If no arguments are given, the command continues in the direction\n"
"of the last invocation.\n"
"--printteam - enables printing the entries' team IDs.\n"
"--difftime - print difference times for all but the first entry.\n"
"--stacktrace - print stack traces for entries that captured one.\n"
" <start> - The base index of the entries to print. Depending on\n"
" whether the iteration direction is forward or\n"
" backward this will be the first or last entry printed\n"
" (potentially, if a filter is specified). The index of\n"
" the first entry in the trace buffer is 1. If 0 is\n"
" specified, the last <count> recorded entries are\n"
" printed (iteration direction is backward). Defaults \n"
" to 0.\n"
" <count> - The number of entries to be printed. Defaults to 30.\n"
" If negative, the -<count> entries before and\n"
" including <start> will be printed.\n"
" <range> - Only relevant if a filter is specified. Specifies the\n"
" number of entries to be filtered -- depending on the\n"
" iteration direction the entries before or after\n"
" <start>. If more than <count> entries match the\n"
" filter, only the first (forward) or last (backward)\n"
" <count> matching entries will be printed. If 0 is\n"
" specified <range> will be set to <count>. If -1,\n"
" <range> will be set to the number of recorded\n"
" entries.\n"
" <pattern> - If specified only entries containing this string are\n"
" printed.\n"
" <filter> - If specified only entries matching this filter\n"
" expression are printed. The expression can consist of\n"
" prefix operators \"not\", \"and\", \"or\", and\n"
" filters \"'thread' <thread>\" (matching entries\n"
" with the given thread ID), \"'team' <team>\"\n"
"(matching entries with the given team ID), and\n"
" \"#<pattern>\" (matching entries containing the given\n"
" string).\n", 0);
#endif // ENABLE_TRACING
return B_OK;
}
void
ktrace_printf(const char *format, ...)
{
#if ENABLE_TRACING
va_list list;
va_start(list, format);
char buffer[256];
vsnprintf(buffer, sizeof(buffer), format, list);
va_end(list);
new(nothrow) KernelTraceEntry(buffer);
#endif // ENABLE_TRACING
}
void
_user_ktrace_output(const char *message)
{
#if ENABLE_TRACING
new(nothrow) UserTraceEntry(message);
#endif // ENABLE_TRACING
}