Nasal-Interpreter/src/nasal_dbg.cpp

300 lines
9.5 KiB
C++

#include "nasal_dbg.h"
namespace nasal {
void operand_line_counter::init_counter() {
for(usize i = 0; i<operand_line_counter::operand_size; ++i) {
operand_counter[i] = 0;
}
}
void operand_line_counter::load_file_line_counter(
const std::vector<std::string>& file_list) {
file_name_list = file_list;
file_line_counter = {};
file_contents = {};
filestream fs;
for(usize i =0; i<file_list.size(); ++i) {
fs.load(file_list[i]);
file_contents.push_back(fs.file_content());
file_line_counter.push_back({});
file_line_counter.back().resize(fs.size(), 0);
}
}
void operand_line_counter::init(const std::vector<std::string>& file_list) {
init_counter();
load_file_line_counter(file_list);
}
void operand_line_counter::dump_operand_count() const {
typedef std::pair<u32, u64> op_count;
std::vector<op_count> opcall;
u64 total = 0;
for(usize i = 0; i<operand_line_counter::operand_size; ++i) {
total += operand_counter[i];
opcall.push_back({i, operand_counter[i]});
}
std::sort(opcall.begin(), opcall.end(),
[](const op_count& a, const op_count& b) {
return a.second>b.second;
}
);
std::clog << "\noperands call info (<1% ignored)\n";
for(const auto& i : opcall) {
u64 rate = i.second*100/total;
if (!rate) {
break;
}
std::clog << " ";
std::clog << operand_name_table.at(static_cast<opcode_type>(i.first));
std::clog << " : " << i.second << " (" << rate << "%)\n";
}
std::clog << " total : " << total << '\n';
}
void operand_line_counter::dump_all_code_line_counter(std::ostream& os) const {
u64 max_call_time = 0;
for(const auto& context : file_line_counter) {
for(const auto& count : context) {
max_call_time = count>max_call_time? count:max_call_time;
}
}
auto pad_length = std::to_string(max_call_time).length();
for(usize i = 0; i<file_name_list.size(); ++i) {
os << "\ncode profiling data of " << file_name_list[i] << ":\n";
const auto& context = file_contents[i];
const auto& counter = file_line_counter[i];
for(usize j = 0; j<context.size(); ++j) {
os << " " << std::right << std::setw(pad_length);
os << std::setfill(' ');
os << (counter[j]==0? "":std::to_string(counter[j]));
os << " " << context[j] << "\n";
}
}
}
void operand_line_counter::dump_this_file_line_counter(std::ostream& os) const {
u64 max_call_time = 0;
for(const auto& count : file_line_counter[0]) {
max_call_time = count>max_call_time? count:max_call_time;
}
auto pad_length = std::to_string(max_call_time).length();
os << "\ncode profiling data of " << file_name_list[0] << ":\n";
const auto& context = file_contents[0];
const auto& counter = file_line_counter[0];
for(usize i = 0; i<context.size(); ++i) {
os << " " << std::right << std::setw(pad_length);
os << std::setfill(' ');
os << (counter[i]==0? "":std::to_string(counter[i]));
os << " " << context[i] << "\n";
}
}
std::vector<std::string> dbg::parse(const std::string& cmd) {
std::vector<std::string> res;
usize last = 0, pos = cmd.find(" ", 0);
while(pos!=std::string::npos) {
if (pos>last) {
res.push_back(cmd.substr(last, pos-last));
}
last = pos+1;
pos = cmd.find(" ", last);
}
if (last<cmd.length()) {
res.push_back(cmd.substr(last));
}
return res;
}
u16 dbg::file_index(const std::string& filename) const {
for(u16 i = 0; i<file_list_size; ++i) {
if (filename==files[i]) {
return i;
}
}
return UINT16_MAX;
}
void dbg::err() const {
std::cerr
<< "incorrect command\n"
<< "input \'h\' to get help\n";
}
void dbg::help() const {
std::clog
<< "<option>\n"
<< " h, help | get help\n"
<< " bt, backtrace | get function call trace\n"
<< " c, continue | run program until break point or exit\n"
<< " f, file | see all the compiled files\n"
<< " g, global | see global values\n"
<< " l, local | see local values\n"
<< " u, upval | see upvalue\n"
<< " r, register | show vm register detail\n"
<< " a, all | show global, local and upvalue\n"
<< " n, next | execute next bytecode\n"
<< " q, exit | exit debugger\n"
<< "<option> <filename> <line>\n"
<< " bk, break | set break point\n";
}
void dbg::list_file() const {
for(usize i = 0; i<file_list_size; ++i) {
std::clog << "[" << i << "] " << files[i] << "\n";
}
}
void dbg::step_info() {
u64 line = bytecode[ctx.pc].line==0? 0:bytecode[ctx.pc].line-1;
u64 begin = (line>>3)==0? 0:((line>>3)<<3);
u64 end = (1+(line>>3))<<3;
src.load(files[bytecode[ctx.pc].fidx]);
std::clog << clear_screen << set_cursor;
std::clog << "\nsource code:\n";
for(u64 i = begin; i<end && i<src.size(); ++i) {
std::clog << (i==line? back_white:reset);
std::clog << (i==line? "--> ":" ") << src[i] << reset << "\n";
}
begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
end = (1+(ctx.pc>>3))<<3;
codestream::set(const_number, const_string, native_function.data(), files);
std::clog << "\nnext bytecode:\n";
for(u64 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
std::clog
<< (i==ctx.pc? back_white:reset)
<< (i==ctx.pc? "--> ":" ")
<< codestream(bytecode[i], i)
<< reset << "\n";
}
stack_info(16);
}
void dbg::interact() {
// special operand, end execution
if (bytecode[ctx.pc].op==op_exit) {
return;
}
// do not need interact while doing profiling
if (do_operand_count) {
return;
}
// is not break point and is not next stop command
const auto& code = bytecode[ctx.pc];
if ((code.fidx!=break_file_index || code.line!=break_line) && !next) {
return;
}
next = false;
std::string cmd;
step_info();
while(true) {
std::clog << ">> ";
std::getline(std::cin, cmd);
auto res = parse(cmd);
if (res.size()==0) {
// enter key without input using cmd_next by default
next = true;
return;
} else if (res.size()==1) {
switch(get_cmd_type(res[0])) {
case cmd_kind::cmd_help: help(); break;
case cmd_kind::cmd_backtrace:
function_call_trace();
trace_back();
break;
case cmd_kind::cmd_continue: return;
case cmd_kind::cmd_list_file: list_file(); break;
case cmd_kind::cmd_global: global_state(); break;
case cmd_kind::cmd_local: local_state(); break;
case cmd_kind::cmd_upval: upvalue_state(); break;
case cmd_kind::cmd_register: register_info(); break;
case cmd_kind::cmd_show_all: all_state_detail(); break;
case cmd_kind::cmd_next: next = true; return;
case cmd_kind::cmd_exit: std::exit(0);
default: err(); break;
}
} else if (res.size()==3 &&
get_cmd_type(res[0])==cmd_kind::cmd_break_point) {
break_file_index = file_index(res[1]);
if (break_file_index==UINT16_MAX) {
std::clog << "cannot find file named `" << res[1] << "`\n";
continue;
}
i32 tmp = atoi(res[2].c_str());
if (tmp<=0) {
std::clog << "incorrect line number `" << res[2] << "`\n";
} else {
break_line = tmp;
}
} else {
err();
}
}
}
void dbg::run(const codegen& gen,
const linker& linker,
const std::vector<std::string>& argv,
bool profile,
bool show_all_prof_result) {
set_detail_report_info(true);
do_operand_count = profile || show_all_prof_result;
const auto& file_list = linker.get_file_list();
file_list_size = file_list.size();
vm_init_enrty(
gen.strs(),
gen.nums(),
gen.natives(),
gen.codes(),
gen.globals(),
file_list,
argv
);
counter.init(file_list);
std::vector<u8> code;
std::vector<u16> code_file_index;
std::vector<u64> code_line;
for(const auto& i : gen.codes()) {
code.push_back(i.op);
code_file_index.push_back(i.fidx);
code_line.push_back(i.line);
imm.push_back(i.num);
}
while(operand_function[code[ctx.pc]]) {
interact();
counter.add_operand_counter(code[ctx.pc]);
counter.add_code_line_counter(code_file_index[ctx.pc], code_line[ctx.pc]);
(this->*operand_function[code[ctx.pc]])();
if (ctx.top>=ctx.canary) {
die("stack overflow");
}
++ctx.pc;
}
counter.dump_operand_count();
if (do_operand_count) {
show_all_prof_result?
counter.dump_all_code_line_counter(std::clog):
counter.dump_this_file_line_counter(std::clog);
}
ngc.info();
ngc.clear();
imm.clear();
return;
}
}