blob: 99423223903ed1c8990a0d2633d3eb5256de9890 [file]
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2022, Google Inc, Steven Rostedt <rostedt@goodmis.org>
*/
#include <stdlib.h>
#include <trace-cmd.h>
#include <trace-cmd-local.h>
struct filter {
struct tep_event_filter *filter;
};
struct tracecmd_filter {
struct tep_handle *tep;
struct filter **event_filters;
struct filter **event_notrace;
bool *last_printed;
int nr_cpus;
int nr_filters;
int nr_notrace;
int kernel_stacktrace_id;
int user_stacktrace_id;
};
static bool test_stacktrace(struct tracecmd_filter *filter, struct tep_record *record,
int stacktrace_id)
{
struct tep_handle *tep = filter->tep;
int id;
if (stacktrace_id < 0)
return false;
id = tep_data_type(tep, record);
if (id != stacktrace_id)
return false;
return filter->last_printed[record->cpu];
}
static bool test_stacktraces(struct tracecmd_filter *filter, struct tep_record *record)
{
return test_stacktrace(filter, record, filter->kernel_stacktrace_id) ||
test_stacktrace(filter, record, filter->user_stacktrace_id);
}
__hidden enum tracecmd_filters tracecmd_filter_match(struct tracecmd_filter *filter,
struct tep_record *record)
{
bool is_stack = false;
bool found = false;
int ret;
int i;
if (!filter)
return TRACECMD_FILTER_NONE;
/* Setup stack traces. If a event is shown, still show stack traces */
if (!filter->kernel_stacktrace_id) {
struct tep_handle *tep = filter->tep;
struct tep_event *event;
/* In case the below logic fails, do not do this again */
filter->kernel_stacktrace_id = -1;
event = tep_find_event_by_name(tep, "ftrace", "kernel_stack");
if (event)
filter->kernel_stacktrace_id = event->id;
event = tep_find_event_by_name(tep, "ftrace", "user_stack");
if (event)
filter->user_stacktrace_id = event->id;
filter->nr_cpus = tep_get_cpus(tep);
filter->last_printed = calloc(filter->nr_cpus, sizeof(*filter->last_printed));
if (!filter->last_printed) {
tracecmd_warning("Could not allocate last_printed array for stack trace filtering");
filter->kernel_stacktrace_id = -1;
filter->user_stacktrace_id = -1;
}
}
for (i = 0; i < filter->nr_filters; i++) {
ret = tep_filter_match(filter->event_filters[i]->filter, record);
switch (ret) {
case TRACECMD_FILTER_NONE:
case TRACECMD_FILTER_MATCH:
found = true;
}
if (found)
break;
}
if (!found && filter->nr_filters) {
/* If this is a stack trace and the last event was printed continue */
if (!test_stacktraces(filter, record))
return TRACECMD_FILTER_MISS;
is_stack = true;
}
found = false;
/* We need to test all negative filters */
for (i = 0; i < filter->nr_notrace; i++) {
ret = tep_filter_match(filter->event_notrace[i]->filter, record);
switch (ret) {
case TRACECMD_FILTER_NONE:
case TRACECMD_FILTER_MATCH:
found = true;
}
if (found)
break;
}
if (filter->last_printed)
filter->last_printed[record->cpu] = !is_stack && !found;
return found ? TRACECMD_FILTER_MISS : TRACECMD_FILTER_MATCH;
}
struct tracecmd_filter *tracecmd_filter_add(struct tracecmd_input *handle,
const char *filter_str, bool neg)
{
struct tracecmd_filter *trace_filter;
struct tep_handle *tep;
struct filter ***filter_ptr;
struct filter **filters;
struct filter *filter;
int *nr;
int ret;
filter = calloc(1, sizeof(*filter));
if (!filter)
return NULL;
tep = tracecmd_get_tep(handle);
trace_filter = tracecmd_filter_get(handle);
if (!trace_filter) {
trace_filter = calloc(1, sizeof(*trace_filter));
if (!trace_filter)
goto fail;
tracecmd_filter_set(handle, trace_filter);
trace_filter->tep = tep;
}
filter->filter = tep_filter_alloc(tep);
if (!filter->filter)
goto fail;
ret = tep_filter_add_filter_str(filter->filter, filter_str);
if (ret < 0)
goto fail;
if (neg) {
filter_ptr = &trace_filter->event_notrace;
nr = &trace_filter->nr_notrace;
} else {
filter_ptr = &trace_filter->event_filters;
nr = &trace_filter->nr_filters;
}
filters = realloc(*filter_ptr, sizeof(*filters) * (*nr + 1));
if (!filters)
goto fail;
*filter_ptr = filters;
filters[*nr] = filter;
(*nr)++;
return trace_filter;
fail:
if (filter) {
tep_filter_free(filter->filter);
free(filter);
}
return NULL;
}
static void free_filters (struct filter **filter, int nr)
{
int i;
for (i = 0; i < nr; i++) {
tep_filter_free(filter[i]->filter);
free(filter[i]);
}
free(filter);
}
__hidden void tracecmd_filter_free(struct tracecmd_filter *trace_filter)
{
if (!trace_filter)
return;
free_filters(trace_filter->event_filters, trace_filter->nr_filters);
free_filters(trace_filter->event_notrace, trace_filter->nr_notrace);
free(trace_filter);
}