From 4c4b14c3bbb2cc9f073ae3d209539d8229a6664f Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sun, 20 Jan 2008 00:34:37 +0000 Subject: [PATCH] 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 --- headers/private/kernel/tracing.h | 4 + src/system/kernel/debug/tracing.cpp | 284 ++++++++++++++++++++++++++-- 2 files changed, 269 insertions(+), 19 deletions(-) diff --git a/headers/private/kernel/tracing.h b/headers/private/kernel/tracing.h index fa6a589fb4..5b856be91a 100644 --- a/headers/private/kernel/tracing.h +++ b/headers/private/kernel/tracing.h @@ -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; diff --git a/src/system/kernel/debug/tracing.cpp b/src/system/kernel/debug/tracing.cpp index 42ef4d9767..697964a9ad 100644 --- a/src/system/kernel/debug/tracing.cpp +++ b/src/system/kernel/debug/tracing.cpp @@ -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(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(_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\") | ([ [ ] ] " - "[ # ])\n" + "[ # | (\"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" " - The number of entries to be printed. Defaults to 30.\n" " - If specified only entries containing this string are\n" - " printed.\n", 0); + " printed.\n" + " - 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' \" (matching entries\n" + " with the given thread ID), or filter of the kind\n" + " \"#\" (matching entries containing the given\n" + " string.\n", 0); #endif // ENABLE_TRACING return B_OK; }