mirror of
https://github.com/subhobhai943/SUB-OS.git
synced 2026-10-25 16:52:29 +00:00
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:
1 parent
cd840d5799
commit
5bd18c4733
9 files changed
+762
-2
No files matched your search
@@ -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"
|
||||
@@ -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)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
Reference in new issue
Block a user