mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'pull-maintainer-updates-060324-1' of https://gitlab.com/stsquad/qemu into staging
maintainer updates (tests, gdbstub, plugins): - expand QOS_PATH_MAX_ELEMENT_SIZE to avoid LTO issues - support fork-follow-mode in gdbstub - new thread-safe scoreboard API for TCG plugins - suppress showing opcodes in plugin disassembly # -----BEGIN PGP SIGNATURE----- # # iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmXoY7oACgkQ+9DbCVqe # KkTdTwf8D8nUB+Ee6LuglW36vtd1ETdMfUmfRis7RIBsXZZ0Tg4+8LyfKkNi1vCL # UMdWQTkSW79RfXr21QEtETokwLZ0CWQMdxDAWfOiz4S+uDgQyBE+lwUsy0mHBmd7 # +J4SQb3adoZ+//9KMJhRU1wL9j3ygpEoKHVJonDObU6K5XuhE18JuBE44q7FqkWl # 0VhoLDgNxrf2PqT+LLP/O3MFLDXPVKbzrZYQF0IoqBTlcqShCoaykhSwiwCZ4Sqq # NO9hVwZIOFOcOF4F6ZqRXaZrwERldoBwG+BeIx1ah20vKFVT12y02dQqdP/oKwe+ # /PXFXDdzs4yMOghb4Go6SiKlKT5g4A== # =s1lF # -----END PGP SIGNATURE----- # gpg: Signature made Wed 06 Mar 2024 12:38:18 GMT # gpg: using RSA key 6685AE99E75167BCAFC8DF35FBD0DB095A9E2A44 # gpg: Good signature from "Alex Bennée (Master Work Key) <alex.bennee@linaro.org>" [full] # Primary key fingerprint: 6685 AE99 E751 67BC AFC8 DF35 FBD0 DB09 5A9E 2A44 * tag 'pull-maintainer-updates-060324-1' of https://gitlab.com/stsquad/qemu: (29 commits) target/riscv: honour show_opcodes when disassembling target/loongarch: honour show_opcodes when disassembling disas/hppa: honour show_opcodes disas: introduce show_opcodes plugins: cleanup codepath for previous inline operation plugins: remove non per_vcpu inline operation from API contrib/plugins/howvec: migrate to new per_vcpu API contrib/plugins/hotblocks: migrate to new per_vcpu API tests/plugin/bb: migrate to new per_vcpu API tests/plugin/insn: migrate to new per_vcpu API tests/plugin/mem: migrate to new per_vcpu API tests/plugin: add test plugin for inline operations plugins: add inline operation per vcpu plugins: implement inline operation relative to cpu_index plugins: define qemu_plugin_u64 plugins: scoreboard API tests/tcg: Add two follow-fork-mode tests gdbstub: Implement follow-fork-mode child gdbstub: Introduce gdb_handle_detach_user() gdbstub: Introduce gdb_handle_set_thread_user() ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+57
-12
@@ -133,16 +133,28 @@ static void gen_empty_udata_cb_no_rwg(void)
|
||||
*/
|
||||
static void gen_empty_inline_cb(void)
|
||||
{
|
||||
TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
|
||||
TCGv_ptr cpu_index_as_ptr = tcg_temp_ebb_new_ptr();
|
||||
TCGv_i64 val = tcg_temp_ebb_new_i64();
|
||||
TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
|
||||
|
||||
tcg_gen_ld_i32(cpu_index, tcg_env,
|
||||
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
|
||||
/* second operand will be replaced by immediate value */
|
||||
tcg_gen_mul_i32(cpu_index, cpu_index, cpu_index);
|
||||
tcg_gen_ext_i32_ptr(cpu_index_as_ptr, cpu_index);
|
||||
|
||||
tcg_gen_movi_ptr(ptr, 0);
|
||||
tcg_gen_add_ptr(ptr, ptr, cpu_index_as_ptr);
|
||||
tcg_gen_ld_i64(val, ptr, 0);
|
||||
/* pass an immediate != 0 so that it doesn't get optimized away */
|
||||
tcg_gen_addi_i64(val, val, 0xdeadface);
|
||||
/* second operand will be replaced by immediate value */
|
||||
tcg_gen_add_i64(val, val, val);
|
||||
|
||||
tcg_gen_st_i64(val, ptr, 0);
|
||||
tcg_temp_free_ptr(ptr);
|
||||
tcg_temp_free_i64(val);
|
||||
tcg_temp_free_ptr(cpu_index_as_ptr);
|
||||
tcg_temp_free_i32(cpu_index);
|
||||
}
|
||||
|
||||
static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info)
|
||||
@@ -290,12 +302,37 @@ static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr)
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_ld_i32(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
return copy_op(begin_op, op, INDEX_op_ld_i32);
|
||||
}
|
||||
|
||||
static TCGOp *copy_ext_i32_ptr(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (UINTPTR_MAX == UINT32_MAX) {
|
||||
op = copy_op(begin_op, op, INDEX_op_mov_i32);
|
||||
} else {
|
||||
op = copy_op(begin_op, op, INDEX_op_ext_i32_i64);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_add_ptr(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (UINTPTR_MAX == UINT32_MAX) {
|
||||
op = copy_op(begin_op, op, INDEX_op_add_i32);
|
||||
} else {
|
||||
op = copy_op(begin_op, op, INDEX_op_add_i64);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (TCG_TARGET_REG_BITS == 32) {
|
||||
/* 2x ld_i32 */
|
||||
op = copy_op(begin_op, op, INDEX_op_ld_i32);
|
||||
op = copy_op(begin_op, op, INDEX_op_ld_i32);
|
||||
op = copy_ld_i32(begin_op, op);
|
||||
op = copy_ld_i32(begin_op, op);
|
||||
} else {
|
||||
/* ld_i64 */
|
||||
op = copy_op(begin_op, op, INDEX_op_ld_i64);
|
||||
@@ -331,6 +368,13 @@ static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op, uint64_t v)
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_mul_i32(TCGOp **begin_op, TCGOp *op, uint32_t v)
|
||||
{
|
||||
op = copy_op(begin_op, op, INDEX_op_mul_i32);
|
||||
op->args[2] = tcgv_i32_arg(tcg_constant_i32(v));
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (UINTPTR_MAX == UINT32_MAX) {
|
||||
@@ -396,18 +440,19 @@ static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb,
|
||||
TCGOp *begin_op, TCGOp *op,
|
||||
int *unused)
|
||||
{
|
||||
/* const_ptr */
|
||||
op = copy_const_ptr(&begin_op, op, cb->userp);
|
||||
char *ptr = cb->inline_insn.entry.score->data->data;
|
||||
size_t elem_size = g_array_get_element_size(
|
||||
cb->inline_insn.entry.score->data);
|
||||
size_t offset = cb->inline_insn.entry.offset;
|
||||
|
||||
/* ld_i64 */
|
||||
op = copy_ld_i32(&begin_op, op);
|
||||
op = copy_mul_i32(&begin_op, op, elem_size);
|
||||
op = copy_ext_i32_ptr(&begin_op, op);
|
||||
op = copy_const_ptr(&begin_op, op, ptr + offset);
|
||||
op = copy_add_ptr(&begin_op, op);
|
||||
op = copy_ld_i64(&begin_op, op);
|
||||
|
||||
/* add_i64 */
|
||||
op = copy_add_i64(&begin_op, op, cb->inline_insn.imm);
|
||||
|
||||
/* st_i64 */
|
||||
op = copy_st_i64(&begin_op, op);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -641,7 +641,7 @@ static abi_long do_bsd_readlink(CPUArchState *env, abi_long arg1,
|
||||
}
|
||||
if (strcmp(p1, "/proc/curproc/file") == 0) {
|
||||
CPUState *cpu = env_cpu(env);
|
||||
TaskState *ts = (TaskState *)cpu->opaque;
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
strncpy(p2, ts->bprm->fullpath, arg3);
|
||||
ret = MIN((abi_long)strlen(ts->bprm->fullpath), arg3);
|
||||
} else {
|
||||
|
||||
@@ -208,7 +208,7 @@ static inline abi_long do_freebsd_fork(void *cpu_env)
|
||||
*/
|
||||
set_second_rval(cpu_env, child_flag);
|
||||
|
||||
fork_end(child_flag);
|
||||
fork_end(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -252,7 +252,7 @@ static inline abi_long do_freebsd_rfork(void *cpu_env, abi_long flags)
|
||||
* value: 0 for parent process, 1 for child process.
|
||||
*/
|
||||
set_second_rval(cpu_env, child_flag);
|
||||
fork_end(child_flag);
|
||||
fork_end(ret);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -285,7 +285,7 @@ static inline abi_long do_freebsd_pdfork(void *cpu_env, abi_ulong target_fdp,
|
||||
* value: 0 for parent process, 1 for child process.
|
||||
*/
|
||||
set_second_rval(cpu_env, child_flag);
|
||||
fork_end(child_flag);
|
||||
fork_end(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
+7
-2
@@ -113,10 +113,13 @@ void fork_start(void)
|
||||
start_exclusive();
|
||||
cpu_list_lock();
|
||||
mmap_fork_start();
|
||||
gdbserver_fork_start();
|
||||
}
|
||||
|
||||
void fork_end(int child)
|
||||
void fork_end(pid_t pid)
|
||||
{
|
||||
bool child = pid == 0;
|
||||
|
||||
if (child) {
|
||||
CPUState *cpu, *next_cpu;
|
||||
/*
|
||||
@@ -134,10 +137,12 @@ void fork_end(int child)
|
||||
* state, so we don't need to end_exclusive() here.
|
||||
*/
|
||||
qemu_init_cpu_list();
|
||||
gdbserver_fork(thread_cpu);
|
||||
get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id();
|
||||
gdbserver_fork_end(thread_cpu, pid);
|
||||
} else {
|
||||
mmap_fork_end(child);
|
||||
cpu_list_unlock();
|
||||
gdbserver_fork_end(thread_cpu, pid);
|
||||
end_exclusive();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -117,6 +117,11 @@ typedef struct TaskState {
|
||||
struct target_sigaltstack sigaltstack_used;
|
||||
} __attribute__((aligned(16))) TaskState;
|
||||
|
||||
static inline TaskState *get_task_state(CPUState *cs)
|
||||
{
|
||||
return cs->opaque;
|
||||
}
|
||||
|
||||
void stop_all_tasks(void);
|
||||
extern const char *interp_prefix;
|
||||
extern const char *qemu_uname_release;
|
||||
@@ -187,7 +192,7 @@ void cpu_loop(CPUArchState *env);
|
||||
char *target_strerror(int err);
|
||||
int get_osversion(void);
|
||||
void fork_start(void);
|
||||
void fork_end(int child);
|
||||
void fork_end(pid_t pid);
|
||||
|
||||
#include "qemu/log.h"
|
||||
|
||||
|
||||
+10
-10
@@ -319,7 +319,7 @@ void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info)
|
||||
|
||||
int block_signals(void)
|
||||
{
|
||||
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||
TaskState *ts = get_task_state(thread_cpu);
|
||||
sigset_t set;
|
||||
|
||||
/*
|
||||
@@ -359,7 +359,7 @@ void dump_core_and_abort(int target_sig)
|
||||
{
|
||||
CPUState *cpu = thread_cpu;
|
||||
CPUArchState *env = cpu_env(cpu);
|
||||
TaskState *ts = cpu->opaque;
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
int core_dumped = 0;
|
||||
int host_sig;
|
||||
struct sigaction act;
|
||||
@@ -421,7 +421,7 @@ void queue_signal(CPUArchState *env, int sig, int si_type,
|
||||
target_siginfo_t *info)
|
||||
{
|
||||
CPUState *cpu = env_cpu(env);
|
||||
TaskState *ts = cpu->opaque;
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
|
||||
trace_user_queue_signal(env, sig);
|
||||
|
||||
@@ -476,7 +476,7 @@ void force_sig_fault(int sig, int code, abi_ulong addr)
|
||||
static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
|
||||
{
|
||||
CPUState *cpu = thread_cpu;
|
||||
TaskState *ts = cpu->opaque;
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
target_siginfo_t tinfo;
|
||||
ucontext_t *uc = puc;
|
||||
struct emulated_sigtable *k;
|
||||
@@ -585,7 +585,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
|
||||
/* compare to kern/kern_sig.c sys_sigaltstack() and kern_sigaltstack() */
|
||||
abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp)
|
||||
{
|
||||
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||
TaskState *ts = get_task_state(thread_cpu);
|
||||
int ret;
|
||||
target_stack_t oss;
|
||||
|
||||
@@ -714,7 +714,7 @@ int do_sigaction(int sig, const struct target_sigaction *act,
|
||||
static inline abi_ulong get_sigframe(struct target_sigaction *ka,
|
||||
CPUArchState *env, size_t frame_size)
|
||||
{
|
||||
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||
TaskState *ts = get_task_state(thread_cpu);
|
||||
abi_ulong sp;
|
||||
|
||||
/* Use default user stack */
|
||||
@@ -789,7 +789,7 @@ static int reset_signal_mask(target_ucontext_t *ucontext)
|
||||
int i;
|
||||
sigset_t blocked;
|
||||
target_sigset_t target_set;
|
||||
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||
TaskState *ts = get_task_state(thread_cpu);
|
||||
|
||||
for (i = 0; i < TARGET_NSIG_WORDS; i++) {
|
||||
__get_user(target_set.__bits[i], &ucontext->uc_sigmask.__bits[i]);
|
||||
@@ -839,7 +839,7 @@ badframe:
|
||||
|
||||
void signal_init(void)
|
||||
{
|
||||
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||
TaskState *ts = get_task_state(thread_cpu);
|
||||
struct sigaction act;
|
||||
struct sigaction oact;
|
||||
int i;
|
||||
@@ -878,7 +878,7 @@ static void handle_pending_signal(CPUArchState *env, int sig,
|
||||
struct emulated_sigtable *k)
|
||||
{
|
||||
CPUState *cpu = env_cpu(env);
|
||||
TaskState *ts = cpu->opaque;
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
struct target_sigaction *sa;
|
||||
int code;
|
||||
sigset_t set;
|
||||
@@ -967,7 +967,7 @@ void process_pending_signals(CPUArchState *env)
|
||||
int sig;
|
||||
sigset_t *blocked_set, set;
|
||||
struct emulated_sigtable *k;
|
||||
TaskState *ts = cpu->opaque;
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
|
||||
while (qatomic_read(&ts->signal_pending)) {
|
||||
sigfillset(&set);
|
||||
|
||||
+30
-20
@@ -34,8 +34,8 @@ static guint64 limit = 20;
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t start_addr;
|
||||
uint64_t exec_count;
|
||||
int trans_count;
|
||||
struct qemu_plugin_scoreboard *exec_count;
|
||||
int trans_count;
|
||||
unsigned long insns;
|
||||
} ExecCount;
|
||||
|
||||
@@ -43,7 +43,17 @@ static gint cmp_exec_count(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
ExecCount *ea = (ExecCount *) a;
|
||||
ExecCount *eb = (ExecCount *) b;
|
||||
return ea->exec_count > eb->exec_count ? -1 : 1;
|
||||
uint64_t count_a =
|
||||
qemu_plugin_u64_sum(qemu_plugin_scoreboard_u64(ea->exec_count));
|
||||
uint64_t count_b =
|
||||
qemu_plugin_u64_sum(qemu_plugin_scoreboard_u64(eb->exec_count));
|
||||
return count_a > count_b ? -1 : 1;
|
||||
}
|
||||
|
||||
static void exec_count_free(gpointer key, gpointer value, gpointer user_data)
|
||||
{
|
||||
ExecCount *cnt = value;
|
||||
qemu_plugin_scoreboard_free(cnt->exec_count);
|
||||
}
|
||||
|
||||
static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
@@ -52,7 +62,6 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
GList *counts, *it;
|
||||
int i;
|
||||
|
||||
g_mutex_lock(&lock);
|
||||
g_string_append_printf(report, "%d entries in the hash table\n",
|
||||
g_hash_table_size(hotblocks));
|
||||
counts = g_hash_table_get_values(hotblocks);
|
||||
@@ -63,16 +72,21 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
|
||||
for (i = 0; i < limit && it->next; i++, it = it->next) {
|
||||
ExecCount *rec = (ExecCount *) it->data;
|
||||
g_string_append_printf(report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n",
|
||||
rec->start_addr, rec->trans_count,
|
||||
rec->insns, rec->exec_count);
|
||||
g_string_append_printf(
|
||||
report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n",
|
||||
rec->start_addr, rec->trans_count,
|
||||
rec->insns,
|
||||
qemu_plugin_u64_sum(
|
||||
qemu_plugin_scoreboard_u64(rec->exec_count)));
|
||||
}
|
||||
|
||||
g_list_free(it);
|
||||
}
|
||||
g_mutex_unlock(&lock);
|
||||
|
||||
qemu_plugin_outs(report->str);
|
||||
|
||||
g_hash_table_foreach(hotblocks, exec_count_free, NULL);
|
||||
g_hash_table_destroy(hotblocks);
|
||||
}
|
||||
|
||||
static void plugin_init(void)
|
||||
@@ -82,15 +96,9 @@ static void plugin_init(void)
|
||||
|
||||
static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
|
||||
{
|
||||
ExecCount *cnt;
|
||||
uint64_t hash = (uint64_t) udata;
|
||||
|
||||
g_mutex_lock(&lock);
|
||||
cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) hash);
|
||||
/* should always succeed */
|
||||
g_assert(cnt);
|
||||
cnt->exec_count++;
|
||||
g_mutex_unlock(&lock);
|
||||
ExecCount *cnt = (ExecCount *)udata;
|
||||
qemu_plugin_u64_add(qemu_plugin_scoreboard_u64(cnt->exec_count),
|
||||
cpu_index, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -114,18 +122,20 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
|
||||
cnt->start_addr = pc;
|
||||
cnt->trans_count = 1;
|
||||
cnt->insns = insns;
|
||||
cnt->exec_count = qemu_plugin_scoreboard_new(sizeof(uint64_t));
|
||||
g_hash_table_insert(hotblocks, (gpointer) hash, (gpointer) cnt);
|
||||
}
|
||||
|
||||
g_mutex_unlock(&lock);
|
||||
|
||||
if (do_inline) {
|
||||
qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64,
|
||||
&cnt->exec_count, 1);
|
||||
qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
|
||||
tb, QEMU_PLUGIN_INLINE_ADD_U64,
|
||||
qemu_plugin_scoreboard_u64(cnt->exec_count), 1);
|
||||
} else {
|
||||
qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec,
|
||||
QEMU_PLUGIN_CB_NO_REGS,
|
||||
(void *)hash);
|
||||
(void *)cnt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+38
-15
@@ -43,13 +43,13 @@ typedef struct {
|
||||
uint32_t mask;
|
||||
uint32_t pattern;
|
||||
CountType what;
|
||||
uint64_t count;
|
||||
qemu_plugin_u64 count;
|
||||
} InsnClassExecCount;
|
||||
|
||||
typedef struct {
|
||||
char *insn;
|
||||
uint32_t opcode;
|
||||
uint64_t count;
|
||||
qemu_plugin_u64 count;
|
||||
InsnClassExecCount *class;
|
||||
} InsnExecCount;
|
||||
|
||||
@@ -159,7 +159,9 @@ static gint cmp_exec_count(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
InsnExecCount *ea = (InsnExecCount *) a;
|
||||
InsnExecCount *eb = (InsnExecCount *) b;
|
||||
return ea->count > eb->count ? -1 : 1;
|
||||
uint64_t count_a = qemu_plugin_u64_sum(ea->count);
|
||||
uint64_t count_b = qemu_plugin_u64_sum(eb->count);
|
||||
return count_a > count_b ? -1 : 1;
|
||||
}
|
||||
|
||||
static void free_record(gpointer data)
|
||||
@@ -167,12 +169,14 @@ static void free_record(gpointer data)
|
||||
InsnExecCount *rec = (InsnExecCount *) data;
|
||||
g_free(rec->insn);
|
||||
g_free(rec);
|
||||
qemu_plugin_scoreboard_free(rec->count.score);
|
||||
}
|
||||
|
||||
static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
{
|
||||
g_autoptr(GString) report = g_string_new("Instruction Classes:\n");
|
||||
int i;
|
||||
uint64_t total_count;
|
||||
GList *counts;
|
||||
InsnClassExecCount *class = NULL;
|
||||
|
||||
@@ -180,11 +184,12 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
class = &class_table[i];
|
||||
switch (class->what) {
|
||||
case COUNT_CLASS:
|
||||
if (class->count || verbose) {
|
||||
total_count = qemu_plugin_u64_sum(class->count);
|
||||
if (total_count || verbose) {
|
||||
g_string_append_printf(report,
|
||||
"Class: %-24s\t(%" PRId64 " hits)\n",
|
||||
class->class,
|
||||
class->count);
|
||||
total_count);
|
||||
}
|
||||
break;
|
||||
case COUNT_INDIVIDUAL:
|
||||
@@ -212,7 +217,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
"Instr: %-24s\t(%" PRId64 " hits)"
|
||||
"\t(op=0x%08x/%s)\n",
|
||||
rec->insn,
|
||||
rec->count,
|
||||
qemu_plugin_u64_sum(rec->count),
|
||||
rec->opcode,
|
||||
rec->class ?
|
||||
rec->class->class : "un-categorised");
|
||||
@@ -221,6 +226,12 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
}
|
||||
|
||||
g_hash_table_destroy(insns);
|
||||
for (i = 0; i < ARRAY_SIZE(class_tables); i++) {
|
||||
for (int j = 0; j < class_tables[i].table_sz; ++j) {
|
||||
qemu_plugin_scoreboard_free(class_tables[i].table[j].count.score);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
qemu_plugin_outs(report->str);
|
||||
}
|
||||
@@ -232,11 +243,12 @@ static void plugin_init(void)
|
||||
|
||||
static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata)
|
||||
{
|
||||
uint64_t *count = (uint64_t *) udata;
|
||||
(*count)++;
|
||||
struct qemu_plugin_scoreboard *score = udata;
|
||||
qemu_plugin_u64_add(qemu_plugin_scoreboard_u64(score), cpu_index, 1);
|
||||
}
|
||||
|
||||
static uint64_t *find_counter(struct qemu_plugin_insn *insn)
|
||||
static struct qemu_plugin_scoreboard *find_counter(
|
||||
struct qemu_plugin_insn *insn)
|
||||
{
|
||||
int i;
|
||||
uint64_t *cnt = NULL;
|
||||
@@ -265,7 +277,7 @@ static uint64_t *find_counter(struct qemu_plugin_insn *insn)
|
||||
case COUNT_NONE:
|
||||
return NULL;
|
||||
case COUNT_CLASS:
|
||||
return &class->count;
|
||||
return class->count.score;
|
||||
case COUNT_INDIVIDUAL:
|
||||
{
|
||||
InsnExecCount *icount;
|
||||
@@ -279,13 +291,16 @@ static uint64_t *find_counter(struct qemu_plugin_insn *insn)
|
||||
icount->opcode = opcode;
|
||||
icount->insn = qemu_plugin_insn_disas(insn);
|
||||
icount->class = class;
|
||||
struct qemu_plugin_scoreboard *score =
|
||||
qemu_plugin_scoreboard_new(sizeof(uint64_t));
|
||||
icount->count = qemu_plugin_scoreboard_u64(score);
|
||||
|
||||
g_hash_table_insert(insns, GUINT_TO_POINTER(opcode),
|
||||
(gpointer) icount);
|
||||
}
|
||||
g_mutex_unlock(&lock);
|
||||
|
||||
return &icount->count;
|
||||
return icount->count.score;
|
||||
}
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
@@ -300,14 +315,14 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
uint64_t *cnt;
|
||||
struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
|
||||
cnt = find_counter(insn);
|
||||
struct qemu_plugin_scoreboard *cnt = find_counter(insn);
|
||||
|
||||
if (cnt) {
|
||||
if (do_inline) {
|
||||
qemu_plugin_register_vcpu_insn_exec_inline(
|
||||
insn, QEMU_PLUGIN_INLINE_ADD_U64, cnt, 1);
|
||||
qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
|
||||
insn, QEMU_PLUGIN_INLINE_ADD_U64,
|
||||
qemu_plugin_scoreboard_u64(cnt), 1);
|
||||
} else {
|
||||
qemu_plugin_register_vcpu_insn_exec_cb(
|
||||
insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, cnt);
|
||||
@@ -322,6 +337,14 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(class_tables); i++) {
|
||||
for (int j = 0; j < class_tables[i].table_sz; ++j) {
|
||||
struct qemu_plugin_scoreboard *score =
|
||||
qemu_plugin_scoreboard_new(sizeof(uint64_t));
|
||||
class_tables[i].table[j].count = qemu_plugin_scoreboard_u64(score);
|
||||
}
|
||||
}
|
||||
|
||||
/* Select a class table appropriate to the guest architecture */
|
||||
for (i = 0; i < ARRAY_SIZE(class_tables); i++) {
|
||||
ClassSelector *entry = &class_tables[i];
|
||||
|
||||
@@ -299,6 +299,7 @@ void disas(FILE *out, const void *code, size_t size)
|
||||
s.info.buffer = code;
|
||||
s.info.buffer_vma = (uintptr_t)code;
|
||||
s.info.buffer_length = size;
|
||||
s.info.show_opcodes = true;
|
||||
|
||||
if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) {
|
||||
return;
|
||||
|
||||
+5
-3
@@ -1972,9 +1972,11 @@ print_insn_hppa (bfd_vma memaddr, disassemble_info *info)
|
||||
|
||||
insn = bfd_getb32 (buffer);
|
||||
|
||||
info->fprintf_func(info->stream, " %02x %02x %02x %02x ",
|
||||
(insn >> 24) & 0xff, (insn >> 16) & 0xff,
|
||||
(insn >> 8) & 0xff, insn & 0xff);
|
||||
if (info->show_opcodes) {
|
||||
info->fprintf_func(info->stream, " %02x %02x %02x %02x ",
|
||||
(insn >> 24) & 0xff, (insn >> 16) & 0xff,
|
||||
(insn >> 8) & 0xff, insn & 0xff);
|
||||
}
|
||||
|
||||
for (i = 0; i < NUMOPCODES; ++i)
|
||||
{
|
||||
|
||||
+15
-13
@@ -5192,19 +5192,21 @@ print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa)
|
||||
}
|
||||
}
|
||||
|
||||
switch (len) {
|
||||
case 2:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_2, inst);
|
||||
break;
|
||||
case 4:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_4, inst);
|
||||
break;
|
||||
case 6:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_6, inst);
|
||||
break;
|
||||
default:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_8, inst);
|
||||
break;
|
||||
if (info->show_opcodes) {
|
||||
switch (len) {
|
||||
case 2:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_2, inst);
|
||||
break;
|
||||
case 4:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_4, inst);
|
||||
break;
|
||||
case 6:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_6, inst);
|
||||
break;
|
||||
default:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_8, inst);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
disasm_inst(buf, sizeof(buf), isa, memaddr, inst,
|
||||
|
||||
+24
-5
@@ -1024,6 +1024,12 @@ static void handle_detach(GArray *params, void *user_ctx)
|
||||
pid = get_param(params, 0)->val_ul;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
if (gdb_handle_detach_user(pid)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
process = gdb_get_process(pid);
|
||||
gdb_process_breakpoint_remove_all(process);
|
||||
process->attached = false;
|
||||
@@ -1099,6 +1105,7 @@ static void handle_cont_with_sig(GArray *params, void *user_ctx)
|
||||
|
||||
static void handle_set_thread(GArray *params, void *user_ctx)
|
||||
{
|
||||
uint32_t pid, tid;
|
||||
CPUState *cpu;
|
||||
|
||||
if (params->len != 2) {
|
||||
@@ -1116,8 +1123,14 @@ static void handle_set_thread(GArray *params, void *user_ctx)
|
||||
return;
|
||||
}
|
||||
|
||||
cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid,
|
||||
get_param(params, 1)->thread_id.tid);
|
||||
pid = get_param(params, 1)->thread_id.pid;
|
||||
tid = get_param(params, 1)->thread_id.tid;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
if (gdb_handle_set_thread_user(pid, tid)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
cpu = gdb_get_cpu(pid, tid);
|
||||
if (!cpu) {
|
||||
gdb_put_packet("E22");
|
||||
return;
|
||||
@@ -1655,9 +1668,15 @@ static void handle_query_supported(GArray *params, void *user_ctx)
|
||||
g_string_append(gdbserver_state.str_buf, ";qXfer:exec-file:read+");
|
||||
#endif
|
||||
|
||||
if (params->len &&
|
||||
strstr(get_param(params, 0)->data, "multiprocess+")) {
|
||||
gdbserver_state.multiprocess = true;
|
||||
if (params->len) {
|
||||
const char *gdb_supported = get_param(params, 0)->data;
|
||||
|
||||
if (strstr(gdb_supported, "multiprocess+")) {
|
||||
gdbserver_state.multiprocess = true;
|
||||
}
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
gdb_handle_query_supported_user(gdb_supported);
|
||||
#endif
|
||||
}
|
||||
|
||||
g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+");
|
||||
|
||||
@@ -196,6 +196,9 @@ void gdb_handle_v_file_pread(GArray *params, void *user_ctx); /* user */
|
||||
void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */
|
||||
void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user */
|
||||
void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */
|
||||
void gdb_handle_query_supported_user(const char *gdb_supported); /* user */
|
||||
bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */
|
||||
bool gdb_handle_detach_user(uint32_t pid); /* user */
|
||||
|
||||
void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ int gdb_target_signal_to_gdb(int sig)
|
||||
|
||||
int gdb_get_cpu_index(CPUState *cpu)
|
||||
{
|
||||
TaskState *ts = (TaskState *) cpu->opaque;
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
return ts ? ts->ts_tid : -1;
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@ void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx)
|
||||
return;
|
||||
}
|
||||
|
||||
TaskState *ts = cpu->opaque;
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
if (!ts || !ts->bprm || !ts->bprm->filename) {
|
||||
gdb_put_packet("E00");
|
||||
return;
|
||||
|
||||
+240
-4
@@ -25,6 +25,61 @@
|
||||
#define GDB_NR_SYSCALLS 1024
|
||||
typedef unsigned long GDBSyscallsMask[BITS_TO_LONGS(GDB_NR_SYSCALLS)];
|
||||
|
||||
/*
|
||||
* Forked child talks to its parent in order to let GDB enforce the
|
||||
* follow-fork-mode. This happens inside a start_exclusive() section, so that
|
||||
* the other threads, which may be forking too, do not interfere. The
|
||||
* implementation relies on GDB not sending $vCont until it has detached
|
||||
* either from the parent (follow-fork-mode child) or from the child
|
||||
* (follow-fork-mode parent).
|
||||
*
|
||||
* The parent and the child share the GDB socket; at any given time only one
|
||||
* of them is allowed to use it, as is reflected in the respective fork_state.
|
||||
* This is negotiated via the fork_sockets pair as a reaction to $Hg.
|
||||
*
|
||||
* Below is a short summary of the possible state transitions:
|
||||
*
|
||||
* ENABLED : Terminal state.
|
||||
* DISABLED : Terminal state.
|
||||
* ACTIVE : Parent initial state.
|
||||
* INACTIVE : Child initial state.
|
||||
* ACTIVE -> DEACTIVATING: On $Hg.
|
||||
* ACTIVE -> ENABLING : On $D.
|
||||
* ACTIVE -> DISABLING : On $D.
|
||||
* ACTIVE -> DISABLED : On communication error.
|
||||
* DEACTIVATING -> INACTIVE : On gdb_read_byte() return.
|
||||
* DEACTIVATING -> DISABLED : On communication error.
|
||||
* INACTIVE -> ACTIVE : On $Hg in the peer.
|
||||
* INACTIVE -> ENABLE : On $D in the peer.
|
||||
* INACTIVE -> DISABLE : On $D in the peer.
|
||||
* INACTIVE -> DISABLED : On communication error.
|
||||
* ENABLING -> ENABLED : On gdb_read_byte() return.
|
||||
* ENABLING -> DISABLED : On communication error.
|
||||
* DISABLING -> DISABLED : On gdb_read_byte() return.
|
||||
*/
|
||||
enum GDBForkState {
|
||||
/* Fully owning the GDB socket. */
|
||||
GDB_FORK_ENABLED,
|
||||
/* Working with the GDB socket; the peer is inactive. */
|
||||
GDB_FORK_ACTIVE,
|
||||
/* Handing off the GDB socket to the peer. */
|
||||
GDB_FORK_DEACTIVATING,
|
||||
/* The peer is working with the GDB socket. */
|
||||
GDB_FORK_INACTIVE,
|
||||
/* Asking the peer to close its GDB socket fd. */
|
||||
GDB_FORK_ENABLING,
|
||||
/* Asking the peer to take over, closing our GDB socket fd. */
|
||||
GDB_FORK_DISABLING,
|
||||
/* The peer has taken over, our GDB socket fd is closed. */
|
||||
GDB_FORK_DISABLED,
|
||||
};
|
||||
|
||||
enum GDBForkMessage {
|
||||
GDB_FORK_ACTIVATE = 'a',
|
||||
GDB_FORK_ENABLE = 'e',
|
||||
GDB_FORK_DISABLE = 'd',
|
||||
};
|
||||
|
||||
/* User-mode specific state */
|
||||
typedef struct {
|
||||
int fd;
|
||||
@@ -36,6 +91,10 @@ typedef struct {
|
||||
*/
|
||||
bool catch_all_syscalls;
|
||||
GDBSyscallsMask catch_syscalls_mask;
|
||||
bool fork_events;
|
||||
enum GDBForkState fork_state;
|
||||
int fork_sockets[2];
|
||||
pid_t fork_peer_pid, fork_peer_tid;
|
||||
} GDBUserState;
|
||||
|
||||
static GDBUserState gdbserver_user_state;
|
||||
@@ -356,16 +415,193 @@ int gdbserver_start(const char *port_or_path)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Disable gdb stub for child processes. */
|
||||
void gdbserver_fork(CPUState *cpu)
|
||||
void gdbserver_fork_start(void)
|
||||
{
|
||||
if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
|
||||
return;
|
||||
}
|
||||
if (!gdbserver_user_state.fork_events ||
|
||||
qemu_socketpair(AF_UNIX, SOCK_STREAM, 0,
|
||||
gdbserver_user_state.fork_sockets) < 0) {
|
||||
gdbserver_user_state.fork_state = GDB_FORK_DISABLED;
|
||||
return;
|
||||
}
|
||||
gdbserver_user_state.fork_state = GDB_FORK_INACTIVE;
|
||||
gdbserver_user_state.fork_peer_pid = getpid();
|
||||
gdbserver_user_state.fork_peer_tid = qemu_get_thread_id();
|
||||
}
|
||||
|
||||
static void disable_gdbstub(CPUState *thread_cpu)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
close(gdbserver_user_state.fd);
|
||||
gdbserver_user_state.fd = -1;
|
||||
cpu_breakpoint_remove_all(cpu, BP_GDB);
|
||||
/* no cpu_watchpoint_remove_all for user-mode */
|
||||
CPU_FOREACH(cpu) {
|
||||
cpu_breakpoint_remove_all(cpu, BP_GDB);
|
||||
/* no cpu_watchpoint_remove_all for user-mode */
|
||||
cpu_single_step(cpu, 0);
|
||||
}
|
||||
tb_flush(thread_cpu);
|
||||
}
|
||||
|
||||
void gdbserver_fork_end(CPUState *cpu, pid_t pid)
|
||||
{
|
||||
char b;
|
||||
int fd;
|
||||
|
||||
if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == -1) {
|
||||
if (gdbserver_user_state.fork_state != GDB_FORK_DISABLED) {
|
||||
g_assert(gdbserver_user_state.fork_state == GDB_FORK_INACTIVE);
|
||||
close(gdbserver_user_state.fork_sockets[0]);
|
||||
close(gdbserver_user_state.fork_sockets[1]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (gdbserver_user_state.fork_state == GDB_FORK_DISABLED) {
|
||||
if (pid == 0) {
|
||||
disable_gdbstub(cpu);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
close(gdbserver_user_state.fork_sockets[0]);
|
||||
fd = gdbserver_user_state.fork_sockets[1];
|
||||
g_assert(gdbserver_state.process_num == 1);
|
||||
g_assert(gdbserver_state.processes[0].pid ==
|
||||
gdbserver_user_state.fork_peer_pid);
|
||||
g_assert(gdbserver_state.processes[0].attached);
|
||||
gdbserver_state.processes[0].pid = getpid();
|
||||
} else {
|
||||
close(gdbserver_user_state.fork_sockets[1]);
|
||||
fd = gdbserver_user_state.fork_sockets[0];
|
||||
gdbserver_user_state.fork_state = GDB_FORK_ACTIVE;
|
||||
gdbserver_user_state.fork_peer_pid = pid;
|
||||
gdbserver_user_state.fork_peer_tid = pid;
|
||||
|
||||
if (!gdbserver_state.allow_stop_reply) {
|
||||
goto fail;
|
||||
}
|
||||
g_string_printf(gdbserver_state.str_buf,
|
||||
"T%02xfork:p%02x.%02x;thread:p%02x.%02x;",
|
||||
gdb_target_signal_to_gdb(gdb_target_sigtrap()),
|
||||
pid, pid, (int)getpid(), qemu_get_thread_id());
|
||||
gdb_put_strbuf();
|
||||
}
|
||||
|
||||
gdbserver_state.state = RS_IDLE;
|
||||
gdbserver_state.allow_stop_reply = false;
|
||||
gdbserver_user_state.running_state = 0;
|
||||
for (;;) {
|
||||
switch (gdbserver_user_state.fork_state) {
|
||||
case GDB_FORK_ENABLED:
|
||||
if (gdbserver_user_state.running_state) {
|
||||
return;
|
||||
}
|
||||
QEMU_FALLTHROUGH;
|
||||
case GDB_FORK_ACTIVE:
|
||||
if (read(gdbserver_user_state.fd, &b, 1) != 1) {
|
||||
goto fail;
|
||||
}
|
||||
gdb_read_byte(b);
|
||||
break;
|
||||
case GDB_FORK_DEACTIVATING:
|
||||
b = GDB_FORK_ACTIVATE;
|
||||
if (write(fd, &b, 1) != 1) {
|
||||
goto fail;
|
||||
}
|
||||
gdbserver_user_state.fork_state = GDB_FORK_INACTIVE;
|
||||
break;
|
||||
case GDB_FORK_INACTIVE:
|
||||
if (read(fd, &b, 1) != 1) {
|
||||
goto fail;
|
||||
}
|
||||
switch (b) {
|
||||
case GDB_FORK_ACTIVATE:
|
||||
gdbserver_user_state.fork_state = GDB_FORK_ACTIVE;
|
||||
break;
|
||||
case GDB_FORK_ENABLE:
|
||||
close(fd);
|
||||
gdbserver_user_state.fork_state = GDB_FORK_ENABLED;
|
||||
break;
|
||||
case GDB_FORK_DISABLE:
|
||||
gdbserver_user_state.fork_state = GDB_FORK_DISABLED;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
break;
|
||||
case GDB_FORK_ENABLING:
|
||||
b = GDB_FORK_DISABLE;
|
||||
if (write(fd, &b, 1) != 1) {
|
||||
goto fail;
|
||||
}
|
||||
close(fd);
|
||||
gdbserver_user_state.fork_state = GDB_FORK_ENABLED;
|
||||
break;
|
||||
case GDB_FORK_DISABLING:
|
||||
b = GDB_FORK_ENABLE;
|
||||
if (write(fd, &b, 1) != 1) {
|
||||
goto fail;
|
||||
}
|
||||
gdbserver_user_state.fork_state = GDB_FORK_DISABLED;
|
||||
break;
|
||||
case GDB_FORK_DISABLED:
|
||||
close(fd);
|
||||
disable_gdbstub(cpu);
|
||||
return;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
if (pid == 0) {
|
||||
disable_gdbstub(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
void gdb_handle_query_supported_user(const char *gdb_supported)
|
||||
{
|
||||
if (strstr(gdb_supported, "fork-events+")) {
|
||||
gdbserver_user_state.fork_events = true;
|
||||
}
|
||||
g_string_append(gdbserver_state.str_buf, ";fork-events+");
|
||||
}
|
||||
|
||||
bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid)
|
||||
{
|
||||
if (gdbserver_user_state.fork_state == GDB_FORK_ACTIVE &&
|
||||
pid == gdbserver_user_state.fork_peer_pid &&
|
||||
tid == gdbserver_user_state.fork_peer_tid) {
|
||||
gdbserver_user_state.fork_state = GDB_FORK_DEACTIVATING;
|
||||
gdb_put_packet("OK");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gdb_handle_detach_user(uint32_t pid)
|
||||
{
|
||||
bool enable;
|
||||
|
||||
if (gdbserver_user_state.fork_state == GDB_FORK_ACTIVE) {
|
||||
enable = pid == gdbserver_user_state.fork_peer_pid;
|
||||
if (enable || pid == getpid()) {
|
||||
gdbserver_user_state.fork_state = enable ? GDB_FORK_ENABLING :
|
||||
GDB_FORK_DISABLING;
|
||||
gdb_put_packet("OK");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -396,6 +396,14 @@ typedef struct disassemble_info {
|
||||
/* Command line options specific to the target disassembler. */
|
||||
char * disassembler_options;
|
||||
|
||||
/*
|
||||
* When true instruct the disassembler it may preface the
|
||||
* disassembly with the opcodes values if it wants to. This is
|
||||
* mainly for the benefit of the plugin interface which doesn't want
|
||||
* that.
|
||||
*/
|
||||
bool show_opcodes;
|
||||
|
||||
/* Field intended to be used by targets in any way they deem suitable. */
|
||||
void *target_info;
|
||||
|
||||
|
||||
@@ -46,10 +46,16 @@ static inline int gdb_handlesig(CPUState *cpu, int sig)
|
||||
void gdb_signalled(CPUArchState *as, int sig);
|
||||
|
||||
/**
|
||||
* gdbserver_fork() - disable gdb stub for child processes.
|
||||
* @cs: CPU
|
||||
* gdbserver_fork_start() - inform gdb of the upcoming fork()
|
||||
*/
|
||||
void gdbserver_fork(CPUState *cs);
|
||||
void gdbserver_fork_start(void);
|
||||
|
||||
/**
|
||||
* gdbserver_fork_end() - inform gdb of the completed fork()
|
||||
* @cs: CPU
|
||||
* @pid: 0 if in child process, -1 if fork failed, child process pid otherwise
|
||||
*/
|
||||
void gdbserver_fork_end(CPUState *cs, pid_t pid);
|
||||
|
||||
/**
|
||||
* gdb_syscall_entry() - inform gdb of syscall entry and yield control to it
|
||||
|
||||
@@ -92,6 +92,7 @@ struct qemu_plugin_dyn_cb {
|
||||
/* fields specific to each dyn_cb type go here */
|
||||
union {
|
||||
struct {
|
||||
qemu_plugin_u64 entry;
|
||||
enum qemu_plugin_op op;
|
||||
uint64_t imm;
|
||||
} inline_insn;
|
||||
@@ -112,6 +113,12 @@ struct qemu_plugin_insn {
|
||||
bool mem_only;
|
||||
};
|
||||
|
||||
/* A scoreboard is an array of values, indexed by vcpu_index */
|
||||
struct qemu_plugin_scoreboard {
|
||||
GArray *data;
|
||||
QLIST_ENTRY(qemu_plugin_scoreboard) entry;
|
||||
};
|
||||
|
||||
/*
|
||||
* qemu_plugin_insn allocate and cleanup functions. We don't expect to
|
||||
* cleanup many of these structures. They are reused for each fresh
|
||||
|
||||
+113
-29
@@ -52,7 +52,11 @@ typedef uint64_t qemu_plugin_id_t;
|
||||
* The plugins export the API they were built against by exposing the
|
||||
* symbol qemu_plugin_version which can be checked.
|
||||
*
|
||||
* version 2: removed qemu_plugin_n_vcpus and qemu_plugin_n_max_vcpus
|
||||
* version 2:
|
||||
* - removed qemu_plugin_n_vcpus and qemu_plugin_n_max_vcpus
|
||||
* - Remove qemu_plugin_register_vcpu_{tb, insn, mem}_exec_inline.
|
||||
* Those functions are replaced by *_per_vcpu variants, which guarantee
|
||||
* thread-safety for operations.
|
||||
*/
|
||||
|
||||
extern QEMU_PLUGIN_EXPORT int qemu_plugin_version;
|
||||
@@ -222,6 +226,19 @@ void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
|
||||
struct qemu_plugin_tb;
|
||||
/** struct qemu_plugin_insn - Opaque handle for a translated instruction */
|
||||
struct qemu_plugin_insn;
|
||||
/** struct qemu_plugin_scoreboard - Opaque handle for a scoreboard */
|
||||
struct qemu_plugin_scoreboard;
|
||||
|
||||
/**
|
||||
* typedef qemu_plugin_u64 - uint64_t member of an entry in a scoreboard
|
||||
*
|
||||
* This field allows to access a specific uint64_t member in one given entry,
|
||||
* located at a specified offset. Inline operations expect this as entry.
|
||||
*/
|
||||
typedef struct {
|
||||
struct qemu_plugin_scoreboard *score;
|
||||
size_t offset;
|
||||
} qemu_plugin_u64;
|
||||
|
||||
/**
|
||||
* enum qemu_plugin_cb_flags - type of callback
|
||||
@@ -297,23 +314,20 @@ enum qemu_plugin_op {
|
||||
};
|
||||
|
||||
/**
|
||||
* qemu_plugin_register_vcpu_tb_exec_inline() - execution inline op
|
||||
* qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu() - execution inline op
|
||||
* @tb: the opaque qemu_plugin_tb handle for the translation
|
||||
* @op: the type of qemu_plugin_op (e.g. ADD_U64)
|
||||
* @ptr: the target memory location for the op
|
||||
* @entry: entry to run op
|
||||
* @imm: the op data (e.g. 1)
|
||||
*
|
||||
* Insert an inline op to every time a translated unit executes.
|
||||
* Useful if you just want to increment a single counter somewhere in
|
||||
* memory.
|
||||
*
|
||||
* Note: ops are not atomic so in multi-threaded/multi-smp situations
|
||||
* you will get inexact results.
|
||||
* Insert an inline op on a given scoreboard entry.
|
||||
*/
|
||||
QEMU_PLUGIN_API
|
||||
void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb,
|
||||
enum qemu_plugin_op op,
|
||||
void *ptr, uint64_t imm);
|
||||
void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
|
||||
struct qemu_plugin_tb *tb,
|
||||
enum qemu_plugin_op op,
|
||||
qemu_plugin_u64 entry,
|
||||
uint64_t imm);
|
||||
|
||||
/**
|
||||
* qemu_plugin_register_vcpu_insn_exec_cb() - register insn execution cb
|
||||
@@ -331,19 +345,20 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* qemu_plugin_register_vcpu_insn_exec_inline() - insn execution inline op
|
||||
* qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu() - insn exec inline op
|
||||
* @insn: the opaque qemu_plugin_insn handle for an instruction
|
||||
* @op: the type of qemu_plugin_op (e.g. ADD_U64)
|
||||
* @ptr: the target memory location for the op
|
||||
* @entry: entry to run op
|
||||
* @imm: the op data (e.g. 1)
|
||||
*
|
||||
* Insert an inline op to every time an instruction executes. Useful
|
||||
* if you just want to increment a single counter somewhere in memory.
|
||||
* Insert an inline op to every time an instruction executes.
|
||||
*/
|
||||
QEMU_PLUGIN_API
|
||||
void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn,
|
||||
enum qemu_plugin_op op,
|
||||
void *ptr, uint64_t imm);
|
||||
void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
|
||||
struct qemu_plugin_insn *insn,
|
||||
enum qemu_plugin_op op,
|
||||
qemu_plugin_u64 entry,
|
||||
uint64_t imm);
|
||||
|
||||
/**
|
||||
* qemu_plugin_tb_n_insns() - query helper for number of insns in TB
|
||||
@@ -553,24 +568,23 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* qemu_plugin_register_vcpu_mem_inline() - register an inline op to any memory access
|
||||
* qemu_plugin_register_vcpu_mem_inline_per_vcpu() - inline op for mem access
|
||||
* @insn: handle for instruction to instrument
|
||||
* @rw: apply to reads, writes or both
|
||||
* @op: the op, of type qemu_plugin_op
|
||||
* @ptr: pointer memory for the op
|
||||
* @entry: entry to run op
|
||||
* @imm: immediate data for @op
|
||||
*
|
||||
* This registers a inline op every memory access generated by the
|
||||
* instruction. This provides for a lightweight but not thread-safe
|
||||
* way of counting the number of operations done.
|
||||
* instruction.
|
||||
*/
|
||||
QEMU_PLUGIN_API
|
||||
void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn,
|
||||
enum qemu_plugin_mem_rw rw,
|
||||
enum qemu_plugin_op op, void *ptr,
|
||||
uint64_t imm);
|
||||
|
||||
|
||||
void qemu_plugin_register_vcpu_mem_inline_per_vcpu(
|
||||
struct qemu_plugin_insn *insn,
|
||||
enum qemu_plugin_mem_rw rw,
|
||||
enum qemu_plugin_op op,
|
||||
qemu_plugin_u64 entry,
|
||||
uint64_t imm);
|
||||
|
||||
typedef void
|
||||
(*qemu_plugin_vcpu_syscall_cb_t)(qemu_plugin_id_t id, unsigned int vcpu_index,
|
||||
@@ -752,5 +766,75 @@ QEMU_PLUGIN_API
|
||||
int qemu_plugin_read_register(struct qemu_plugin_register *handle,
|
||||
GByteArray *buf);
|
||||
|
||||
/**
|
||||
* qemu_plugin_scoreboard_new() - alloc a new scoreboard
|
||||
*
|
||||
* @element_size: size (in bytes) for one entry
|
||||
*
|
||||
* Returns a pointer to a new scoreboard. It must be freed using
|
||||
* qemu_plugin_scoreboard_free.
|
||||
*/
|
||||
QEMU_PLUGIN_API
|
||||
struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size);
|
||||
|
||||
/**
|
||||
* qemu_plugin_scoreboard_free() - free a scoreboard
|
||||
* @score: scoreboard to free
|
||||
*/
|
||||
QEMU_PLUGIN_API
|
||||
void qemu_plugin_scoreboard_free(struct qemu_plugin_scoreboard *score);
|
||||
|
||||
/**
|
||||
* qemu_plugin_scoreboard_find() - get pointer to an entry of a scoreboard
|
||||
* @score: scoreboard to query
|
||||
* @vcpu_index: entry index
|
||||
*
|
||||
* Returns address of entry of a scoreboard matching a given vcpu_index. This
|
||||
* address can be modified later if scoreboard is resized.
|
||||
*/
|
||||
QEMU_PLUGIN_API
|
||||
void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score,
|
||||
unsigned int vcpu_index);
|
||||
|
||||
/* Macros to define a qemu_plugin_u64 */
|
||||
#define qemu_plugin_scoreboard_u64(score) \
|
||||
(qemu_plugin_u64) {score, 0}
|
||||
#define qemu_plugin_scoreboard_u64_in_struct(score, type, member) \
|
||||
(qemu_plugin_u64) {score, offsetof(type, member)}
|
||||
|
||||
/**
|
||||
* qemu_plugin_u64_add() - add a value to a qemu_plugin_u64 for a given vcpu
|
||||
* @entry: entry to query
|
||||
* @vcpu_index: entry index
|
||||
* @added: value to add
|
||||
*/
|
||||
QEMU_PLUGIN_API
|
||||
void qemu_plugin_u64_add(qemu_plugin_u64 entry, unsigned int vcpu_index,
|
||||
uint64_t added);
|
||||
|
||||
/**
|
||||
* qemu_plugin_u64_get() - get value of a qemu_plugin_u64 for a given vcpu
|
||||
* @entry: entry to query
|
||||
* @vcpu_index: entry index
|
||||
*/
|
||||
QEMU_PLUGIN_API
|
||||
uint64_t qemu_plugin_u64_get(qemu_plugin_u64 entry, unsigned int vcpu_index);
|
||||
|
||||
/**
|
||||
* qemu_plugin_u64_set() - set value of a qemu_plugin_u64 for a given vcpu
|
||||
* @entry: entry to query
|
||||
* @vcpu_index: entry index
|
||||
* @val: new value
|
||||
*/
|
||||
QEMU_PLUGIN_API
|
||||
void qemu_plugin_u64_set(qemu_plugin_u64 entry, unsigned int vcpu_index,
|
||||
uint64_t val);
|
||||
|
||||
/**
|
||||
* qemu_plugin_u64_sum() - return sum of all vcpu entries in a scoreboard
|
||||
* @entry: entry to sum
|
||||
*/
|
||||
QEMU_PLUGIN_API
|
||||
uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry);
|
||||
|
||||
#endif /* QEMU_QEMU_PLUGIN_H */
|
||||
|
||||
@@ -134,7 +134,7 @@ extern char safe_syscall_start[];
|
||||
extern char safe_syscall_end[];
|
||||
|
||||
#define safe_syscall(...) \
|
||||
safe_syscall_base(&((TaskState *)thread_cpu->opaque)->signal_pending, \
|
||||
safe_syscall_base(&get_task_state(thread_cpu)->signal_pending, \
|
||||
__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user