diff options
| -rw-r--r-- | arch/salis-v1/arch.j2.c | 48 | ||||
| -rw-r--r-- | bench.j2.c | 52 | ||||
| -rw-r--r-- | core.j2.c | 9 | ||||
| -rwxr-xr-x | salis.py | 54 | ||||
| -rw-r--r-- | ui/curses/ui.j2.c | 177 | ||||
| -rw-r--r-- | ui/daemon/ui.j2.c | 42 |
6 files changed, 310 insertions, 72 deletions
diff --git a/arch/salis-v1/arch.j2.c b/arch/salis-v1/arch.j2.c index aaa5284..f2b9990 100644 --- a/arch/salis-v1/arch.j2.c +++ b/arch/salis-v1/arch.j2.c @@ -773,7 +773,7 @@ void arch_push_data_header() { assert(g_sim_data); const char *sql = ( - "create table data(" + "create table trend(" "step int not null, " {% for i in range(args.cores) %} "cycl_{{ i }} int not null, " @@ -791,13 +791,23 @@ void arch_push_data_header() { ");" ); - {% set sql_call %}sqlite3_exec(g_sim_data, sql, NULL, NULL, NULL){% endset %} + g_info("Generating 'trend' table in SQLite database"); - {% if not args.optimized %} - assert({{ sql_call }} == 0); - {% else %} - {{ sql_call }}; - {% endif %} + int sql_res; + char *sql_err; + + // Only handle busy database errors + // Application should fail on all other error conditions + while ((sql_res = sqlite3_exec(g_sim_data, sql, NULL, NULL, &sql_err)) == SQLITE_BUSY) { + g_warn("Busy SQLite database returned error '%d' with message:", sql_res); + g_warn(sql_err); + + sqlite3_free(sql_err); + + g_info("Will retry query..."); + } + + assert(sql_res == 0); } void arch_push_data_line() { @@ -835,7 +845,7 @@ void arch_push_data_line() { asprintf( &sql, - "insert into data (" + "insert into trend (" "step, " {% for i in range(args.cores) %} "cycl_{{ i }}, " @@ -876,13 +886,23 @@ void arch_push_data_line() { {% endfor %} ); - {% set sql_call %}sqlite3_exec(g_sim_data, sql, NULL, NULL, NULL){% endset %} + g_info("Pushing row to 'trend' table in SQLite database"); - {% if not args.optimized %} - assert({{ sql_call }} == 0); - {% else %} - {{ sql_call }}; - {% endif %} + int sql_res; + char *sql_err; + + // Only handle busy database errors + // Application should fail on all other error conditions + while ((sql_res = sqlite3_exec(g_sim_data, sql, NULL, NULL, &sql_err)) == SQLITE_BUSY) { + g_warn("Busy SQLite database returned error '%d' with message:", sql_res); + g_warn(sql_err); + + sqlite3_free(sql_err); + + g_info("Will retry query..."); + } + + assert(sql_res == 0); // Free query string returned by 'asprintf()' free(sql); @@ -4,38 +4,48 @@ // Simple benchmark test helps measure simulation speed. // Steps the simulation N times and prints part of the simulator's state. +void log_impl(const char *format, ...) { + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); +} + int main() { - printf("Salis Benchmark Test\n\n"); + g_info = log_impl; + g_warn = log_impl; + + g_info("Salis Benchmark Test\n\n"); salis_init(); salis_step({{ args.steps }}); - printf("seed => %#lx\n", {{ args.seed }}); - printf("g_steps => %#lx\n", g_steps); - printf("g_syncs => %#lx\n", g_syncs); + g_info("seed => %#lx\n", {{ args.seed }}); + g_info("g_steps => %#lx\n", g_steps); + g_info("g_syncs => %#lx\n", g_syncs); for (int i = 0; i < {{ args.cores }}; ++i) { - putchar('\n'); - printf("core %d mall => %#lx\n", i, g_cores[i].mall); - printf("core %d mut0 => %#lx\n", i, g_cores[i].muta[0]); - printf("core %d mut1 => %#lx\n", i, g_cores[i].muta[1]); - printf("core %d mut2 => %#lx\n", i, g_cores[i].muta[2]); - printf("core %d mut3 => %#lx\n", i, g_cores[i].muta[3]); - printf("core %d pnum => %#lx\n", i, g_cores[i].pnum); - printf("core %d pcap => %#lx\n", i, g_cores[i].pcap); - printf("core %d pfst => %#lx\n", i, g_cores[i].pfst); - printf("core %d plst => %#lx\n", i, g_cores[i].plst); - printf("core %d pcur => %#lx\n", i, g_cores[i].pcur); - printf("core %d psli => %#lx\n", i, g_cores[i].psli); - printf("core %d cycl => %#lx\n", i, g_cores[i].cycl); - printf("core %d ivpt => %#lx\n", i, g_cores[i].ivpt); - putchar('\n'); + g_info("\n"); + g_info("core %d mall => %#lx\n", i, g_cores[i].mall); + g_info("core %d mut0 => %#lx\n", i, g_cores[i].muta[0]); + g_info("core %d mut1 => %#lx\n", i, g_cores[i].muta[1]); + g_info("core %d mut2 => %#lx\n", i, g_cores[i].muta[2]); + g_info("core %d mut3 => %#lx\n", i, g_cores[i].muta[3]); + g_info("core %d pnum => %#lx\n", i, g_cores[i].pnum); + g_info("core %d pcap => %#lx\n", i, g_cores[i].pcap); + g_info("core %d pfst => %#lx\n", i, g_cores[i].pfst); + g_info("core %d plst => %#lx\n", i, g_cores[i].plst); + g_info("core %d pcur => %#lx\n", i, g_cores[i].pcur); + g_info("core %d psli => %#lx\n", i, g_cores[i].psli); + g_info("core %d cycl => %#lx\n", i, g_cores[i].cycl); + g_info("core %d ivpt => %#lx\n", i, g_cores[i].ivpt); + g_info("\n"); for (int j = 0; j < 32; ++j) { - printf("%02x ", g_cores[i].mvec[j]); + g_info("%02x ", g_cores[i].mvec[j]); } - putchar('\n'); + g_info("\n"); } salis_free(); @@ -62,6 +62,11 @@ char g_asav_pbuf[{{ auto_save_name_len }}]; sqlite3 *g_sim_data; {% endif %} +// Before invoking salis_init(), +// each UI must install its own logger functions +void (*g_info)(const char *fmt, ...); +void (*g_warn)(const char *fmt, ...); + // Forward declarations // Each architecture must define these {% if args.command in ["bench", "new"] and anc_bytes is defined %} @@ -582,12 +587,16 @@ void salis_auto_save() { assert(rem >= 0); assert(rem < {{ auto_save_name_len }}); + g_info("Saving simulation state on step '%#lx'", g_steps); salis_save(g_asav_pbuf); } {% endif %} {% if args.command in ["bench", "new"] %} void salis_init() { + assert(g_info); + assert(g_warn); + uint64_t seed = {{ args.seed }}; for (int i = 0; i < {{ args.cores }}; ++i) { @@ -123,25 +123,21 @@ for parser, option in parser_map: args = main_parser.parse_args() - -def log(msg, val=""): - print(f"\033[1;34m{msg}\033[0m", val, flush=True) - +def info(msg, val=""): + print(f"\033[1;34mINFO:\033[0m {msg}", val) def warn(msg, val=""): - print(f"\033[1;31m{msg}\033[0m", val, flush=True) - + print(f"\033[1;31mWARN:\033[0m {msg}", val) def error(msg, val=""): - warn(f"ERROR: {msg}", val) + print(f"\033[1;31mERROR:\033[0m {msg}", val) sys.exit(1) - # ------------------------------------------------------------------------------ # Load configuration # ------------------------------------------------------------------------------ -log(headline) -log(f"Called '{script}' with the following options:") +info(headline) +info(f"Called '{script}' with the following options:") for key, val in vars(args).items(): print(f"{key} = {repr(val)}") @@ -155,7 +151,7 @@ if args.command in ["load"]: if not os.path.isdir(sim_dir): error("No simulation found named:", args.name) - log(f"Sourcing configuration from '{sim_opts}':") + info(f"Sourcing configuration from '{sim_opts}':") sys.path.append(sim_dir) import opts as opts_module @@ -178,10 +174,10 @@ if args.command in ["new"]: if os.path.isdir(sim_dir): error("Simulation directory found at:", sim_dir) - log("Creating new simulation directory at:", sim_dir) + info("Creating new simulation directory at:", sim_dir) os.mkdir(sim_dir) - log("Creating configuration file at:", sim_opts) + info("Creating configuration file at:", sim_opts) opts = ( option["long"].replace("-", "_") @@ -197,13 +193,13 @@ if args.command in ["new"]: # Load architecture and UI variables # ------------------------------------------------------------------------------ arch_path = f"arch/{args.arch}" -log("Loading architecture specific variables from:", f"{arch_path}/arch_vars.py") +info("Loading architecture specific variables from:", f"{arch_path}/arch_vars.py") sys.path.append(arch_path) import arch_vars if args.command in ["load", "new"]: ui_path = f"ui/{args.ui}" - log("Loading UI specific variables from:", f"{ui_path}/ui_vars.py") + info("Loading UI specific variables from:", f"{ui_path}/ui_vars.py") sys.path.append(ui_path) import ui_vars @@ -215,6 +211,7 @@ ul_pow = lambda val: f"{hex(2 ** val)}ul" includes = [ "assert.h", + "stdarg.h", "stdbool.h", "stddef.h", "stdint.h", @@ -249,13 +246,13 @@ if args.command in ["load", "new"]: data_push_interval = ul_pow(args.data_push_pow) data_push_busy_timeout = 600000 includes.append("sqlite3.h") - log("Data will be aggregated at:", data_push_path) + info("Data will be aggregated at:", data_push_path) else: warn("Data aggregation disabled") if args.compress: includes.append("zlib.h") - log("Save file compression enabled") + info("Save file compression enabled") else: warn("Save file compression disabled") @@ -295,16 +292,16 @@ if args.command in ["bench", "new"] and args.anc is not None: if not found: error("Unrecognized instruction in ancestor file:", line) - log(f"Compiled ancestor file '{anc_path}' into byte array:", anc_bytes) + info(f"Compiled ancestor file '{anc_path}' into byte array:", anc_bytes) # ------------------------------------------------------------------------------ # Emit C source # ------------------------------------------------------------------------------ tempdir = TemporaryDirectory(prefix="salis_", delete=args.delete_temp_dir) -log("Created a temporary salis directory at:", tempdir.name) +info("Created a temporary salis directory at:", tempdir.name) salis_src = f"{tempdir.name}/salis.c" -log("Emitting C source at:", salis_src) +info("Emitting C source at:", salis_src) jinja_env = Environment( loader = FileSystemLoader("."), @@ -316,7 +313,7 @@ jinja_env = Environment( source_str = jinja_env.get_template("core.j2.c").render(**locals()) if args.print_source: - log("Printing C source and exiting...") + info("Printing C source and exiting...") print(source_str) exit(0) @@ -327,7 +324,7 @@ with open(salis_src, "w") as file: # Build executable # ------------------------------------------------------------------------------ salis_bin = f"{tempdir.name}/salis_bin" -log("Building salis binary at:", salis_bin) +info("Building salis binary at:", salis_bin) build_cmd = ["gcc", salis_src, "-o", salis_bin, "-Wall", "-Wextra", "-Werror", "-Wno-overlength-strings", "-pedantic", "-std=c11"] build_cmd.extend(["-O3", "-DNDEBUG"] if args.optimized else ["-ggdb"]) @@ -342,19 +339,19 @@ if args.command in ["load", "new"]: # This allows managing large SQL strings more easily build_cmd.extend(["-lsqlite3", "-D_GNU_SOURCE"] if args.data_push_pow != 0 else []) -log("Using build command:", " ".join(build_cmd)) +info("Using build command:", " ".join(build_cmd)) subprocess.run(build_cmd, check=True) # ------------------------------------------------------------------------------ # Run salis binary # ------------------------------------------------------------------------------ -log("Running salis binary...") +info("Running salis binary...") run_cmd = [args.pre_cmd] if args.pre_cmd else [] run_cmd.append(salis_bin) -log("Using run command:", " ".join(run_cmd)) -salis_sp = subprocess.Popen(run_cmd, stdout=sys.stdout) +info("Using run command:", " ".join(run_cmd)) +salis_sp = subprocess.Popen(run_cmd, stdout=sys.stdout, stderr=sys.stderr) # Ctrl-C terminates the simulator gracefully. # When using signals (e.g. SIGTERM), they must be sent to the entire process group @@ -364,3 +361,8 @@ try: except KeyboardInterrupt: salis_sp.terminate() salis_sp.wait() + +code = salis_sp.returncode + +if code != 0: + error("Salis binary returned code:", code) diff --git a/ui/curses/ui.j2.c b/ui/curses/ui.j2.c index 4370774..9ebd387 100644 --- a/ui/curses/ui.j2.c +++ b/ui/curses/ui.j2.c @@ -11,6 +11,9 @@ {% set proc_field_width = 21 %} {% set proc_page_lines = 12 %} +{% set log_line_size = 1024 %} +{% set log_line_count = 1024 %} + {% macro ctrl(x) %}('{{ x }}' & 0x1f){% endmacro %} {% if not args.optimized %} @@ -27,6 +30,7 @@ enum { PAGE_PROCESS, PAGE_WORLD, PAGE_IPC, + PAGE_LOG, PAGE_COUNT, }; @@ -35,6 +39,7 @@ enum { PAIR_NOUSE, PAIR_NORMAL, PAIR_HEADER, + PAIR_WARN, PAIR_LIVE_PROC, PAIR_SELECTED_PROC, PAIR_FREE_CELL, @@ -72,6 +77,12 @@ bool g_wcursor_mode; int g_wcursor_x; int g_wcursor_y; uint64_t g_wcursor_pointed; +uint64_t g_log_cnt; +unsigned g_log_ptr; +unsigned g_log_scroll; +bool g_log_warns[{{ log_line_count }}]; +time_t g_log_times[{{ log_line_count }}]; +char g_logs[{{ log_line_count }}][{{ log_line_size }}]; uint64_t g_vlin; uint64_t g_vsiz; uint64_t g_vlin_rng; @@ -310,7 +321,7 @@ void gfx_render(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t p } // ---------------------------------------------------------------------------- -// TUI functions +// TUI generic functions // ---------------------------------------------------------------------------- void ui_line_buff_free() { if (g_line_buff) { @@ -387,6 +398,9 @@ void ui_ulx_field(int l, const char *label, uint64_t value) { ui_line(false, l, PAIR_NORMAL, A_NORMAL, "%-4s : %#18lx", label, value); } +// ---------------------------------------------------------------------------- +// Core page functions +// ---------------------------------------------------------------------------- void ui_print_core(int l) { ui_line(false, ++l, PAIR_HEADER, A_BOLD, "CORE [%d]", g_core); ui_ulx_field(++l, "cycl", g_cores[g_core].cycl); @@ -409,6 +423,9 @@ void ui_print_core(int l) { {% endfor %} } +// ---------------------------------------------------------------------------- +// Process page functions +// ---------------------------------------------------------------------------- int ui_proc_pair(uint64_t pix) { if (pix == g_proc_selected) { return PAIR_SELECTED_PROC; @@ -566,6 +583,9 @@ void ui_print_process(int l) { } } +// ---------------------------------------------------------------------------- +// World page functions +// ---------------------------------------------------------------------------- void ui_world_resize() { assert(g_wrld_zoom); @@ -718,6 +738,9 @@ void ui_print_world(int l) { } } +// ---------------------------------------------------------------------------- +// IPC page functions +// ---------------------------------------------------------------------------- void ui_print_ipc_field(int l, uint64_t i, int color) { uint8_t iinst = g_cores[g_core].iviv[i]; uint64_t iaddr = g_cores[g_core].ivav[i]; @@ -771,6 +794,118 @@ void ui_print_ipc(int l) { ui_print_ipc_data(); } +// ---------------------------------------------------------------------------- +// Log page functions +// ---------------------------------------------------------------------------- +void ui_info_impl(const char *format, ...) { + g_log_warns[g_log_ptr] = false; + g_log_times[g_log_ptr] = time(NULL); + + va_list args; + va_start(args, format); + vsnprintf(g_logs[g_log_ptr], {{ log_line_size }}, format, args); + va_end(args); + + g_log_cnt++; + g_log_ptr = (g_log_ptr + 1) % {{ log_line_count }}; +} + +void ui_warn_impl(const char *format, ...) { + g_log_warns[g_log_ptr] = true; + g_log_times[g_log_ptr] = time(NULL); + + va_list args; + va_start(args, format); + vsnprintf(g_logs[g_log_ptr], {{ log_line_size }}, format, args); + va_end(args); + + g_log_cnt++; + g_log_ptr = (g_log_ptr + 1) % {{ log_line_count }}; +} + +void ui_clear_log_line(int line) { + assert(line >= 0 && line < LINES); + move(line, {{ pane_width }}); + clrtoeol(); +} + +void ui_print_log_line(unsigned lptr, int line) { + assert(lptr < {{ log_line_count }}); + assert(line >= 0 && line < LINES); + ui_clear_log_line(line); + + // Prints a log entry + if (strlen(g_logs[lptr])) { + struct tm tm = *localtime(&g_log_times[lptr]); + + // Timestamp + ui_field( + line, + {{ pane_width }}, + PAIR_NORMAL, + A_NORMAL, + "%d-%02d-%02d %02d:%02d:%02d", + tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec + ); + + // Level + ui_field( + line, + {{ pane_width }} + 20, + g_log_warns[lptr] ? PAIR_WARN : PAIR_HEADER, + A_NORMAL, + g_log_warns[lptr] ? "WARN:" : "INFO:" + ); + + // Message + ui_field( + line, + {{ pane_width }} + 26, + PAIR_NORMAL, + A_NORMAL, + g_logs[lptr] + ); + } +} + +void ui_print_log(int l) { + l++; + + ui_line(true, l++, PAIR_HEADER, A_BOLD, "LOG"); + ui_ulx_field(l++, "lscr", g_log_scroll); + ui_ulx_field(l++, "lcnt", g_log_cnt); + ui_ulx_field(l++, "lptr", g_log_ptr); + + unsigned lptr = g_log_ptr; + int line = LINES + g_log_scroll; + + while (line) { + lptr = (lptr - 1 + {{ log_line_count }}) % {{ log_line_count }}; + line--; + + if (line < LINES) { + ui_print_log_line(lptr, line); + } + + if (lptr == g_log_ptr) { + break; + } + } + + while (line) { + line--; + ui_clear_log_line(line); + } +} + +// ---------------------------------------------------------------------------- +// Main print function +// ---------------------------------------------------------------------------- void ui_print() { int l = 1; @@ -799,11 +934,17 @@ void ui_print() { case PAGE_IPC: ui_print_ipc(l); break; + case PAGE_LOG: + ui_print_log(l); + break; default: break; } } +// ---------------------------------------------------------------------------- +// Control function +// ---------------------------------------------------------------------------- void ev_vscroll(int ev) { switch (g_page) { case PAGE_PROCESS: @@ -879,6 +1020,28 @@ void ev_vscroll(int ev) { } break; + case PAGE_LOG: + switch (ev) { + case 'W': + g_log_scroll += LINES; + g_log_scroll = g_log_scroll >= {{ log_line_count }} ? {{ log_line_count }} - 1 : g_log_scroll; + break; + case 'S': + g_log_scroll -= g_log_scroll < (uint64_t)LINES ? g_log_scroll : (uint64_t)LINES; + break; + case 'w': + g_log_scroll += 1; + g_log_scroll = g_log_scroll >= {{ log_line_count }} ? {{ log_line_count }} - 1 : g_log_scroll; + break; + case 's': + g_log_scroll -= g_log_scroll ? 1 : 0; + break; + case 'q': + g_log_scroll = 0; + break; + } + + break; default: break; } @@ -1037,7 +1200,7 @@ void ev_handle() { break; case KEY_SLEFT: clear(); - g_core = (g_core - 1) % {{ args.cores }}; + g_core = (g_core + {{ args.cores }} - 1) % {{ args.cores }}; break; case KEY_SRIGHT: clear(); @@ -1045,7 +1208,7 @@ void ev_handle() { break; case KEY_LEFT: clear(); - g_page = (g_page - 1) % PAGE_COUNT; + g_page = (g_page + PAGE_COUNT - 1) % PAGE_COUNT; break; case KEY_RIGHT: clear(); @@ -1134,6 +1297,9 @@ void ev_handle() { } } +// ---------------------------------------------------------------------------- +// Main functions +// ---------------------------------------------------------------------------- void init() { setlocale(LC_ALL, ""); @@ -1148,6 +1314,7 @@ void init() { init_pair(PAIR_NORMAL, COLOR_WHITE, COLOR_BLACK ); init_pair(PAIR_HEADER, COLOR_BLUE, COLOR_BLACK ); + init_pair(PAIR_WARN, COLOR_RED, COLOR_BLACK ); init_pair(PAIR_LIVE_PROC, COLOR_BLUE, COLOR_BLACK ); init_pair(PAIR_SELECTED_PROC, COLOR_YELLOW, COLOR_BLACK ); init_pair(PAIR_FREE_CELL, COLOR_BLACK, COLOR_BLUE ); @@ -1158,6 +1325,10 @@ void init() { init_pair(PAIR_SELECTED_IP, COLOR_BLACK, COLOR_RED ); init_pair(PAIR_SELECTED_SP, COLOR_BLACK, COLOR_MAGENTA); + // Install loggers + g_info = ui_info_impl; + g_warn = ui_warn_impl; + {% if args.command == "new" %} salis_init(); {% elif args.command == "load" %} diff --git a/ui/daemon/ui.j2.c b/ui/daemon/ui.j2.c index 7d92f9d..02df79b 100644 --- a/ui/daemon/ui.j2.c +++ b/ui/daemon/ui.j2.c @@ -8,13 +8,36 @@ volatile bool g_running; uint64_t g_step_block; +void info_impl(const char *restrict fmt, ...) { + assert(fmt); + printf("\033[1;34mINFO:\033[0m "); + + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + + printf("\n"); +} + +void warn_impl(const char *restrict fmt, ...) { + assert(fmt); + printf("\033[1;31mWARN:\033[0m "); + + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + + printf("\n"); +} + void sig_handler(int signo) { - switch (signo) { - case SIGINT: - case SIGTERM: - printf("Signal received, will stop simulator soon...\n"); + (void)signo; + + if (g_running) { + g_warn("Signal received, will stop simulator soon..."); g_running = false; - break; } } @@ -31,10 +54,13 @@ void step_block() { g_step_block >>= 1; } - printf("Simulator running on step '%#lx'\n", g_steps); + g_info("Simulator running on step '%#lx'", g_steps); } int main() { + g_info = info_impl; + g_warn = warn_impl; + {% if args.command == "new" %} salis_init(); {% elif args.command == "load" %} @@ -51,10 +77,10 @@ int main() { step_block(); } - printf("Saving simulation...\n"); + g_info("Saving simulation..."); salis_save("{{ sim_path }}"); salis_free(); - printf("Exiting salis...\n"); + g_info("Exiting salis..."); return 0; } |
