summaryrefslogtreecommitdiff
path: root/Applications
diff options
context:
space:
mode:
authorTom <tomut@yahoo.com>2021-01-04 20:14:52 -0700
committerAndreas Kling <kling@serenityos.org>2021-01-05 10:59:00 +0100
commitde6a4d49b8d4d938eea5114749fa247f041f1f3f (patch)
treefcbab5fb9036d9e3ff883d304357ae035de9775c /Applications
parentb4a783d9231d1c0d8dcc24d5659c34443e2976ab (diff)
downloadserenity-de6a4d49b8d4d938eea5114749fa247f041f1f3f.zip
SystemMonitor: Beef up CPU and memory graphs
We now show the total CPU usage as well as the kernel portion. For the memory graphs we show the amount of committed memory, actually allocated memory, and the portion of the kernel heap.
Diffstat (limited to 'Applications')
-rw-r--r--Applications/SystemMonitor/GraphWidget.cpp126
-rw-r--r--Applications/SystemMonitor/GraphWidget.h30
-rw-r--r--Applications/SystemMonitor/MemoryStatsWidget.cpp26
-rw-r--r--Applications/SystemMonitor/MemoryStatsWidget.h1
-rw-r--r--Applications/SystemMonitor/ProcessModel.cpp26
-rw-r--r--Applications/SystemMonitor/ProcessModel.h2
-rw-r--r--Applications/SystemMonitor/main.cpp50
7 files changed, 207 insertions, 54 deletions
diff --git a/Applications/SystemMonitor/GraphWidget.cpp b/Applications/SystemMonitor/GraphWidget.cpp
index 7bfb2d5bbf..5c1d9982d4 100644
--- a/Applications/SystemMonitor/GraphWidget.cpp
+++ b/Applications/SystemMonitor/GraphWidget.cpp
@@ -27,6 +27,7 @@
#include "GraphWidget.h"
#include <LibGUI/Painter.h>
#include <LibGfx/Font.h>
+#include <LibGfx/Path.h>
GraphWidget::GraphWidget()
{
@@ -36,9 +37,9 @@ GraphWidget::~GraphWidget()
{
}
-void GraphWidget::add_value(int value)
+void GraphWidget::add_value(Vector<int, 1>&& value)
{
- m_values.enqueue(value);
+ m_values.enqueue(move(value));
update();
}
@@ -48,28 +49,115 @@ void GraphWidget::paint_event(GUI::PaintEvent& event)
GUI::Painter painter(*this);
painter.add_clip_rect(event.rect());
painter.add_clip_rect(frame_inner_rect());
- painter.fill_rect(event.rect(), Color::Black);
+ painter.fill_rect(event.rect(), m_background_color);
auto inner_rect = frame_inner_rect();
float scale = (float)inner_rect.height() / (float)m_max;
- Gfx::IntPoint prev_point;
- for (size_t i = 0; i < m_values.size(); ++i) {
- int x = inner_rect.right() - (i * 2) + 1;
- if (x < 0)
- break;
- float scaled_value = (float)m_values.at(m_values.size() - i - 1) * scale;
- Gfx::IntPoint point = { x, inner_rect.bottom() - (int)scaled_value };
- if (i != 0)
- painter.draw_line(prev_point, point, m_graph_color);
- prev_point = point;
+ if (!m_values.is_empty()) {
+ // Draw one set of values at a time
+ for (size_t k = 0; k < m_value_format.size(); k++) {
+ const auto& format = m_value_format[k];
+ if (format.line_color == Color::Transparent && format.background_color == Color::Transparent)
+ continue;
+ m_calculated_points.clear_with_capacity();
+ for (size_t i = 0; i < m_values.size(); i++) {
+ int x = inner_rect.right() - (i * 2) + 1;
+ if (x < 0)
+ break;
+ const auto& current_values = m_values.at(m_values.size() - i - 1);
+ if (current_values.size() <= k) {
+ // Don't have a data point
+ m_calculated_points.append({ -1, -1 });
+ continue;
+ }
+ float value = current_values[k];
+ if (m_stack_values) {
+ for (size_t l = k + 1; l < current_values.size(); l++)
+ value += current_values[l];
+ }
+ float scaled_value = value * scale;
+ Gfx::IntPoint current_point { x, inner_rect.bottom() - (int)scaled_value };
+ m_calculated_points.append(current_point);
+ }
+ ASSERT(m_calculated_points.size() <= m_values.size());
+ if (format.background_color != Color::Transparent) {
+ // Fill the background for the area we have values for
+ Gfx::Path path;
+ size_t points_in_path = 0;
+ bool started_path = false;
+ const Gfx::IntPoint* current_point = nullptr;
+ const Gfx::IntPoint* first_point = nullptr;
+ auto check_fill_area = [&]() {
+ if (!started_path)
+ return;
+ if (points_in_path > 1) {
+ ASSERT(current_point);
+ ASSERT(first_point);
+ path.line_to({ current_point->x() - 1, inner_rect.bottom() + 1 });
+ path.line_to({ first_point->x() + 1, inner_rect.bottom() + 1 });
+ path.close();
+ painter.fill_path(path, format.background_color, Gfx::Painter::WindingRule::EvenOdd);
+ } else if (points_in_path == 1 && current_point) {
+ // Can't fill any area, we only have one data point.
+ // Just draw a vertical line as a "fill"...
+ painter.draw_line(*current_point, { current_point->x(), inner_rect.bottom() }, format.background_color);
+ }
+ path = {};
+ points_in_path = 0;
+ first_point = nullptr;
+ started_path = false;
+ };
+ for (size_t i = 0; i < m_calculated_points.size(); i++) {
+ current_point = &m_calculated_points[i];
+ if (current_point->x() < 0) {
+ check_fill_area();
+ continue;
+ }
+ if (!started_path) {
+ path.move_to({ current_point->x() + 1, current_point->y() });
+ points_in_path = 1;
+ first_point = current_point;
+ started_path = true;
+ } else {
+ path.line_to({ current_point->x(), current_point->y() });
+ points_in_path++;
+ }
+ }
+ check_fill_area();
+ }
+ if (format.line_color != Color::Transparent) {
+ // Draw the line for the data points we have
+ const Gfx::IntPoint* previous_point = nullptr;
+ for (size_t i = 0; i < m_calculated_points.size(); i++) {
+ const auto& current_point = m_calculated_points[i];
+ if (current_point.x() < 0) {
+ previous_point = nullptr;
+ continue;
+ }
+ if (previous_point)
+ painter.draw_line(*previous_point, current_point, format.line_color);
+ previous_point = &current_point;
+ }
+ }
+ }
}
- if (!m_values.is_empty() && text_formatter) {
- Gfx::IntRect text_rect = inner_rect.shrunken(8, 8);
- text_rect.set_height(font().glyph_height());
- auto text = text_formatter(m_values.last(), m_max);
- painter.draw_text(text_rect.translated(1, 1), text.characters(), Gfx::TextAlignment::CenterRight, Color::Black);
- painter.draw_text(text_rect, text.characters(), Gfx::TextAlignment::CenterRight, m_text_color);
+ if (!m_values.is_empty() && !m_value_format.is_empty()) {
+ const auto& current_values = m_values.last();
+ int y = 0;
+ for (size_t i = 0; i < min(m_value_format.size(), current_values.size()); i++) {
+ const auto& format = m_value_format[i];
+ if (!format.text_formatter)
+ continue;
+ auto constrain_rect = inner_rect.shrunken(8, 8);
+ auto text_rect = constrain_rect.translated(0, y).intersected(constrain_rect);
+ text_rect.set_height(font().glyph_height());
+ auto text = format.text_formatter(current_values[i]);
+ if (format.text_shadow_color != Color::Transparent)
+ painter.draw_text(text_rect.translated(1, 1), text.characters(), Gfx::TextAlignment::CenterRight, format.text_shadow_color);
+ painter.draw_text(text_rect, text.characters(), Gfx::TextAlignment::CenterRight, format.line_color);
+ y += text_rect.height() + 4;
+ }
}
}
diff --git a/Applications/SystemMonitor/GraphWidget.h b/Applications/SystemMonitor/GraphWidget.h
index 598e44b2a0..fc40f59bbd 100644
--- a/Applications/SystemMonitor/GraphWidget.h
+++ b/Applications/SystemMonitor/GraphWidget.h
@@ -35,12 +35,25 @@ public:
virtual ~GraphWidget() override;
void set_max(int max) { m_max = max; }
- void add_value(int);
+ int max() const { return m_max; }
- void set_graph_color(Color color) { m_graph_color = color; }
- void set_text_color(Color color) { m_text_color = color; }
+ void add_value(Vector<int, 1>&&);
- Function<String(int value, int max)> text_formatter;
+ void set_background_color(Color color) { m_background_color = color; }
+
+ struct ValueFormat {
+ Color line_color { Color::Transparent };
+ Color background_color { Color::Transparent };
+ Color text_shadow_color { Color::Transparent };
+ Function<String(int)> text_formatter;
+ };
+ void set_value_format(size_t index, ValueFormat&& format)
+ {
+ if (m_value_format.size() <= index)
+ m_value_format.resize(index + 1);
+ m_value_format[index] = move(format);
+ }
+ void set_stack_values(bool stack_values) { m_stack_values = stack_values; }
private:
explicit GraphWidget();
@@ -48,7 +61,10 @@ private:
virtual void paint_event(GUI::PaintEvent&) override;
int m_max { 100 };
- CircularQueue<int, 4000> m_values;
- Color m_graph_color;
- Color m_text_color;
+ Vector<ValueFormat, 1> m_value_format;
+ CircularQueue<Vector<int, 1>, 4000> m_values;
+ Color m_background_color { Color::Black };
+ bool m_stack_values { false };
+
+ Vector<Gfx::IntPoint, 1> m_calculated_points;
};
diff --git a/Applications/SystemMonitor/MemoryStatsWidget.cpp b/Applications/SystemMonitor/MemoryStatsWidget.cpp
index feaf7631f1..6675ea8c4a 100644
--- a/Applications/SystemMonitor/MemoryStatsWidget.cpp
+++ b/Applications/SystemMonitor/MemoryStatsWidget.cpp
@@ -69,7 +69,8 @@ MemoryStatsWidget::MemoryStatsWidget(GraphWidget& graph)
return label;
};
- m_user_physical_pages_label = build_widgets_for_label("Userspace physical:");
+ m_user_physical_pages_label = build_widgets_for_label("Physical memory:");
+ m_user_physical_pages_committed_label = build_widgets_for_label("Committed memory:");
m_supervisor_physical_pages_label = build_widgets_for_label("Supervisor physical:");
m_kmalloc_space_label = build_widgets_for_label("Kernel heap:");
m_kmalloc_count_label = build_widgets_for_label("Calls kmalloc:");
@@ -109,22 +110,29 @@ void MemoryStatsWidget::refresh()
unsigned kmalloc_available = json.get("kmalloc_available").to_u32();
unsigned user_physical_allocated = json.get("user_physical_allocated").to_u32();
unsigned user_physical_available = json.get("user_physical_available").to_u32();
+ unsigned user_physical_committed = json.get("user_physical_committed").to_u32();
+ unsigned user_physical_uncommitted = json.get("user_physical_uncommitted").to_u32();
unsigned super_physical_alloc = json.get("super_physical_allocated").to_u32();
unsigned super_physical_free = json.get("super_physical_available").to_u32();
unsigned kmalloc_call_count = json.get("kmalloc_call_count").to_u32();
unsigned kfree_call_count = json.get("kfree_call_count").to_u32();
- size_t kmalloc_sum_available = kmalloc_allocated + kmalloc_available;
- size_t user_pages_available = user_physical_allocated + user_physical_available;
- size_t supervisor_pages_available = super_physical_alloc + super_physical_free;
+ size_t kmalloc_bytes_total = kmalloc_allocated + kmalloc_available;
+ size_t user_physical_pages_total = user_physical_allocated + user_physical_available;
+ size_t supervisor_pages_total = super_physical_alloc + super_physical_free;
- m_kmalloc_space_label->set_text(String::formatted("{}K/{}K", bytes_to_kb(kmalloc_allocated), bytes_to_kb(kmalloc_sum_available)));
- m_user_physical_pages_label->set_text(String::formatted("{}K/{}K", page_count_to_kb(user_physical_allocated), page_count_to_kb(user_pages_available)));
- m_supervisor_physical_pages_label->set_text(String::formatted("{}K/{}K", page_count_to_kb(super_physical_alloc), page_count_to_kb(supervisor_pages_available)));
+ size_t physical_pages_total = user_physical_pages_total + supervisor_pages_total;
+ size_t physical_pages_in_use = user_physical_allocated + super_physical_alloc;
+ size_t total_userphysical_and_swappable_pages = user_physical_allocated + user_physical_committed + user_physical_uncommitted;
+
+ m_kmalloc_space_label->set_text(String::formatted("{}K/{}K", bytes_to_kb(kmalloc_allocated), bytes_to_kb(kmalloc_bytes_total)));
+ m_user_physical_pages_label->set_text(String::formatted("{}K/{}K", page_count_to_kb(physical_pages_in_use), page_count_to_kb(physical_pages_total)));
+ m_user_physical_pages_committed_label->set_text(String::formatted("{}K", page_count_to_kb(user_physical_committed)));
+ m_supervisor_physical_pages_label->set_text(String::formatted("{}K/{}K", page_count_to_kb(super_physical_alloc), page_count_to_kb(supervisor_pages_total)));
m_kmalloc_count_label->set_text(String::formatted("{}", kmalloc_call_count));
m_kfree_count_label->set_text(String::formatted("{}", kfree_call_count));
m_kmalloc_difference_label->set_text(String::formatted("{:+}", kmalloc_call_count - kfree_call_count));
- m_graph.set_max(page_count_to_kb(user_pages_available));
- m_graph.add_value(page_count_to_kb(user_physical_allocated));
+ m_graph.set_max(page_count_to_kb(total_userphysical_and_swappable_pages) + bytes_to_kb(kmalloc_bytes_total));
+ m_graph.add_value({ (int)page_count_to_kb(user_physical_committed), (int)page_count_to_kb(user_physical_allocated), (int)bytes_to_kb(kmalloc_bytes_total) });
}
diff --git a/Applications/SystemMonitor/MemoryStatsWidget.h b/Applications/SystemMonitor/MemoryStatsWidget.h
index 18c3f7a974..e915730257 100644
--- a/Applications/SystemMonitor/MemoryStatsWidget.h
+++ b/Applications/SystemMonitor/MemoryStatsWidget.h
@@ -44,6 +44,7 @@ private:
GraphWidget& m_graph;
RefPtr<GUI::Label> m_user_physical_pages_label;
+ RefPtr<GUI::Label> m_user_physical_pages_committed_label;
RefPtr<GUI::Label> m_supervisor_physical_pages_label;
RefPtr<GUI::Label> m_kmalloc_space_label;
RefPtr<GUI::Label> m_kmalloc_count_label;
diff --git a/Applications/SystemMonitor/ProcessModel.cpp b/Applications/SystemMonitor/ProcessModel.cpp
index 1c9f30a0ef..c12a5503ab 100644
--- a/Applications/SystemMonitor/ProcessModel.cpp
+++ b/Applications/SystemMonitor/ProcessModel.cpp
@@ -352,12 +352,15 @@ void ProcessModel::update()
auto previous_pid_count = m_pids.size();
auto all_processes = Core::ProcessStatisticsReader::get_all(m_proc_all);
- u64 last_sum_ticks_scheduled = 0;
- for (auto& it : m_threads)
- last_sum_ticks_scheduled += it.value->current_state.ticks_user + it.value->current_state.ticks_kernel;
+ u64 last_sum_ticks_scheduled = 0, last_sum_ticks_scheduled_kernel = 0;
+ for (auto& it : m_threads) {
+ auto& current_state = it.value->current_state;
+ last_sum_ticks_scheduled += current_state.ticks_user + current_state.ticks_kernel;
+ last_sum_ticks_scheduled_kernel += current_state.ticks_kernel;
+ }
HashTable<PidAndTid> live_pids;
- u64 sum_ticks_scheduled = 0;
+ u64 sum_ticks_scheduled = 0, sum_ticks_scheduled_kernel = 0;
if (all_processes.has_value()) {
for (auto& it : all_processes.value()) {
for (auto& thread : it.value.threads) {
@@ -399,6 +402,7 @@ void ProcessModel::update()
state.effective_priority = thread.effective_priority;
state.state = thread.state;
sum_ticks_scheduled += thread.ticks_user + thread.ticks_kernel;
+ sum_ticks_scheduled_kernel += thread.ticks_kernel;
{
auto pit = m_threads.find({ it.value.pid, thread.tid });
if (pit == m_threads.end())
@@ -415,8 +419,10 @@ void ProcessModel::update()
}
m_pids.clear();
- for (auto& c : m_cpus)
+ for (auto& c : m_cpus) {
c.total_cpu_percent = 0.0;
+ c.total_cpu_percent_kernel = 0.0;
+ }
Vector<PidAndTid, 16> pids_to_remove;
for (auto& it : m_threads) {
if (!live_pids.contains(it.key)) {
@@ -424,11 +430,15 @@ void ProcessModel::update()
continue;
}
auto& process = *it.value;
- u32 times_scheduled_diff = (process.current_state.ticks_user + process.current_state.ticks_kernel)
+ u32 ticks_scheduled_diff = (process.current_state.ticks_user + process.current_state.ticks_kernel)
- (process.previous_state.ticks_user + process.previous_state.ticks_kernel);
- process.current_state.cpu_percent = ((float)times_scheduled_diff * 100) / (float)(sum_ticks_scheduled - last_sum_ticks_scheduled);
+ u32 ticks_scheduled_diff_kernel = process.current_state.ticks_kernel - process.previous_state.ticks_kernel;
+ process.current_state.cpu_percent = ((float)ticks_scheduled_diff * 100) / (float)(sum_ticks_scheduled - last_sum_ticks_scheduled);
+ process.current_state.cpu_percent_kernel = ((float)ticks_scheduled_diff_kernel * 100) / (float)(sum_ticks_scheduled - last_sum_ticks_scheduled);
if (it.key.pid != 0) {
- m_cpus[process.current_state.cpu].total_cpu_percent += process.current_state.cpu_percent;
+ auto& cpu_info = m_cpus[process.current_state.cpu];
+ cpu_info.total_cpu_percent += process.current_state.cpu_percent;
+ cpu_info.total_cpu_percent_kernel += process.current_state.cpu_percent_kernel;
m_pids.append(it.key);
}
}
diff --git a/Applications/SystemMonitor/ProcessModel.h b/Applications/SystemMonitor/ProcessModel.h
index d54fecdb4b..11f39cac76 100644
--- a/Applications/SystemMonitor/ProcessModel.h
+++ b/Applications/SystemMonitor/ProcessModel.h
@@ -95,6 +95,7 @@ public:
struct CpuInfo {
u32 id;
float total_cpu_percent { 0.0 };
+ float total_cpu_percent_kernel { 0.0 };
CpuInfo(u32 id)
: id(id)
@@ -144,6 +145,7 @@ private:
unsigned file_read_bytes;
unsigned file_write_bytes;
float cpu_percent;
+ float cpu_percent_kernel;
};
struct Thread {
diff --git a/Applications/SystemMonitor/main.cpp b/Applications/SystemMonitor/main.cpp
index ef2fdcea71..9464d60dd1 100644
--- a/Applications/SystemMonitor/main.cpp
+++ b/Applications/SystemMonitor/main.cpp
@@ -549,16 +549,26 @@ NonnullRefPtr<GUI::Widget> build_graphs_tab()
for (size_t i = 0; i < ProcessModel::the().cpus().size(); i++) {
auto& cpu_graph = cpu_graph_group_box.add<GraphWidget>();
cpu_graph.set_max(100);
- cpu_graph.set_text_color(Color::Green);
- cpu_graph.set_graph_color(Color::from_rgb(0x00bb00));
- cpu_graph.text_formatter = [](int value, int) {
- return String::formatted("{}%", value);
- };
+ cpu_graph.set_background_color(Color::White);
+ cpu_graph.set_value_format(0, {
+ .line_color = Color::Blue,
+ .background_color = Color::from_rgb(0xaaaaff),
+ .text_formatter = [](int value) {
+ return String::formatted("Total: {}%", value);
+ },
+ });
+ cpu_graph.set_value_format(1, {
+ .line_color = Color::Red,
+ .background_color = Color::from_rgb(0xffaaaa),
+ .text_formatter = [](int value) {
+ return String::formatted("Kernel: {}%", value);
+ },
+ });
cpu_graphs.append(&cpu_graph);
}
ProcessModel::the().on_cpu_info_change = [cpu_graphs](const NonnullOwnPtrVector<ProcessModel::CpuInfo>& cpus) {
for (size_t i = 0; i < cpus.size(); i++)
- cpu_graphs[i]->add_value(cpus[i].total_cpu_percent);
+ cpu_graphs[i]->add_value({ (int)cpus[i].total_cpu_percent, (int)cpus[i].total_cpu_percent_kernel });
};
auto& memory_graph_group_box = self.add<GUI::GroupBox>("Memory usage");
@@ -566,11 +576,29 @@ NonnullRefPtr<GUI::Widget> build_graphs_tab()
memory_graph_group_box.layout()->set_margins({ 6, 16, 6, 6 });
memory_graph_group_box.set_fixed_height(120);
auto& memory_graph = memory_graph_group_box.add<GraphWidget>();
- memory_graph.set_text_color(Color::Cyan);
- memory_graph.set_graph_color(Color::from_rgb(0x00bbbb));
- memory_graph.text_formatter = [](int value, int max) {
- return String::formatted("{} / {} KiB", value, max);
- };
+ memory_graph.set_background_color(Color::White);
+ memory_graph.set_stack_values(true);
+ memory_graph.set_value_format(0, {
+ .line_color = Color::from_rgb(0x619910),
+ .background_color = Color::from_rgb(0xbbffbb),
+ .text_formatter = [&memory_graph](int value) {
+ return String::formatted("Committed: {} KiB", value);
+ },
+ });
+ memory_graph.set_value_format(1, {
+ .line_color = Color::Blue,
+ .background_color = Color::from_rgb(0xaaaaff),
+ .text_formatter = [&memory_graph](int value) {
+ return String::formatted("Allocated: {} KiB", value);
+ },
+ });
+ memory_graph.set_value_format(2, {
+ .line_color = Color::Red,
+ .background_color = Color::from_rgb(0xffaaaa),
+ .text_formatter = [&memory_graph](int value) {
+ return String::formatted("Kernel heap: {} KiB", value);
+ },
+ });
self.add<MemoryStatsWidget>(memory_graph);
};