Newer
Older
#include "monitor/monitor.h"
#include "monitor/expr.h"
#include "monitor/watchpoint.h"
#include "nemu.h"
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>
Recolic Keghart
committed
#include <stdexcept>
#include <rlib/stdio.hpp>
using namespace rlib::literals;
using rlib::println;
using rlib::printfln;
using rlib::string;
#include <sstream>
#include <iomanip>
#include <rlib/3rdparty/prettyprint.hpp>
using namespace rlib::_3rdparty::std;
void cpu_exec(uint64_t);
/* We use the `readline' library to provide more flexibility to read from stdin. */
char* rl_gets() {
static char *line_read = NULL;
if (line_read) {
free(line_read);
line_read = NULL;
}
line_read = readline("(nemu) ");
if (line_read && *line_read) {
add_history(line_read);
}
return line_read;
}
static int cmd_c(char *args) {
cpu_exec(-1);
return 0;
}
static int cmd_n(char *args) {
cpu_exec(1);
return 0;
}
static int cmd_info(char *args);
static int cmd_x(char *args);
static int cmd_w(char *args);
static int cmd_d(char *args);
static int cmd_q(char *args) {
return -1;
}
static int cmd_help(char *args);
static struct {
const char *name;
const char *description;
int (*handler) (char *);
} cmd_table [] = {
{ "help", "Display informations about all supported commands", cmd_help },
{ "c", "Continue the execution of the program", cmd_c },
{ "n", "= GDB `n`", cmd_n },
{ "info", "= GDB `info`, supporting `info r` / `info w`", cmd_info },
{ "x", "x <bytes> <start address>, dump memory content.", cmd_x },
{ "w", "w <expr>, add watchpoint for $expr", cmd_w },
{ "d", "d <watchpoint id>, delete watchpoint by id", cmd_d },
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
{ "q", "Exit NEMU", cmd_q },
/* TODO: Add more commands */
};
#define NR_CMD (sizeof(cmd_table) / sizeof(cmd_table[0]))
static int cmd_help(char *args) {
/* extract the first argument */
char *arg = strtok(NULL, " ");
int i;
if (arg == NULL) {
/* no argument given */
for (i = 0; i < NR_CMD; i ++) {
printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
}
}
else {
for (i = 0; i < NR_CMD; i ++) {
if (strcmp(arg, cmd_table[i].name) == 0) {
printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
return 0;
}
}
printf("Unknown command '%s'\n", arg);
}
return 0;
}
void ui_mainloop(int is_batch_mode) {
if (is_batch_mode) {
cmd_c(NULL);
return;
}
for (char *str; (str = rl_gets()) != NULL; ) {
char *str_end = str + strlen(str);
/* extract the first token as the command */
char *cmd = strtok(str, " ");
if (cmd == NULL) { continue; }
/* treat the remaining string as the arguments,
* which may need further parsing
*/
char *args = cmd + strlen(cmd) + 1;
if (args >= str_end) {
args = NULL;
}
#ifdef HAS_IOE
extern void sdl_clear_event_queue(void);
sdl_clear_event_queue();
#endif
int i;
for (i = 0; i < NR_CMD; i ++) {
if (strcmp(cmd, cmd_table[i].name) == 0) {
Recolic Keghart
committed
try {
if (cmd_table[i].handler(args) < 0) { return; }
}
catch(std::exception &e) {
println("Exception caught:", e.what());
}
break;
}
}
if (i == NR_CMD) { printf("Unknown command '%s'\n", cmd); }
}
}
// 3rdparty prettyprint will print rlib::string as array. Let's convert it to normal string.
std::string dumpReg(uint32_t val) {
Recolic Keghart
committed
return string("{}{}[32b=0x{}{}, {}L16b=0x{}]").format(std::setfill('0'), std::setw(8), std::hex, val, std::setw(4), (uint16_t)val);
Recolic Keghart
committed
auto dumpMem(uint32_t begin_addr, uint64_t size) {
std::stringstream res;
res << std::hex;
for(uint64_t cter = 0; cter < size; cter += 4) {
if(cter % 32 == 0) {
res << '\n';
res << "0x" << std::setfill('0') << std::setw(8) << begin_addr + cter << ": ";
}
res << std::setfill('0') << std::setw(8) << vaddr_read(begin_addr + cter, 4) << ' ';
Recolic Keghart
committed
return res.str();
}
static int cmd_info(char *_args) {
if(_args == NULL)
throw std::runtime_error("Usage: info <what>");
if(string(_args).strip() == "r") {
println("Registers:");
printfln("%eax={}, %ebx={}, %ecx={}, %edx={}", dumpReg(cpu.eax), dumpReg(cpu.ebx), dumpReg(cpu.ecx), dumpReg(cpu.edx));
printfln("%esp={}, %ebp={}, %esi={}, %edi={}", dumpReg(cpu.esp), dumpReg(cpu.ebp), dumpReg(cpu.esi), dumpReg(cpu.edi));
printfln("%eip={}", dumpReg(cpu.eip));
}
else if(string(_args).strip() == "w") {
println("Watchpoints:");
println(watchpoints);
}
return 0;
Recolic Keghart
committed
static int cmd_x(char *_args) {
if(_args == NULL)
throw std::runtime_error("Usage: x <size> <startAddr>");
auto args = string(_args).strip().split();
if(args.size() != 2)
throw std::runtime_error("Usage: x <size> <startAddr>");
printfln("Dumping {}{} bytes from {}{}{}:{}", std::dec, args[0], std::hex, args[1], std::dec, dumpMem(std::stoull(args[1], 0, 16), args[0].as<uint64_t>()));
}
static int cmd_w(char *_args) {
if(_args == NULL)
throw std::invalid_argument("w <expr>");
watchpoints.emplace_front(std::string(_args), ++max_watchpoint_id);
auto iter = watchpoints.begin(); // not thread-safe.
rlib::println("Add watchpoint:", *iter);
return 0;
}
static int cmd_d(char *_args) {
if(_args == NULL)
throw std::invalid_argument("d <wp id>");
auto to_remove = rlib::string(_args).as<int>();
watchpoints.remove_if([&](auto &wp){
return wp.id == to_remove;
});
return 0;
}