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:
Peter Maydell
2024-03-06 16:56:20 +00:00
57 changed files with 1258 additions and 339 deletions
+57 -12
View File
@@ -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
View File
@@ -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 {
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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];
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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+");
+3
View File
@@ -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 */
+2 -2
View File
@@ -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
View File
@@ -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;
}
/*
+8
View File
@@ -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;
+9 -3
View File
@@ -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
+7
View File
@@ -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
View File
@@ -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 */
+1 -1
View File
@@ -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