Testing, gdbstub and plugin updates

- ensure gitlab references master registry
   - add special rule for hexagon image
   - clean-up gdbstub's argument handling
   - fix replay HMP commands to accept long icount
   - minor re-factor of gdbstub replay handling
   - update syscall plugin to be more useful
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmCtHeQACgkQ+9DbCVqe
 KkR86Af9FecMhuVDn7+doBEJcxMvXYdZZ6SLMf63hhhIn2ZdaiNfZMB7mIZwWiAw
 6FFmCt+A5iySDnMGcbIyIMQp3yi72yLMz5jZcXaSCFN6rO7Wq3t9fBCMFv7Raya9
 SMxqRdmRH+TI2ixQu3tUMa2zyb+lLIBd2MvNMgTLXl+pvamrXY8OV+2QjwlK306+
 emDyT91cEK3w4Qvo3qVi1taJVso98ZO3dOdG2pZFZzZe8bj0r85I4TFMJGOIvMEc
 k7AUFLvaSW12pICFWVP/MdOH/Xo5o7Na1cBi0u56OzvAnwva1KsSnIRUHd9ypbPW
 63DWav1Cv8oYEdjvaMu2QUz/K3+KQA==
 =pl1z
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/stsquad/tags/pull-testing-and-misc-updates-250521-2' into staging

Testing, gdbstub and plugin updates

  - ensure gitlab references master registry
  - add special rule for hexagon image
  - clean-up gdbstub's argument handling
  - fix replay HMP commands to accept long icount
  - minor re-factor of gdbstub replay handling
  - update syscall plugin to be more useful

# gpg: Signature made Tue 25 May 2021 16:55:16 BST
# 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

* remotes/stsquad/tags/pull-testing-and-misc-updates-250521-2:
  plugins/syscall: Added a table-like summary output
  gdbstub: tidy away reverse debugging check into function
  hmp-commands: expand type of icount to "l" in replay commands
  gdbstub: Replace GdbCmdContext with plain g_array()
  gdbstub: Constify GdbCmdParseEntry
  gitlab: add special rule for the hexagon container
  gitlab: explicitly reference the upstream registry

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-05-25 17:31:04 +01:00
commit 0319ad22bd
5 changed files with 298 additions and 185 deletions

View File

@ -12,10 +12,9 @@
script: script:
- echo "TAG:$TAG" - echo "TAG:$TAG"
- echo "COMMON_TAG:$COMMON_TAG" - echo "COMMON_TAG:$COMMON_TAG"
- docker pull "$TAG" || docker pull "$COMMON_TAG" || true
- ./tests/docker/docker.py --engine docker build - ./tests/docker/docker.py --engine docker build
-t "qemu/$NAME" -f "tests/docker/dockerfiles/$NAME.docker" -t "qemu/$NAME" -f "tests/docker/dockerfiles/$NAME.docker"
-r $CI_REGISTRY_IMAGE -r $CI_REGISTRY/qemu-project/qemu
- docker tag "qemu/$NAME" "$TAG" - docker tag "qemu/$NAME" "$TAG"
- docker push "$TAG" - docker push "$TAG"
after_script: after_script:
@ -102,6 +101,34 @@ armhf-debian-cross-container:
variables: variables:
NAME: debian-armhf-cross NAME: debian-armhf-cross
# We never want to build hexagon in the CI system and by default we
# always want to refer to the master registry where it lives.
hexagon-cross-container:
image: docker:stable
stage: containers
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project"'
when: never
- when: always
variables:
NAME: debian-hexagon-cross
GIT_DEPTH: 1
services:
- docker:dind
before_script:
- export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest"
- export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/qemu/$NAME:latest"
- docker info
- docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
script:
- echo "TAG:$TAG"
- echo "COMMON_TAG:$COMMON_TAG"
- docker pull $COMMON_TAG
- docker tag $COMMON_TAG $TAG
- docker push "$TAG"
after_script:
- docker logout
hppa-debian-cross-container: hppa-debian-cross-container:
extends: .container_job_template extends: .container_job_template
stage: containers-layer2 stage: containers-layer2

View File

@ -417,10 +417,13 @@ build-user-static:
MAKE_CHECK_ARGS: check-tcg MAKE_CHECK_ARGS: check-tcg
# Because the hexagon cross-compiler takes so long to build we don't rely # Because the hexagon cross-compiler takes so long to build we don't rely
# on the CI system to build it and hence this job has no dependency # on the CI system to build it and hence this job has an optional dependency
# declared. The image is manually uploaded. # declared. The image is manually uploaded.
build-user-hexagon: build-user-hexagon:
extends: .native_build_job_template extends: .native_build_job_template
needs:
job: hexagon-cross-container
optional: true
variables: variables:
IMAGE: debian-hexagon-cross IMAGE: debian-hexagon-cross
TARGETS: hexagon-linux-user TARGETS: hexagon-linux-user

343
gdbstub.c
View File

@ -465,6 +465,15 @@ int use_gdb_syscalls(void)
return gdb_syscall_mode == GDB_SYS_ENABLED; return gdb_syscall_mode == GDB_SYS_ENABLED;
} }
static bool stub_can_reverse(void)
{
#ifdef CONFIG_USER_ONLY
return false;
#else
return replay_mode == REPLAY_MODE_PLAY;
#endif
}
/* Resume execution. */ /* Resume execution. */
static inline void gdb_continue(void) static inline void gdb_continue(void)
{ {
@ -1338,6 +1347,8 @@ typedef union GdbCmdVariant {
} thread_id; } thread_id;
} GdbCmdVariant; } GdbCmdVariant;
#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i))
static const char *cmd_next_param(const char *param, const char delimiter) static const char *cmd_next_param(const char *param, const char delimiter)
{ {
static const char all_delimiters[] = ",;:="; static const char all_delimiters[] = ",;:=";
@ -1363,55 +1374,52 @@ static const char *cmd_next_param(const char *param, const char delimiter)
} }
static int cmd_parse_params(const char *data, const char *schema, static int cmd_parse_params(const char *data, const char *schema,
GdbCmdVariant *params, int *num_params) GArray *params)
{ {
int curr_param;
const char *curr_schema, *curr_data; const char *curr_schema, *curr_data;
*num_params = 0; g_assert(schema);
g_assert(params->len == 0);
if (!schema) {
return 0;
}
curr_schema = schema; curr_schema = schema;
curr_param = 0;
curr_data = data; curr_data = data;
while (curr_schema[0] && curr_schema[1] && *curr_data) { while (curr_schema[0] && curr_schema[1] && *curr_data) {
GdbCmdVariant this_param;
switch (curr_schema[0]) { switch (curr_schema[0]) {
case 'l': case 'l':
if (qemu_strtoul(curr_data, &curr_data, 16, if (qemu_strtoul(curr_data, &curr_data, 16,
&params[curr_param].val_ul)) { &this_param.val_ul)) {
return -EINVAL; return -EINVAL;
} }
curr_param++;
curr_data = cmd_next_param(curr_data, curr_schema[1]); curr_data = cmd_next_param(curr_data, curr_schema[1]);
g_array_append_val(params, this_param);
break; break;
case 'L': case 'L':
if (qemu_strtou64(curr_data, &curr_data, 16, if (qemu_strtou64(curr_data, &curr_data, 16,
(uint64_t *)&params[curr_param].val_ull)) { (uint64_t *)&this_param.val_ull)) {
return -EINVAL; return -EINVAL;
} }
curr_param++;
curr_data = cmd_next_param(curr_data, curr_schema[1]); curr_data = cmd_next_param(curr_data, curr_schema[1]);
g_array_append_val(params, this_param);
break; break;
case 's': case 's':
params[curr_param].data = curr_data; this_param.data = curr_data;
curr_param++;
curr_data = cmd_next_param(curr_data, curr_schema[1]); curr_data = cmd_next_param(curr_data, curr_schema[1]);
g_array_append_val(params, this_param);
break; break;
case 'o': case 'o':
params[curr_param].opcode = *(uint8_t *)curr_data; this_param.opcode = *(uint8_t *)curr_data;
curr_param++;
curr_data = cmd_next_param(curr_data, curr_schema[1]); curr_data = cmd_next_param(curr_data, curr_schema[1]);
g_array_append_val(params, this_param);
break; break;
case 't': case 't':
params[curr_param].thread_id.kind = this_param.thread_id.kind =
read_thread_id(curr_data, &curr_data, read_thread_id(curr_data, &curr_data,
&params[curr_param].thread_id.pid, &this_param.thread_id.pid,
&params[curr_param].thread_id.tid); &this_param.thread_id.tid);
curr_param++;
curr_data = cmd_next_param(curr_data, curr_schema[1]); curr_data = cmd_next_param(curr_data, curr_schema[1]);
g_array_append_val(params, this_param);
break; break;
case '?': case '?':
curr_data = cmd_next_param(curr_data, curr_schema[1]); curr_data = cmd_next_param(curr_data, curr_schema[1]);
@ -1422,16 +1430,10 @@ static int cmd_parse_params(const char *data, const char *schema,
curr_schema += 2; curr_schema += 2;
} }
*num_params = curr_param;
return 0; return 0;
} }
typedef struct GdbCmdContext { typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx);
GdbCmdVariant *params;
int num_params;
} GdbCmdContext;
typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx);
/* /*
* cmd_startswith -> cmd is compared using startswith * cmd_startswith -> cmd is compared using startswith
@ -1471,8 +1473,8 @@ static inline int startswith(const char *string, const char *pattern)
static int process_string_cmd(void *user_ctx, const char *data, static int process_string_cmd(void *user_ctx, const char *data,
const GdbCmdParseEntry *cmds, int num_cmds) const GdbCmdParseEntry *cmds, int num_cmds)
{ {
int i, schema_len, max_num_params = 0; int i;
GdbCmdContext gdb_ctx; g_autoptr(GArray) params = g_array_new(false, true, sizeof(GdbCmdVariant));
if (!cmds) { if (!cmds) {
return -1; return -1;
@ -1488,24 +1490,13 @@ static int process_string_cmd(void *user_ctx, const char *data,
} }
if (cmd->schema) { if (cmd->schema) {
schema_len = strlen(cmd->schema); if (cmd_parse_params(&data[strlen(cmd->cmd)],
if (schema_len % 2) { cmd->schema, params)) {
return -2; return -1;
} }
max_num_params = schema_len / 2;
} }
gdb_ctx.params = cmd->handler(params, user_ctx);
(GdbCmdVariant *)alloca(sizeof(*gdb_ctx.params) * max_num_params);
memset(gdb_ctx.params, 0, sizeof(*gdb_ctx.params) * max_num_params);
if (cmd_parse_params(&data[strlen(cmd->cmd)], cmd->schema,
gdb_ctx.params, &gdb_ctx.num_params)) {
return -1;
}
cmd->handler(&gdb_ctx, user_ctx);
return 0; return 0;
} }
@ -1528,18 +1519,18 @@ static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd)
} }
} }
static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_detach(GArray *params, void *user_ctx)
{ {
GDBProcess *process; GDBProcess *process;
uint32_t pid = 1; uint32_t pid = 1;
if (gdbserver_state.multiprocess) { if (gdbserver_state.multiprocess) {
if (!gdb_ctx->num_params) { if (!params->len) {
put_packet("E22"); put_packet("E22");
return; return;
} }
pid = gdb_ctx->params[0].val_ul; pid = get_param(params, 0)->val_ul;
} }
process = gdb_get_process(pid); process = gdb_get_process(pid);
@ -1562,22 +1553,22 @@ static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet("OK"); put_packet("OK");
} }
static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_thread_alive(GArray *params, void *user_ctx)
{ {
CPUState *cpu; CPUState *cpu;
if (!gdb_ctx->num_params) { if (!params->len) {
put_packet("E22"); put_packet("E22");
return; return;
} }
if (gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { if (get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) {
put_packet("E22"); put_packet("E22");
return; return;
} }
cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid, cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid,
gdb_ctx->params[0].thread_id.tid); get_param(params, 0)->thread_id.tid);
if (!cpu) { if (!cpu) {
put_packet("E22"); put_packet("E22");
return; return;
@ -1586,17 +1577,17 @@ static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet("OK"); put_packet("OK");
} }
static void handle_continue(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_continue(GArray *params, void *user_ctx)
{ {
if (gdb_ctx->num_params) { if (params->len) {
gdb_set_cpu_pc(gdb_ctx->params[0].val_ull); gdb_set_cpu_pc(get_param(params, 0)->val_ull);
} }
gdbserver_state.signal = 0; gdbserver_state.signal = 0;
gdb_continue(); gdb_continue();
} }
static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_cont_with_sig(GArray *params, void *user_ctx)
{ {
unsigned long signal = 0; unsigned long signal = 0;
@ -1604,8 +1595,8 @@ static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx)
* Note: C sig;[addr] is currently unsupported and we simply * Note: C sig;[addr] is currently unsupported and we simply
* omit the addr parameter * omit the addr parameter
*/ */
if (gdb_ctx->num_params) { if (params->len) {
signal = gdb_ctx->params[0].val_ul; signal = get_param(params, 0)->val_ul;
} }
gdbserver_state.signal = gdb_signal_to_target(signal); gdbserver_state.signal = gdb_signal_to_target(signal);
@ -1615,27 +1606,27 @@ static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx)
gdb_continue(); gdb_continue();
} }
static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_set_thread(GArray *params, void *user_ctx)
{ {
CPUState *cpu; CPUState *cpu;
if (gdb_ctx->num_params != 2) { if (params->len != 2) {
put_packet("E22"); put_packet("E22");
return; return;
} }
if (gdb_ctx->params[1].thread_id.kind == GDB_READ_THREAD_ERR) { if (get_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) {
put_packet("E22"); put_packet("E22");
return; return;
} }
if (gdb_ctx->params[1].thread_id.kind != GDB_ONE_THREAD) { if (get_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) {
put_packet("OK"); put_packet("OK");
return; return;
} }
cpu = gdb_get_cpu(gdb_ctx->params[1].thread_id.pid, cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid,
gdb_ctx->params[1].thread_id.tid); get_param(params, 1)->thread_id.tid);
if (!cpu) { if (!cpu) {
put_packet("E22"); put_packet("E22");
return; return;
@ -1645,7 +1636,7 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx)
* Note: This command is deprecated and modern gdb's will be using the * Note: This command is deprecated and modern gdb's will be using the
* vCont command instead. * vCont command instead.
*/ */
switch (gdb_ctx->params[0].opcode) { switch (get_param(params, 0)->opcode) {
case 'c': case 'c':
gdbserver_state.c_cpu = cpu; gdbserver_state.c_cpu = cpu;
put_packet("OK"); put_packet("OK");
@ -1660,18 +1651,18 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx)
} }
} }
static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_insert_bp(GArray *params, void *user_ctx)
{ {
int res; int res;
if (gdb_ctx->num_params != 3) { if (params->len != 3) {
put_packet("E22"); put_packet("E22");
return; return;
} }
res = gdb_breakpoint_insert(gdb_ctx->params[0].val_ul, res = gdb_breakpoint_insert(get_param(params, 0)->val_ul,
gdb_ctx->params[1].val_ull, get_param(params, 1)->val_ull,
gdb_ctx->params[2].val_ull); get_param(params, 2)->val_ull);
if (res >= 0) { if (res >= 0) {
put_packet("OK"); put_packet("OK");
return; return;
@ -1683,18 +1674,18 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet("E22"); put_packet("E22");
} }
static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_remove_bp(GArray *params, void *user_ctx)
{ {
int res; int res;
if (gdb_ctx->num_params != 3) { if (params->len != 3) {
put_packet("E22"); put_packet("E22");
return; return;
} }
res = gdb_breakpoint_remove(gdb_ctx->params[0].val_ul, res = gdb_breakpoint_remove(get_param(params, 0)->val_ul,
gdb_ctx->params[1].val_ull, get_param(params, 1)->val_ull,
gdb_ctx->params[2].val_ull); get_param(params, 2)->val_ull);
if (res >= 0) { if (res >= 0) {
put_packet("OK"); put_packet("OK");
return; return;
@ -1717,7 +1708,7 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx)
* the remote gdb to fallback to older methods. * the remote gdb to fallback to older methods.
*/ */
static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_set_reg(GArray *params, void *user_ctx)
{ {
int reg_size; int reg_size;
@ -1726,19 +1717,19 @@ static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
return; return;
} }
if (gdb_ctx->num_params != 2) { if (params->len != 2) {
put_packet("E22"); put_packet("E22");
return; return;
} }
reg_size = strlen(gdb_ctx->params[1].data) / 2; reg_size = strlen(get_param(params, 1)->data) / 2;
hextomem(gdbserver_state.mem_buf, gdb_ctx->params[1].data, reg_size); hextomem(gdbserver_state.mem_buf, get_param(params, 1)->data, reg_size);
gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data,
gdb_ctx->params[0].val_ull); get_param(params, 0)->val_ull);
put_packet("OK"); put_packet("OK");
} }
static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_get_reg(GArray *params, void *user_ctx)
{ {
int reg_size; int reg_size;
@ -1747,14 +1738,14 @@ static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
return; return;
} }
if (!gdb_ctx->num_params) { if (!params->len) {
put_packet("E14"); put_packet("E14");
return; return;
} }
reg_size = gdb_read_register(gdbserver_state.g_cpu, reg_size = gdb_read_register(gdbserver_state.g_cpu,
gdbserver_state.mem_buf, gdbserver_state.mem_buf,
gdb_ctx->params[0].val_ull); get_param(params, 0)->val_ull);
if (!reg_size) { if (!reg_size) {
put_packet("E14"); put_packet("E14");
return; return;
@ -1766,22 +1757,24 @@ static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf(); put_strbuf();
} }
static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_write_mem(GArray *params, void *user_ctx)
{ {
if (gdb_ctx->num_params != 3) { if (params->len != 3) {
put_packet("E22"); put_packet("E22");
return; return;
} }
/* hextomem() reads 2*len bytes */ /* hextomem() reads 2*len bytes */
if (gdb_ctx->params[1].val_ull > strlen(gdb_ctx->params[2].data) / 2) { if (get_param(params, 1)->val_ull >
strlen(get_param(params, 2)->data) / 2) {
put_packet("E22"); put_packet("E22");
return; return;
} }
hextomem(gdbserver_state.mem_buf, gdb_ctx->params[2].data, hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data,
gdb_ctx->params[1].val_ull); get_param(params, 1)->val_ull);
if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull, if (target_memory_rw_debug(gdbserver_state.g_cpu,
get_param(params, 0)->val_ull,
gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->data,
gdbserver_state.mem_buf->len, true)) { gdbserver_state.mem_buf->len, true)) {
put_packet("E14"); put_packet("E14");
@ -1791,22 +1784,24 @@ static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet("OK"); put_packet("OK");
} }
static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_read_mem(GArray *params, void *user_ctx)
{ {
if (gdb_ctx->num_params != 2) { if (params->len != 2) {
put_packet("E22"); put_packet("E22");
return; return;
} }
/* memtohex() doubles the required space */ /* memtohex() doubles the required space */
if (gdb_ctx->params[1].val_ull > MAX_PACKET_LENGTH / 2) { if (get_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) {
put_packet("E22"); put_packet("E22");
return; return;
} }
g_byte_array_set_size(gdbserver_state.mem_buf, gdb_ctx->params[1].val_ull); g_byte_array_set_size(gdbserver_state.mem_buf,
get_param(params, 1)->val_ull);
if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull, if (target_memory_rw_debug(gdbserver_state.g_cpu,
get_param(params, 0)->val_ull,
gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->data,
gdbserver_state.mem_buf->len, false)) { gdbserver_state.mem_buf->len, false)) {
put_packet("E14"); put_packet("E14");
@ -1818,19 +1813,19 @@ static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf(); put_strbuf();
} }
static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_write_all_regs(GArray *params, void *user_ctx)
{ {
target_ulong addr, len; target_ulong addr, len;
uint8_t *registers; uint8_t *registers;
int reg_size; int reg_size;
if (!gdb_ctx->num_params) { if (!params->len) {
return; return;
} }
cpu_synchronize_state(gdbserver_state.g_cpu); cpu_synchronize_state(gdbserver_state.g_cpu);
len = strlen(gdb_ctx->params[0].data) / 2; len = strlen(get_param(params, 0)->data) / 2;
hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len); hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
registers = gdbserver_state.mem_buf->data; registers = gdbserver_state.mem_buf->data;
for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0;
addr++) { addr++) {
@ -1841,7 +1836,7 @@ static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet("OK"); put_packet("OK");
} }
static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_read_all_regs(GArray *params, void *user_ctx)
{ {
target_ulong addr, len; target_ulong addr, len;
@ -1859,14 +1854,14 @@ static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf(); put_strbuf();
} }
static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_file_io(GArray *params, void *user_ctx)
{ {
if (gdb_ctx->num_params >= 1 && gdbserver_state.current_syscall_cb) { if (params->len >= 1 && gdbserver_state.current_syscall_cb) {
target_ulong ret, err; target_ulong ret, err;
ret = (target_ulong)gdb_ctx->params[0].val_ull; ret = (target_ulong)get_param(params, 0)->val_ull;
if (gdb_ctx->num_params >= 2) { if (params->len >= 2) {
err = (target_ulong)gdb_ctx->params[1].val_ull; err = (target_ulong)get_param(params, 1)->val_ull;
} else { } else {
err = 0; err = 0;
} }
@ -1874,7 +1869,7 @@ static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx)
gdbserver_state.current_syscall_cb = NULL; gdbserver_state.current_syscall_cb = NULL;
} }
if (gdb_ctx->num_params >= 3 && gdb_ctx->params[2].opcode == (uint8_t)'C') { if (params->len >= 3 && get_param(params, 2)->opcode == (uint8_t)'C') {
put_packet("T02"); put_packet("T02");
return; return;
} }
@ -1882,23 +1877,23 @@ static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx)
gdb_continue(); gdb_continue();
} }
static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_step(GArray *params, void *user_ctx)
{ {
if (gdb_ctx->num_params) { if (params->len) {
gdb_set_cpu_pc((target_ulong)gdb_ctx->params[0].val_ull); gdb_set_cpu_pc((target_ulong)get_param(params, 0)->val_ull);
} }
cpu_single_step(gdbserver_state.c_cpu, get_sstep_flags()); cpu_single_step(gdbserver_state.c_cpu, get_sstep_flags());
gdb_continue(); gdb_continue();
} }
static void handle_backward(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_backward(GArray *params, void *user_ctx)
{ {
if (replay_mode != REPLAY_MODE_PLAY) { if (!stub_can_reverse()) {
put_packet("E22"); put_packet("E22");
} }
if (gdb_ctx->num_params == 1) { if (params->len == 1) {
switch (gdb_ctx->params[0].opcode) { switch (get_param(params, 0)->opcode) {
case 's': case 's':
if (replay_reverse_step()) { if (replay_reverse_step()) {
gdb_continue(); gdb_continue();
@ -1920,20 +1915,20 @@ static void handle_backward(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet(""); put_packet("");
} }
static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_v_cont_query(GArray *params, void *user_ctx)
{ {
put_packet("vCont;c;C;s;S"); put_packet("vCont;c;C;s;S");
} }
static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_v_cont(GArray *params, void *user_ctx)
{ {
int res; int res;
if (!gdb_ctx->num_params) { if (!params->len) {
return; return;
} }
res = gdb_handle_vcont(gdb_ctx->params[0].data); res = gdb_handle_vcont(get_param(params, 0)->data);
if ((res == -EINVAL) || (res == -ERANGE)) { if ((res == -EINVAL) || (res == -ERANGE)) {
put_packet("E22"); put_packet("E22");
} else if (res) { } else if (res) {
@ -1941,17 +1936,17 @@ static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx)
} }
} }
static void handle_v_attach(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_v_attach(GArray *params, void *user_ctx)
{ {
GDBProcess *process; GDBProcess *process;
CPUState *cpu; CPUState *cpu;
g_string_assign(gdbserver_state.str_buf, "E22"); g_string_assign(gdbserver_state.str_buf, "E22");
if (!gdb_ctx->num_params) { if (!params->len) {
goto cleanup; goto cleanup;
} }
process = gdb_get_process(gdb_ctx->params[0].val_ul); process = gdb_get_process(get_param(params, 0)->val_ul);
if (!process) { if (!process) {
goto cleanup; goto cleanup;
} }
@ -1972,7 +1967,7 @@ cleanup:
put_strbuf(); put_strbuf();
} }
static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_v_kill(GArray *params, void *user_ctx)
{ {
/* Kill the target */ /* Kill the target */
put_packet("OK"); put_packet("OK");
@ -1981,7 +1976,7 @@ static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx)
exit(0); exit(0);
} }
static GdbCmdParseEntry gdb_v_commands_table[] = { static const GdbCmdParseEntry gdb_v_commands_table[] = {
/* Order is important if has same prefix */ /* Order is important if has same prefix */
{ {
.handler = handle_v_cont_query, .handler = handle_v_cont_query,
@ -2007,43 +2002,43 @@ static GdbCmdParseEntry gdb_v_commands_table[] = {
}, },
}; };
static void handle_v_commands(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_v_commands(GArray *params, void *user_ctx)
{ {
if (!gdb_ctx->num_params) { if (!params->len) {
return; return;
} }
if (process_string_cmd(NULL, gdb_ctx->params[0].data, if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_v_commands_table, gdb_v_commands_table,
ARRAY_SIZE(gdb_v_commands_table))) { ARRAY_SIZE(gdb_v_commands_table))) {
put_packet(""); put_packet("");
} }
} }
static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_qemu_sstepbits(GArray *params, void *user_ctx)
{ {
g_string_printf(gdbserver_state.str_buf, "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", g_string_printf(gdbserver_state.str_buf, "ENABLE=%x,NOIRQ=%x,NOTIMER=%x",
SSTEP_ENABLE, SSTEP_NOIRQ, SSTEP_NOTIMER); SSTEP_ENABLE, SSTEP_NOIRQ, SSTEP_NOTIMER);
put_strbuf(); put_strbuf();
} }
static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_set_qemu_sstep(GArray *params, void *user_ctx)
{ {
if (!gdb_ctx->num_params) { if (!params->len) {
return; return;
} }
sstep_flags = gdb_ctx->params[0].val_ul; sstep_flags = get_param(params, 0)->val_ul;
put_packet("OK"); put_packet("OK");
} }
static void handle_query_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_qemu_sstep(GArray *params, void *user_ctx)
{ {
g_string_printf(gdbserver_state.str_buf, "0x%x", sstep_flags); g_string_printf(gdbserver_state.str_buf, "0x%x", sstep_flags);
put_strbuf(); put_strbuf();
} }
static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_curr_tid(GArray *params, void *user_ctx)
{ {
CPUState *cpu; CPUState *cpu;
GDBProcess *process; GDBProcess *process;
@ -2060,7 +2055,7 @@ static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf(); put_strbuf();
} }
static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_threads(GArray *params, void *user_ctx)
{ {
if (!gdbserver_state.query_cpu) { if (!gdbserver_state.query_cpu) {
put_packet("l"); put_packet("l");
@ -2073,25 +2068,25 @@ static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx)
gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu);
} }
static void handle_query_first_threads(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_first_threads(GArray *params, void *user_ctx)
{ {
gdbserver_state.query_cpu = gdb_first_attached_cpu(); gdbserver_state.query_cpu = gdb_first_attached_cpu();
handle_query_threads(gdb_ctx, user_ctx); handle_query_threads(params, user_ctx);
} }
static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_thread_extra(GArray *params, void *user_ctx)
{ {
g_autoptr(GString) rs = g_string_new(NULL); g_autoptr(GString) rs = g_string_new(NULL);
CPUState *cpu; CPUState *cpu;
if (!gdb_ctx->num_params || if (!params->len ||
gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) {
put_packet("E22"); put_packet("E22");
return; return;
} }
cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid, cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid,
gdb_ctx->params[0].thread_id.tid); get_param(params, 0)->thread_id.tid);
if (!cpu) { if (!cpu) {
return; return;
} }
@ -2116,7 +2111,7 @@ static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx)
} }
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_offsets(GArray *params, void *user_ctx)
{ {
TaskState *ts; TaskState *ts;
@ -2131,17 +2126,17 @@ static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf(); put_strbuf();
} }
#else #else
static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_rcmd(GArray *params, void *user_ctx)
{ {
const guint8 zero = 0; const guint8 zero = 0;
int len; int len;
if (!gdb_ctx->num_params) { if (!params->len) {
put_packet("E22"); put_packet("E22");
return; return;
} }
len = strlen(gdb_ctx->params[0].data); len = strlen(get_param(params, 0)->data);
if (len % 2) { if (len % 2) {
put_packet("E01"); put_packet("E01");
return; return;
@ -2149,7 +2144,7 @@ static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx)
g_assert(gdbserver_state.mem_buf->len == 0); g_assert(gdbserver_state.mem_buf->len == 0);
len = len / 2; len = len / 2;
hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len); hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); g_byte_array_append(gdbserver_state.mem_buf, &zero, 1);
qemu_chr_be_write(gdbserver_state.mon_chr, gdbserver_state.mem_buf->data, qemu_chr_be_write(gdbserver_state.mon_chr, gdbserver_state.mem_buf->data,
gdbserver_state.mem_buf->len); gdbserver_state.mem_buf->len);
@ -2157,7 +2152,7 @@ static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx)
} }
#endif #endif
static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_supported(GArray *params, void *user_ctx)
{ {
CPUClass *cc; CPUClass *cc;
@ -2167,7 +2162,7 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+");
} }
if (replay_mode == REPLAY_MODE_PLAY) { if (stub_can_reverse()) {
g_string_append(gdbserver_state.str_buf, g_string_append(gdbserver_state.str_buf,
";ReverseStep+;ReverseContinue+"); ";ReverseStep+;ReverseContinue+");
} }
@ -2178,8 +2173,8 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
} }
#endif #endif
if (gdb_ctx->num_params && if (params->len &&
strstr(gdb_ctx->params[0].data, "multiprocess+")) { strstr(get_param(params, 0)->data, "multiprocess+")) {
gdbserver_state.multiprocess = true; gdbserver_state.multiprocess = true;
} }
@ -2187,7 +2182,7 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf(); put_strbuf();
} }
static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_xfer_features(GArray *params, void *user_ctx)
{ {
GDBProcess *process; GDBProcess *process;
CPUClass *cc; CPUClass *cc;
@ -2195,7 +2190,7 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx)
const char *xml; const char *xml;
const char *p; const char *p;
if (gdb_ctx->num_params < 3) { if (params->len < 3) {
put_packet("E22"); put_packet("E22");
return; return;
} }
@ -2208,15 +2203,15 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx)
} }
gdb_has_xml = true; gdb_has_xml = true;
p = gdb_ctx->params[0].data; p = get_param(params, 0)->data;
xml = get_feature_xml(p, &p, process); xml = get_feature_xml(p, &p, process);
if (!xml) { if (!xml) {
put_packet("E00"); put_packet("E00");
return; return;
} }
addr = gdb_ctx->params[1].val_ul; addr = get_param(params, 1)->val_ul;
len = gdb_ctx->params[2].val_ul; len = get_param(params, 2)->val_ul;
total_len = strlen(xml); total_len = strlen(xml);
if (addr > total_len) { if (addr > total_len) {
put_packet("E00"); put_packet("E00");
@ -2240,18 +2235,18 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx)
} }
#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER) #if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER)
static void handle_query_xfer_auxv(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_xfer_auxv(GArray *params, void *user_ctx)
{ {
TaskState *ts; TaskState *ts;
unsigned long offset, len, saved_auxv, auxv_len; unsigned long offset, len, saved_auxv, auxv_len;
if (gdb_ctx->num_params < 2) { if (params->len < 2) {
put_packet("E22"); put_packet("E22");
return; return;
} }
offset = gdb_ctx->params[0].val_ul; offset = get_param(params, 0)->val_ul;
len = gdb_ctx->params[1].val_ul; len = get_param(params, 1)->val_ul;
ts = gdbserver_state.c_cpu->opaque; ts = gdbserver_state.c_cpu->opaque;
saved_auxv = ts->info->saved_auxv; saved_auxv = ts->info->saved_auxv;
auxv_len = ts->info->auxv_len; auxv_len = ts->info->auxv_len;
@ -2286,12 +2281,12 @@ static void handle_query_xfer_auxv(GdbCmdContext *gdb_ctx, void *user_ctx)
} }
#endif #endif
static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_attached(GArray *params, void *user_ctx)
{ {
put_packet(GDB_ATTACHED); put_packet(GDB_ATTACHED);
} }
static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_qemu_supported(GArray *params, void *user_ctx)
{ {
g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep"); g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep");
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
@ -2301,21 +2296,21 @@ static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
} }
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, static void handle_query_qemu_phy_mem_mode(GArray *params,
void *user_ctx) void *user_ctx)
{ {
g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode); g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode);
put_strbuf(); put_strbuf();
} }
static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx)
{ {
if (!gdb_ctx->num_params) { if (!params->len) {
put_packet("E22"); put_packet("E22");
return; return;
} }
if (!gdb_ctx->params[0].val_ul) { if (!get_param(params, 0)->val_ul) {
phy_memory_mode = 0; phy_memory_mode = 0;
} else { } else {
phy_memory_mode = 1; phy_memory_mode = 1;
@ -2324,7 +2319,7 @@ static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx)
} }
#endif #endif
static GdbCmdParseEntry gdb_gen_query_set_common_table[] = { static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
/* Order is important if has same prefix */ /* Order is important if has same prefix */
{ {
.handler = handle_query_qemu_sstepbits, .handler = handle_query_qemu_sstepbits,
@ -2342,7 +2337,7 @@ static GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
}, },
}; };
static GdbCmdParseEntry gdb_gen_query_table[] = { static const GdbCmdParseEntry gdb_gen_query_table[] = {
{ {
.handler = handle_query_curr_tid, .handler = handle_query_curr_tid,
.cmd = "C", .cmd = "C",
@ -2420,7 +2415,7 @@ static GdbCmdParseEntry gdb_gen_query_table[] = {
#endif #endif
}; };
static GdbCmdParseEntry gdb_gen_set_table[] = { static const GdbCmdParseEntry gdb_gen_set_table[] = {
/* Order is important if has same prefix */ /* Order is important if has same prefix */
{ {
.handler = handle_set_qemu_sstep, .handler = handle_set_qemu_sstep,
@ -2438,45 +2433,45 @@ static GdbCmdParseEntry gdb_gen_set_table[] = {
#endif #endif
}; };
static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_gen_query(GArray *params, void *user_ctx)
{ {
if (!gdb_ctx->num_params) { if (!params->len) {
return; return;
} }
if (!process_string_cmd(NULL, gdb_ctx->params[0].data, if (!process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_query_set_common_table, gdb_gen_query_set_common_table,
ARRAY_SIZE(gdb_gen_query_set_common_table))) { ARRAY_SIZE(gdb_gen_query_set_common_table))) {
return; return;
} }
if (process_string_cmd(NULL, gdb_ctx->params[0].data, if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_query_table, gdb_gen_query_table,
ARRAY_SIZE(gdb_gen_query_table))) { ARRAY_SIZE(gdb_gen_query_table))) {
put_packet(""); put_packet("");
} }
} }
static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_gen_set(GArray *params, void *user_ctx)
{ {
if (!gdb_ctx->num_params) { if (!params->len) {
return; return;
} }
if (!process_string_cmd(NULL, gdb_ctx->params[0].data, if (!process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_query_set_common_table, gdb_gen_query_set_common_table,
ARRAY_SIZE(gdb_gen_query_set_common_table))) { ARRAY_SIZE(gdb_gen_query_set_common_table))) {
return; return;
} }
if (process_string_cmd(NULL, gdb_ctx->params[0].data, if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_set_table, gdb_gen_set_table,
ARRAY_SIZE(gdb_gen_set_table))) { ARRAY_SIZE(gdb_gen_set_table))) {
put_packet(""); put_packet("");
} }
} }
static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_target_halt(GArray *params, void *user_ctx)
{ {
g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP);
gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf); gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf);

View File

@ -1667,7 +1667,7 @@ ERST
{ {
.name = "replay_break", .name = "replay_break",
.args_type = "icount:i", .args_type = "icount:l",
.params = "icount", .params = "icount",
.help = "set breakpoint at the specified instruction count", .help = "set breakpoint at the specified instruction count",
.cmd = hmp_replay_break, .cmd = hmp_replay_break,
@ -1699,7 +1699,7 @@ ERST
{ {
.name = "replay_seek", .name = "replay_seek",
.args_type = "icount:i", .args_type = "icount:l",
.params = "icount", .params = "icount",
.help = "replay execution to the specified instruction count", .help = "replay execution to the specified instruction count",
.cmd = hmp_replay_seek, .cmd = hmp_replay_seek,

View File

@ -16,32 +16,120 @@
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
typedef struct {
int64_t num;
int64_t calls;
int64_t errors;
} SyscallStats;
static GMutex lock;
static GHashTable *statistics;
static SyscallStats *get_or_create_entry(int64_t num)
{
SyscallStats *entry =
(SyscallStats *) g_hash_table_lookup(statistics, GINT_TO_POINTER(num));
if (!entry) {
entry = g_new0(SyscallStats, 1);
entry->num = num;
g_hash_table_insert(statistics, GINT_TO_POINTER(num), (gpointer) entry);
}
return entry;
}
static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index, static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index,
int64_t num, uint64_t a1, uint64_t a2, int64_t num, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a3, uint64_t a4, uint64_t a5,
uint64_t a6, uint64_t a7, uint64_t a8) uint64_t a6, uint64_t a7, uint64_t a8)
{ {
g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num); if (statistics) {
qemu_plugin_outs(out); SyscallStats *entry;
g_mutex_lock(&lock);
entry = get_or_create_entry(num);
entry->calls++;
g_mutex_unlock(&lock);
} else {
g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num);
qemu_plugin_outs(out);
}
} }
static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx, static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx,
int64_t num, int64_t ret) int64_t num, int64_t ret)
{
if (statistics) {
SyscallStats *entry;
g_mutex_lock(&lock);
/* Should always return an existent entry. */
entry = get_or_create_entry(num);
if (ret < 0) {
entry->errors++;
}
g_mutex_unlock(&lock);
} else {
g_autofree gchar *out;
out = g_strdup_printf("syscall #%" PRIi64 " returned -> %" PRIi64 "\n",
num, ret);
qemu_plugin_outs(out);
}
}
static void print_entry(gpointer val, gpointer user_data)
{ {
g_autofree gchar *out; g_autofree gchar *out;
out = g_strdup_printf("syscall #%" PRIi64 " returned -> %" PRIi64 "\n", SyscallStats *entry = (SyscallStats *) val;
num, ret); int64_t syscall_num = entry->num;
out = g_strdup_printf(
"%-13" PRIi64 "%-6" PRIi64 " %" PRIi64 "\n",
syscall_num, entry->calls, entry->errors);
qemu_plugin_outs(out); qemu_plugin_outs(out);
} }
/* ************************************************************************* */ static gint comp_func(gconstpointer ea, gconstpointer eb)
{
SyscallStats *ent_a = (SyscallStats *) ea;
SyscallStats *ent_b = (SyscallStats *) eb;
static void plugin_exit(qemu_plugin_id_t id, void *p) {} return ent_a->calls > ent_b->calls ? -1 : 1;
}
/* ************************************************************************* */
static void plugin_exit(qemu_plugin_id_t id, void *p)
{
if (!statistics) {
return;
}
g_mutex_lock(&lock);
GList *entries = g_hash_table_get_values(statistics);
entries = g_list_sort(entries, comp_func);
qemu_plugin_outs("syscall no. calls errors\n");
g_list_foreach(entries, print_entry, NULL);
g_list_free(entries);
g_hash_table_destroy(statistics);
g_mutex_unlock(&lock);
}
QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
const qemu_info_t *info, const qemu_info_t *info,
int argc, char **argv) int argc, char **argv)
{ {
if (argc == 0) {
statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free);
} else {
for (int i = 0; i < argc; i++) {
if (g_strcmp0(argv[i], "print") != 0) {
fprintf(stderr, "unsupported argument: %s\n", argv[i]);
return -1;
}
}
}
qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall); qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall);
qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret); qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret);
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);