mirror of
https://github.com/open-goal/jak-project
synced 2026-07-08 22:45:00 -04:00
Add some debugger memory utilities (#109)
* add some memory utilities * run waitpid in a separate thread and support very simple breakpoints * fix breakpoints * add missing windows stub function * fix error message on exit
This commit is contained in:
@@ -1,7 +1,50 @@
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
#include "goalc/emitter/disassemble.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
u32 Compiler::parse_address_spec(const goos::Object& form) {
|
||||
if (form.is_int()) {
|
||||
return form.as_int();
|
||||
}
|
||||
|
||||
if (form.is_pair()) {
|
||||
auto first = form.as_pair()->car;
|
||||
auto rest = form.as_pair()->cdr;
|
||||
if (first.is_symbol() && symbol_string(first) == "sym") {
|
||||
if (rest.is_pair() && rest.as_pair()->car.is_symbol()) {
|
||||
u32 addr = m_debugger.get_symbol_address(symbol_string(rest.as_pair()->car));
|
||||
if (!addr) {
|
||||
throw_compile_error(form, "debugger doesn't know where the symbol is");
|
||||
}
|
||||
return addr;
|
||||
} else {
|
||||
throw_compile_error(form, "invalid sym form");
|
||||
return 0;
|
||||
}
|
||||
} else if (first.is_symbol() && symbol_string(first) == "sym-val") {
|
||||
if (rest.is_pair() && rest.as_pair()->car.is_symbol()) {
|
||||
u32 addr = 0;
|
||||
if (!m_debugger.get_symbol_value(symbol_string(rest.as_pair()->car), &addr)) {
|
||||
throw_compile_error(form, "debugger doesn't know where the symbol is");
|
||||
}
|
||||
return addr;
|
||||
} else {
|
||||
throw_compile_error(form, "invalid sym-val form");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
throw_compile_error(form, "can't parse this address spec");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
throw_compile_error(form, "can't parse this address spec");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Val* Compiler::compile_dbg(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
// todo - do something with args.
|
||||
(void)form;
|
||||
@@ -104,5 +147,236 @@ Val* Compiler::compile_dump_all(const goos::Object& form, const goos::Object& re
|
||||
} else {
|
||||
file_util::write_binary_file(file_util::get_file_path({dest_file}), buffer, EE_MAIN_MEM_SIZE);
|
||||
}
|
||||
return get_none();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
enum PrintMode { HEX, UNSIGNED_DEC, SIGNED_DEC, FLOAT };
|
||||
|
||||
template <typename T>
|
||||
void mem_print(T* data, int count, u32 start_addr, PrintMode mode) {
|
||||
// always print 16 bytes / line.
|
||||
int elt_per_line = 16 / sizeof(T);
|
||||
|
||||
// pad wit the correct number of zeros.
|
||||
std::string format_string;
|
||||
|
||||
switch (mode) {
|
||||
case HEX:
|
||||
format_string = "0x{:0" + std::to_string(2 * sizeof(T)) + "x} ";
|
||||
break;
|
||||
case UNSIGNED_DEC:
|
||||
case SIGNED_DEC:
|
||||
format_string = "{:" + std::to_string(3 * sizeof(T)) + "d} ";
|
||||
break;
|
||||
case FLOAT:
|
||||
format_string = "{:8.4f} "; // todo, is this what we want?
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// loop over elts
|
||||
for (int i = 0; i < count; i++) {
|
||||
if ((i % elt_per_line) == 0) {
|
||||
// first in line, so we should print the GOAL address
|
||||
fmt::print(" 0x{:08x}: ", start_addr + (i * sizeof(T)));
|
||||
}
|
||||
|
||||
// print the thing
|
||||
fmt::print(format_string, data[i]);
|
||||
|
||||
if ((i % elt_per_line) == (elt_per_line - 1)) {
|
||||
// last in line, newline!
|
||||
fmt::print("\n");
|
||||
}
|
||||
}
|
||||
fmt::print("\n");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Val* Compiler::compile_pm(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
(void)env;
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {goos::ObjectType::INTEGER, {}, goos::ObjectType::INTEGER},
|
||||
{{"print-mode", {false, goos::ObjectType::SYMBOL}}});
|
||||
|
||||
int elt_size = args.unnamed.at(0).as_int();
|
||||
u32 addr = parse_address_spec(args.unnamed.at(1));
|
||||
u32 elts = args.unnamed.at(2).as_int();
|
||||
|
||||
PrintMode mode = HEX;
|
||||
|
||||
if (args.has_named("print-mode")) {
|
||||
auto mode_name = symbol_string(args.get_named("print-mode"));
|
||||
if (mode_name == "hex") {
|
||||
mode = HEX;
|
||||
} else if (mode_name == "unsigned-dec") {
|
||||
mode = UNSIGNED_DEC;
|
||||
} else if (mode_name == "signed-dec") {
|
||||
mode = SIGNED_DEC;
|
||||
} else if (mode_name == "float") {
|
||||
mode = FLOAT;
|
||||
} else {
|
||||
throw_compile_error(form, "Unknown print-mode for :pm " + mode_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_debugger.is_halted()) {
|
||||
throw_compile_error(
|
||||
form, "Cannot print memory, the debugger must be connected and the target must be halted.");
|
||||
}
|
||||
|
||||
auto mem_size = elts * elt_size;
|
||||
if (mem_size > 1024 * 1024) {
|
||||
throw_compile_error(
|
||||
form,
|
||||
fmt::format(":pm used on over 1 MB of memory, this probably isn't what you meant to do."));
|
||||
}
|
||||
|
||||
std::vector<u8> mem;
|
||||
mem.resize(mem_size);
|
||||
|
||||
if (addr < EE_MAIN_MEM_LOW_PROTECT || (addr + mem_size) > EE_MAIN_MEM_SIZE) {
|
||||
throw_compile_error(form, ":pm memory out of range");
|
||||
}
|
||||
|
||||
m_debugger.read_memory(mem.data(), mem_size, addr);
|
||||
|
||||
switch (mode) {
|
||||
case HEX:
|
||||
case UNSIGNED_DEC:
|
||||
switch (elt_size) {
|
||||
case 1:
|
||||
mem_print((u8*)mem.data(), elts, addr, mode);
|
||||
break;
|
||||
case 2:
|
||||
mem_print((u16*)mem.data(), elts, addr, mode);
|
||||
break;
|
||||
case 4:
|
||||
mem_print((u32*)mem.data(), elts, addr, mode);
|
||||
break;
|
||||
case 8:
|
||||
mem_print((u64*)mem.data(), elts, addr, mode);
|
||||
break;
|
||||
default:
|
||||
throw_compile_error(form, ":pm bad element size");
|
||||
}
|
||||
break;
|
||||
case SIGNED_DEC:
|
||||
switch (elt_size) {
|
||||
case 1:
|
||||
mem_print((s8*)mem.data(), elts, addr, mode);
|
||||
break;
|
||||
case 2:
|
||||
mem_print((s16*)mem.data(), elts, addr, mode);
|
||||
break;
|
||||
case 4:
|
||||
mem_print((s32*)mem.data(), elts, addr, mode);
|
||||
break;
|
||||
case 8:
|
||||
mem_print((s64*)mem.data(), elts, addr, mode);
|
||||
break;
|
||||
default:
|
||||
throw_compile_error(form, ":pm bad element size");
|
||||
}
|
||||
break;
|
||||
case FLOAT:
|
||||
switch (elt_size) {
|
||||
case 4:
|
||||
mem_print((float*)mem.data(), elts, addr, mode);
|
||||
break;
|
||||
default:
|
||||
throw_compile_error(form, ":pm bad element size");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return get_none();
|
||||
}
|
||||
|
||||
Val* Compiler::compile_di(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
(void)form;
|
||||
(void)rest;
|
||||
(void)env;
|
||||
if (!m_debugger.is_halted()) {
|
||||
throw_compile_error(
|
||||
form,
|
||||
"Cannot get debug info, the debugger must be connected and the target must be halted.");
|
||||
}
|
||||
|
||||
m_debugger.get_break_info();
|
||||
return get_none();
|
||||
}
|
||||
|
||||
Val* Compiler::compile_disasm(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
(void)env;
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, goos::ObjectType::INTEGER}, {});
|
||||
u32 addr = parse_address_spec(args.unnamed.at(0));
|
||||
u32 size = args.unnamed.at(1).as_int();
|
||||
|
||||
if (!m_debugger.is_halted()) {
|
||||
throw_compile_error(
|
||||
form,
|
||||
"Cannot disassemble memory, the debugger must be connected and the target must be halted.");
|
||||
}
|
||||
|
||||
if (size > 1024 * 1024) {
|
||||
throw_compile_error(
|
||||
form,
|
||||
fmt::format(
|
||||
":disasm used on over 1 MB of memory, this probably isn't what you meant to do."));
|
||||
}
|
||||
|
||||
std::vector<u8> mem;
|
||||
mem.resize(size);
|
||||
|
||||
if (addr < EE_MAIN_MEM_LOW_PROTECT || (addr + size) > EE_MAIN_MEM_SIZE) {
|
||||
throw_compile_error(form, ":disasm memory out of range");
|
||||
}
|
||||
|
||||
m_debugger.read_memory(mem.data(), size, addr);
|
||||
|
||||
fmt::print("{}\n",
|
||||
disassemble_x86(mem.data(), mem.size(), m_debugger.get_x86_base_addr() + addr));
|
||||
|
||||
return get_none();
|
||||
}
|
||||
|
||||
Val* Compiler::compile_bp(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
(void)env;
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}}, {});
|
||||
|
||||
if (!m_debugger.is_halted()) {
|
||||
throw_compile_error(
|
||||
form,
|
||||
"Cannot add breakpoint, the debugger must be connected and the target must be halted.");
|
||||
}
|
||||
|
||||
u32 addr = parse_address_spec(args.unnamed.at(0));
|
||||
m_debugger.add_addr_breakpoint(addr);
|
||||
|
||||
return get_none();
|
||||
}
|
||||
|
||||
Val* Compiler::compile_ubp(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
(void)env;
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}}, {});
|
||||
|
||||
if (!m_debugger.is_halted()) {
|
||||
throw_compile_error(
|
||||
form,
|
||||
"Cannot remove breakpoint, the debugger must be connected and the target must be halted.");
|
||||
}
|
||||
|
||||
u32 addr = parse_address_spec(args.unnamed.at(0));
|
||||
m_debugger.remove_addr_breakpoint(addr);
|
||||
|
||||
return get_none();
|
||||
}
|
||||
Reference in New Issue
Block a user