AbstractTraceEntry::AddDump(char*, size_t). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23655 a95241bf-73f2-0310-859d-f6bbb57e9c96
440 lines
8.6 KiB
C++
440 lines
8.6 KiB
C++
/*
|
|
* Copyright 2008, Ingo Weinhold, [email protected].
|
|
* Copyright 2008, Axel Dörfler, [email protected].
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include <tracing.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <debug.h>
|
|
#include <kernel.h>
|
|
#include <util/AutoLock.h>
|
|
|
|
|
|
#if ENABLE_TRACING
|
|
|
|
//#define TRACE_TRACING
|
|
#ifdef TRACE_TRACING
|
|
# define TRACE(x) dprintf x
|
|
#else
|
|
# define TRACE(x) ;
|
|
#endif
|
|
|
|
|
|
enum {
|
|
WRAP_ENTRY = 0x01,
|
|
ENTRY_INITIALIZED = 0x02,
|
|
BUFFER_ENTRY = 0x04
|
|
};
|
|
|
|
static const size_t kBufferSize = MAX_TRACE_SIZE / 4 - 1;
|
|
|
|
static trace_entry* sBuffer;
|
|
static trace_entry* sFirstEntry;
|
|
static trace_entry* sAfterLastEntry;
|
|
static uint32 sEntries;
|
|
static uint32 sWritten;
|
|
static spinlock sLock;
|
|
|
|
|
|
static trace_entry*
|
|
next_entry(trace_entry* entry)
|
|
{
|
|
entry += entry->size >> 2;
|
|
if ((entry->flags & WRAP_ENTRY) != 0)
|
|
entry = sBuffer;
|
|
|
|
if (entry == sAfterLastEntry)
|
|
return NULL;
|
|
|
|
return entry;
|
|
}
|
|
|
|
|
|
static void
|
|
make_space(size_t needed)
|
|
{
|
|
if (sAfterLastEntry + needed / 4 > sBuffer + kBufferSize - 1) {
|
|
sAfterLastEntry->size = 0;
|
|
sAfterLastEntry->flags = WRAP_ENTRY;
|
|
sAfterLastEntry = sBuffer;
|
|
}
|
|
|
|
int32 space = (sFirstEntry - sAfterLastEntry) * 4;
|
|
TRACE(("make_space(%lu), left %ld\n", needed, space));
|
|
if (space < 0)
|
|
sAfterLastEntry = sBuffer;
|
|
else if ((size_t)space < needed)
|
|
needed -= space;
|
|
else
|
|
return;
|
|
|
|
while (true) {
|
|
// TODO: If the entry is not ENTRY_INITIALIZED yet, we must not
|
|
// discard it, or the owner might overwrite memory we're allocating.
|
|
trace_entry* removed = sFirstEntry;
|
|
uint16 freed = sFirstEntry->size;
|
|
TRACE((" skip start %p, %u bytes\n", sFirstEntry, freed));
|
|
|
|
sFirstEntry = next_entry(sFirstEntry);
|
|
if (sFirstEntry == NULL)
|
|
sFirstEntry = sAfterLastEntry;
|
|
|
|
if (!(removed->flags & BUFFER_ENTRY)) {
|
|
((TraceEntry*)removed)->~TraceEntry();
|
|
sEntries--;
|
|
}
|
|
|
|
if (needed <= freed)
|
|
break;
|
|
|
|
needed -= freed;
|
|
}
|
|
|
|
TRACE((" out: start %p, entries %ld\n", sFirstEntry, sEntries));
|
|
}
|
|
|
|
|
|
static trace_entry*
|
|
allocate_entry(size_t size, uint16 flags)
|
|
{
|
|
if (sBuffer == NULL)
|
|
return NULL;
|
|
|
|
InterruptsSpinLocker _(sLock);
|
|
|
|
size = (size + 3) & ~3;
|
|
|
|
TRACE(("allocate_entry(%lu), start %p, end %p, buffer %p\n", size,
|
|
sFirstEntry, sAfterLastEntry, sBuffer));
|
|
|
|
if (sFirstEntry < sAfterLastEntry || sEntries == 0) {
|
|
// the buffer ahead of us is still empty
|
|
uint32 space = (sBuffer + kBufferSize - sAfterLastEntry) * 4;
|
|
TRACE((" free after end %p: %lu\n", sAfterLastEntry, space));
|
|
if (space < size)
|
|
make_space(size);
|
|
} else {
|
|
// we need to overwrite existing entries
|
|
make_space(size);
|
|
}
|
|
|
|
trace_entry* entry = sAfterLastEntry;
|
|
entry->size = size;
|
|
entry->flags = flags;
|
|
sAfterLastEntry += size >> 2;
|
|
|
|
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)
|
|
: fBuffer(buffer),
|
|
fCapacity(bufferSize)
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
|
|
void
|
|
TraceOutput::Clear()
|
|
{
|
|
if (fCapacity > 0)
|
|
fBuffer[0] = '\0';
|
|
fSize = 0;
|
|
}
|
|
|
|
|
|
void
|
|
TraceOutput::Print(const char* format,...)
|
|
{
|
|
if (IsFull())
|
|
return;
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
fSize += vsnprintf(fBuffer + fSize, fCapacity - fSize, format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
// #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::Initialized()
|
|
{
|
|
#if ENABLE_TRACING
|
|
flags |= ENTRY_INITIALIZED;
|
|
sWritten++;
|
|
#endif
|
|
}
|
|
|
|
|
|
void*
|
|
TraceEntry::operator new(size_t size, const std::nothrow_t&) throw()
|
|
{
|
|
#if ENABLE_TRACING
|
|
return allocate_entry(size, 0);
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
AbstractTraceEntry::~AbstractTraceEntry()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
AbstractTraceEntry::Dump(TraceOutput& out)
|
|
{
|
|
out.Print("[%6ld] %Ld: ", fThread, fTime);
|
|
AddDump(out);
|
|
}
|
|
|
|
|
|
void
|
|
AbstractTraceEntry::AddDump(TraceOutput& out)
|
|
{
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
#if ENABLE_TRACING
|
|
|
|
|
|
int
|
|
dump_tracing(int argc, char** argv)
|
|
{
|
|
const char *pattern = NULL;
|
|
if (argv[argc - 1][0] == '#')
|
|
pattern = argv[--argc] + 1;
|
|
|
|
// Note: start and index are Pascal-like indices (i.e. in [1, sEntries]).
|
|
int32 count = 30;
|
|
int32 start = 0; // special index: print the last count entries
|
|
int32 cont = 0;
|
|
|
|
if (argc == 2) {
|
|
if (strcmp(argv[1], "forward") == 0)
|
|
cont = 1;
|
|
else if (strcmp(argv[1], "backward") == 0)
|
|
cont = -1;
|
|
else
|
|
start = parse_expression(argv[1]);
|
|
}
|
|
|
|
if (argc == 3) {
|
|
start = parse_expression(argv[1]);
|
|
count = parse_expression(argv[2]);
|
|
} else if (argc > 3) {
|
|
print_debugger_command_usage(argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
if (cont != 0) {
|
|
start = get_debug_variable("_tracingStart", start);
|
|
count = get_debug_variable("_tracingCount", count);
|
|
start = max_c(1, start + count * cont);
|
|
}
|
|
|
|
if ((uint32)count > sEntries)
|
|
count = sEntries;
|
|
if (start <= 0)
|
|
start = max_c(1, sEntries - count + 1);
|
|
if (uint32(start + count) > sEntries)
|
|
count = sEntries - start + 1;
|
|
|
|
int32 index = 1;
|
|
int32 dumped = 0;
|
|
|
|
for (trace_entry* current = sFirstEntry; current != NULL;
|
|
current = next_entry(current), index++) {
|
|
if ((current->flags & BUFFER_ENTRY) != 0) {
|
|
// skip buffer entries
|
|
index--;
|
|
continue;
|
|
}
|
|
if (index < start)
|
|
continue;
|
|
if (index >= start + count)
|
|
break;
|
|
|
|
if ((current->flags & ENTRY_INITIALIZED) != 0) {
|
|
char buffer[256];
|
|
TraceOutput out(buffer, sizeof(buffer));
|
|
|
|
((TraceEntry*)current)->Dump(out);
|
|
|
|
if (pattern != NULL && strstr(buffer, pattern) == NULL)
|
|
continue;
|
|
|
|
kprintf("%5ld. %s\n", index, buffer);
|
|
} else
|
|
kprintf("%5ld. ** uninitialized entry **\n", index);
|
|
|
|
dumped++;
|
|
}
|
|
|
|
kprintf("entries %ld to %ld (%ld of %ld). %ld entries written\n", start,
|
|
start + count - 1, dumped, sEntries, sWritten);
|
|
|
|
set_debug_variable("_tracingStart", start);
|
|
set_debug_variable("_tracingCount", count);
|
|
|
|
return cont != 0 ? B_KDEBUG_CONT : 0;
|
|
}
|
|
|
|
|
|
#endif // ENABLE_TRACING
|
|
|
|
|
|
extern "C" uint8*
|
|
alloc_tracing_buffer(size_t size)
|
|
{
|
|
if (size == 0 || size >= 65532)
|
|
return NULL;
|
|
|
|
#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;
|
|
}
|
|
|
|
|
|
extern "C" status_t
|
|
tracing_init(void)
|
|
{
|
|
#if ENABLE_TRACING
|
|
area_id area = create_area("tracing log", (void**)&sBuffer,
|
|
B_ANY_KERNEL_ADDRESS, MAX_TRACE_SIZE, B_FULL_LOCK,
|
|
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
|
|
if (area < B_OK)
|
|
return area;
|
|
|
|
sFirstEntry = sBuffer;
|
|
sAfterLastEntry = sBuffer;
|
|
|
|
add_debugger_command_etc("traced", &dump_tracing,
|
|
"Dump recorded trace entries",
|
|
"(\"forward\" | \"backward\") | ([ <start> [ <count> ] ] "
|
|
"[ #<pattern> ])\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"
|
|
" <start> - The index of the first entry to print. The index of\n"
|
|
" the first recorded entry is 1. If 0 is specified, the\n"
|
|
" last <count> recorded entries are printed. Defaults \n"
|
|
" to 0.\n"
|
|
" <count> - The number of entries to be printed. Defaults to 30.\n"
|
|
" <pattern> - If specified only entries containing this string are\n"
|
|
" printed.\n", 0);
|
|
#endif // ENABLE_TRACING
|
|
return B_OK;
|
|
}
|
|
|