Repository: SerenityOS/serenity
File: Userland/Applications/Debugger/main.cpp (C++)

Code before:
static void handle_sigint(int)
{
    outln("Debugger: SIGINT");

    // The destructor of DebugSession takes care of detaching
    g_debug_session = nullptr;
}

static void handle_print_registers(PtraceRegisters const& regs)
{
#if ARCH(X86_64)
    outln("rax={:p} rbx={:p} rcx={:p} rdx={:p}", regs.rax, regs.rbx, regs.rcx, regs.rdx);
    outln("rsp={:p} rbp={:p} rsi={:p} rdi={:p}", regs.rsp, regs.rbp, regs.rsi, regs.rdi);
    outln("r8 ={:p} r9 ={:p} r10={:p} r11={:p}", regs.r8, regs.r9, regs.r10, regs.r11);
    outln("r12={:p} r13={:p} r14={:p} r15={:p}", regs.r12, regs.r13, regs.r14, regs.r15);
    outln("rip={:p} rflags={:p}", regs.rip, regs.rflags);
#elif ARCH(AARCH64)
    outln("x0 ={:p} x1 ={:p} x2 ={:p} x3 ={:p}", regs.x[0], regs.x[1], regs.x[2], regs.x[3]);
    outln("x4 ={:p} x5 ={:p} x6 ={:p} x7 ={:p}", regs.x[4], regs.x[5], regs.x[6], regs.x[7]);
    outln("x8 ={:p} x9 ={:p} x10={:p} x11={:p}", regs.x[8], regs.x[9], regs.x[10], regs.x[11]);
    outln("x12={:p} x13={:p} x14={:p} x15={:p}", regs.x[12], regs.x[13], regs.x[14], regs.x[15]);
    outln("x16={:p} x17={:p} x18={:p} x19={:p}", regs.x[16], regs.x[17], regs.x[18], regs.x[19]);
    outln("x20={:p} x21={:p} x22={:p} x23={:p}", regs.x[20], regs.x[21], regs.x[22], regs.x[23]);
    outln("x24={:p} x25={:p} x26={:p} x27={:p}", regs.x[24], regs.x[25], regs.x[26], regs.x[27]);
    outln("x28={:p} fp ={:p} lr ={:p} pstate={:p}", regs.x[28], regs.x[29], regs.x[30], regs.spsr_el1);
    outln("sp ={:p} pc ={:p}", regs.sp, regs.pc);
#elif ARCH(RISCV64)
    outln("Program counter pc={:p}", regs.pc);
    outln("ra={:p} sp={:p} gp={:p} tp={:p} fp={:p}", regs.x[0], regs.x[1], regs.x[2], regs.x[3], regs.x[7]);
    outln("a0={:p} a1={:p} a2={:p} a3={:p} a4={:p} a5={:p} a6={:p} a7={:p}", regs.x[9], regs.x[10], regs.x[11], regs.x[12], regs.x[13], regs.x[14], regs.x[15], regs.x[16]);
    outln("t0={:p} t1={:p} t2={:p} t3={:p} t4={:p} t5={:p} t6={:p}", regs.x[4], regs.x[5], regs.x[6], regs.x[27], regs.x[28], regs.x[29], regs.x[30]);
    outln("s1={:p} s2={:p} s3={:p} s4={:p} s5={:p} s6={:p} s7={:p} s8={:p} s9={:p} s10={:p} s11={:p}", regs.x[8], regs.x[17], regs.x[18], regs.x[19], regs.x[20], regs.x[21], regs.x[22], regs.x[23], regs.x[24], regs.x[25], regs.x[26]);
#else
#    error Unknown architecture
#endif
}

static bool handle_disassemble_command(ByteString const& command, FlatPtr first_instruction)
{
    auto parts = command.split(' ');
    size_t number_of_instructions_to_disassemble = 5;
    if (parts.size() == 2) {
        auto number = parts[1].to_number<unsigned>();
        if (!number.has_value())
            return false;
        number_of_instructions_to_disassemble = number.value();
    }

    // FIXME: Instead of using a fixed "dump_size",
    //        we can feed instructions to the disassembler one by one
    constexpr size_t dump_size = 0x100;

Review comment (bug):
I would print the PC, SP, and PSTATE in the same format as here:
https://github.com/SerenityOS/serenity/blob/b7ce8ab808b67da3cf5bcc3ca0ce69a05900d015/Userland/Applications/CrashReporter/main.cpp#L134-L135
(similar to RISC-V a couple of lines down, just also with SP and PSTATE since RISC-V's SP is a GPR and RISC-V doesn't have condition codes)

I'm not sure if you should also call x29 and x30 fp and lr.

Code after:
static void handle_sigint(int)
{
    outln("Debugger: SIGINT");

    // The destructor of DebugSession takes care of detaching
    g_debug_session = nullptr;
}

static void handle_print_registers(PtraceRegisters const& regs)
{
#if ARCH(X86_64)
    outln("rax={:p} rbx={:p} rcx={:p} rdx={:p}", regs.rax, regs.rbx, regs.rcx, regs.rdx);
    outln("rsp={:p} rbp={:p} rsi={:p} rdi={:p}", regs.rsp, regs.rbp, regs.rsi, regs.rdi);
    outln(" r8={:p}  r9={:p} r10={:p} r11={:p}", regs.r8, regs.r9, regs.r10, regs.r11);
    outln("r12={:p} r13={:p} r14={:p} r15={:p}", regs.r12, regs.r13, regs.r14, regs.r15);
    outln("rip={:p} rflags={:p}", regs.rip, regs.rflags);
#elif ARCH(AARCH64)
    outln("Stack pointer   sp={:p}", regs.sp);
    outln("Program counter pc={:p}", regs.pc);
    outln("Process state   pstate={:p}", regs.spsr_el1);
    outln(" x0={:p}  x1={:p}  x2={:p}  x3={:p}  x4={:p}", regs.x[0], regs.x[1], regs.x[2], regs.x[3], regs.x[4]);
    outln(" x5={:p}  x6={:p}  x7={:p}  x8={:p}  x9={:p}", regs.x[5], regs.x[6], regs.x[7], regs.x[8], regs.x[9]);
    outln("x10={:p} x11={:p} x12={:p} x13={:p} x14={:p}", regs.x[10], regs.x[11], regs.x[12], regs.x[13], regs.x[14]);
    outln("x15={:p} x16={:p} x17={:p} x18={:p} x19={:p}", regs.x[15], regs.x[16], regs.x[17], regs.x[18], regs.x[19]);
    outln("x20={:p} x21={:p} x22={:p} x23={:p} x24={:p}", regs.x[20], regs.x[21], regs.x[22], regs.x[23], regs.x[24]);
    outln("x25={:p} x26={:p} x27={:p} x28={:p} x29={:p}", regs.x[25], regs.x[26], regs.x[27], regs.x[28], regs.x[29]);
    outln("x30={:p}", regs.x[30]);
#elif ARCH(RISCV64)
    outln("Program counter pc={:p}", regs.pc);
    outln("ra={:p} sp={:p} gp={:p} tp={:p} fp={:p}", regs.x[0], regs.x[1], regs.x[2], regs.x[3], regs.x[7]);
    outln("a0={:p} a1={:p} a2={:p} a3={:p} a4={:p} a5={:p} a6={:p} a7={:p}", regs.x[9], regs.x[10], regs.x[11], regs.x[12], regs.x[13], regs.x[14], regs.x[15], regs.x[16]);
    outln("t0={:p} t1={:p} t2={:p} t3={:p} t4={:p} t5={:p} t6={:p}", regs.x[4], regs.x[5], regs.x[6], regs.x[27], regs.x[28], regs.x[29], regs.x[30]);
    outln("s1={:p} s2={:p} s3={:p} s4={:p} s5={:p} s6={:p} s7={:p} s8={:p} s9={:p} s10={:p} s11={:p}", regs.x[8], regs.x[17], regs.x[18], regs.x[19], regs.x[20], regs.x[21], regs.x[22], regs.x[23], regs.x[24], regs.x[25], regs.x[26]);
#else
#    error Unknown architecture
#endif
}

static bool handle_disassemble_command(ByteString const& command, FlatPtr first_instruction)
{
    auto parts = command.split(' ');
    size_t number_of_instructions_to_disassemble = 5;
    if (parts.size() == 2) {
        auto number = parts[1].to_number<unsigned>();
        if (!number.has_value())
            return false;
        number_of_instructions_to_disassemble = number.value();
    }

    // FIXME: Instead of using a fixed "dump_size",
    //        we can feed instructions to the disassembler one by one