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 #include +#include GraphWidget::GraphWidget() { @@ -36,9 +37,9 @@ GraphWidget::~GraphWidget() { } -void GraphWidget::add_value(int value) +void GraphWidget::add_value(Vector&& 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 = ¤t_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&&); - Function 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 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 m_values; - Color m_graph_color; - Color m_text_color; + Vector m_value_format; + CircularQueue, 4000> m_values; + Color m_background_color { Color::Black }; + bool m_stack_values { false }; + + Vector 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 m_user_physical_pages_label; + RefPtr m_user_physical_pages_committed_label; RefPtr m_supervisor_physical_pages_label; RefPtr m_kmalloc_space_label; RefPtr 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 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 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 build_graphs_tab() for (size_t i = 0; i < ProcessModel::the().cpus().size(); i++) { auto& cpu_graph = cpu_graph_group_box.add(); 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& 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("Memory usage"); @@ -566,11 +576,29 @@ NonnullRefPtr 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(); - 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(memory_graph); };