From f1fb58ead3e1d05dadb63ae9d9d56d45b6f40f61 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 16 Jun 2024 00:05:21 +0800 Subject: [PATCH 1/9] :bug: fix render bug in gc info --- src/nasal_dbg.cpp | 2 +- src/nasal_dbg.h | 2 +- src/nasal_gc.cpp | 22 +++--- src/nasal_opcode.cpp | 18 +++-- src/nasal_opcode.h | 180 +++++++++++++++++++++---------------------- src/nasal_type.cpp | 16 +++- src/nasal_type.h | 6 +- src/nasal_vm.cpp | 105 +++++++++++++++++-------- src/nasal_vm.h | 6 +- 9 files changed, 210 insertions(+), 147 deletions(-) diff --git a/src/nasal_dbg.cpp b/src/nasal_dbg.cpp index 4f6c4cb..edc99b2 100644 --- a/src/nasal_dbg.cpp +++ b/src/nasal_dbg.cpp @@ -47,7 +47,7 @@ void operand_line_counter::dump_operand_count() const { break; } std::clog << " "; - std::clog << operand_name_table.at(static_cast(i.first)); + std::clog << operand_name_table.at(static_cast(i.first)); std::clog << " : " << i.second << " (" << rate << "%)\n"; } std::clog << " total : " << total << '\n'; diff --git a/src/nasal_dbg.h b/src/nasal_dbg.h index c554941..02f0b91 100644 --- a/src/nasal_dbg.h +++ b/src/nasal_dbg.h @@ -15,7 +15,7 @@ namespace nasal { // and show them before each line of the source file class operand_line_counter { private: - static const usize operand_size = op_code_type::op_ret + 1; + static const usize operand_size = opcode_type::op_ret + 1; u64 operand_counter[operand_size]; std::vector file_name_list; std::vector> file_line_counter; diff --git a/src/nasal_gc.cpp b/src/nasal_gc.cpp index 6f2a999..6192849 100644 --- a/src/nasal_gc.cpp +++ b/src/nasal_gc.cpp @@ -315,16 +315,20 @@ void gc::info() const { indent_string += "─"; } const auto first_line = "╭" + indent_string + "┬" + - indent_string + "─" + - indent_string + "─" + + indent_string + "┬" + + indent_string + "┬" + indent_string + "╮"; + const auto second_line = "├" + indent_string + "┼" + + indent_string + "┼" + + indent_string + "┼" + + indent_string + "┤"; const auto mid_line = "├" + indent_string + "┼" + - indent_string + "┼" + - indent_string + "┼" + + indent_string + "┴" + + indent_string + "┴" + indent_string + "┤"; const auto another_mid_line = "├" + indent_string + "┼" + - indent_string + "┴" + - indent_string + "┴" + + indent_string + "─" + + indent_string + "─" + indent_string + "┤"; const auto last_line = "╰" + indent_string + "┴" + indent_string + "─" + @@ -336,7 +340,7 @@ void gc::info() const { std::clog << " │ " << left << setw(indent) << setfill(' ') << "gc count"; std::clog << " │ " << left << setw(indent) << setfill(' ') << "alloc count"; std::clog << " │ " << left << setw(indent) << setfill(' ') << "memory size"; - std::clog << " │\n" << mid_line << "\n"; + std::clog << " │\n" << second_line << "\n"; double total = 0; for(u8 i = 0; i=0; i -= 8) { - out << hex << setw(2) << setfill('0') << ((num>>i)&0xff) << dec << " "; + auto this_byte = ((num>>i)&0xff); + out << hex << setw(2) << setfill('0') << this_byte << dec << " "; } // dump operand name - out << " " << operand_name_table.at(static_cast(op)) << " "; + out << " " << operand_name_table.at(static_cast(op)) << " "; switch(op) { case op_addeq: @@ -56,7 +57,7 @@ void codestream::dump(std::ostream& out) const { break; case op_lnkeqc: out << hex << "0x" << num << dec; - out << " (" << util::rawstr(const_string[num], 16) << ")"; + out << " (\"" << util::rawstr(const_string[num], 32) << "\")"; break; case op_addecp: case op_subecp: @@ -67,7 +68,7 @@ void codestream::dump(std::ostream& out) const { break; case op_lnkecp: out << hex << "0x" << num << dec; - out << " (" << util::rawstr(const_string[num], 16) << ") sp-1"; + out << " (\"" << util::rawstr(const_string[num], 32) << "\") sp-1"; break; case op_addc: case op_subc: @@ -100,8 +101,11 @@ void codestream::dump(std::ostream& out) const { case op_loadl: out << hex << "0x" << num << dec; break; case op_callb: - out << hex << "0x" << num << dec << " [" - << natives[num].name << "]"; break; + out << hex << "0x" << num << dec; + out << " [" << natives[num].name << "@0x"; + out << hex << reinterpret_cast(natives[num].func) << dec; + out << "]"; + break; case op_upval: case op_mupval: case op_loadu: @@ -116,7 +120,7 @@ void codestream::dump(std::ostream& out) const { case op_deft: case op_dyn: out << hex << "0x" << num << dec; - out << " (" << util::rawstr(const_string[num], 16) << ")"; + out << " (\"" << util::rawstr(const_string[num], 32) << "\")"; break; default: if (files) { diff --git a/src/nasal_opcode.h b/src/nasal_opcode.h index e64ade5..0b9fa1a 100644 --- a/src/nasal_opcode.h +++ b/src/nasal_opcode.h @@ -7,7 +7,7 @@ namespace nasal { -enum op_code_type: u8 { +enum opcode_type: u8 { op_exit, // stop the virtual machine op_repl, // in repl mode: print value on stack top op_intl, // local scope size @@ -98,95 +98,95 @@ enum op_code_type: u8 { op_ret // return }; -static std::unordered_map operand_name_table = { - {op_code_type::op_exit, "exit "}, - {op_code_type::op_repl, "repl "}, - {op_code_type::op_intl, "intl "}, - {op_code_type::op_loadg, "loadg "}, - {op_code_type::op_loadl, "loadl "}, - {op_code_type::op_loadu, "loadu "}, - {op_code_type::op_dup, "dup "}, - {op_code_type::op_pnum, "pnum "}, - {op_code_type::op_pnil, "pnil "}, - {op_code_type::op_pstr, "pstr "}, - {op_code_type::op_newv, "newv "}, - {op_code_type::op_newh, "newh "}, - {op_code_type::op_newf, "newf "}, - {op_code_type::op_happ, "happ "}, - {op_code_type::op_para, "para "}, - {op_code_type::op_deft, "def "}, - {op_code_type::op_dyn, "dyn "}, - {op_code_type::op_lnot, "lnot "}, - {op_code_type::op_usub, "usub "}, - {op_code_type::op_bnot, "bitnot"}, - {op_code_type::op_btor, "bitor "}, - {op_code_type::op_btxor, "bitxor"}, - {op_code_type::op_btand, "bitand"}, - {op_code_type::op_add, "add "}, - {op_code_type::op_sub, "sub "}, - {op_code_type::op_mul, "mult "}, - {op_code_type::op_div, "div "}, - {op_code_type::op_lnk, "lnk "}, - {op_code_type::op_addc, "addc "}, - {op_code_type::op_subc, "subc "}, - {op_code_type::op_mulc, "multc "}, - {op_code_type::op_divc, "divc "}, - {op_code_type::op_lnkc, "lnkc "}, - {op_code_type::op_addeq, "addeq "}, - {op_code_type::op_subeq, "subeq "}, - {op_code_type::op_muleq, "muleq "}, - {op_code_type::op_diveq, "diveq "}, - {op_code_type::op_lnkeq, "lnkeq "}, - {op_code_type::op_btandeq, "bandeq"}, - {op_code_type::op_btoreq, "boreq "}, - {op_code_type::op_btxoreq, "bxoreq"}, - {op_code_type::op_addeqc, "addeqc"}, - {op_code_type::op_subeqc, "subeqc"}, - {op_code_type::op_muleqc, "muleqc"}, - {op_code_type::op_diveqc, "diveqc"}, - {op_code_type::op_lnkeqc, "lnkeqc"}, - {op_code_type::op_addecp, "addecp"}, - {op_code_type::op_subecp, "subecp"}, - {op_code_type::op_mulecp, "mulecp"}, - {op_code_type::op_divecp, "divecp"}, - {op_code_type::op_lnkecp, "lnkecp"}, - {op_code_type::op_meq, "meq "}, - {op_code_type::op_eq, "eq "}, - {op_code_type::op_neq, "neq "}, - {op_code_type::op_less, "less "}, - {op_code_type::op_leq, "leq "}, - {op_code_type::op_grt, "grt "}, - {op_code_type::op_geq, "geq "}, - {op_code_type::op_lessc, "lessc "}, - {op_code_type::op_leqc, "leqc "}, - {op_code_type::op_grtc, "grtc "}, - {op_code_type::op_geqc, "geqc "}, - {op_code_type::op_pop, "pop "}, - {op_code_type::op_jmp, "jmp "}, - {op_code_type::op_jt, "jt "}, - {op_code_type::op_jf, "jf "}, - {op_code_type::op_cnt, "cnt "}, - {op_code_type::op_findex, "findx "}, - {op_code_type::op_feach, "feach "}, - {op_code_type::op_callg, "callg "}, - {op_code_type::op_calll, "calll "}, - {op_code_type::op_upval, "upval "}, - {op_code_type::op_callv, "callv "}, - {op_code_type::op_callvi, "callvi"}, - {op_code_type::op_callh, "callh "}, - {op_code_type::op_callfv, "callfv"}, - {op_code_type::op_callfh, "callfh"}, - {op_code_type::op_callb, "callb "}, - {op_code_type::op_slcbeg, "slcbeg"}, - {op_code_type::op_slcend, "slcend"}, - {op_code_type::op_slc, "slice "}, - {op_code_type::op_slc2, "slice2"}, - {op_code_type::op_mcallg, "mcallg"}, - {op_code_type::op_mcalll, "mcalll"}, - {op_code_type::op_mupval, "mupval"}, - {op_code_type::op_mcallv, "mcallv"}, - {op_code_type::op_mcallh, "mcallh"}, - {op_code_type::op_ret, "ret "} +static std::unordered_map operand_name_table = { + {opcode_type::op_exit, "exit "}, + {opcode_type::op_repl, "repl "}, + {opcode_type::op_intl, "intl "}, + {opcode_type::op_loadg, "loadg "}, + {opcode_type::op_loadl, "loadl "}, + {opcode_type::op_loadu, "loadu "}, + {opcode_type::op_dup, "dup "}, + {opcode_type::op_pnum, "pnum "}, + {opcode_type::op_pnil, "pnil "}, + {opcode_type::op_pstr, "pstr "}, + {opcode_type::op_newv, "newv "}, + {opcode_type::op_newh, "newh "}, + {opcode_type::op_newf, "newf "}, + {opcode_type::op_happ, "happ "}, + {opcode_type::op_para, "para "}, + {opcode_type::op_deft, "def "}, + {opcode_type::op_dyn, "dyn "}, + {opcode_type::op_lnot, "lnot "}, + {opcode_type::op_usub, "usub "}, + {opcode_type::op_bnot, "bitnot"}, + {opcode_type::op_btor, "bitor "}, + {opcode_type::op_btxor, "bitxor"}, + {opcode_type::op_btand, "bitand"}, + {opcode_type::op_add, "add "}, + {opcode_type::op_sub, "sub "}, + {opcode_type::op_mul, "mult "}, + {opcode_type::op_div, "div "}, + {opcode_type::op_lnk, "lnk "}, + {opcode_type::op_addc, "addc "}, + {opcode_type::op_subc, "subc "}, + {opcode_type::op_mulc, "multc "}, + {opcode_type::op_divc, "divc "}, + {opcode_type::op_lnkc, "lnkc "}, + {opcode_type::op_addeq, "addeq "}, + {opcode_type::op_subeq, "subeq "}, + {opcode_type::op_muleq, "muleq "}, + {opcode_type::op_diveq, "diveq "}, + {opcode_type::op_lnkeq, "lnkeq "}, + {opcode_type::op_btandeq, "bandeq"}, + {opcode_type::op_btoreq, "boreq "}, + {opcode_type::op_btxoreq, "bxoreq"}, + {opcode_type::op_addeqc, "addeqc"}, + {opcode_type::op_subeqc, "subeqc"}, + {opcode_type::op_muleqc, "muleqc"}, + {opcode_type::op_diveqc, "diveqc"}, + {opcode_type::op_lnkeqc, "lnkeqc"}, + {opcode_type::op_addecp, "addecp"}, + {opcode_type::op_subecp, "subecp"}, + {opcode_type::op_mulecp, "mulecp"}, + {opcode_type::op_divecp, "divecp"}, + {opcode_type::op_lnkecp, "lnkecp"}, + {opcode_type::op_meq, "meq "}, + {opcode_type::op_eq, "eq "}, + {opcode_type::op_neq, "neq "}, + {opcode_type::op_less, "less "}, + {opcode_type::op_leq, "leq "}, + {opcode_type::op_grt, "grt "}, + {opcode_type::op_geq, "geq "}, + {opcode_type::op_lessc, "lessc "}, + {opcode_type::op_leqc, "leqc "}, + {opcode_type::op_grtc, "grtc "}, + {opcode_type::op_geqc, "geqc "}, + {opcode_type::op_pop, "pop "}, + {opcode_type::op_jmp, "jmp "}, + {opcode_type::op_jt, "jt "}, + {opcode_type::op_jf, "jf "}, + {opcode_type::op_cnt, "cnt "}, + {opcode_type::op_findex, "findx "}, + {opcode_type::op_feach, "feach "}, + {opcode_type::op_callg, "callg "}, + {opcode_type::op_calll, "calll "}, + {opcode_type::op_upval, "upval "}, + {opcode_type::op_callv, "callv "}, + {opcode_type::op_callvi, "callvi"}, + {opcode_type::op_callh, "callh "}, + {opcode_type::op_callfv, "callfv"}, + {opcode_type::op_callfh, "callfh"}, + {opcode_type::op_callb, "callb "}, + {opcode_type::op_slcbeg, "slcbeg"}, + {opcode_type::op_slcend, "slcend"}, + {opcode_type::op_slc, "slice "}, + {opcode_type::op_slc2, "slice2"}, + {opcode_type::op_mcallg, "mcallg"}, + {opcode_type::op_mcalll, "mcalll"}, + {opcode_type::op_mupval, "mupval"}, + {opcode_type::op_mcallv, "mcallv"}, + {opcode_type::op_mcallh, "mcallh"}, + {opcode_type::op_ret, "ret "} }; struct opcode { diff --git a/src/nasal_type.cpp b/src/nasal_type.cpp index 4d6b2a2..93d3485 100644 --- a/src/nasal_type.cpp +++ b/src/nasal_type.cpp @@ -89,12 +89,18 @@ std::ostream& operator<<(std::ostream& out, nas_hash& hash) { out << (hash.elems.size()? "{..}":"{}"); return out; } + + // mark print, to avoid infinite recursion hash.printed = true; + + static const char* sep[] = {", ", "}"}; usize iter = 0, size = hash.elems.size(); out << "{"; for(auto& i : hash.elems) { - out << i.first << ":" << i.second << ",}"[(++iter)==size]; + out << i.first << ": " << i.second << sep[(++iter)==size]; } + + // restore flag hash.printed = false; return out; } @@ -212,12 +218,18 @@ std::ostream& operator<<(std::ostream& out, nas_map& mp) { out << (mp.mapper.size()? "{..}":"{}"); return out; } + + // mark print, to avoid infinite recursion mp.printed = true; + + static const char* sep[] = {", ", "}"}; usize iter = 0, size = mp.mapper.size(); out << "{"; for(auto& i : mp.mapper) { - out << i.first << ":" << *i.second << ",}"[(++iter)==size]; + out << i.first << ": " << *i.second << sep[(++iter)==size]; } + + // restore flag mp.printed = false; return out; } diff --git a/src/nasal_type.h b/src/nasal_type.h index 5a759e5..d8302f7 100644 --- a/src/nasal_type.h +++ b/src/nasal_type.h @@ -131,7 +131,7 @@ struct nas_vec { // mark if this is printed, avoid stack overflow bool printed = false; - usize size() const {return elems.size();} + auto size() const { return elems.size(); } var get_value(const i32); var* get_memory(const i32); }; @@ -142,7 +142,7 @@ struct nas_hash { // mark if this is printed, avoid stack overflow bool printed = false; - usize size() const {return elems.size();} + auto size() const { return elems.size(); } var get_value(const std::string&); var* get_memory(const std::string&); }; @@ -256,9 +256,11 @@ struct nas_map { bool printed = false; std::unordered_map mapper; +public: void clear() { mapper.clear(); } + auto size() const { return mapper.size(); } var get_value(const std::string&); var* get_memory(const std::string&); diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index 0cda417..1d7460e 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -63,12 +63,12 @@ void vm::context_and_global_init() { } } -void vm::hash_value_info(var& val) { +void vm::hash_value_info(var& val, const usize max_show_elems) { std::clog << "{"; usize count = 0; for(const auto& i : val.hash().elems) { ++count; - if (count>3) { + if (count>max_show_elems) { break; } @@ -77,55 +77,92 @@ void vm::hash_value_info(var& val) { std::clog << ", "; } } - if (val.hash().size()>3) { + if (val.hash().size()>max_show_elems) { std::clog << "..."; } std::clog << "}"; } +void vm::namespace_value_info(var& val, const usize max_show_elems) { + std::clog << "{"; + usize count = 0; + for(const auto& i : val.map().mapper) { + ++count; + if (count>max_show_elems) { + break; + } + + std::clog << i.first; + if (count!=val.map().size()) { + std::clog << ", "; + } + } + if (val.map().size()>max_show_elems) { + std::clog << "..."; + } + std::clog << "}"; +} + +void vm::value_name_form(const var& val) { + std::clog << "| "; + switch(val.type) { + case vm_type::vm_none: std::clog << "null "; break; + case vm_type::vm_ret: std::clog << "return "; break; + case vm_type::vm_addr: std::clog << "address "; break; + case vm_type::vm_cnt: std::clog << "counter "; break; + case vm_type::vm_nil: std::clog << "nil "; break; + case vm_type::vm_num: std::clog << "number "; break; + case vm_type::vm_str: std::clog << "string "; break; + case vm_type::vm_func: std::clog << "function "; break; + case vm_type::vm_upval: std::clog << "upvalue "; break; + case vm_type::vm_vec: std::clog << "vector "; break; + case vm_type::vm_hash: std::clog << "hash "; break; + case vm_type::vm_ghost: std::clog << "ghost "; break; + case vm_type::vm_co: std::clog << "coroutine"; break; + case vm_type::vm_map: std::clog << "namespace"; break; + default: std::clog << "error "; break; + } + std::clog << " | "; +} + void vm::value_info(var& val) { const auto p = reinterpret_cast(val.val.gcobj); + + value_name_form(val); + switch(val.type) { - case vm_type::vm_none: std::clog << "| null |"; break; + case vm_type::vm_none: std::clog << "null"; break; case vm_type::vm_ret: - std::clog << "| pc | 0x" << std::hex << val.ret() << std::dec; + std::clog << "0x" << std::hex << val.ret() << std::dec; break; case vm_type::vm_addr: - std::clog << "| addr | 0x"; + std::clog << "0x"; std::clog << std::hex << reinterpret_cast(val.addr()) << std::dec; break; - case vm_type::vm_cnt: std::clog << "| cnt | " << val.cnt(); break; - case vm_type::vm_nil: std::clog << "| nil |"; break; - case vm_type::vm_num: std::clog << "| num | " << val.num(); break; + case vm_type::vm_cnt: std::clog << val.cnt(); break; + case vm_type::vm_nil: std::clog << "nil"; break; + case vm_type::vm_num: std::clog << val.num(); break; case vm_type::vm_str: - std::clog << "| str | \"" << util::rawstr(val.str(), 16) << "\""; - break; - case vm_type::vm_func: - std::clog << "| func | " << val.func(); + std::clog << "\"" << util::rawstr(val.str(), 16) << "\""; break; + case vm_type::vm_func: std::clog << val.func(); break; case vm_type::vm_upval: - std::clog << "| upval| <0x" << std::hex << p << std::dec; - std::clog << "> [" << val.upval().size << " val]"; break; - case vm_type::vm_vec: - std::clog << "| vec | [" << val.vec().size() << " val]"; break; - case vm_type::vm_hash: - std::clog << "| hash | "; - hash_value_info(val); + std::clog << "<0x" << std::hex << p << std::dec; + std::clog << "> [" << val.upval().size << " val]"; break; + case vm_type::vm_vec: + std::clog << "[" << val.vec().size() << " val]"; break; + case vm_type::vm_hash: hash_value_info(val, 4); break; case vm_type::vm_ghost: - std::clog << "| obj | <0x" << std::hex << p << "> " << std::dec; + std::clog << "<0x" << std::hex << p << "> " << std::dec; std::clog << "object:" << val.ghost().type_name; break; case vm_type::vm_co: - std::clog << "| co | coroutine@0x" << std::hex << p << std::dec; - break; - case vm_type::vm_map: - std::clog << "| nmspc| ["; - std::clog << val.map().mapper.size() << " val]"; + std::clog << "coroutine@0x" << std::hex << p << std::dec; break; + case vm_type::vm_map: namespace_value_info(val, 4); break; default: - std::clog << "| err | <0x" << std::hex << p << std::dec; - std::clog << "> unknown object"; + std::clog << "<0x" << std::hex << p << std::dec << "> unknown"; break; } std::clog << "\n"; @@ -247,16 +284,16 @@ void vm::stack_info(const u64 limit = 16) { void vm::register_info() { std::clog << "\nregister (" << (ngc.cort? "coroutine":"main") << ")\n"; std::clog << std::hex - << " [ pc ] | pc | 0x" << ctx.pc << "\n" - << " [ global ] | addr | 0x" + << " [ pc ] | pc | 0x" << ctx.pc << "\n" + << " [ global ] | address | 0x" << reinterpret_cast(global) << "\n" - << " [ local ] | addr | 0x" + << " [ local ] | address | 0x" << reinterpret_cast(ctx.localr) << "\n" - << " [ memr ] | addr | 0x" + << " [ memr ] | address | 0x" << reinterpret_cast(ctx.memr) << "\n" - << " [ canary ] | addr | 0x" + << " [ canary ] | address | 0x" << reinterpret_cast(ctx.canary) << "\n" - << " [ top ] | addr | 0x" + << " [ top ] | address | 0x" << reinterpret_cast(ctx.top) << "\n" << std::dec; std::clog << " [ funcr ] "; value_info(ctx.funcr); diff --git a/src/nasal_vm.h b/src/nasal_vm.h index 89b9cf3..ec00167 100644 --- a/src/nasal_vm.h +++ b/src/nasal_vm.h @@ -65,7 +65,9 @@ protected: protected: /* debug functions */ bool verbose = false; - void hash_value_info(var&); + void hash_value_info(var&, const usize); + void namespace_value_info(var&, const usize); + void value_name_form(const var&); void value_info(var&); void function_detail_info(const nas_func&); void function_call_trace(); @@ -76,6 +78,8 @@ protected: void local_state(); void upvalue_state(); void all_state_detail(); + +protected: std::string report_lack_arguments(u32, const nas_func&) const; std::string report_special_call_lack_arguments(var*, const nas_func&) const; std::string report_key_not_found(const std::string&, const nas_hash&) const; From abb587c62b5cbd338e75adc28b1f99be10666342 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 16 Jun 2024 00:37:56 +0800 Subject: [PATCH 2/9] :memo: update --- src/nasal_vm.cpp | 23 ++++++++++++++++------- src/nasal_vm.h | 1 + 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index 1d7460e..88935e6 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -83,6 +83,17 @@ void vm::hash_value_info(var& val, const usize max_show_elems) { std::clog << "}"; } +void vm::coroutine_value_info(var& val) { + std::clog << "[ "; + switch(val.co().status) { + case nas_co::status::dead: std::clog << "dead"; break; + case nas_co::status::running: std::clog << "running"; break; + case nas_co::status::suspended: std::clog << "suspended"; break; + } + std::clog << " ] @0x"; + std::clog << std::hex << reinterpret_cast(val.val.gcobj) << std::dec; +} + void vm::namespace_value_info(var& val, const usize max_show_elems) { std::clog << "{"; usize count = 0; @@ -157,9 +168,7 @@ void vm::value_info(var& val) { std::clog << "<0x" << std::hex << p << "> " << std::dec; std::clog << "object:" << val.ghost().type_name; break; - case vm_type::vm_co: - std::clog << "coroutine@0x" << std::hex << p << std::dec; - break; + case vm_type::vm_co: coroutine_value_info(val); break; case vm_type::vm_map: namespace_value_info(val, 4); break; default: std::clog << "<0x" << std::hex << p << std::dec << "> unknown"; @@ -383,8 +392,8 @@ std::string vm::report_lack_arguments(u32 argc, const nas_func& func) const { return result + out.str(); } -std::string vm::report_special_call_lack_arguments( - var* local, const nas_func& func) const { +std::string vm::report_special_call_lack_arguments(var* local, + const nas_func& func) const { auto result = std::string("lack argument(s) when calling function:\n func("); std::vector argument_list = {}; argument_list.resize(func.keys.size()); @@ -407,8 +416,8 @@ std::string vm::report_special_call_lack_arguments( return result + out.str(); } -std::string vm::report_key_not_found( - const std::string& not_found, const nas_hash& hash) const { +std::string vm::report_key_not_found(const std::string& not_found, + const nas_hash& hash) const { auto result = "member \"" + not_found + "\" doesn't exist in hash {"; for(const auto& i : hash.elems) { result += i.first + ", "; diff --git a/src/nasal_vm.h b/src/nasal_vm.h index ec00167..7643e70 100644 --- a/src/nasal_vm.h +++ b/src/nasal_vm.h @@ -66,6 +66,7 @@ protected: /* debug functions */ bool verbose = false; void hash_value_info(var&, const usize); + void coroutine_value_info(var&); void namespace_value_info(var&, const usize); void value_name_form(const var&); void value_info(var&); From 1de0874c8d675e7d47d13d509e93afab209f5d80 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 16 Jun 2024 21:57:23 +0800 Subject: [PATCH 3/9] :sparkles: add detailed call trace info --- src/nasal_type.cpp | 2 +- src/nasal_type.h | 2 +- src/nasal_vm.cpp | 198 +++++++++++++++++++++++++++++---------------- src/nasal_vm.h | 6 ++ test/coroutine.nas | 2 +- 5 files changed, 137 insertions(+), 73 deletions(-) diff --git a/src/nasal_type.cpp b/src/nasal_type.cpp index 93d3485..2380ae3 100644 --- a/src/nasal_type.cpp +++ b/src/nasal_type.cpp @@ -348,7 +348,7 @@ var var::addr(var* p) { return {vm_type::vm_addr, p}; } -var* var::addr() { +var* var::addr() const { return val.addr; } diff --git a/src/nasal_type.h b/src/nasal_type.h index d8302f7..f3e45c4 100644 --- a/src/nasal_type.h +++ b/src/nasal_type.h @@ -95,7 +95,7 @@ public: public: // get value - var* addr(); + var* addr() const; u64 ret() const; i64& cnt(); f64 num() const; diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index 88935e6..067a04b 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -63,6 +63,33 @@ void vm::context_and_global_init() { } } +void vm::return_address_info(const var& val) { + std::clog << "0x"; + std::clog << std::hex << val.ret() << std::dec; +} + +void vm::memory_address_info(const var& val) { + std::clog << "0x"; + std::clog << std::hex << reinterpret_cast(val.addr()) << std::dec; +} + +void vm::raw_string_info(var& val) { + std::clog << "\"" << util::rawstr(val.str(), 24) << "\""; +} + +void vm::upvalue_info(var& val) { + std::clog << "[" << val.upval().size << " val] "; + if (val.upval().on_stack) { + std::clog << "offset:0x" << std::hex; + std::clog << reinterpret_cast(val.upval().stack_frame_offset); + std::clog << std::dec; + } +} + +void vm::vector_value_info(var& val) { + std::clog << "[" << val.vec().size() << " val]"; +} + void vm::hash_value_info(var& val, const usize max_show_elems) { std::clog << "{"; usize count = 0; @@ -83,6 +110,11 @@ void vm::hash_value_info(var& val, const usize max_show_elems) { std::clog << "}"; } +void vm::ghost_type_info(var& val) { + std::clog << "" << std::dec; +} + void vm::coroutine_value_info(var& val) { std::clog << "[ "; switch(val.co().status) { @@ -117,68 +149,51 @@ void vm::namespace_value_info(var& val, const usize max_show_elems) { void vm::value_name_form(const var& val) { std::clog << "| "; switch(val.type) { - case vm_type::vm_none: std::clog << "null "; break; - case vm_type::vm_ret: std::clog << "return "; break; - case vm_type::vm_addr: std::clog << "address "; break; - case vm_type::vm_cnt: std::clog << "counter "; break; - case vm_type::vm_nil: std::clog << "nil "; break; - case vm_type::vm_num: std::clog << "number "; break; - case vm_type::vm_str: std::clog << "string "; break; - case vm_type::vm_func: std::clog << "function "; break; - case vm_type::vm_upval: std::clog << "upvalue "; break; - case vm_type::vm_vec: std::clog << "vector "; break; - case vm_type::vm_hash: std::clog << "hash "; break; - case vm_type::vm_ghost: std::clog << "ghost "; break; - case vm_type::vm_co: std::clog << "coroutine"; break; - case vm_type::vm_map: std::clog << "namespace"; break; - default: std::clog << "error "; break; + case vm_type::vm_none: std::clog << "null "; break; + case vm_type::vm_ret: std::clog << "ret "; break; + case vm_type::vm_addr: std::clog << "addr "; break; + case vm_type::vm_cnt: std::clog << "cnt "; break; + case vm_type::vm_nil: std::clog << "nil "; break; + case vm_type::vm_num: std::clog << "num "; break; + case vm_type::vm_str: std::clog << "str "; break; + case vm_type::vm_func: std::clog << "func "; break; + case vm_type::vm_upval: std::clog << "upval "; break; + case vm_type::vm_vec: std::clog << "vec "; break; + case vm_type::vm_hash: std::clog << "hash "; break; + case vm_type::vm_ghost: std::clog << "ghost "; break; + case vm_type::vm_co: std::clog << "co "; break; + case vm_type::vm_map: std::clog << "map "; break; + default: std::clog << "err "; break; } std::clog << " | "; } void vm::value_info(var& val) { - const auto p = reinterpret_cast(val.val.gcobj); - value_name_form(val); switch(val.type) { - case vm_type::vm_none: std::clog << "null"; break; - case vm_type::vm_ret: - std::clog << "0x" << std::hex << val.ret() << std::dec; - break; - case vm_type::vm_addr: - std::clog << "0x"; - std::clog << std::hex << reinterpret_cast(val.addr()) << std::dec; - break; + case vm_type::vm_none: break; + case vm_type::vm_ret: return_address_info(val); break; + case vm_type::vm_addr: memory_address_info(val); break; case vm_type::vm_cnt: std::clog << val.cnt(); break; - case vm_type::vm_nil: std::clog << "nil"; break; + case vm_type::vm_nil: break; case vm_type::vm_num: std::clog << val.num(); break; - case vm_type::vm_str: - std::clog << "\"" << util::rawstr(val.str(), 16) << "\""; - break; + case vm_type::vm_str: raw_string_info(val); break; case vm_type::vm_func: std::clog << val.func(); break; - case vm_type::vm_upval: - std::clog << "<0x" << std::hex << p << std::dec; - std::clog << "> [" << val.upval().size << " val]"; - break; - case vm_type::vm_vec: - std::clog << "[" << val.vec().size() << " val]"; break; + case vm_type::vm_upval: upvalue_info(val); break; + case vm_type::vm_vec: vector_value_info(val); break; case vm_type::vm_hash: hash_value_info(val, 4); break; - case vm_type::vm_ghost: - std::clog << "<0x" << std::hex << p << "> " << std::dec; - std::clog << "object:" << val.ghost().type_name; - break; + case vm_type::vm_ghost: ghost_type_info(val); break; case vm_type::vm_co: coroutine_value_info(val); break; case vm_type::vm_map: namespace_value_info(val, 4); break; - default: - std::clog << "<0x" << std::hex << p << std::dec << "> unknown"; - break; + default: std::clog << "unknown"; break; } std::clog << "\n"; } void vm::function_detail_info(const nas_func& func) { - std::clog << "func"; + std::clog << "func@"; + std::clog << std::hex << reinterpret_cast(&func) << std::dec; std::vector argument_list = {}; argument_list.resize(func.keys.size()); @@ -203,47 +218,86 @@ void vm::function_detail_info(const nas_func& func) { } void vm::function_call_trace() { + util::windows_code_page_manager cp; + cp.set_utf8_output(); + var* bottom = ctx.stack; var* top = ctx.top; // generate trace back std::stack functions; + std::stack callsite; + + // load call trace for(var* i = bottom; i<=top; ++i) { + // i-1 is the callsite program counter of this function if (i->is_func() && i-1>=bottom && (i-1)->is_ret()) { functions.push(&i->func()); + callsite.push((i-1)->ret()); } } - if (functions.empty()) { + + // function register stores the latest-called function + if (functions.empty() && !ctx.funcr.is_func()) { + cp.restore_code_page(); return; } - std::clog << "\ncall trace " << (ngc.cort? "(coroutine)":"(main)") << "\n"; - const nas_func* last = nullptr; - u32 same = 0; - for(auto func = last; !functions.empty(); functions.pop()) { - func = functions.top(); - if (last==func) { - ++same; - continue; - } else if (same) { - std::clog << " --> " << same << " same call(s)\n"; - same = 0; - } + std::clog << "\ncall trace "; + std::clog << (ngc.cort? "(coroutine)":"(main)") << "\n"; + std::clog << " crash occurred in\n "; + function_detail_info(ctx.funcr.func()); + std::clog << " at " << files[bytecode[ctx.pc].fidx] << ":"; + std::clog << bytecode[ctx.pc].line << "\n"; + // another condition may exist: + // have ret pc on stack, but no function at the top of the ret pc + for(var* i = top; i>=bottom; --i) { + if (i->is_ret() && i->ret()!=callsite.top()) { + std::clog << " call "; + function_detail_info(ctx.funcr.func()); + std::clog << " from "; + std::clog << files[bytecode[i->ret()].fidx] << ":"; + std::clog << bytecode[i->ret()].line << "\n"; + break; + } + } + + const nas_func* last = nullptr; + u64 last_callsite = SIZE_MAX; + u64 same_count = 0; + for(; !functions.empty() && !callsite.empty(); functions.pop(), callsite.pop()) { + auto func = functions.top(); + auto place = callsite.top(); + + if (last==func && last_callsite==place) { + ++same_count; + continue; + } else if (same_count) { + std::clog << " `--> " << same_count << " same call(s)\n"; + same_count = 0; + } + last = func; + last_callsite = place; + + // output called function std::clog << " call "; function_detail_info(*func); - std::clog << "\n"; + + // output callsite + std::clog << " from "; + std::clog << files[bytecode[place].fidx] << ":"; + std::clog << bytecode[place].line << "\n"; } - if (same) { - std::clog << " --> " << same << " same call(s)\n"; + if (same_count) { + std::clog << " `--> " << same_count << " same call(s)\n"; } + + cp.restore_code_page(); } void vm::trace_back() { - // var* bottom = ctx.stack; - // var* top = ctx.top; - // generate trace back std::stack ret; for(var* i = ctx.stack; i<=ctx.top; ++i) { @@ -251,10 +305,14 @@ void vm::trace_back() { ret.push(i->ret()); } } - ret.push(ctx.pc); // store the position program crashed - std::clog << "\nback trace " << (ngc.cort? "(coroutine)":"(main)") << "\n"; + // store the position program crashed + ret.push(ctx.pc); + + std::clog << "\nback trace "; + std::clog << (ngc.cort? "(coroutine)":"(main)") << "\n"; codestream::set(const_number, const_string, native_function.data(), files); + for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) { if ((p = ret.top())==prev) { ++same; @@ -293,16 +351,16 @@ void vm::stack_info(const u64 limit = 16) { void vm::register_info() { std::clog << "\nregister (" << (ngc.cort? "coroutine":"main") << ")\n"; std::clog << std::hex - << " [ pc ] | pc | 0x" << ctx.pc << "\n" - << " [ global ] | address | 0x" + << " [ pc ] | pc | 0x" << ctx.pc << "\n" + << " [ global ] | addr | 0x" << reinterpret_cast(global) << "\n" - << " [ local ] | address | 0x" + << " [ local ] | addr | 0x" << reinterpret_cast(ctx.localr) << "\n" - << " [ memr ] | address | 0x" + << " [ memr ] | addr | 0x" << reinterpret_cast(ctx.memr) << "\n" - << " [ canary ] | address | 0x" + << " [ canary ] | addr | 0x" << reinterpret_cast(ctx.canary) << "\n" - << " [ top ] | address | 0x" + << " [ top ] | addr | 0x" << reinterpret_cast(ctx.top) << "\n" << std::dec; std::clog << " [ funcr ] "; value_info(ctx.funcr); diff --git a/src/nasal_vm.h b/src/nasal_vm.h index 7643e70..478f0d9 100644 --- a/src/nasal_vm.h +++ b/src/nasal_vm.h @@ -65,7 +65,13 @@ protected: protected: /* debug functions */ bool verbose = false; + void return_address_info(const var&); + void memory_address_info(const var&); + void raw_string_info(var&); + void upvalue_info(var&); + void vector_value_info(var&); void hash_value_info(var&, const usize); + void ghost_type_info(var&); void coroutine_value_info(var&); void namespace_value_info(var&, const usize); void value_name_form(const var&); diff --git a/test/coroutine.nas b/test/coroutine.nas index f0e938e..43a6628 100644 --- a/test/coroutine.nas +++ b/test/coroutine.nas @@ -73,7 +73,7 @@ println("coroutine state:\e[91m ",coroutine.status(co),"\e[0m"); println("coroutine yield: ",coroutine.resume(co)); println("coroutine state:\e[91m ",coroutine.status(co),"\e[0m"); -var co=coroutine.create(func { +var co = coroutine.create(func { var a=1; var b = func() { b(); From 4d838dc694600b8287a74931a5cf9bfab1bbbe2c Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 16 Jun 2024 22:41:50 +0800 Subject: [PATCH 4/9] :sparkles: add `subprocess.active` --- src/nasal_codegen.h | 1 + src/natives/subprocess.cpp | 26 ++++++++++++++ src/natives/subprocess.h | 1 + std/subprocess.nas | 4 +++ test/watchdog.nas | 69 ++++++++++++++------------------------ 5 files changed, 57 insertions(+), 44 deletions(-) diff --git a/src/nasal_codegen.h b/src/nasal_codegen.h index 961a105..938cdb8 100644 --- a/src/nasal_codegen.h +++ b/src/nasal_codegen.h @@ -60,6 +60,7 @@ private: "__chdir", "__environ", "__getcwd", "__getenv", // subprocess "__subprocess_create", + "__subprocess_active", "__subprocess_terminate" }; diff --git a/src/natives/subprocess.cpp b/src/natives/subprocess.cpp index 1e5394d..7acbc33 100644 --- a/src/natives/subprocess.cpp +++ b/src/natives/subprocess.cpp @@ -148,6 +148,31 @@ var builtin_subprocess_create(context* ctx, gc* ngc) { return obj; } +var builtin_subprocess_active(context* ctx, gc* ngc) { + auto obj = ctx->localr[1]; + if (!obj.object_check(subprocess::name())) { + return nas_err("subprocess::active", + "need correct subprocess object" + ); + } + +#ifdef WIN32 + auto pi = &obj.ghost().get()->pi; + + DWORD res; + GetExitCodeProcess(pi->hProcess, &res); + + return res==STILL_ACTIVE? one:zero; +#else + auto pid = obj.ghost().get()->pid; + + int status; + pid_t result = waitpid(pid, &status, WNOHANG); + + return result==0? one:zero; +#endif +} + var builtin_subprocess_terminate(context* ctx, gc* ngc) { auto obj = ctx->localr[1]; if (!obj.object_check(subprocess::name())) { @@ -186,6 +211,7 @@ var builtin_subprocess_terminate(context* ctx, gc* ngc) { nasal_builtin_table subprocess_native[] = { {"__subprocess_create", builtin_subprocess_create}, + {"__subprocess_active", builtin_subprocess_active}, {"__subprocess_terminate", builtin_subprocess_terminate}, {nullptr, nullptr} }; diff --git a/src/natives/subprocess.h b/src/natives/subprocess.h index 587527f..58db648 100644 --- a/src/natives/subprocess.h +++ b/src/natives/subprocess.h @@ -31,6 +31,7 @@ public: void subprocess_desc_dtor(void*); var builtin_subprocess_create(context*, gc*); +var builtin_subprocess_active(context*, gc*); var builtin_subprocess_terminate(context*, gc*); extern nasal_builtin_table subprocess_native[]; diff --git a/std/subprocess.nas b/std/subprocess.nas index f256ca8..8cf248a 100644 --- a/std/subprocess.nas +++ b/std/subprocess.nas @@ -3,6 +3,10 @@ var create = func(vec) { return __subprocess_create(vec); } +var active = func(pid) { + return __subprocess_active(pid); +} + var terminate = func(pid) { return __subprocess_terminate(pid); } diff --git a/test/watchdog.nas b/test/watchdog.nas index 6b0658b..3f205e4 100644 --- a/test/watchdog.nas +++ b/test/watchdog.nas @@ -27,87 +27,68 @@ var usage = func() { println( os_time(), info_hd(), - "\e[1musage: nasal watchdog.nas [\"argv\"]\e[0m" + "usage: nasal watchdog.nas [\"argv\"]" ); } var argv = runtime.argv(); if (size(argv)<1) { - println( - os_time(), - err_hd(), - "\e[1mneed correct file path to watch\e[0m" - ); + println(os_time(), err_hd(), "need correct file path to watch"); usage(); exit(-1); } var filename = argv[0]; if (!io.exists(filename)) { - println( - os_time(), - err_hd(), - "\e[1mfile <", - filename, - "> does not exist\e[0m" - ); + println(os_time(), err_hd(), "file <", filename, "> does not exist"); usage(); exit(-1); } var args = []; if (size(argv)==2) { - println( - os_time(), - info_hd(), - "\e[1mwith argument(s) ", - argv[1], - "\e[0m" - ); + println(os_time(), info_hd(), "with argument(s) ", argv[1]); args = split(" ", argv[1]); } var modified_time = io.fstat(filename).st_mtime; -println(os_time(), info_hd(), "\e[1mwatching ", filename, " ..\e[0m"); +println(os_time(), info_hd(), "watching ", filename, " .."); while(1) { unix.sleep(1); if (!io.exists(filename)) { - println( - os_time(), - err_hd(), - "\e[1mfile <", - filename, - "> does not exist\e[0m" - ); + println(os_time(), err_hd(), "file <", filename, "> does not exist"); break; } var latest_modified_time = io.fstat(filename).st_mtime; if (latest_modified_time!=modified_time) { modified_time = latest_modified_time; - println(os_time(), modified_hd(), "\e[1m", filename, "\e[0m"); + println(os_time(), modified_hd(), filename); var cmd = (os.platform()=="windows"?"":"./") ~ "nasal " ~ filename; foreach(var i; args) { cmd ~= " " ~ i; } - println( - os_time(), - info_hd(), - "\e[1mexecuting command \"", - cmd, - "\"\e[0m" - ); + println(os_time(), info_hd(), "executing command \"", cmd, "\""); - var subproc = subprocess.create(["nasal", filename]~args); + var subproc = subprocess.create(["nasal", filename] ~ args); - unix.sleep(2); + # check if active every 0.5s + var exited = false; + for(var t = 0; t<=4; t+=0.1) { + unix.sleep(0.1); + if (!subprocess.active(subproc)) { + exited = true; + break; + } + if (io.fstat(filename).st_mtime!=modified_time) { + println(os_time(), modified_hd(), "file changed, reloading.."); + break; + } + } + + # get return value var ret = subprocess.terminate(subproc); - - println( - os_time(), - ret!=0? err_hd():info_hd(), - "\e[1mprocess returned " ~ ret ~ "\e[0m" - ); + println(os_time(), ret!=0? err_hd():info_hd(), "process returned ", ret); } } \ No newline at end of file From 9dcb45d7866bf938365bb88acbc0f33639f8c02a Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Mon, 17 Jun 2024 21:36:10 +0800 Subject: [PATCH 5/9] :bug: fix trace bug in call trace --- src/nasal_vm.cpp | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index 067a04b..c3bd940 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -231,9 +231,21 @@ void vm::function_call_trace() { // load call trace for(var* i = bottom; i<=top; ++i) { // i-1 is the callsite program counter of this function - if (i->is_func() && i-1>=bottom && (i-1)->is_ret()) { - functions.push(&i->func()); - callsite.push((i-1)->ret()); + if (i->is_addr() && i+2<=top && + (i+1)->is_ret() && (i+1)->ret()>0 && + (i+2)->is_func()) { + functions.push(&(i+2)->func()); + callsite.push((i+1)->ret()); + } + } + + // another condition may exist + // have ret pc on stack, but no function at the top of the ret pc + for(var * i = top; i>=bottom; --i) { + if ((i->is_addr() && i+2<=top && (i+1)->is_ret() && !(i+2)->is_func()) || + (i->is_addr() && i+1<=top && i+2>top && (i+1)->is_ret())) { + functions.push(&ctx.funcr.func()); + callsite.push((i+1)->ret()); } } @@ -250,19 +262,6 @@ void vm::function_call_trace() { std::clog << " at " << files[bytecode[ctx.pc].fidx] << ":"; std::clog << bytecode[ctx.pc].line << "\n"; - // another condition may exist: - // have ret pc on stack, but no function at the top of the ret pc - for(var* i = top; i>=bottom; --i) { - if (i->is_ret() && i->ret()!=callsite.top()) { - std::clog << " call "; - function_detail_info(ctx.funcr.func()); - std::clog << " from "; - std::clog << files[bytecode[i->ret()].fidx] << ":"; - std::clog << bytecode[i->ret()].line << "\n"; - break; - } - } - const nas_func* last = nullptr; u64 last_callsite = SIZE_MAX; u64 same_count = 0; @@ -299,7 +298,7 @@ void vm::function_call_trace() { void vm::trace_back() { // generate trace back - std::stack ret; + std::stack ret; for(var* i = ctx.stack; i<=ctx.top; ++i) { if (i->is_ret() && i->ret()!=0) { ret.push(i->ret()); @@ -313,12 +312,11 @@ void vm::trace_back() { std::clog << (ngc.cort? "(coroutine)":"(main)") << "\n"; codestream::set(const_number, const_string, native_function.data(), files); - for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) { + for(u64 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) { if ((p = ret.top())==prev) { ++same; continue; - } - if (same) { + } else if (same) { std::clog << " 0x" << std::hex << std::setw(8) << std::setfill('0') << prev << std::dec << " " From 35c8afe56b55906f1dd746582e083f06a85b7d5b Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Mon, 17 Jun 2024 23:52:34 +0800 Subject: [PATCH 6/9] :fire: watchdog do not exist until edited --- src/nasal_opcode.cpp | 4 ++-- test/watchdog.nas | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nasal_opcode.cpp b/src/nasal_opcode.cpp index 3dc8030..51e4540 100644 --- a/src/nasal_opcode.cpp +++ b/src/nasal_opcode.cpp @@ -102,9 +102,9 @@ void codestream::dump(std::ostream& out) const { out << hex << "0x" << num << dec; break; case op_callb: out << hex << "0x" << num << dec; - out << " [" << natives[num].name << "@0x"; + out << " <" << natives[num].name << "@0x"; out << hex << reinterpret_cast(natives[num].func) << dec; - out << "]"; + out << ">"; break; case op_upval: case op_mupval: diff --git a/test/watchdog.nas b/test/watchdog.nas index 3f205e4..1e4360f 100644 --- a/test/watchdog.nas +++ b/test/watchdog.nas @@ -75,8 +75,8 @@ while(1) { # check if active every 0.5s var exited = false; - for(var t = 0; t<=4; t+=0.1) { - unix.sleep(0.1); + while(1) { + unix.sleep(0.5); if (!subprocess.active(subproc)) { exited = true; break; From 456ed5c78225ac30696c6ef30720ebf6d9b9f687 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Tue, 18 Jun 2024 01:00:22 +0800 Subject: [PATCH 7/9] :bug: fix error return value after call waitpid --- src/natives/subprocess.cpp | 25 ++++++++++++++++++++----- src/natives/subprocess.h | 1 + test/watchdog.nas | 2 +- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/natives/subprocess.cpp b/src/natives/subprocess.cpp index 7acbc33..53c6bf6 100644 --- a/src/natives/subprocess.cpp +++ b/src/natives/subprocess.cpp @@ -169,6 +169,11 @@ var builtin_subprocess_active(context* ctx, gc* ngc) { int status; pid_t result = waitpid(pid, &status, WNOHANG); + // this means the child process is returned + if (result==pid) { + obj.ghost().get()->status = status; + } + return result==0? one:zero; #endif } @@ -192,21 +197,31 @@ var builtin_subprocess_terminate(context* ctx, gc* ngc) { CloseHandle(pi->hProcess); CloseHandle(pi->hThread); + + return var::num(res); #else auto pid = obj.ghost().get()->pid; int status; pid_t result = waitpid(pid, &status, WNOHANG); - // child process running - if (result==0) { - kill(pid, SIGTERM); + if (result<0) { + auto pstat = obj.ghost().get()->status; + // if pstat is not 0, means child process already exited + auto res = WIFEXITED(pstat)? WEXITSTATUS(pstat):-1; + return var::num(res); } - auto res = WIFEXITED(status)? WEXITSTATUS(status):-1; -#endif + // child process is still running + if (result==0) { + kill(pid, SIGTERM); + return var::num(-1); + } + // child process exited when calling the waitpid above + auto res = WIFEXITED(status)? WEXITSTATUS(status):-1; return var::num(res); +#endif } nasal_builtin_table subprocess_native[] = { diff --git a/src/natives/subprocess.h b/src/natives/subprocess.h index 58db648..1af161c 100644 --- a/src/natives/subprocess.h +++ b/src/natives/subprocess.h @@ -17,6 +17,7 @@ namespace nasal { struct subprocess { #ifndef WIN32 pid_t pid; + int status = 0; #else STARTUPINFOW si; PROCESS_INFORMATION pi; diff --git a/test/watchdog.nas b/test/watchdog.nas index 1e4360f..9f1e3fa 100644 --- a/test/watchdog.nas +++ b/test/watchdog.nas @@ -65,7 +65,7 @@ while(1) { modified_time = latest_modified_time; println(os_time(), modified_hd(), filename); - var cmd = (os.platform()=="windows"?"":"./") ~ "nasal " ~ filename; + var cmd = "nasal " ~ filename; foreach(var i; args) { cmd ~= " " ~ i; } From bbd4d1907b2e83c9c9471ad934532ea14f6ed678 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Thu, 20 Jun 2024 00:29:14 +0800 Subject: [PATCH 8/9] :art: optimize implementation of dlopen --- doc/tutorial.md | 6 ++-- doc/tutorial_zh.md | 7 ++--- makefile | 2 ++ module/libfib.nas | 2 +- module/libkey.nas | 2 +- module/libmat.nas | 2 +- module/libnasock.nas | 2 +- src/natives/dylib_lib.cpp | 66 +++++++++++++++++++++++++++++++++------ src/natives/dylib_lib.h | 5 +++ src/natives/io_lib.cpp | 2 +- src/natives/unix_lib.cpp | 2 +- std/dylib.nas | 16 ---------- test/module_test.nas | 2 +- 13 files changed, 76 insertions(+), 40 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 6188298..1b4cad7 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -639,11 +639,11 @@ Windows(`.dll`): `g++ -shared -o libfib.dll fib.o` -Then we write a test nasal file to run this fib function, using `os.platform()` we could write a cross-platform program: +Then we write a test nasal file to run this fib function: ```javascript use std.dylib; -var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var dlhandle = dylib.dlopen("libfib"); var fib = dlhandle.fib; for(var i = 1; i<30; i += 1) println(dylib.dlcall(fib, i)); @@ -660,7 +660,7 @@ dylib.dlclose(dlhandle.lib); ```javascript use std.dylib; -var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var dlhandle = dylib.dlopen("libfib"); var fib = dlhandle.fib; var invoke = dylib.limitcall(1); # this means the called function has only one parameter for(var i = 1; i<30; i += 1) diff --git a/doc/tutorial_zh.md b/doc/tutorial_zh.md index 62910ad..bc2220a 100644 --- a/doc/tutorial_zh.md +++ b/doc/tutorial_zh.md @@ -618,12 +618,11 @@ Windows(`.dll`): `g++ -shared -o libfib.dll fib.o` -好了,那么我们可以写一个测试用的nasal代码来运行这个斐波那契函数了。 -下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台: +好了,那么我们可以写一个测试用的nasal代码来运行这个斐波那契函数了: ```javascript use std.dylib; -var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var dlhandle = dylib.dlopen("libfib"); var fib = dlhandle.fib; for(var i = 1; i<30; i += 1) println(dylib.dlcall(fib, i)); @@ -640,7 +639,7 @@ dylib.dlclose(dlhandle.lib); ```javascript use std.dylib; -var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var dlhandle = dylib.dlopen("libfib"); var fib = dlhandle.fib; var invoke = dylib.limitcall(1); # this means the called function has only one parameter for(var i = 1; i<30; i += 1) diff --git a/makefile b/makefile index 808bda3..dea3eef 100644 --- a/makefile +++ b/makefile @@ -189,6 +189,8 @@ build/dylib_lib.o: \ src/nasal.h\ src/nasal_type.h\ src/nasal_gc.h\ + src/util/util.h\ + src/util/fs.h\ src/natives/dylib_lib.h src/natives/dylib_lib.cpp | build $(CXX) $(CXXFLAGS) src/natives/dylib_lib.cpp -o build/dylib_lib.o diff --git a/module/libfib.nas b/module/libfib.nas index 9df5c2c..b5a2519 100644 --- a/module/libfib.nas +++ b/module/libfib.nas @@ -1,7 +1,7 @@ use std.dylib; use std.os; -var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var _dl = dylib.dlopen("libfib"); var _fib = _dl.fib; diff --git a/module/libkey.nas b/module/libkey.nas index 26f46da..3475100 100644 --- a/module/libkey.nas +++ b/module/libkey.nas @@ -6,7 +6,7 @@ var ( getch, nonblock ) = func { - var lib = dylib.dlopen("libkey"~(os.platform()=="windows"? ".dll":".so")); + var lib = dylib.dlopen("libkey"); var kb = lib.nas_kbhit; var gt = lib.nas_getch; var nb = lib.nas_noblock; diff --git a/module/libmat.nas b/module/libmat.nas index cbf02c9..fe7793f 100644 --- a/module/libmat.nas +++ b/module/libmat.nas @@ -1,7 +1,7 @@ use std.dylib; use std.os; -var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so")); +var _dl = dylib.dlopen("libmat"); var _vec2 = _dl.nas_vec2; diff --git a/module/libnasock.nas b/module/libnasock.nas index e686cf1..661226c 100644 --- a/module/libnasock.nas +++ b/module/libnasock.nas @@ -2,7 +2,7 @@ use std.dylib; use std.os; var socket = func() { - var lib = dylib.dlopen("libnasock"~(os.platform()=="windows"? ".dll":".so")); + var lib = dylib.dlopen("libnasock"); var sock = lib.nas_socket; var closesocket = lib.nas_closesocket; diff --git a/src/natives/dylib_lib.cpp b/src/natives/dylib_lib.cpp index 329d8ed..1af6fcf 100644 --- a/src/natives/dylib_lib.cpp +++ b/src/natives/dylib_lib.cpp @@ -1,9 +1,14 @@ #include "natives/dylib_lib.h" +#include "util/util.h" +#include "util/fs.h" + +#include +#include namespace nasal { -const auto dynamic_library_type_name = "dylib"; -const auto function_address_type_name = "faddr"; +const auto dynamic_library_type_name = "nasal::dynamic_library"; +const auto function_address_type_name = "nasal::function_address"; void dynamic_library_destructor(void* pointer) { #ifdef _WIN32 @@ -13,33 +18,74 @@ void dynamic_library_destructor(void* pointer) { #endif } +std::string search_dynamic_library_path(const std::string& dlname) { + const auto ext = (util::is_windows()? ".dll":".so"); + const auto lib_path = (util::is_windows()? ".\\":"./") + dlname + ext; + if (fs::exists(lib_path)) { + return lib_path; + } + const auto env_path = std::string(getenv("PATH")); + const auto sep = (util::is_windows()? ";":":"); + + // do split string + std::vector env_path_vec = {}; + usize last = 0; + usize pos = env_path.find(sep, 0); + while(pos!=std::string::npos) { + if (pos>last) { + env_path_vec.push_back(env_path.substr(last, pos-last)); + } + last = pos + 1; + pos = env_path.find(sep, last); + } + if (last!=env_path.length()) { + env_path_vec.push_back(env_path.substr(last)); + } + + const auto path_front = util::is_windows()? "\\module\\":"/module/"; + for(auto& p : env_path_vec) { + p += path_front + lib_path; + if (fs::exists(p)) { + return p; + } + } + return ""; +} + var builtin_dlopen(context* ctx, gc* ngc) { - auto dlname = ctx->localr[1]; - if (!dlname.is_str()) { + auto dl = ctx->localr[1]; + if (!dl.is_str()) { return nas_err("dylib::dlopen", "\"libname\" must be string"); } + const auto dlname = search_dynamic_library_path(dl.str()); + if (dlname.empty()) { + return nas_err("dylib::dlopen", + "cannot find dynamic lib <" + dl.str() + ">" + ); + } + // get library pointer #ifdef _WIN32 - wchar_t* wide_string = new wchar_t[dlname.str().size()+1]; + wchar_t* wide_string = new wchar_t[dlname.size()+1]; if (!wide_string) { return nas_err("dylib::dlopen", "malloc failed"); } - memset(wide_string, 0, sizeof(wchar_t) * dlname.str().size() + 1); - mbstowcs(wide_string, dlname.str().c_str(), dlname.str().size() + 1); + memset(wide_string, 0, sizeof(wchar_t) * dlname.size() + 1); + mbstowcs(wide_string, dlname.c_str(), dlname.size() + 1); // load library by using wide string name - void* dynamic_library_pointer = LoadLibraryA(dlname.str().c_str()); + void* dynamic_library_pointer = LoadLibraryA(dlname.c_str()); delete []wide_string; #else void* dynamic_library_pointer = dlopen( - dlname.str().c_str(), RTLD_LOCAL|RTLD_LAZY + dlname.c_str(), RTLD_LOCAL|RTLD_LAZY ); #endif // check library pointer and insert into returned hashmap if (!dynamic_library_pointer) { return nas_err("dylib::dlopen", - "cannot open dynamic lib <" + dlname.str() + ">" + "cannot open dynamic lib <" + dl.str() + ">" ); } auto return_hash = ngc->temp = ngc->alloc(vm_type::vm_hash); diff --git a/src/natives/dylib_lib.h b/src/natives/dylib_lib.h index 3b49dea..67a9ef0 100644 --- a/src/natives/dylib_lib.h +++ b/src/natives/dylib_lib.h @@ -11,10 +11,15 @@ #include #endif +#include +#include + namespace nasal { void dynamic_library_destructor(void*); +std::string search_dynamic_library_path(const std::string&); + var builtin_dlopen(context*, gc*); var builtin_dlclose(context*, gc*); var builtin_dlcallv(context*, gc*); diff --git a/src/natives/io_lib.cpp b/src/natives/io_lib.cpp index 83f81dc..d7f0c8e 100644 --- a/src/natives/io_lib.cpp +++ b/src/natives/io_lib.cpp @@ -6,7 +6,7 @@ namespace nasal { -const auto file_type_name = "file"; +const auto file_type_name = "nasal::FILE"; void filehandle_destructor(void* ptr) { fclose(static_cast(ptr)); diff --git a/src/natives/unix_lib.cpp b/src/natives/unix_lib.cpp index a23ddd5..3296dab 100644 --- a/src/natives/unix_lib.cpp +++ b/src/natives/unix_lib.cpp @@ -2,7 +2,7 @@ namespace nasal { -const auto dir_type_name = "dir"; +const auto dir_type_name = "nasal::DIR"; void dir_entry_destructor(void* ptr) { #ifndef _MSC_VER diff --git a/std/dylib.nas b/std/dylib.nas index a63b0a0..1b8fb12 100644 --- a/std/dylib.nas +++ b/std/dylib.nas @@ -8,22 +8,6 @@ use std.unix; # open dynamic lib. return a hash including dl pointer and function pointers var dlopen = func(libname) { - # find dynamic lib from local dir first - libname = (os.platform()=="windows"? ".\\":"./")~libname; - if (io.exists(libname)) - return __dlopen(libname); - # find dynamic lib through PATH - var envpath = split(os.platform()=="windows"? ";":":", unix.getenv("PATH")); - # first find ./module - append(envpath, "."); - var path = os.platform()=="windows"? "\\module\\":"/module/"; - foreach(var p;envpath) { - p ~= path~libname; - if (io.exists(p)) { - libname = p; - break; - } - } return __dlopen(libname); } diff --git a/test/module_test.nas b/test/module_test.nas index 8c5ebdb..b64c3f3 100644 --- a/test/module_test.nas +++ b/test/module_test.nas @@ -23,7 +23,7 @@ func() { }(); var speed_test = func() { - var dd = dylib.dlopen("libfib."~(os.platform()=="windows"? "dll":"so")); + var dd = dylib.dlopen("libfib"); println("[dylib ] ", dd); var fd = dd.quick_fib; var vec_call = dylib.dlcall; From 858ffdcb614f45df2398f4eb79eddb0b19f6c19d Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Thu, 20 Jun 2024 00:31:58 +0800 Subject: [PATCH 9/9] :bug: forgot this --- src/natives/dylib_lib.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/natives/dylib_lib.cpp b/src/natives/dylib_lib.cpp index 1af6fcf..d405974 100644 --- a/src/natives/dylib_lib.cpp +++ b/src/natives/dylib_lib.cpp @@ -28,7 +28,7 @@ std::string search_dynamic_library_path(const std::string& dlname) { const auto sep = (util::is_windows()? ";":":"); // do split string - std::vector env_path_vec = {}; + std::vector env_path_vec = {"."}; usize last = 0; usize pos = env_path.find(sep, 0); while(pos!=std::string::npos) {