mirror of
https://github.com/RGBCube/serenity
synced 2025-05-25 18:05:07 +00:00
ResourceGraph: Handle low memory situations bit better
Rather than crashing with an ASSERT, when we can't read from /proc we draw the graph with red instead. This also alerts the user that memory is very low.
This commit is contained in:
parent
f98ca35b83
commit
754bf22da7
1 changed files with 48 additions and 23 deletions
|
@ -51,11 +51,12 @@ class GraphWidget final : public GUI::Frame {
|
||||||
public:
|
public:
|
||||||
static constexpr size_t history_size = 30;
|
static constexpr size_t history_size = 30;
|
||||||
|
|
||||||
GraphWidget(GraphType graph_type, Optional<Gfx::Color> graph_color)
|
GraphWidget(GraphType graph_type, Optional<Gfx::Color> graph_color, Optional<Gfx::Color> graph_error_color)
|
||||||
: m_graph_type(graph_type)
|
: m_graph_type(graph_type)
|
||||||
{
|
{
|
||||||
set_frame_thickness(1);
|
set_frame_thickness(1);
|
||||||
m_graph_color = graph_color.value_or(palette().menu_selection());
|
m_graph_color = graph_color.value_or(palette().menu_selection());
|
||||||
|
m_graph_error_color = graph_error_color.value_or(Color::Red);
|
||||||
start_timer(1000);
|
start_timer(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,24 +67,32 @@ private:
|
||||||
case GraphType::CPU: {
|
case GraphType::CPU: {
|
||||||
unsigned busy;
|
unsigned busy;
|
||||||
unsigned idle;
|
unsigned idle;
|
||||||
get_cpu_usage(busy, idle);
|
if (get_cpu_usage(busy, idle)) {
|
||||||
unsigned busy_diff = busy - m_last_cpu_busy;
|
unsigned busy_diff = busy - m_last_cpu_busy;
|
||||||
unsigned idle_diff = idle - m_last_cpu_idle;
|
unsigned idle_diff = idle - m_last_cpu_idle;
|
||||||
m_last_cpu_busy = busy;
|
m_last_cpu_busy = busy;
|
||||||
m_last_cpu_idle = idle;
|
m_last_cpu_idle = idle;
|
||||||
float cpu = (float)busy_diff / (float)(busy_diff + idle_diff);
|
float cpu = (float)busy_diff / (float)(busy_diff + idle_diff);
|
||||||
m_history.enqueue(cpu);
|
m_history.enqueue(cpu);
|
||||||
m_tooltip = String::format("CPU usage: %.1f%%", 100 * cpu);
|
m_tooltip = String::format("CPU usage: %.1f%%", 100 * cpu);
|
||||||
|
} else {
|
||||||
|
m_history.enqueue(-1);
|
||||||
|
m_tooltip = StringView("Unable to determine CPU usage");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GraphType::Memory: {
|
case GraphType::Memory: {
|
||||||
unsigned allocated;
|
unsigned allocated;
|
||||||
unsigned available;
|
unsigned available;
|
||||||
get_memory_usage(allocated, available);
|
if (get_memory_usage(allocated, available)) {
|
||||||
float total_memory = allocated + available;
|
float total_memory = allocated + available;
|
||||||
float memory = (float)allocated / total_memory;
|
float memory = (float)allocated / total_memory;
|
||||||
m_history.enqueue(memory);
|
m_history.enqueue(memory);
|
||||||
m_tooltip = String::format("Memory: %.1f MiB of %.1f MiB in use", (float)allocated / MiB, total_memory / MiB);
|
m_tooltip = String::format("Memory: %.1f MiB of %.1f MiB in use", (float)allocated / MiB, total_memory / MiB);
|
||||||
|
} else {
|
||||||
|
m_history.enqueue(-1);
|
||||||
|
m_tooltip = StringView("Unable to determine memory usage");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -103,10 +112,17 @@ private:
|
||||||
int i = m_history.capacity() - m_history.size();
|
int i = m_history.capacity() - m_history.size();
|
||||||
auto rect = frame_inner_rect();
|
auto rect = frame_inner_rect();
|
||||||
for (auto value : m_history) {
|
for (auto value : m_history) {
|
||||||
painter.draw_line(
|
if (value >= 0) {
|
||||||
{ rect.x() + i, rect.bottom() },
|
painter.draw_line(
|
||||||
{ rect.x() + i, rect.top() + (int)(round(rect.height() - (value * rect.height()))) },
|
{ rect.x() + i, rect.bottom() },
|
||||||
m_graph_color);
|
{ rect.x() + i, rect.top() + (int)(round(rect.height() - (value * rect.height()))) },
|
||||||
|
m_graph_color);
|
||||||
|
} else {
|
||||||
|
painter.draw_line(
|
||||||
|
{ rect.x() + i, rect.top() },
|
||||||
|
{ rect.x() + i, rect.bottom() },
|
||||||
|
m_graph_error_color);
|
||||||
|
}
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,12 +141,14 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_cpu_usage(unsigned& busy, unsigned& idle)
|
static bool get_cpu_usage(unsigned& busy, unsigned& idle)
|
||||||
{
|
{
|
||||||
busy = 0;
|
busy = 0;
|
||||||
idle = 0;
|
idle = 0;
|
||||||
|
|
||||||
auto all_processes = Core::ProcessStatisticsReader::get_all();
|
auto all_processes = Core::ProcessStatisticsReader::get_all();
|
||||||
|
if (all_processes.is_empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
for (auto& it : all_processes) {
|
for (auto& it : all_processes) {
|
||||||
for (auto& jt : it.value.threads) {
|
for (auto& jt : it.value.threads) {
|
||||||
|
@ -140,13 +158,14 @@ private:
|
||||||
busy += jt.ticks_user + jt.ticks_kernel;
|
busy += jt.ticks_user + jt.ticks_kernel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_memory_usage(unsigned& allocated, unsigned& available)
|
static bool get_memory_usage(unsigned& allocated, unsigned& available)
|
||||||
{
|
{
|
||||||
auto proc_memstat = Core::File::construct("/proc/memstat");
|
auto proc_memstat = Core::File::construct("/proc/memstat");
|
||||||
if (!proc_memstat->open(Core::IODevice::OpenMode::ReadOnly))
|
if (!proc_memstat->open(Core::IODevice::OpenMode::ReadOnly))
|
||||||
ASSERT_NOT_REACHED();
|
return false;
|
||||||
|
|
||||||
auto file_contents = proc_memstat->read_all();
|
auto file_contents = proc_memstat->read_all();
|
||||||
auto json = JsonValue::from_string(file_contents);
|
auto json = JsonValue::from_string(file_contents);
|
||||||
|
@ -155,10 +174,12 @@ private:
|
||||||
unsigned user_physical_available = json.value().as_object().get("user_physical_available").to_u32();
|
unsigned user_physical_available = json.value().as_object().get("user_physical_available").to_u32();
|
||||||
allocated = (user_physical_allocated * PAGE_SIZE);
|
allocated = (user_physical_allocated * PAGE_SIZE);
|
||||||
available = (user_physical_available * PAGE_SIZE);
|
available = (user_physical_available * PAGE_SIZE);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphType m_graph_type;
|
GraphType m_graph_type;
|
||||||
Gfx::Color m_graph_color;
|
Gfx::Color m_graph_color;
|
||||||
|
Gfx::Color m_graph_error_color;
|
||||||
CircularQueue<float, history_size> m_history;
|
CircularQueue<float, history_size> m_history;
|
||||||
unsigned m_last_cpu_busy { 0 };
|
unsigned m_last_cpu_busy { 0 };
|
||||||
unsigned m_last_cpu_idle { 0 };
|
unsigned m_last_cpu_idle { 0 };
|
||||||
|
@ -183,11 +204,13 @@ int main(int argc, char** argv)
|
||||||
bool memory = false;
|
bool memory = false;
|
||||||
const char* name = nullptr;
|
const char* name = nullptr;
|
||||||
const char* color = nullptr;
|
const char* color = nullptr;
|
||||||
|
const char* error_color = nullptr;
|
||||||
Core::ArgsParser args_parser;
|
Core::ArgsParser args_parser;
|
||||||
args_parser.add_option(cpu, "Show CPU usage", "cpu", 'C');
|
args_parser.add_option(cpu, "Show CPU usage", "cpu", 'C');
|
||||||
args_parser.add_option(memory, "Show memory usage", "memory", 'M');
|
args_parser.add_option(memory, "Show memory usage", "memory", 'M');
|
||||||
args_parser.add_option(name, "Applet name used by WindowServer.ini to set the applet order", "name", 'n', "name");
|
args_parser.add_option(name, "Applet name used by WindowServer.ini to set the applet order", "name", 'n', "name");
|
||||||
args_parser.add_option(color, "Graph color", "color", 'c', "color");
|
args_parser.add_option(color, "Graph color", "color", 'c', "color");
|
||||||
|
args_parser.add_option(error_color, "Graph color (error)", "error-color", 'e', "error-color");
|
||||||
args_parser.parse(argc, argv);
|
args_parser.parse(argc, argv);
|
||||||
|
|
||||||
if (!cpu && !memory) {
|
if (!cpu && !memory) {
|
||||||
|
@ -207,16 +230,18 @@ int main(int argc, char** argv)
|
||||||
if (name == nullptr)
|
if (name == nullptr)
|
||||||
name = "ResourceGraph";
|
name = "ResourceGraph";
|
||||||
|
|
||||||
Optional<Gfx::Color> graph_color;
|
Optional<Gfx::Color> graph_color, graph_error_color;
|
||||||
if (color != nullptr)
|
if (color != nullptr)
|
||||||
graph_color = Gfx::Color::from_string(color);
|
graph_color = Gfx::Color::from_string(color);
|
||||||
|
if (error_color != nullptr)
|
||||||
|
graph_error_color = Gfx::Color::from_string(error_color);
|
||||||
|
|
||||||
auto window = GUI::Window::construct();
|
auto window = GUI::Window::construct();
|
||||||
window->set_title(name);
|
window->set_title(name);
|
||||||
window->set_window_type(GUI::WindowType::MenuApplet);
|
window->set_window_type(GUI::WindowType::MenuApplet);
|
||||||
window->resize(GraphWidget::history_size + 2, 16);
|
window->resize(GraphWidget::history_size + 2, 16);
|
||||||
|
|
||||||
window->set_main_widget<GraphWidget>(graph_type, graph_color);
|
window->set_main_widget<GraphWidget>(graph_type, graph_color, graph_error_color);
|
||||||
window->show();
|
window->show();
|
||||||
|
|
||||||
if (unveil("/res", "r") < 0) {
|
if (unveil("/res", "r") < 0) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue