Skip to content

Commit

Permalink
debug: Add trace message filter (#890)
Browse files Browse the repository at this point in the history
* debug: Add trace message filter

* core: Support inverting glob matches

* core: Minor optimization in string_match_glob

* debug: Show enabled color

* debug: Fix inconsitently named view
  • Loading branch information
BastianBlokland committed Jun 15, 2024
1 parent 40d72b1 commit 15e2687
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 20 deletions.
2 changes: 2 additions & 0 deletions libs/core/include/core_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,12 @@ usize string_find_last_any(String, String chars);

/**
* Match the given string to a glob pattern.
* NOTE: Ascii only.
*
* Supported pattern syntax:
* '?' matches any single character.
* '*' matches any number of any characters including none.
* '!' inverts the entire match (not per segment and cannot be disabled after enabling).
*/
bool string_match_glob(String, String pattern, StringMatchFlags);

Expand Down
25 changes: 18 additions & 7 deletions libs/core/src/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,22 @@ usize string_find_last_any(const String str, const String chars) {
return sentinel_usize;
}

static bool glob_match_char(u8 a, u8 b, const StringMatchFlags flags) {
if (flags & StringMatchFlags_IgnoreCase) {
a ^= a >= 'A' && a <= 'Z' ? 0x20 : 0;
b ^= b >= 'A' && b <= 'Z' ? 0x20 : 0;
}
return a == b;
}

bool string_match_glob(const String str, const String pattern, const StringMatchFlags flags) {
/**
* Basic glob matching algorithm.
* More information on the implementation: https://research.swtch.com/glob.
* TODO: Invert currently inverts the entire match instead of inverting the segment.
* TODO: Add unicode support.
*/
bool patternInvert = false;
usize patternIdx = 0;
usize strIdx = 0;
usize nextPatternIdx = 0;
Expand All @@ -149,15 +160,15 @@ bool string_match_glob(const String str, const String pattern, const StringMatch
nextPatternIdx = patternIdx++;
nextStrIdx = strIdx + 1;
continue;
case '!':
++patternIdx;
patternInvert = true;
continue;
default:
if (strIdx >= str.size || string_is_empty(str)) {
break;
}
const bool charMatches =
flags & StringMatchFlags_IgnoreCase
? ascii_to_lower(*string_at(str, strIdx)) == ascii_to_lower(patternChar)
: *string_at(str, strIdx) == patternChar;
if (!charMatches) {
if (!glob_match_char(*string_at(str, strIdx), patternChar, flags)) {
break;
}
++patternIdx;
Expand All @@ -171,10 +182,10 @@ bool string_match_glob(const String str, const String pattern, const StringMatch
strIdx = nextStrIdx;
continue;
}
return false;
return patternInvert;
}
// Entire pattern matched.
return true;
return !patternInvert;
}

String string_trim(const String value, const String chars) {
Expand Down
9 changes: 9 additions & 0 deletions libs/core/test/test_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,15 @@ spec(string) {
check(!string_match_glob(string_lit("helloworld"), string_lit("h??lo?w?rld?"), f));
check(!string_match_glob(string_lit("hello world"), string_lit("h??lo?w?rld?"), f));

check(string_match_glob(string_lit("hello"), string_lit("!world"), f));
check(!string_match_glob(string_lit("world"), string_lit("!world"), f));
check(string_match_glob(string_lit("hello"), string_lit("!*world"), f));
check(string_match_glob(string_lit("worldhello"), string_lit("!*world"), f));
check(string_match_glob(string_empty, string_lit("!hello"), f));
check(string_match_glob(string_lit("world"), string_lit("!hello"), f));
check(!string_match_glob(string_lit("world"), string_lit("!*world"), f));
check(!string_match_glob(string_lit(" world"), string_lit("!?world"), f));

check(string_match_glob(string_lit("HeLlO"), string_lit("hello"), StringMatchFlags_IgnoreCase));
check(
!string_match_glob(string_lit("HeLlOZ"), string_lit("hello"), StringMatchFlags_IgnoreCase));
Expand Down
2 changes: 1 addition & 1 deletion libs/debug/src/asset.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

// clang-format off

static const String g_tooltipFilter = string_static("Filter assets by identifier.\nSupports glob characters \a.b*\ar and \a.b?\ar.");
static const String g_tooltipFilter = string_static("Filter assets by identifier.\nSupports glob characters \a.b*\ar and \a.b?\ar (\a.b!\ar prefix to invert).");
static const String g_tooltipReload = string_static("Request the asset to be reloaded.\nReload is delayed until all systems release the asset and reacquire it.");

// clang-format on
Expand Down
2 changes: 1 addition & 1 deletion libs/debug/src/ecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

// clang-format off

static const String g_tooltipFilter = string_static("Filter entries by name.\nSupports glob characters \a.b*\ar and \a.b?\ar.");
static const String g_tooltipFilter = string_static("Filter entries by name.\nSupports glob characters \a.b*\ar and \a.b?\ar (\a.b!\ar prefix to invert).");
static const String g_tooltipFreeze = string_static("Freeze the data set (halts data collection).");
static const String g_tooltipDumpGraph = string_static("Dump the current task graph as a dot file.");

Expand Down
2 changes: 1 addition & 1 deletion libs/debug/src/level.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
static const String g_tooltipReload = string_static("Reload the current level.");
static const String g_tooltipUnload = string_static("Unload the current level.");
static const String g_tooltipSave = string_static("Save the current level.");
static const String g_tooltipFilter = string_static("Filter levels by identifier.\nSupports glob characters \a.b*\ar and \a.b?\ar.");
static const String g_tooltipFilter = string_static("Filter levels by identifier.\nSupports glob characters \a.b*\ar and \a.b?\ar (\a.b!\ar prefix to invert).");
static const String g_queryPatternLevel = string_static("levels/*.level");
static const String g_queryPatternTerrain = string_static("terrains/*.terrain");

Expand Down
2 changes: 1 addition & 1 deletion libs/debug/src/prefab.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

// clang-format off

static const String g_tooltipFilter = string_static("Filter prefab's by identifier.\nSupports glob characters \a.b*\ar and \a.b?\ar.");
static const String g_tooltipFilter = string_static("Filter prefab's by identifier.\nSupports glob characters \a.b*\ar and \a.b?\ar (\a.b!\ar prefix to invert).");
static const String g_tooltipVolatile = string_static("Volatile prefab instances will not be persisted in the level.");
static const f32 g_createMinInteractDist = 1.0f;
static const f32 g_createMaxInteractDist = 250.0f;
Expand Down
2 changes: 1 addition & 1 deletion libs/debug/src/rend.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ static const String g_tooltipVerbose = string_static("Should verbose lo
static const String g_tooltipDefaults = string_static("Reset all settings to their defaults.");
static const String g_tooltipReset = string_static("Re-initialize the renderer.");
static const String g_tooltipFreeze = string_static("Freeze the data set (halts data collection).");
static const String g_tooltipResourceFilter = string_static("Filter resources by name.\nSupports glob characters \a.b*\ar and \a.b?\ar.");
static const String g_tooltipResourceFilter = string_static("Filter resources by name.\nSupports glob characters \a.b*\ar and \a.b?\ar (\a.b!\ar prefix to invert).");
static const String g_tooltipShadows = string_static("Enable shadow-map rendering to allow geometry to occlude the light radiance.");
static const String g_tooltipShadowFilterSize = string_static("Shadow filter size (in meters).\nControls the size of the soft shadow edge.");
static const String g_tooltipAmbientOcclusion = string_static("\a.b[SSAO]\ar Sample the geometry depth-buffer to compute a occlusion factor (how exposed it is to ambient lighting) for each fragment.");
Expand Down
8 changes: 4 additions & 4 deletions libs/debug/src/stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ ecs_view_define(StatsUpdateView) {
ecs_access_read(UiStatsComp);
}

ecs_view_define(CanvasWrite) {
ecs_view_define(CanvasWriteView) {
ecs_view_flags(EcsViewFlags_Exclusive); // Only access the canvas's we create.
ecs_access_write(UiCanvasComp);
}
Expand Down Expand Up @@ -670,7 +670,7 @@ ecs_system_define(DebugStatsUpdateSys) {
const EcsRunnerStats ecsRunnerStats = ecs_runner_stats_query(g_ecsRunningRunner);
debug_stats_global_update(statsGlobal, &ecsRunnerStats);

EcsIterator* canvasItr = ecs_view_itr(ecs_world_view_t(world, CanvasWrite));
EcsIterator* canvasItr = ecs_view_itr(ecs_world_view_t(world, CanvasWriteView));

EcsView* statsView = ecs_world_view_t(world, StatsUpdateView);
for (EcsIterator* itr = ecs_view_itr(statsView); ecs_view_walk(itr);) {
Expand Down Expand Up @@ -721,14 +721,14 @@ ecs_module_init(debug_stats_module) {
ecs_register_view(GlobalView);
ecs_register_view(StatsCreateView);
ecs_register_view(StatsUpdateView);
ecs_register_view(CanvasWrite);
ecs_register_view(CanvasWriteView);

ecs_register_system(DebugStatsCreateSys, ecs_view_id(StatsCreateView));
ecs_register_system(
DebugStatsUpdateSys,
ecs_view_id(GlobalView),
ecs_view_id(StatsUpdateView),
ecs_view_id(CanvasWrite));
ecs_view_id(CanvasWriteView));
}

void debug_stats_notify(DebugStatsGlobalComp* comp, const String key, const String value) {
Expand Down
31 changes: 27 additions & 4 deletions libs/debug/src/trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ typedef struct {
typedef struct {
bool enabled, picking;
u8 eventId;
DynString msgFilter;
TimeDuration threshold;
} DebugTraceTrigger;

Expand All @@ -49,6 +50,7 @@ ecs_comp_define(DebugTracePanelComp) {

static void ecs_destruct_trace_panel(void* data) {
DebugTracePanelComp* comp = data;
dynstring_destroy(&comp->trigger.msgFilter);
array_for_t(comp->threads, DebugTraceData, thread) { dynarray_destroy(&thread->events); }
}

Expand All @@ -59,7 +61,20 @@ static void trace_trigger_set(DebugTraceTrigger* t, const u8 eventId) {
}

static bool trace_trigger_match(const DebugTraceTrigger* t, const TraceStoreEvent* evt) {
return t->enabled && t->eventId == evt->id && evt->timeDur >= t->threshold;
if (!t->enabled) {
return false;
}
if (evt->id != t->eventId) {
return false;
}
if (evt->timeDur < t->threshold) {
return false;
}
if (!t->msgFilter.size) {
return true;
}
const String evtMsg = mem_create(evt->msgData, evt->msgLength);
return string_match_glob(evtMsg, dynstring_view(&t->msgFilter), StringMatchFlags_IgnoreCase);
}

static UiColor trace_event_color(const TraceColor col) {
Expand Down Expand Up @@ -130,7 +145,7 @@ static UiColor trace_trigger_button_color(const DebugTraceTrigger* t) {

static void trace_options_trigger_draw(
UiCanvasComp* c, DebugTracePanelComp* panel, const TraceSink* sinkStore) {
static const UiVector g_popupSize = {.x = 255.0f, .y = 100.0f};
static const UiVector g_popupSize = {.x = 255.0f, .y = 130.0f};

DebugTraceTrigger* t = &panel->trigger;
const UiId popupId = ui_canvas_id_peek(c);
Expand Down Expand Up @@ -172,13 +187,15 @@ static void trace_options_trigger_draw(
ui_table_next_row(c, &table);
ui_label(c, string_lit("Action"));
ui_table_next_column(c, &table);
const UiColor enabledColor = t->enabled ? ui_color(16, 192, 0, 192) : ui_color(255, 16, 0, 192);
if (t->enabled) {
if (ui_button(c, .label = string_lit("Disable"))) {
if (ui_button(c, .label = string_lit("Disable"), .frameColor = enabledColor)) {
t->enabled = false;
}
} else {
const UiWidgetFlags enableFlags = hasEvent ? UiWidget_Default : UiWidget_Disabled;
if (ui_button(c, .label = string_lit("Enable"), .flags = enableFlags)) {
if (ui_button(
c, .label = string_lit("Enable"), .flags = enableFlags, .frameColor = enabledColor)) {
t->enabled = true;
panel->freeze = false;
}
Expand All @@ -197,6 +214,11 @@ static void trace_options_trigger_draw(
ui_canvas_persistent_flags_unset(c, popupId, UiPersistentFlags_Open);
}

ui_table_next_row(c, &table);
ui_label(c, string_lit("Message"));
ui_table_next_column(c, &table);
ui_textbox(c, &t->msgFilter, .placeholder = string_lit("*"));

ui_table_next_row(c, &table);
ui_label(c, string_lit("Threshold"));
ui_table_next_column(c, &table);
Expand Down Expand Up @@ -571,6 +593,7 @@ debug_trace_panel_open(EcsWorld* world, const EcsEntityId window, const DebugPan
.timeHead = time_steady_clock(),
.timeWindow = time_milliseconds(100),
.trigger.eventId = sentinel_u8,
.trigger.msgFilter = dynstring_create(g_allocHeap, 0),
.trigger.threshold = time_milliseconds(20));

array_for_t(tracePanel->threads, DebugTraceData, thread) {
Expand Down

0 comments on commit 15e2687

Please sign in to comment.