Serenity Operating System
at master 146 lines 6.0 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include "MemoryStatsWidget.h" 9#include "GraphWidget.h" 10#include <AK/JsonObject.h> 11#include <AK/NumberFormat.h> 12#include <LibCore/DeprecatedFile.h> 13#include <LibCore/Object.h> 14#include <LibGUI/BoxLayout.h> 15#include <LibGUI/Label.h> 16#include <LibGUI/Painter.h> 17#include <LibGfx/Font/FontDatabase.h> 18#include <LibGfx/StylePainter.h> 19 20REGISTER_WIDGET(SystemMonitor, MemoryStatsWidget) 21 22namespace SystemMonitor { 23 24static MemoryStatsWidget* s_the; 25 26MemoryStatsWidget* MemoryStatsWidget::the() 27{ 28 return s_the; 29} 30 31MemoryStatsWidget::MemoryStatsWidget() 32 : MemoryStatsWidget(nullptr) 33{ 34} 35 36MemoryStatsWidget::MemoryStatsWidget(GraphWidget* graph) 37 : m_graph(graph) 38{ 39 VERIFY(!s_the); 40 s_the = this; 41 42 REGISTER_STRING_PROPERTY("memory_graph", graph_widget_name, set_graph_widget_via_name); 43 44 set_fixed_height(110); 45 46 set_layout<GUI::VerticalBoxLayout>(GUI::Margins { 8, 0, 0 }, 3); 47 48 auto build_widgets_for_label = [this](DeprecatedString const& description) -> RefPtr<GUI::Label> { 49 auto& container = add<GUI::Widget>(); 50 container.set_layout<GUI::HorizontalBoxLayout>(); 51 container.set_fixed_size(275, 12); 52 auto& description_label = container.add<GUI::Label>(description); 53 description_label.set_font(Gfx::FontDatabase::default_font().bold_variant()); 54 description_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); 55 auto& label = container.add<GUI::Label>(); 56 label.set_text_alignment(Gfx::TextAlignment::CenterRight); 57 return label; 58 }; 59 60 m_physical_pages_label = build_widgets_for_label("Physical memory:"); 61 m_physical_pages_committed_label = build_widgets_for_label("Committed memory:"); 62 m_kmalloc_space_label = build_widgets_for_label("Kernel heap:"); 63 m_kmalloc_count_label = build_widgets_for_label("Calls kmalloc:"); 64 m_kfree_count_label = build_widgets_for_label("Calls kfree:"); 65 m_kmalloc_difference_label = build_widgets_for_label("Difference:"); 66 67 refresh(); 68} 69 70void MemoryStatsWidget::set_graph_widget(GraphWidget& graph) 71{ 72 m_graph = &graph; 73} 74 75void MemoryStatsWidget::set_graph_widget_via_name(DeprecatedString name) 76{ 77 m_graph_widget_name = move(name); 78 if (!m_graph_widget_name.is_null()) { 79 // FIXME: We assume here that the graph widget is a sibling or descendant of a sibling. This prevents more complex hierarchies. 80 auto* maybe_graph = parent_widget()->find_descendant_of_type_named<GraphWidget>(m_graph_widget_name); 81 if (maybe_graph) { 82 m_graph = maybe_graph; 83 // Delete the stored graph name to signal that we found the widget 84 m_graph_widget_name = {}; 85 } else { 86 dbgln("MemoryStatsWidget: Couldn't find graph of name '{}', retrying later.", m_graph_widget_name); 87 } 88 } 89} 90 91DeprecatedString MemoryStatsWidget::graph_widget_name() 92{ 93 if (m_graph) 94 return m_graph->name(); 95 return m_graph_widget_name; 96} 97 98static inline u64 page_count_to_bytes(size_t count) 99{ 100 return count * 4096; 101} 102 103void MemoryStatsWidget::refresh() 104{ 105 auto proc_memstat = Core::DeprecatedFile::construct("/sys/kernel/memstat"); 106 if (!proc_memstat->open(Core::OpenMode::ReadOnly)) 107 VERIFY_NOT_REACHED(); 108 109 auto file_contents = proc_memstat->read_all(); 110 auto json_result = JsonValue::from_string(file_contents).release_value_but_fixme_should_propagate_errors(); 111 auto const& json = json_result.as_object(); 112 113 u32 kmalloc_allocated = json.get_u32("kmalloc_allocated"sv).value_or(0); 114 u32 kmalloc_available = json.get_u32("kmalloc_available"sv).value_or(0); 115 u64 physical_allocated = json.get_u64("physical_allocated"sv).value_or(0); 116 u64 physical_available = json.get_u64("physical_available"sv).value_or(0); 117 u64 physical_committed = json.get_u64("physical_committed"sv).value_or(0); 118 u64 physical_uncommitted = json.get_u64("physical_uncommitted"sv).value_or(0); 119 u32 kmalloc_call_count = json.get_u32("kmalloc_call_count"sv).value_or(0); 120 u32 kfree_call_count = json.get_u32("kfree_call_count"sv).value_or(0); 121 122 u64 kmalloc_bytes_total = kmalloc_allocated + kmalloc_available; 123 u64 physical_pages_total = physical_allocated + physical_available; 124 125 u64 physical_pages_in_use = physical_allocated; 126 u64 total_userphysical_and_swappable_pages = physical_allocated + physical_committed + physical_uncommitted; 127 128 m_kmalloc_space_label->set_text(DeprecatedString::formatted("{}/{}", human_readable_size(kmalloc_allocated), human_readable_size(kmalloc_bytes_total))); 129 m_physical_pages_label->set_text(DeprecatedString::formatted("{}/{}", human_readable_size(page_count_to_bytes(physical_pages_in_use)), human_readable_size(page_count_to_bytes(physical_pages_total)))); 130 m_physical_pages_committed_label->set_text(DeprecatedString::formatted("{}", human_readable_size(page_count_to_bytes(physical_committed)))); 131 m_kmalloc_count_label->set_text(DeprecatedString::formatted("{}", kmalloc_call_count)); 132 m_kfree_count_label->set_text(DeprecatedString::formatted("{}", kfree_call_count)); 133 m_kmalloc_difference_label->set_text(DeprecatedString::formatted("{:+}", kmalloc_call_count - kfree_call_count)); 134 135 // Because the initialization order of us and the graph is unknown, we might get a couple of updates where the graph widget lookup fails. 136 // Therefore, we can retry indefinitely. (Should not be too much of a performance hit, as we don't update that often.) 137 if (!m_graph) 138 set_graph_widget_via_name(move(m_graph_widget_name)); 139 140 if (m_graph) { 141 m_graph->set_max(page_count_to_bytes(total_userphysical_and_swappable_pages) + kmalloc_bytes_total); 142 m_graph->add_value({ page_count_to_bytes(physical_committed), page_count_to_bytes(physical_allocated), kmalloc_bytes_total }); 143 } 144} 145 146}