mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-29 23:28:52 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
75
tools/perf/arch/x86/util/dwarf-regs.c
Normal file
75
tools/perf/arch/x86/util/dwarf-regs.c
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* dwarf-regs.c : Mapping of DWARF debug register numbers into register names.
|
||||
* Extracted from probe-finder.c
|
||||
*
|
||||
* Written by Masami Hiramatsu <mhiramat@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
/*
|
||||
* Generic dwarf analysis helpers
|
||||
*/
|
||||
|
||||
#define X86_32_MAX_REGS 8
|
||||
const char *x86_32_regs_table[X86_32_MAX_REGS] = {
|
||||
"%ax",
|
||||
"%cx",
|
||||
"%dx",
|
||||
"%bx",
|
||||
"$stack", /* Stack address instead of %sp */
|
||||
"%bp",
|
||||
"%si",
|
||||
"%di",
|
||||
};
|
||||
|
||||
#define X86_64_MAX_REGS 16
|
||||
const char *x86_64_regs_table[X86_64_MAX_REGS] = {
|
||||
"%ax",
|
||||
"%dx",
|
||||
"%cx",
|
||||
"%bx",
|
||||
"%si",
|
||||
"%di",
|
||||
"%bp",
|
||||
"%sp",
|
||||
"%r8",
|
||||
"%r9",
|
||||
"%r10",
|
||||
"%r11",
|
||||
"%r12",
|
||||
"%r13",
|
||||
"%r14",
|
||||
"%r15",
|
||||
};
|
||||
|
||||
/* TODO: switching by dwarf address size */
|
||||
#ifdef __x86_64__
|
||||
#define ARCH_MAX_REGS X86_64_MAX_REGS
|
||||
#define arch_regs_table x86_64_regs_table
|
||||
#else
|
||||
#define ARCH_MAX_REGS X86_32_MAX_REGS
|
||||
#define arch_regs_table x86_32_regs_table
|
||||
#endif
|
||||
|
||||
/* Return architecture dependent register string (for kprobe-tracer) */
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
|
||||
}
|
||||
59
tools/perf/arch/x86/util/header.c
Normal file
59
tools/perf/arch/x86/util/header.c
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../../util/header.h"
|
||||
|
||||
static inline void
|
||||
cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c,
|
||||
unsigned int *d)
|
||||
{
|
||||
__asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t"
|
||||
"movl %%ebx, %%esi\n\t.byte 0x5b"
|
||||
: "=a" (*a),
|
||||
"=S" (*b),
|
||||
"=c" (*c),
|
||||
"=d" (*d)
|
||||
: "a" (op));
|
||||
}
|
||||
|
||||
int
|
||||
get_cpuid(char *buffer, size_t sz)
|
||||
{
|
||||
unsigned int a, b, c, d, lvl;
|
||||
int family = -1, model = -1, step = -1;
|
||||
int nb;
|
||||
char vendor[16];
|
||||
|
||||
cpuid(0, &lvl, &b, &c, &d);
|
||||
strncpy(&vendor[0], (char *)(&b), 4);
|
||||
strncpy(&vendor[4], (char *)(&d), 4);
|
||||
strncpy(&vendor[8], (char *)(&c), 4);
|
||||
vendor[12] = '\0';
|
||||
|
||||
if (lvl >= 1) {
|
||||
cpuid(1, &a, &b, &c, &d);
|
||||
|
||||
family = (a >> 8) & 0xf; /* bits 11 - 8 */
|
||||
model = (a >> 4) & 0xf; /* Bits 7 - 4 */
|
||||
step = a & 0xf;
|
||||
|
||||
/* extended family */
|
||||
if (family == 0xf)
|
||||
family += (a >> 20) & 0xff;
|
||||
|
||||
/* extended model */
|
||||
if (family >= 0x6)
|
||||
model += ((a >> 16) & 0xf) << 4;
|
||||
}
|
||||
nb = scnprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step);
|
||||
|
||||
/* look for end marker to ensure the entire data fit */
|
||||
if (strchr(buffer, '$')) {
|
||||
buffer[nb-1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
156
tools/perf/arch/x86/util/kvm-stat.c
Normal file
156
tools/perf/arch/x86/util/kvm-stat.c
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#include "../../util/kvm-stat.h"
|
||||
#include <asm/kvm_perf.h>
|
||||
|
||||
define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS);
|
||||
define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS);
|
||||
|
||||
static struct kvm_events_ops exit_events = {
|
||||
.is_begin_event = exit_event_begin,
|
||||
.is_end_event = exit_event_end,
|
||||
.decode_key = exit_event_decode_key,
|
||||
.name = "VM-EXIT"
|
||||
};
|
||||
|
||||
/*
|
||||
* For the mmio events, we treat:
|
||||
* the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
|
||||
* the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
|
||||
*/
|
||||
static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
key->key = perf_evsel__intval(evsel, sample, "gpa");
|
||||
key->info = perf_evsel__intval(evsel, sample, "type");
|
||||
}
|
||||
|
||||
#define KVM_TRACE_MMIO_READ_UNSATISFIED 0
|
||||
#define KVM_TRACE_MMIO_READ 1
|
||||
#define KVM_TRACE_MMIO_WRITE 2
|
||||
|
||||
static bool mmio_event_begin(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample, struct event_key *key)
|
||||
{
|
||||
/* MMIO read begin event in kernel. */
|
||||
if (kvm_exit_event(evsel))
|
||||
return true;
|
||||
|
||||
/* MMIO write begin event in kernel. */
|
||||
if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
|
||||
perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) {
|
||||
mmio_event_get_key(evsel, sample, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
/* MMIO write end event in kernel. */
|
||||
if (kvm_entry_event(evsel))
|
||||
return true;
|
||||
|
||||
/* MMIO read end event in kernel.*/
|
||||
if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
|
||||
perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) {
|
||||
mmio_event_get_key(evsel, sample, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
|
||||
struct event_key *key,
|
||||
char *decode)
|
||||
{
|
||||
scnprintf(decode, DECODE_STR_LEN, "%#lx:%s",
|
||||
(unsigned long)key->key,
|
||||
key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R");
|
||||
}
|
||||
|
||||
static struct kvm_events_ops mmio_events = {
|
||||
.is_begin_event = mmio_event_begin,
|
||||
.is_end_event = mmio_event_end,
|
||||
.decode_key = mmio_event_decode_key,
|
||||
.name = "MMIO Access"
|
||||
};
|
||||
|
||||
/* The time of emulation pio access is from kvm_pio to kvm_entry. */
|
||||
static void ioport_event_get_key(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
key->key = perf_evsel__intval(evsel, sample, "port");
|
||||
key->info = perf_evsel__intval(evsel, sample, "rw");
|
||||
}
|
||||
|
||||
static bool ioport_event_begin(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
if (!strcmp(evsel->name, "kvm:kvm_pio")) {
|
||||
ioport_event_get_key(evsel, sample, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ioport_event_end(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct event_key *key __maybe_unused)
|
||||
{
|
||||
return kvm_entry_event(evsel);
|
||||
}
|
||||
|
||||
static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
|
||||
struct event_key *key,
|
||||
char *decode)
|
||||
{
|
||||
scnprintf(decode, DECODE_STR_LEN, "%#llx:%s",
|
||||
(unsigned long long)key->key,
|
||||
key->info ? "POUT" : "PIN");
|
||||
}
|
||||
|
||||
static struct kvm_events_ops ioport_events = {
|
||||
.is_begin_event = ioport_event_begin,
|
||||
.is_end_event = ioport_event_end,
|
||||
.decode_key = ioport_event_decode_key,
|
||||
.name = "IO Port Access"
|
||||
};
|
||||
|
||||
const char * const kvm_events_tp[] = {
|
||||
"kvm:kvm_entry",
|
||||
"kvm:kvm_exit",
|
||||
"kvm:kvm_mmio",
|
||||
"kvm:kvm_pio",
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct kvm_reg_events_ops kvm_reg_events_ops[] = {
|
||||
{ .name = "vmexit", .ops = &exit_events },
|
||||
{ .name = "mmio", .ops = &mmio_events },
|
||||
{ .name = "ioport", .ops = &ioport_events },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
const char * const kvm_skip_events[] = {
|
||||
"HLT",
|
||||
NULL,
|
||||
};
|
||||
|
||||
int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid)
|
||||
{
|
||||
if (strstr(cpuid, "Intel")) {
|
||||
kvm->exit_reasons = vmx_exit_reasons;
|
||||
kvm->exit_reasons_isa = "VMX";
|
||||
} else if (strstr(cpuid, "AMD")) {
|
||||
kvm->exit_reasons = svm_exit_reasons;
|
||||
kvm->exit_reasons_isa = "SVM";
|
||||
} else
|
||||
return -ENOTSUP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
48
tools/perf/arch/x86/util/tsc.c
Normal file
48
tools/perf/arch/x86/util/tsc.c
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/perf_event.h>
|
||||
|
||||
#include "../../perf.h"
|
||||
#include <linux/types.h>
|
||||
#include "../../util/debug.h"
|
||||
#include "../../util/tsc.h"
|
||||
#include "tsc.h"
|
||||
|
||||
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
|
||||
struct perf_tsc_conversion *tc)
|
||||
{
|
||||
bool cap_user_time_zero;
|
||||
u32 seq;
|
||||
int i = 0;
|
||||
|
||||
while (1) {
|
||||
seq = pc->lock;
|
||||
rmb();
|
||||
tc->time_mult = pc->time_mult;
|
||||
tc->time_shift = pc->time_shift;
|
||||
tc->time_zero = pc->time_zero;
|
||||
cap_user_time_zero = pc->cap_user_time_zero;
|
||||
rmb();
|
||||
if (pc->lock == seq && !(seq & 1))
|
||||
break;
|
||||
if (++i > 10000) {
|
||||
pr_debug("failed to get perf_event_mmap_page lock\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cap_user_time_zero)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 rdtsc(void)
|
||||
{
|
||||
unsigned int low, high;
|
||||
|
||||
asm volatile("rdtsc" : "=a" (low), "=d" (high));
|
||||
|
||||
return low | ((u64)high) << 32;
|
||||
}
|
||||
17
tools/perf/arch/x86/util/tsc.h
Normal file
17
tools/perf/arch/x86/util/tsc.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
|
||||
#define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct perf_tsc_conversion {
|
||||
u16 time_shift;
|
||||
u32 time_mult;
|
||||
u64 time_zero;
|
||||
};
|
||||
|
||||
struct perf_event_mmap_page;
|
||||
|
||||
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
|
||||
struct perf_tsc_conversion *tc);
|
||||
|
||||
#endif /* TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ */
|
||||
51
tools/perf/arch/x86/util/unwind-libdw.c
Normal file
51
tools/perf/arch/x86/util/unwind-libdw.c
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#include <elfutils/libdwfl.h>
|
||||
#include "../../util/unwind-libdw.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = &ui->sample->user_regs;
|
||||
Dwarf_Word dwarf_regs[17];
|
||||
unsigned nregs;
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_X86_##r); \
|
||||
val; \
|
||||
})
|
||||
|
||||
if (user_regs->abi == PERF_SAMPLE_REGS_ABI_32) {
|
||||
dwarf_regs[0] = REG(AX);
|
||||
dwarf_regs[1] = REG(CX);
|
||||
dwarf_regs[2] = REG(DX);
|
||||
dwarf_regs[3] = REG(BX);
|
||||
dwarf_regs[4] = REG(SP);
|
||||
dwarf_regs[5] = REG(BP);
|
||||
dwarf_regs[6] = REG(SI);
|
||||
dwarf_regs[7] = REG(DI);
|
||||
dwarf_regs[8] = REG(IP);
|
||||
nregs = 9;
|
||||
} else {
|
||||
dwarf_regs[0] = REG(AX);
|
||||
dwarf_regs[1] = REG(DX);
|
||||
dwarf_regs[2] = REG(CX);
|
||||
dwarf_regs[3] = REG(BX);
|
||||
dwarf_regs[4] = REG(SI);
|
||||
dwarf_regs[5] = REG(DI);
|
||||
dwarf_regs[6] = REG(BP);
|
||||
dwarf_regs[7] = REG(SP);
|
||||
dwarf_regs[8] = REG(R8);
|
||||
dwarf_regs[9] = REG(R9);
|
||||
dwarf_regs[10] = REG(R10);
|
||||
dwarf_regs[11] = REG(R11);
|
||||
dwarf_regs[12] = REG(R12);
|
||||
dwarf_regs[13] = REG(R13);
|
||||
dwarf_regs[14] = REG(R14);
|
||||
dwarf_regs[15] = REG(R15);
|
||||
dwarf_regs[16] = REG(IP);
|
||||
nregs = 17;
|
||||
}
|
||||
|
||||
return dwfl_thread_state_registers(thread, 0, nregs, dwarf_regs);
|
||||
}
|
||||
112
tools/perf/arch/x86/util/unwind-libunwind.c
Normal file
112
tools/perf/arch/x86/util/unwind-libunwind.c
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <libunwind.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/unwind.h"
|
||||
#include "../../util/debug.h"
|
||||
|
||||
#ifdef HAVE_ARCH_X86_64_SUPPORT
|
||||
int libunwind__arch_reg_id(int regnum)
|
||||
{
|
||||
int id;
|
||||
|
||||
switch (regnum) {
|
||||
case UNW_X86_64_RAX:
|
||||
id = PERF_REG_X86_AX;
|
||||
break;
|
||||
case UNW_X86_64_RDX:
|
||||
id = PERF_REG_X86_DX;
|
||||
break;
|
||||
case UNW_X86_64_RCX:
|
||||
id = PERF_REG_X86_CX;
|
||||
break;
|
||||
case UNW_X86_64_RBX:
|
||||
id = PERF_REG_X86_BX;
|
||||
break;
|
||||
case UNW_X86_64_RSI:
|
||||
id = PERF_REG_X86_SI;
|
||||
break;
|
||||
case UNW_X86_64_RDI:
|
||||
id = PERF_REG_X86_DI;
|
||||
break;
|
||||
case UNW_X86_64_RBP:
|
||||
id = PERF_REG_X86_BP;
|
||||
break;
|
||||
case UNW_X86_64_RSP:
|
||||
id = PERF_REG_X86_SP;
|
||||
break;
|
||||
case UNW_X86_64_R8:
|
||||
id = PERF_REG_X86_R8;
|
||||
break;
|
||||
case UNW_X86_64_R9:
|
||||
id = PERF_REG_X86_R9;
|
||||
break;
|
||||
case UNW_X86_64_R10:
|
||||
id = PERF_REG_X86_R10;
|
||||
break;
|
||||
case UNW_X86_64_R11:
|
||||
id = PERF_REG_X86_R11;
|
||||
break;
|
||||
case UNW_X86_64_R12:
|
||||
id = PERF_REG_X86_R12;
|
||||
break;
|
||||
case UNW_X86_64_R13:
|
||||
id = PERF_REG_X86_R13;
|
||||
break;
|
||||
case UNW_X86_64_R14:
|
||||
id = PERF_REG_X86_R14;
|
||||
break;
|
||||
case UNW_X86_64_R15:
|
||||
id = PERF_REG_X86_R15;
|
||||
break;
|
||||
case UNW_X86_64_RIP:
|
||||
id = PERF_REG_X86_IP;
|
||||
break;
|
||||
default:
|
||||
pr_err("unwind: invalid reg id %d\n", regnum);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
#else
|
||||
int libunwind__arch_reg_id(int regnum)
|
||||
{
|
||||
int id;
|
||||
|
||||
switch (regnum) {
|
||||
case UNW_X86_EAX:
|
||||
id = PERF_REG_X86_AX;
|
||||
break;
|
||||
case UNW_X86_EDX:
|
||||
id = PERF_REG_X86_DX;
|
||||
break;
|
||||
case UNW_X86_ECX:
|
||||
id = PERF_REG_X86_CX;
|
||||
break;
|
||||
case UNW_X86_EBX:
|
||||
id = PERF_REG_X86_BX;
|
||||
break;
|
||||
case UNW_X86_ESI:
|
||||
id = PERF_REG_X86_SI;
|
||||
break;
|
||||
case UNW_X86_EDI:
|
||||
id = PERF_REG_X86_DI;
|
||||
break;
|
||||
case UNW_X86_EBP:
|
||||
id = PERF_REG_X86_BP;
|
||||
break;
|
||||
case UNW_X86_ESP:
|
||||
id = PERF_REG_X86_SP;
|
||||
break;
|
||||
case UNW_X86_EIP:
|
||||
id = PERF_REG_X86_IP;
|
||||
break;
|
||||
default:
|
||||
pr_err("unwind: invalid reg id %d\n", regnum);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
#endif /* HAVE_ARCH_X86_64_SUPPORT */
|
||||
Loading…
Add table
Add a link
Reference in a new issue