The "traced" command can now be passed a filter expression consisting of

boolean operators ("not", "and", "or") and filters matching thread IDs
or contained strings.
I'm still not fully happy with the command. It should be possible to
define a filter and then comfortably scroll through the matching
entries. Currently having to specify an index range of the unfiltered
entries is rather unhandy in combination with filtering.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23658 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-01-20 00:34:37 +00:00
parent c59c68c5e0
commit 4c4b14c3bb
2 changed files with 269 additions and 19 deletions
+4
View File
@@ -27,6 +27,10 @@ class TraceOutput {
void Print(const char* format,...);
bool IsFull() const { return fSize >= fCapacity; }
char* Buffer() const { return fBuffer; }
size_t Capacity() const { return fCapacity; }
size_t Size() const { return fSize; }
private:
char* fBuffer;
size_t fCapacity;
+265 -19
View File
@@ -240,6 +240,208 @@ AbstractTraceEntry::AddDump(TraceOutput& out)
}
// #pragma mark - trace filters
class LazyTraceOutput : public TraceOutput {
public:
LazyTraceOutput(char* buffer, size_t bufferSize)
: TraceOutput(buffer, bufferSize)
{
}
const char* DumpEntry(const TraceEntry* entry)
{
if (Size() == 0) {
const_cast<TraceEntry*>(entry)->Dump(*this);
// Dump() should probably be const
}
return Buffer();
}
};
class TraceFilter {
public:
virtual ~TraceFilter()
{
}
virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out)
{
return false;
}
public:
union {
thread_id fThread;
const char* fString;
struct {
TraceFilter* first;
TraceFilter* second;
} fSubFilters;
};
};
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 PatternTraceFilter : public TraceFilter {
public:
virtual bool Filter(const TraceEntry* entry, LazyTraceOutput& out)
{
return strstr(out.DumpEntry(entry), fString) != 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;
}
bool Filter(const TraceEntry* entry, LazyTraceOutput& out)
{
return fFilters[0].Filter(entry, out);
}
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 (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 {
// 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 -
@@ -249,36 +451,73 @@ AbstractTraceEntry::AddDump(TraceOutput& out)
int
dump_tracing(int argc, char** argv)
{
const char *pattern = NULL;
if (argv[argc - 1][0] == '#')
pattern = argv[--argc] + 1;
int argi = 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)
bool hasFilter = false;
if (argi < argc) {
if (strcmp(argv[argi], "forward") == 0) {
cont = 1;
else if (strcmp(argv[1], "backward") == 0)
argi++;
} else if (strcmp(argv[argi], "backward") == 0) {
cont = -1;
else
start = parse_expression(argv[1]);
argi++;
}
}
if (argc == 3) {
start = parse_expression(argv[1]);
count = parse_expression(argv[2]);
} else if (argc > 3) {
if (cont != 0 && argi < argc) {
print_debugger_command_usage(argv[0]);
return 0;
}
// start
if (argi < argc) {
if (strcmp(argv[argi], "filter") == 0) {
hasFilter = true;
argi++;
} else if (argv[argi][0] == '#') {
hasFilter = true;
} else {
start = parse_expression(argv[argi]);
argi++;
}
}
// count
if (!hasFilter && argi < argc) {
if (strcmp(argv[argi], "filter") == 0) {
hasFilter = true;
argi++;
} else if (argv[argi][0] == '#') {
hasFilter = true;
} else {
count = 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;
}
}
if (cont != 0) {
start = get_debug_variable("_tracingStart", start);
count = get_debug_variable("_tracingCount", count);
start = max_c(1, start + count * cont);
hasFilter = get_debug_variable("_tracingFilter", count);
}
if ((uint32)count > sEntries)
@@ -305,14 +544,13 @@ dump_tracing(int argc, char** argv)
if ((current->flags & ENTRY_INITIALIZED) != 0) {
char buffer[256];
TraceOutput out(buffer, sizeof(buffer));
LazyTraceOutput out(buffer, sizeof(buffer));
((TraceEntry*)current)->Dump(out);
if (pattern != NULL && strstr(buffer, pattern) == NULL)
TraceEntry* entry = (TraceEntry*)current;
if (hasFilter && !TraceFilterParser::Default()->Filter(entry, out))
continue;
kprintf("%5ld. %s\n", index, buffer);
kprintf("%5ld. %s\n", index, out.DumpEntry(entry));
} else
kprintf("%5ld. ** uninitialized entry **\n", index);
@@ -324,6 +562,7 @@ dump_tracing(int argc, char** argv)
set_debug_variable("_tracingStart", start);
set_debug_variable("_tracingCount", count);
set_debug_variable("_tracingFilter", hasFilter);
return cont != 0 ? B_KDEBUG_CONT : 0;
}
@@ -420,7 +659,7 @@ tracing_init(void)
add_debugger_command_etc("traced", &dump_tracing,
"Dump recorded trace entries",
"(\"forward\" | \"backward\") | ([ <start> [ <count> ] ] "
"[ #<pattern> ])\n"
"[ #<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"
@@ -432,7 +671,14 @@ tracing_init(void)
" 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);
" 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\", filters of\n"
" the kind \"'thread' <thread>\" (matching entries\n"
" with the given thread ID), or filter of the kind\n"
" \"#<pattern>\" (matching entries containing the given\n"
" string.\n", 0);
#endif // ENABLE_TRACING
return B_OK;
}