Skip to content

Commit c45c88f

Browse files
long-long-float2011eric
authored andcommitted
Add an option to dump registers as JSON (sysprog21#128)
1 parent 1798e3b commit c45c88f

File tree

7 files changed

+96
-6
lines changed

7 files changed

+96
-6
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,20 @@ $ riscv32-unknown-elf-gdb
153153
Congratulate yourself if `riscv-gdb` does not produce an error message. Now that the GDB
154154
command line is available, you can communicate with `rv32emu`.
155155

156+
## Command line options
157+
158+
### Dump registers as JSON
159+
160+
If an option `--dump-registers [filename]` is specified, the emulator outputs registers as JSON format.
161+
This can be used for tests using the emulator, such as compiler tests.
162+
163+
You can use the option with `--quiet` to use the output directly.
164+
165+
```
166+
# Read the register x10 (a0).
167+
$ build/rv32emu --dump-registers - out.elf --quiet | jq .x10
168+
```
169+
156170
## Contributing
157171
See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
158172

src/emulate.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <assert.h>
77
#include <stdbool.h>
8+
#include <stdio.h>
89
#include <stdlib.h>
910
#include <string.h>
1011

@@ -1588,3 +1589,25 @@ void ecall_handler(riscv_t *rv)
15881589
rv_except_ecall_M(rv, 0);
15891590
syscall_handler(rv);
15901591
}
1592+
1593+
void dump_registers(riscv_t *rv, char *out_file_path)
1594+
{
1595+
FILE *f;
1596+
if (strncmp(out_file_path, "-", 1) == 0) {
1597+
f = stdout;
1598+
} else {
1599+
f = fopen(out_file_path, "w");
1600+
}
1601+
1602+
if (!f) {
1603+
fprintf(stderr, "Cannot open registers output file.\n");
1604+
return;
1605+
}
1606+
1607+
fprintf(f, "{\n");
1608+
for (unsigned i = 0; i < RV_N_REGS; i++) {
1609+
char *comma = i < RV_N_REGS - 1 ? "," : "";
1610+
fprintf(f, " \"x%d\": %u%s\n", i, rv->X[i], comma);
1611+
}
1612+
fprintf(f, "}\n");
1613+
}

src/main.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,17 @@ static bool opt_trace = false;
1818
static bool opt_gdbstub = false;
1919
#endif
2020

21+
/* dump registers as JSON */
22+
static bool opt_dump_regs = false;
23+
static char *registers_out_file;
24+
2125
/* RISC-V arch-test */
2226
static bool opt_arch_test = false;
2327
static char *signature_out_file;
2428

29+
/* Quiet outputs */
30+
static bool opt_quiet_outputs = false;
31+
2532
/* target executable */
2633
static const char *opt_prog_name = "a.out";
2734

@@ -91,6 +98,9 @@ static void print_usage(const char *filename)
9198
#if RV32_HAS(GDBSTUB)
9299
" --gdbstub : allow remote GDB connections (as gdbstub)\n"
93100
#endif
101+
" --dump-registers [filename]: dump registers as JSON to the "
102+
"given file or `-` (STDOUT)\n"
103+
" --quiet : Suppress outputs other than `dump-registers`\n"
94104
" --arch-test [filename] : dump signature to the given file, "
95105
"required by arch-test test\n",
96106
filename);
@@ -115,6 +125,23 @@ static bool parse_args(int argc, char **args)
115125
continue;
116126
}
117127
#endif
128+
if (!strcmp(arg, "--dump-registers")) {
129+
opt_dump_regs = true;
130+
if (i + 1 >= argc) {
131+
fprintf(stderr,
132+
"Filename for registers output required by "
133+
"dump-registers.\n");
134+
return false;
135+
}
136+
registers_out_file = args[++i];
137+
continue;
138+
}
139+
140+
if (!strcmp(arg, "--quiet")) {
141+
opt_quiet_outputs = true;
142+
continue;
143+
}
144+
118145
if (!strcmp(arg, "--arch-test")) {
119146
opt_arch_test = true;
120147
if (i + 1 >= argc) {
@@ -212,7 +239,7 @@ int main(int argc, char **args)
212239
state->break_addr = end->st_value;
213240

214241
/* create the RISC-V runtime */
215-
riscv_t *rv = rv_create(&io, state);
242+
riscv_t *rv = rv_create(&io, state, !opt_quiet_outputs);
216243
if (!rv) {
217244
fprintf(stderr, "Unable to create riscv emulator\n");
218245
return 1;
@@ -237,6 +264,10 @@ int main(int argc, char **args)
237264
run(rv);
238265
}
239266

267+
/* dump registers as JSON */
268+
if (opt_dump_regs)
269+
dump_registers(rv, registers_out_file);
270+
240271
/* dump test result in test mode */
241272
if (opt_arch_test)
242273
dump_test_signature(rv, elf);

src/riscv.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ riscv_word_t rv_get_reg(riscv_t *rv, uint32_t reg)
7676
return ~0U;
7777
}
7878

79-
riscv_t *rv_create(const riscv_io_t *io, riscv_user_t userdata)
79+
riscv_t *rv_create(const riscv_io_t *io,
80+
riscv_user_t userdata,
81+
bool output_exit_code)
8082
{
8183
assert(io);
8284

@@ -88,6 +90,8 @@ riscv_t *rv_create(const riscv_io_t *io, riscv_user_t userdata)
8890
/* copy over the userdata */
8991
rv->userdata = userdata;
9092

93+
rv->output_exit_code = output_exit_code;
94+
9195
/* initialize the block map */
9296
block_map_init(&rv->block_map, 10);
9397

@@ -107,6 +111,11 @@ bool rv_has_halted(riscv_t *rv)
107111
return rv->halt;
108112
}
109113

114+
bool rv_enables_to_output_exit_code(riscv_t *rv)
115+
{
116+
return rv->output_exit_code;
117+
}
118+
110119
void rv_delete(riscv_t *rv)
111120
{
112121
assert(rv);

src/riscv.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ typedef struct {
113113
} riscv_io_t;
114114

115115
/* create a RISC-V emulator */
116-
riscv_t *rv_create(const riscv_io_t *io, riscv_user_t user_data);
116+
riscv_t *rv_create(const riscv_io_t *io,
117+
riscv_user_t user_data,
118+
bool output_exit_code);
117119

118120
/* delete a RISC-V emulator */
119121
void rv_delete(riscv_t *rv);
@@ -150,6 +152,9 @@ void syscall_handler(riscv_t *rv);
150152
/* environment call handler */
151153
void ecall_handler(riscv_t *rv);
152154

155+
/* dump registers as JSON to out_file_path */
156+
void dump_registers(riscv_t *rv, char *out_file_path);
157+
153158
/* breakpoint exception handler */
154159
void ebreak_handler(riscv_t *rv);
155160

@@ -159,6 +164,9 @@ void rv_halt(riscv_t *rv);
159164
/* return the halt state */
160165
bool rv_has_halted(riscv_t *rv);
161166

167+
/* return the flag of outputting exit code */
168+
bool rv_enables_to_output_exit_code(riscv_t *rv);
169+
162170
#ifdef __cplusplus
163171
};
164172
#endif

src/riscv_private.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ struct riscv_internal {
121121

122122
bool compressed; /**< current instruction is compressed or not */
123123
block_map_t block_map; /**< basic block map */
124+
125+
/* print exit code on syscall_exit */
126+
bool output_exit_code;
124127
};
125128

126129
/* sign extend a 16 bit value */

src/syscall.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,11 @@ static void syscall_exit(riscv_t *rv)
107107
{
108108
rv_halt(rv);
109109

110-
riscv_word_t code = rv_get_reg(rv, rv_reg_a0);
111-
/* FIXME: add an option to show/hide this message */
112-
fprintf(stdout, "inferior exit code %d\n", (int) code);
110+
/* To avoid mixing with JSON output */
111+
if (rv_enables_to_output_exit_code(rv)) {
112+
riscv_word_t code = rv_get_reg(rv, rv_reg_a0);
113+
fprintf(stdout, "inferior exit code %d\n", (int) code);
114+
}
113115
}
114116

115117
/* brk(increment)

0 commit comments

Comments
 (0)