Currently, the auto-analysis is attached to the timerlat top instance.
The idea was to avoid creating another instance just for that, so one
instance could be reused.
The drawback is that, by doing so, the auto-analysis run for the entire
session, consuming CPU time. On my 24 box CPUs for timerlat with a 100
us period consumed 50 % with auto analysis, but only 16 % without.
By creating an instance for auto-analysis, we can keep the processing
stopped until a stop tracing condition is hit. Once it happens,
timerlat auto-analysis can use its own trace instance to parse only
the end of the trace.
By doing so, auto-analysis stop consuming cpu time when it is not
needed.
If the --aa-only is passed, the timerlat top instance is reused for
auto analysis.
Link: https://lkml.kernel.org/r/346b7168c1bae552a415715ec6d23c129a43bdb7.1686066600.git.bristot@kernel.org
Cc: William White <chwhite@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Tested-by: Juri Lelli <juri.lelli@redhat.com>
Signed-off-by: Daniel Bristot de Oliveira <bristot@kernel.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
#include "utils.h"
#include "osnoise.h"
#include "timerlat.h"
+#include <unistd.h>
enum timelat_state {
TIMERLAT_INIT = 0,
*
* Returns 0 on success, -1 otherwise.
*/
-int timerlat_aa_handler(struct trace_seq *s, struct tep_record *record,
+static int timerlat_aa_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
ns_to_usf(total));
}
+static int timerlat_auto_analysis_collect_trace(struct timerlat_aa_context *taa_ctx)
+{
+ struct trace_instance *trace = &taa_ctx->tool->trace;
+ int retval;
+
+ retval = tracefs_iterate_raw_events(trace->tep,
+ trace->inst,
+ NULL,
+ 0,
+ collect_registered_events,
+ trace);
+ if (retval < 0) {
+ err_msg("Error iterating on events\n");
+ return 0;
+ }
+
+ return 1;
+}
+
/**
* timerlat_auto_analysis - Analyze the collected data
*/
struct tep_handle *tep;
int cpu;
+ timerlat_auto_analysis_collect_trace(taa_ctx);
+
/* bring stop tracing to the ns scale */
irq_thresh = irq_thresh * 1000;
thread_thresh = thread_thresh * 1000;
*/
static void timerlat_aa_unregister_events(struct osnoise_tool *tool, int dump_tasks)
{
+
+ tep_unregister_event_handler(tool->trace.tep, -1, "ftrace", "timerlat",
+ timerlat_aa_handler, tool);
+
tracefs_event_disable(tool->trace.inst, "osnoise", NULL);
tep_unregister_event_handler(tool->trace.tep, -1, "osnoise", "nmi_noise",
{
int retval;
+ tep_register_event_handler(tool->trace.tep, -1, "ftrace", "timerlat",
+ timerlat_aa_handler, tool);
+
+
/*
* register auto-analysis handlers.
*/
*
* Returns 0 on success, -1 otherwise.
*/
-int timerlat_aa_init(struct osnoise_tool *tool, int nr_cpus, int dump_tasks)
+int timerlat_aa_init(struct osnoise_tool *tool, int dump_tasks)
{
+ int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
struct timerlat_aa_context *taa_ctx;
int retval;
* Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
*/
-int timerlat_aa_init(struct osnoise_tool *tool, int nr_cpus, int dump_task);
+int timerlat_aa_init(struct osnoise_tool *tool, int dump_task);
void timerlat_aa_destroy(void);
-int timerlat_aa_handler(struct trace_seq *s, struct tep_record *record,
- struct tep_event *event, void *context);
-
void timerlat_auto_analysis(int irq_thresh, int thread_thresh);
timerlat_top_update(top, cpu, thread, latency);
}
- if (!params->no_aa)
- timerlat_aa_handler(s, record, event, context);
-
return 0;
}
{
struct osnoise_tool *top;
int nr_cpus;
- int retval;
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
tep_register_event_handler(top->trace.tep, -1, "ftrace", "timerlat",
timerlat_top_handler, top);
- /*
- * If no auto analysis, we are ready.
- */
- if (params->no_aa)
- return top;
-
- retval = timerlat_aa_init(top, nr_cpus, params->dump_tasks);
- if (retval)
- goto out_err;
-
return top;
out_err:
struct timerlat_top_params *params;
struct osnoise_tool *record = NULL;
struct osnoise_tool *top = NULL;
+ struct osnoise_tool *aa = NULL;
struct trace_instance *trace;
int dma_latency_fd = -1;
int return_value = 1;
trace_instance_start(&record->trace);
}
+ if (!params->no_aa) {
+ if (params->aa_only) {
+ /* as top is not used for display, use it for aa */
+ aa = top;
+ } else {
+ /* otherwise, a new instance is needed */
+ aa = osnoise_init_tool("timerlat_aa");
+ if (!aa)
+ goto out_top;
+ }
+
+ retval = timerlat_aa_init(aa, params->dump_tasks);
+ if (retval) {
+ err_msg("Failed to enable the auto analysis instance\n");
+ goto out_top;
+ }
+
+ /* if it is re-using the main instance, there is no need to start it */
+ if (aa != top) {
+ retval = enable_timerlat(&aa->trace);
+ if (retval) {
+ err_msg("Failed to enable timerlat tracer\n");
+ goto out_top;
+ }
+
+ trace_instance_start(&aa->trace);
+ }
+ }
+
top->start_time = time(NULL);
timerlat_top_set_signals(params);
}
out_top:
+ timerlat_aa_destroy();
if (dma_latency_fd >= 0)
close(dma_latency_fd);
trace_events_destroy(&record->trace, params->events);
params->events = NULL;
out_free:
timerlat_free_top(top->data);
- timerlat_aa_destroy();
+ if (aa && aa != top)
+ osnoise_destroy_tool(aa);
osnoise_destroy_tool(record);
osnoise_destroy_tool(top);
free(params);