diff options
| -rw-r--r-- | arch/dummy/arch.j2.c | 6 | ||||
| -rw-r--r-- | arch/salis-v1/arch.j2.c | 238 | ||||
| -rw-r--r-- | arch/salis-v1/arch_vars.py | 24 | ||||
| -rw-r--r-- | core.j2.c | 65 | ||||
| -rwxr-xr-x | salis.py | 9 | ||||
| -rw-r--r-- | ui/curses/ui.j2.c | 9 |
6 files changed, 245 insertions, 106 deletions
diff --git a/arch/dummy/arch.j2.c b/arch/dummy/arch.j2.c index c331b34..f51494f 100644 --- a/arch/dummy/arch.j2.c +++ b/arch/dummy/arch.j2.c @@ -28,6 +28,12 @@ void arch_core_init(struct Core *core) { } {% endif %} +void arch_core_free(struct Core *core) { + assert(core); + + (void)core; +} + {% if args.command in ["load", "new"] %} void arch_core_save(FILE *f, const struct Core *core) { assert(f); diff --git a/arch/salis-v1/arch.j2.c b/arch/salis-v1/arch.j2.c index 9c3ea45..269ebe2 100644 --- a/arch/salis-v1/arch.j2.c +++ b/arch/salis-v1/arch.j2.c @@ -30,9 +30,26 @@ void arch_core_init(struct Core *core) { panc->ip = addr_clone; panc->sp = addr_clone; } + + // Prepare event data aggregators + // Allocation event vector + core->aevc = 1; + core->aeva = malloc(sizeof(uint64_t)); + assert(core->aeva); } {% endif %} +void arch_core_free(struct Core *core) { + assert(core); + + // Free event data aggregators + assert(core->aeva); + + free(core->aeva); + + core->aeva = NULL; +} + {% if args.command in ["load", "new"] %} void arch_core_save(FILE *f, const struct Core *core) { assert(f); @@ -47,6 +64,10 @@ void arch_core_save(FILE *f, const struct Core *core) { fwrite(&core->wmb0, sizeof(uint64_t), 1, f); fwrite(&core->wmb1, sizeof(uint64_t), 1, f); fwrite(&core->wdea, sizeof(uint64_t), 1, f); + + fwrite(&core->aevp, sizeof(uint64_t), 1, f); + fwrite(&core->aevc, sizeof(uint64_t), 1, f); + fwrite( core->aeva, sizeof(uint64_t), core->aevp, f); } {% endif %} @@ -64,6 +85,11 @@ void arch_core_load(FILE *f, struct Core *core) { fread(&core->wmb0, sizeof(uint64_t), 1, f); fread(&core->wmb1, sizeof(uint64_t), 1, f); fread(&core->wdea, sizeof(uint64_t), 1, f); + + fread(&core->aevp, sizeof(uint64_t), 1, f); + fread(&core->aevc, sizeof(uint64_t), 1, f); + core->aeva = calloc(core->aevc, sizeof(uint64_t)); + fread( core->aeva, sizeof(uint64_t), core->aevp, f); } {% endif %} @@ -388,6 +414,17 @@ void _alloc(struct Core *core, uint64_t pix, bool fwrd) { // Enlarge child block 1 byte mvec_alloc(core, proc->sp); + // Record allocation event + if (core->aevp == core->aevc) { + core->aeva = realloc(core->aeva, sizeof(uint64_t) * core->aevc * 2); + core->aevc *= 2; + + assert(core->aeva); + } + + core->aeva[core->aevp] = proc->sp; + ++core->aevp; + if (!proc->mb1s || !fwrd) { proc->mb1a = proc->sp; } @@ -832,8 +869,10 @@ const char *arch_mnemonic(uint8_t inst) { void arch_push_data_header() { assert(g_sim_data); - // General trends from all cores will be queried together - const char *trend_sql = ( + // Creates general trend table for all cores + g_info("Generating 'trend' table in SQLite database"); + salis_exec_sql( + 0, NULL, NULL, "create table trend (" "step int not null, " {% for i in range(args.cores) %} @@ -855,30 +894,48 @@ void arch_push_data_header() { ");" ); - g_info("Generating 'trend' table in SQLite database"); - salis_exec_sql(trend_sql); - - // Core-specific instruction data will be queried separately - // A table is created for each core - {% for t in ["pop", "exe", "wrt"] %} - {% for i in range(args.cores) %} - const char *{{ t }}_sql_{{ i }} = ( - "create table {{ t }}_{{ i }} (" - "step int not null, " - // Cycle data allows normalizing against each core's cycle speed - {% for j in range(args.cores) %} - "cycl_{{ j }} int not null, " - {% endfor %} - {% for j in arch_vars.inst_set %} - "inst_{{ j[0]|join(' ') }} int not null{% if not loop.last %},{% endif %} " - {% endfor %} - ");" - ); + // Creates core-specific instruction data tables + const char *iprefs[] = { "pop", "exe", "wrt" }; + int iprefs_cnt = sizeof(iprefs) / sizeof(iprefs[0]); - g_info("Generating '{{ t }}_{{ i }}' table in SQLite database"); - salis_exec_sql({{ t }}_sql_{{ i }}); - {% endfor %} - {% endfor %} + for (int i = 0; i < {{ args.cores }}; ++i) { + for (int j = 0; j < iprefs_cnt; ++j) { + g_info("Generating '%s_%d' table in SQLite database", iprefs[j], i); + salis_exec_sql( + 0, NULL, NULL, + "create table %s_%d (" + "step int not null, " + // Cycle data for all cores allows + // normalizing against each core's cycle speed + {% for i in range(args.cores) %} + "cycl_{{ i }} int not null, " + {% endfor %} + {% for i in arch_vars.inst_set %} + "{{ i[0][0] }} int not null{% if not loop.last %},{% endif %} " + {% endfor %} + ");", + iprefs[j], i + ); + } + } + + // Creates core-specific memory event tables + const char *eprefs[] = { "aev" }; + int eprefs_cnt = sizeof(eprefs) / sizeof(eprefs[0]); + + for (int i = 0; i < {{ args.cores }}; ++i) { + for (int j = 0; j < eprefs_cnt; ++j) { + g_info("Generating '%s_%d' table in SQLite database", eprefs[j], i); + salis_exec_sql( + 0, NULL, NULL, + "create table %s_%d (" + "step int not null, " + "evts blob not null" + ");", + eprefs[j], i + ); + } + } } void arch_push_data_line() { @@ -920,11 +977,10 @@ void arch_push_data_line() { {% endif %} } - // Insert new row - char *trend_sql = NULL; - - asprintf( - &trend_sql, + // Insert row into trend table + g_info("Pushing row to 'trend' table in SQLite database"); + salis_exec_sql( + 0, NULL, NULL, "insert into trend (" "step, " {% for i in range(args.cores) %} @@ -950,7 +1006,7 @@ void arch_push_data_line() { {% endfor %} ");", g_steps, - {% for i in range(args.cores) %} + {% for i in range(args.cores) +%} g_cores[{{ i }}].cycl, g_cores[{{ i }}].mall, g_cores[{{ i }}].pnum, @@ -965,59 +1021,83 @@ void arch_push_data_line() { g_cores[{{ i }}].wmb0, g_cores[{{ i }}].wmb1, g_cores[{{ i }}].wdea{% if not loop.last %},{% endif %} - {% endfor %} + {% endfor +%} ); - g_info("Pushing row to 'trend' table in SQLite database"); - salis_exec_sql(trend_sql); - free(trend_sql); + // Insert row into instruction data tables + const char *iprefs[] = { "pop", "exe", "wrt" }; + int iprefs_cnt = sizeof(iprefs) / sizeof(iprefs[0]); - {% for t in ["pop", "exe", "wrt"] %} - {% for i in range(args.cores) %} + for (int i = 0; i < {{ args.cores }}; ++i) { + for (int j = 0; j < iprefs_cnt; ++j) { + uint64_t *table; + + if (!strcmp("pop", iprefs[j])) { + // Population is generated above, prior to push + table = ipop[i]; + } else if (!strcmp("exe", iprefs[j])) { + table = g_cores[i].iexe; + } else if (!strcmp("wrt", iprefs[j])) { + table = g_cores[i].iwrt; + } - {% if t == "pop" %} - // Reference instruction population vector that was - // manually generated above - {% set table = "ipop[%d]"|format(i) %} - {% else %} - {% set table = "g_cores[%d].i%s"|format(i, t) %} - {% endif %} + g_info("Pushing row to '%s_%d' table in SQLite database", iprefs[j], i); + salis_exec_sql( + 0, NULL, NULL, + "insert into %s_%d (" + "step, " + {% for i in range(args.cores) %} + "cycl_{{ i }}, " + {% endfor %} + {% for i in arch_vars.inst_set %} + "{{ i[0][0] }}{% if not loop.last %},{% endif %} " + {% endfor %} + ") values (" + "%ld, " + {% for _ in range(args.cores) %} + "%ld, " + {% endfor %} + {% for _ in range(arch_vars.inst_count) %} + "%ld{% if not loop.last %},{% endif %} " + {% endfor %} + ");", + iprefs[j], + i, + g_steps, + {% for j in range(args.cores) %} + g_cores[{{ j }}].cycl, + {% endfor %} + {% for j in range(arch_vars.inst_count) %} + table[{{ j }}]{% if not loop.last %},{% endif +%} + {% endfor %} + ); + } + } - char *{{ t }}_sql_{{ i }} = NULL; + // Insert row into memory event tables + const char *eprefs[] = { "aev" }; + int eprefs_cnt = sizeof(eprefs) / sizeof(eprefs[0]); - asprintf( - &{{ t }}_sql_{{ i }}, - "insert into {{ t }}_{{ i }} (" - "step, " - {% for j in range(args.cores) %} - "cycl_{{ j }}, " - {% endfor %} - {% for j in arch_vars.inst_set %} - "inst_{{ j[0]|join(' ') }}{% if not loop.last %},{% endif %} " - {% endfor %} - ") values (" - "%ld, " - {% for _ in range(args.cores) %} - "%ld, " - {% endfor %} - {% for _ in range(arch_vars.inst_count) %} - "%ld{% if not loop.last %},{% endif %} " - {% endfor %} - ");", - g_steps, - {% for j in range(args.cores) %} - g_cores[{{ j }}].cycl, - {% endfor %} - {% for j in range(arch_vars.inst_count) %} - {{ table }}[{{ j }}]{% if not loop.last %},{% endif %} - {% endfor %} - ); + for (int i = 0; i < {{ args.cores }}; ++i) { + for (int j = 0; j < eprefs_cnt; ++j) { + uint64_t ptr; + uint64_t *table; - g_info("Pushing row to '{{ t }}_{{ i }}' table in SQLite database"); - salis_exec_sql({{ t }}_sql_{{ i }}); - free({{ t }}_sql_{{ i }}); - {% endfor %} - {% endfor %} + if (!strcmp("aev", eprefs[j])) { + ptr = g_cores[i].aevp; + table = g_cores[i].aeva; + } + + int blob_size = ptr * sizeof(uint64_t); + + g_info("Pushing row to '%s_%d' table in SQLite database", eprefs[j], i); + salis_exec_sql( + 1, (const void **)&table, &blob_size, + "insert into %s_%d (step, evts) values (%ld, ?);", + eprefs[j], i, g_steps + ); + } + } // Reset all data aggregation core fields to zero for (int i = 0; i < {{ args.cores }}; ++i) { @@ -1033,6 +1113,10 @@ void arch_push_data_line() { core->wmb0 = 0; core->wmb1 = 0; core->wdea = 0; + + // Event vectors + memset(core->aeva, 0, sizeof(uint64_t) * core->aevp); + core->aevp = 0; } } {% endif %} diff --git a/arch/salis-v1/arch_vars.py b/arch/salis-v1/arch_vars.py index f469ad6..8bf40a7 100644 --- a/arch/salis-v1/arch_vars.py +++ b/arch/salis-v1/arch_vars.py @@ -107,13 +107,19 @@ inst_count = len(inst_set) # Extra fields used exclusively for data aggregation core_fields = [ - ("uint64_t", f"iexe[{inst_count}]"), - ("uint64_t", f"iwrt[{inst_count}]"), - ("uint64_t", "emb0"), - ("uint64_t", "emb1"), - ("uint64_t", "eliv"), - ("uint64_t", "edea"), - ("uint64_t", "wmb0"), - ("uint64_t", "wmb1"), - ("uint64_t", "wdea"), + ("uint64_t", f"iexe[{inst_count}]", False), + ("uint64_t", f"iwrt[{inst_count}]", False), + ("uint64_t", "emb0", True), + ("uint64_t", "emb1", True), + ("uint64_t", "eliv", True), + ("uint64_t", "edea", True), + ("uint64_t", "wmb0", True), + ("uint64_t", "wmb1", True), + ("uint64_t", "wdea", True), + + # Event data aggregators + # Allocation event vector + ("uint64_t", "aevp", True), + ("uint64_t", "aevc", True), + ("uint64_t *", "aeva", False), ] @@ -39,7 +39,7 @@ struct Core { uint8_t *iviv; // Architecture specific custom fields - {% for type, val in arch_vars.core_fields %} + {% for type, val, _ in arch_vars.core_fields %} {{ type }} {{ val }}; {% endfor %} @@ -73,6 +73,8 @@ void (*g_warn)(const char *fmt, ...); void arch_core_init(struct Core *core); {% endif %} +void arch_core_free(struct Core *core); + {% if args.command in ["load", "new"] %} void arch_core_save(FILE *f, const struct Core *core); {% endif %} @@ -610,27 +612,58 @@ void salis_auto_save() { {% endif %} {% if data_push_path is defined %} -void salis_exec_sql(const char *sql) { - assert(sql); +void salis_exec_sql(int blob_cnt, const void **blobs, const int *blob_sizes, const char *sql_format, ...) { + assert(sql_format); + + va_list args; + va_start(args, sql_format); + int sql_len = vsnprintf(NULL, 0, sql_format, args) + 1; + char *sql_str = malloc(sql_len); + assert(sql_str); + va_end(args); + + va_start(args, sql_format); + vsprintf(sql_str, sql_format, args); + va_end(args); + + // Prepare statement + int sql_res; + sqlite3_stmt *sql_stmt; + + sql_res = sqlite3_prepare_v2(g_sim_data, sql_str, -1, &sql_stmt, NULL); + assert(sql_res == SQLITE_OK); + free(sql_str); + + // Caller may pass multiple binary blobs to the query + for (int i = 0; i < blob_cnt; ++i) { + assert(blobs[i]); + sql_res = sqlite3_bind_blob(sql_stmt, i + 1, blobs[i], blob_sizes[i], SQLITE_STATIC); + assert(sql_res == SQLITE_OK); + } - int sql_res; - char *sql_err; + // Execute the statement + // Only handle SQLITE_BUSY error, in which case we retry the query. + // In principle, setting 'journal_mode=wal;' should help prevent busy database errors. + while (true) { + sql_res = sqlite3_step(sql_stmt); + + if (sql_res == SQLITE_DONE || sql_res == SQLITE_ROW) { + break; + } - while ((sql_res = sqlite3_exec(g_sim_data, sql, NULL, NULL, &sql_err)) == SQLITE_BUSY) { g_warn("SQLite database returned error '%d' with message:", sql_res); - g_warn(sql_err); - sqlite3_free(sql_err); + g_warn(sqlite3_errmsg(g_sim_data)); - switch (sql_res) { - case SQLITE_BUSY: - // Only handle busy database errors + if (sql_res == SQLITE_BUSY) { g_info("Will retry query..."); continue; - default: - // Application should fail on all other error conditions - assert(false); } + + // Fail on unhandled error + assert(false); } + + sqlite3_finalize(sql_stmt); } {% endif %} @@ -659,7 +692,7 @@ void salis_init() { // Enable Write-Ahead Logging (WAL) // This seems to help prevent DB locks when displaying live data // See: https://sqlite.org/wal.html - salis_exec_sql("pragma journal_mode=wal;"); + salis_exec_sql(0, NULL, NULL, "pragma journal_mode=wal;"); arch_push_data_header(); arch_push_data_line(); @@ -871,6 +904,8 @@ void salis_free() { {% endif %} for (int i = 0; i < {{ args.cores }}; ++i) { + arch_core_free(&g_cores[i]); + assert(g_cores[i].pvec); assert(g_cores[i].iviv); assert(g_cores[i].ivav); @@ -343,12 +343,11 @@ build_cmd.extend(["-O3", "-DNDEBUG"] if args.optimized else ["-ggdb"]) if args.command in ["load", "new"]: build_cmd.extend(ui_vars.flags) - # Enable POSIX extensions (open_memstream) + # Enable POSIX extensions (open_memstream) if compression is enabled + # This makes it easy to generate compressed data arrays for lzip using + # C's native FILE interface. build_cmd.extend(["-lz", "-D_POSIX_C_SOURCE=200809L"] if args.compress else []) - - # Enable GNU extensions (asprintf) - # This allows managing large SQL strings more easily - build_cmd.extend(["-lsqlite3", "-D_GNU_SOURCE"] if args.data_push_pow != 0 else []) + build_cmd.extend(["-lsqlite3"] if args.data_push_pow != 0 else []) info("Using build command:", " ".join(build_cmd)) subprocess.run(build_cmd, check=True) diff --git a/ui/curses/ui.j2.c b/ui/curses/ui.j2.c index ddeaf3b..850a3f5 100644 --- a/ui/curses/ui.j2.c +++ b/ui/curses/ui.j2.c @@ -416,6 +416,15 @@ void ui_print_core(int l) { ui_ulx_field(++l, "pcur", g_cores[g_core].pcur); ui_ulx_field(++l, "psli", g_cores[g_core].psli); ui_ulx_field(++l, "ivpt", g_cores[g_core].ivpt); + + ++l; + + ui_line(false, ++l, PAIR_HEADER, A_BOLD, "ARCH SPECIFIC"); + {% for type, name, print in arch_vars.core_fields if print %} + {% if type == "uint64_t" %} + ui_ulx_field(++l, "{{ name }}", g_cores[g_core].{{ name }}); + {% endif %} + {% endfor %} } // ---------------------------------------------------------------------------- |
