jingoro2112 / wrench

practical embedded script interpreter
MIT License
99 stars 10 forks source link

Can't debug if starting line is a function call #37

Closed darksotmoon closed 2 weeks ago

darksotmoon commented 3 weeks ago

Yes, debugging might be work in progress, but it seems mostly working.

Works

function test_fn()
{
  print( "In function\n" );
}
print("start");
test_fn();
print("end");

Does not work

function test_fn()
{
  print( "In function\n" );
}
// print("start");
test_fn();
print("end");

If wr_callFunction the error is WR_ERR_cannot_call_function_context_yielded but I am unsure why the byte code has a yield command without disassembling it.

jingoro2112 commented 3 weeks ago

broke is broke :)

I am working on it on time available, there are a couple of really bad bugs with debug/stepping right now. I folded the debug functionality into yield, which didn't exist when first laid down the debugger, and there is some pain there. I will try to release a working debugger ASAP, even if it's not fully functional, it should at least do the basic stepping/variable display.

On Tue, Jun 25, 2024 at 11:21 AM darksotmoon @.***> wrote:

Yes, debugging might be work in progress, but it seems mostly working.

Works

function test_fn() { print( "In function\n" ); } print("start"); test_fn(); print("end");

Does not work

function test_fn() { print( "In function\n" ); } // print("start"); test_fn(); print("end");

If wr_callFunction the error is WR_ERR_cannot_call_function_context_yielded but I am unsure why the byte code has a yield command without disassembling it.

— Reply to this email directly, view it on GitHub https://github.com/jingoro2112/wrench/issues/37, or unsubscribe https://github.com/notifications/unsubscribe-auth/AALIKA7PDYB6OY2MY6KHOQDZJGDHJAVCNFSM6AAAAABJ4CVC6GVHI2DSMVQWIX3LMV43ASLTON2WKOZSGM3TEOJXGIYTEMI . You are receiving this because you are subscribed to this thread.Message ID: @.***>

jingoro2112 commented 2 weeks ago

I think you will find that these cases both now work properly, with step into and over working :)

I am continuing to refine the debugger, its still very much alpha but at least works!

darksotmoon commented 2 weeks ago

I believe that my error was from my own memory corrupt when capturing the printf output. Apologies about that.

But I went ahead and made a disassembler and the output is better with your changes. Before the changes CallFunctionByIndex would have two junk opcodes following it.

function test_fn()
{
  print("In function\n");
}
// print("start");
test_fn();
print("end");
; disassembly

; size 0x6F 111

; hash 'print' = 0x16378a88

; header
0x0000 0x00 number globals = 0
0x0001 0x01 number units = 2
0x0002 0x03 compiler flags = 0x03
0x0003 0x0000 unit namespace offset
0x0005 0x004C unit function offset
0x0007 0x745399A2 unit hash
0x000B 0x00 unit args
0x000C 0x00 frame space
0x000D 0x01 frame adjust
0x000E 0xFD468B0E source hash
0x0012 0x0017 symbol size = m_symbolBlockLen 23
0x0014 0x02 unit count = 2
0x0016 unit 0
0x0016 0x00 locals = 0
0x0017 0x00 args = 0
0x0018 0x3A 0x3A 0x67 0x6C 0x6F 0x62 0x61 0x6C 0x00 '::global' hash 0x46A75FDE
0x0021 unit 1
0x0021 0x00 locals = 0
0x0022 0x00 args = 0
0x0023 0x74 0x65 0x73 0x74 0x5F 0x66 0x6E 0x00 'test_fn' hash 0x745399A2
0x002B end of header

0x002B unit 0 ::global
; test_fn();
0x002B 0xF5 DebugInfo 4006 line number 6
0x002E 0xF5 DebugInfo 0001 WRD_FunctionCall
0x0031 0x07 CallFunctionByIndex args 0 index 0 pop 3
; print("end");
0x0037 0xF5 DebugInfo 4007 line number 7
0x003A 0x04 LiteralString size 3 'end'
0x0040 0xF5 DebugInfo 0E00 WRD_FunctionCall
0x0043 0x06 CallFunctionByHashAndPop args 0x01 hash 0x16378A88
0x0049 0x02 LiteralZero 
0x004A 0xEF GlobalStop 
0x004B 0x14 Stop 

0x004C unit 1 test_fn
;   print("In function\n");
0x004C 0xF5 DebugInfo 4003 line number 3
0x004F 0x04 LiteralString size 12 'In function\n'
0x005E 0xF5 DebugInfo 0E00 WRD_FunctionCall
0x0061 0x06 CallFunctionByHashAndPop args 0x01 hash 0x16378A88
; }
0x0067 0xF5 DebugInfo 4004 line number 4
0x006A 0x12 ReturnZero 

0x006B 0xE704BBC2 final hash
jingoro2112 commented 2 weeks ago

you have a disassembler? I've almost written one a dozen times but never found the time. If you want me to add it to the repo send it on over! I'll give you full credit though I'll probably modify it :)

On Sun, Jun 30, 2024 at 10:24 AM darksotmoon @.***> wrote:

I believe that my error was from my own memory corrupt when capturing the printf output. Apologies about that.

But I went ahead and made a disassembler and the output is better with your changes. Before the changes CallFunctionByIndex would have two junk opcodes following it.

function test_fn() { print("In function\n"); }// print("start");test_fn();print("end");

; disassembly ; size 0x6F 111 ; hash 'print' = 0x16378a88 ; header0x0000 0x00 number globals = 00x0001 0x01 number units = 20x0002 0x03 compiler flags = 0x030x0003 0x0000 unit namespace offset0x0005 0x004C unit function offset0x0007 0x745399A2 unit hash0x000B 0x00 unit args0x000C 0x00 frame space0x000D 0x01 frame adjust0x000E 0xFD468B0E source hash0x0012 0x0017 symbol size = m_symbolBlockLen 230x0014 0x02 unit count = 20x0016 unit 00x0016 0x00 locals = 00x0017 0x00 args = 00x0018 0x3A 0x3A 0x67 0x6C 0x6F 0x62 0x61 0x6C 0x00 '::global' hash 0x46A75FDE0x0021 unit 10x0021 0x00 locals = 00x0022 0x00 args = 00x0023 0x74 0x65 0x73 0x74 0x5F 0x66 0x6E 0x00 'test_fn' hash 0x745399A20x002B end of header 0x002B unit 0 ::global; test_fn();0x002B 0xF5 DebugInfo 4006 line number 60x002E 0xF5 DebugInfo 0001 WRD_FunctionCall0x0031 0x07 CallFunctionByIndex args 0 index 0 pop 3; print("end");0x0037 0xF5 DebugInfo 4007 line number 70x003A 0x04 LiteralString size 3 'end'0x0040 0xF5 DebugInfo 0E00 WRD_FunctionCall0x0043 0x06 CallFunctionByHashAndPop args 0x01 hash 0x16378A880x0049 0x02 LiteralZero 0x004A 0xEF GlobalStop 0x004B 0x14 Stop 0x004C unit 1 test_fn; print("In function\n");0x004C 0xF5 DebugInfo 4003 line number 30x004F 0x04 LiteralString size 12 'In function\n'0x005E 0xF5 DebugInfo 0E00 WRD_FunctionCall0x0061 0x06 CallFunctionByHashAndPop args 0x01 hash 0x16378A88; }0x0067 0xF5 DebugInfo 4004 line number 40x006A 0x12 ReturnZero 0x006B 0xE704BBC2 final hash

— Reply to this email directly, view it on GitHub https://github.com/jingoro2112/wrench/issues/37#issuecomment-2198579945, or unsubscribe https://github.com/notifications/unsubscribe-auth/AALIKA6NVUZ4WB7UQCF7HN3ZKAIKNAVCNFSM6AAAAABJ4CVC6GVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCOJYGU3TSOJUGU . You are receiving this because you modified the open/close state.Message ID: @.***>

darksotmoon commented 2 weeks ago

It's a quick hack that supports only the opcodes that I used in the sample source. And I never bothered to learn the underlying method of the VM. And there is the ugly get_source_line, and mostly but not quite C.

#include "wrench.h"
#include <vector>

////////////////////////////////////////////////////////////////////////////////

#define MAX_PAYLOAD_BYTES 4

typedef struct {
    uint8_t opcode;
    int payload[MAX_PAYLOAD_BYTES];
} opcode_t;

typedef enum { PL_NONE, PL_ARG, PL_INDEX, PL_HASH, PL_POP } payload_t;

static const opcode_t opcode_s[] =
{
    { O_CallFunctionByHash, { PL_ARG, PL_HASH }},
    { O_CallFunctionByHashAndPop, { PL_ARG, PL_HASH }},
    { O_CallFunctionByIndex, { PL_ARG, PL_INDEX, PL_POP }},
    { O_PushIndexFunctionReturnValue, { PL_INDEX }},
};
static const int opcode_count = sizeof(opcode_s) / sizeof(opcode_t);

////////////////////////////////////////////////////////////////////////////////

static int offset(uint8_t * bytecode, uint8_t * code)
{
    return code - bytecode;
}

static const char * get_source_line(const char * source, int line)
{
    int l = 0;
    const char * r = source;
    const char * st = source;
    while((l < line) && *r)
    {
        st = r;
        if(*st == '\r')
              st++;
        while(*r)
        {
            if(*r == '\n')
            {
                l++;
                r++;
                break;
            }
             r++;
        }
    }
    return st;
}

void wr_disassemble(unsigned char * bytecode, int bytecode_len, const char * source, const char * filename)
{
    FILE * out = fopen(filename, "w+");
    if(out != NULL)
    {
        uint8_t * code = bytecode;
        std::vector<std::string> unit_names;

        fprintf(out, "; disassembly\n");
        fprintf(out, "\n");
        fprintf(out, "; size 0x%X %d\n", bytecode_len, bytecode_len);
        fprintf(out, "\n");
        fprintf(out, "; hash 'print' = 0x%.08x\n", wr_hashStr("print"));
        fprintf(out, "\n");
        fprintf(out, "; header\n");

        uint8_t num_globals = *code;
        fprintf(out, "0x%.04X 0x%.02X number globals = %d\n", offset(bytecode, code), num_globals, num_globals);
        code++;

        uint8_t num_units = *code + 1;
        fprintf(out, "0x%.04X 0x%.02X number units = %d\n", offset(bytecode, code), *code, num_units);
        code++;

        uint8_t compile_flags = *code;
        fprintf(out, "0x%.04X 0x%.02X compiler flags = 0x%.02X\n", offset(bytecode, code), compile_flags, compile_flags);
        code++;

        for(unsigned int u=1UL; u<num_units; u++)
        {
            fprintf(out, "0x%.04X 0x%.04X unit namespace offset\n", offset(bytecode, code), *(uint16_t *)code);
            code += sizeof(uint16_t);

            fprintf(out, "0x%.04X 0x%.04X unit function offset\n", offset(bytecode, code), *(uint16_t *)code);
            code += sizeof(uint16_t);

            uint32_t hash = *(uint32_t *)code;
            fprintf(out, "0x%.04X 0x%.08X unit hash\n", offset(bytecode, code), hash);
            code += sizeof(uint32_t);

            fprintf(out, "0x%.04X 0x%.02X unit args\n", offset(bytecode, code), *code);
            code += sizeof(uint8_t);

            fprintf(out, "0x%.04X 0x%.02X frame space\n", offset(bytecode, code), *code);
            code += sizeof(uint8_t);

            fprintf(out, "0x%.04X 0x%.02X frame adjust\n", offset(bytecode, code), *code);
            code += sizeof(uint8_t);
        }

        if(compile_flags & WR_INCLUDE_GLOBALS)
        {
            for(unsigned int i=0; i<num_globals; i++)
            {
                uint32_t hash = *(uint32_t *)code;
                fprintf(out, "0x%.04X 0x%.08X global %d hash\n", offset(bytecode, code), hash, i);
                code += sizeof(uint32_t);
            }
        }

        if(compile_flags & WR_EMBED_DEBUG_CODE)
        {
            uint32_t hash = *(uint32_t *)code;
            fprintf(out, "0x%.04X 0x%.08X source hash\n", offset(bytecode, code), hash);
            code += sizeof(uint32_t);

            uint16_t symbol_size = *(uint16_t *)code;
            fprintf(out, "0x%.04X 0x%.04X symbol size = m_symbolBlockLen %d\n", offset(bytecode, code), symbol_size, symbol_size);
            code += sizeof(uint16_t);

            uint8_t unit = *(uint16_t *)code;
            fprintf(out, "0x%.04X 0x%.02X unit count = %d\n", offset(bytecode, code), unit, unit);
            code += sizeof(uint16_t);

            for(unsigned int u=0; u<num_units; u++)
            {
                fprintf(out, "0x%.04X unit %d\n", offset(bytecode, code), u);

                uint8_t locals = *code;
                fprintf(out, "0x%.04X 0x%.02X locals = %d\n", offset(bytecode, code), locals, locals);
                code++;

                uint8_t args = *code;
                fprintf(out, "0x%.04X 0x%.02X args = %d\n", offset(bytecode, code), args, args);
                code++;

                fprintf(out, "0x%.04X ", offset(bytecode, code));
                uint32_t hash = wr_hashStr((char *)code);
                unit_names.push_back((char *)code);
                int len = 0;
                for(uint8_t * c = code; *c; c++)
                    len++;
                for(int i=0; i<= len; i++)
                    fprintf(out, "0x%.02X ", code[i]);
                fprintf(out, "'");
                for(int i=0; i< len; i++)
                    fprintf(out, "%c", code[i]);
                fprintf(out, "' hash 0x%.08X\n", hash);
                code += len + 1; // for null

                for(int i=0; i<locals; i++)
                {
                    fprintf(out, "0x%.04X ", offset(bytecode, code));
                    uint32_t hash = wr_hashStr((char *)code);
                    int len = 0;
                    for(uint8_t * c = code; *c; c++)
                        len++;
                    for(int i=0; i<= len; i++)
                        fprintf(out, "0x%.02X ", code[i]);
                    fprintf(out, "'");
                    for(int i=0; i< len; i++)
                        fprintf(out, "%c", code[i]);
                    fprintf(out, "' hash 0x%.08X\n", hash);
                    code += len + 1; // for null
                }
            }
        }

        fprintf(out, "0x%.04X end of header\n", offset(bytecode, code));

        for(unsigned int u=0; u<num_units; u++)
        {
            fprintf(out, "\n");
            fprintf(out, "0x%.04X unit %d %s\n", offset(bytecode, code), u, u < unit_names.size() ? unit_names[u].c_str() : "?");

            bool finished = false;
            while(!finished)
            {
                uint8_t * start = code;
                uint8_t opcode = *code;
                code++;

                char desc[64] = { 0 };
                if(opcode == O_DebugInfo)
                {
                    uint16_t val = (code[1] << 8) + code[0];
                    code += 2;
                    sprintf(desc, "%.04X", val);
                    if((val & WRD_TypeMask) == WRD_FunctionCall)
                    {
                        strcat(desc, " WRD_FunctionCall");
                        if((val & WRD_TypeMask) == WRD_ExternalFunction)
                            strcat(desc, " WRD_ExternalFunction");
                    }
                    if((val & WRD_TypeMask) == WRD_LineNumber)
                    {
                        int line = val & WRD_PayloadMask;
                        const char * src = get_source_line(source, line);
                        fprintf(out, "; ");
                        while(*src && (*src != '\r') && (*src != '\n'))
                            fputc(*src++, out);
                        fprintf(out, "\n");
                        strcat(desc, " line number ");
                        char no[32] = { 0 };
                        sprintf(no, "%d", line);
                        strcat(desc, no);
                    }
                }
                else if(opcode == O_LiteralString)
                {
                    uint16_t val = (code[1] << 8) + code[0];
                    code += 2;
                    sprintf(desc, "size %d '", val);
                    for(int j=0; j<val; j++, code++)
                    {
                        char c = code[0];
                        if(c == '\n')
                            strcat(desc, "\\n");
                        else
                            desc[strlen(desc)] = c;
                    }
                    strcat(desc, "'");
                }
                else
                {
                    for(int oi=0; oi<opcode_count; oi++)
                    {
                        if(opcode_s[oi].opcode == opcode)
                        {
                            for(int p=0; p<MAX_PAYLOAD_BYTES; p++)
                            {
                                switch(opcode_s[oi].payload[p])
                                {
                                    case PL_NONE:
                                        break;
                                    case PL_ARG:
                                        {
                                            char buf[32];
                                            sprintf(buf, " args 0x%.02X", code[0]);
                                            strcat(desc, buf);
                                            code += sizeof(uint8_t);
                                        }
                                        break;
                                    case PL_HASH:
                                        {
                                            char buf[32];
                                            sprintf(buf, " hash 0x%.08X", *(uint32_t *)code);
                                            strcat(desc, buf);
                                            code += sizeof(uint32_t);
                                        }
                                        break;
                                    case PL_INDEX:
                                        {
                                            char buf[32];
                                            sprintf(buf, " index 0x%.02X", code[0]);
                                            strcat(desc, buf);
                                            code += sizeof(uint8_t);
                                        }
                                        break;
                                    case PL_POP:
                                        {
                                            char buf[32];
                                            sprintf(buf, " pop 0x%.02X", code[0]);
                                            strcat(desc, buf);
                                            code += code[0];
                                        }
                                        break;
                                }
                            }
                            break;
                        }
                    }
        }

                if((opcode == O_Return) || (opcode == O_ReturnZero) || (opcode == O_Stop))
                    finished = true;

                fprintf(out, "0x%.04X 0x%.02X %s%s\n", offset(bytecode, start), opcode, (opcode <= O_DebugInfo) ? c_opcodeName[opcode] : "!!!", desc);
            }
        }

        fprintf(out, "\n");
        fprintf(out, "0x%.04X 0x%.08X final hash\n", offset(bytecode, code), *(uint32_t *)code);

        fclose(out);

    }

    #if 0
    std::string dump_name = filename;
    dump_name += ".bin";
    FILE * dump = fopen(dump_name.c_str(), "w+");
    if(dump != NULL)
    {
        uint8_t * code = bytecode;
        fwrite(bytecode, bytecode_len, 1, dump);
        fclose(dump);
    }
    #endif
}