2020-01-18 11:38:21 +03:00
/*
2021-04-04 21:46:46 +03:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2021-09-18 21:57:54 +03:00
* Copyright ( c ) 2021 , Undefine < cqundefine @ gmail . com >
2022-02-10 22:28:48 +03:00
* Copyright ( c ) 2022 , the SerenityOS developers .
2020-01-18 11:38:21 +03:00
*
2021-04-22 11:24:48 +03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 11:38:21 +03:00
*/
2019-06-07 12:48:03 +03:00
# include "GraphWidget.h"
# include "MemoryStatsWidget.h"
2019-08-12 12:56:30 +03:00
# include "NetworkStatisticsWidget.h"
2019-08-03 09:26:04 +03:00
# include "ProcessFileDescriptorMapWidget.h"
2019-07-28 13:15:24 +03:00
# include "ProcessMemoryMapWidget.h"
2019-10-02 21:26:19 +03:00
# include "ProcessModel.h"
2021-04-11 14:24:59 +03:00
# include "ProcessStateWidget.h"
2020-01-21 00:32:44 +03:00
# include "ProcessUnveiledPathsWidget.h"
2020-08-15 21:05:46 +03:00
# include "ThreadStackWidget.h"
# include <AK/NumberFormat.h>
2022-03-24 15:30:52 +03:00
# include <Applications/SystemMonitor/ProcessWindowGML.h>
2022-03-23 03:32:44 +03:00
# include <Applications/SystemMonitor/SystemMonitorGML.h>
2021-09-18 21:57:54 +03:00
# include <LibConfig/Client.h>
2021-01-08 03:03:49 +03:00
# include <LibCore/ArgsParser.h>
2022-04-05 01:32:37 +03:00
# include <LibCore/EventLoop.h>
# include <LibCore/Object.h>
2021-12-03 02:28:36 +03:00
# include <LibCore/System.h>
2020-02-06 17:04:03 +03:00
# include <LibCore/Timer.h>
2020-02-06 22:33:02 +03:00
# include <LibGUI/Action.h>
# include <LibGUI/ActionGroup.h>
# include <LibGUI/Application.h>
# include <LibGUI/BoxLayout.h>
2021-04-11 13:50:46 +03:00
# include <LibGUI/FileIconProvider.h>
2020-02-06 22:33:02 +03:00
# include <LibGUI/GroupBox.h>
2020-11-02 22:30:17 +03:00
# include <LibGUI/Icon.h>
2023-04-29 17:42:04 +03:00
# include <LibGUI/ImageWidget.h>
2020-02-06 22:33:02 +03:00
# include <LibGUI/JsonArrayModel.h>
# include <LibGUI/Label.h>
# include <LibGUI/LazyWidget.h>
2020-02-15 03:56:30 +03:00
# include <LibGUI/Menu.h>
2021-04-13 17:18:20 +03:00
# include <LibGUI/Menubar.h>
2021-11-12 19:28:04 +03:00
# include <LibGUI/MessageBox.h>
2020-02-06 22:33:02 +03:00
# include <LibGUI/Painter.h>
2022-05-27 01:28:58 +03:00
# include <LibGUI/Process.h>
2021-04-04 23:04:49 +03:00
# include <LibGUI/SeparatorWidget.h>
2020-02-06 22:33:02 +03:00
# include <LibGUI/SortingProxyModel.h>
2021-04-04 22:39:54 +03:00
# include <LibGUI/StackWidget.h>
2021-04-13 17:18:20 +03:00
# include <LibGUI/Statusbar.h>
2020-02-06 22:33:02 +03:00
# include <LibGUI/TabWidget.h>
2022-04-05 01:32:37 +03:00
# include <LibGUI/TreeView.h>
2020-02-06 22:33:02 +03:00
# include <LibGUI/Widget.h>
# include <LibGUI/Window.h>
2022-04-09 10:28:38 +03:00
# include <LibGfx/Font/FontDatabase.h>
2020-02-15 03:18:12 +03:00
# include <LibGfx/Palette.h>
2021-12-03 02:28:36 +03:00
# include <LibMain/Main.h>
2022-03-24 13:22:44 +03:00
# include <LibThreading/BackgroundAction.h>
2020-08-04 15:23:18 +03:00
# include <serenity.h>
2019-02-28 03:43:50 +03:00
# include <signal.h>
2020-07-01 22:08:16 +03:00
# include <spawn.h>
2019-06-07 12:48:03 +03:00
# include <stdio.h>
# include <unistd.h>
2019-02-28 03:43:50 +03:00
2022-01-07 16:50:07 +03:00
static ErrorOr < NonnullRefPtr < GUI : : Window > > build_process_window ( pid_t ) ;
2023-05-21 17:48:10 +03:00
static ErrorOr < void > build_performance_tab ( GUI : : Widget & ) ;
2019-08-11 11:11:21 +03:00
2021-04-13 17:18:20 +03:00
static RefPtr < GUI : : Statusbar > statusbar ;
2021-04-04 23:18:59 +03:00
2022-03-24 15:30:52 +03:00
namespace SystemMonitor {
class ProgressbarPaintingDelegate final : public GUI : : TableCellPaintingDelegate {
public :
virtual ~ ProgressbarPaintingDelegate ( ) override = default ;
virtual void paint ( GUI : : Painter & painter , Gfx : : IntRect const & a_rect , Palette const & palette , GUI : : ModelIndex const & index ) override
{
auto rect = a_rect . shrunken ( 2 , 2 ) ;
auto percentage = index . data ( GUI : : ModelRole : : Custom ) . to_i32 ( ) ;
auto data = index . data ( ) ;
2022-12-04 21:02:33 +03:00
DeprecatedString text ;
2022-03-24 15:30:52 +03:00
if ( data . is_string ( ) )
text = data . as_string ( ) ;
Gfx : : StylePainter : : paint_progressbar ( painter , rect , palette , 0 , 100 , percentage , text ) ;
painter . draw_rect ( rect , Color : : Black ) ;
}
} ;
2020-05-05 01:04:42 +03:00
class UnavailableProcessWidget final : public GUI : : Frame {
C_OBJECT ( UnavailableProcessWidget )
public :
2022-02-10 22:28:48 +03:00
virtual ~ UnavailableProcessWidget ( ) override = default ;
2020-05-05 01:04:42 +03:00
2023-05-21 17:51:56 +03:00
String const & text ( ) const { return m_text ; }
void set_text ( String text )
2022-03-24 15:30:52 +03:00
{
m_text = move ( text ) ;
update ( ) ;
}
2020-05-05 01:04:42 +03:00
private :
2022-03-24 15:30:52 +03:00
UnavailableProcessWidget ( )
2020-05-05 01:04:42 +03:00
{
2023-05-21 17:51:56 +03:00
REGISTER_STRING_PROPERTY ( " text " , text , set_text ) ;
2020-05-05 01:04:42 +03:00
}
virtual void paint_event ( GUI : : PaintEvent & event ) override
{
Frame : : paint_event ( event ) ;
if ( text ( ) . is_empty ( ) )
return ;
GUI : : Painter painter ( * this ) ;
painter . add_clip_rect ( event . rect ( ) ) ;
painter . draw_text ( frame_inner_rect ( ) , text ( ) , Gfx : : TextAlignment : : Center , palette ( ) . window_text ( ) , Gfx : : TextElision : : Right ) ;
}
2023-05-21 17:51:56 +03:00
String m_text ;
2020-05-05 01:04:42 +03:00
} ;
2022-03-24 13:04:08 +03:00
class StorageTabWidget final : public GUI : : LazyWidget {
C_OBJECT ( StorageTabWidget )
public :
StorageTabWidget ( )
{
this - > on_first_show = [ ] ( GUI : : LazyWidget & self ) {
auto & fs_table_view = * self . find_child_of_type_named < GUI : : TableView > ( " storage_table " ) ;
Vector < GUI : : JsonArrayModel : : FieldSpec > df_fields ;
2023-05-14 20:38:08 +03:00
df_fields . empend ( " mount_point " , " Mount point " _string . release_value_but_fixme_should_propagate_errors ( ) , Gfx : : TextAlignment : : CenterLeft ) ;
df_fields . empend ( " class_name " , " Class " _short_string , Gfx : : TextAlignment : : CenterLeft ) ;
df_fields . empend ( " source " , " Source " _short_string , Gfx : : TextAlignment : : CenterLeft ) ;
2022-03-24 13:04:08 +03:00
df_fields . empend (
2023-05-14 20:38:08 +03:00
" Size " _short_string , Gfx : : TextAlignment : : CenterRight ,
2022-12-21 23:13:19 +03:00
[ ] ( JsonObject const & object ) {
2022-03-24 13:04:08 +03:00
StringBuilder size_builder ;
2022-07-11 23:10:18 +03:00
size_builder . append ( ' ' ) ;
2022-12-21 23:13:19 +03:00
size_builder . append ( human_readable_size ( object . get_u64 ( " total_block_count " sv ) . value_or ( 0 ) * object . get_u64 ( " block_size " sv ) . value_or ( 0 ) ) ) ;
2022-07-11 23:10:18 +03:00
size_builder . append ( ' ' ) ;
2022-12-06 04:12:49 +03:00
return size_builder . to_deprecated_string ( ) ;
2022-03-24 13:04:08 +03:00
} ,
2022-12-21 23:13:19 +03:00
[ ] ( JsonObject const & object ) {
return object . get_u64 ( " total_block_count " sv ) . value_or ( 0 ) * object . get_u64 ( " block_size " sv ) . value_or ( 0 ) ;
2022-03-24 13:04:08 +03:00
} ,
2022-12-21 23:13:19 +03:00
[ ] ( JsonObject const & object ) {
auto total_blocks = object . get_u64 ( " total_block_count " sv ) . value_or ( 0 ) ;
2022-03-24 13:04:08 +03:00
if ( total_blocks = = 0 )
return 0 ;
2022-12-21 23:13:19 +03:00
auto free_blocks = object . get_u64 ( " free_block_count " sv ) . value_or ( 0 ) ;
2022-03-24 13:04:08 +03:00
auto used_blocks = total_blocks - free_blocks ;
int percentage = ( static_cast < double > ( used_blocks ) / static_cast < double > ( total_blocks ) * 100.0 ) ;
return percentage ;
} ) ;
df_fields . empend (
2023-05-14 20:38:08 +03:00
" Used " _short_string , Gfx : : TextAlignment : : CenterRight ,
2022-12-21 23:13:19 +03:00
[ ] ( JsonObject const & object ) {
auto total_blocks = object . get_u64 ( " total_block_count " sv ) . value_or ( 0 ) ;
auto free_blocks = object . get_u64 ( " free_block_count " sv ) . value_or ( 0 ) ;
2022-03-24 13:04:08 +03:00
auto used_blocks = total_blocks - free_blocks ;
2022-12-21 23:13:19 +03:00
return human_readable_size ( used_blocks * object . get_u64 ( " block_size " sv ) . value_or ( 0 ) ) ; } ,
[ ] ( JsonObject const & object ) {
auto total_blocks = object . get_u64 ( " total_block_count " sv ) . value_or ( 0 ) ;
auto free_blocks = object . get_u64 ( " free_block_count " sv ) . value_or ( 0 ) ;
2022-03-24 13:04:08 +03:00
auto used_blocks = total_blocks - free_blocks ;
2022-12-21 23:13:19 +03:00
return used_blocks * object . get_u64 ( " block_size " sv ) . value_or ( 0 ) ;
2022-03-24 13:04:08 +03:00
} ) ;
df_fields . empend (
2023-05-14 20:38:08 +03:00
" Available " _string . release_value_but_fixme_should_propagate_errors ( ) , Gfx : : TextAlignment : : CenterRight ,
2022-12-21 23:13:19 +03:00
[ ] ( JsonObject const & object ) {
return human_readable_size ( object . get_u64 ( " free_block_count " sv ) . value_or ( 0 ) * object . get_u64 ( " block_size " sv ) . value_or ( 0 ) ) ;
2022-03-24 13:04:08 +03:00
} ,
2022-12-21 23:13:19 +03:00
[ ] ( JsonObject const & object ) {
return object . get_u64 ( " free_block_count " sv ) . value_or ( 0 ) * object . get_u64 ( " block_size " sv ) . value_or ( 0 ) ;
2022-03-24 13:04:08 +03:00
} ) ;
2023-05-14 20:38:08 +03:00
df_fields . empend ( " Access " _short_string , Gfx : : TextAlignment : : CenterLeft , [ ] ( JsonObject const & object ) {
2022-12-21 23:13:19 +03:00
bool readonly = object . get_bool ( " readonly " sv ) . value_or ( false ) ;
int mount_flags = object . get_i32 ( " mount_flags " sv ) . value_or ( 0 ) ;
2022-03-24 13:04:08 +03:00
return readonly | | ( mount_flags & MS_RDONLY ) ? " Read-only " : " Read/Write " ;
} ) ;
2023-05-14 20:38:08 +03:00
df_fields . empend ( " Mount flags " _string . release_value_but_fixme_should_propagate_errors ( ) , Gfx : : TextAlignment : : CenterLeft , [ ] ( JsonObject const & object ) {
2022-12-21 23:13:19 +03:00
int mount_flags = object . get_i32 ( " mount_flags " sv ) . value_or ( 0 ) ;
2022-03-24 13:04:08 +03:00
StringBuilder builder ;
bool first = true ;
2022-07-11 20:32:29 +03:00
auto check = [ & ] ( int flag , StringView name ) {
2022-03-24 13:04:08 +03:00
if ( ! ( mount_flags & flag ) )
return ;
if ( ! first )
builder . append ( ' , ' ) ;
builder . append ( name ) ;
first = false ;
} ;
2022-07-11 20:32:29 +03:00
check ( MS_NODEV , " nodev " sv ) ;
check ( MS_NOEXEC , " noexec " sv ) ;
check ( MS_NOSUID , " nosuid " sv ) ;
check ( MS_BIND , " bind " sv ) ;
check ( MS_RDONLY , " ro " sv ) ;
check ( MS_WXALLOWED , " wxallowed " sv ) ;
check ( MS_AXALLOWED , " axallowed " sv ) ;
2022-10-21 19:30:06 +03:00
check ( MS_NOREGULAR , " noregular " sv ) ;
2022-03-24 13:04:08 +03:00
if ( builder . string_view ( ) . is_empty ( ) )
2022-12-04 21:02:33 +03:00
return DeprecatedString ( " defaults " ) ;
2022-12-06 04:12:49 +03:00
return builder . to_deprecated_string ( ) ;
2022-03-24 13:04:08 +03:00
} ) ;
2023-05-14 20:38:08 +03:00
df_fields . empend ( " free_block_count " , " Free blocks " _string . release_value_but_fixme_should_propagate_errors ( ) , Gfx : : TextAlignment : : CenterRight ) ;
df_fields . empend ( " total_block_count " , " Total blocks " _string . release_value_but_fixme_should_propagate_errors ( ) , Gfx : : TextAlignment : : CenterRight ) ;
df_fields . empend ( " free_inode_count " , " Free inodes " _string . release_value_but_fixme_should_propagate_errors ( ) , Gfx : : TextAlignment : : CenterRight ) ;
df_fields . empend ( " total_inode_count " , " Total inodes " _string . release_value_but_fixme_should_propagate_errors ( ) , Gfx : : TextAlignment : : CenterRight ) ;
df_fields . empend ( " block_size " , " Block size " _string . release_value_but_fixme_should_propagate_errors ( ) , Gfx : : TextAlignment : : CenterRight ) ;
2022-03-24 13:04:08 +03:00
2022-10-14 21:55:17 +03:00
fs_table_view . set_model ( MUST ( GUI : : SortingProxyModel : : create ( GUI : : JsonArrayModel : : create ( " /sys/kernel/df " , move ( df_fields ) ) ) ) ) ;
2022-03-24 13:04:08 +03:00
fs_table_view . set_column_painting_delegate ( 3 , make < ProgressbarPaintingDelegate > ( ) ) ;
fs_table_view . model ( ) - > invalidate ( ) ;
} ;
}
} ;
2022-03-24 12:51:14 +03:00
}
2022-03-24 13:04:08 +03:00
REGISTER_WIDGET ( SystemMonitor , StorageTabWidget )
2022-03-24 15:30:52 +03:00
REGISTER_WIDGET ( SystemMonitor , UnavailableProcessWidget )
2022-03-24 12:51:14 +03:00
2020-08-04 22:02:19 +03:00
static bool can_access_pid ( pid_t pid )
2020-05-05 01:04:42 +03:00
{
2021-07-27 23:52:32 +03:00
int rc = kill ( pid , 0 ) ;
return rc = = 0 ;
2020-05-05 01:04:42 +03:00
}
2021-12-03 02:28:36 +03:00
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
2019-02-28 03:43:50 +03:00
{
2021-05-13 21:26:20 +03:00
{
// Before we do anything else, boost our process priority to the maximum allowed.
// It's very frustrating when the system is bogged down under load and you just want
// System Monitor to work.
sched_param param {
. sched_priority = THREAD_PRIORITY_MAX ,
} ;
sched_setparam ( 0 , & param ) ;
}
2021-12-03 02:28:36 +03:00
TRY ( Core : : System : : pledge ( " stdio thread proc recvfd sendfd rpath exec unix " ) ) ;
2020-01-11 23:17:39 +03:00
2023-05-05 07:24:53 +03:00
auto app = TRY ( GUI : : Application : : create ( arguments ) ) ;
2019-02-28 03:43:50 +03:00
2022-02-11 19:33:09 +03:00
Config : : pledge_domain ( " SystemMonitor " ) ;
2021-11-11 14:07:13 +03:00
2021-12-03 02:28:36 +03:00
TRY ( Core : : System : : unveil ( " /etc/passwd " , " r " ) ) ;
TRY ( Core : : System : : unveil ( " /res " , " r " ) ) ;
TRY ( Core : : System : : unveil ( " /proc " , " r " ) ) ;
2022-10-14 21:55:17 +03:00
TRY ( Core : : System : : unveil ( " /sys/kernel " , " r " ) ) ;
2021-12-03 02:28:36 +03:00
TRY ( Core : : System : : unveil ( " /dev " , " r " ) ) ;
TRY ( Core : : System : : unveil ( " /bin " , " r " ) ) ;
2022-10-29 22:52:20 +03:00
TRY ( Core : : System : : unveil ( " /bin/Escalator " , " x " ) ) ;
2023-04-27 22:09:31 +03:00
TRY ( Core : : System : : unveil ( " /bin/NetworkSettings " , " x " ) ) ;
2021-12-03 02:28:36 +03:00
TRY ( Core : : System : : unveil ( " /usr/lib " , " r " ) ) ;
2021-05-22 19:23:51 +03:00
2021-08-13 22:00:34 +03:00
// This directory only exists if ports are installed
2021-12-03 02:28:36 +03:00
if ( auto result = Core : : System : : unveil ( " /usr/local/bin " , " r " ) ; result . is_error ( ) & & result . error ( ) . code ( ) ! = ENOENT )
return result . release_error ( ) ;
2021-08-13 11:07:49 +03:00
2021-12-03 02:28:36 +03:00
if ( auto result = Core : : System : : unveil ( " /usr/local/lib " , " r " ) ; result . is_error ( ) & & result . error ( ) . code ( ) ! = ENOENT )
return result . release_error ( ) ;
2020-08-02 18:13:52 +03:00
2022-08-19 12:38:19 +03:00
// This file is only accessible when running as root if it is available on the disk image.
// It might be possible to not have this file on the disk image, if the user decided to not
// include kernel symbols for debug purposes so don't fail if the error is ENOENT.
if ( auto result = Core : : System : : unveil ( " /boot/Kernel.debug " , " r " ) ; result . is_error ( ) & & ( result . error ( ) . code ( ) ! = EACCES & & result . error ( ) . code ( ) ! = ENOENT ) )
2022-02-21 23:17:03 +03:00
return result . release_error ( ) ;
2021-12-03 02:28:36 +03:00
TRY ( Core : : System : : unveil ( " /bin/Profiler " , " rx " ) ) ;
2023-05-21 22:40:39 +03:00
// HackStudio doesn't exist in the minimal build configuration.
if ( auto result = Core : : System : : unveil ( " /bin/HackStudio " , " rx " ) ; result . is_error ( ) & & result . error ( ) . code ( ) ! = ENOENT )
return result . release_error ( ) ;
2021-12-19 02:56:17 +03:00
TRY ( Core : : System : : unveil ( nullptr , nullptr ) ) ;
2020-01-21 20:45:18 +03:00
2021-12-03 02:28:36 +03:00
StringView args_tab = " processes " sv ;
2021-01-08 03:03:49 +03:00
Core : : ArgsParser parser ;
2021-08-18 13:50:24 +03:00
parser . add_option ( args_tab , " Tab, one of 'processes', 'graphs', 'fs', 'hardware', or 'network' " , " open-tab " , ' t ' , " tab " ) ;
2021-12-03 02:28:36 +03:00
parser . parse ( arguments ) ;
2021-01-08 03:03:49 +03:00
StringView args_tab_view = args_tab ;
2023-05-21 17:48:10 +03:00
auto app_icon = TRY ( GUI : : Icon : : try_create_default_icon ( " app-system-monitor " sv ) ) ;
2020-11-02 22:30:17 +03:00
2023-05-21 17:48:10 +03:00
auto window = TRY ( GUI : : Window : : try_create ( ) ) ;
2020-01-23 17:14:21 +03:00
window - > set_title ( " System Monitor " ) ;
2021-04-05 14:36:43 +03:00
window - > resize ( 560 , 430 ) ;
2020-01-23 17:14:21 +03:00
2023-01-06 19:48:37 +03:00
auto main_widget = TRY ( window - > set_main_widget < GUI : : Widget > ( ) ) ;
2023-01-07 15:38:23 +03:00
TRY ( main_widget - > load_from_gml ( system_monitor_gml ) ) ;
2022-03-23 03:32:44 +03:00
auto & tabwidget = * main_widget - > find_descendant_of_type_named < GUI : : TabWidget > ( " main_tabs " ) ;
statusbar = main_widget - > find_descendant_of_type_named < GUI : : Statusbar > ( " statusbar " ) ;
2021-04-04 23:04:49 +03:00
2022-03-23 03:32:44 +03:00
auto & process_table_container = * tabwidget . find_descendant_of_type_named < GUI : : Widget > ( " processes " ) ;
2019-07-27 10:39:43 +03:00
2021-04-04 21:46:46 +03:00
auto process_model = ProcessModel : : create ( ) ;
process_model - > on_state_update = [ & ] ( int process_count , int thread_count ) {
2023-06-04 11:24:38 +03:00
statusbar - > set_text ( 0 , String : : formatted ( " Processes: {} " , process_count ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
statusbar - > set_text ( 1 , String : : formatted ( " Threads: {} " , thread_count ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2021-04-04 21:46:46 +03:00
} ;
2022-03-23 03:32:44 +03:00
auto & performance_widget = * tabwidget . find_descendant_of_type_named < GUI : : Widget > ( " performance " ) ;
2023-05-21 17:48:10 +03:00
TRY ( build_performance_tab ( performance_widget ) ) ;
2019-05-07 18:11:48 +03:00
2022-04-05 01:32:37 +03:00
auto & process_table_view = * process_table_container . find_child_of_type_named < GUI : : TreeView > ( " process_table " ) ;
2021-12-24 15:12:04 +03:00
process_table_view . set_model ( TRY ( GUI : : SortingProxyModel : : create ( process_model ) ) ) ;
2022-08-30 15:25:11 +03:00
for ( auto column = 0 ; column < ProcessModel : : Column : : __Count ; + + column ) {
process_table_view . set_column_visible ( column ,
2023-06-13 18:30:15 +03:00
Config : : read_bool ( " SystemMonitor " sv , " ProcessTableColumns " sv , TRY ( process_model - > column_name ( column ) ) ,
2022-08-30 15:25:11 +03:00
process_model - > is_default_column ( column ) ) ) ;
}
2021-04-05 14:36:43 +03:00
2020-08-16 11:44:10 +03:00
process_table_view . set_key_column_and_sort_order ( ProcessModel : : Column : : CPU , GUI : : SortOrder : : Descending ) ;
2021-05-25 17:13:19 +03:00
process_model - > update ( ) ;
2019-03-10 14:13:22 +03:00
2022-07-11 20:32:29 +03:00
i32 frequency = Config : : read_i32 ( " SystemMonitor " sv , " Monitor " sv , " Frequency " sv , 3 ) ;
2021-09-18 21:57:54 +03:00
if ( frequency ! = 1 & & frequency ! = 3 & & frequency ! = 5 ) {
frequency = 3 ;
2022-07-11 20:32:29 +03:00
Config : : write_i32 ( " SystemMonitor " sv , " Monitor " sv , " Frequency " sv , frequency ) ;
2021-09-18 21:57:54 +03:00
}
2023-01-04 06:55:43 +03:00
auto update_stats = [ & ] {
// FIXME: remove the primitive re-toggling code once persistent model indices work.
auto toggled_indices = process_table_view . selection ( ) . indices ( ) ;
toggled_indices . remove_all_matching ( [ & ] ( auto const & index ) { return ! process_table_view . is_toggled ( index ) ; } ) ;
process_model - > update ( ) ;
if ( ! process_table_view . selection ( ) . is_empty ( ) )
process_table_view . selection ( ) . for_each_index ( [ & ] ( auto & selection ) {
if ( toggled_indices . contains_slow ( selection ) )
process_table_view . expand_all_parents_of ( selection ) ;
} ) ;
2022-04-05 01:35:15 +03:00
2023-01-04 06:55:43 +03:00
if ( auto * memory_stats_widget = SystemMonitor : : MemoryStatsWidget : : the ( ) )
memory_stats_widget - > refresh ( ) ;
} ;
update_stats ( ) ;
2023-05-21 17:48:10 +03:00
auto refresh_timer = TRY ( window - > try_add < Core : : Timer > ( frequency * 1000 , move ( update_stats ) ) ) ;
refresh_timer - > start ( ) ;
2019-02-28 03:43:50 +03:00
2020-08-09 21:19:28 +03:00
auto selected_id = [ & ] ( ProcessModel : : Column column ) - > pid_t {
2020-08-04 22:02:19 +03:00
if ( process_table_view . selection ( ) . is_empty ( ) )
return - 1 ;
2022-04-05 01:25:14 +03:00
auto pid_index = process_table_view . model ( ) - > index ( process_table_view . selection ( ) . first ( ) . row ( ) , column , process_table_view . selection ( ) . first ( ) . parent ( ) ) ;
2020-08-16 17:14:39 +03:00
return pid_index . data ( ) . to_i32 ( ) ;
2020-08-04 22:02:19 +03:00
} ;
2022-12-04 21:02:33 +03:00
auto selected_name = [ & ] ( ProcessModel : : Column column ) - > DeprecatedString {
2021-11-12 19:28:04 +03:00
if ( process_table_view . selection ( ) . is_empty ( ) )
return { } ;
2022-04-05 01:25:14 +03:00
auto pid_index = process_table_view . model ( ) - > index ( process_table_view . selection ( ) . first ( ) . row ( ) , column , process_table_view . selection ( ) . first ( ) . parent ( ) ) ;
2022-12-06 04:12:49 +03:00
return pid_index . data ( ) . to_deprecated_string ( ) ;
2021-11-12 19:28:04 +03:00
} ;
2021-04-04 23:45:45 +03:00
auto kill_action = GUI : : Action : : create (
2023-01-20 22:06:05 +03:00
" &Kill Process " , { Mod_Ctrl , Key_K } , { Key_Delete } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/kill.png " sv ) ) , [ & ] ( const GUI : : Action & ) {
2021-04-04 23:45:45 +03:00
pid_t pid = selected_id ( ProcessModel : : Column : : PID ) ;
2021-11-12 19:28:04 +03:00
if ( pid = = - 1 )
return ;
2022-12-04 21:02:33 +03:00
auto rc = GUI : : MessageBox : : show ( window , DeprecatedString : : formatted ( " Do you really want to kill \" {} \" (PID {})? " , selected_name ( ProcessModel : : Column : : Name ) , pid ) , " System Monitor " sv , GUI : : MessageBox : : Type : : Question , GUI : : MessageBox : : InputType : : YesNo ) ;
2022-05-13 15:10:27 +03:00
if ( rc = = GUI : : Dialog : : ExecResult : : Yes )
2021-04-04 23:45:45 +03:00
kill ( pid , SIGKILL ) ;
} ,
& process_table_view ) ;
auto stop_action = GUI : : Action : : create (
2023-01-20 22:06:05 +03:00
" &Stop Process " , { Mod_Ctrl , Key_S } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/stop-hand.png " sv ) ) , [ & ] ( const GUI : : Action & ) {
2021-04-04 23:45:45 +03:00
pid_t pid = selected_id ( ProcessModel : : Column : : PID ) ;
2021-11-12 19:28:04 +03:00
if ( pid = = - 1 )
return ;
2022-12-04 21:02:33 +03:00
auto rc = GUI : : MessageBox : : show ( window , DeprecatedString : : formatted ( " Do you really want to stop \" {} \" (PID {})? " , selected_name ( ProcessModel : : Column : : Name ) , pid ) , " System Monitor " sv , GUI : : MessageBox : : Type : : Question , GUI : : MessageBox : : InputType : : YesNo ) ;
2022-05-13 15:10:27 +03:00
if ( rc = = GUI : : Dialog : : ExecResult : : Yes )
2021-04-04 23:45:45 +03:00
kill ( pid , SIGSTOP ) ;
} ,
& process_table_view ) ;
auto continue_action = GUI : : Action : : create (
2023-01-20 22:06:05 +03:00
" &Continue Process " , { Mod_Ctrl , Key_C } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/continue.png " sv ) ) , [ & ] ( const GUI : : Action & ) {
2021-04-04 23:45:45 +03:00
pid_t pid = selected_id ( ProcessModel : : Column : : PID ) ;
if ( pid ! = - 1 )
kill ( pid , SIGCONT ) ;
} ,
& process_table_view ) ;
auto profile_action = GUI : : Action : : create (
2021-05-21 19:51:06 +03:00
" &Profile Process " , { Mod_Ctrl , Key_P } ,
2023-01-20 22:06:05 +03:00
TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-profiler.png " sv ) ) , [ & ] ( auto & ) {
2020-08-09 21:19:28 +03:00
pid_t pid = selected_id ( ProcessModel : : Column : : PID ) ;
2022-05-27 01:28:58 +03:00
if ( pid = = - 1 )
return ;
2022-12-04 21:02:33 +03:00
auto pid_string = DeprecatedString : : number ( pid ) ;
2022-07-11 20:32:29 +03:00
GUI : : Process : : spawn_or_show_error ( window , " /bin/Profiler " sv , Array { " --pid " , pid_string . characters ( ) } ) ;
2021-04-04 23:45:45 +03:00
} ,
& process_table_view ) ;
2020-07-01 22:08:16 +03:00
2023-02-19 23:12:07 +03:00
auto debug_action = GUI : : Action : : create (
" Debug in HackStudio " , { Mod_Ctrl , Key_D } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-hack-studio.png " sv ) ) , [ & ] ( const GUI : : Action & ) {
pid_t pid = selected_id ( ProcessModel : : Column : : PID ) ;
if ( pid = = - 1 )
return ;
auto pid_string = DeprecatedString : : number ( pid ) ;
GUI : : Process : : spawn_or_show_error ( window , " /bin/HackStudio " sv , Array { " --pid " , pid_string . characters ( ) } ) ;
} ,
& process_table_view ) ;
2021-04-04 22:52:55 +03:00
HashMap < pid_t , NonnullRefPtr < GUI : : Window > > process_windows ;
2021-04-04 23:39:41 +03:00
auto process_properties_action = GUI : : CommonActions : : make_properties_action (
[ & ] ( auto & ) {
auto pid = selected_id ( ProcessModel : : Column : : PID ) ;
2021-05-18 09:10:27 +03:00
if ( pid = = - 1 )
return ;
2021-04-04 23:39:41 +03:00
RefPtr < GUI : : Window > process_window ;
2021-04-04 23:42:05 +03:00
auto it = process_windows . find ( pid ) ;
if ( it = = process_windows . end ( ) ) {
2022-01-07 16:50:07 +03:00
auto process_window_or_error = build_process_window ( pid ) ;
if ( process_window_or_error . is_error ( ) )
return ;
process_window = process_window_or_error . release_value ( ) ;
2021-04-04 23:39:41 +03:00
process_window - > on_close_request = [ pid , & process_windows ] {
process_windows . remove ( pid ) ;
return GUI : : Window : : CloseRequestDecision : : Close ;
} ;
process_windows . set ( pid , * process_window ) ;
2021-04-04 23:42:05 +03:00
} else {
process_window = it - > value ;
2021-04-04 23:39:41 +03:00
}
process_window - > show ( ) ;
process_window - > move_to_front ( ) ;
2021-04-04 23:45:45 +03:00
} ,
& process_table_view ) ;
2021-04-04 22:52:55 +03:00
2023-05-21 17:48:10 +03:00
auto file_menu = TRY ( window - > try_add_menu ( " &File " _short_string ) ) ;
TRY ( file_menu - > try_add_action ( GUI : : CommonActions : : make_quit_action ( [ ] ( auto & ) {
2020-07-04 17:52:01 +03:00
GUI : : Application : : the ( ) - > quit ( ) ;
2023-05-21 17:48:10 +03:00
} ) ) ) ;
auto process_context_menu = TRY ( GUI : : Menu : : try_create ( ) ) ;
TRY ( process_context_menu - > try_add_action ( kill_action ) ) ;
TRY ( process_context_menu - > try_add_action ( stop_action ) ) ;
TRY ( process_context_menu - > try_add_action ( continue_action ) ) ;
TRY ( process_context_menu - > try_add_separator ( ) ) ;
TRY ( process_context_menu - > try_add_action ( profile_action ) ) ;
TRY ( process_context_menu - > try_add_action ( debug_action ) ) ;
TRY ( process_context_menu - > try_add_separator ( ) ) ;
TRY ( process_context_menu - > try_add_action ( process_properties_action ) ) ;
2020-12-21 02:09:48 +03:00
process_table_view . on_context_menu_request = [ & ] ( [[maybe_unused]] const GUI : : ModelIndex & index , const GUI : : ContextMenuEvent & event ) {
2021-05-18 09:10:27 +03:00
if ( index . is_valid ( ) )
process_context_menu - > popup ( event . screen_position ( ) , process_properties_action ) ;
2019-06-30 09:15:18 +03:00
} ;
2023-05-21 17:48:10 +03:00
auto frequency_menu = TRY ( window - > try_add_menu ( TRY ( " F&requency " _string ) ) ) ;
2020-02-02 17:07:41 +03:00
GUI : : ActionGroup frequency_action_group ;
2020-01-08 22:45:43 +03:00
frequency_action_group . set_exclusive ( true ) ;
2023-05-21 17:48:10 +03:00
auto make_frequency_action = [ & ] ( int seconds ) - > ErrorOr < void > {
2022-12-04 21:02:33 +03:00
auto action = GUI : : Action : : create_checkable ( DeprecatedString : : formatted ( " &{} Sec " , seconds ) , [ & refresh_timer , seconds ] ( auto & ) {
2022-07-11 20:32:29 +03:00
Config : : write_i32 ( " SystemMonitor " sv , " Monitor " sv , " Frequency " sv , seconds ) ;
2023-05-21 17:48:10 +03:00
refresh_timer - > restart ( seconds * 1000 ) ;
2020-01-08 22:45:43 +03:00
} ) ;
2023-06-04 12:22:04 +03:00
action - > set_status_tip ( TRY ( String : : formatted ( " Refresh every {} seconds " , seconds ) ) ) ;
2021-09-18 21:57:54 +03:00
action - > set_checked ( frequency = = seconds ) ;
2020-01-08 22:45:43 +03:00
frequency_action_group . add_action ( * action ) ;
2023-05-21 17:48:10 +03:00
TRY ( frequency_menu - > try_add_action ( * action ) ) ;
return { } ;
2020-01-08 22:45:43 +03:00
} ;
2023-05-21 17:48:10 +03:00
TRY ( make_frequency_action ( 1 ) ) ;
TRY ( make_frequency_action ( 3 ) ) ;
TRY ( make_frequency_action ( 5 ) ) ;
2020-01-08 22:45:43 +03:00
2023-05-21 17:48:10 +03:00
auto help_menu = TRY ( window - > try_add_menu ( " &Help " _short_string ) ) ;
TRY ( help_menu - > try_add_action ( GUI : : CommonActions : : make_command_palette_action ( window ) ) ) ;
TRY ( help_menu - > try_add_action ( GUI : : CommonActions : : make_about_action ( " System Monitor " , app_icon , window ) ) ) ;
2019-02-28 03:43:50 +03:00
2021-04-04 22:39:54 +03:00
process_table_view . on_activation = [ & ] ( auto & ) {
2021-05-28 18:01:36 +03:00
if ( process_properties_action - > is_enabled ( ) )
process_properties_action - > activate ( ) ;
} ;
2021-07-28 01:23:34 +03:00
static pid_t last_selected_pid ;
2021-05-28 18:01:36 +03:00
process_table_view . on_selection_change = [ & ] {
pid_t pid = selected_id ( ProcessModel : : Column : : PID ) ;
2021-07-28 01:23:34 +03:00
if ( pid = = last_selected_pid | | pid < 1 )
return ;
last_selected_pid = pid ;
2021-05-28 18:01:36 +03:00
bool has_access = can_access_pid ( pid ) ;
kill_action - > set_enabled ( has_access ) ;
stop_action - > set_enabled ( has_access ) ;
continue_action - > set_enabled ( has_access ) ;
profile_action - > set_enabled ( has_access ) ;
2023-02-19 23:12:07 +03:00
debug_action - > set_enabled ( has_access ) ;
2021-05-28 18:01:36 +03:00
process_properties_action - > set_enabled ( has_access ) ;
2019-07-27 10:39:43 +03:00
} ;
2021-04-23 18:25:33 +03:00
app - > on_action_enter = [ ] ( GUI : : Action const & action ) {
statusbar - > set_override_text ( action . status_tip ( ) ) ;
} ;
app - > on_action_leave = [ ] ( GUI : : Action const & ) {
statusbar - > set_override_text ( { } ) ;
} ;
2019-02-28 03:43:50 +03:00
window - > show ( ) ;
2020-11-02 22:30:17 +03:00
window - > set_icon ( app_icon . bitmap_for_size ( 16 ) ) ;
2019-04-16 18:55:27 +03:00
2021-01-08 03:03:49 +03:00
if ( args_tab_view = = " processes " )
2021-04-04 23:26:35 +03:00
tabwidget . set_active_widget ( & process_table_container ) ;
2021-01-08 03:03:49 +03:00
else if ( args_tab_view = = " graphs " )
2022-03-23 03:32:44 +03:00
tabwidget . set_active_widget ( & performance_widget ) ;
2021-01-08 03:03:49 +03:00
else if ( args_tab_view = = " fs " )
2022-03-24 13:04:08 +03:00
tabwidget . set_active_widget ( tabwidget . find_descendant_of_type_named < SystemMonitor : : StorageTabWidget > ( " storage " ) ) ;
2021-01-08 03:03:49 +03:00
else if ( args_tab_view = = " network " )
2022-03-23 03:32:44 +03:00
tabwidget . set_active_widget ( tabwidget . find_descendant_of_type_named < GUI : : Widget > ( " network " ) ) ;
2021-01-08 03:03:49 +03:00
2022-08-30 15:25:11 +03:00
int exec = app - > exec ( ) ;
// When exiting the application, save the configuration of the columns
// to be loaded the next time the application is opened.
auto & process_table_header = process_table_view . column_header ( ) ;
for ( auto column = 0 ; column < ProcessModel : : Column : : __Count ; + + column )
2023-06-13 18:30:15 +03:00
Config : : write_bool ( " SystemMonitor " sv , " ProcessTableColumns " sv , TRY ( process_model - > column_name ( column ) ) , process_table_header . is_section_visible ( column ) ) ;
2022-08-30 15:25:11 +03:00
return exec ;
2019-02-28 03:43:50 +03:00
}
2019-08-11 11:11:21 +03:00
2022-01-07 16:50:07 +03:00
ErrorOr < NonnullRefPtr < GUI : : Window > > build_process_window ( pid_t pid )
2021-04-04 22:39:54 +03:00
{
2023-05-21 17:48:10 +03:00
auto window = TRY ( GUI : : Window : : try_create ( ) ) ;
2021-04-04 22:39:54 +03:00
window - > resize ( 480 , 360 ) ;
2022-12-04 21:02:33 +03:00
window - > set_title ( DeprecatedString : : formatted ( " PID {} - System Monitor " , pid ) ) ;
2021-04-04 22:39:54 +03:00
2023-05-21 17:48:10 +03:00
auto app_icon = TRY ( GUI : : Icon : : try_create_default_icon ( " app-system-monitor " sv ) ) ;
2022-02-18 13:51:50 +03:00
window - > set_icon ( app_icon . bitmap_for_size ( 16 ) ) ;
2023-01-06 19:48:37 +03:00
auto main_widget = TRY ( window - > set_main_widget < GUI : : Widget > ( ) ) ;
2023-01-07 15:38:23 +03:00
TRY ( main_widget - > load_from_gml ( process_window_gml ) ) ;
2021-04-11 13:50:46 +03:00
GUI : : ModelIndex process_index ;
for ( int row = 0 ; row < ProcessModel : : the ( ) . row_count ( { } ) ; + + row ) {
auto index = ProcessModel : : the ( ) . index ( row , ProcessModel : : Column : : PID ) ;
if ( index . data ( ) . to_i32 ( ) = = pid ) {
process_index = index ;
break ;
}
}
VERIFY ( process_index . is_valid ( ) ) ;
if ( auto icon_data = process_index . sibling_at_column ( ProcessModel : : Column : : Icon ) . data ( ) ; icon_data . is_icon ( ) ) {
2023-04-29 17:42:04 +03:00
main_widget - > find_descendant_of_type_named < GUI : : ImageWidget > ( " process_icon " ) - > set_bitmap ( icon_data . as_icon ( ) . bitmap_for_size ( 32 ) ) ;
2021-04-11 13:50:46 +03:00
}
2023-05-21 17:48:10 +03:00
main_widget - > find_descendant_of_type_named < GUI : : Label > ( " process_name " ) - > set_text ( TRY ( String : : formatted ( " {} (PID {}) " , process_index . sibling_at_column ( ProcessModel : : Column : : Name ) . data ( ) . to_deprecated_string ( ) , pid ) ) ) ;
2021-04-04 22:39:54 +03:00
2022-03-24 15:30:52 +03:00
main_widget - > find_descendant_of_type_named < SystemMonitor : : ProcessStateWidget > ( " process_state " ) - > set_pid ( pid ) ;
main_widget - > find_descendant_of_type_named < SystemMonitor : : ProcessFileDescriptorMapWidget > ( " open_files " ) - > set_pid ( pid ) ;
main_widget - > find_descendant_of_type_named < SystemMonitor : : ThreadStackWidget > ( " thread_stack " ) - > set_ids ( pid , pid ) ;
main_widget - > find_descendant_of_type_named < SystemMonitor : : ProcessMemoryMapWidget > ( " memory_map " ) - > set_pid ( pid ) ;
main_widget - > find_descendant_of_type_named < SystemMonitor : : ProcessUnveiledPathsWidget > ( " unveiled_paths " ) - > set_pid ( pid ) ;
2021-04-04 22:39:54 +03:00
2022-03-24 15:30:52 +03:00
auto & widget_stack = * main_widget - > find_descendant_of_type_named < GUI : : StackWidget > ( " widget_stack " ) ;
auto & unavailable_process_widget = * widget_stack . find_descendant_of_type_named < SystemMonitor : : UnavailableProcessWidget > ( " unavailable_process " ) ;
2023-05-21 17:51:56 +03:00
unavailable_process_widget . set_text ( TRY ( String : : formatted ( " Unable to access PID {} " , pid ) ) ) ;
2021-04-04 22:39:54 +03:00
if ( can_access_pid ( pid ) )
2022-03-24 15:30:52 +03:00
widget_stack . set_active_widget ( widget_stack . find_descendant_of_type_named < GUI : : TabWidget > ( " available_process " ) ) ;
2021-04-04 22:39:54 +03:00
else
widget_stack . set_active_widget ( & unavailable_process_widget ) ;
return window ;
}
2023-05-21 17:48:10 +03:00
ErrorOr < void > build_performance_tab ( GUI : : Widget & graphs_container )
2019-10-02 21:26:19 +03:00
{
2022-03-23 03:32:44 +03:00
auto & cpu_graph_group_box = * graphs_container . find_descendant_of_type_named < GUI : : GroupBox > ( " cpu_graph " ) ;
2022-01-14 01:55:58 +03:00
2022-01-16 16:15:02 +03:00
size_t cpu_graphs_per_row = min ( 4 , ProcessModel : : the ( ) . cpus ( ) . size ( ) ) ;
2022-01-14 01:55:58 +03:00
auto cpu_graph_rows = ceil_div ( ProcessModel : : the ( ) . cpus ( ) . size ( ) , cpu_graphs_per_row ) ;
cpu_graph_group_box . set_fixed_height ( 120u * cpu_graph_rows ) ;
2022-03-19 01:56:04 +03:00
Vector < SystemMonitor : : GraphWidget & > cpu_graphs ;
2022-01-14 01:55:58 +03:00
for ( auto row = 0u ; row < cpu_graph_rows ; + + row ) {
2023-05-21 17:48:10 +03:00
auto cpu_graph_row = TRY ( cpu_graph_group_box . try_add < GUI : : Widget > ( ) ) ;
TRY ( cpu_graph_row - > try_set_layout < GUI : : HorizontalBoxLayout > ( 6 ) ) ;
cpu_graph_row - > set_fixed_height ( 108 ) ;
2022-01-14 01:55:58 +03:00
for ( auto i = 0u ; i < cpu_graphs_per_row ; + + i ) {
2023-05-21 17:48:10 +03:00
auto cpu_graph = TRY ( cpu_graph_row - > try_add < SystemMonitor : : GraphWidget > ( ) ) ;
cpu_graph - > set_max ( 100 ) ;
cpu_graph - > set_value_format ( 0 , {
. graph_color_role = ColorRole : : SyntaxPreprocessorStatement ,
. text_formatter = [ ] ( u64 value ) {
return DeprecatedString : : formatted ( " Total: {}% " , value ) ;
} ,
} ) ;
cpu_graph - > set_value_format ( 1 , {
. graph_color_role = ColorRole : : SyntaxPreprocessorValue ,
. text_formatter = [ ] ( u64 value ) {
return DeprecatedString : : formatted ( " Kernel: {}% " , value ) ;
} ,
} ) ;
2022-01-14 01:55:58 +03:00
cpu_graphs . append ( cpu_graph ) ;
}
2021-04-04 21:49:37 +03:00
}
2023-03-06 19:16:25 +03:00
ProcessModel : : the ( ) . on_cpu_info_change = [ cpu_graphs ] ( Vector < NonnullOwnPtr < ProcessModel : : CpuInfo > > const & cpus ) mutable {
2021-04-04 23:18:59 +03:00
float sum_cpu = 0 ;
for ( size_t i = 0 ; i < cpus . size ( ) ; + + i ) {
2023-03-06 19:16:25 +03:00
cpu_graphs [ i ] . add_value ( { static_cast < size_t > ( cpus [ i ] - > total_cpu_percent ) , static_cast < size_t > ( cpus [ i ] - > total_cpu_percent_kernel ) } ) ;
sum_cpu + = cpus [ i ] - > total_cpu_percent ;
2021-04-04 23:18:59 +03:00
}
float cpu_usage = sum_cpu / ( float ) cpus . size ( ) ;
2023-06-04 11:24:38 +03:00
statusbar - > set_text ( 2 , String : : formatted ( " CPU usage: {}% " , ( int ) roundf ( cpu_usage ) ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2019-10-02 21:26:19 +03:00
} ;
2021-04-04 21:49:37 +03:00
2022-03-23 03:32:44 +03:00
auto & memory_graph = * graphs_container . find_descendant_of_type_named < SystemMonitor : : GraphWidget > ( " memory_graph " ) ;
2021-04-04 21:49:37 +03:00
memory_graph . set_value_format ( 0 , {
. graph_color_role = ColorRole : : SyntaxComment ,
2022-05-05 22:17:43 +03:00
. text_formatter = [ ] ( u64 bytes ) {
2022-12-04 21:02:33 +03:00
return DeprecatedString : : formatted ( " Committed: {} " , human_readable_size ( bytes ) ) ;
2021-04-04 21:49:37 +03:00
} ,
} ) ;
memory_graph . set_value_format ( 1 , {
. graph_color_role = ColorRole : : SyntaxPreprocessorStatement ,
2022-05-05 22:17:43 +03:00
. text_formatter = [ ] ( u64 bytes ) {
2022-12-04 21:02:33 +03:00
return DeprecatedString : : formatted ( " Allocated: {} " , human_readable_size ( bytes ) ) ;
2021-04-04 21:49:37 +03:00
} ,
} ) ;
memory_graph . set_value_format ( 2 , {
. graph_color_role = ColorRole : : SyntaxPreprocessorValue ,
2022-05-05 22:17:43 +03:00
. text_formatter = [ ] ( u64 bytes ) {
2022-12-04 21:02:33 +03:00
return DeprecatedString : : formatted ( " Kernel heap: {} " , human_readable_size ( bytes ) ) ;
2021-04-04 21:49:37 +03:00
} ,
} ) ;
2023-05-21 17:48:10 +03:00
return { } ;
2019-10-02 21:26:19 +03:00
}