#include "qemu/rcu.h"
#include "exec/log.h"
#include "qemu/main-loop.h"
-#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
-#include "hw/i386/apic.h"
-#endif
#include "sysemu/cpus.h"
#include "exec/cpu-all.h"
#include "sysemu/cpu-timers.h"
hash = tb_jmp_cache_hash_func(pc);
jc = cpu->tb_jmp_cache;
- if (cflags & CF_PCREL) {
- /* Use acquire to ensure current load of pc from jc. */
- tb = qatomic_load_acquire(&jc->array[hash].tb);
+ tb = qatomic_read(&jc->array[hash].tb);
+ if (likely(tb &&
+ jc->array[hash].pc == pc &&
+ tb->cs_base == cs_base &&
+ tb->flags == flags &&
+ tb_cflags(tb) == cflags)) {
+ goto hit;
+ }
- if (likely(tb &&
- jc->array[hash].pc == pc &&
- tb->cs_base == cs_base &&
- tb->flags == flags &&
- tb_cflags(tb) == cflags)) {
- return tb;
- }
- tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags);
- if (tb == NULL) {
- return NULL;
- }
- jc->array[hash].pc = pc;
- /* Ensure pc is written first. */
- qatomic_store_release(&jc->array[hash].tb, tb);
- } else {
- /* Use rcu_read to ensure current load of pc from *tb. */
- tb = qatomic_rcu_read(&jc->array[hash].tb);
-
- if (likely(tb &&
- tb->pc == pc &&
- tb->cs_base == cs_base &&
- tb->flags == flags &&
- tb_cflags(tb) == cflags)) {
- return tb;
- }
- tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags);
- if (tb == NULL) {
- return NULL;
- }
- /* Use the pc value already stored in tb->pc. */
- qatomic_set(&jc->array[hash].tb, tb);
+ tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags);
+ if (tb == NULL) {
+ return NULL;
}
+ jc->array[hash].pc = pc;
+ qatomic_set(&jc->array[hash].tb, tb);
+
+hit:
+ /*
+ * As long as tb is not NULL, the contents are consistent. Therefore,
+ * the virtual PC has to match for non-CF_PCREL translations.
+ */
+ assert((tb_cflags(tb) & CF_PCREL) || tb->pc == pc);
return tb;
}
#ifdef CONFIG_USER_ONLY
g_assert_not_reached();
#else
- CPUClass *cc = CPU_GET_CLASS(cpu);
- assert(cc->tcg_ops->debug_check_breakpoint);
- match_bp = cc->tcg_ops->debug_check_breakpoint(cpu);
+ const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
+ assert(tcg_ops->debug_check_breakpoint);
+ match_bp = tcg_ops->debug_check_breakpoint(cpu);
#endif
}
* counter hit zero); we must restore the guest PC to the address
* of the start of the TB.
*/
- CPUClass *cc = CPU_GET_CLASS(cpu);
+ CPUClass *cc = cpu->cc;
+ const TCGCPUOps *tcg_ops = cc->tcg_ops;
- if (cc->tcg_ops->synchronize_from_tb) {
- cc->tcg_ops->synchronize_from_tb(cpu, last_tb);
+ if (tcg_ops->synchronize_from_tb) {
+ tcg_ops->synchronize_from_tb(cpu, last_tb);
} else {
tcg_debug_assert(!(tb_cflags(last_tb) & CF_PCREL));
assert(cc->set_pc);
static void cpu_exec_enter(CPUState *cpu)
{
- CPUClass *cc = CPU_GET_CLASS(cpu);
+ const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
- if (cc->tcg_ops->cpu_exec_enter) {
- cc->tcg_ops->cpu_exec_enter(cpu);
+ if (tcg_ops->cpu_exec_enter) {
+ tcg_ops->cpu_exec_enter(cpu);
}
}
static void cpu_exec_exit(CPUState *cpu)
{
- CPUClass *cc = CPU_GET_CLASS(cpu);
+ const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
- if (cc->tcg_ops->cpu_exec_exit) {
- cc->tcg_ops->cpu_exec_exit(cpu);
+ if (tcg_ops->cpu_exec_exit) {
+ tcg_ops->cpu_exec_exit(cpu);
}
}
{
#ifndef CONFIG_USER_ONLY
if (cpu->halted) {
-#if defined(TARGET_I386)
- if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
- X86CPU *x86_cpu = X86_CPU(cpu);
- bql_lock();
- apic_poll_irq(x86_cpu->apic_state);
- cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
- bql_unlock();
+ const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
+
+ if (tcg_ops->cpu_exec_halt) {
+ tcg_ops->cpu_exec_halt(cpu);
}
-#endif /* TARGET_I386 */
if (!cpu_has_work(cpu)) {
return true;
}
static inline void cpu_handle_debug_exception(CPUState *cpu)
{
- CPUClass *cc = CPU_GET_CLASS(cpu);
+ const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
CPUWatchpoint *wp;
if (!cpu->watchpoint_hit) {
}
}
- if (cc->tcg_ops->debug_excp_handler) {
- cc->tcg_ops->debug_excp_handler(cpu);
+ if (tcg_ops->debug_excp_handler) {
+ tcg_ops->debug_excp_handler(cpu);
}
}
#endif
return false;
}
+
if (cpu->exception_index >= EXCP_INTERRUPT) {
/* exit request from the cpu execution loop */
*ret = cpu->exception_index;
}
cpu->exception_index = -1;
return true;
- } else {
+ }
+
#if defined(CONFIG_USER_ONLY)
- /* if user mode only, we simulate a fake exception
- which will be handled outside the cpu execution
- loop */
+ /*
+ * If user mode only, we simulate a fake exception which will be
+ * handled outside the cpu execution loop.
+ */
#if defined(TARGET_I386)
- CPUClass *cc = CPU_GET_CLASS(cpu);
- cc->tcg_ops->fake_user_interrupt(cpu);
+ const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
+ tcg_ops->fake_user_interrupt(cpu);
#endif /* TARGET_I386 */
- *ret = cpu->exception_index;
- cpu->exception_index = -1;
- return true;
+ *ret = cpu->exception_index;
+ cpu->exception_index = -1;
+ return true;
#else
- if (replay_exception()) {
- CPUClass *cc = CPU_GET_CLASS(cpu);
- bql_lock();
- cc->tcg_ops->do_interrupt(cpu);
- bql_unlock();
- cpu->exception_index = -1;
+ if (replay_exception()) {
+ const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
- if (unlikely(cpu->singlestep_enabled)) {
- /*
- * After processing the exception, ensure an EXCP_DEBUG is
- * raised when single-stepping so that GDB doesn't miss the
- * next instruction.
- */
- *ret = EXCP_DEBUG;
- cpu_handle_debug_exception(cpu);
- return true;
- }
- } else if (!replay_has_interrupt()) {
- /* give a chance to iothread in replay mode */
- *ret = EXCP_INTERRUPT;
+ bql_lock();
+ tcg_ops->do_interrupt(cpu);
+ bql_unlock();
+ cpu->exception_index = -1;
+
+ if (unlikely(cpu->singlestep_enabled)) {
+ /*
+ * After processing the exception, ensure an EXCP_DEBUG is
+ * raised when single-stepping so that GDB doesn't miss the
+ * next instruction.
+ */
+ *ret = EXCP_DEBUG;
+ cpu_handle_debug_exception(cpu);
return true;
}
-#endif
+ } else if (!replay_has_interrupt()) {
+ /* give a chance to iothread in replay mode */
+ *ret = EXCP_INTERRUPT;
+ return true;
}
+#endif
return false;
}
-#ifndef CONFIG_USER_ONLY
-/*
- * CPU_INTERRUPT_POLL is a virtual event which gets converted into a
- * "real" interrupt event later. It does not need to be recorded for
- * replay purposes.
- */
-static inline bool need_replay_interrupt(int interrupt_request)
+static inline bool icount_exit_request(CPUState *cpu)
{
-#if defined(TARGET_I386)
- return !(interrupt_request & CPU_INTERRUPT_POLL);
-#else
- return true;
-#endif
+ if (!icount_enabled()) {
+ return false;
+ }
+ if (cpu->cflags_next_tb != -1 && !(cpu->cflags_next_tb & CF_USE_ICOUNT)) {
+ return false;
+ }
+ return cpu->neg.icount_decr.u16.low + cpu->icount_extra == 0;
}
-#endif /* !CONFIG_USER_ONLY */
static inline bool cpu_handle_interrupt(CPUState *cpu,
TranslationBlock **last_tb)
True when it is, and we should restart on a new TB,
and via longjmp via cpu_loop_exit. */
else {
- CPUClass *cc = CPU_GET_CLASS(cpu);
+ const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
- if (cc->tcg_ops->cpu_exec_interrupt &&
- cc->tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) {
- if (need_replay_interrupt(interrupt_request)) {
+ if (tcg_ops->cpu_exec_interrupt &&
+ tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) {
+ if (!tcg_ops->need_replay_interrupt ||
+ tcg_ops->need_replay_interrupt(interrupt_request)) {
replay_interrupt();
}
/*
}
/* Finally, check if we need to exit to the main loop. */
- if (unlikely(qatomic_read(&cpu->exit_request))
- || (icount_enabled()
- && (cpu->cflags_next_tb == -1 || cpu->cflags_next_tb & CF_USE_ICOUNT)
- && cpu->neg.icount_decr.u16.low + cpu->icount_extra == 0)) {
+ if (unlikely(qatomic_read(&cpu->exit_request)) || icount_exit_request(cpu)) {
qatomic_set(&cpu->exit_request, 0);
if (cpu->exception_index == -1) {
cpu->exception_index = EXCP_INTERRUPT;
*/
h = tb_jmp_cache_hash_func(pc);
jc = cpu->tb_jmp_cache;
- if (cflags & CF_PCREL) {
- jc->array[h].pc = pc;
- /* Ensure pc is written first. */
- qatomic_store_release(&jc->array[h].tb, tb);
- } else {
- /* Use the pc value already stored in tb->pc. */
- qatomic_set(&jc->array[h].tb, tb);
- }
+ jc->array[h].pc = pc;
+ qatomic_set(&jc->array[h].tb, tb);
}
#ifndef CONFIG_USER_ONLY
return EXCP_HALTED;
}
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
cpu_exec_enter(cpu);
/*
ret = cpu_exec_setjmp(cpu, &sc);
cpu_exec_exit(cpu);
- rcu_read_unlock();
-
return ret;
}
bool tcg_exec_realizefn(CPUState *cpu, Error **errp)
{
static bool tcg_target_initialized;
- CPUClass *cc = CPU_GET_CLASS(cpu);
if (!tcg_target_initialized) {
- cc->tcg_ops->initialize();
+ cpu->cc->tcg_ops->initialize();
tcg_target_initialized = true;
}
+++ /dev/null
-/*
- * Debug information support.
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#include "qemu/osdep.h"
-#include "qemu/lockable.h"
-
-#include <elfutils/libdwfl.h>
-
-#include "debuginfo.h"
-
-static QemuMutex lock;
-static Dwfl *dwfl;
-static const Dwfl_Callbacks dwfl_callbacks = {
- .find_elf = NULL,
- .find_debuginfo = dwfl_standard_find_debuginfo,
- .section_address = NULL,
- .debuginfo_path = NULL,
-};
-
-__attribute__((constructor))
-static void debuginfo_init(void)
-{
- qemu_mutex_init(&lock);
-}
-
-void debuginfo_report_elf(const char *name, int fd, uint64_t bias)
-{
- QEMU_LOCK_GUARD(&lock);
-
- if (dwfl) {
- dwfl_report_begin_add(dwfl);
- } else {
- dwfl = dwfl_begin(&dwfl_callbacks);
- }
-
- if (dwfl) {
- dwfl_report_elf(dwfl, name, name, fd, bias, true);
- dwfl_report_end(dwfl, NULL, NULL);
- }
-}
-
-void debuginfo_lock(void)
-{
- qemu_mutex_lock(&lock);
-}
-
-void debuginfo_query(struct debuginfo_query *q, size_t n)
-{
- const char *symbol, *file;
- Dwfl_Module *dwfl_module;
- Dwfl_Line *dwfl_line;
- GElf_Off dwfl_offset;
- GElf_Sym dwfl_sym;
- size_t i;
- int line;
-
- if (!dwfl) {
- return;
- }
-
- for (i = 0; i < n; i++) {
- dwfl_module = dwfl_addrmodule(dwfl, q[i].address);
- if (!dwfl_module) {
- continue;
- }
-
- if (q[i].flags & DEBUGINFO_SYMBOL) {
- symbol = dwfl_module_addrinfo(dwfl_module, q[i].address,
- &dwfl_offset, &dwfl_sym,
- NULL, NULL, NULL);
- if (symbol) {
- q[i].symbol = symbol;
- q[i].offset = dwfl_offset;
- }
- }
-
- if (q[i].flags & DEBUGINFO_LINE) {
- dwfl_line = dwfl_module_getsrc(dwfl_module, q[i].address);
- if (dwfl_line) {
- file = dwfl_lineinfo(dwfl_line, NULL, &line, 0, NULL, NULL);
- if (file) {
- q[i].file = file;
- q[i].line = line;
- }
- }
- }
- }
-}
-
-void debuginfo_unlock(void)
-{
- qemu_mutex_unlock(&lock);
-}
+++ /dev/null
-/*
- * Debug information support.
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef ACCEL_TCG_DEBUGINFO_H
-#define ACCEL_TCG_DEBUGINFO_H
-
-#include "qemu/bitops.h"
-
-/*
- * Debuginfo describing a certain address.
- */
-struct debuginfo_query {
- uint64_t address; /* Input: address. */
- int flags; /* Input: debuginfo subset. */
- const char *symbol; /* Symbol that the address is part of. */
- uint64_t offset; /* Offset from the symbol. */
- const char *file; /* Source file associated with the address. */
- int line; /* Line number in the source file. */
-};
-
-/*
- * Debuginfo subsets.
- */
-#define DEBUGINFO_SYMBOL BIT(1)
-#define DEBUGINFO_LINE BIT(2)
-
-#if defined(CONFIG_TCG) && defined(CONFIG_LIBDW)
-/*
- * Load debuginfo for the specified guest ELF image.
- * Return true on success, false on failure.
- */
-void debuginfo_report_elf(const char *name, int fd, uint64_t bias);
-
-/*
- * Take the debuginfo lock.
- */
-void debuginfo_lock(void);
-
-/*
- * Fill each on N Qs with the debuginfo about Q->ADDRESS as specified by
- * Q->FLAGS:
- *
- * - DEBUGINFO_SYMBOL: update Q->SYMBOL and Q->OFFSET. If symbol debuginfo is
- * missing, then leave them as is.
- * - DEBUINFO_LINE: update Q->FILE and Q->LINE. If line debuginfo is missing,
- * then leave them as is.
- *
- * This function must be called under the debuginfo lock. The results can be
- * accessed only until the debuginfo lock is released.
- */
-void debuginfo_query(struct debuginfo_query *q, size_t n);
-
-/*
- * Release the debuginfo lock.
- */
-void debuginfo_unlock(void);
-#else
-static inline void debuginfo_report_elf(const char *image_name, int image_fd,
- uint64_t load_bias)
-{
-}
-
-static inline void debuginfo_lock(void)
-{
-}
-
-static inline void debuginfo_query(struct debuginfo_query *q, size_t n)
-{
-}
-
-static inline void debuginfo_unlock(void)
-{
-}
-#endif
-
-#endif
-tcg_ss = ss.source_set()
common_ss.add(when: 'CONFIG_TCG', if_true: files(
'cpu-exec-common.c',
))
-tcg_ss.add(files(
+tcg_specific_ss = ss.source_set()
+tcg_specific_ss.add(files(
'tcg-all.c',
'cpu-exec.c',
'tb-maint.c',
'translate-all.c',
'translator.c',
))
-tcg_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c'))
-tcg_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_false: files('user-exec-stub.c'))
+tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c'))
+tcg_specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_false: files('user-exec-stub.c'))
if get_option('plugins')
- tcg_ss.add(files('plugin-gen.c'))
+ tcg_specific_ss.add(files('plugin-gen.c'))
endif
-tcg_ss.add(when: libdw, if_true: files('debuginfo.c'))
-if host_os == 'linux'
- tcg_ss.add(files('perf.c'))
-endif
-specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss)
+specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss)
specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files(
'cputlb.c',
+++ /dev/null
-/*
- * Linux perf perf-<pid>.map and jit-<pid>.dump integration.
- *
- * The jitdump spec can be found at [1].
- *
- * [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/tools/perf/Documentation/jitdump-specification.txt
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#include "qemu/osdep.h"
-#include "elf.h"
-#include "exec/exec-all.h"
-#include "qemu/timer.h"
-#include "tcg/tcg.h"
-
-#include "debuginfo.h"
-#include "perf.h"
-
-static FILE *safe_fopen_w(const char *path)
-{
- int saved_errno;
- FILE *f;
- int fd;
-
- /* Delete the old file, if any. */
- unlink(path);
-
- /* Avoid symlink attacks by using O_CREAT | O_EXCL. */
- fd = open(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
- if (fd == -1) {
- return NULL;
- }
-
- /* Convert fd to FILE*. */
- f = fdopen(fd, "w");
- if (f == NULL) {
- saved_errno = errno;
- close(fd);
- errno = saved_errno;
- return NULL;
- }
-
- return f;
-}
-
-static FILE *perfmap;
-
-void perf_enable_perfmap(void)
-{
- char map_file[32];
-
- snprintf(map_file, sizeof(map_file), "/tmp/perf-%d.map", getpid());
- perfmap = safe_fopen_w(map_file);
- if (perfmap == NULL) {
- warn_report("Could not open %s: %s, proceeding without perfmap",
- map_file, strerror(errno));
- }
-}
-
-/* Get PC and size of code JITed for guest instruction #INSN. */
-static void get_host_pc_size(uintptr_t *host_pc, uint16_t *host_size,
- const void *start, size_t insn)
-{
- uint16_t start_off = insn ? tcg_ctx->gen_insn_end_off[insn - 1] : 0;
-
- if (host_pc) {
- *host_pc = (uintptr_t)start + start_off;
- }
- if (host_size) {
- *host_size = tcg_ctx->gen_insn_end_off[insn] - start_off;
- }
-}
-
-static const char *pretty_symbol(const struct debuginfo_query *q, size_t *len)
-{
- static __thread char buf[64];
- int tmp;
-
- if (!q->symbol) {
- tmp = snprintf(buf, sizeof(buf), "guest-0x%"PRIx64, q->address);
- if (len) {
- *len = MIN(tmp + 1, sizeof(buf));
- }
- return buf;
- }
-
- if (!q->offset) {
- if (len) {
- *len = strlen(q->symbol) + 1;
- }
- return q->symbol;
- }
-
- tmp = snprintf(buf, sizeof(buf), "%s+0x%"PRIx64, q->symbol, q->offset);
- if (len) {
- *len = MIN(tmp + 1, sizeof(buf));
- }
- return buf;
-}
-
-static void write_perfmap_entry(const void *start, size_t insn,
- const struct debuginfo_query *q)
-{
- uint16_t host_size;
- uintptr_t host_pc;
-
- get_host_pc_size(&host_pc, &host_size, start, insn);
- fprintf(perfmap, "%"PRIxPTR" %"PRIx16" %s\n",
- host_pc, host_size, pretty_symbol(q, NULL));
-}
-
-static FILE *jitdump;
-static size_t perf_marker_size;
-static void *perf_marker = MAP_FAILED;
-
-#define JITHEADER_MAGIC 0x4A695444
-#define JITHEADER_VERSION 1
-
-struct jitheader {
- uint32_t magic;
- uint32_t version;
- uint32_t total_size;
- uint32_t elf_mach;
- uint32_t pad1;
- uint32_t pid;
- uint64_t timestamp;
- uint64_t flags;
-};
-
-enum jit_record_type {
- JIT_CODE_LOAD = 0,
- JIT_CODE_DEBUG_INFO = 2,
-};
-
-struct jr_prefix {
- uint32_t id;
- uint32_t total_size;
- uint64_t timestamp;
-};
-
-struct jr_code_load {
- struct jr_prefix p;
-
- uint32_t pid;
- uint32_t tid;
- uint64_t vma;
- uint64_t code_addr;
- uint64_t code_size;
- uint64_t code_index;
-};
-
-struct debug_entry {
- uint64_t addr;
- int lineno;
- int discrim;
- const char name[];
-};
-
-struct jr_code_debug_info {
- struct jr_prefix p;
-
- uint64_t code_addr;
- uint64_t nr_entry;
- struct debug_entry entries[];
-};
-
-static uint32_t get_e_machine(void)
-{
- Elf64_Ehdr elf_header;
- FILE *exe;
- size_t n;
-
- QEMU_BUILD_BUG_ON(offsetof(Elf32_Ehdr, e_machine) !=
- offsetof(Elf64_Ehdr, e_machine));
-
- exe = fopen("/proc/self/exe", "r");
- if (exe == NULL) {
- return EM_NONE;
- }
-
- n = fread(&elf_header, sizeof(elf_header), 1, exe);
- fclose(exe);
- if (n != 1) {
- return EM_NONE;
- }
-
- return elf_header.e_machine;
-}
-
-void perf_enable_jitdump(void)
-{
- struct jitheader header;
- char jitdump_file[32];
-
- if (!use_rt_clock) {
- warn_report("CLOCK_MONOTONIC is not available, proceeding without jitdump");
- return;
- }
-
- snprintf(jitdump_file, sizeof(jitdump_file), "jit-%d.dump", getpid());
- jitdump = safe_fopen_w(jitdump_file);
- if (jitdump == NULL) {
- warn_report("Could not open %s: %s, proceeding without jitdump",
- jitdump_file, strerror(errno));
- return;
- }
-
- /*
- * `perf inject` will see that the mapped file name in the corresponding
- * PERF_RECORD_MMAP or PERF_RECORD_MMAP2 event is of the form jit-%d.dump
- * and will process it as a jitdump file.
- */
- perf_marker_size = qemu_real_host_page_size();
- perf_marker = mmap(NULL, perf_marker_size, PROT_READ | PROT_EXEC,
- MAP_PRIVATE, fileno(jitdump), 0);
- if (perf_marker == MAP_FAILED) {
- warn_report("Could not map %s: %s, proceeding without jitdump",
- jitdump_file, strerror(errno));
- fclose(jitdump);
- jitdump = NULL;
- return;
- }
-
- header.magic = JITHEADER_MAGIC;
- header.version = JITHEADER_VERSION;
- header.total_size = sizeof(header);
- header.elf_mach = get_e_machine();
- header.pad1 = 0;
- header.pid = getpid();
- header.timestamp = get_clock();
- header.flags = 0;
- fwrite(&header, sizeof(header), 1, jitdump);
-}
-
-void perf_report_prologue(const void *start, size_t size)
-{
- if (perfmap) {
- fprintf(perfmap, "%"PRIxPTR" %zx tcg-prologue-buffer\n",
- (uintptr_t)start, size);
- }
-}
-
-/* Write a JIT_CODE_DEBUG_INFO jitdump entry. */
-static void write_jr_code_debug_info(const void *start,
- const struct debuginfo_query *q,
- size_t icount)
-{
- struct jr_code_debug_info rec;
- struct debug_entry ent;
- uintptr_t host_pc;
- int insn;
-
- /* Write the header. */
- rec.p.id = JIT_CODE_DEBUG_INFO;
- rec.p.total_size = sizeof(rec) + sizeof(ent) + 1;
- rec.p.timestamp = get_clock();
- rec.code_addr = (uintptr_t)start;
- rec.nr_entry = 1;
- for (insn = 0; insn < icount; insn++) {
- if (q[insn].file) {
- rec.p.total_size += sizeof(ent) + strlen(q[insn].file) + 1;
- rec.nr_entry++;
- }
- }
- fwrite(&rec, sizeof(rec), 1, jitdump);
-
- /* Write the main debug entries. */
- for (insn = 0; insn < icount; insn++) {
- if (q[insn].file) {
- get_host_pc_size(&host_pc, NULL, start, insn);
- ent.addr = host_pc;
- ent.lineno = q[insn].line;
- ent.discrim = 0;
- fwrite(&ent, sizeof(ent), 1, jitdump);
- fwrite(q[insn].file, strlen(q[insn].file) + 1, 1, jitdump);
- }
- }
-
- /* Write the trailing debug_entry. */
- ent.addr = (uintptr_t)start + tcg_ctx->gen_insn_end_off[icount - 1];
- ent.lineno = 0;
- ent.discrim = 0;
- fwrite(&ent, sizeof(ent), 1, jitdump);
- fwrite("", 1, 1, jitdump);
-}
-
-/* Write a JIT_CODE_LOAD jitdump entry. */
-static void write_jr_code_load(const void *start, uint16_t host_size,
- const struct debuginfo_query *q)
-{
- static uint64_t code_index;
- struct jr_code_load rec;
- const char *symbol;
- size_t symbol_size;
-
- symbol = pretty_symbol(q, &symbol_size);
- rec.p.id = JIT_CODE_LOAD;
- rec.p.total_size = sizeof(rec) + symbol_size + host_size;
- rec.p.timestamp = get_clock();
- rec.pid = getpid();
- rec.tid = qemu_get_thread_id();
- rec.vma = (uintptr_t)start;
- rec.code_addr = (uintptr_t)start;
- rec.code_size = host_size;
- rec.code_index = code_index++;
- fwrite(&rec, sizeof(rec), 1, jitdump);
- fwrite(symbol, symbol_size, 1, jitdump);
- fwrite(start, host_size, 1, jitdump);
-}
-
-void perf_report_code(uint64_t guest_pc, TranslationBlock *tb,
- const void *start)
-{
- struct debuginfo_query *q;
- size_t insn, start_words;
- uint64_t *gen_insn_data;
-
- if (!perfmap && !jitdump) {
- return;
- }
-
- q = g_try_malloc0_n(tb->icount, sizeof(*q));
- if (!q) {
- return;
- }
-
- debuginfo_lock();
-
- /* Query debuginfo for each guest instruction. */
- gen_insn_data = tcg_ctx->gen_insn_data;
- start_words = tcg_ctx->insn_start_words;
-
- for (insn = 0; insn < tb->icount; insn++) {
- /* FIXME: This replicates the restore_state_to_opc() logic. */
- q[insn].address = gen_insn_data[insn * start_words + 0];
- if (tb_cflags(tb) & CF_PCREL) {
- q[insn].address |= (guest_pc & TARGET_PAGE_MASK);
- } else {
-#if defined(TARGET_I386)
- q[insn].address -= tb->cs_base;
-#endif
- }
- q[insn].flags = DEBUGINFO_SYMBOL | (jitdump ? DEBUGINFO_LINE : 0);
- }
- debuginfo_query(q, tb->icount);
-
- /* Emit perfmap entries if needed. */
- if (perfmap) {
- flockfile(perfmap);
- for (insn = 0; insn < tb->icount; insn++) {
- write_perfmap_entry(start, insn, &q[insn]);
- }
- funlockfile(perfmap);
- }
-
- /* Emit jitdump entries if needed. */
- if (jitdump) {
- flockfile(jitdump);
- write_jr_code_debug_info(start, q, tb->icount);
- write_jr_code_load(start, tcg_ctx->gen_insn_end_off[tb->icount - 1],
- q);
- funlockfile(jitdump);
- }
-
- debuginfo_unlock();
- g_free(q);
-}
-
-void perf_exit(void)
-{
- if (perfmap) {
- fclose(perfmap);
- perfmap = NULL;
- }
-
- if (perf_marker != MAP_FAILED) {
- munmap(perf_marker, perf_marker_size);
- perf_marker = MAP_FAILED;
- }
-
- if (jitdump) {
- fclose(jitdump);
- jitdump = NULL;
- }
-}
+++ /dev/null
-/*
- * Linux perf perf-<pid>.map and jit-<pid>.dump integration.
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef ACCEL_TCG_PERF_H
-#define ACCEL_TCG_PERF_H
-
-#if defined(CONFIG_TCG) && defined(CONFIG_LINUX)
-/* Start writing perf-<pid>.map. */
-void perf_enable_perfmap(void);
-
-/* Start writing jit-<pid>.dump. */
-void perf_enable_jitdump(void);
-
-/* Add information about TCG prologue to profiler maps. */
-void perf_report_prologue(const void *start, size_t size);
-
-/* Add information about JITted guest code to profiler maps. */
-void perf_report_code(uint64_t guest_pc, TranslationBlock *tb,
- const void *start);
-
-/* Stop writing perf-<pid>.map and/or jit-<pid>.dump. */
-void perf_exit(void);
-#else
-static inline void perf_enable_perfmap(void)
-{
-}
-
-static inline void perf_enable_jitdump(void)
-{
-}
-
-static inline void perf_report_prologue(const void *start, size_t size)
-{
-}
-
-static inline void perf_report_code(uint64_t guest_pc, TranslationBlock *tb,
- const void *start)
-{
-}
-
-static inline void perf_exit(void)
-{
-}
-#endif
-
-#endif
#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
/*
- * Accessed in parallel; all accesses to 'tb' must be atomic.
- * For CF_PCREL, accesses to 'pc' must be protected by a
- * load_acquire/store_release to 'tb'.
+ * Invalidated in parallel; all accesses to 'tb' must be atomic.
+ * A valid entry is read/written by a single CPU, therefore there is
+ * no need for qatomic_rcu_read() and pc is always consistent with a
+ * non-NULL value of 'tb'. Strictly speaking pc is only needed for
+ * CF_PCREL, but it's used always for simplicity.
*/
struct CPUJumpCache {
struct rcu_head rcu;
if (cpu_can_run(cpu)) {
int r;
bql_unlock();
- r = tcg_cpus_exec(cpu);
+ r = tcg_cpu_exec(cpu);
bql_lock();
switch (r) {
case EXCP_DEBUG:
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
- tcg_cpus_destroy(cpu);
+ tcg_cpu_destroy(cpu);
bql_unlock();
rcu_remove_force_rcu_notifier(&force_rcu.notifier);
rcu_unregister_thread();
CPU_FOREACH(cpu) {
if (cpu->unplug && !cpu_can_run(cpu)) {
- tcg_cpus_destroy(cpu);
+ tcg_cpu_destroy(cpu);
break;
}
}
if (icount_enabled()) {
icount_prepare_for_run(cpu, cpu_budget);
}
- r = tcg_cpus_exec(cpu);
+ r = tcg_cpu_exec(cpu);
if (icount_enabled()) {
icount_process_data(cpu);
}
cpu->tcg_cflags |= cflags;
}
-void tcg_cpus_destroy(CPUState *cpu)
+void tcg_cpu_destroy(CPUState *cpu)
{
cpu_thread_signal_destroyed(cpu);
}
-int tcg_cpus_exec(CPUState *cpu)
+int tcg_cpu_exec(CPUState *cpu)
{
int ret;
assert(tcg_enabled());
#include "sysemu/cpus.h"
-void tcg_cpus_destroy(CPUState *cpu);
-int tcg_cpus_exec(CPUState *cpu);
+void tcg_cpu_destroy(CPUState *cpu);
+int tcg_cpu_exec(CPUState *cpu);
void tcg_handle_interrupt(CPUState *cpu, int mask);
void tcg_cpu_init_cflags(CPUState *cpu, bool parallel);
#include "tb-context.h"
#include "internal-common.h"
#include "internal-target.h"
-#include "perf.h"
+#include "tcg/perf.h"
#include "tcg/insn-start-words.h"
TBContext tb_ctx;
void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr,
MMUAccessType access_type, bool maperr, uintptr_t ra)
{
- const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops;
+ const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops;
if (tcg_ops->record_sigsegv) {
tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra);
void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr,
MMUAccessType access_type, uintptr_t ra)
{
- const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops;
+ const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops;
if (tcg_ops->record_sigbus) {
tcg_ops->record_sigbus(cpu, addr, access_type, ra);
vaddr l, page;
void * p;
uint8_t *buf = ptr;
+ ssize_t written;
+ int ret = -1;
+ int fd = -1;
while (len > 0) {
page = addr & TARGET_PAGE_MASK;
if (l > len)
l = len;
flags = page_get_flags(page);
- if (!(flags & PAGE_VALID))
- return -1;
+ if (!(flags & PAGE_VALID)) {
+ goto out_close;
+ }
if (is_write) {
- if (!(flags & PAGE_WRITE))
- return -1;
- /* XXX: this code should not depend on lock_user */
- if (!(p = lock_user(VERIFY_WRITE, addr, l, 0)))
- return -1;
- memcpy(p, buf, l);
- unlock_user(p, addr, l);
- } else {
- if (!(flags & PAGE_READ))
- return -1;
+ if (flags & PAGE_WRITE) {
+ /* XXX: this code should not depend on lock_user */
+ p = lock_user(VERIFY_WRITE, addr, l, 0);
+ if (!p) {
+ goto out_close;
+ }
+ memcpy(p, buf, l);
+ unlock_user(p, addr, l);
+ } else {
+ /* Bypass the host page protection using ptrace. */
+ if (fd == -1) {
+ fd = open("/proc/self/mem", O_WRONLY);
+ if (fd == -1) {
+ goto out;
+ }
+ }
+ /*
+ * If there is a TranslationBlock and we weren't bypassing the
+ * host page protection, the memcpy() above would SEGV,
+ * ultimately leading to page_unprotect(). So invalidate the
+ * translations manually. Both invalidation and pwrite() must
+ * be under mmap_lock() in order to prevent the creation of
+ * another TranslationBlock in between.
+ */
+ mmap_lock();
+ tb_invalidate_phys_range(addr, addr + l - 1);
+ written = pwrite(fd, buf, l,
+ (off_t)(uintptr_t)g2h_untagged(addr));
+ mmap_unlock();
+ if (written != l) {
+ goto out_close;
+ }
+ }
+ } else if (flags & PAGE_READ) {
/* XXX: this code should not depend on lock_user */
- if (!(p = lock_user(VERIFY_READ, addr, l, 1)))
- return -1;
+ p = lock_user(VERIFY_READ, addr, l, 1);
+ if (!p) {
+ goto out_close;
+ }
memcpy(buf, p, l);
unlock_user(p, addr, 0);
+ } else {
+ /* Bypass the host page protection using ptrace. */
+ if (fd == -1) {
+ fd = open("/proc/self/mem", O_RDONLY);
+ if (fd == -1) {
+ goto out;
+ }
+ }
+ if (pread(fd, buf, l,
+ (off_t)(uintptr_t)g2h_untagged(addr)) != l) {
+ goto out_close;
+ }
}
len -= l;
buf += l;
addr += l;
}
- return 0;
+ ret = 0;
+out_close:
+ if (fd != -1) {
+ close(fd);
+ }
+out:
+ return ret;
}
#endif
#include "hw/boards.h"
#include "qemu/cutils.h"
#include "sysemu/runstate.h"
-#include "accel/tcg/debuginfo.h"
+#include "tcg/debuginfo.h"
#include <zlib.h>
#define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE)
-/* same as PROT_xxx */
-#define PAGE_READ 0x0001
-#define PAGE_WRITE 0x0002
-#define PAGE_EXEC 0x0004
-#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC)
-#define PAGE_VALID 0x0008
-/*
- * Original state of the write flag (used when tracking self-modifying code)
- */
-#define PAGE_WRITE_ORG 0x0010
-/*
- * Invalidate the TLB entry immediately, helpful for s390x
- * Low-Address-Protection. Used with PAGE_WRITE in tlb_set_page_with_attrs()
- */
-#define PAGE_WRITE_INV 0x0020
-/* For use with page_set_flags: page is being replaced; target_data cleared. */
-#define PAGE_RESET 0x0040
-/* For linux-user, indicates that the page is MAP_ANON. */
-#define PAGE_ANON 0x0080
-
#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY)
/* FIXME: Code that sets/uses this is broken and needs to go away. */
#define PAGE_RESERVED 0x0100
#endif
-/* Target-specific bits that will be used via page_get_flags(). */
-#define PAGE_TARGET_1 0x0200
-#define PAGE_TARGET_2 0x0400
-
/*
* For linux-user, indicates that the page is mapped with the same semantics
* in both guest and host.
#endif /* !CONFIG_USER_ONLY */
-/* accel/tcg/cpu-exec.c */
-int cpu_exec(CPUState *cpu);
-
/* Validate correct placement of CPUArchState. */
QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0);
QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState));
-/**
- * env_archcpu(env)
- * @env: The architecture environment
- *
- * Return the ArchCPU associated with the environment.
- */
-static inline ArchCPU *env_archcpu(CPUArchState *env)
-{
- return (void *)env - sizeof(CPUState);
-}
-
-/**
- * env_cpu(env)
- * @env: The architecture environment
- *
- * Return the CPUState associated with the environment.
- */
-static inline CPUState *env_cpu(CPUArchState *env)
-{
- return (void *)env - sizeof(CPUState);
-}
-
#endif /* CPU_ALL_H */
/* CPU interfaces that are target independent. */
+#include "exec/vaddr.h"
#ifndef CONFIG_USER_ONLY
#include "exec/hwaddr.h"
#endif
+#include "hw/core/cpu.h"
#define EXCP_INTERRUPT 0x10000 /* async interruption */
#define EXCP_HLT 0x10001 /* hlt instruction reached */
#define EXCP_YIELD 0x10004 /* cpu wants to yield timeslice to another */
#define EXCP_ATOMIC 0x10005 /* stop-the-world and emulate atomic */
-/**
- * vaddr:
- * Type wide enough to contain any #target_ulong virtual address.
- */
-typedef uint64_t vaddr;
-#define VADDR_PRId PRId64
-#define VADDR_PRIu PRIu64
-#define VADDR_PRIo PRIo64
-#define VADDR_PRIx PRIx64
-#define VADDR_PRIX PRIX64
-#define VADDR_MAX UINT64_MAX
-
void cpu_exec_init_all(void);
void cpu_exec_step_atomic(CPUState *cpu);
G_NORETURN void cpu_loop_exit(CPUState *cpu);
G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc);
+/* same as PROT_xxx */
+#define PAGE_READ 0x0001
+#define PAGE_WRITE 0x0002
+#define PAGE_EXEC 0x0004
+#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC)
+#define PAGE_VALID 0x0008
+/*
+ * Original state of the write flag (used when tracking self-modifying code)
+ */
+#define PAGE_WRITE_ORG 0x0010
+/*
+ * Invalidate the TLB entry immediately, helpful for s390x
+ * Low-Address-Protection. Used with PAGE_WRITE in tlb_set_page_with_attrs()
+ */
+#define PAGE_WRITE_INV 0x0020
+/* For use with page_set_flags: page is being replaced; target_data cleared. */
+#define PAGE_RESET 0x0040
+/* For linux-user, indicates that the page is MAP_ANON. */
+#define PAGE_ANON 0x0080
+
+/* Target-specific bits that will be used via page_get_flags(). */
+#define PAGE_TARGET_1 0x0200
+#define PAGE_TARGET_2 0x0400
+
+/*
+ * For linux-user, indicates that the page is mapped with the same semantics
+ * in both guest and host.
+ */
+#define PAGE_PASSTHROUGH 0x0800
+
+/* accel/tcg/cpu-exec.c */
+int cpu_exec(CPUState *cpu);
+
+/**
+ * env_archcpu(env)
+ * @env: The architecture environment
+ *
+ * Return the ArchCPU associated with the environment.
+ */
+static inline ArchCPU *env_archcpu(CPUArchState *env)
+{
+ return (void *)env - sizeof(CPUState);
+}
+
+/**
+ * env_cpu(env)
+ * @env: The architecture environment
+ *
+ * Return the CPUState associated with the environment.
+ */
+static inline CPUState *env_cpu(CPUArchState *env)
+{
+ return (void *)env - sizeof(CPUState);
+}
+
#endif /* CPU_COMMON_H */
h2g_nocheck(x); \
})
#else
-typedef target_ulong abi_ptr;
-#define TARGET_ABI_FMT_ptr TARGET_FMT_lx
+typedef vaddr abi_ptr;
+#define TARGET_ABI_FMT_ptr VADDR_PRIx
#endif
uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr);
#endif
-/* Hide the qatomic_read to make code a little easier on the eyes */
-static inline uint32_t tb_cflags(const TranslationBlock *tb)
-{
- return qatomic_read(&tb->cflags);
-}
-
static inline tb_page_addr_t tb_page_addr0(const TranslationBlock *tb)
{
#ifdef CONFIG_USER_ONLY
/* The alignment given to TranslationBlock during allocation. */
#define CODE_GEN_ALIGN 16
+/* Hide the qatomic_read to make code a little easier on the eyes */
+static inline uint32_t tb_cflags(const TranslationBlock *tb)
+{
+ return qatomic_read(&tb->cflags);
+}
+
#endif /* EXEC_TRANSLATION_BLOCK_H */
* the target-specific DisasContext, and then invoke translator_loop.
*/
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc);
+ vaddr pc, void *host_pc);
/**
* DisasJumpType:
*/
typedef struct DisasContextBase {
TranslationBlock *tb;
- target_ulong pc_first;
- target_ulong pc_next;
+ vaddr pc_first;
+ vaddr pc_next;
DisasJumpType is_jmp;
int num_insns;
int max_insns;
* Translators can use this to enforce the rule that only single-insn
* translation blocks are allowed to cross page boundaries.
*/
-static inline bool is_same_page(const DisasContextBase *db, target_ulong addr)
+static inline bool is_same_page(const DisasContextBase *db, vaddr addr)
{
return ((addr ^ db->pc_first) & TARGET_PAGE_MASK) == 0;
}
--- /dev/null
+/* Define vaddr. */
+
+#ifndef VADDR_H
+#define VADDR_H
+
+/**
+ * vaddr:
+ * Type wide enough to contain any #target_ulong virtual address.
+ */
+typedef uint64_t vaddr;
+#define VADDR_PRId PRId64
+#define VADDR_PRIu PRIu64
+#define VADDR_PRIo PRIo64
+#define VADDR_PRIx PRIx64
+#define VADDR_PRIX PRIX64
+#define VADDR_MAX UINT64_MAX
+
+#endif
#include "hw/qdev-core.h"
#include "disas/dis-asm.h"
-#include "exec/cpu-common.h"
#include "exec/hwaddr.h"
+#include "exec/vaddr.h"
#include "exec/memattrs.h"
#include "exec/tlb-common.h"
#include "qapi/qapi-types-run-state.h"
typedef struct CPUWatchpoint CPUWatchpoint;
-/* see tcg-cpu-ops.h */
-struct TCGCPUOps;
-
/* see accel-cpu.h */
struct AccelCPUClass;
const struct SysemuCPUOps *sysemu_ops;
/* when TCG is not available, this pointer is NULL */
- const struct TCGCPUOps *tcg_ops;
+ const TCGCPUOps *tcg_ops;
/*
* if not NULL, this is called in order for the CPUClass to initialize
void (*debug_excp_handler)(CPUState *cpu);
#ifdef NEED_CPU_H
-#if defined(CONFIG_USER_ONLY) && defined(TARGET_I386)
+#ifdef CONFIG_USER_ONLY
/**
* @fake_user_interrupt: Callback for 'fake exception' handling.
*
* cpu execution loop (hack for x86 user mode).
*/
void (*fake_user_interrupt)(CPUState *cpu);
-#else
- /**
- * @do_interrupt: Callback for interrupt handling.
- */
- void (*do_interrupt)(CPUState *cpu);
-#endif /* !CONFIG_USER_ONLY || !TARGET_I386 */
-#ifdef CONFIG_USER_ONLY
+
/**
* record_sigsegv:
* @cpu: cpu context
void (*record_sigbus)(CPUState *cpu, vaddr addr,
MMUAccessType access_type, uintptr_t ra);
#else
+ /** @do_interrupt: Callback for interrupt handling. */
+ void (*do_interrupt)(CPUState *cpu);
/** @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec */
bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request);
+ /** @cpu_exec_halt: Callback for handling halt in cpu_exec */
+ void (*cpu_exec_halt)(CPUState *cpu);
/**
* @tlb_fill: Handle a softmmu tlb miss
*
*/
bool (*io_recompile_replay_branch)(CPUState *cpu,
const TranslationBlock *tb);
+ /**
+ * @need_replay_interrupt: Return %true if @interrupt_request
+ * needs to be recorded for replay purposes.
+ */
+ bool (*need_replay_interrupt)(int interrupt_request);
#endif /* !CONFIG_USER_ONLY */
#endif /* NEED_CPU_H */
typedef struct ReservedRegion ReservedRegion;
typedef struct SHPCDevice SHPCDevice;
typedef struct SSIBus SSIBus;
+typedef struct TCGCPUOps TCGCPUOps;
typedef struct TCGHelperInfo TCGHelperInfo;
typedef struct TranslationBlock TranslationBlock;
typedef struct VirtIODevice VirtIODevice;
--- /dev/null
+/*
+ * Debug information support.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef TCG_DEBUGINFO_H
+#define TCG_DEBUGINFO_H
+
+#include "qemu/bitops.h"
+
+/*
+ * Debuginfo describing a certain address.
+ */
+struct debuginfo_query {
+ uint64_t address; /* Input: address. */
+ int flags; /* Input: debuginfo subset. */
+ const char *symbol; /* Symbol that the address is part of. */
+ uint64_t offset; /* Offset from the symbol. */
+ const char *file; /* Source file associated with the address. */
+ int line; /* Line number in the source file. */
+};
+
+/*
+ * Debuginfo subsets.
+ */
+#define DEBUGINFO_SYMBOL BIT(1)
+#define DEBUGINFO_LINE BIT(2)
+
+#if defined(CONFIG_TCG) && defined(CONFIG_LIBDW)
+/*
+ * Load debuginfo for the specified guest ELF image.
+ * Return true on success, false on failure.
+ */
+void debuginfo_report_elf(const char *name, int fd, uint64_t bias);
+
+/*
+ * Take the debuginfo lock.
+ */
+void debuginfo_lock(void);
+
+/*
+ * Fill each on N Qs with the debuginfo about Q->ADDRESS as specified by
+ * Q->FLAGS:
+ *
+ * - DEBUGINFO_SYMBOL: update Q->SYMBOL and Q->OFFSET. If symbol debuginfo is
+ * missing, then leave them as is.
+ * - DEBUINFO_LINE: update Q->FILE and Q->LINE. If line debuginfo is missing,
+ * then leave them as is.
+ *
+ * This function must be called under the debuginfo lock. The results can be
+ * accessed only until the debuginfo lock is released.
+ */
+void debuginfo_query(struct debuginfo_query *q, size_t n);
+
+/*
+ * Release the debuginfo lock.
+ */
+void debuginfo_unlock(void);
+#else
+static inline void debuginfo_report_elf(const char *image_name, int image_fd,
+ uint64_t load_bias)
+{
+}
+
+static inline void debuginfo_lock(void)
+{
+}
+
+static inline void debuginfo_query(struct debuginfo_query *q, size_t n)
+{
+}
+
+static inline void debuginfo_unlock(void)
+{
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * Linux perf perf-<pid>.map and jit-<pid>.dump integration.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef TCG_PERF_H
+#define TCG_PERF_H
+
+#if defined(CONFIG_TCG) && defined(CONFIG_LINUX)
+/* Start writing perf-<pid>.map. */
+void perf_enable_perfmap(void);
+
+/* Start writing jit-<pid>.dump. */
+void perf_enable_jitdump(void);
+
+/* Add information about TCG prologue to profiler maps. */
+void perf_report_prologue(const void *start, size_t size);
+
+/* Add information about JITted guest code to profiler maps. */
+void perf_report_code(uint64_t guest_pc, TranslationBlock *tb,
+ const void *start);
+
+/* Stop writing perf-<pid>.map and/or jit-<pid>.dump. */
+void perf_exit(void);
+#else
+static inline void perf_enable_perfmap(void)
+{
+}
+
+static inline void perf_enable_jitdump(void)
+{
+}
+
+static inline void perf_report_prologue(const void *start, size_t size)
+{
+}
+
+static inline void perf_report_code(uint64_t guest_pc, TranslationBlock *tb,
+ const void *start)
+{
+}
+
+static inline void perf_exit(void)
+{
+}
+#endif
+
+#endif
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "target_signal.h"
-#include "accel/tcg/debuginfo.h"
+#include "tcg/debuginfo.h"
#ifdef TARGET_ARM
#include "target/arm/cpu-features.h"
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
-#include "accel/tcg/perf.h"
+#include "tcg/perf.h"
#include "gdbstub/syscalls.h"
#include "qemu.h"
#include "user-internals.h"
#include "signal-common.h"
#include "loader.h"
#include "user-mmap.h"
-#include "accel/tcg/perf.h"
+#include "tcg/perf.h"
#ifdef CONFIG_SEMIHOSTING
#include "semihosting/semihost.h"
void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr,
MMUAccessType access_type, bool maperr, uintptr_t ra)
{
- const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops;
+ const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops;
if (tcg_ops->record_sigsegv) {
tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra);
void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr,
MMUAccessType access_type, uintptr_t ra)
{
- const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops;
+ const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops;
if (tcg_ops->record_sigbus) {
tcg_ops->record_sigbus(cpu, addr, access_type, ra);
return TARGET_PAGE_SIZE;
}
-int qemu_target_page_mask(void)
-{
- return TARGET_PAGE_MASK;
-}
-
int qemu_target_page_bits(void)
{
return TARGET_PAGE_BITS;
#endif
#include "sysemu/qtest.h"
#ifdef CONFIG_TCG
-#include "accel/tcg/perf.h"
+#include "tcg/perf.h"
#endif
#include "disas/disas.h"
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps alpha_tcg_ops = {
+static const TCGCPUOps alpha_tcg_ops = {
.initialize = alpha_translate_init,
.restore_state_to_opc = alpha_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc;
translator_loop(cpu, tb, max_insns, pc, host_pc, &alpha_tr_ops, &dc.base);
#endif
#ifdef CONFIG_TCG
-static const struct TCGCPUOps arm_tcg_ops = {
+static const TCGCPUOps arm_tcg_ops = {
.initialize = arm_translate_init,
.synchronize_from_tb = arm_cpu_synchronize_from_tb,
.debug_excp_handler = arm_debug_excp_handler,
cpu->reset_sctlr = 0x00000078;
}
-static const struct TCGCPUOps arm_v7m_tcg_ops = {
+static const TCGCPUOps arm_v7m_tcg_ops = {
.initialize = arm_translate_init,
.synchronize_from_tb = arm_cpu_synchronize_from_tb,
.debug_excp_handler = arm_debug_excp_handler,
/* generate intermediate code for basic block 'tb'. */
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc = { };
const TranslatorOps *ops = &arm_translator_ops;
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps avr_tcg_ops = {
+static const TCGCPUOps avr_tcg_ops = {
.initialize = avr_cpu_tcg_init,
.synchronize_from_tb = avr_cpu_synchronize_from_tb,
.restore_state_to_opc = avr_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc = { };
translator_loop(cs, tb, max_insns, pc, host_pc, &avr_tr_ops, &dc.base);
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps crisv10_tcg_ops = {
+static const TCGCPUOps crisv10_tcg_ops = {
.initialize = cris_initialize_crisv10_tcg,
.restore_state_to_opc = cris_restore_state_to_opc,
#endif /* !CONFIG_USER_ONLY */
};
-static const struct TCGCPUOps crisv32_tcg_ops = {
+static const TCGCPUOps crisv32_tcg_ops = {
.initialize = cris_initialize_tcg,
.restore_state_to_opc = cris_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc;
translator_loop(cs, tb, max_insns, pc, host_pc, &cris_tr_ops, &dc.base);
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps hexagon_tcg_ops = {
+static const TCGCPUOps hexagon_tcg_ops = {
.initialize = hexagon_translate_init,
.synchronize_from_tb = hexagon_cpu_synchronize_from_tb,
.restore_state_to_opc = hexagon_restore_state_to_opc,
g_assert(ctx->base.num_insns == 1);
}
- HEX_DEBUG_LOG("decode_packet: pc = 0x%x\n", ctx->base.pc_next);
+ HEX_DEBUG_LOG("decode_packet: pc = 0x%" VADDR_PRIx "\n",
+ ctx->base.pc_next);
HEX_DEBUG_LOG(" words = { ");
for (int i = 0; i < nwords; i++) {
HEX_DEBUG_LOG("0x%x, ", words[i]);
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext ctx;
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps hppa_tcg_ops = {
+static const TCGCPUOps hppa_tcg_ops = {
.initialize = hppa_translate_init,
.synchronize_from_tb = hppa_cpu_synchronize_from_tb,
.restore_state_to_opc = hppa_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext ctx;
translator_loop(cs, tb, max_insns, pc, host_pc, &hppa_tr_ops, &ctx.base);
*/
void x86_cpu_do_interrupt(CPUState *cpu);
#ifndef CONFIG_USER_ONLY
+void x86_cpu_exec_halt(CPUState *cpu);
+bool x86_need_replay_interrupt(int interrupt_request);
bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req);
#endif
#include "qemu/osdep.h"
#include "qemu/log.h"
+#include "qemu/main-loop.h"
#include "cpu.h"
#include "exec/helper-proto.h"
#include "exec/cpu_ldst.h"
}
}
+void x86_cpu_exec_halt(CPUState *cpu)
+{
+ if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
+ X86CPU *x86_cpu = X86_CPU(cpu);
+
+ bql_lock();
+ apic_poll_irq(x86_cpu->apic_state);
+ cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
+ bql_unlock();
+ }
+}
+
+bool x86_need_replay_interrupt(int interrupt_request)
+{
+ /*
+ * CPU_INTERRUPT_POLL is a virtual event which gets converted into a
+ * "real" interrupt event later. It does not need to be recorded for
+ * replay purposes.
+ */
+ return !(interrupt_request & CPU_INTERRUPT_POLL);
+}
+
bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
X86CPU *cpu = X86_CPU(cs);
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps x86_tcg_ops = {
+static const TCGCPUOps x86_tcg_ops = {
.initialize = tcg_x86_init,
.synchronize_from_tb = x86_cpu_synchronize_from_tb,
.restore_state_to_opc = x86_restore_state_to_opc,
#else
.tlb_fill = x86_cpu_tlb_fill,
.do_interrupt = x86_cpu_do_interrupt,
+ .cpu_exec_halt = x86_cpu_exec_halt,
.cpu_exec_interrupt = x86_cpu_exec_interrupt,
.do_unaligned_access = x86_cpu_do_unaligned_access,
.debug_excp_handler = breakpoint_handler,
.debug_check_breakpoint = x86_debug_check_breakpoint,
+ .need_replay_interrupt = x86_need_replay_interrupt,
#endif /* !CONFIG_USER_ONLY */
};
/* generate intermediate code for basic block 'tb'. */
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc;
#ifdef CONFIG_TCG
#include "hw/core/tcg-cpu-ops.h"
-static struct TCGCPUOps loongarch_tcg_ops = {
+static const TCGCPUOps loongarch_tcg_ops = {
.initialize = loongarch_translate_init,
.synchronize_from_tb = loongarch_cpu_synchronize_from_tb,
.restore_state_to_opc = loongarch_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext ctx;
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps m68k_tcg_ops = {
+static const TCGCPUOps m68k_tcg_ops = {
.initialize = m68k_tcg_init,
.restore_state_to_opc = m68k_restore_state_to_opc,
* for the 680x0 series, as well as those that are implemented
* but actually illegal for CPU32 or pre-68020.
*/
- qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %08x\n",
+ qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %" VADDR_PRIx "\n",
insn, s->base.pc_next);
gen_exception(s, s->base.pc_next, EXCP_ILLEGAL);
}
};
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc;
translator_loop(cpu, tb, max_insns, pc, host_pc, &m68k_tr_ops, &dc.base);
subdir('sparc')
subdir('tricore')
subdir('xtensa')
+
+specific_ss.add(files('target-common.c'))
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps mb_tcg_ops = {
+static const TCGCPUOps mb_tcg_ops = {
.initialize = mb_tcg_init,
.synchronize_from_tb = mb_cpu_synchronize_from_tb,
.restore_state_to_opc = mb_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc;
translator_loop(cpu, tb, max_insns, pc, host_pc, &mb_tr_ops, &dc.base);
* NB: cannot be const, as some elements are changed for specific
* mips hardware (see hw/mips/jazz.c).
*/
-static const struct TCGCPUOps mips_tcg_ops = {
+static const TCGCPUOps mips_tcg_ops = {
.initialize = mips_tcg_init,
.synchronize_from_tb = mips_cpu_synchronize_from_tb,
.restore_state_to_opc = mips_restore_state_to_opc,
if (ctx->hflags & MIPS_HFLAG_BMASK) {
#ifdef MIPS_DEBUG_DISAS
- LOG_DISAS("Branch in delay / forbidden slot at PC 0x"
- TARGET_FMT_lx "\n", ctx->base.pc_next);
+ LOG_DISAS("Branch in delay / forbidden slot at PC 0x%016"
+ VADDR_PRIx "\n", ctx->base.pc_next);
#endif
gen_reserved_instruction(ctx);
goto out;
if (ctx->hflags & MIPS_HFLAG_BMASK) {
#ifdef MIPS_DEBUG_DISAS
- LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx
- "\n", ctx->base.pc_next);
+ LOG_DISAS("Branch in delay / forbidden slot at PC 0x%016"
+ VADDR_PRIx "\n", ctx->base.pc_next);
#endif
gen_reserved_instruction(ctx);
return;
if (ctx->hflags & MIPS_HFLAG_BMASK) {
#ifdef MIPS_DEBUG_DISAS
- LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx
- "\n", ctx->base.pc_next);
+ LOG_DISAS("Branch in delay / forbidden slot at PC 0x%016"
+ VADDR_PRIx "\n", ctx->base.pc_next);
#endif
gen_reserved_instruction(ctx);
return;
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext ctx;
do { \
if (MIPS_DEBUG_DISAS) { \
qemu_log_mask(CPU_LOG_TB_IN_ASM, \
- TARGET_FMT_lx ": %08x Invalid %s %03x %03x %03x\n", \
+ "%016" VADDR_PRIx \
+ ": %08x Invalid %s %03x %03x %03x\n", \
ctx->base.pc_next, ctx->opcode, op, \
ctx->opcode >> 26, ctx->opcode & 0x3F, \
((ctx->opcode >> 16) & 0x1F)); \
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps nios2_tcg_ops = {
+static const TCGCPUOps nios2_tcg_ops = {
.initialize = nios2_tcg_init,
.restore_state_to_opc = nios2_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc;
translator_loop(cs, tb, max_insns, pc, host_pc, &nios2_tr_ops, &dc.base);
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps openrisc_tcg_ops = {
+static const TCGCPUOps openrisc_tcg_ops = {
.initialize = openrisc_translate_init,
.synchronize_from_tb = openrisc_cpu_synchronize_from_tb,
.restore_state_to_opc = openrisc_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext ctx;
#ifdef CONFIG_TCG
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps ppc_tcg_ops = {
+static const TCGCPUOps ppc_tcg_ops = {
.initialize = ppc_translate_init,
.restore_state_to_opc = ppc_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext ctx;
env->bins = data[1];
}
-static const struct TCGCPUOps riscv_tcg_ops = {
+static const TCGCPUOps riscv_tcg_ops = {
.initialize = riscv_translate_init,
.synchronize_from_tb = riscv_cpu_synchronize_from_tb,
.restore_state_to_opc = riscv_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext ctx;
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps rx_tcg_ops = {
+static const TCGCPUOps rx_tcg_ops = {
.initialize = rx_translate_init,
.synchronize_from_tb = rx_cpu_synchronize_from_tb,
.restore_state_to_opc = rx_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc;
#ifdef CONFIG_TCG
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps s390_tcg_ops = {
+static const TCGCPUOps s390_tcg_ops = {
.initialize = s390x_translate_init,
.restore_state_to_opc = s390x_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc;
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps superh_tcg_ops = {
+static const TCGCPUOps superh_tcg_ops = {
.initialize = sh4_translate_init,
.synchronize_from_tb = superh_cpu_synchronize_from_tb,
.restore_state_to_opc = superh_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext ctx;
#ifdef CONFIG_TCG
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps sparc_tcg_ops = {
+static const TCGCPUOps sparc_tcg_ops = {
.initialize = sparc_tcg_init,
.synchronize_from_tb = sparc_cpu_synchronize_from_tb,
.restore_state_to_opc = sparc_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc = {};
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "qemu/osdep.h"
+
+#include "cpu.h"
+#include "exec/target_page.h"
+
+int qemu_target_page_mask(void)
+{
+ return TARGET_PAGE_MASK;
+}
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps tricore_tcg_ops = {
+static const TCGCPUOps tricore_tcg_ops = {
.initialize = tricore_tcg_init,
.synchronize_from_tb = tricore_cpu_synchronize_from_tb,
.restore_state_to_opc = tricore_restore_state_to_opc,
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext ctx;
translator_loop(cs, tb, max_insns, pc, host_pc,
#include "hw/core/tcg-cpu-ops.h"
-static const struct TCGCPUOps xtensa_tcg_ops = {
+static const TCGCPUOps xtensa_tcg_ops = {
.initialize = xtensa_translate_init,
.debug_excp_handler = xtensa_breakpoint_handler,
.restore_state_to_opc = xtensa_restore_state_to_opc,
};
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns,
- target_ulong pc, void *host_pc)
+ vaddr pc, void *host_pc)
{
DisasContext dc = {};
translator_loop(cpu, tb, max_insns, pc, host_pc,
--- /dev/null
+/*
+ * Debug information support.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/lockable.h"
+#include "tcg/debuginfo.h"
+
+#include <elfutils/libdwfl.h>
+
+static QemuMutex lock;
+static Dwfl *dwfl;
+static const Dwfl_Callbacks dwfl_callbacks = {
+ .find_elf = NULL,
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .section_address = NULL,
+ .debuginfo_path = NULL,
+};
+
+__attribute__((constructor))
+static void debuginfo_init(void)
+{
+ qemu_mutex_init(&lock);
+}
+
+void debuginfo_report_elf(const char *name, int fd, uint64_t bias)
+{
+ QEMU_LOCK_GUARD(&lock);
+
+ if (dwfl) {
+ dwfl_report_begin_add(dwfl);
+ } else {
+ dwfl = dwfl_begin(&dwfl_callbacks);
+ }
+
+ if (dwfl) {
+ dwfl_report_elf(dwfl, name, name, fd, bias, true);
+ dwfl_report_end(dwfl, NULL, NULL);
+ }
+}
+
+void debuginfo_lock(void)
+{
+ qemu_mutex_lock(&lock);
+}
+
+void debuginfo_query(struct debuginfo_query *q, size_t n)
+{
+ const char *symbol, *file;
+ Dwfl_Module *dwfl_module;
+ Dwfl_Line *dwfl_line;
+ GElf_Off dwfl_offset;
+ GElf_Sym dwfl_sym;
+ size_t i;
+ int line;
+
+ if (!dwfl) {
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ dwfl_module = dwfl_addrmodule(dwfl, q[i].address);
+ if (!dwfl_module) {
+ continue;
+ }
+
+ if (q[i].flags & DEBUGINFO_SYMBOL) {
+ symbol = dwfl_module_addrinfo(dwfl_module, q[i].address,
+ &dwfl_offset, &dwfl_sym,
+ NULL, NULL, NULL);
+ if (symbol) {
+ q[i].symbol = symbol;
+ q[i].offset = dwfl_offset;
+ }
+ }
+
+ if (q[i].flags & DEBUGINFO_LINE) {
+ dwfl_line = dwfl_module_getsrc(dwfl_module, q[i].address);
+ if (dwfl_line) {
+ file = dwfl_lineinfo(dwfl_line, NULL, &line, 0, NULL, NULL);
+ if (file) {
+ q[i].file = file;
+ q[i].line = line;
+ }
+ }
+ }
+ }
+}
+
+void debuginfo_unlock(void)
+{
+ qemu_mutex_unlock(&lock);
+}
tcg_ss.add(files('tci.c'))
endif
+tcg_ss.add(when: libdw, if_true: files('debuginfo.c'))
+if host_os == 'linux'
+ tcg_ss.add(files('perf.c'))
+endif
+
tcg_ss = tcg_ss.apply({})
libtcg_user = static_library('tcg_user',
--- /dev/null
+/*
+ * Linux perf perf-<pid>.map and jit-<pid>.dump integration.
+ *
+ * The jitdump spec can be found at [1].
+ *
+ * [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/tools/perf/Documentation/jitdump-specification.txt
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "elf.h"
+#include "exec/target_page.h"
+#include "exec/translation-block.h"
+#include "qemu/timer.h"
+#include "tcg/debuginfo.h"
+#include "tcg/perf.h"
+#include "tcg/tcg.h"
+
+static FILE *safe_fopen_w(const char *path)
+{
+ int saved_errno;
+ FILE *f;
+ int fd;
+
+ /* Delete the old file, if any. */
+ unlink(path);
+
+ /* Avoid symlink attacks by using O_CREAT | O_EXCL. */
+ fd = open(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ return NULL;
+ }
+
+ /* Convert fd to FILE*. */
+ f = fdopen(fd, "w");
+ if (f == NULL) {
+ saved_errno = errno;
+ close(fd);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ return f;
+}
+
+static FILE *perfmap;
+
+void perf_enable_perfmap(void)
+{
+ char map_file[32];
+
+ snprintf(map_file, sizeof(map_file), "/tmp/perf-%d.map", getpid());
+ perfmap = safe_fopen_w(map_file);
+ if (perfmap == NULL) {
+ warn_report("Could not open %s: %s, proceeding without perfmap",
+ map_file, strerror(errno));
+ }
+}
+
+/* Get PC and size of code JITed for guest instruction #INSN. */
+static void get_host_pc_size(uintptr_t *host_pc, uint16_t *host_size,
+ const void *start, size_t insn)
+{
+ uint16_t start_off = insn ? tcg_ctx->gen_insn_end_off[insn - 1] : 0;
+
+ if (host_pc) {
+ *host_pc = (uintptr_t)start + start_off;
+ }
+ if (host_size) {
+ *host_size = tcg_ctx->gen_insn_end_off[insn] - start_off;
+ }
+}
+
+static const char *pretty_symbol(const struct debuginfo_query *q, size_t *len)
+{
+ static __thread char buf[64];
+ int tmp;
+
+ if (!q->symbol) {
+ tmp = snprintf(buf, sizeof(buf), "guest-0x%"PRIx64, q->address);
+ if (len) {
+ *len = MIN(tmp + 1, sizeof(buf));
+ }
+ return buf;
+ }
+
+ if (!q->offset) {
+ if (len) {
+ *len = strlen(q->symbol) + 1;
+ }
+ return q->symbol;
+ }
+
+ tmp = snprintf(buf, sizeof(buf), "%s+0x%"PRIx64, q->symbol, q->offset);
+ if (len) {
+ *len = MIN(tmp + 1, sizeof(buf));
+ }
+ return buf;
+}
+
+static void write_perfmap_entry(const void *start, size_t insn,
+ const struct debuginfo_query *q)
+{
+ uint16_t host_size;
+ uintptr_t host_pc;
+
+ get_host_pc_size(&host_pc, &host_size, start, insn);
+ fprintf(perfmap, "%"PRIxPTR" %"PRIx16" %s\n",
+ host_pc, host_size, pretty_symbol(q, NULL));
+}
+
+static FILE *jitdump;
+static size_t perf_marker_size;
+static void *perf_marker = MAP_FAILED;
+
+#define JITHEADER_MAGIC 0x4A695444
+#define JITHEADER_VERSION 1
+
+struct jitheader {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t total_size;
+ uint32_t elf_mach;
+ uint32_t pad1;
+ uint32_t pid;
+ uint64_t timestamp;
+ uint64_t flags;
+};
+
+enum jit_record_type {
+ JIT_CODE_LOAD = 0,
+ JIT_CODE_DEBUG_INFO = 2,
+};
+
+struct jr_prefix {
+ uint32_t id;
+ uint32_t total_size;
+ uint64_t timestamp;
+};
+
+struct jr_code_load {
+ struct jr_prefix p;
+
+ uint32_t pid;
+ uint32_t tid;
+ uint64_t vma;
+ uint64_t code_addr;
+ uint64_t code_size;
+ uint64_t code_index;
+};
+
+struct debug_entry {
+ uint64_t addr;
+ int lineno;
+ int discrim;
+ const char name[];
+};
+
+struct jr_code_debug_info {
+ struct jr_prefix p;
+
+ uint64_t code_addr;
+ uint64_t nr_entry;
+ struct debug_entry entries[];
+};
+
+static uint32_t get_e_machine(void)
+{
+ Elf64_Ehdr elf_header;
+ FILE *exe;
+ size_t n;
+
+ QEMU_BUILD_BUG_ON(offsetof(Elf32_Ehdr, e_machine) !=
+ offsetof(Elf64_Ehdr, e_machine));
+
+ exe = fopen("/proc/self/exe", "r");
+ if (exe == NULL) {
+ return EM_NONE;
+ }
+
+ n = fread(&elf_header, sizeof(elf_header), 1, exe);
+ fclose(exe);
+ if (n != 1) {
+ return EM_NONE;
+ }
+
+ return elf_header.e_machine;
+}
+
+void perf_enable_jitdump(void)
+{
+ struct jitheader header;
+ char jitdump_file[32];
+
+ if (!use_rt_clock) {
+ warn_report("CLOCK_MONOTONIC is not available, proceeding without jitdump");
+ return;
+ }
+
+ snprintf(jitdump_file, sizeof(jitdump_file), "jit-%d.dump", getpid());
+ jitdump = safe_fopen_w(jitdump_file);
+ if (jitdump == NULL) {
+ warn_report("Could not open %s: %s, proceeding without jitdump",
+ jitdump_file, strerror(errno));
+ return;
+ }
+
+ /*
+ * `perf inject` will see that the mapped file name in the corresponding
+ * PERF_RECORD_MMAP or PERF_RECORD_MMAP2 event is of the form jit-%d.dump
+ * and will process it as a jitdump file.
+ */
+ perf_marker_size = qemu_real_host_page_size();
+ perf_marker = mmap(NULL, perf_marker_size, PROT_READ | PROT_EXEC,
+ MAP_PRIVATE, fileno(jitdump), 0);
+ if (perf_marker == MAP_FAILED) {
+ warn_report("Could not map %s: %s, proceeding without jitdump",
+ jitdump_file, strerror(errno));
+ fclose(jitdump);
+ jitdump = NULL;
+ return;
+ }
+
+ header.magic = JITHEADER_MAGIC;
+ header.version = JITHEADER_VERSION;
+ header.total_size = sizeof(header);
+ header.elf_mach = get_e_machine();
+ header.pad1 = 0;
+ header.pid = getpid();
+ header.timestamp = get_clock();
+ header.flags = 0;
+ fwrite(&header, sizeof(header), 1, jitdump);
+}
+
+void perf_report_prologue(const void *start, size_t size)
+{
+ if (perfmap) {
+ fprintf(perfmap, "%"PRIxPTR" %zx tcg-prologue-buffer\n",
+ (uintptr_t)start, size);
+ }
+}
+
+/* Write a JIT_CODE_DEBUG_INFO jitdump entry. */
+static void write_jr_code_debug_info(const void *start,
+ const struct debuginfo_query *q,
+ size_t icount)
+{
+ struct jr_code_debug_info rec;
+ struct debug_entry ent;
+ uintptr_t host_pc;
+ int insn;
+
+ /* Write the header. */
+ rec.p.id = JIT_CODE_DEBUG_INFO;
+ rec.p.total_size = sizeof(rec) + sizeof(ent) + 1;
+ rec.p.timestamp = get_clock();
+ rec.code_addr = (uintptr_t)start;
+ rec.nr_entry = 1;
+ for (insn = 0; insn < icount; insn++) {
+ if (q[insn].file) {
+ rec.p.total_size += sizeof(ent) + strlen(q[insn].file) + 1;
+ rec.nr_entry++;
+ }
+ }
+ fwrite(&rec, sizeof(rec), 1, jitdump);
+
+ /* Write the main debug entries. */
+ for (insn = 0; insn < icount; insn++) {
+ if (q[insn].file) {
+ get_host_pc_size(&host_pc, NULL, start, insn);
+ ent.addr = host_pc;
+ ent.lineno = q[insn].line;
+ ent.discrim = 0;
+ fwrite(&ent, sizeof(ent), 1, jitdump);
+ fwrite(q[insn].file, strlen(q[insn].file) + 1, 1, jitdump);
+ }
+ }
+
+ /* Write the trailing debug_entry. */
+ ent.addr = (uintptr_t)start + tcg_ctx->gen_insn_end_off[icount - 1];
+ ent.lineno = 0;
+ ent.discrim = 0;
+ fwrite(&ent, sizeof(ent), 1, jitdump);
+ fwrite("", 1, 1, jitdump);
+}
+
+/* Write a JIT_CODE_LOAD jitdump entry. */
+static void write_jr_code_load(const void *start, uint16_t host_size,
+ const struct debuginfo_query *q)
+{
+ static uint64_t code_index;
+ struct jr_code_load rec;
+ const char *symbol;
+ size_t symbol_size;
+
+ symbol = pretty_symbol(q, &symbol_size);
+ rec.p.id = JIT_CODE_LOAD;
+ rec.p.total_size = sizeof(rec) + symbol_size + host_size;
+ rec.p.timestamp = get_clock();
+ rec.pid = getpid();
+ rec.tid = qemu_get_thread_id();
+ rec.vma = (uintptr_t)start;
+ rec.code_addr = (uintptr_t)start;
+ rec.code_size = host_size;
+ rec.code_index = code_index++;
+ fwrite(&rec, sizeof(rec), 1, jitdump);
+ fwrite(symbol, symbol_size, 1, jitdump);
+ fwrite(start, host_size, 1, jitdump);
+}
+
+void perf_report_code(uint64_t guest_pc, TranslationBlock *tb,
+ const void *start)
+{
+ struct debuginfo_query *q;
+ size_t insn, start_words;
+ uint64_t *gen_insn_data;
+
+ if (!perfmap && !jitdump) {
+ return;
+ }
+
+ q = g_try_malloc0_n(tb->icount, sizeof(*q));
+ if (!q) {
+ return;
+ }
+
+ debuginfo_lock();
+
+ /* Query debuginfo for each guest instruction. */
+ gen_insn_data = tcg_ctx->gen_insn_data;
+ start_words = tcg_ctx->insn_start_words;
+
+ for (insn = 0; insn < tb->icount; insn++) {
+ /* FIXME: This replicates the restore_state_to_opc() logic. */
+ q[insn].address = gen_insn_data[insn * start_words + 0];
+ if (tb_cflags(tb) & CF_PCREL) {
+ q[insn].address |= (guest_pc & qemu_target_page_mask());
+ }
+ q[insn].flags = DEBUGINFO_SYMBOL | (jitdump ? DEBUGINFO_LINE : 0);
+ }
+ debuginfo_query(q, tb->icount);
+
+ /* Emit perfmap entries if needed. */
+ if (perfmap) {
+ flockfile(perfmap);
+ for (insn = 0; insn < tb->icount; insn++) {
+ write_perfmap_entry(start, insn, &q[insn]);
+ }
+ funlockfile(perfmap);
+ }
+
+ /* Emit jitdump entries if needed. */
+ if (jitdump) {
+ flockfile(jitdump);
+ write_jr_code_debug_info(start, q, tb->icount);
+ write_jr_code_load(start, tcg_ctx->gen_insn_end_off[tb->icount - 1],
+ q);
+ funlockfile(jitdump);
+ }
+
+ debuginfo_unlock();
+ g_free(q);
+}
+
+void perf_exit(void)
+{
+ if (perfmap) {
+ fclose(perfmap);
+ perfmap = NULL;
+ }
+
+ if (perf_marker != MAP_FAILED) {
+ munmap(perf_marker, perf_marker_size);
+ perf_marker = MAP_FAILED;
+ }
+
+ if (jitdump) {
+ fclose(jitdump);
+ jitdump = NULL;
+ }
+}
#include "tcg/tcg-ldst.h"
#include "tcg/tcg-temp-internal.h"
#include "tcg-internal.h"
-#include "accel/tcg/perf.h"
+#include "tcg/perf.h"
#ifdef CONFIG_USER_ONLY
#include "exec/user/guest-base.h"
#endif
sleep(1)
log(output, "GDB CMD: %s" % (gdb_cmd))
- result = subprocess.call(gdb_cmd, shell=True, stdout=output, stderr=stderr)
+ gdb_env = dict(os.environ)
+ gdb_pythonpath = gdb_env.get("PYTHONPATH", "").split(os.pathsep)
+ gdb_pythonpath.append(os.path.dirname(os.path.realpath(__file__)))
+ gdb_env["PYTHONPATH"] = os.pathsep.join(gdb_pythonpath)
+ result = subprocess.call(gdb_cmd, shell=True, stdout=output, stderr=stderr,
+ env=gdb_env)
# A result of greater than 128 indicates a fatal signal (likely a
# crash due to gdb internal failure). That's a problem for GDB and
--- /dev/null
+"""Helper functions for gdbstub testing
+
+"""
+from __future__ import print_function
+import gdb
+import os
+import sys
+import traceback
+
+fail_count = 0
+
+
+def report(cond, msg):
+ """Report success/fail of a test"""
+ if cond:
+ print("PASS: {}".format(msg))
+ else:
+ print("FAIL: {}".format(msg))
+ global fail_count
+ fail_count += 1
+
+
+def main(test, expected_arch=None):
+ """Run a test function
+
+ This runs as the script it sourced (via -x, via run-test.py)."""
+ try:
+ inferior = gdb.selected_inferior()
+ arch = inferior.architecture()
+ print("ATTACHED: {}".format(arch.name()))
+ if expected_arch is not None:
+ report(arch.name() == expected_arch,
+ "connected to {}".format(expected_arch))
+ except (gdb.error, AttributeError):
+ print("SKIP: not connected")
+ exit(0)
+
+ if gdb.parse_and_eval("$pc") == 0:
+ print("SKIP: PC not set")
+ exit(0)
+
+ try:
+ test()
+ except:
+ print("GDB Exception:")
+ traceback.print_exc(file=sys.stdout)
+ global fail_count
+ fail_count += 1
+ if "QEMU_TEST_INTERACTIVE" in os.environ:
+ import code
+ code.InteractiveConsole(locals=globals()).interact()
+ raise
+
+ try:
+ gdb.execute("kill")
+ except gdb.error:
+ pass
+
+ print("All tests complete: {} failures".format(fail_count))
+ exit(fail_count)
#
import gdb
-import sys
+from test_gdbstub import main, report
initial_vlen = 0
-failcount = 0
-def report(cond, msg):
- "Report success/fail of test"
- if cond:
- print ("PASS: %s" % (msg))
- else:
- print ("FAIL: %s" % (msg))
- global failcount
- failcount += 1
class TestBreakpoint(gdb.Breakpoint):
def __init__(self, sym_name="__sve_ld_done"):
gdb.execute("c")
-#
-# This runs as the script it sourced (via -x, via run-test.py)
-#
-try:
- inferior = gdb.selected_inferior()
- arch = inferior.architecture()
- report(arch.name() == "aarch64", "connected to aarch64")
-except (gdb.error, AttributeError):
- print("SKIPPING (not connected)", file=sys.stderr)
- exit(0)
-
-try:
- # Run the actual tests
- run_test()
-except:
- print ("GDB Exception: %s" % (sys.exc_info()[0]))
- failcount += 1
- import code
- code.InteractiveConsole(locals=globals()).interact()
- raise
-print("All tests complete: %d failures" % failcount)
-exit(failcount)
+main(run_test, expected_arch="aarch64")
#
import gdb
-import sys
+from test_gdbstub import main, report
MAGIC = 0xDEADBEEF
-failcount = 0
-
-def report(cond, msg):
- "Report success/fail of test"
- if cond:
- print ("PASS: %s" % (msg))
- else:
- print ("FAIL: %s" % (msg))
- global failcount
- failcount += 1
def run_test():
"Run through the tests one by one"
report(str(v.type) == "uint64_t", "size of %s" % (reg))
report(int(v) == MAGIC, "%s is 0x%x" % (reg, MAGIC))
-#
-# This runs as the script it sourced (via -x, via run-test.py)
-#
-try:
- inferior = gdb.selected_inferior()
- arch = inferior.architecture()
- report(arch.name() == "aarch64", "connected to aarch64")
-except (gdb.error, AttributeError):
- print("SKIPPING (not connected)", file=sys.stderr)
- exit(0)
-
-try:
- # Run the actual tests
- run_test()
-except:
- print ("GDB Exception: %s" % (sys.exc_info()[0]))
- failcount += 1
-
-print("All tests complete: %d failures" % failcount)
-exit(failcount)
+main(run_test, expected_arch="aarch64")
--bin $< --test $(MULTIARCH_SRC)/gdbstub/registers.py, \
checking register enumeration)
+run-gdbstub-prot-none: prot-none
+ $(call run-test, $@, env PROT_NONE_PY=1 $(GDB_SCRIPT) \
+ --gdb $(GDB) \
+ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
+ --bin $< --test $(MULTIARCH_SRC)/gdbstub/prot-none.py, \
+ accessing PROT_NONE memory)
+
else
run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst -%,,$(TARGET_NAME)) support")
endif
EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \
run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint \
- run-gdbstub-registers
+ run-gdbstub-registers run-gdbstub-prot-none
# ARM Compatible Semi Hosting Tests
#
#
import gdb
-import sys
-
-failcount = 0
-
-
-def report(cond, msg):
- "Report success/fail of test"
- if cond:
- print("PASS: %s" % (msg))
- else:
- print("FAIL: %s" % (msg))
- global failcount
- failcount += 1
+from test_gdbstub import main, report
def check_interrupt(thread):
Test if interrupting the code always lands us on the same thread when
running with scheduler-lock enabled.
"""
+ if len(gdb.selected_inferior().threads()) == 1:
+ print("SKIP: set to run on a single thread")
+ exit(0)
gdb.execute("set scheduler-locking on")
for thread in gdb.selected_inferior().threads():
"thread %d resumes correctly on interrupt" % thread.num)
-#
-# This runs as the script it sourced (via -x, via run-test.py)
-#
-try:
- inferior = gdb.selected_inferior()
- arch = inferior.architecture()
- print("ATTACHED: %s" % arch.name())
-except (gdb.error, AttributeError):
- print("SKIPPING (not connected)", file=sys.stderr)
- exit(0)
-
-if gdb.parse_and_eval('$pc') == 0:
- print("SKIP: PC not set")
- exit(0)
-if len(gdb.selected_inferior().threads()) == 1:
- print("SKIP: set to run on a single thread")
- exit(0)
-
-try:
- # Run the actual tests
- run_test()
-except (gdb.error):
- print("GDB Exception: %s" % (sys.exc_info()[0]))
- failcount += 1
- pass
-
-# Finally kill the inferior and exit gdb with a count of failures
-gdb.execute("kill")
-exit(failcount)
+main(run_test)
import gdb
import sys
-
-failcount = 0
-
-
-def report(cond, msg):
- "Report success/fail of test"
- if cond:
- print("PASS: %s" % (msg))
- else:
- print("FAIL: %s" % (msg))
- global failcount
- failcount += 1
+from test_gdbstub import main, report
def check_step():
report(cbp.hit_count == 0, "didn't reach backstop")
-#
-# This runs as the script it sourced (via -x, via run-test.py)
-#
-try:
- inferior = gdb.selected_inferior()
- arch = inferior.architecture()
- print("ATTACHED: %s" % arch.name())
-except (gdb.error, AttributeError):
- print("SKIPPING (not connected)", file=sys.stderr)
- exit(0)
-
-if gdb.parse_and_eval('$pc') == 0:
- print("SKIP: PC not set")
- exit(0)
-
-try:
- # Run the actual tests
- run_test()
-except (gdb.error):
- print("GDB Exception: %s" % (sys.exc_info()[0]))
- failcount += 1
- pass
-
-# Finally kill the inferior and exit gdb with a count of failures
-gdb.execute("kill")
-exit(failcount)
+
+main(run_test)
--- /dev/null
+"""Test that GDB can access PROT_NONE pages.
+
+This runs as a sourced script (via -x, via run-test.py).
+
+SPDX-License-Identifier: GPL-2.0-or-later
+"""
+import ctypes
+from test_gdbstub import main, report
+
+
+def probe_proc_self_mem():
+ buf = ctypes.create_string_buffer(b'aaa')
+ try:
+ with open("/proc/self/mem", "rb") as fp:
+ fp.seek(ctypes.addressof(buf))
+ return fp.read(3) == b'aaa'
+ except OSError:
+ return False
+
+
+def run_test():
+ """Run through the tests one by one"""
+ if not probe_proc_self_mem:
+ print("SKIP: /proc/self/mem is not usable")
+ exit(0)
+ gdb.Breakpoint("break_here")
+ gdb.execute("continue")
+ val = gdb.parse_and_eval("*(char[2] *)q").string()
+ report(val == "42", "{} == 42".format(val))
+ gdb.execute("set *(char[3] *)q = \"24\"")
+ gdb.execute("continue")
+ exitcode = int(gdb.parse_and_eval("$_exitcode"))
+ report(exitcode == 0, "{} == 0".format(exitcode))
+
+
+main(run_test)
# SPDX-License-Identifier: GPL-2.0-or-later
import gdb
-import sys
import xml.etree.ElementTree as ET
+from test_gdbstub import main, report
-initial_vlen = 0
-failcount = 0
-def report(cond, msg):
- "Report success/fail of test."
- if cond:
- print("PASS: %s" % (msg))
- else:
- print("FAIL: %s" % (msg))
- global failcount
- failcount += 1
+initial_vlen = 0
def fetch_xml_regmap():
return reg_map
+
def get_register_by_regnum(reg_map, regnum):
"""
Helper to find a register from the map via its XML regnum
return entry
return None
+
def crosscheck_remote_xml(reg_map):
"""
Cross-check the list of remote-registers with the XML info.
elif "seen" not in x_reg:
print(f"{x_reg} wasn't seen in remote-registers")
+
def initial_register_read(reg_map):
"""
Do an initial read of all registers that we know gdb cares about
complete_and_diff(reg_map)
-#
-# This runs as the script it sourced (via -x, via run-test.py)
-#
-try:
- inferior = gdb.selected_inferior()
- arch = inferior.architecture()
- print("ATTACHED: %s" % arch.name())
-except (gdb.error, AttributeError):
- print("SKIPPING (not connected)", file=sys.stderr)
- exit(0)
-
-if gdb.parse_and_eval('$pc') == 0:
- print("SKIP: PC not set")
- exit(0)
-
-try:
- run_test()
-except (gdb.error):
- print ("GDB Exception: %s" % (sys.exc_info()[0]))
- failcount += 1
- pass
-
-print("All tests complete: %d failures" % failcount)
-exit(failcount)
+main(run_test)
#
import gdb
-import sys
+from test_gdbstub import main, report
+
initial_vlen = 0
-failcount = 0
-def report(cond, msg):
- "Report success/fail of test"
- if cond:
- print("PASS: %s" % (msg))
- else:
- print("FAIL: %s" % (msg))
- global failcount
- failcount += 1
def check_break(sym_name):
"Setup breakpoint, continue and check we stopped."
bp.delete()
+
def run_test():
"Run through the tests one by one"
# finally check we don't barf inspecting registers
gdb.execute("info registers")
-#
-# This runs as the script it sourced (via -x, via run-test.py)
-#
-try:
- inferior = gdb.selected_inferior()
- arch = inferior.architecture()
- print("ATTACHED: %s" % arch.name())
-except (gdb.error, AttributeError):
- print("SKIPPING (not connected)", file=sys.stderr)
- exit(0)
-
-if gdb.parse_and_eval('$pc') == 0:
- print("SKIP: PC not set")
- exit(0)
-
-try:
- # Run the actual tests
- run_test()
-except (gdb.error):
- print ("GDB Exception: %s" % (sys.exc_info()[0]))
- failcount += 1
- pass
-
-print("All tests complete: %d failures" % failcount)
-exit(failcount)
+
+main(run_test)
This runs as a sourced script (via -x, via run-test.py)."""
from __future__ import print_function
import gdb
-import sys
-
-
-n_failures = 0
-
-
-def report(cond, msg):
- """Report success/fail of a test"""
- if cond:
- print("PASS: {}".format(msg))
- else:
- print("FAIL: {}".format(msg))
- global n_failures
- n_failures += 1
+from test_gdbstub import main, report
def run_test():
# report("/sha1" in mappings, "Found the test binary name in the mappings")
-def main():
- """Prepare the environment and run through the tests"""
- try:
- inferior = gdb.selected_inferior()
- print("ATTACHED: {}".format(inferior.architecture().name()))
- except (gdb.error, AttributeError):
- print("SKIPPING (not connected)")
- exit(0)
-
- if gdb.parse_and_eval('$pc') == 0:
- print("SKIP: PC not set")
- exit(0)
-
- try:
- # Run the actual tests
- run_test()
- except gdb.error:
- report(False, "GDB Exception: {}".format(sys.exc_info()[0]))
- print("All tests complete: %d failures" % n_failures)
- exit(n_failures)
-
-
-main()
+main(run_test)
#
import gdb
-import sys
+from test_gdbstub import main, report
-failcount = 0
-
-def report(cond, msg):
- "Report success/fail of test"
- if cond:
- print ("PASS: %s" % (msg))
- else:
- print ("FAIL: %s" % (msg))
- global failcount
- failcount += 1
def run_test():
"Run through the tests one by one"
report(isinstance(auxv, str), "Fetched auxv from inferior")
report(auxv.find("sha1"), "Found test binary name in auxv")
-#
-# This runs as the script it sourced (via -x, via run-test.py)
-#
-try:
- inferior = gdb.selected_inferior()
- arch = inferior.architecture()
- print("ATTACHED: %s" % arch.name())
-except (gdb.error, AttributeError):
- print("SKIPPING (not connected)", file=sys.stderr)
- exit(0)
-
-if gdb.parse_and_eval('$pc') == 0:
- print("SKIP: PC not set")
- exit(0)
-
-try:
- # Run the actual tests
- run_test()
-except (gdb.error):
- print ("GDB Exception: %s" % (sys.exc_info()[0]))
- failcount += 1
- pass
-print("All tests complete: %d failures" % failcount)
-exit(failcount)
+main(run_test)
#
import gdb
-import sys
+from test_gdbstub import main, report
-failcount = 0
-
-def report(cond, msg):
- "Report success/fail of test"
- if cond:
- print ("PASS: %s" % (msg))
- else:
- print ("FAIL: %s" % (msg))
- global failcount
- failcount += 1
def run_test():
"Run through the tests one by one"
frame = gdb.selected_frame()
report(str(frame.function()) == "thread1_func", "break @ %s"%frame)
-#
-# This runs as the script it sourced (via -x, via run-test.py)
-#
-try:
- inferior = gdb.selected_inferior()
- arch = inferior.architecture()
- print("ATTACHED: %s" % arch.name())
-except (gdb.error, AttributeError):
- print("SKIPPING (not connected)", file=sys.stderr)
- exit(0)
-
-if gdb.parse_and_eval('$pc') == 0:
- print("SKIP: PC not set")
- exit(0)
-
-try:
- # Run the actual tests
- run_test()
-except (gdb.error):
- print ("GDB Exception: %s" % (sys.exc_info()[0]))
- failcount += 1
- pass
-print("All tests complete: %d failures" % failcount)
-exit(failcount)
+main(run_test)
--- /dev/null
+/*
+ * Test that GDB can access PROT_NONE pages.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+void break_here(void *q)
+{
+}
+
+int main(void)
+{
+ long pagesize = sysconf(_SC_PAGESIZE);
+ void *p, *q;
+ int err;
+
+ p = mmap(NULL, pagesize * 2, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ assert(p != MAP_FAILED);
+ q = p + pagesize - 1;
+ strcpy(q, "42");
+
+ err = mprotect(p, pagesize * 2, PROT_NONE);
+ assert(err == 0);
+
+ break_here(q);
+
+ err = mprotect(p, pagesize * 2, PROT_READ);
+ assert(err == 0);
+ if (getenv("PROT_NONE_PY")) {
+ assert(strcmp(q, "24") == 0);
+ }
+
+ return EXIT_SUCCESS;
+}
#
import gdb
-import sys
-
-failcount = 0
-
-
-def report(cond, msg):
- """Report success/fail of test"""
- if cond:
- print("PASS: %s" % (msg))
- else:
- print("FAIL: %s" % (msg))
- global failcount
- failcount += 1
+from test_gdbstub import main, report
def run_test():
gdb.Breakpoint("_exit")
gdb.execute("c")
status = int(gdb.parse_and_eval("$r2"))
- report(status == 0, "status == 0");
-
-
-#
-# This runs as the script it sourced (via -x, via run-test.py)
-#
-try:
- inferior = gdb.selected_inferior()
- arch = inferior.architecture()
- print("ATTACHED: %s" % arch.name())
-except (gdb.error, AttributeError):
- print("SKIPPING (not connected)", file=sys.stderr)
- exit(0)
-
-if gdb.parse_and_eval("$pc") == 0:
- print("SKIP: PC not set")
- exit(0)
+ report(status == 0, "status == 0")
-try:
- # Run the actual tests
- run_test()
-except (gdb.error):
- print("GDB Exception: %s" % (sys.exc_info()[0]))
- failcount += 1
- pass
-print("All tests complete: %d failures" % failcount)
-exit(failcount)
+main(run_test)
This runs as a sourced script (via -x, via run-test.py)."""
from __future__ import print_function
import gdb
-import sys
-
-
-n_failures = 0
-
-
-def report(cond, msg):
- """Report success/fail of a test"""
- if cond:
- print("PASS: {}".format(msg))
- else:
- print("FAIL: {}".format(msg))
- global n_failures
- n_failures += 1
+from test_gdbstub import main, report
def run_test():
gdb.execute("si")
-def main():
- """Prepare the environment and run through the tests"""
- try:
- inferior = gdb.selected_inferior()
- print("ATTACHED: {}".format(inferior.architecture().name()))
- except (gdb.error, AttributeError):
- print("SKIPPING (not connected)")
- exit(0)
-
- if gdb.parse_and_eval('$pc') == 0:
- print("SKIP: PC not set")
- exit(0)
-
- try:
- # Run the actual tests
- run_test()
- except gdb.error:
- report(False, "GDB Exception: {}".format(sys.exc_info()[0]))
- print("All tests complete: %d failures" % n_failures)
- exit(n_failures)
-
-
-main()
+main(run_test)