aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoan-Sin Tan <freedom.tan@linaro.org>2014-12-30 14:17:47 +0800
committerTuukka Tikkanen <tuukka.tikkanen@linaro.org>2015-01-02 05:08:33 +0200
commit4f89e2ee90c16932606aa6278ec46b4f6295b4ba (patch)
treed72fa2a9acac1426848526793998863d380ea5c6
parentc4433ad725b6128eda8018dbc8aa1094e01f9b88 (diff)
Idlestat: Save and restore kernel ftrace options
Idlestat modifies the kernel trace options via sysfs interface every time a trace is created. The user may have prefer to have the original trace options restored once idlestat is finished with the task. This patch adds code to save and restore the trace options that might be modified by idlestat. idlestore_store_trace_options(): Saves the options by 1) record the original trace buffer size and 2) scan the whole ftrace event hierarchy, storing the paths of enabled event types into a list. idlestat_restore_trace_options(): Restores the options including buffer size and enabled events. Disables all other events. Signed-off-by: Koan-Sin Tan <freedom.tan@linaro.org> Signed-off-by: Tuukka Tikkanen <tuukka.tikkanen@linaro.org>
-rw-r--r--idlestat.c33
-rw-r--r--trace.c118
-rw-r--r--trace.h4
-rw-r--r--utils.c24
-rw-r--r--utils.h1
5 files changed, 170 insertions, 10 deletions
diff --git a/idlestat.c b/idlestat.c
index aef09a1..bcaf122 100644
--- a/idlestat.c
+++ b/idlestat.c
@@ -1578,6 +1578,7 @@ int main(int argc, char *argv[], char *const envp[])
struct init_pstates *initp = NULL;
struct report_ops *output_handler = NULL;
struct cpu_topology *cpu_topo = NULL;
+ struct trace_options *saved_trace_options = NULL;
void *report_data = NULL;
args = getoptions(argc, argv, &options);
@@ -1616,7 +1617,6 @@ int main(int argc, char *argv[], char *const envp[])
/* Acquisition time specified means we will get the traces */
if ((options.mode == TRACE) || args < argc) {
-
/* Read cpu topology info from sysfs */
cpu_topo = read_sysfs_cpu_topo();
if (is_err(cpu_topo)) {
@@ -1632,48 +1632,52 @@ int main(int argc, char *argv[], char *const envp[])
return 1;
}
+ saved_trace_options = idlestat_store_trace_options();
+ if (is_err(saved_trace_options))
+ return 1;
+
/* Initialize the traces for cpu_idle and increase the
* buffer size to let 'idlestat' to sleep instead of
* acquiring data, hence preventing it to pertubate the
* measurements. */
if (idlestat_init_trace(options.duration))
- return 1;
+ goto err_restore_trace_options;
/* Remove all the previous traces */
if (idlestat_flush_trace())
- return 1;
+ goto err_restore_trace_options;
/* Get starting timestamp */
if (get_trace_ts(&start_ts) == -1)
- return 1;
+ goto err_restore_trace_options;
initp = build_init_pstates();
/* Start the recording */
if (idlestat_trace_enable(true))
- return 1;
+ goto err_restore_trace_options;
/* We want to prevent to begin the acquisition with a cpu in
* idle state because we won't be able later to close the
* state and to determine which state it was. */
if (idlestat_wake_all())
- return 1;
+ goto err_restore_trace_options;
/* Execute the command or wait a specified delay */
if (execute(argc - args, &argv[args], envp, &options))
- return 1;
+ goto err_restore_trace_options;
/* Wake up all cpus again to account for last idle state */
if (idlestat_wake_all())
- return 1;
+ goto err_restore_trace_options;
/* Stop tracing */
if (idlestat_trace_enable(false))
- return 1;
+ goto err_restore_trace_options;
/* Get ending timestamp */
if (get_trace_ts(&end_ts) == -1)
- return 1;
+ goto err_restore_trace_options;
/* At this point we should have some spurious wake up
* at the beginning of the traces and at the end (wake
@@ -1682,6 +1686,10 @@ int main(int argc, char *argv[], char *const envp[])
* of other traces and could be negligible. */
if (idlestat_store(options.filename, start_ts, end_ts,
initp, cpu_topo))
+ goto err_restore_trace_options;
+
+ /* Restore original kernel ftrace options */
+ if (idlestat_restore_trace_options(saved_trace_options))
return 1;
/* Discard topology, will be reloaded during trace load */
@@ -1747,4 +1755,9 @@ int main(int argc, char *argv[], char *const envp[])
output_handler->release_report_data(report_data);
return 0;
+
+ err_restore_trace_options:
+ /* Restore original kernel ftrace options */
+ idlestat_restore_trace_options(saved_trace_options);
+ return 1;
}
diff --git a/trace.c b/trace.c
index 5ce2e88..9ec1c87 100644
--- a/trace.c
+++ b/trace.c
@@ -30,9 +30,43 @@
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fts.h>
#include "trace.h"
#include "utils.h"
+#include "list.h"
+
+struct trace_options {
+ int buffer_size;
+ struct list_head list;
+};
+
+struct enabled_eventtype {
+ char *name;
+ struct list_head list;
+};
+
+int idlestat_restore_trace_options(struct trace_options *options)
+{
+ struct enabled_eventtype *pos, *n;
+ int write_problem = 0;
+
+ if (write_int(TRACE_BUFFER_SIZE_PATH, options->buffer_size))
+ write_problem = -1;
+
+ list_for_each_entry_safe(pos, n, &options->list, list) {
+ if (write_int(pos->name, 1))
+ write_problem = -1;
+
+ free(pos->name);
+ list_del(&pos->list);
+ free(pos);
+ }
+ free(options);
+ return write_problem;
+}
int idlestat_trace_enable(bool enable)
{
@@ -44,6 +78,90 @@ int idlestat_flush_trace(void)
return write_int(TRACE_FILE, 0);
}
+static int events_scan(char *name, struct list_head *head)
+{
+ FTSENT *file = NULL;
+ char value;
+ struct enabled_eventtype *found_type;
+ char *paths[2];
+
+ paths[0] = name;
+ paths[1] = NULL;
+
+ FTS *fts = fts_open(paths, FTS_PHYSICAL, NULL);
+ if (!fts)
+ return error("fts_open");
+
+ while (NULL != (file = fts_read(fts))) {
+ if (file->fts_info == FTS_ERR) {
+ fprintf(stderr, "%s: %s\n", file->fts_path,
+ strerror(file->fts_errno));
+ fts_close(fts);
+ return -1;
+ }
+
+ if (strcmp(file->fts_name, "enable"))
+ continue;
+
+ if (read_char(file->fts_path, &value)) {
+ fts_close(fts);
+ return -1;
+ }
+
+ if (value != '1')
+ continue;
+
+ found_type = calloc(1, sizeof(struct enabled_eventtype));
+ if (!found_type) {
+ fts_close(fts);
+ return error(__func__);
+ }
+
+ found_type->name = strdup(file->fts_path);
+ if (!found_type->name) {
+ free(found_type);
+ return error(__func__);
+ }
+
+ list_add(&found_type->list, head);
+ }
+
+ fts_close(fts);
+ return 0;
+}
+
+#define TRACE_EVENTS_DIR TRACE_PATH "/events/"
+
+struct trace_options *idlestat_store_trace_options()
+{
+ int status;
+ struct trace_options *options;
+ struct enabled_eventtype *pos, *n;
+
+ options = calloc(1, sizeof(struct trace_options));
+ if (!options)
+ return ptrerror(__func__);
+ INIT_LIST_HEAD(&options->list);
+
+ if (read_int(TRACE_BUFFER_SIZE_PATH, &options->buffer_size))
+ goto cannot_get_event_options;
+
+ status = events_scan(TRACE_EVENTS_DIR, &options->list);
+ if (status == 0)
+ return options;
+
+cannot_get_event_options:
+ /* Failure, clean up */
+ list_for_each_entry_safe(pos, n, &options->list, list) {
+ free(pos->name);
+ list_del(&pos->list);
+ free(pos);
+ }
+
+ free(options);
+ return ptrerror(NULL);
+}
+
int idlestat_init_trace(unsigned int duration)
{
int bufsize;
diff --git a/trace.h b/trace.h
index 90b4a6a..52893ee 100644
--- a/trace.h
+++ b/trace.h
@@ -39,8 +39,12 @@
#define TRACE_CPUFREQ_NRHITS_PER_SEC 100
#define TRACE_CPUFREQ_LENGTH 196
+struct trace_options;
+
extern int idlestat_trace_enable(bool enable);
extern int idlestat_flush_trace(void);
extern int idlestat_init_trace(unsigned int duration);
+extern struct trace_options *idlestat_store_trace_options(void);
+extern int idlestat_restore_trace_options(struct trace_options *options);
#endif
diff --git a/utils.c b/utils.c
index 18ec001..9a3f09d 100644
--- a/utils.c
+++ b/utils.c
@@ -126,6 +126,30 @@ int read_int(const char *path, int *val)
return 0;
}
+int read_char(const char *path, char *val)
+{
+ FILE *f;
+ int ret;
+
+ f = fopen(path, "r");
+
+ if (!f) {
+ fprintf(stderr, "failed to open '%s': %m\n", path);
+ return -1;
+ }
+
+ ret = fscanf(f, "%c", val);
+ fclose(f);
+
+ if (ret != 1) {
+ fprintf(stderr,
+ "%s: failed to parse a char\n", path);
+ return -1;
+ }
+
+ return 0;
+}
+
int store_line(const char *line, void *data)
{
FILE *f = data;
diff --git a/utils.h b/utils.h
index dc79bcc..c657d4f 100644
--- a/utils.h
+++ b/utils.h
@@ -35,6 +35,7 @@ extern int verbose_fprintf(FILE *f, int min_level, const char *fmt, ...);
extern int write_int(const char *path, int val);
extern int read_int(const char *path, int *val);
+extern int read_char(const char *path, char *val);
extern int store_line(const char *line, void *data);
extern int file_read_value(const char *path, const char *name,
const char *format, void *value);