perf machine: Workaround missing maps for x86 PTI entry trampolines
cover perf machine: Workaround missing maps for x86 PTI entry trampolines
Hi
x86 PTI trampolines were backported to 4.14, refer
commit: c631a16e5b84521ae80ab10d47cd6ccd2afc6817 ("x86/entry/64:
Create a per-CPU SYSCALL entry trampoline")
so the corresponding perf tools workaround also needs to be backported. Here are 5 patches for 4.14 stable backported from 4.18, to address that.
Regards
Adrian
PATCH 1/5 perf machine: Add machine__is() to identify machine arch
commit dbbd34a666ee117d0e39e71a47f38f02c4a5c698 upstream.
Add a function to identify the machine architecture.
--- tools/perf/util/env.c | 19 +++++++++++++++++++ tools/perf/util/env.h | 3 +++ tools/perf/util/machine.c | 9 +++++++++ tools/perf/util/machine.h | 2 ++ 4 files changed, 33 insertions(+)
tools/perf/util/env.c +19 -0
@@ -3,6 +3,7 @@#include "env.h" #include "util.h" #include <errno.h> +#include <sys/utsname.h> struct perf_env perf_env;
@@ -87,6 +88,24 @@ int perf_env__read_cpu_topology_map(struct perf_env *env)return 0 ; } +static int perf_env__read_arch (struct perf_env *env) +{ +struct utsname uts; + +if (env->arch) +return 0 ; + +if (!uname (&uts)) + env->arch =strdup (uts.machine); + +return env->arch ?0 : -ENOMEM ; +} + +const char *perf_env__raw_arch (struct perf_env *env) +{ +return env && !perf_env__read_arch (env) ? env->arch :"unknown" ; +} +void cpu_cache_level__free (struct cpu_cache_level *cache) {free (cache->type);
tools/perf/util/env.h +3 -0
@@ -65,4 +65,7 @@ int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]);int perf_env__read_cpu_topology_map (struct perf_env *env);void cpu_cache_level__free (struct cpu_cache_level *cache); + +const char *perf_env__raw_arch (struct perf_env *env); +#endif /* __PERF_ENV_H */
tools/perf/util/machine.c +9 -0
@@ -2238,6 +2238,15 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,return 0 ; } +/* +* Compares the raw arch string. N.B. see instead perf_env__arch() if a +* normalized arch is needed. +*/ +bool machine__is (struct machine *machine,const char *arch) +{ +return machine && !strcmp (perf_env__raw_arch (machine->env), arch); +} +int machine__get_kernel_start (struct machine *machine) {struct map *map =machine__kernel_map (machine);
tools/perf/util/machine.h +2 -0
@@ -169,6 +169,8 @@ static inline bool machine__is_host(struct machine *machine)return machine ? machine->pid ==HOST_KERNEL_ID : false; } +bool machine__is (struct machine *machine,const char *arch); +struct thread *__machine__findnew_thread (struct machine *machine,pid_t pid,pid_t tid);struct thread *machine__findnew_thread (struct machine *machine,pid_t pid,pid_t tid);
Signature · 1 line
2.17.1
PATCH 2/5 perf tools: Fix kernel_start for PTI on x86
In reply to Adrian Huntercommit 19422a9f2a3be7f3a046285ffae4cbb571aa853a upstream.
On x86_64, PTI entry trampolines are less than the start of kernel text, but still above 2^63. So leave kernel_start = 1ULL << 63 for x86_64.
--- tools/perf/util/machine.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
tools/perf/util/machine.c +6 -1
@@ -2263,7 +2263,12 @@ int machine__get_kernel_start(struct machine *machine) machine->kernel_start =1ULL <<63 ;if (map) { err =map__load (map); -if (!err) +/* +* On x86_64, PTI entry trampolines are less than the +* start of kernel text, but still above 2^63. So leave +* kernel_start = 1ULL << 63 for x86_64. +*/ +if (!err && !machine__is (machine,"x86_64" )) machine->kernel_start = map->start; }return err;
Signature · 1 line
2.17.1
PATCH 3/5 perf machine: Add nr_cpus_avail()
In reply to Adrian Huntercommit 9cecca325ea879c84fcd31a5e609a514c1a1dbd1 upstream.
Add a function to return the number of the machine's available CPUs.
--- tools/perf/util/env.c | 13 +++++++++++++ tools/perf/util/env.h | 1 + tools/perf/util/machine.c | 5 +++++ tools/perf/util/machine.h | 1 + 4 files changed, 20 insertions(+)
tools/perf/util/env.c +13 -0
@@ -101,11 +101,24 @@ static int perf_env__read_arch(struct perf_env *env)return env->arch ?0 : -ENOMEM ; } +static int perf_env__read_nr_cpus_avail (struct perf_env *env) +{ +if (env->nr_cpus_avail ==0 ) + env->nr_cpus_avail =cpu__max_present_cpu (); + +return env->nr_cpus_avail ?0 : -ENOENT ; +} +const char *perf_env__raw_arch (struct perf_env *env) {return env && !perf_env__read_arch (env) ? env->arch :"unknown" ; } +int perf_env__nr_cpus_avail (struct perf_env *env) +{ +return env && !perf_env__read_nr_cpus_avail (env) ? env->nr_cpus_avail :0 ; +} +void cpu_cache_level__free (struct cpu_cache_level *cache) {free (cache->type);
tools/perf/util/env.h +1 -0
@@ -67,5 +67,6 @@ int perf_env__read_cpu_topology_map(struct perf_env *env);void cpu_cache_level__free (struct cpu_cache_level *cache);const char *perf_env__raw_arch (struct perf_env *env); +int perf_env__nr_cpus_avail (struct perf_env *env);#endif /* __PERF_ENV_H */
tools/perf/util/machine.c +5 -0
@@ -2247,6 +2247,11 @@ bool machine__is(struct machine *machine, const char *arch)return machine && !strcmp (perf_env__raw_arch (machine->env), arch); } +int machine__nr_cpus_avail (struct machine *machine) +{ +return machine ?perf_env__nr_cpus_avail (machine->env) :0 ; +} +int machine__get_kernel_start (struct machine *machine) {struct map *map =machine__kernel_map (machine);
tools/perf/util/machine.h +1 -0
@@ -170,6 +170,7 @@ static inline bool machine__is_host(struct machine *machine) }bool machine__is (struct machine *machine,const char *arch); +int machine__nr_cpus_avail (struct machine *machine);struct thread *__machine__findnew_thread (struct machine *machine,pid_t pid,pid_t tid);struct thread *machine__findnew_thread (struct machine *machine,pid_t pid,pid_t tid);
Signature · 1 line
2.17.1
PATCH 4/5 perf machine: Workaround missing maps for x86 PTI entry trampolines
In reply to Adrian Huntercommit 4d99e4136580d178e3523281a820be17bf814bf8 upstream.
On x86_64 the PTI entry trampolines are not in the kernel map created by perf tools. That results in the addresses having no symbols and prevents annotation. It also causes Intel PT to have decoding errors at the trampoline addresses.
Workaround that by creating maps for the trampolines.
At present the kernel does not export information revealing where the trampolines are. Until that happens, the addresses are hardcoded.
--- tools/perf/util/machine.c | 96 +++++++++++++++++++++++++++++++++++++++ tools/perf/util/machine.h | 3 ++ tools/perf/util/symbol.c | 12 +++-- 3 files changed, 106 insertions(+), 5 deletions(-)
tools/perf/util/machine.c +96 -0
@@ -818,6 +818,102 @@ static int machine__get_running_kernel_start(struct machine *machine,return 0 ; } +/* Kernel-space maps for symbols that are outside the main kernel map and module maps */ +struct extra_kernel_map { +u64 start; +u64 end; +u64 pgoff; +}; + +static int machine__create_extra_kernel_map (struct machine *machine, +struct dso *kernel, +struct extra_kernel_map *xm) +{ +struct kmap *kmap; +struct map *map; + + map =map__new2 (xm->start, kernel,MAP__FUNCTION ); +if (!map) +return -1 ; + + map->end = xm->end; + map->pgoff = xm->pgoff; + + kmap =map__kmap (map); + + kmap->kmaps = &machine->kmaps; + +map_groups__insert (&machine->kmaps, map); + +pr_debug2 ("Added extra kernel map %" PRIx64"-%" PRIx64"\n" , + map->start, map->end); + +map__put (map); + +return 0 ; +} + +static u64 find_entry_trampoline (struct dso *dso) +{ +/* Duplicates are removed so lookup all aliases */ +const char *syms[] = { +"_entry_trampoline" , +"__entry_trampoline_start" , +"entry_SYSCALL_64_trampoline" , + }; +struct symbol *sym =dso__first_symbol (dso,MAP__FUNCTION ); +unsigned int i; + +for (; sym; sym =dso__next_symbol (sym)) { +if (sym->binding !=STB_GLOBAL ) +continue ; +for (i =0 ; i <ARRAY_SIZE (syms); i++) { +if (!strcmp (sym->name, syms[i])) +return sym->start; + } + } + +return 0 ; +} + +/* +* These values can be used for kernels that do not have symbols for the entry +* trampolines in kallsyms. +*/ +#define X86_64_CPU_ENTRY_AREA_PER_CPU 0xfffffe0000000000ULL +#define X86_64_CPU_ENTRY_AREA_SIZE 0x2c000 +#define X86_64_ENTRY_TRAMPOLINE 0x6000 + +/* Map x86_64 PTI entry trampolines */ +int machine__map_x86_64_entry_trampolines (struct machine *machine, +struct dso *kernel) +{ +u64 pgoff =find_entry_trampoline (kernel); +int nr_cpus_avail, cpu; + +if (!pgoff) +return 0 ; + + nr_cpus_avail =machine__nr_cpus_avail (machine); + +/* Add a 1 page map for each CPU's entry trampoline */ +for (cpu =0 ; cpu < nr_cpus_avail; cpu++) { +u64 va =X86_64_CPU_ENTRY_AREA_PER_CPU + + cpu *X86_64_CPU_ENTRY_AREA_SIZE + +X86_64_ENTRY_TRAMPOLINE ; +struct extra_kernel_map xm = { + .start = va, + .end = va + page_size, + .pgoff = pgoff, + }; + +if (machine__create_extra_kernel_map (machine, kernel, &xm) <0 ) +return -1 ; + } + +return 0 ; +} +int __machine__create_kernel_maps (struct machine *machine,struct dso *kernel) {int type;
tools/perf/util/machine.h +3 -0
@@ -266,4 +266,7 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,*/ char *machine__resolve_kernel_addr (void *vmachine,unsigned long long *addrp,char **modp); +int machine__map_x86_64_entry_trampolines (struct machine *machine, +struct dso *kernel); +#endif /* __PERF_MACHINE_H */
tools/perf/util/symbol.c +7 -5
@@ -1513,20 +1513,22 @@ int dso__load(struct dso *dso, struct map *map) goto out; } +if (map->groups && map->groups->machine) + machine = map->groups->machine; +else + machine =NULL ; +if (dso->kernel) {if (dso->kernel ==DSO_TYPE_KERNEL ) ret =dso__load_kernel_sym (dso, map);else if (dso->kernel ==DSO_TYPE_GUEST_KERNEL ) ret =dso__load_guest_kernel_sym (dso, map); +if (machine__is (machine,"x86_64" )) +machine__map_x86_64_entry_trampolines (machine, dso); goto out; } -if (map->groups && map->groups->machine) - machine = map->groups->machine; -else - machine =NULL ; - dso->adjust_symbols =0 ;if (perfmap) {
Signature · 1 line
2.17.1
PATCH 5/5 perf test code-reading: Fix perf_env setup for PTI entry trampolines
In reply to Adrian Huntercommit f6c66d73bb8192d357bb5fb8cd5826920f811d8c upstream.
The "Object code reading" test will not create maps for the PTI entry trampolines unless the machine environment exists to show that the arch is x86_64.
--- tools/perf/tests/code-reading.c | 1 + 1 file changed, 1 insertion(+)
tools/perf/tests/code-reading.c +1 -0
@@ -527,6 +527,7 @@ static int do_test_code_reading(bool try_kcore) pid =getpid (); machine =machine__new_host (); + machine->env = &perf_env; ret =machine__create_kernel_maps (machine);if (ret <0 ) {
Signature · 1 line
2.17.1
Quoted 7 lines · Adrian Hunter
Hi
x86 PTI trampolines were backported to 4.14, refer
commit:c631a16e5b84521ae80ab10d47cd6ccd2afc6817("x86/entry/64:
Create a per-CPU SYSCALL entry trampoline")
so the corresponding perf tools workaround also needs to be backported. Here are 5 patches for 4.14 stable backported from 4.18, to address that.
Queued for 4.14, thank you.
--
Thanks,
Sasha