feat(cpp,gui): C++ Analytics Engine + live GUI Analytics app

Kernel (C++17 freestanding):
  - New Analytics Engine (kernel/cpp/cpp_analytics.cpp): templated rolling
    time-series (TimeSeries<N>) sampling four live channels -- CPU load %,
    memory %, kernel-heap %, network pkt/interval -- with windowed statistics
    (min/max/mean/stddev) computed in pure integer math (kernel is -mno-sse, so
    no FP; stddev uses a Newton integer isqrt). Demonstrates virtual dispatch
    via an Observer pattern (IAnalyticsObserver + a ThresholdAlarmObserver that
    counts breaches) and RAII singletons.
  - Extended cpp_containers.hpp with Optional<T>, Pair<A,B>, and the kmin/kmax/
    kclamp/kswap algorithms.
  - Full C fallback (cpp_fallback.c) so the engine is functionally identical on
    aarch64/armv8i, which don't build the .cpp path.
  - Wired cpp_analytics_init() into boot [8/14]; seeds a few samples so graphs
    are populated immediately.

GUI:
  - New "Analytics" desktop app (gui/gui_apps.c): a 2x2 grid of live line-charts
    backed by the C++ engine, each with gridlines, a soft baseline fill, and
    min/avg/max(/stddev) overlays; click a panel to focus one channel full-size,
    click again to return. Throttled to ~2 Hz sampling off the compositor's
    repaint cadence. Registered on the desktop and in the start menu.

Userland: new `cppstat [samples]` applet dumps the engine's per-channel stats
and Observer breach counters.

Verified in QEMU (x86_64): boots clean on x86_64/aarch64/armv8i; `cppstat`
prints live stats (CPU 6%, Mem 7%, Heap 22%, computed min/avg/max/stddev,
Observer breaches); the GUI Analytics window renders all four graphs with the
Kernel Heap curve visibly climbing 22->55% as windows open, compositor steady
at ~53 FPS.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX
This commit is contained in:
subhobhai943andClaude Opus 4.8 committed 2026-08-26 10:42:48 +05:30
1 parent cd840d5799
commit 5bd18c4733
9 files changed
+762 -2

No files matched your search

+166
View File
@@ -13,6 +13,8 @@
#include <kernel/nt/reg.h>
#include <lib/string.h>
#include <lib/printf.h>
#include <kernel/cpp_analytics.h>
#include <kernel/ktime.h>
static int simple_atoi(const char* s) {
if (!s) return 0;
@@ -888,3 +890,167 @@ void gui_app_regedit_launch(int x, int y, int w, int h) {
win->handle_event = regedit_event;
}
}
// =================================================================
// 12. Analytics -- live charts backed by the C++ Analytics Engine
// =================================================================
typedef struct {
uint64_t last_sample_ns; // throttle: pull a new sample ~2 Hz
int selected; // -1 = grid overview, else single-channel focus
} analytics_data_t;
#define ANALYTICS_SAMPLE_INTERVAL_NS 500000000ULL // 0.5s
static uint32_t analytics_channel_color(int ch) {
switch (ch) {
case 0: return GUI_THEME_PRIMARY; // CPU
case 1: return GUI_THEME_ACCENT; // Memory
case 2: return GUI_THEME_WARNING; // Heap
case 3: return GUI_THEME_SUCCESS; // Network
default: return GUI_THEME_TEXT_MUTED;
}
}
// Draw one channel's line graph inside the rectangle (gx,gy,gw,gh).
static void analytics_draw_graph(int ch, int gx, int gy, int gw, int gh, bool detailed) {
uint32_t color = analytics_channel_color(ch);
// Panel background + border
gui_gfx_fill_rect(gx, gy, gw, gh, GUI_THEME_BG_DARK);
gui_gfx_draw_rect(gx, gy, gw, gh, GUI_THEME_BORDER);
// Header: name + last value
uint32_t mn, mx, avg, last, sd;
cpp_analytics_get_stats(ch, &mn, &mx, &avg, &last, &sd);
uint32_t scale = cpp_analytics_channel_scale(ch);
if (scale == 0) scale = 1;
char hdr[48];
snprintf(hdr, sizeof(hdr), "%s", cpp_analytics_channel_name(ch));
gui_gfx_draw_string(gx + 6, gy + 5, hdr, color);
char val[24];
snprintf(val, sizeof(val), "%u%s", last, cpp_analytics_channel_unit(ch));
gui_gfx_draw_string(gx + gw - 8 * (int)strlen(val) - 6, gy + 5, val, GUI_THEME_TEXT_MAIN);
// Plot area
int px = gx + 6;
int py = gy + 20;
int pw = gw - 12;
int ph = gh - (detailed ? 40 : 34);
if (ph < 8) ph = 8;
// Horizontal gridlines (25/50/75%)
for (int g = 1; g < 4; g++) {
int gyl = py + (ph * g) / 4;
gui_gfx_draw_line(px, gyl, px + pw, gyl, GUI_THEME_BG_ELEVATED);
}
// Fetch the series and draw it as a connected line with a soft fill.
uint32_t series[CPP_ANALYTICS_HISTORY];
int n = cpp_analytics_get_series(ch, series, CPP_ANALYTICS_HISTORY);
if (n >= 1) {
int prev_x = 0, prev_y = 0;
for (int i = 0; i < n; i++) {
int x = px + (n > 1 ? (pw * i) / (n - 1) : pw);
uint32_t v = series[i];
if (v > scale) v = scale;
int y = py + ph - (int)(((uint64_t)v * ph) / scale);
// vertical fill to baseline
gui_gfx_draw_line(x, y, x, py + ph, (color & 0x00FFFFFF) | 0x30000000);
if (i > 0) gui_gfx_draw_line(prev_x, prev_y, x, y, color);
prev_x = x; prev_y = y;
}
}
// Footer stats
char foot[64];
if (detailed) {
snprintf(foot, sizeof(foot), "min %u avg %u max %u sd %u", mn, avg, mx, sd);
} else {
snprintf(foot, sizeof(foot), "min %u avg %u max %u", mn, avg, mx);
}
gui_gfx_draw_string(gx + 6, gy + gh - 12, foot, GUI_THEME_TEXT_DIM);
}
static void analytics_paint(gui_window_t* win) {
analytics_data_t* ad = (analytics_data_t*)win->user_data;
if (!ad) return;
// Throttled live sampling driven by the compositor's repaint cadence.
uint64_t now = ktime_ns();
if (now - ad->last_sample_ns >= ANALYTICS_SAMPLE_INTERVAL_NS) {
cpp_analytics_sample();
ad->last_sample_ns = now;
}
int cx = win->x + 10;
int top = win->y + GUI_TITLEBAR_HEIGHT + 4;
char title[72];
snprintf(title, sizeof(title), "C++ Analytics Engine %llu samples",
(unsigned long long)cpp_analytics_sample_count());
gui_gfx_draw_string_16_shadow(cx, top, title, GUI_THEME_PRIMARY, GUI_COLOR_BLACK);
gui_gfx_draw_string(cx, top + 20, "Templated time-series + Observer pattern (kernel/cpp)", GUI_THEME_TEXT_MUTED);
int grid_top = top + 38;
int nch = cpp_analytics_channel_count();
if (ad->selected >= 0 && ad->selected < nch) {
// Focused single-channel view.
analytics_draw_graph(ad->selected, cx, grid_top,
win->width - 20, win->height - (grid_top - win->y) - 10, true);
gui_gfx_draw_string(cx + 6, win->y + win->height - 12,
"(click to return to overview)", GUI_THEME_TEXT_DIM);
} else {
// 2x2 overview grid.
int gw = (win->width - 20 - 8) / 2;
int gh = (win->height - (grid_top - win->y) - 10 - 8) / 2;
if (gw < 60) gw = 60;
if (gh < 40) gh = 40;
for (int i = 0; i < nch && i < 4; i++) {
int col = i % 2, row = i / 2;
analytics_draw_graph(i, cx + col * (gw + 8), grid_top + row * (gh + 8), gw, gh, false);
}
}
}
static void analytics_event(gui_window_t* win, const gui_event_t* ev) {
analytics_data_t* ad = (analytics_data_t*)win->user_data;
if (!ad) return;
if (ev->type == GUI_EVENT_CLOSE) {
if (win->user_data) { kfree(win->user_data); win->user_data = NULL; }
return;
}
if (ev->type != GUI_EVENT_MOUSE_DOWN) return;
if (ad->selected >= 0) {
ad->selected = -1; // any click leaves focus mode
return;
}
// Overview: figure out which quadrant was clicked and focus it.
int grid_top_rel = GUI_TITLEBAR_HEIGHT + 4 + 38;
int gw = (win->width - 20 - 8) / 2;
int gh = (win->height - grid_top_rel - 10 - 8) / 2;
if (gw < 1 || gh < 1) return;
int rx = ev->rel_x - 10;
int ry = ev->rel_y - grid_top_rel;
if (rx < 0 || ry < 0) return;
int col = rx / (gw + 8);
int row = ry / (gh + 8);
if (col > 1) col = 1;
if (row > 1) row = 1;
int idx = row * 2 + col;
if (idx >= 0 && idx < cpp_analytics_channel_count()) ad->selected = idx;
}
void gui_app_analytics_launch(int x, int y, int w, int h) {
gui_window_t* win = gui_wm_create_window("Analytics", x, y, (w > 0) ? w : 560, (h > 0) ? h : 400);
if (win) {
analytics_data_t* ad = (analytics_data_t*)kzalloc(sizeof(analytics_data_t));
if (ad) { ad->selected = -1; ad->last_sample_ns = 0; }
win->user_data = ad;
win->paint = analytics_paint;
win->handle_event = analytics_event;
}
}
+3 -2
View File
@@ -63,14 +63,15 @@ static const desktop_app_t g_apps[] = {
{ "RegEdit", GUI_ICON_SETTINGS, gui_app_regedit_launch, 300, 90, 480, 360 },
{ "Clock", GUI_ICON_CLOCK, gui_app_clock_launch, 460, 120, 280, 260 },
{ "Settings", GUI_ICON_SETTINGS, gui_app_settings_launch, 160, 60, 400, 320 },
{ "Analytics", GUI_ICON_MONITOR, gui_app_analytics_launch,320, 70, 560, 400 },
{ "About", GUI_ICON_INFO, gui_app_about_launch, 240, 140, 330, 170 },
};
#define APP_COUNT ((int)(sizeof(g_apps) / sizeof(g_apps[0])))
// Which of the above appear on the desktop itself.
// Indices into g_apps[]: Terminal, Files, Monitor, Kernel Log, KTest, Life,
// Rust Lab, Settings. (Objects/RegEdit live in the start menu.)
static const int g_desktop_icons[] = { 0, 1, 2, 4, 5, 9, 10, 14 };
// Rust Lab, Settings, Analytics. (Objects/RegEdit live in the start menu.)
static const int g_desktop_icons[] = { 0, 1, 2, 4, 5, 9, 10, 14, 15 };
#define DESKTOP_ICON_COUNT ((int)(sizeof(g_desktop_icons) / sizeof(g_desktop_icons[0])))
static bool g_start_menu_open = false;
+1
View File
@@ -13,5 +13,6 @@ void gui_app_life_launch(int x, int y, int w, int h);
void gui_app_rustlab_launch(int x, int y, int w, int h);
void gui_app_objbrowse_launch(int x, int y, int w, int h);
void gui_app_regedit_launch(int x, int y, int w, int h);
void gui_app_analytics_launch(int x, int y, int w, int h);
#endif // _GUI_APPS_H
+52
View File
@@ -0,0 +1,52 @@
#ifndef _KERNEL_CPP_ANALYTICS_H
#define _KERNEL_CPP_ANALYTICS_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
// The C++ analytics engine keeps a rolling time-series per channel and computes
// online statistics (min/max/mean/variance via Welford) over it. Channels are:
// 0 = CPU load % 1 = Memory used %
// 2 = Kernel heap % 3 = Network packets/interval
#define CPP_ANALYTICS_CHANNELS 4
#define CPP_ANALYTICS_HISTORY 64
// Bring up the engine (registers channels + an example observer). Idempotent.
void cpp_analytics_init(void);
// Pull one live sample from the kernel (metrics/pmm/heap/NIC) into every
// channel. Call this periodically (the GUI Analytics app and the `cppstat`
// applet both drive it). Returns the total number of samples taken so far.
uint64_t cpp_analytics_sample(void);
// Copy up to `max` most-recent values of a channel into `out` (oldest first,
// newest last). Returns the number written.
int cpp_analytics_get_series(int channel, uint32_t* out, int max);
// Fetch the online statistics for a channel. Any out pointer may be NULL.
void cpp_analytics_get_stats(int channel, uint32_t* out_min, uint32_t* out_max,
uint32_t* out_avg, uint32_t* out_last,
uint32_t* out_stddev);
// Human-readable channel name / unit suffix and the current scale ceiling used
// for graphing (e.g. 100 for a percentage channel).
const char* cpp_analytics_channel_name(int channel);
const char* cpp_analytics_channel_unit(int channel);
uint32_t cpp_analytics_channel_scale(int channel);
int cpp_analytics_channel_count(void);
uint64_t cpp_analytics_sample_count(void);
// Print a textual dashboard (used by the `cppstat` lazybox applet).
void cpp_analytics_dump(void);
#ifdef __cplusplus
}
#endif
#endif // _KERNEL_CPP_ANALYTICS_H
+323
View File
@@ -0,0 +1,323 @@
// C++ Kernel Analytics Engine for SUB-OS.
//
// A freestanding C++17 telemetry pipeline that demonstrates templates, virtual
// dispatch (the Observer pattern) and RAII inside the kernel while doing real
// work: it samples live system metrics (CPU, memory, heap, network) into rolling
// per-channel time-series and computes windowed statistics (min / max / mean /
// standard deviation) using pure integer arithmetic -- the kernel is built with
// -mno-sse, so no floating point is available.
//
// The GUI "Analytics" app and the `cppstat` shell applet both render the series
// this engine maintains.
#include "cpp_containers.hpp"
#include <kernel/cpp_analytics.h>
extern "C" {
#include <kernel/printk.h>
#include <kernel/metrics.h>
#include <mm/pmm.h>
#include <mm/kmalloc.h>
#include <lib/string.h>
void cpp_call_global_constructors(void);
}
namespace kernel {
// Integer square root (Newton's method) -- for standard deviation without FP.
static uint32_t isqrt64(uint64_t n) {
if (n == 0) return 0;
uint64_t x = n, y = (x + 1) / 2;
while (y < x) { x = y; y = (x + n / x) / 2; }
return static_cast<uint32_t>(x);
}
// -----------------------------------------------------------------------------
// TimeSeries<N>: a fixed-capacity ring of samples plus on-demand windowed stats.
// -----------------------------------------------------------------------------
template<size_t N>
class TimeSeries {
private:
uint32_t m_ring[N];
size_t m_head = 0; // index of the next write
size_t m_count = 0; // number of valid samples (<= N)
uint64_t m_total = 0; // all-time sample count (for the running average)
uint64_t m_sum_all = 0;
public:
void push(uint32_t v) {
m_ring[m_head] = v;
m_head = (m_head + 1) % N;
if (m_count < N) m_count++;
m_total++;
m_sum_all += v;
}
size_t count() const { return m_count; }
size_t capacity() const { return N; }
uint64_t total_samples() const { return m_total; }
// Copy the window oldest-first into out[], returning how many were written.
int copy(uint32_t* out, int max) const {
int n = static_cast<int>(m_count < static_cast<size_t>(max) ? m_count : static_cast<size_t>(max));
// The oldest element sits at (m_head - m_count) modulo N.
size_t start = (m_head + N - m_count) % N;
for (int i = 0; i < n; i++) out[i] = m_ring[(start + i) % N];
return n;
}
uint32_t last() const {
if (m_count == 0) return 0;
return m_ring[(m_head + N - 1) % N];
}
void stats(uint32_t& mn, uint32_t& mx, uint32_t& avg, uint32_t& stddev) const {
if (m_count == 0) { mn = mx = avg = stddev = 0; return; }
uint32_t lo = 0xFFFFFFFFu, hi = 0;
uint64_t sum = 0;
for (size_t i = 0; i < m_count; i++) {
uint32_t v = m_ring[i];
if (v < lo) lo = v;
if (v > hi) hi = v;
sum += v;
}
uint32_t mean = static_cast<uint32_t>(sum / m_count);
// Windowed variance = mean of squared deviations (integer).
uint64_t acc = 0;
for (size_t i = 0; i < m_count; i++) {
int64_t d = static_cast<int64_t>(m_ring[i]) - static_cast<int64_t>(mean);
acc += static_cast<uint64_t>(d * d);
}
mn = lo; mx = hi; avg = mean;
stddev = isqrt64(acc / m_count);
}
};
// -----------------------------------------------------------------------------
// Observer pattern: subscribers are notified of every sample on every channel.
// -----------------------------------------------------------------------------
class IAnalyticsObserver {
public:
virtual ~IAnalyticsObserver() = default;
virtual void on_sample(int channel, uint32_t value) = 0;
};
// A concrete observer that counts threshold breaches per channel -- a small but
// real use of virtual dispatch (e.g. "CPU load crossed 90%").
class ThresholdAlarmObserver : public IAnalyticsObserver {
private:
uint32_t m_threshold[CPP_ANALYTICS_CHANNELS];
uint64_t m_breaches[CPP_ANALYTICS_CHANNELS];
public:
ThresholdAlarmObserver() {
for (int i = 0; i < CPP_ANALYTICS_CHANNELS; i++) { m_threshold[i] = 90; m_breaches[i] = 0; }
}
void set_threshold(int ch, uint32_t t) {
if (ch >= 0 && ch < CPP_ANALYTICS_CHANNELS) m_threshold[ch] = t;
}
void on_sample(int channel, uint32_t value) override {
if (channel < 0 || channel >= CPP_ANALYTICS_CHANNELS) return;
if (value >= m_threshold[channel]) m_breaches[channel]++;
}
uint64_t breaches(int ch) const {
return (ch >= 0 && ch < CPP_ANALYTICS_CHANNELS) ? m_breaches[ch] : 0;
}
};
// -----------------------------------------------------------------------------
// A single metric channel: name, unit, graph scale and its time-series.
// -----------------------------------------------------------------------------
struct Channel {
const char* name;
const char* unit;
uint32_t scale; // graph ceiling (0 => auto-scale to max)
TimeSeries<CPP_ANALYTICS_HISTORY> series;
};
// -----------------------------------------------------------------------------
// The engine singleton.
// -----------------------------------------------------------------------------
class AnalyticsEngine {
private:
Channel m_ch[CPP_ANALYTICS_CHANNELS];
Vector<IAnalyticsObserver*> m_observers; // non-owning
ThresholdAlarmObserver* m_alarm = nullptr;
uint64_t m_samples = 0;
// Previous cumulative packet counter, to derive a per-interval rate.
uint64_t m_prev_net_pkts = 0;
bool m_have_prev = false;
AnalyticsEngine() {
m_ch[0].name = "CPU Load"; m_ch[0].unit = "%"; m_ch[0].scale = 100;
m_ch[1].name = "Memory"; m_ch[1].unit = "%"; m_ch[1].scale = 100;
m_ch[2].name = "Kernel Heap";m_ch[2].unit = "%"; m_ch[2].scale = 100;
m_ch[3].name = "Net Traffic";m_ch[3].unit = "pkt/s"; m_ch[3].scale = 0; // auto
}
public:
static AnalyticsEngine& instance() {
static AnalyticsEngine* s = nullptr;
if (!s) s = new AnalyticsEngine();
return *s;
}
void add_observer(IAnalyticsObserver* o) { if (o) m_observers.push_back(o); }
void ensure_alarm() {
if (!m_alarm) {
m_alarm = new ThresholdAlarmObserver();
add_observer(m_alarm);
}
}
ThresholdAlarmObserver* alarm() { return m_alarm; }
void notify(int ch, uint32_t v) {
for (auto* o : m_observers) if (o) o->on_sample(ch, v);
}
uint64_t sample() {
system_metrics_t m;
metrics_sample(&m);
uint32_t cpu = m.cpu_user_pct + m.cpu_system_pct;
if (cpu > 100) cpu = 100;
uint32_t mem = 0;
if (m.mem_total_kb) mem = static_cast<uint32_t>((m.mem_used_kb * 100) / m.mem_total_kb);
if (mem > 100) mem = 100;
uint32_t heap = 0;
size_t ht = heap_get_total_bytes();
if (ht) heap = static_cast<uint32_t>((static_cast<uint64_t>(heap_get_used_bytes()) * 100) / ht);
if (heap > 100) heap = 100;
uint64_t pkts = m.net_rx_packets + m.net_tx_packets;
uint32_t rate = 0;
if (m_have_prev && pkts >= m_prev_net_pkts) rate = static_cast<uint32_t>(pkts - m_prev_net_pkts);
m_prev_net_pkts = pkts;
m_have_prev = true;
uint32_t vals[CPP_ANALYTICS_CHANNELS] = { cpu, mem, heap, rate };
for (int i = 0; i < CPP_ANALYTICS_CHANNELS; i++) {
m_ch[i].series.push(vals[i]);
notify(i, vals[i]);
}
m_samples++;
return m_samples;
}
int channels() const { return CPP_ANALYTICS_CHANNELS; }
uint64_t sample_count() const { return m_samples; }
Channel* channel(int i) {
if (i < 0 || i >= CPP_ANALYTICS_CHANNELS) return nullptr;
return &m_ch[i];
}
};
} // namespace kernel
// -----------------------------------------------------------------------------
// C-FFI bridge
// -----------------------------------------------------------------------------
using kernel::AnalyticsEngine;
extern "C" {
void cpp_analytics_init(void) {
cpp_call_global_constructors();
auto& e = AnalyticsEngine::instance();
e.ensure_alarm();
// Seed a few samples so the graphs are not empty on first paint.
for (int i = 0; i < 4; i++) e.sample();
printk(KERN_INFO "CXX: Analytics Engine online (%d channels, %d-sample history, Observer pattern)\n",
CPP_ANALYTICS_CHANNELS, CPP_ANALYTICS_HISTORY);
}
uint64_t cpp_analytics_sample(void) {
return AnalyticsEngine::instance().sample();
}
int cpp_analytics_get_series(int channel, uint32_t* out, int max) {
if (!out || max <= 0) return 0;
auto* c = AnalyticsEngine::instance().channel(channel);
if (!c) return 0;
return c->series.copy(out, max);
}
void cpp_analytics_get_stats(int channel, uint32_t* out_min, uint32_t* out_max,
uint32_t* out_avg, uint32_t* out_last,
uint32_t* out_stddev) {
auto* c = AnalyticsEngine::instance().channel(channel);
uint32_t mn = 0, mx = 0, avg = 0, sd = 0, last = 0;
if (c) { c->series.stats(mn, mx, avg, sd); last = c->series.last(); }
if (out_min) *out_min = mn;
if (out_max) *out_max = mx;
if (out_avg) *out_avg = avg;
if (out_last) *out_last = last;
if (out_stddev) *out_stddev = sd;
}
const char* cpp_analytics_channel_name(int channel) {
auto* c = AnalyticsEngine::instance().channel(channel);
return c ? c->name : "";
}
const char* cpp_analytics_channel_unit(int channel) {
auto* c = AnalyticsEngine::instance().channel(channel);
return c ? c->unit : "";
}
uint32_t cpp_analytics_channel_scale(int channel) {
auto* c = AnalyticsEngine::instance().channel(channel);
if (!c) return 100;
if (c->scale) return c->scale;
// Auto-scale: ceil to a sensible ceiling above the observed max.
uint32_t mn, mx, avg, sd;
c->series.stats(mn, mx, avg, sd);
uint32_t ceil = mx + mx / 4 + 1;
return ceil < 10 ? 10 : ceil;
}
int cpp_analytics_channel_count(void) {
return AnalyticsEngine::instance().channels();
}
uint64_t cpp_analytics_sample_count(void) {
return AnalyticsEngine::instance().sample_count();
}
void cpp_analytics_dump(void) {
auto& e = AnalyticsEngine::instance();
e.sample();
printk(ANSI_BRIGHT_CYAN "=================================================================\n" ANSI_RESET);
printk(ANSI_BRIGHT_CYAN " SUB-OS C++ Analytics Engine (samples: %llu)\n" ANSI_RESET,
static_cast<unsigned long long>(e.sample_count()));
printk(ANSI_BRIGHT_CYAN "=================================================================\n" ANSI_RESET);
printk(ANSI_BOLD "%-14s %6s %6s %6s %6s %8s\n" ANSI_RESET,
"CHANNEL", "LAST", "MIN", "AVG", "MAX", "STDDEV");
printk("-----------------------------------------------------------------\n");
for (int i = 0; i < e.channels(); i++) {
uint32_t mn, mx, avg, last, sd;
cpp_analytics_get_stats(i, &mn, &mx, &avg, &last, &sd);
char label[24];
snprintf(label, sizeof(label), "%s(%s)", cpp_analytics_channel_name(i), cpp_analytics_channel_unit(i));
printk(ANSI_BRIGHT_YELLOW "%-14s" ANSI_RESET " %6u %6u " ANSI_BRIGHT_GREEN "%6u" ANSI_RESET " %6u %8u\n",
label, last, mn, avg, mx, sd);
}
auto* alarm = e.alarm();
if (alarm) {
printk("-----------------------------------------------------------------\n");
printk(" Threshold alarm breaches (Observer): CPU=%llu MEM=%llu HEAP=%llu\n",
static_cast<unsigned long long>(alarm->breaches(0)),
static_cast<unsigned long long>(alarm->breaches(1)),
static_cast<unsigned long long>(alarm->breaches(2)));
}
printk("\n");
}
} // extern "C"
+83
View File
@@ -42,6 +42,89 @@ constexpr T&& forward(typename RemoveReference<T>::type&& arg) noexcept {
return static_cast<T&&>(arg);
}
// -----------------------------------------------------------------------------
// Freestanding numeric algorithms (namespaced to avoid clashing with the C
// kmin/kmax macros/inlines the kernel exposes elsewhere).
// -----------------------------------------------------------------------------
template<typename T>
constexpr const T& kmin(const T& a, const T& b) { return (a < b) ? a : b; }
template<typename T>
constexpr const T& kmax(const T& a, const T& b) { return (a > b) ? a : b; }
template<typename T>
constexpr const T& kclamp(const T& v, const T& lo, const T& hi) {
return (v < lo) ? lo : ((v > hi) ? hi : v);
}
template<typename T>
constexpr void kswap(T& a, T& b) {
T tmp = move(a);
a = move(b);
b = move(tmp);
}
// -----------------------------------------------------------------------------
// Pair (Freestanding two-element tuple)
// -----------------------------------------------------------------------------
template<typename A, typename B>
struct Pair {
A first;
B second;
constexpr Pair() : first(), second() {}
constexpr Pair(const A& a, const B& b) : first(a), second(b) {}
};
template<typename A, typename B>
constexpr Pair<A, B> make_pair(const A& a, const B& b) { return Pair<A, B>(a, b); }
// -----------------------------------------------------------------------------
// Optional (Freestanding maybe-value, no heap)
// -----------------------------------------------------------------------------
template<typename T>
class Optional {
private:
alignas(T) unsigned char m_storage[sizeof(T)];
bool m_has;
T* ptr() { return reinterpret_cast<T*>(m_storage); }
const T* ptr() const { return reinterpret_cast<const T*>(m_storage); }
public:
constexpr Optional() noexcept : m_has(false) {}
Optional(const T& value) : m_has(true) {
new (static_cast<void*>(m_storage)) T(value);
}
Optional(const Optional& other) : m_has(other.m_has) {
if (m_has) new (static_cast<void*>(m_storage)) T(*other.ptr());
}
~Optional() { reset(); }
Optional& operator=(const Optional& other) {
if (this != &other) {
reset();
m_has = other.m_has;
if (m_has) new (static_cast<void*>(m_storage)) T(*other.ptr());
}
return *this;
}
void reset() {
if (m_has) { ptr()->~T(); m_has = false; }
}
bool has_value() const noexcept { return m_has; }
explicit operator bool() const noexcept { return m_has; }
T& value() { return *ptr(); }
const T& value() const { return *ptr(); }
T value_or(const T& fallback) const { return m_has ? *ptr() : fallback; }
};
// -----------------------------------------------------------------------------
// UniquePtr (Freestanding Smart Pointer RAII)
// -----------------------------------------------------------------------------
+121
View File
@@ -1,7 +1,10 @@
// C++ Kernel Subsystem C-Fallback for Non-x86 Cross Targets
#include <kernel/cpp_kernel.h>
#include <kernel/cpp_analytics.h>
#include <kernel/printk.h>
#include <kernel/metrics.h>
#include <mm/pmm.h>
#include <mm/kmalloc.h>
int cpp_kernel_init(void) {
printk(KERN_INFO "CXX: C++ Kernel Subsystem Bridge online (Cross-Architecture Mode)\n");
@@ -45,3 +48,121 @@ int cpp_run_benchmarks(void) {
printk(" UniquePtr RAII : " ANSI_BRIGHT_GREEN "PASSED\n\n" ANSI_RESET);
return 0;
}
// ---------------------------------------------------------------------------
// Analytics engine C fallback (functionally equivalent to the C++ engine so the
// GUI Analytics app and `cppstat` work identically on ARM targets).
// ---------------------------------------------------------------------------
static uint32_t fb_ring[CPP_ANALYTICS_CHANNELS][CPP_ANALYTICS_HISTORY];
static int fb_head[CPP_ANALYTICS_CHANNELS];
static int fb_count[CPP_ANALYTICS_CHANNELS];
static uint64_t fb_samples;
static uint64_t fb_prev_pkts;
static bool fb_have_prev;
static const char* fb_names[CPP_ANALYTICS_CHANNELS] = { "CPU Load", "Memory", "Kernel Heap", "Net Traffic" };
static const char* fb_units[CPP_ANALYTICS_CHANNELS] = { "%", "%", "%", "pkt/s" };
static uint32_t fb_scale[CPP_ANALYTICS_CHANNELS] = { 100, 100, 100, 0 };
static uint32_t fb_isqrt(uint64_t n) {
if (n == 0) return 0;
uint64_t x = n, y = (x + 1) / 2;
while (y < x) { x = y; y = (x + n / x) / 2; }
return (uint32_t)x;
}
void cpp_analytics_init(void) {
for (int i = 0; i < CPP_ANALYTICS_CHANNELS; i++) { fb_head[i] = 0; fb_count[i] = 0; }
fb_samples = 0; fb_have_prev = false; fb_prev_pkts = 0;
for (int i = 0; i < 4; i++) cpp_analytics_sample();
printk(KERN_INFO "CXX: Analytics Engine online (Cross-Arch Fallback, %d channels)\n",
CPP_ANALYTICS_CHANNELS);
}
uint64_t cpp_analytics_sample(void) {
system_metrics_t m;
metrics_sample(&m);
uint32_t cpu = m.cpu_user_pct + m.cpu_system_pct; if (cpu > 100) cpu = 100;
uint32_t mem = m.mem_total_kb ? (uint32_t)((m.mem_used_kb * 100) / m.mem_total_kb) : 0;
if (mem > 100) mem = 100;
uint32_t heap = 0; size_t ht = heap_get_total_bytes();
if (ht) heap = (uint32_t)(((uint64_t)heap_get_used_bytes() * 100) / ht);
if (heap > 100) heap = 100;
uint64_t pkts = m.net_rx_packets + m.net_tx_packets;
uint32_t rate = (fb_have_prev && pkts >= fb_prev_pkts) ? (uint32_t)(pkts - fb_prev_pkts) : 0;
fb_prev_pkts = pkts; fb_have_prev = true;
uint32_t vals[CPP_ANALYTICS_CHANNELS] = { cpu, mem, heap, rate };
for (int i = 0; i < CPP_ANALYTICS_CHANNELS; i++) {
fb_ring[i][fb_head[i]] = vals[i];
fb_head[i] = (fb_head[i] + 1) % CPP_ANALYTICS_HISTORY;
if (fb_count[i] < CPP_ANALYTICS_HISTORY) fb_count[i]++;
}
fb_samples++;
return fb_samples;
}
int cpp_analytics_get_series(int ch, uint32_t* out, int max) {
if (ch < 0 || ch >= CPP_ANALYTICS_CHANNELS || !out || max <= 0) return 0;
int n = fb_count[ch] < max ? fb_count[ch] : max;
int start = (fb_head[ch] + CPP_ANALYTICS_HISTORY - fb_count[ch]) % CPP_ANALYTICS_HISTORY;
for (int i = 0; i < n; i++) out[i] = fb_ring[ch][(start + i) % CPP_ANALYTICS_HISTORY];
return n;
}
void cpp_analytics_get_stats(int ch, uint32_t* mn_o, uint32_t* mx_o,
uint32_t* avg_o, uint32_t* last_o, uint32_t* sd_o) {
uint32_t mn = 0, mx = 0, avg = 0, sd = 0, last = 0;
if (ch >= 0 && ch < CPP_ANALYTICS_CHANNELS && fb_count[ch] > 0) {
uint32_t lo = 0xFFFFFFFFu, hi = 0; uint64_t sum = 0;
for (int i = 0; i < fb_count[ch]; i++) {
uint32_t v = fb_ring[ch][i];
if (v < lo) lo = v;
if (v > hi) hi = v;
sum += v;
}
avg = (uint32_t)(sum / fb_count[ch]);
uint64_t acc = 0;
for (int i = 0; i < fb_count[ch]; i++) {
int64_t d = (int64_t)fb_ring[ch][i] - (int64_t)avg; acc += (uint64_t)(d * d);
}
mn = lo; mx = hi; sd = fb_isqrt(acc / fb_count[ch]);
last = fb_ring[ch][(fb_head[ch] + CPP_ANALYTICS_HISTORY - 1) % CPP_ANALYTICS_HISTORY];
}
if (mn_o) *mn_o = mn;
if (mx_o) *mx_o = mx;
if (avg_o) *avg_o = avg;
if (last_o) *last_o = last;
if (sd_o) *sd_o = sd;
}
const char* cpp_analytics_channel_name(int ch) {
return (ch >= 0 && ch < CPP_ANALYTICS_CHANNELS) ? fb_names[ch] : "";
}
const char* cpp_analytics_channel_unit(int ch) {
return (ch >= 0 && ch < CPP_ANALYTICS_CHANNELS) ? fb_units[ch] : "";
}
uint32_t cpp_analytics_channel_scale(int ch) {
if (ch < 0 || ch >= CPP_ANALYTICS_CHANNELS) return 100;
if (fb_scale[ch]) return fb_scale[ch];
uint32_t mn, mx, avg, last, sd; cpp_analytics_get_stats(ch, &mn, &mx, &avg, &last, &sd);
uint32_t ceil = mx + mx / 4 + 1; return ceil < 10 ? 10 : ceil;
}
int cpp_analytics_channel_count(void) { return CPP_ANALYTICS_CHANNELS; }
uint64_t cpp_analytics_sample_count(void) { return fb_samples; }
void cpp_analytics_dump(void) {
cpp_analytics_sample();
printk(ANSI_BRIGHT_CYAN "=== SUB-OS C++ Analytics Engine (samples: %llu) ===\n" ANSI_RESET,
(unsigned long long)fb_samples);
printk(ANSI_BOLD "%-14s %6s %6s %6s %6s %8s\n" ANSI_RESET,
"CHANNEL", "LAST", "MIN", "AVG", "MAX", "STDDEV");
for (int i = 0; i < CPP_ANALYTICS_CHANNELS; i++) {
uint32_t mn, mx, avg, last, sd;
cpp_analytics_get_stats(i, &mn, &mx, &avg, &last, &sd);
char label[24];
snprintf(label, sizeof(label), "%s(%s)", fb_names[i], fb_units[i]);
printk(ANSI_BRIGHT_YELLOW "%-14s" ANSI_RESET " %6u %6u " ANSI_BRIGHT_GREEN "%6u" ANSI_RESET " %6u %8u\n",
label, last, mn, avg, mx, sd);
}
printk("\n");
}
+2
View File
@@ -83,6 +83,7 @@
#include <kernel/rust.h>
#include <kernel/sub_lang.h>
#include <kernel/cpp_kernel.h>
#include <kernel/cpp_analytics.h>
#include <net/filter.h>
#include <net/http.h>
#include <net/ssh.h>
@@ -200,6 +201,7 @@ static void subos_modular_core_boot(void) {
boot_logo_splash_progress(55, "Starting Rust, C++ and SUB-Lang engines");
rust_kernel_init();
cpp_kernel_init();
cpp_analytics_init();
sub_kernel_init();
prng_seed(0, 0);
certs_init();
+11
View File
@@ -41,6 +41,7 @@
#include <kernel/ktime.h>
#include <kernel/sub_lang.h>
#include <kernel/cpp_kernel.h>
#include <kernel/cpp_analytics.h>
#include <userland/snake.h>
#include <ipc/shm_posix.h>
#include <net/dns_cache.h>
@@ -1328,6 +1329,15 @@ static int applet_cppbench(int argc, char** argv) {
return cpp_run_benchmarks();
}
static int applet_cppstat(int argc, char** argv) {
int n = (argc >= 2) ? atoi(argv[1]) : 1;
if (n < 1) n = 1;
if (n > 32) n = 32;
for (int i = 0; i < n; i++) cpp_analytics_sample();
cpp_analytics_dump();
return 0;
}
static int applet_shm(int argc, char** argv) {
(void)argc; (void)argv;
posix_shm_dump();
@@ -2629,6 +2639,7 @@ static const lazybox_applet_t applets[] = {
{"cpptest", applet_cpptest, "cpptest", "C++ polymorphism & new/del", "System"},
{"cppdevs", applet_cppdevs, "cppdevs", "C++ OOP device tree", "System"},
{"cppbench", applet_cppbench, "cppbench", "C++ OOP benchmark suite", "System"},
{"cppstat", applet_cppstat, "cppstat [samples]", "C++ analytics engine stats", "System"},
{"shm", applet_shm, "shm", "POSIX /dev/shm shared memory", "System"},
{"dnscache", applet_dnscache, "dnscache [-f]", "DNS resolver cache & metrics", "Network"},
{"beep", applet_beep, "beep [freq/jingle] [ms]", "PC speaker & HDA tone synth", "Sound"},