aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/v1/arch_plots.cpp18
-rw-r--r--core/sql.c4
-rw-r--r--data/client.cpp596
-rw-r--r--data/server.c89
-rwxr-xr-xsalis.py4
5 files changed, 490 insertions, 221 deletions
diff --git a/arch/v1/arch_plots.cpp b/arch/v1/arch_plots.cpp
new file mode 100644
index 0000000..fb49816
--- /dev/null
+++ b/arch/v1/arch_plots.cpp
@@ -0,0 +1,18 @@
+std::array g_arch_traces = std::to_array<const char *>({
+#define FOR_CORE(i) \
+ "wmb0_" #i, \
+ "wmb1_" #i, \
+ "wdea_" #i,
+ FOR_CORES
+#undef FOR_CORE
+});
+
+std::array g_arch_plots = std::to_array<Plot>({
+ {"wevs", "general", {
+#define FOR_CORE(i) "wmb0_" #i, "wmb1_" #i, "wdea_" #i,
+ FOR_CORES
+#undef FOR_CORE
+ }},
+});
+
+#define ARCH_PLOT_COUNT g_arch_plots.size()
diff --git a/core/sql.c b/core/sql.c
index b1c0c2a..cc59726 100644
--- a/core/sql.c
+++ b/core/sql.c
@@ -2,7 +2,7 @@
sqlite3 *g_sim_db;
-void sql_exec(int blob_cnt, const void **blobs, const int *blob_sizes, void (*callback)(void *data), void *data, const char *sql_format, ...) {
+void sql_exec(int blob_cnt, const void **blobs, const int *blob_sizes, void (*callback)(sqlite3_stmt *sql_stmt, void *data), void *data, const char *sql_format, ...) {
assert(sql_format);
va_list args;
@@ -34,7 +34,7 @@ void sql_exec(int blob_cnt, const void **blobs, const int *blob_sizes, void (*ca
if (sql_res == SQLITE_ROW) {
if (callback) {
- callback(data);
+ callback(sql_stmt, data);
}
continue;
diff --git a/data/client.cpp b/data/client.cpp
index 730ea7a..227ebaa 100644
--- a/data/client.cpp
+++ b/data/client.cpp
@@ -1,189 +1,249 @@
#include <arpa/inet.h>
#include <GLFW/glfw3.h>
-#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <implot.h>
#include <json-c/json.h>
-#include <limits.h>
#include <math.h>
#include <signal.h>
#include <threads.h>
-#include <initializer_list>
+#include <algorithm>
+#include <array>
+#include <map>
+#include <tuple>
+#include <vector>
#include "logger.c"
-#define FETCH_INTERVAL 10
-#define FETCH_INTERVAL_SUBDIV 1000
-
-#define COLOR_BG ImVec4(0.f, 0.f, 0.f, 1.f)
+// ----------------------------------------------------------------------------
+// Defines
+// ----------------------------------------------------------------------------
+#define COLOR_BLACK ImVec4(0.f, 0.f, 0.f, 1.f)
#define FONT_SIZE 12.f
#define FONT_SOURCE "/usr/share/fonts/droid/DroidSansMono.ttf"
#define GLSL_VERSION "#version 130"
-#define WINDOW_STYLE (ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings)
+#define PLOT_MAX_COLS 8
+#define PLOT_MIN_COLS 1
+#define PLOT_SCROLL_OFFSET 28.f
-#define DEFAULT_ENTRIES 2000
-#define DEFAULT_NTH 1
-#define DEFAULT_X_AXIS 0
-#define DEFAULT_X_LOW 0
-#define DEFAULT_X_HIGH INT_MAX
-#define DEFAULT_HM_LEFT 0
-#define DEFAULT_HM_PIXEL_COUNT 0x400
+#define IMGUI_WINDOW_FLAGS ( \
+ ImGuiWindowFlags_NoBackground | \
+ ImGuiWindowFlags_NoDecoration | \
+ ImGuiWindowFlags_NoMove | \
+ ImGuiWindowFlags_NoSavedSettings \
+)
-#define PLOT_MIN_COLS 1
-#define PLOT_MAX_COLS 8
+#define DATA_FETCH_INTERVAL 10
+#define DATA_FETCH_INTERVAL_SUBDIV 1000
+
+#define DEFVAL_ENTRIES 0x800l
+#define DEFVAL_NTH 1l
+#define DEFVAL_X_AXIS 0
+#define DEFVAL_X_LOW 0l
+#define DEFVAL_X_HIGH INT64_MAX
+#define DEFVAL_HM_LEFT 0l
+#define DEFVAL_HM_PIXEL_COUNT 0x400l
+// ----------------------------------------------------------------------------
+// Declarations
+// ----------------------------------------------------------------------------
enum Status {
STATUS_STOPPED,
STATUS_RUNNING,
STATUS_STOPPING,
};
-struct Plot {
- Plot(const char *name, const char *section, std::initializer_list<const char *> cols)
- : name(name), section(section), cols(cols.begin()) {}
+struct CompString {
+ bool operator()(const char *a, const char *b) const {
+ return strcmp(a, b) < 0;
+ }
+};
+struct Plot {
const char *name;
const char *section;
- const char *const *cols;
+ std::vector<const char *> traces;
};
-// Window globals
-GLFWwindow *g_window;
-ImGuiIO *g_io;
-ImGuiStyle *g_imgui_style;
-ImPlotStyle *g_implot_style;
+// ----------------------------------------------------------------------------
+// Plots
+// ----------------------------------------------------------------------------
+#include "arch_plots.cpp"
-// Data globals
-const char *g_x_axes[] = {
+template <class T, size_t N, size_t M>
+std::array<T, N + M> merge_array(const std::array<T, N> &first, const std::array<T, M> &second) {
+ std::array<T, N + M> result{};
+ for (size_t i = 0; i < N; i++) result[i] = first[i];
+ for (size_t i = 0; i < M; i++) result[N + i] = second[i];
+ return result;
+}
+
+std::array g_core_traces = std::to_array<const char *>({
+ "rowid",
"step",
-#define FOR_CORE(i) "cycl_" #i,
+#define FOR_CORE(i) \
+ "cycl_" #i, \
+ "mall_" #i, \
+ "pnum_" #i, \
+ "pfst_" #i, \
+ "plst_" #i, \
+ "amb0_" #i, \
+ "amb1_" #i, \
+ "emb0_" #i, \
+ "emb1_" #i, \
+ "eliv_" #i, \
+ "edea_" #i,
FOR_CORES
#undef FOR_CORE
-};
+});
-int g_status;
-int g_entries = DEFAULT_ENTRIES;
-int g_nth = DEFAULT_NTH;
-int g_x_axis = DEFAULT_X_AXIS;
-int g_x_low = DEFAULT_X_LOW;
-int g_x_high = DEFAULT_X_HIGH;
-int g_hm_left = DEFAULT_HM_LEFT;
-int g_hm_pixel_count = DEFAULT_HM_PIXEL_COUNT;
-int g_hm_pixel_pow; // calculate on startup
-int g_x_current;
-thrd_t g_data_fetching_thread;
-
-// Plot globals
-Plot g_plots[] = {
- Plot("cycl", "general", {
+std::array g_core_plots = std::to_array<Plot>({
+ {"cycl", "general", {
#define FOR_CORE(i) "cycl_" #i,
FOR_CORES
#undef FOR_CORE
- nullptr,
- }),
- Plot("mall", "general", {
+ }},
+ {"mall", "general", {
#define FOR_CORE(i) "mall_" #i,
FOR_CORES
#undef FOR_CORE
- nullptr,
- }),
- Plot("pnum", "general", {
+ }},
+ {"pnum", "general", {
#define FOR_CORE(i) "pnum_" #i,
FOR_CORES
#undef FOR_CORE
- nullptr,
- }),
- Plot("ppop", "general", {
+ }},
+ {"ppop", "general", {
#define FOR_CORE(i) "pfst_" #i, "plst_" #i,
FOR_CORES
#undef FOR_CORE
- nullptr,
- }),
- Plot("ambs", "general", {
+ }},
+ {"ambs", "general", {
#define FOR_CORE(i) "amb0_" #i, "amb1_" #i,
FOR_CORES
#undef FOR_CORE
- nullptr,
- }),
- Plot("eevs", "general", {
+ }},
+ {"eevs", "general", {
#define FOR_CORE(i) "emb0_" #i, "emb1_" #i, "eliv_" #i, "edea_" #i,
FOR_CORES
#undef FOR_CORE
- nullptr,
- }),
+ }},
+});
+
+std::map<const char *, std::vector<ImS64>, CompString> g_trace_map;
+std::array g_traces = merge_array(g_core_traces, g_arch_traces);
+std::array g_plots = merge_array(g_core_plots, g_arch_plots);
+
+#define TRACE_COUNT g_traces.size()
+#define PLOT_COUNT g_plots.size()
+
+// ----------------------------------------------------------------------------
+// Globals
+// ----------------------------------------------------------------------------
+GLFWwindow *g_window;
+ImGuiIO *g_imgui_io;
+ImGuiStyle *g_imgui_style;
+ImPlotStyle *g_implot_style;
+
+const char *g_x_axes[] = {
+ "rowid",
+ "step",
+#define FOR_CORE(i) "cycl_" #i,
+ FOR_CORES
+#undef FOR_CORE
};
-#define PLOT_COUNT (int)(sizeof(g_plots) / sizeof(g_plots[0]))
+int g_status;
+int g_x_axis = DEFVAL_X_AXIS;
+int64_t g_entries = DEFVAL_ENTRIES;
+int64_t g_nth = DEFVAL_NTH;
+int64_t g_x_low = DEFVAL_X_LOW;
+int64_t g_x_high = DEFVAL_X_HIGH;
+int64_t g_hm_left = DEFVAL_HM_LEFT;
+int64_t g_hm_pixel_count = DEFVAL_HM_PIXEL_COUNT;
+int64_t g_hm_pixel_pow; // calculate on startup
+int64_t g_x_current = -1l;
+
+thrd_t g_fetching_thread;
+mtx_t g_fetching_mutex;
+int g_trace_len;
+int g_trace_offset;
// Layout globals
-float g_data_col_width;
bool g_data_col_visible = true;
+bool g_plot_maximized;
+bool g_plot_scroll;
+float g_plot_scroll_current;
+float g_plot_scroll_to;
+float g_data_col_width;
+float g_plot_cells_top[PLOT_MAX_COLS][PLOT_COUNT];
+float g_plot_cells_bottom[PLOT_MAX_COLS][PLOT_COUNT];
+Plot *g_plot_cells[PLOT_MAX_COLS][PLOT_COUNT];
+Plot *g_plot_selected = &g_plots[0];
int g_plot_cols = 2;
int g_plot_col_selected;
int g_plot_row_selected;
-Plot *g_plot_cells[PLOT_MAX_COLS][PLOT_COUNT];
-Plot *g_plot_selected = &g_plots[0];
-bool g_plot_maximized;
// ----------------------------------------------------------------------------
// Data functions
// ----------------------------------------------------------------------------
-int data_calc_max_hm_pixel_pow(void) {
- return (int)floor(log2((float)(MVEC_SIZE - g_hm_left) / (float)g_hm_pixel_count));
+int64_t data_max_hm_pixel_pow(void) {
+ return (int64_t)floor(log2((double)(MVEC_SIZE - g_hm_left) / (double)g_hm_pixel_count));
}
-void data_clamp(int *field, int low, int high) {
- assert(field);
- if (*field < low) *field = low;
- if (*field > high) *field = high;
-}
-
-void data_touched(void) {
- data_clamp(&g_entries, 1, DEFAULT_ENTRIES);
- data_clamp(&g_nth, DEFAULT_NTH, INT_MAX);
- data_clamp(&g_x_low, DEFAULT_X_LOW, INT_MAX);
- data_clamp(&g_x_high, g_x_low + 1, DEFAULT_X_HIGH);
+void data_on_field_change(void) {
+ g_entries = std::clamp(g_entries, 1l, DEFVAL_ENTRIES);
+ g_nth = std::clamp(g_nth, DEFVAL_NTH, INT64_MAX);
+ g_x_low = std::clamp(g_x_low, DEFVAL_X_LOW, INT64_MAX);
+ g_x_high = std::clamp(g_x_high, g_x_low + 1l, DEFVAL_X_HIGH);
#if !defined(MVEC_LOOP)
- data_clamp(&g_hm_left, DEFAULT_HM_LEFT, MVEC_SIZE);
+ g_hm_left = std::clamp(g_hm_left, DEFVAL_HM_LEFT, (int64_t)MVEC_SIZE);
#endif
- data_clamp(&g_hm_pixel_count, 1, DEFAULT_HM_PIXEL_COUNT);
- data_clamp(&g_hm_pixel_pow, 0, data_calc_max_hm_pixel_pow());
- g_x_current = 0;
+ g_hm_pixel_count = std::clamp(g_hm_pixel_count, 1l, DEFVAL_HM_PIXEL_COUNT);
+ g_hm_pixel_pow = std::clamp(g_hm_pixel_pow, 0l, data_max_hm_pixel_pow());
+
+ g_x_current = -1l;
+
+ for (auto &trace : g_trace_map) {
+ trace.second.clear();
+ }
}
-void data_reset_values(void) {
- g_entries = DEFAULT_ENTRIES;
- g_nth = DEFAULT_NTH;
- g_x_axis = DEFAULT_X_AXIS;
- g_x_low = DEFAULT_X_LOW;
- g_x_high = DEFAULT_X_HIGH;
- g_hm_left = DEFAULT_HM_LEFT;
- g_hm_pixel_count = DEFAULT_HM_PIXEL_COUNT;
- g_hm_pixel_pow = data_calc_max_hm_pixel_pow();
+void data_reset_fields(void) {
+ g_entries = DEFVAL_ENTRIES;
+ g_nth = DEFVAL_NTH;
+ g_x_axis = DEFVAL_X_AXIS;
+ g_x_low = DEFVAL_X_LOW;
+ g_x_high = DEFVAL_X_HIGH;
+ g_hm_left = DEFVAL_HM_LEFT;
+ g_hm_pixel_count = DEFVAL_HM_PIXEL_COUNT;
+ g_hm_pixel_pow = data_max_hm_pixel_pow();
+
+ g_trace_len = 0;
+ g_trace_offset = 0;
+
+ data_on_field_change();
}
void data_reset_plot_cells(void) {
- for (int i = 0; i < PLOT_MAX_COLS; i++) {
- for (int j = 0; j < PLOT_COUNT; j++) {
- g_plot_cells[i][j] = nullptr;
- }
- }
+ memset(g_plot_cells, 0, PLOT_MAX_COLS * PLOT_COUNT * sizeof(nullptr));
+ memset(g_plot_cells_top, 0, PLOT_MAX_COLS * PLOT_COUNT * sizeof(float));
+ memset(g_plot_cells_bottom, 0, PLOT_MAX_COLS * PLOT_COUNT * sizeof(float));
}
void data_fetch(void) {
struct json_object *request = json_object_new_object();
json_object_object_add(request, "request", json_object_new_string("data"));
- json_object_object_add(request, "entries", json_object_new_int(g_entries));
- json_object_object_add(request, "nth", json_object_new_int(g_nth));
+ json_object_object_add(request, "entries", json_object_new_int64(g_entries));
+ json_object_object_add(request, "nth", json_object_new_int64(g_nth));
json_object_object_add(request, "x-axis", json_object_new_string(g_x_axes[g_x_axis]));
- json_object_object_add(request, "x-low", json_object_new_int(g_x_low));
- json_object_object_add(request, "x-high", json_object_new_int(g_x_high));
- json_object_object_add(request, "hm-left", json_object_new_int(g_hm_left));
- json_object_object_add(request, "hm-pixel-count", json_object_new_int(g_hm_pixel_count));
- json_object_object_add(request, "hm-pixel-pow", json_object_new_int(g_hm_pixel_pow));
- json_object_object_add(request, "x-current", json_object_new_int(g_x_current));
+ json_object_object_add(request, "x-low", json_object_new_int64(g_x_low));
+ json_object_object_add(request, "x-high", json_object_new_int64(g_x_high));
+ json_object_object_add(request, "hm-left", json_object_new_int64(g_hm_left));
+ json_object_object_add(request, "hm-pixel-count", json_object_new_int64(g_hm_pixel_count));
+ json_object_object_add(request, "hm-pixel-pow", json_object_new_int64(g_hm_pixel_pow));
+ json_object_object_add(request, "x-current", json_object_new_int64(g_x_current));
const char *request_str = json_object_to_json_string(request);
log_info("Sending request to server: %s", request_str);
@@ -201,20 +261,52 @@ void data_fetch(void) {
const char *response_str = json_object_to_json_string(response);
log_info("Server responded with: %s", response_str);
+ mtx_lock(&g_fetching_mutex);
+
+ json_object_object_foreach(response, key, value) {
+ if (g_trace_map.contains(key)) {
+ for (size_t i = 0; i < json_object_array_length(value); i++) {
+ ImS64 point = json_object_get_int64(json_object_array_get_idx(value, i));
+ g_trace_map.at(key).push_back(point);
+ }
+ }
+ }
+
+ g_x_current = g_trace_map.at(g_x_axes[g_x_axis]).back();
json_object_put(request);
json_object_put(response);
+
+ g_trace_len = g_trace_map.at("rowid").size();
+
+ if (g_trace_len >= g_entries * 2) {
+ for (auto &trace : g_trace_map) {
+ trace.second.erase(trace.second.begin(), trace.second.end() - g_entries);
+ }
+
+ g_trace_len = g_trace_map.at("rowid").size();
+ }
+
+ g_trace_offset = 0;
+
+ if (g_trace_len > g_entries) {
+ g_trace_offset = g_trace_len - g_entries;
+ g_trace_len = g_entries;
+ }
+
+ mtx_unlock(&g_fetching_mutex);
}
int data_fetching_thread(void *data) {
(void)data;
+ assert(!data);
assert(g_status == STATUS_RUNNING || g_status == STATUS_STOPPING);
while (g_status == STATUS_RUNNING) {
data_fetch();
- for (int i = 0; i < FETCH_INTERVAL_SUBDIV && g_status == STATUS_RUNNING; i++) {
- usleep((FETCH_INTERVAL * 1000000) / FETCH_INTERVAL_SUBDIV);
+ for (int i = 0; i < DATA_FETCH_INTERVAL_SUBDIV && g_status == STATUS_RUNNING; i++) {
+ usleep((DATA_FETCH_INTERVAL * 1000000) / DATA_FETCH_INTERVAL_SUBDIV);
}
}
@@ -226,27 +318,36 @@ int data_fetching_thread(void *data) {
void data_start_fetching(void) {
log_info("Starting data fetching thread");
g_status = STATUS_RUNNING;
- thrd_create(&g_data_fetching_thread, (thrd_start_t)data_fetching_thread, nullptr);
+ thrd_create(&g_fetching_thread, (thrd_start_t)data_fetching_thread, nullptr);
}
void data_stop_fetching(void) {
assert(g_status == STATUS_RUNNING);
log_info("Stopping data fetching thread");
g_status = STATUS_STOPPING;
- thrd_join(g_data_fetching_thread, nullptr);
+ thrd_join(g_fetching_thread, nullptr);
}
// ----------------------------------------------------------------------------
// GUI functions
// ----------------------------------------------------------------------------
-void gui_print_data_col(void) {
+void gui_render_data_input(const char *label, int64_t *target) {
+ assert(label);
+ assert(target);
+
+ if (ImGui::InputScalar(label, ImGuiDataType_U64, target, nullptr, nullptr, "%#lx")) {
+ data_on_field_change();
+ }
+}
+
+void gui_render_data_col(void) {
const ImGuiViewport *viewport = ImGui::GetMainViewport();
- const ImVec2 next_win_pos = viewport->Pos;
- const ImVec2 next_win_size = ImVec2(-1.f, viewport->Size.y);
+ const ImVec2 win_pos = viewport->Pos;
+ const ImVec2 win_size = ImVec2(-1.f, viewport->Size.y);
- ImGui::SetNextWindowPos(next_win_pos);
- ImGui::SetNextWindowSize(next_win_size);
- ImGui::Begin("data-col", nullptr, WINDOW_STYLE);
+ ImGui::SetNextWindowPos(win_pos);
+ ImGui::SetNextWindowSize(win_size);
+ ImGui::Begin("data-col", nullptr, IMGUI_WINDOW_FLAGS);
g_data_col_width = ImGui::GetWindowWidth();
ImGui::SeparatorText("SALIS data client");
@@ -267,81 +368,127 @@ void gui_print_data_col(void) {
switch (g_status) {
case STATUS_STOPPED:
- if (ImGui::InputInt("entries", &g_entries, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched();
- if (ImGui::InputInt("nth", &g_nth, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched();
+ gui_render_data_input("entries", &g_entries);
+ gui_render_data_input("nth", &g_nth);
if (ImGui::BeginCombo("x-axis", g_x_axes[g_x_axis])) {
- for (int i = 0; i < CORES + 1; i++) {
+ for (int i = 0; i < CORES + 2; i++) {
if (ImGui::Selectable(g_x_axes[i], g_x_axis == i)) {
+ data_reset_fields();
g_x_axis = i;
- data_touched();
}
}
ImGui::EndCombo();
}
- if (ImGui::InputInt("x-low", &g_x_low, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched();
- if (ImGui::InputInt("x-high", &g_x_high, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched();
- if (ImGui::InputInt("hm-left", &g_hm_left, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched();
- if (ImGui::InputInt("hm-pxl-count", &g_hm_pixel_count, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched();
- if (ImGui::InputInt("hm-pxl-pow", &g_hm_pixel_pow, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched();
- if (ImGui::Button("Run", ImVec2(-1.f, 0.f))) data_start_fetching();
- if (ImGui::Button("Reset", ImVec2(-1.f, 0.f))) data_reset_values();
-
+ gui_render_data_input("x-low", &g_x_low);
+ gui_render_data_input("x-high", &g_x_high);
+ gui_render_data_input("hm-left", &g_hm_left);
+ gui_render_data_input("hm-pxl-count", &g_hm_pixel_count);
+ gui_render_data_input("hm-pxl-pow", &g_hm_pixel_pow);
break;
case STATUS_RUNNING:
case STATUS_STOPPING:
- ImGui::LabelText("entries", "%d", g_entries);
- ImGui::LabelText("nth", "%d", g_nth);
+ ImGui::LabelText("entries", "%#lx", g_entries);
+ ImGui::LabelText("nth", "%#lx", g_nth);
ImGui::LabelText("x-axis", "%s", g_x_axes[g_x_axis]);
- ImGui::LabelText("x-low", "%d", g_x_low);
- ImGui::LabelText("x-high", "%d", g_x_high);
- ImGui::LabelText("hm-left", "%d", g_hm_left);
- ImGui::LabelText("hm-pxl-count", "%d", g_hm_pixel_count);
- ImGui::LabelText("hm-pxl-pow", "%d", g_hm_pixel_pow);
+ ImGui::LabelText("x-low", "%#lx", g_x_low);
+ ImGui::LabelText("x-high", "%#lx", g_x_high);
+ ImGui::LabelText("hm-left", "%#lx", g_hm_left);
+ ImGui::LabelText("hm-pxl-count", "%#lx", g_hm_pixel_count);
+ ImGui::LabelText("hm-pxl-pow", "%#lx", g_hm_pixel_pow);
+ }
+
+ switch (g_status) {
+ case STATUS_STOPPED:
+ if (ImGui::Button("Run", ImVec2(-1.f, 0.f))) {
+ data_start_fetching();
+ }
- if (g_status == STATUS_RUNNING) {
- if (ImGui::Button("Stop", ImVec2(-1.f, 0.f))) data_stop_fetching();
- ImGui::LabelText("##", "Running");
- } else {
- ImGui::LabelText("##", "Stopping");
+ if (ImGui::Button("Reset", ImVec2(-1.f, 0.f))) {
+ data_reset_fields();
}
+
+ break;
+ case STATUS_RUNNING:
+ if (ImGui::Button("Stop", ImVec2(-1.f, 0.f))) {
+ data_stop_fetching();
+ }
+
+ ImGui::LabelText("##", "Running");
+ break;
+ case STATUS_STOPPING:
+ ImGui::LabelText("##", "Stopping");
+ break;
}
ImGui::SeparatorText("Layout");
ImGui::SliderInt("cols", &g_plot_cols, PLOT_MIN_COLS, PLOT_MAX_COLS);
-
ImGui::End();
}
-void gui_print_plots(void) {
+void gui_render_plot(const Plot *plot, const ImVec2 &frame_size=ImVec2(-1.f, 0.f)) {
+ int plot_flags = ImPlotFlags_NoMenus;
+ int axis_flags = ImPlotAxisFlags_NoMenus;
+
+ if (g_status != STATUS_STOPPED) {
+ ImPlot::SetNextAxesToFit();
+ plot_flags |= ImPlotFlags_NoInputs | ImPlotFlags_NoMouseText;
+ axis_flags |= ImPlotAxisFlags_NoHighlight;
+ }
+
+ if (ImPlot::BeginPlot(plot->name, frame_size, plot_flags)) {
+ ImPlot::SetupAxes(nullptr, nullptr, axis_flags, axis_flags);
+ ImS64 *x = g_trace_map.at(g_x_axes[g_x_axis]).data() + g_trace_offset;
+
+ for (auto &trace : plot->traces) {
+ ImS64 *y = g_trace_map.at(trace).data() + g_trace_offset;
+ ImPlot::PlotLine(trace, x, y, g_trace_len);
+ }
+
+ ImPlot::EndPlot();
+ }
+}
+
+void gui_render_plots(void) {
const char *section_current = g_plots[0].section;
const char *section_next = nullptr;
bool plots_covered[PLOT_COUNT] = { 0 };
const ImGuiViewport *viewport = ImGui::GetMainViewport();
- const ImVec2 next_win_pos = g_data_col_visible ? ImVec2(g_data_col_width, viewport->Pos.y) : viewport->Pos;
- const ImVec2 next_win_size = g_data_col_visible ? ImVec2(viewport->Size.x - g_data_col_width, -1.f) : ImVec2(viewport->Size.x, -1.f);
+ const ImVec2 win_pos = g_data_col_visible ? ImVec2(g_data_col_width, viewport->Pos.y) : viewport->Pos;
+ const ImVec2 win_size = g_data_col_visible ? ImVec2(viewport->Size.x - g_data_col_width, -1.f) : ImVec2(viewport->Size.x, -1.f);
- ImGui::SetNextWindowPos(next_win_pos);
- ImGui::SetNextWindowSize(next_win_size);
- ImGui::Begin("plots", nullptr, WINDOW_STYLE);
+ if (g_plot_scroll) {
+ ImGui::SetNextWindowScroll(ImVec2(-1.f, g_plot_scroll_to));
+ g_plot_scroll = false;
+ g_plot_scroll_to = 0.f;
+ }
+
+ ImGui::SetNextWindowPos(win_pos);
+ ImGui::SetNextWindowSize(win_size);
+ ImGui::Begin("plots", nullptr, IMGUI_WINDOW_FLAGS);
+ g_plot_scroll_current = ImGui::GetScrollY();
int col = 0;
int row = 0;
+ mtx_lock(&g_fetching_mutex);
+
while (section_current) {
ImGui::SeparatorText(section_current);
ImGui::BeginTable("plots-table", g_plot_cols);
- for (int i = 0; i < PLOT_COUNT; i++) {
+ for (size_t i = 0; i < PLOT_COUNT; i++) {
if (g_plots[i].section != section_current) {
section_next = (!section_next && !plots_covered[i]) ? g_plots[i].section : section_next;
continue;
}
ImGui::TableNextColumn();
+ g_plot_cells[col][row] = &g_plots[i];
+ g_plot_cells_top[col][row] = ImGui::GetCursorPosY();
if (&g_plots[i] == g_plot_selected) {
g_plot_col_selected = col;
@@ -349,102 +496,106 @@ void gui_print_plots(void) {
g_implot_style->Colors[ImPlotCol_FrameBg] = g_imgui_style->Colors[ImGuiCol_FrameBg];
}
- if (ImPlot::BeginPlot(g_plots[i].name)) {
- //int test_x[] = {0,1,2,3};
- //int test_y1[] = {1,2,3,4};
- //int test_y2[] = {2,4,8,16};
- //ImPlot::PlotLine("test1", test_x, test_y1, 4);
- //ImPlot::PlotLine("test2", test_x, test_y2, 4);
- ImPlot::EndPlot();
- }
+ gui_render_plot(&g_plots[i]);
- g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BG;
+ if (&g_plots[i] == g_plot_selected) {
+ g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BLACK;
+ }
- g_plot_cells[col][row] = &g_plots[i];
+ g_plot_cells_bottom[col][row] = ImGui::GetCursorPosY();
col = (col + 1) % g_plot_cols;
row += col ? 0 : 1;
-
plots_covered[i] = true;
}
section_current = section_next;
section_next = nullptr;
ImGui::EndTable();
-
row += col ? 1 : 0;
col = 0;
}
+ mtx_unlock(&g_fetching_mutex);
+
ImGui::End();
}
-void gui_print_plot_maximized(void) {
+void gui_render_plot_maximized(void) {
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(viewport->Size);
- ImGui::Begin("plot-fullscreen", nullptr, WINDOW_STYLE);
+ ImGui::Begin("plot-fullscreen", nullptr, IMGUI_WINDOW_FLAGS);
- if (ImPlot::BeginPlot(g_plot_selected->name, viewport->Size)) {
- //int test_x[] = {0,1,2,3};
- //int test_y1[] = {1,2,3,4};
- //int test_y2[] = {2,4,8,16};
- //ImPlot::PlotLine("test1", test_x, test_y1, 4);
- //ImPlot::PlotLine("test2", test_x, test_y2, 4);
- ImPlot::EndPlot();
- }
+ gui_render_plot(g_plot_selected, viewport->Size);
ImGui::End();
}
-void gui_print(void) {
+void gui_plot_queue_scroll(int col_selected, int row_selected) {
+ const ImGuiViewport *viewport = ImGui::GetMainViewport();
+ float plot_top = g_plot_cells_top[col_selected][row_selected];
+ float plot_bottom = g_plot_cells_bottom[col_selected][row_selected];
+ float win_top = g_plot_scroll_current;
+ float win_bottom = win_top + viewport->Size.y;
+
+ if (plot_bottom > win_bottom) {
+ g_plot_scroll_to = win_top + (plot_bottom - win_bottom);
+ g_plot_scroll = true;
+ }
+
+ if (plot_top < win_top) {
+ g_plot_scroll_to = plot_top - PLOT_SCROLL_OFFSET;
+ g_plot_scroll = true;
+ }
+}
+
+void gui_render(void) {
if (g_plot_maximized) {
- gui_print_plot_maximized();
- } else {
- if (g_data_col_visible) gui_print_data_col();
- gui_print_plots();
+ gui_render_plot_maximized();
+ return;
}
+
+ if (g_data_col_visible) {
+ gui_render_data_col();
+ }
+
+ gui_render_plots();
}
// ----------------------------------------------------------------------------
// Main functions
// ----------------------------------------------------------------------------
-void sig_handler(int signo) {
+void win_sig_handler(int signo) {
(void)signo;
log_warn("Signal received, will stop SALIS data client...");
- if (g_status == STATUS_RUNNING) data_stop_fetching();
+
+ if (g_status == STATUS_RUNNING) {
+ data_stop_fetching();
+ }
+
glfwSetWindowShouldClose(g_window, GLFW_TRUE);
}
-void glfw_error_callback(int error, const char* description) {
+void win_error_callback(int error, const char* description) {
log_warn("GLFW error %d: %s", error, description);
}
-void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
+void win_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
(void)window;
(void)scancode;
- if (action != GLFW_PRESS) return;
+ if (action != GLFW_PRESS || ImGui::IsAnyItemActive()) {
+ return;
+ }
if (g_plot_maximized) {
- switch (mods) {
- case GLFW_MOD_CONTROL:
- switch (key) {
- case GLFW_KEY_C:
- glfwSetWindowShouldClose(g_window, GLFW_TRUE);
- break;
- }
-
- break;
-
- case 0:
- switch (key) {
- case GLFW_KEY_F:
- g_plot_maximized = !g_plot_maximized;
- break;
- }
+ if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_C) {
+ glfwSetWindowShouldClose(g_window, GLFW_TRUE);
+ }
- break;
+ if (mods == 0 && key == GLFW_KEY_F) {
+ g_plot_maximized = !g_plot_maximized;
}
return;
@@ -484,14 +635,27 @@ void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, in
case GLFW_KEY_UP:
g_plot_row_selected -= g_plot_row_selected ? 1 : 0;
g_plot_selected = g_plot_cells[g_plot_col_selected][g_plot_row_selected];
+ gui_plot_queue_scroll(g_plot_col_selected, g_plot_row_selected);
break;
case GLFW_KEY_DOWN:
- g_plot_row_selected += (g_plot_row_selected < PLOT_COUNT - 1 && g_plot_cells[g_plot_col_selected][g_plot_row_selected + 1]) ? 1 : 0;
+ g_plot_row_selected += (g_plot_row_selected < (int)PLOT_COUNT - 1 && g_plot_cells[g_plot_col_selected][g_plot_row_selected + 1]) ? 1 : 0;
g_plot_selected = g_plot_cells[g_plot_col_selected][g_plot_row_selected];
+ gui_plot_queue_scroll(g_plot_col_selected, g_plot_row_selected);
break;
case GLFW_KEY_F:
g_plot_maximized = !g_plot_maximized;
break;
+ case GLFW_KEY_SPACE:
+ switch (g_status) {
+ case STATUS_STOPPED:
+ data_start_fetching();
+ break;
+ case STATUS_RUNNING:
+ data_stop_fetching();
+ break;
+ }
+
+ break;
}
break;
@@ -502,20 +666,20 @@ int main(int argc, char **argv) {
(void)argc;
(void)argv;
- signal(SIGINT, sig_handler);
- signal(SIGTERM, sig_handler);
+ signal(SIGINT, win_sig_handler);
+ signal(SIGTERM, win_sig_handler);
log_info("Starting SALIS data client");
log_info("Initializing GLFW");
- glfwSetErrorCallback(glfw_error_callback);
+ glfwSetErrorCallback(win_error_callback);
glfwInitHint(GLFW_WAYLAND_LIBDECOR, GLFW_WAYLAND_DISABLE_LIBDECOR);
if (!glfwInit()) assert(false);
float scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor());
g_window = glfwCreateWindow((int)(800 * scale), (int)(600 * scale), "SALIS data client", nullptr, nullptr);
assert(g_window);
- glfwSetKeyCallback(g_window, glfw_key_callback);
+ glfwSetKeyCallback(g_window, win_key_callback);
glfwMakeContextCurrent(g_window);
glfwSwapInterval(1); // enable vsync
@@ -524,26 +688,32 @@ int main(int argc, char **argv) {
ImGui::CreateContext();
ImPlot::CreateContext();
- g_io = &ImGui::GetIO();
- g_io->Fonts->AddFontFromFileTTF(FONT_SOURCE);
- g_io->IniFilename = nullptr;
+ g_imgui_io = &ImGui::GetIO();
+ g_imgui_io->Fonts->AddFontFromFileTTF(FONT_SOURCE);
+ g_imgui_io->IniFilename = nullptr;
g_imgui_style = &ImGui::GetStyle();
- g_imgui_style->Colors[ImGuiCol_WindowBg] = COLOR_BG;
+ g_imgui_style->Colors[ImGuiCol_WindowBg] = COLOR_BLACK;
g_imgui_style->FontScaleDpi = scale;
g_imgui_style->FontSizeBase = FONT_SIZE;
g_imgui_style->ItemSpacing = ImVec2(g_imgui_style->ItemSpacing.x, 2.f);
g_imgui_style->ScaleAllSizes(scale);
g_implot_style = &ImPlot::GetStyle();
- g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BG;
+ g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BLACK;
ImGui_ImplGlfw_InitForOpenGL(g_window, true);
ImGui_ImplOpenGL3_Init(GLSL_VERSION);
- g_hm_pixel_pow = data_calc_max_hm_pixel_pow();
+ for (auto &i : g_traces) {
+ g_trace_map[i] = {};
+ g_trace_map[i].reserve(g_entries);
+ }
+
+ g_hm_pixel_pow = data_max_hm_pixel_pow();
+
+ mtx_init(&g_fetching_mutex, mtx_plain);
- // Main loop
while (!glfwWindowShouldClose(g_window)) {
glfwPollEvents();
@@ -551,10 +721,11 @@ int main(int argc, char **argv) {
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
- gui_print();
+ gui_render();
ImGui::Render();
- int display_w, display_h;
+ int display_w;
+ int display_h;
glfwGetFramebufferSize(g_window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClearColor(0.f, 0.f, 0.f, 1.f);
@@ -563,6 +734,8 @@ int main(int argc, char **argv) {
glfwSwapBuffers(g_window);
}
+ mtx_destroy(&g_fetching_mutex);
+
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImPlot::DestroyContext();
@@ -571,5 +744,6 @@ int main(int argc, char **argv) {
log_info("Stopping SALIS data client");
glfwDestroyWindow(g_window);
glfwTerminate();
+
return 0;
}
diff --git a/data/server.c b/data/server.c
index 8581b83..d54d853 100644
--- a/data/server.c
+++ b/data/server.c
@@ -15,9 +15,44 @@ struct Socket {
struct sockaddr_in addr;
};
+struct json_object *g_response_header;
+
// ----------------------------------------------------------------------------
-// SQL functions
+// SQL callbacks
// ----------------------------------------------------------------------------
+void sql_callback_add_column_name(sqlite3_stmt *sql_stmt, void *data) {
+ assert(sql_stmt);
+ assert(data);
+ assert(sqlite3_column_type(sql_stmt, 0) == SQLITE_TEXT);
+ assert(!strcmp(sqlite3_column_name(sql_stmt, 0), "name"));
+
+ const char *col_name = (const char *)sqlite3_column_text(sql_stmt, 0);
+ struct json_object *response = (struct json_object *)data;
+
+ if (!json_object_object_get_ex(response, col_name, NULL)) {
+ json_object_object_add(response, col_name, json_object_new_array());
+ }
+}
+
+void sql_callback_add_data(sqlite3_stmt *sql_stmt, void *data) {
+ assert(sql_stmt);
+ assert(data);
+
+ struct json_object *response = (struct json_object *)data;
+
+ for (int i = 0; i < sqlite3_column_count(sql_stmt); i++) {
+ const char *col_name = sqlite3_column_name(sql_stmt, i);
+ struct json_object *col_data = json_object_object_get(response, col_name);
+
+ if (col_data) {
+ if (sqlite3_column_type(sql_stmt, i) == SQLITE_BLOB) {
+ // TODO: render blobs in parallel
+ } else {
+ json_object_array_add(col_data, json_object_new_int64(sqlite3_column_int64(sql_stmt, i)));
+ }
+ }
+ }
+}
// ----------------------------------------------------------------------------
// Main functions
@@ -25,6 +60,7 @@ struct Socket {
void sig_handler(int signo) {
(void)signo;
log_warn("Signal received, will stop SALIS data server");
+ json_object_put(g_response_header);
sql_close();
exit(0);
}
@@ -65,15 +101,45 @@ void respond_data(int socket_fd, struct json_object *request) {
const char *request_str = json_object_to_json_string(request);
log_info("Client requested simulation data with the following parameters: %s", request_str);
+ const char *x_axis = json_object_get_string(json_object_object_get(request, "x-axis"));
+ int64_t x_current = json_object_get_int64(json_object_object_get(request, "x-current"));
+ int64_t x_high = json_object_get_int64(json_object_object_get(request, "x-high"));
+ int64_t nth = json_object_get_int64(json_object_object_get(request, "nth"));
+ int64_t entries = json_object_get_int64(json_object_object_get(request, "entries"));
+
+ const char *x_axis_pref = (!strcmp(x_axis, "rowid") || !strcmp(x_axis, "step")) ? "core." : "";
+
+ struct json_object *response = NULL;
+ json_object_deep_copy(g_response_header, &response, NULL);
+
+ sql_exec(
+ 0, NULL, NULL,
+ sql_callback_add_data,
+ response,
+ "select * from ("
+ "select core.rowid, core.step, * from core inner join arch "
+ "where core.rowid = arch.rowid and %s%s > %ld and %s%s <= %ld and core.rowid %% %ld == 0 "
+ "order by %s%s desc limit %ld"
+ ") order by %s asc;",
+ x_axis_pref,
+ x_axis,
+ x_current,
+ x_axis_pref,
+ x_axis,
+ x_high,
+ nth,
+ x_axis_pref,
+ x_axis,
+ entries,
+ x_axis
+ );
- struct json_object *response = json_object_new_object();
- json_object_object_add(response, "response", json_object_new_string("hello!"));
const char *response_str = json_object_to_json_string(response);
- log_info("Sending response to client: %s", response_str);
+ log_info("Responding to client with: %s", response_str);
json_object_to_fd(socket_fd, response, 0);
+ json_object_put(response);
shutdown(socket_fd, SHUT_WR);
- json_object_put(response);
}
int handle_client(struct Socket *socket) {
@@ -120,6 +186,17 @@ int main(void) {
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
+ log_info("Creating response header");
+ g_response_header = json_object_new_object();
+ json_object_object_add(g_response_header, "rowid", json_object_new_array());
+ sql_exec(
+ 0, NULL, NULL,
+ sql_callback_add_column_name,
+ g_response_header,
+ "select name from pragma_table_info('core') union "
+ "select name from pragma_table_info('arch');"
+ );
+
log_info("Binding to port: %d", PORT);
int opt = 1;
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
@@ -130,8 +207,8 @@ int main(void) {
socket_addr.sin_port = htons(PORT);
bind(socket_fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_in));
- log_info("Listening...");
listen(socket_fd, BACKLOG);
+ log_info("Listening...");
while (true) {
struct Socket *socket = calloc(1, sizeof(struct Socket));
diff --git a/salis.py b/salis.py
index bb2f35a..9d0613b 100755
--- a/salis.py
+++ b/salis.py
@@ -65,8 +65,6 @@ options = {
(("F", "muta-flip"), (new,), fmt_id): {"action": "store_true", "help": "cosmic rays flip bits instead of randomizing whole bytes", "required": False},
(("g", "c-compiler"), (new, load, server, client), fmt_id): {"metavar": "CC", "help": "C compiler to use", "default": "gcc", "required": False, "type": str},
(("G", "c-compiler-flags"), (new, load, server, client), fmt_id): {"metavar": "FLAGS", "help": "base set of flags to pass to C compiler", "default": "-Wall -Wextra -Werror -Wno-overlength-strings -pedantic", "required": False, "type": str},
- (("g++", "cpp-compiler"), (client,), fmt_id): {"metavar": "CXX", "help": "C++ compiler to use", "default": "g++", "required": False, "type": str},
- (("G++", "cpp-compiler-flags"), (client,), fmt_id): {"metavar": "FLAGS", "help": "base set of flags to pass to C++ compiler", "default": "-Wall -Wextra -Werror -pedantic", "required": False, "type": str},
(("H", "home"), (new, load, server), fmt_id): {"metavar": "PATH", "help": "salis home directory", "default": os.path.join(os.environ["HOME"], ".salis"), "required": False, "type": str},
(("i", "ip"), (client,), fmt_id): {"metavar": "IP", "help": "ip address of server", "default": "127.0.0.1", "required": False, "type": str},
(("M", "muta-pow"), (new,), fmt_id): {"metavar": "POW", "help": "mutator range exponent; each step a cosmic ray hits addr, where addr = rand_uint64() %% 2^{POW}; lower values of POW mean higher mutation rates", "default": 32, "required": False, "type": pos},
@@ -79,6 +77,8 @@ options = {
(("T", "keep-temp-dir"), (new, load, server, client), fmt_id): {"action": "store_true", "help": "keep temporary directory on exit", "required": False},
(("t", "thread-gap"), (new, load), fmt_hex): {"metavar": "N", "help": "memory gap between core elements in bytes; may help reduce cache misses", "default": 0x100, "required": False, "type": nat},
(("u", "ui"), (new, load), fmt_id): {"choices": uis, "help": "user interface", "default": "curses", "required": False, "type": str},
+ (("x", "cpp-compiler"), (client,), fmt_id): {"metavar": "CXX", "help": "C++ compiler to use", "default": "g++", "required": False, "type": str},
+ (("X", "cpp-compiler-flags"), (client,), fmt_id): {"metavar": "FLAGS", "help": "base set of flags to pass to C++ compiler", "default": "-Wall -Wextra -Werror -pedantic -std=c++20 -lstdc++", "required": False, "type": str},
(("y", "sync-pow"), (new,), fmt_id): {"metavar": "POW", "help": "core sync interval exponent; sync events occur every N steps, where N = 2^{POW}", "default": 20, "required": False, "type": pos},
(("z", "auto-save-pow"), (new,), fmt_id): {"metavar": "POW", "help": "auto-save interval exponent; auto-saves occur every N steps, where N = 2^{POW}", "default": 36, "required": False, "type": pos},
}