@@ -3,7 +3,8 @@ libtracefs(3)
NAME
----
-tracefs_iterate_raw_events, tracefs_iterate_stop, tracefs_follow_event, tracefs_follow_missed_events - Iterate over events in the ring buffer
+tracefs_iterate_raw_events, tracefs_iterate_stop, tracefs_follow_event, tracefs_follow_missed_events,
+tracefs_follow_event_clear, tracefs_follow_missed_events_clear - Iterate over events in the ring buffer
SYNOPSIS
--------
@@ -28,6 +29,10 @@ int *tracefs_follow_missed_events*(struct tracefs_instance pass:[*]_instance_,
struct tep_record pass:[*],
int, void pass:[*]),
void pass:[*]_callback_data_);
+
+int *tracefs_follow_event_clear*(struct tracefs_instance pass:[*]_instance_,
+ const char pass:[*]_system_, const char pass:[*]_event_name_);
+int *tracefs_follow_missed_events_clear*(struct tracefs_instance pass:[*]_instance_);
--
DESCRIPTION
@@ -68,11 +73,24 @@ record that came after the missed events and _event_ will be of the type of
event _record_ is. _cpu_ will be set to the CPU that missed the events, and
_callback_data_ will be the content that was passed in to the function.
+The *tracefs_follow_event_clear()* will remove followers from _instance_ that
+match _system_ and _event_name_. If _system_ and _event_name_ are both NULL,
+then it will remove all event followers associated to _instance_. If just _system_
+is NULL, then it will remove all followers that follow events that match _event_name_. If just _event_name_
+is NULL, then it will remove all followers that are attached to events that are
+apart of a system that matches _system_.
+
+The *tracefs_follow_missed_events_clear()* will remove all followers for missed
+events.
+
RETURN VALUE
------------
The *tracefs_iterate_raw_events()* function returns -1 in case of an error or
0 otherwise.
+Both *tracefs_follow_event_clear()* and *tracefs_follow_missed_events_clear()* return
+0 on success and -1 on error, or if it found no followers that match and should be removed.
+
EXAMPLE
-------
[source,c]
@@ -176,7 +194,17 @@ int main (int argc, char **argv, char **env)
tracefs_follow_missed_events(instance, missed_callback, NULL);
tracefs_follow_event(tep, instance, "sched", "sched_switch", sched_callback, &this_pid);
tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, &my_data);
+
+ /* Note, the clear here is to show how to clear all followers
+ * in case tracefs_iterate_raw_events() is called again, but
+ * does not want to include the followers. It's not needed
+ * here because tracefs_instance_free() will clean them up.
+ */
+ tracefs_follow_event_clear(instance, NULL, NULL);
+ tracefs_follow_missed_events_clear(instance);
+
tracefs_instance_destroy(instance);
+ tracefs_instance_free(instance);
if (my_data.stopped) {
if (counter > MAX_COUNT)
@@ -138,6 +138,10 @@ int tracefs_follow_missed_events(struct tracefs_instance *instance,
struct tep_record *,
int, void *),
void *callback_data);
+int tracefs_follow_event_clear(struct tracefs_instance *instance,
+ const char *system, const char *event_name);
+int tracefs_follow_missed_events_clear(struct tracefs_instance *instance);
+
char *tracefs_event_get_file(struct tracefs_instance *instance,
const char *system, const char *event,
@@ -392,6 +392,109 @@ int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instan
return 0;
}
+/**
+ * tracefs_follow_event_clear - Remove callbacks for specific events for iterators
+ * @instance: The instance to follow
+ * @system: The system of the event to remove (NULL for all)
+ * @event_name: The name of the event to remove (NULL for all)
+ *
+ * This removes all callbacks from an instance that matches a specific
+ * event. If @event_name is NULL, then it removes all followers that match
+ * @system. If @system is NULL, then it removes all followers that match
+ * @event_name. If both @system and @event_name are NULL then it removes all
+ * followers for all events.
+ *
+ * Returns 0 on success and -1 on error (which includes no followers found)
+ */
+int tracefs_follow_event_clear(struct tracefs_instance *instance,
+ const char *system, const char *event_name)
+{
+ struct follow_event **followers;
+ struct follow_event *follower;
+ int *nr_followers;
+ int nr;
+ int i, n;
+
+ if (instance) {
+ followers = &instance->followers;
+ nr_followers = &instance->nr_followers;
+ } else {
+ followers = &root_followers;
+ nr_followers = &nr_root_followers;
+ }
+
+ if (!*nr_followers)
+ return -1;
+
+ /* If both system and event_name are NULL just remove all */
+ if (!system && !event_name) {
+ free(*followers);
+ *followers = NULL;
+ *nr_followers = 0;
+ return 0;
+ }
+
+ nr = *nr_followers;
+ follower = *followers;
+
+ for (i = 0, n = 0; i < nr; i++) {
+ if (event_name && strcmp(event_name, follower[n].event->name) != 0) {
+ n++;
+ continue;
+ }
+ if (system && strcmp(system, follower[n].event->system) != 0) {
+ n++;
+ continue;
+ }
+ /* If there are no more after this, continue to increment i */
+ if (i == nr - 1)
+ continue;
+ /* Remove this follower */
+ memmove(&follower[n], &follower[n + 1],
+ sizeof(*follower) * (nr - (n + 1)));
+ }
+
+ /* Did we find anything? */
+ if (n == i)
+ return -1;
+
+ /* NULL out the rest */
+ memset(&follower[n], 0, (sizeof(*follower)) * (nr - n));
+ *nr_followers = n;
+
+ return 0;
+}
+
+/**
+ * tracefs_follow_missed_events_clear - Remove callbacks for missed events
+ * @instance: The instance to remove missed callback followers
+ *
+ * This removes all callbacks from an instance that are for missed events.
+ *
+ * Returns 0 on success and -1 on error (which includes no followers found)
+ */
+int tracefs_follow_missed_events_clear(struct tracefs_instance *instance)
+{
+ struct follow_event **followers;
+ int *nr_followers;
+
+ if (instance) {
+ followers = &instance->missed_followers;
+ nr_followers = &instance->nr_missed_followers;
+ } else {
+ followers = &root_missed_followers;
+ nr_followers = &nr_root_missed_followers;
+ }
+
+ if (!*nr_followers)
+ return -1;
+
+ free(*followers);
+ *followers = NULL;
+ *nr_followers = 0;
+ return 0;
+}
+
static bool top_iterate_keep_going;
/*
@@ -63,6 +63,16 @@ static struct test_sample test_array[TEST_ARRAY_SIZE];
static int test_found;
static unsigned long long last_ts;
+static void msleep(int ms)
+{
+ struct timespec tspec;
+
+ /* Sleep for 1ms */
+ tspec.tv_sec = 0;
+ tspec.tv_nsec = 1000000 * ms;
+ nanosleep(&tspec, NULL);
+}
+
static int test_callback(struct tep_event *event, struct tep_record *record,
int cpu, void *context)
{
@@ -608,10 +618,23 @@ static void test_trace_cpu_read(void)
struct follow_data {
struct tep_event *sched_switch;
struct tep_event *sched_waking;
+ struct tep_event *getppid;
struct tep_event *function;
int missed;
+ int switch_hit;
+ int waking_hit;
+ int getppid_hit;
+ int missed_hit;
};
+static void clear_hits(struct follow_data *fdata)
+{
+ fdata->switch_hit = 0;
+ fdata->waking_hit = 0;
+ fdata->getppid_hit = 0;
+ fdata->missed_hit = 0;
+}
+
static int switch_callback(struct tep_event *event, struct tep_record *record,
int cpu, void *data)
{
@@ -619,6 +642,7 @@ static int switch_callback(struct tep_event *event, struct tep_record *record,
CU_TEST(cpu == record->cpu);
CU_TEST(event->id == fdata->sched_switch->id);
+ fdata->switch_hit++;
return 0;
}
@@ -629,6 +653,18 @@ static int waking_callback(struct tep_event *event, struct tep_record *record,
CU_TEST(cpu == record->cpu);
CU_TEST(event->id == fdata->sched_waking->id);
+ fdata->waking_hit++;
+ return 0;
+}
+
+static int getppid_callback(struct tep_event *event, struct tep_record *record,
+ int cpu, void *data)
+{
+ struct follow_data *fdata = data;
+
+ CU_TEST(cpu == record->cpu);
+ CU_TEST(event->id == fdata->getppid->id);
+ fdata->getppid_hit++;
return 0;
}
@@ -648,6 +684,7 @@ static int missed_callback(struct tep_event *event, struct tep_record *record,
struct follow_data *fdata = data;
fdata->missed = record->missed_events;
+ fdata->missed_hit++;
return 0;
}
@@ -725,6 +762,11 @@ static void test_instance_follow_events(struct tracefs_instance *instance)
ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, all_callback, &fdata);
CU_TEST(ret == 0);
+ ret = tracefs_follow_event_clear(instance, NULL, NULL);
+ CU_TEST(ret == 0);
+ ret = tracefs_follow_missed_events_clear(instance);
+ CU_TEST(ret == 0);
+
pthread_join(thread, NULL);
tracefs_tracer_clear(instance);
@@ -737,6 +779,193 @@ static void test_follow_events(void)
test_instance_follow_events(test_instance);
}
+static void test_instance_follow_events_clear(struct tracefs_instance *instance)
+{
+ struct follow_data fdata;
+ struct tep_handle *tep;
+ char **list;
+ int ret;
+
+ memset(&fdata, 0, sizeof(fdata));
+
+ tep = test_tep;
+
+ fdata.sched_switch = tep_find_event_by_name(tep, "sched", "sched_switch");
+ CU_TEST(fdata.sched_switch != NULL);
+ if (!fdata.sched_switch)
+ return;
+
+ fdata.sched_waking = tep_find_event_by_name(tep, "sched", "sched_waking");
+ CU_TEST(fdata.sched_waking != NULL);
+ if (!fdata.sched_waking)
+ return;
+
+ fdata.getppid = tep_find_event_by_name(tep, EVENT_SYSTEM, EVENT_NAME);
+ CU_TEST(fdata.getppid != NULL);
+ if (!fdata.getppid)
+ return;
+
+ ret = tracefs_follow_event(tep, instance, "sched", "sched_switch",
+ switch_callback, &fdata);
+ CU_TEST(ret == 0);
+
+ ret = tracefs_follow_event(tep, instance, "sched", "sched_waking",
+ waking_callback, &fdata);
+ CU_TEST(ret == 0);
+
+ ret = tracefs_follow_event(tep, instance, EVENT_SYSTEM, EVENT_NAME,
+ getppid_callback, &fdata);
+ CU_TEST(ret == 0);
+
+ ret = tracefs_follow_missed_events(instance, missed_callback, &fdata);
+ CU_TEST(ret == 0);
+
+ ret = tracefs_event_enable(instance, "sched", "sched_switch");
+ CU_TEST(ret == 0);
+
+ ret = tracefs_event_enable(instance, "sched", "sched_waking");
+ CU_TEST(ret == 0);
+
+ ret = tracefs_event_enable(instance, EVENT_SYSTEM, EVENT_NAME);
+ CU_TEST(ret == 0);
+
+ tracefs_trace_on(instance);
+ call_getppid(100);
+ msleep(100);
+ tracefs_trace_off(instance);
+
+ ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, &fdata);
+ CU_TEST(ret == 0);
+
+ /* Make sure all are hit */
+ CU_TEST(fdata.switch_hit > 0);
+ CU_TEST(fdata.waking_hit > 0);
+ CU_TEST(fdata.getppid_hit == 100);
+ /* No missed events */
+ CU_TEST(fdata.missed_hit == 0);
+ clear_hits(&fdata);
+
+
+
+ /* Disable getppid and do the same thing */
+ ret = tracefs_follow_event_clear(instance, EVENT_SYSTEM, EVENT_NAME);
+ CU_TEST(ret == 0);
+
+ tracefs_trace_on(instance);
+ call_getppid(100);
+ msleep(100);
+ tracefs_trace_off(instance);
+
+ ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, &fdata);
+ CU_TEST(ret == 0);
+
+ /* All but getppid should be hit */
+ CU_TEST(fdata.switch_hit > 0);
+ CU_TEST(fdata.waking_hit > 0);
+ CU_TEST(fdata.getppid_hit == 0);
+ /* No missed events */
+ CU_TEST(fdata.missed_hit == 0);
+ clear_hits(&fdata);
+
+
+
+ /* Add function and remove sched */
+ ret = tracefs_follow_event(tep, instance, "ftrace", "function",
+ function_callback, &fdata);
+ CU_TEST(ret == 0);
+ ret = tracefs_follow_event_clear(instance, "sched", NULL);
+ CU_TEST(ret == 0);
+
+ tracefs_trace_on(instance);
+ call_getppid(100);
+ msleep(100);
+ tracefs_trace_off(instance);
+
+ ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, &fdata);
+ CU_TEST(ret == 0);
+
+ /* Nothing should have been hit */
+ CU_TEST(fdata.switch_hit == 0);
+ CU_TEST(fdata.waking_hit == 0);
+ CU_TEST(fdata.getppid_hit == 0);
+ /* No missed events */
+ CU_TEST(fdata.missed_hit == 0);
+ clear_hits(&fdata);
+
+
+ /* Enable function tracing and see if we missed hits */
+ ret = tracefs_tracer_set(instance, TRACEFS_TRACER_FUNCTION);
+ CU_TEST(ret == 0);
+
+ fdata.function = tep_find_event_by_name(tep, "ftrace", "function");
+ CU_TEST(fdata.function != NULL);
+ if (!fdata.function)
+ return;
+
+ tracefs_trace_on(instance);
+ call_getppid(100);
+ /* Stir the kernel a bit */
+ list = tracefs_event_systems(NULL);
+ tracefs_list_free(list);
+ sleep(1);
+ tracefs_trace_off(instance);
+
+ ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, &fdata);
+ CU_TEST(ret == 0);
+
+ /* Nothing should have been hit */
+ CU_TEST(fdata.switch_hit == 0);
+ CU_TEST(fdata.waking_hit == 0);
+ CU_TEST(fdata.getppid_hit == 0);
+ /* We should have missed events! */
+ CU_TEST(fdata.missed_hit > 0);
+ clear_hits(&fdata);
+
+
+ /* Now remove missed events follower */
+ ret = tracefs_follow_missed_events_clear(instance);
+ CU_TEST(ret == 0);
+
+ tracefs_trace_on(instance);
+ call_getppid(100);
+ sleep(1);
+ tracefs_trace_off(instance);
+
+ ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, &fdata);
+ CU_TEST(ret == 0);
+
+ /* Nothing should have been hit */
+ CU_TEST(fdata.switch_hit == 0);
+ CU_TEST(fdata.waking_hit == 0);
+ CU_TEST(fdata.getppid_hit == 0);
+ /* No missed events either */
+ CU_TEST(fdata.missed_hit == 0);
+ clear_hits(&fdata);
+
+ /* Turn everything off */
+ tracefs_tracer_clear(instance);
+ tracefs_event_disable(instance, NULL, NULL);
+
+ tracefs_trace_on(instance);
+
+ /* Clear the function follower */
+ ret = tracefs_follow_event_clear(instance, NULL, "function");
+
+ /* Should not have any more followers */
+ ret = tracefs_follow_event_clear(instance, NULL, NULL);
+ CU_TEST(ret != 0);
+
+ /* Nor missed event followers */
+ ret = tracefs_follow_missed_events_clear(instance);
+ CU_TEST(ret != 0);
+}
+
+static void test_follow_events_clear(void)
+{
+ test_instance_follow_events_clear(NULL);
+ test_instance_follow_events_clear(test_instance);
+}
+
extern char *find_tracing_dir(bool debugfs, bool mount);
static void test_mounting(void)
{
@@ -2570,6 +2799,7 @@ void test_tracefs_lib(void)
/* Follow events test must be after the iterate raw events above */
CU_add_test(suite, "Follow events", test_follow_events);
+ CU_add_test(suite, "Follow events clear", test_follow_events_clear);
CU_add_test(suite, "tracefs_tracers API",
test_tracers);