* [PATCH v3 1/3] perf bench: add kallsyms parsing
2020-05-05 14:36 [PATCH v3 0/3] Improvements for kallsyms__parse Ian Rogers
@ 2020-05-05 14:36 ` Ian Rogers
2020-05-05 14:36 ` [PATCH v3 2/3] lib kallsyms: parse using io api Ian Rogers
2020-05-05 14:36 ` [PATCH v3 3/3] lib kallsyms: move hex2u64 out of header Ian Rogers
2 siblings, 0 replies; 4+ messages in thread
From: Ian Rogers @ 2020-05-05 14:36 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Namhyung Kim,
Thomas Gleixner, linux-kernel
Cc: Stephane Eranian, Ian Rogers
Add a benchmark for kallsyms parsing. Example output:
Running 'internals/kallsyms-parse' benchmark:
Average kallsyms__parse took: 103.971 ms (+- 0.121 ms)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/bench/Build | 1 +
tools/perf/bench/bench.h | 1 +
tools/perf/bench/kallsyms-parse.c | 75 +++++++++++++++++++++++++++++++
tools/perf/builtin-bench.c | 1 +
4 files changed, 78 insertions(+)
create mode 100644 tools/perf/bench/kallsyms-parse.c
diff --git a/tools/perf/bench/Build b/tools/perf/bench/Build
index 042827385c87..768e408757a0 100644
--- a/tools/perf/bench/Build
+++ b/tools/perf/bench/Build
@@ -9,6 +9,7 @@ perf-y += futex-lock-pi.o
perf-y += epoll-wait.o
perf-y += epoll-ctl.o
perf-y += synthesize.o
+perf-y += kallsyms-parse.o
perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-lib.o
perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
index 4d669c803237..61cae4966cae 100644
--- a/tools/perf/bench/bench.h
+++ b/tools/perf/bench/bench.h
@@ -44,6 +44,7 @@ int bench_futex_lock_pi(int argc, const char **argv);
int bench_epoll_wait(int argc, const char **argv);
int bench_epoll_ctl(int argc, const char **argv);
int bench_synthesize(int argc, const char **argv);
+int bench_kallsyms_parse(int argc, const char **argv);
#define BENCH_FORMAT_DEFAULT_STR "default"
#define BENCH_FORMAT_DEFAULT 0
diff --git a/tools/perf/bench/kallsyms-parse.c b/tools/perf/bench/kallsyms-parse.c
new file mode 100644
index 000000000000..2b0d0f980ae9
--- /dev/null
+++ b/tools/perf/bench/kallsyms-parse.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Benchmark of /proc/kallsyms parsing.
+ *
+ * Copyright 2020 Google LLC.
+ */
+#include <stdlib.h>
+#include "bench.h"
+#include "../util/stat.h"
+#include <linux/time64.h>
+#include <subcmd/parse-options.h>
+#include <symbol/kallsyms.h>
+
+static unsigned int iterations = 100;
+
+static const struct option options[] = {
+ OPT_UINTEGER('i', "iterations", &iterations,
+ "Number of iterations used to compute average"),
+ OPT_END()
+};
+
+static const char *const bench_usage[] = {
+ "perf bench internals kallsyms-parse <options>",
+ NULL
+};
+
+static int bench_process_symbol(void *arg __maybe_unused,
+ const char *name __maybe_unused,
+ char type __maybe_unused,
+ u64 start __maybe_unused)
+{
+ return 0;
+}
+
+static int do_kallsyms_parse(void)
+{
+ struct timeval start, end, diff;
+ u64 runtime_us;
+ unsigned int i;
+ double time_average, time_stddev;
+ int err;
+ struct stats time_stats;
+
+ init_stats(&time_stats);
+
+ for (i = 0; i < iterations; i++) {
+ gettimeofday(&start, NULL);
+ err = kallsyms__parse("/proc/kallsyms", NULL,
+ bench_process_symbol);
+ if (err)
+ return err;
+
+ gettimeofday(&end, NULL);
+ timersub(&end, &start, &diff);
+ runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&time_stats, runtime_us);
+ }
+
+ time_average = avg_stats(&time_stats) / USEC_PER_MSEC;
+ time_stddev = stddev_stats(&time_stats) / USEC_PER_MSEC;
+ printf(" Average kallsyms__parse took: %.3f ms (+- %.3f ms)\n",
+ time_average, time_stddev);
+ return 0;
+}
+
+int bench_kallsyms_parse(int argc, const char **argv)
+{
+ argc = parse_options(argc, argv, options, bench_usage, 0);
+ if (argc) {
+ usage_with_options(bench_usage, options);
+ exit(EXIT_FAILURE);
+ }
+
+ return do_kallsyms_parse();
+}
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index 11c79a8d85d6..083273209c88 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -78,6 +78,7 @@ static struct bench epoll_benchmarks[] = {
static struct bench internals_benchmarks[] = {
{ "synthesize", "Benchmark perf event synthesis", bench_synthesize },
+ { "kallsyms-parse", "Benchmark kallsyms parsing", bench_kallsyms_parse },
{ NULL, NULL, NULL }
};
--
2.26.2.526.g744177e7f7-goog
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v3 2/3] lib kallsyms: parse using io api
2020-05-05 14:36 [PATCH v3 0/3] Improvements for kallsyms__parse Ian Rogers
2020-05-05 14:36 ` [PATCH v3 1/3] perf bench: add kallsyms parsing Ian Rogers
@ 2020-05-05 14:36 ` Ian Rogers
2020-05-05 14:36 ` [PATCH v3 3/3] lib kallsyms: move hex2u64 out of header Ian Rogers
2 siblings, 0 replies; 4+ messages in thread
From: Ian Rogers @ 2020-05-05 14:36 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Namhyung Kim,
Thomas Gleixner, linux-kernel
Cc: Stephane Eranian, Ian Rogers
Perf record will call kallsyms__parse 4 times during startup and process
megabytes of data. This changes kallsyms__parse to use the io library
rather than fgets to improve performance of the user code by over 8%.
Before:
Running 'internals/kallsyms-parse' benchmark:
Average kallsyms__parse took: 103.988 ms (+- 0.203 ms)
After:
Running 'internals/kallsyms-parse' benchmark:
Average kallsyms__parse took: 95.571 ms (+- 0.006 ms)
For a workload like:
$ perf record /bin/true
Run under 'perf record -e cycles:u -g' the time goes from:
Before
30.10% 1.67% perf perf [.] kallsyms__parse
After
25.55% 20.04% perf perf [.] kallsyms__parse
So a little under 5% of the start-up time is removed. A lot of what
remains is on the kernel side, but caching kallsyms within perf would
at least impact memory footprint.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/lib/api/io.h | 3 ++
tools/lib/symbol/kallsyms.c | 81 +++++++++++++++++++------------------
2 files changed, 45 insertions(+), 39 deletions(-)
diff --git a/tools/lib/api/io.h b/tools/lib/api/io.h
index b7e55b5f8a4a..777c20f6b604 100644
--- a/tools/lib/api/io.h
+++ b/tools/lib/api/io.h
@@ -7,6 +7,9 @@
#ifndef __API_IO__
#define __API_IO__
+#include <stdlib.h>
+#include <unistd.h>
+
struct io {
/* File descriptor being read/ */
int fd;
diff --git a/tools/lib/symbol/kallsyms.c b/tools/lib/symbol/kallsyms.c
index 1a7a9f877095..1f2f519a4e3f 100644
--- a/tools/lib/symbol/kallsyms.c
+++ b/tools/lib/symbol/kallsyms.c
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include "symbol/kallsyms.h"
+#include "api/io.h"
#include <stdio.h>
-#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
u8 kallsyms2elf_type(char type)
{
@@ -28,61 +30,62 @@ int hex2u64(const char *ptr, u64 *long_val)
return p - ptr;
}
+static void read_to_eol(struct io *io)
+{
+ int ch;
+
+ for (;;) {
+ ch = io__get_char(io);
+ if (ch < 0 || ch == '\n')
+ return;
+ }
+}
+
int kallsyms__parse(const char *filename, void *arg,
int (*process_symbol)(void *arg, const char *name,
char type, u64 start))
{
- char *line = NULL;
- size_t n;
- int err = -1;
- FILE *file = fopen(filename, "r");
+ struct io io;
+ char bf[BUFSIZ];
+ int err;
- if (file == NULL)
- goto out_failure;
+ io.fd = open(filename, O_RDONLY, 0);
- err = 0;
-
- while (!feof(file)) {
- u64 start;
- int line_len, len;
- char symbol_type;
- char *symbol_name;
-
- line_len = getline(&line, &n, file);
- if (line_len < 0 || !line)
- break;
+ if (io.fd < 0)
+ return -1;
- line[--line_len] = '\0'; /* \n */
+ io__init(&io, io.fd, bf, sizeof(bf));
- len = hex2u64(line, &start);
+ err = 0;
+ while (!io.eof) {
+ __u64 start;
+ int ch;
+ size_t i;
+ char symbol_type;
+ char symbol_name[KSYM_NAME_LEN + 1];
- /* Skip the line if we failed to parse the address. */
- if (!len)
+ if (io__get_hex(&io, &start) != ' ') {
+ read_to_eol(&io);
continue;
-
- len++;
- if (len + 2 >= line_len)
+ }
+ symbol_type = io__get_char(&io);
+ if (io__get_char(&io) != ' ') {
+ read_to_eol(&io);
continue;
-
- symbol_type = line[len];
- len += 2;
- symbol_name = line + len;
- len = line_len - len;
-
- if (len >= KSYM_NAME_LEN) {
- err = -1;
- break;
}
+ for (i = 0; i < sizeof(symbol_name); i++) {
+ ch = io__get_char(&io);
+ if (ch < 0 || ch == '\n')
+ break;
+ symbol_name[i] = ch;
+ }
+ symbol_name[i] = '\0';
err = process_symbol(arg, symbol_name, symbol_type, start);
if (err)
break;
}
- free(line);
- fclose(file);
+ close(io.fd);
return err;
-
-out_failure:
- return -1;
}
--
2.26.2.526.g744177e7f7-goog
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v3 3/3] lib kallsyms: move hex2u64 out of header
2020-05-05 14:36 [PATCH v3 0/3] Improvements for kallsyms__parse Ian Rogers
2020-05-05 14:36 ` [PATCH v3 1/3] perf bench: add kallsyms parsing Ian Rogers
2020-05-05 14:36 ` [PATCH v3 2/3] lib kallsyms: parse using io api Ian Rogers
@ 2020-05-05 14:36 ` Ian Rogers
2 siblings, 0 replies; 4+ messages in thread
From: Ian Rogers @ 2020-05-05 14:36 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Namhyung Kim,
Thomas Gleixner, linux-kernel
Cc: Stephane Eranian, Ian Rogers
hex2u64 is a helper that's out of place in kallsyms.h as not being kallsyms
related. Move from kallsyms.h to the only user.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/lib/symbol/kallsyms.c | 13 -------------
tools/lib/symbol/kallsyms.h | 2 --
tools/perf/util/symbol.c | 14 ++++++++++++++
3 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/tools/lib/symbol/kallsyms.c b/tools/lib/symbol/kallsyms.c
index 1f2f519a4e3f..e335ac2b9e19 100644
--- a/tools/lib/symbol/kallsyms.c
+++ b/tools/lib/symbol/kallsyms.c
@@ -17,19 +17,6 @@ bool kallsyms__is_function(char symbol_type)
return symbol_type == 'T' || symbol_type == 'W';
}
-/*
- * While we find nice hex chars, build a long_val.
- * Return number of chars processed.
- */
-int hex2u64(const char *ptr, u64 *long_val)
-{
- char *p;
-
- *long_val = strtoull(ptr, &p, 16);
-
- return p - ptr;
-}
-
static void read_to_eol(struct io *io)
{
int ch;
diff --git a/tools/lib/symbol/kallsyms.h b/tools/lib/symbol/kallsyms.h
index bd988f7b18d4..72ab9870454b 100644
--- a/tools/lib/symbol/kallsyms.h
+++ b/tools/lib/symbol/kallsyms.h
@@ -18,8 +18,6 @@ static inline u8 kallsyms2elf_binding(char type)
return isupper(type) ? STB_GLOBAL : STB_LOCAL;
}
-int hex2u64(const char *ptr, u64 *long_val);
-
u8 kallsyms2elf_type(char type);
bool kallsyms__is_function(char symbol_type);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 8f4300492dc7..381da6b39f89 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -566,6 +566,20 @@ void dso__sort_by_name(struct dso *dso)
return symbols__sort_by_name(&dso->symbol_names, &dso->symbols);
}
+/*
+ * While we find nice hex chars, build a long_val.
+ * Return number of chars processed.
+ */
+static int hex2u64(const char *ptr, u64 *long_val)
+{
+ char *p;
+
+ *long_val = strtoull(ptr, &p, 16);
+
+ return p - ptr;
+}
+
+
int modules__parse(const char *filename, void *arg,
int (*process_module)(void *arg, const char *name,
u64 start, u64 size))
--
2.26.2.526.g744177e7f7-goog
^ permalink raw reply [flat|nested] 4+ messages in thread