2020-01-18 05:38:21 -03:00
/*
2021-04-04 15:46:46 -03:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2021-09-18 15:57:54 -03:00
* Copyright ( c ) 2021 , Undefine < cqundefine @ gmail . com >
2022-02-10 16:28:48 -03:00
* Copyright ( c ) 2022 , the SerenityOS developers .
2020-01-18 05:38:21 -03:00
*
2021-04-22 05:24:48 -03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 05:38:21 -03:00
*/
2019-06-07 06:48:03 -03:00
# include "GraphWidget.h"
# include "MemoryStatsWidget.h"
2019-08-12 06:56:30 -03:00
# include "NetworkStatisticsWidget.h"
2019-08-03 03:26:04 -03:00
# include "ProcessFileDescriptorMapWidget.h"
2019-07-28 07:15:24 -03:00
# include "ProcessMemoryMapWidget.h"
2019-10-02 15:26:19 -03:00
# include "ProcessModel.h"
2021-04-11 08:24:59 -03:00
# include "ProcessStateWidget.h"
2020-01-20 18:32:44 -03:00
# include "ProcessUnveiledPathsWidget.h"
2020-08-15 15:05:46 -03:00
# include "ThreadStackWidget.h"
# include <AK/NumberFormat.h>
2022-03-22 21:32:44 -03:00
# include <Applications/SystemMonitor/SystemMonitorGML.h>
2021-09-18 15:57:54 -03:00
# include <LibConfig/Client.h>
2021-01-07 21:03:49 -03:00
# include <LibCore/ArgsParser.h>
2021-12-02 20:28:36 -03:00
# include <LibCore/System.h>
2020-02-06 11:04:03 -03:00
# include <LibCore/Timer.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Action.h>
# include <LibGUI/ActionGroup.h>
# include <LibGUI/Application.h>
# include <LibGUI/BoxLayout.h>
2021-04-11 07:50:46 -03:00
# include <LibGUI/FileIconProvider.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/GroupBox.h>
2020-11-02 16:30:17 -03:00
# include <LibGUI/Icon.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/JsonArrayModel.h>
# include <LibGUI/Label.h>
# include <LibGUI/LazyWidget.h>
2020-02-14 21:56:30 -03:00
# include <LibGUI/Menu.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/Menubar.h>
2021-11-12 13:28:04 -03:00
# include <LibGUI/MessageBox.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Painter.h>
2021-04-04 17:04:49 -03:00
# include <LibGUI/SeparatorWidget.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/SortingProxyModel.h>
2021-04-04 16:39:54 -03:00
# include <LibGUI/StackWidget.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/Statusbar.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/TabWidget.h>
2020-08-04 16:02:19 -03:00
# include <LibGUI/TableView.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Widget.h>
# include <LibGUI/Window.h>
2021-04-11 07:50:46 -03:00
# include <LibGfx/FontDatabase.h>
2020-02-14 21:18:12 -03:00
# include <LibGfx/Palette.h>
2021-12-02 20:28:36 -03:00
# include <LibMain/Main.h>
2019-08-14 08:50:25 -03:00
# include <LibPCIDB/Database.h>
2020-08-04 09:23:18 -03:00
# include <serenity.h>
2019-02-27 21:43:50 -03:00
# include <signal.h>
2020-07-01 16:08:16 -03:00
# include <spawn.h>
2019-06-07 06:48:03 -03:00
# include <stdio.h>
# include <unistd.h>
2019-02-27 21:43:50 -03:00
2022-01-07 10:50:07 -03:00
static ErrorOr < NonnullRefPtr < GUI : : Window > > build_process_window ( pid_t ) ;
2022-03-22 21:32:44 -03:00
static void build_storage_widget ( GUI : : LazyWidget & ) ;
static void build_hardware_tab ( GUI : : LazyWidget & ) ;
static void build_performance_tab ( GUI : : Widget & ) ;
2019-08-11 05:11:21 -03:00
2021-04-13 11:18:20 -03:00
static RefPtr < GUI : : Statusbar > statusbar ;
2021-04-04 17:18:59 -03:00
2020-05-04 19:04:42 -03:00
class UnavailableProcessWidget final : public GUI : : Frame {
C_OBJECT ( UnavailableProcessWidget )
public :
2022-02-10 16:28:48 -03:00
virtual ~ UnavailableProcessWidget ( ) override = default ;
2020-05-04 19:04:42 -03:00
2022-04-01 14:58:27 -03:00
String const & text ( ) const { return m_text ; }
2020-05-04 19:04:42 -03:00
void set_text ( String text ) { m_text = move ( text ) ; }
private :
UnavailableProcessWidget ( String text )
: m_text ( move ( text ) )
{
}
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 ) ;
}
String m_text ;
} ;
2020-08-04 16:02:19 -03:00
static bool can_access_pid ( pid_t pid )
2020-05-04 19:04:42 -03:00
{
2021-07-27 17:52:32 -03:00
int rc = kill ( pid , 0 ) ;
return rc = = 0 ;
2020-05-04 19:04:42 -03:00
}
2021-12-02 20:28:36 -03:00
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
2019-02-27 21:43:50 -03:00
{
2021-05-13 15: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-02 20:28:36 -03:00
TRY ( Core : : System : : pledge ( " stdio thread proc recvfd sendfd rpath exec unix " ) ) ;
2020-01-11 17:17:39 -03:00
2021-12-02 20:28:36 -03:00
auto app = TRY ( GUI : : Application : : try_create ( arguments ) ) ;
2019-02-27 21:43:50 -03:00
2022-02-11 13:33:09 -03:00
Config : : pledge_domain ( " SystemMonitor " ) ;
2021-11-11 08:07:13 -03:00
2021-12-02 20:28:36 -03:00
TRY ( Core : : System : : unveil ( " /etc/passwd " , " r " ) ) ;
TRY ( Core : : System : : unveil ( " /res " , " r " ) ) ;
TRY ( Core : : System : : unveil ( " /proc " , " r " ) ) ;
TRY ( Core : : System : : unveil ( " /dev " , " r " ) ) ;
TRY ( Core : : System : : unveil ( " /bin " , " r " ) ) ;
TRY ( Core : : System : : unveil ( " /usr/lib " , " r " ) ) ;
2021-05-22 13:23:51 -03:00
2021-08-13 16:00:34 -03:00
// This directory only exists if ports are installed
2021-12-02 20: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 05:07:49 -03:00
2021-12-02 20: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 12:13:52 -03:00
2022-02-21 17:17:03 -03:00
// This file is only accesible when running as root
if ( auto result = Core : : System : : unveil ( " /boot/Kernel.debug " , " r " ) ; result . is_error ( ) & & result . error ( ) . code ( ) ! = EACCES )
return result . release_error ( ) ;
2021-12-02 20:28:36 -03:00
TRY ( Core : : System : : unveil ( " /bin/Profiler " , " rx " ) ) ;
TRY ( Core : : System : : unveil ( " /bin/Inspector " , " rx " ) ) ;
2021-12-18 20:56:17 -03:00
TRY ( Core : : System : : unveil ( nullptr , nullptr ) ) ;
2020-01-21 14:45:18 -03:00
2021-12-02 20:28:36 -03:00
StringView args_tab = " processes " sv ;
2021-01-07 21:03:49 -03:00
Core : : ArgsParser parser ;
2021-08-18 07:50:24 -03:00
parser . add_option ( args_tab , " Tab, one of 'processes', 'graphs', 'fs', 'hardware', or 'network' " , " open-tab " , ' t ' , " tab " ) ;
2021-12-02 20:28:36 -03:00
parser . parse ( arguments ) ;
2021-01-07 21:03:49 -03:00
StringView args_tab_view = args_tab ;
2020-11-02 16:30:17 -03:00
auto app_icon = GUI : : Icon : : default_icon ( " app-system-monitor " ) ;
2020-02-02 11:07:41 -03:00
auto window = GUI : : Window : : construct ( ) ;
2020-01-23 11:14:21 -03:00
window - > set_title ( " System Monitor " ) ;
2021-04-05 08:36:43 -03:00
window - > resize ( 560 , 430 ) ;
2020-01-23 11:14:21 -03:00
2022-01-07 10:50:07 -03:00
auto main_widget = TRY ( window - > try_set_main_widget < GUI : : Widget > ( ) ) ;
2022-03-22 21:32:44 -03:00
main_widget - > load_from_gml ( system_monitor_gml ) ;
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 17:04:49 -03:00
2022-03-22 21:32:44 -03:00
auto & process_table_container = * tabwidget . find_descendant_of_type_named < GUI : : Widget > ( " processes " ) ;
2019-07-27 04:39:43 -03:00
2021-04-04 15:46:46 -03:00
auto process_model = ProcessModel : : create ( ) ;
process_model - > on_state_update = [ & ] ( int process_count , int thread_count ) {
2021-04-04 17:18:59 -03:00
statusbar - > set_text ( 0 , String : : formatted ( " Processes: {} " , process_count ) ) ;
statusbar - > set_text ( 1 , String : : formatted ( " Threads: {} " , thread_count ) ) ;
2021-04-04 15:46:46 -03:00
} ;
2022-03-22 21:32:44 -03:00
auto & performance_widget = * tabwidget . find_descendant_of_type_named < GUI : : Widget > ( " performance " ) ;
build_performance_tab ( performance_widget ) ;
2019-05-07 12:11:48 -03:00
2022-03-22 21:32:44 -03:00
auto & storage_widget = * tabwidget . find_descendant_of_type_named < GUI : : LazyWidget > ( " storage " ) ;
build_storage_widget ( storage_widget ) ;
2019-08-11 05:11:21 -03:00
2022-03-22 21:32:44 -03:00
auto & hardware_widget = * tabwidget . find_descendant_of_type_named < GUI : : LazyWidget > ( " hardware " ) ;
build_hardware_tab ( hardware_widget ) ;
2019-08-08 15:43:30 -03:00
2022-03-22 21:32:44 -03:00
auto & process_table_view = * process_table_container . find_child_of_type_named < GUI : : TableView > ( " process_table " ) ;
2021-12-24 09:12:04 -03:00
process_table_view . set_model ( TRY ( GUI : : SortingProxyModel : : create ( process_model ) ) ) ;
2021-04-05 08:36:43 -03:00
for ( auto column = 0 ; column < ProcessModel : : Column : : __Count ; + + column )
process_table_view . set_column_visible ( column , false ) ;
process_table_view . set_column_visible ( ProcessModel : : Column : : Icon , true ) ;
process_table_view . set_column_visible ( ProcessModel : : Column : : PID , true ) ;
process_table_view . set_column_visible ( ProcessModel : : Column : : Name , true ) ;
process_table_view . set_column_visible ( ProcessModel : : Column : : CPU , true ) ;
process_table_view . set_column_visible ( ProcessModel : : Column : : User , true ) ;
process_table_view . set_column_visible ( ProcessModel : : Column : : Virtual , true ) ;
process_table_view . set_column_visible ( ProcessModel : : Column : : DirtyPrivate , true ) ;
2020-08-16 05:44:10 -03:00
process_table_view . set_key_column_and_sort_order ( ProcessModel : : Column : : CPU , GUI : : SortOrder : : Descending ) ;
2021-05-25 11:13:19 -03:00
process_model - > update ( ) ;
2019-03-10 08:13:22 -03:00
2021-09-18 15:57:54 -03:00
i32 frequency = Config : : read_i32 ( " SystemMonitor " , " Monitor " , " Frequency " , 3 ) ;
if ( frequency ! = 1 & & frequency ! = 3 & & frequency ! = 5 ) {
frequency = 3 ;
Config : : write_i32 ( " SystemMonitor " , " Monitor " , " Frequency " , frequency ) ;
}
2020-03-04 15:07:55 -03:00
auto & refresh_timer = window - > add < Core : : Timer > (
2021-09-18 15:57:54 -03:00
frequency * 1000 , [ & ] {
2021-05-25 11:13:19 -03:00
process_model - > update ( ) ;
2022-03-18 20:19:56 -03:00
if ( auto * memory_stats_widget = SystemMonitor : : MemoryStatsWidget : : the ( ) )
2020-02-14 21:18:12 -03:00
memory_stats_widget - > refresh ( ) ;
2020-02-23 06:57:42 -03:00
} ) ;
2019-02-27 21:43:50 -03:00
2020-08-09 15:19:28 -03:00
auto selected_id = [ & ] ( ProcessModel : : Column column ) - > pid_t {
2020-08-04 16:02:19 -03:00
if ( process_table_view . selection ( ) . is_empty ( ) )
return - 1 ;
2020-08-09 15:19:28 -03:00
auto pid_index = process_table_view . model ( ) - > index ( process_table_view . selection ( ) . first ( ) . row ( ) , column ) ;
2020-08-16 11:14:39 -03:00
return pid_index . data ( ) . to_i32 ( ) ;
2020-08-04 16:02:19 -03:00
} ;
2021-11-12 13:28:04 -03:00
auto selected_name = [ & ] ( ProcessModel : : Column column ) - > String {
if ( process_table_view . selection ( ) . is_empty ( ) )
return { } ;
auto pid_index = process_table_view . model ( ) - > index ( process_table_view . selection ( ) . first ( ) . row ( ) , column ) ;
return pid_index . data ( ) . to_string ( ) ;
} ;
2021-04-04 17:45:45 -03:00
auto kill_action = GUI : : Action : : create (
2021-11-12 13:18:50 -03:00
" &Kill Process " , { Mod_Ctrl , Key_K } , { Key_Delete } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/kill.png " ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( const GUI : : Action & ) {
2021-04-04 17:45:45 -03:00
pid_t pid = selected_id ( ProcessModel : : Column : : PID ) ;
2021-11-12 13:28:04 -03:00
if ( pid = = - 1 )
return ;
auto rc = GUI : : MessageBox : : show ( window , String : : formatted ( " Do you really want to kill \" {} \" (PID {})? " , selected_name ( ProcessModel : : Column : : Name ) , pid ) , " System Monitor " , GUI : : MessageBox : : Type : : Question , GUI : : MessageBox : : InputType : : YesNo ) ;
if ( rc = = GUI : : Dialog : : ExecYes )
2021-04-04 17:45:45 -03:00
kill ( pid , SIGKILL ) ;
} ,
& process_table_view ) ;
auto stop_action = GUI : : Action : : create (
2021-11-06 12:25:29 -03:00
" &Stop Process " , { Mod_Ctrl , Key_S } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/stop-hand.png " ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( const GUI : : Action & ) {
2021-04-04 17:45:45 -03:00
pid_t pid = selected_id ( ProcessModel : : Column : : PID ) ;
2021-11-12 13:28:04 -03:00
if ( pid = = - 1 )
return ;
auto rc = GUI : : MessageBox : : show ( window , String : : formatted ( " Do you really want to stop \" {} \" (PID {})? " , selected_name ( ProcessModel : : Column : : Name ) , pid ) , " System Monitor " , GUI : : MessageBox : : Type : : Question , GUI : : MessageBox : : InputType : : YesNo ) ;
if ( rc = = GUI : : Dialog : : ExecYes )
2021-04-04 17:45:45 -03:00
kill ( pid , SIGSTOP ) ;
} ,
& process_table_view ) ;
auto continue_action = GUI : : Action : : create (
2021-11-06 12:25:29 -03:00
" &Continue Process " , { Mod_Ctrl , Key_C } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/continue.png " ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( const GUI : : Action & ) {
2021-04-04 17: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 13:51:06 -03:00
" &Profile Process " , { Mod_Ctrl , Key_P } ,
2021-11-06 12:25:29 -03:00
Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/app-profiler.png " ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( auto & ) {
2020-08-09 15:19:28 -03:00
pid_t pid = selected_id ( ProcessModel : : Column : : PID ) ;
2020-08-02 12:02:56 -03:00
if ( pid ! = - 1 ) {
2021-01-16 08:00:33 -03:00
auto pid_string = String : : number ( pid ) ;
2020-08-02 12:02:56 -03:00
pid_t child ;
const char * argv [ ] = { " /bin/Profiler " , " --pid " , pid_string . characters ( ) , nullptr } ;
if ( ( errno = posix_spawn ( & child , " /bin/Profiler " , nullptr , nullptr , const_cast < char * * > ( argv ) , environ ) ) ) {
perror ( " posix_spawn " ) ;
2020-08-04 09:23:18 -03:00
} else {
if ( disown ( child ) < 0 )
perror ( " disown " ) ;
2020-08-02 12:02:56 -03:00
}
2020-07-01 16:08:16 -03:00
}
2021-04-04 17:45:45 -03:00
} ,
& process_table_view ) ;
2020-07-01 16:08:16 -03:00
2021-04-04 16:52:55 -03:00
HashMap < pid_t , NonnullRefPtr < GUI : : Window > > process_windows ;
2021-04-04 17:39:41 -03:00
auto process_properties_action = GUI : : CommonActions : : make_properties_action (
[ & ] ( auto & ) {
auto pid = selected_id ( ProcessModel : : Column : : PID ) ;
2021-05-18 03:10:27 -03:00
if ( pid = = - 1 )
return ;
2021-04-04 17:39:41 -03:00
RefPtr < GUI : : Window > process_window ;
2021-04-04 17:42:05 -03:00
auto it = process_windows . find ( pid ) ;
if ( it = = process_windows . end ( ) ) {
2022-01-07 10: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 17: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 17:42:05 -03:00
} else {
process_window = it - > value ;
2021-04-04 17:39:41 -03:00
}
process_window - > show ( ) ;
process_window - > move_to_front ( ) ;
2021-04-04 17:45:45 -03:00
} ,
& process_table_view ) ;
2021-04-04 16:52:55 -03:00
2021-07-21 16:21:03 -03:00
auto & file_menu = window - > add_menu ( " &File " ) ;
2021-05-01 05:45:39 -03:00
file_menu . add_action ( GUI : : CommonActions : : make_quit_action ( [ ] ( auto & ) {
2020-07-04 11:52:01 -03:00
GUI : : Application : : the ( ) - > quit ( ) ;
2019-02-27 21:43:50 -03:00
} ) ) ;
2020-02-02 11:07:41 -03:00
auto process_context_menu = GUI : : Menu : : construct ( ) ;
2019-06-30 03:15:18 -03:00
process_context_menu - > add_action ( kill_action ) ;
process_context_menu - > add_action ( stop_action ) ;
process_context_menu - > add_action ( continue_action ) ;
2020-07-01 16:08:16 -03:00
process_context_menu - > add_separator ( ) ;
process_context_menu - > add_action ( profile_action ) ;
2021-04-04 16:52:55 -03:00
process_context_menu - > add_separator ( ) ;
process_context_menu - > add_action ( process_properties_action ) ;
2020-12-20 20:09:48 -03:00
process_table_view . on_context_menu_request = [ & ] ( [[maybe_unused]] const GUI : : ModelIndex & index , const GUI : : ContextMenuEvent & event ) {
2021-05-18 03:10:27 -03:00
if ( index . is_valid ( ) )
process_context_menu - > popup ( event . screen_position ( ) , process_properties_action ) ;
2019-06-30 03:15:18 -03:00
} ;
2021-07-21 16:21:03 -03:00
auto & frequency_menu = window - > add_menu ( " F&requency " ) ;
2020-02-02 11:07:41 -03:00
GUI : : ActionGroup frequency_action_group ;
2020-01-08 16:45:43 -03:00
frequency_action_group . set_exclusive ( true ) ;
2021-09-18 15:57:54 -03:00
auto make_frequency_action = [ & ] ( int seconds ) {
2021-04-23 12:25:33 -03:00
auto action = GUI : : Action : : create_checkable ( String : : formatted ( " &{} Sec " , seconds ) , [ & refresh_timer , seconds ] ( auto & ) {
2021-09-18 15:57:54 -03:00
Config : : write_i32 ( " SystemMonitor " , " Monitor " , " Frequency " , seconds ) ;
2021-04-23 12:25:33 -03:00
refresh_timer . restart ( seconds * 1000 ) ;
2020-01-08 16:45:43 -03:00
} ) ;
2021-04-23 12:25:33 -03:00
action - > set_status_tip ( String : : formatted ( " Refresh every {} seconds " , seconds ) ) ;
2021-09-18 15:57:54 -03:00
action - > set_checked ( frequency = = seconds ) ;
2020-01-08 16:45:43 -03:00
frequency_action_group . add_action ( * action ) ;
2020-04-04 07:18:40 -03:00
frequency_menu . add_action ( * action ) ;
2020-01-08 16:45:43 -03:00
} ;
2021-04-23 12:25:33 -03:00
make_frequency_action ( 1 ) ;
2021-09-18 15:57:54 -03:00
make_frequency_action ( 3 ) ;
2021-04-23 12:25:33 -03:00
make_frequency_action ( 5 ) ;
2020-01-08 16:45:43 -03:00
2021-07-21 16:21:03 -03:00
auto & help_menu = window - > add_menu ( " &Help " ) ;
2021-01-04 19:51:49 -03:00
help_menu . add_action ( GUI : : CommonActions : : make_about_action ( " System Monitor " , app_icon , window ) ) ;
2019-02-27 21:43:50 -03:00
2021-04-04 16:39:54 -03:00
process_table_view . on_activation = [ & ] ( auto & ) {
2021-05-28 12:01:36 -03:00
if ( process_properties_action - > is_enabled ( ) )
process_properties_action - > activate ( ) ;
} ;
2021-07-27 19:23:34 -03:00
static pid_t last_selected_pid ;
2021-05-28 12:01:36 -03:00
process_table_view . on_selection_change = [ & ] {
pid_t pid = selected_id ( ProcessModel : : Column : : PID ) ;
2021-07-27 19:23:34 -03:00
if ( pid = = last_selected_pid | | pid < 1 )
return ;
last_selected_pid = pid ;
2021-05-28 12: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 ) ;
process_properties_action - > set_enabled ( has_access ) ;
2019-07-27 04:39:43 -03:00
} ;
2021-04-23 12: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-27 21:43:50 -03:00
window - > show ( ) ;
2020-11-02 16:30:17 -03:00
window - > set_icon ( app_icon . bitmap_for_size ( 16 ) ) ;
2019-04-16 12:55:27 -03:00
2021-01-07 21:03:49 -03:00
if ( args_tab_view = = " processes " )
2021-04-04 17:26:35 -03:00
tabwidget . set_active_widget ( & process_table_container ) ;
2021-01-07 21:03:49 -03:00
else if ( args_tab_view = = " graphs " )
2022-03-22 21:32:44 -03:00
tabwidget . set_active_widget ( & performance_widget ) ;
2021-01-07 21:03:49 -03:00
else if ( args_tab_view = = " fs " )
2022-03-22 21:32:44 -03:00
tabwidget . set_active_widget ( & storage_widget ) ;
2021-08-18 07:50:24 -03:00
else if ( args_tab_view = = " hardware " )
2022-03-22 21:32:44 -03:00
tabwidget . set_active_widget ( & hardware_widget ) ;
2021-01-07 21:03:49 -03:00
else if ( args_tab_view = = " network " )
2022-03-22 21:32:44 -03:00
tabwidget . set_active_widget ( tabwidget . find_descendant_of_type_named < GUI : : Widget > ( " network " ) ) ;
2021-01-07 21:03:49 -03:00
2020-07-04 09:05:19 -03:00
return app - > exec ( ) ;
2019-02-27 21:43:50 -03:00
}
2019-08-11 05:11:21 -03:00
2021-04-13 11:18:20 -03:00
class ProgressbarPaintingDelegate final : public GUI : : TableCellPaintingDelegate {
2019-08-14 15:38:18 -03:00
public :
2022-02-10 16:28:48 -03:00
virtual ~ ProgressbarPaintingDelegate ( ) override = default ;
2019-08-14 15:38:18 -03:00
2022-04-01 14:58:27 -03:00
virtual void paint ( GUI : : Painter & painter , Gfx : : IntRect const & a_rect , Palette const & palette , const GUI : : ModelIndex & index ) override
2019-08-14 15:38:18 -03:00
{
auto rect = a_rect . shrunken ( 2 , 2 ) ;
2020-08-16 11:05:24 -03:00
auto percentage = index . data ( GUI : : ModelRole : : Custom ) . to_i32 ( ) ;
2019-08-14 15:38:18 -03:00
2020-08-16 11:14:39 -03:00
auto data = index . data ( ) ;
2019-08-14 15:38:18 -03:00
String text ;
if ( data . is_string ( ) )
text = data . as_string ( ) ;
2021-04-13 11:18:20 -03:00
Gfx : : StylePainter : : paint_progressbar ( painter , rect , palette , 0 , 100 , percentage , text ) ;
2019-08-14 15:38:18 -03:00
painter . draw_rect ( rect , Color : : Black ) ;
}
} ;
2022-01-07 10:50:07 -03:00
ErrorOr < NonnullRefPtr < GUI : : Window > > build_process_window ( pid_t pid )
2021-04-04 16:39:54 -03:00
{
auto window = GUI : : Window : : construct ( ) ;
window - > resize ( 480 , 360 ) ;
2021-05-12 13:52:56 -03:00
window - > set_title ( String : : formatted ( " PID {} - System Monitor " , pid ) ) ;
2021-04-04 16:39:54 -03:00
2022-02-18 07:51:50 -03:00
auto app_icon = GUI : : Icon : : default_icon ( " app-system-monitor " ) ;
window - > set_icon ( app_icon . bitmap_for_size ( 16 ) ) ;
2022-01-07 10:50:07 -03:00
auto main_widget = TRY ( window - > try_set_main_widget < GUI : : Widget > ( ) ) ;
main_widget - > set_fill_with_background_color ( true ) ;
main_widget - > set_layout < GUI : : VerticalBoxLayout > ( ) ;
2021-04-04 16:39:54 -03:00
2022-01-07 10:50:07 -03:00
auto & hero_container = main_widget - > add < GUI : : Widget > ( ) ;
2021-04-11 07:50:46 -03:00
hero_container . set_shrink_to_fit ( true ) ;
hero_container . set_layout < GUI : : HorizontalBoxLayout > ( ) ;
Userland+LibGUI: Add shorthand versions of the Margins constructor
This allows for typing [8] instead of [8, 8, 8, 8] to specify the same
margin on all edges, for example. The constructors follow CSS' style of
specifying margins. The added constructors are:
- Margins(int all): Sets the same margin on all edges.
- Margins(int vertical, int horizontal): Sets the first argument to top
and bottom margins, and the second argument to left and right margins.
- Margins(int top, int vertical, int bottom): Sets the first argument to
the top margin, the second argument to the left and right margins,
and the third argument to the bottom margin.
2021-08-16 21:11:38 -03:00
hero_container . layout ( ) - > set_margins ( 4 ) ;
2021-04-11 07:50:46 -03:00
hero_container . layout ( ) - > set_spacing ( 8 ) ;
auto & icon_label = hero_container . add < GUI : : Label > ( ) ;
icon_label . set_fixed_size ( 32 , 32 ) ;
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 ( ) ) {
icon_label . set_icon ( icon_data . as_icon ( ) . bitmap_for_size ( 32 ) ) ;
}
auto & process_name_label = hero_container . add < GUI : : Label > ( ) ;
2021-05-20 13:40:48 -03:00
process_name_label . set_font ( Gfx : : FontDatabase : : default_font ( ) . bold_variant ( ) ) ;
2021-04-11 07:50:46 -03:00
process_name_label . set_text_alignment ( Gfx : : TextAlignment : : CenterLeft ) ;
process_name_label . set_text ( String : : formatted ( " {} (PID {}) " ,
process_index . sibling_at_column ( ProcessModel : : Column : : Name ) . data ( ) . to_string ( ) ,
pid ) ) ;
2022-01-07 10:50:07 -03:00
auto & separator = main_widget - > add < GUI : : HorizontalSeparator > ( ) ;
2021-04-11 07:50:46 -03:00
separator . set_fixed_height ( 2 ) ;
2022-01-07 10:50:07 -03:00
auto & widget_stack = main_widget - > add < GUI : : StackWidget > ( ) ;
2021-04-04 16:39:54 -03:00
auto & unavailable_process_widget = widget_stack . add < UnavailableProcessWidget > ( String : : formatted ( " Unable to access PID {} " , pid ) ) ;
auto & process_tab_widget = widget_stack . add < GUI : : TabWidget > ( ) ;
2021-04-11 08:24:59 -03:00
process_tab_widget . add_tab < ProcessStateWidget > ( " State " , pid ) ;
2021-04-04 16:39:54 -03:00
auto & memory_map_widget = process_tab_widget . add_tab < ProcessMemoryMapWidget > ( " Memory map " ) ;
auto & open_files_widget = process_tab_widget . add_tab < ProcessFileDescriptorMapWidget > ( " Open files " ) ;
auto & unveiled_paths_widget = process_tab_widget . add_tab < ProcessUnveiledPathsWidget > ( " Unveiled paths " ) ;
auto & thread_stack_widget = process_tab_widget . add_tab < ThreadStackWidget > ( " Stack " ) ;
open_files_widget . set_pid ( pid ) ;
thread_stack_widget . set_ids ( pid , pid ) ;
memory_map_widget . set_pid ( pid ) ;
unveiled_paths_widget . set_pid ( pid ) ;
if ( can_access_pid ( pid ) )
widget_stack . set_active_widget ( & process_tab_widget ) ;
else
widget_stack . set_active_widget ( & unavailable_process_widget ) ;
return window ;
}
2022-03-22 21:32:44 -03:00
void build_storage_widget ( GUI : : LazyWidget & widget )
2019-08-11 05:11:21 -03:00
{
2022-03-22 21:32:44 -03:00
widget . on_first_show = [ ] ( GUI : : LazyWidget & self ) {
auto & fs_table_view = * self . find_child_of_type_named < GUI : : TableView > ( " storage_table " ) ;
2019-10-02 15:26:19 -03:00
2020-02-02 11:07:41 -03:00
Vector < GUI : : JsonArrayModel : : FieldSpec > df_fields ;
2020-02-06 07:56:38 -03:00
df_fields . empend ( " mount_point " , " Mount point " , Gfx : : TextAlignment : : CenterLeft ) ;
df_fields . empend ( " class_name " , " Class " , Gfx : : TextAlignment : : CenterLeft ) ;
2020-04-06 05:55:21 -03:00
df_fields . empend ( " source " , " Source " , Gfx : : TextAlignment : : CenterLeft ) ;
2019-10-02 15:26:19 -03:00
df_fields . empend (
2020-02-06 07:56:38 -03:00
" Size " , Gfx : : TextAlignment : : CenterRight ,
2019-10-02 15:26:19 -03:00
[ ] ( const JsonObject & object ) {
2020-02-22 17:20:01 -03:00
StringBuilder size_builder ;
size_builder . append ( " " ) ;
2021-05-26 10:26:16 -03:00
size_builder . append ( human_readable_size ( object . get ( " total_block_count " ) . to_u64 ( ) * object . get ( " block_size " ) . to_u64 ( ) ) ) ;
2020-02-22 17:20:01 -03:00
size_builder . append ( " " ) ;
return size_builder . to_string ( ) ;
2019-10-02 15:26:19 -03:00
} ,
[ ] ( const JsonObject & object ) {
2021-05-26 10:26:16 -03:00
return object . get ( " total_block_count " ) . to_u64 ( ) * object . get ( " block_size " ) . to_u64 ( ) ;
2020-03-02 16:59:47 -03:00
} ,
[ ] ( const JsonObject & object ) {
2021-05-26 10:26:16 -03:00
auto total_blocks = object . get ( " total_block_count " ) . to_u64 ( ) ;
2020-03-02 16:59:47 -03:00
if ( total_blocks = = 0 )
return 0 ;
2021-05-26 10:26:16 -03:00
auto free_blocks = object . get ( " free_block_count " ) . to_u64 ( ) ;
2020-03-02 16:59:47 -03:00
auto used_blocks = total_blocks - free_blocks ;
2021-05-26 10:26:16 -03:00
int percentage = ( static_cast < double > ( used_blocks ) / static_cast < double > ( total_blocks ) * 100.0 ) ;
2020-03-02 16:59:47 -03:00
return percentage ;
2019-10-02 15:26:19 -03:00
} ) ;
df_fields . empend (
2020-02-06 07:56:38 -03:00
" Used " , Gfx : : TextAlignment : : CenterRight ,
2019-10-02 15:26:19 -03:00
[ ] ( const JsonObject & object ) {
2021-05-26 10:26:16 -03:00
auto total_blocks = object . get ( " total_block_count " ) . to_u64 ( ) ;
auto free_blocks = object . get ( " free_block_count " ) . to_u64 ( ) ;
2019-08-12 06:56:30 -03:00
auto used_blocks = total_blocks - free_blocks ;
2021-05-26 10:26:16 -03:00
return human_readable_size ( used_blocks * object . get ( " block_size " ) . to_u64 ( ) ) ; } ,
2019-10-02 15:26:19 -03:00
[ ] ( const JsonObject & object ) {
2021-05-26 10:26:16 -03:00
auto total_blocks = object . get ( " total_block_count " ) . to_u64 ( ) ;
auto free_blocks = object . get ( " free_block_count " ) . to_u64 ( ) ;
2019-10-02 15:26:19 -03:00
auto used_blocks = total_blocks - free_blocks ;
2021-05-26 10:26:16 -03:00
return used_blocks * object . get ( " block_size " ) . to_u64 ( ) ;
2019-10-02 15:26:19 -03:00
} ) ;
df_fields . empend (
2020-02-06 07:56:38 -03:00
" Available " , Gfx : : TextAlignment : : CenterRight ,
2019-10-02 15:26:19 -03:00
[ ] ( const JsonObject & object ) {
2021-05-26 10:26:16 -03:00
return human_readable_size ( object . get ( " free_block_count " ) . to_u64 ( ) * object . get ( " block_size " ) . to_u64 ( ) ) ;
2019-10-02 15:26:19 -03:00
} ,
[ ] ( const JsonObject & object ) {
2021-05-26 10:26:16 -03:00
return object . get ( " free_block_count " ) . to_u64 ( ) * object . get ( " block_size " ) . to_u64 ( ) ;
2019-10-02 15:26:19 -03:00
} ) ;
2020-02-06 07:56:38 -03:00
df_fields . empend ( " Access " , Gfx : : TextAlignment : : CenterLeft , [ ] ( const JsonObject & object ) {
2020-05-28 12:03:58 -03:00
bool readonly = object . get ( " readonly " ) . to_bool ( ) ;
int mount_flags = object . get ( " mount_flags " ) . to_int ( ) ;
return readonly | | ( mount_flags & MS_RDONLY ) ? " Read-only " : " Read/Write " ;
2019-08-12 06:56:30 -03:00
} ) ;
2020-02-06 07:56:38 -03:00
df_fields . empend ( " Mount flags " , Gfx : : TextAlignment : : CenterLeft , [ ] ( const JsonObject & object ) {
2020-01-11 13:00:15 -03:00
int mount_flags = object . get ( " mount_flags " ) . to_int ( ) ;
StringBuilder builder ;
bool first = true ;
auto check = [ & ] ( int flag , const char * name ) {
if ( ! ( mount_flags & flag ) )
return ;
if ( ! first )
builder . append ( ' , ' ) ;
builder . append ( name ) ;
first = false ;
} ;
check ( MS_NODEV , " nodev " ) ;
check ( MS_NOEXEC , " noexec " ) ;
check ( MS_NOSUID , " nosuid " ) ;
check ( MS_BIND , " bind " ) ;
2020-05-28 12:03:58 -03:00
check ( MS_RDONLY , " ro " ) ;
2022-03-09 08:37:09 -03:00
check ( MS_WXALLOWED , " wxallowed " ) ;
2020-01-11 13:00:15 -03:00
if ( builder . string_view ( ) . is_empty ( ) )
return String ( " defaults " ) ;
return builder . to_string ( ) ;
} ) ;
2020-02-06 07:56:38 -03:00
df_fields . empend ( " free_block_count " , " Free blocks " , Gfx : : TextAlignment : : CenterRight ) ;
df_fields . empend ( " total_block_count " , " Total blocks " , Gfx : : TextAlignment : : CenterRight ) ;
df_fields . empend ( " free_inode_count " , " Free inodes " , Gfx : : TextAlignment : : CenterRight ) ;
df_fields . empend ( " total_inode_count " , " Total inodes " , Gfx : : TextAlignment : : CenterRight ) ;
df_fields . empend ( " block_size " , " Block size " , Gfx : : TextAlignment : : CenterRight ) ;
2021-05-25 11:13:19 -03:00
2021-12-24 09:12:04 -03:00
fs_table_view . set_model ( MUST ( GUI : : SortingProxyModel : : create ( GUI : : JsonArrayModel : : create ( " /proc/df " , move ( df_fields ) ) ) ) ) ;
2019-08-14 15:38:18 -03:00
2021-04-13 11:18:20 -03:00
fs_table_view . set_column_painting_delegate ( 3 , make < ProgressbarPaintingDelegate > ( ) ) ;
2019-08-14 15:38:18 -03:00
2021-05-25 11:13:19 -03:00
fs_table_view . model ( ) - > invalidate ( ) ;
2019-10-02 15:26:19 -03:00
} ;
2019-08-11 05:11:21 -03:00
}
2019-08-14 08:50:25 -03:00
2022-03-22 21:32:44 -03:00
void build_hardware_tab ( GUI : : LazyWidget & widget )
2019-08-14 08:50:25 -03:00
{
2022-03-22 21:32:44 -03:00
widget . on_first_show = [ ] ( GUI : : LazyWidget & self ) {
2021-08-18 07:50:24 -03:00
{
Vector < GUI : : JsonArrayModel : : FieldSpec > processors_field ;
processors_field . empend ( " processor " , " Processor " , Gfx : : TextAlignment : : CenterRight ) ;
processors_field . empend ( " cpuid " , " CPUID " , Gfx : : TextAlignment : : CenterLeft ) ;
2022-01-11 08:49:09 -03:00
processors_field . empend ( " brand " , " Brand " , Gfx : : TextAlignment : : CenterLeft ) ;
2021-08-18 07:50:24 -03:00
processors_field . empend ( " Features " , Gfx : : TextAlignment : : CenterLeft , [ ] ( auto & object ) {
StringBuilder builder ;
auto features = object . get ( " features " ) . as_array ( ) ;
for ( auto & feature : features . values ( ) ) {
builder . append ( feature . to_string ( ) ) ;
builder . append ( ' ' ) ;
}
return GUI : : Variant ( builder . to_string ( ) ) ;
2019-10-02 15:26:19 -03:00
} ) ;
2021-08-18 07:50:24 -03:00
processors_field . empend ( " family " , " Family " , Gfx : : TextAlignment : : CenterRight ) ;
processors_field . empend ( " model " , " Model " , Gfx : : TextAlignment : : CenterRight ) ;
processors_field . empend ( " stepping " , " Stepping " , Gfx : : TextAlignment : : CenterRight ) ;
processors_field . empend ( " type " , " Type " , Gfx : : TextAlignment : : CenterRight ) ;
2022-03-22 21:32:44 -03:00
auto & processors_table_view = * self . find_descendant_of_type_named < GUI : : TableView > ( " cpus_table " ) ;
2021-08-18 07:50:24 -03:00
auto json_model = GUI : : JsonArrayModel : : create ( " /proc/cpuinfo " , move ( processors_field ) ) ;
processors_table_view . set_model ( json_model ) ;
json_model - > invalidate ( ) ;
}
2019-10-02 15:26:19 -03:00
2021-08-18 07:50:24 -03:00
{
auto db = PCIDB : : Database : : open ( ) ;
if ( ! db )
warnln ( " Couldn't open PCI ID database! " ) ;
Vector < GUI : : JsonArrayModel : : FieldSpec > pci_fields ;
pci_fields . empend (
" Address " , Gfx : : TextAlignment : : CenterLeft ,
[ ] ( const JsonObject & object ) {
auto seg = object . get ( " seg " ) . to_u32 ( ) ;
auto bus = object . get ( " bus " ) . to_u32 ( ) ;
auto device = object . get ( " device " ) . to_u32 ( ) ;
auto function = object . get ( " function " ) . to_u32 ( ) ;
return String : : formatted ( " {:04x}:{:02x}:{:02x}.{} " , seg , bus , device , function ) ;
} ) ;
pci_fields . empend (
" Class " , Gfx : : TextAlignment : : CenterLeft ,
[ db ] ( const JsonObject & object ) {
auto class_id = object . get ( " class " ) . to_u32 ( ) ;
String class_name = db ? db - > get_class ( class_id ) : nullptr ;
2022-01-17 09:08:12 -03:00
return class_name . is_empty ( ) ? String : : formatted ( " Unknown class: {:04x} " , class_id ) : class_name ;
2021-08-18 07:50:24 -03:00
} ) ;
pci_fields . empend (
" Vendor " , Gfx : : TextAlignment : : CenterLeft ,
[ db ] ( const JsonObject & object ) {
auto vendor_id = object . get ( " vendor_id " ) . to_u32 ( ) ;
String vendor_name = db ? db - > get_vendor ( vendor_id ) : nullptr ;
2022-01-17 09:08:12 -03:00
return vendor_name . is_empty ( ) ? String : : formatted ( " Unknown vendor: {:02x} " , vendor_id ) : vendor_name ;
2021-08-18 07:50:24 -03:00
} ) ;
pci_fields . empend (
" Device " , Gfx : : TextAlignment : : CenterLeft ,
[ db ] ( const JsonObject & object ) {
auto vendor_id = object . get ( " vendor_id " ) . to_u32 ( ) ;
auto device_id = object . get ( " device_id " ) . to_u32 ( ) ;
String device_name = db ? db - > get_device ( vendor_id , device_id ) : nullptr ;
2022-01-17 09:08:12 -03:00
return device_name . is_empty ( ) ? String : : formatted ( " Unknown device: {:02x} " , device_id ) : device_name ;
2021-08-18 07:50:24 -03:00
} ) ;
pci_fields . empend (
" Revision " , Gfx : : TextAlignment : : CenterRight ,
[ ] ( const JsonObject & object ) {
auto revision_id = object . get ( " revision_id " ) . to_u32 ( ) ;
return String : : formatted ( " {:02x} " , revision_id ) ;
} ) ;
2022-03-22 21:32:44 -03:00
auto & pci_table_view = * self . find_descendant_of_type_named < GUI : : TableView > ( " pci_dev_table " ) ;
2021-12-24 09:12:04 -03:00
pci_table_view . set_model ( MUST ( GUI : : SortingProxyModel : : create ( GUI : : JsonArrayModel : : create ( " /proc/pci " , move ( pci_fields ) ) ) ) ) ;
2021-08-18 07:50:24 -03:00
pci_table_view . model ( ) - > invalidate ( ) ;
}
2019-10-02 15:26:19 -03:00
} ;
2019-08-14 08:50:25 -03:00
}
2019-08-18 10:18:20 -03:00
2022-03-22 21:32:44 -03:00
void build_performance_tab ( GUI : : Widget & graphs_container )
2019-10-02 15:26:19 -03:00
{
2022-03-22 21:32:44 -03:00
auto & cpu_graph_group_box = * graphs_container . find_descendant_of_type_named < GUI : : GroupBox > ( " cpu_graph " ) ;
2022-01-13 19:55:58 -03:00
2022-01-16 10:15:02 -03:00
size_t cpu_graphs_per_row = min ( 4 , ProcessModel : : the ( ) . cpus ( ) . size ( ) ) ;
2022-01-13 19: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-18 19:56:04 -03:00
Vector < SystemMonitor : : GraphWidget & > cpu_graphs ;
2022-01-13 19:55:58 -03:00
for ( auto row = 0u ; row < cpu_graph_rows ; + + row ) {
auto & cpu_graph_row = cpu_graph_group_box . add < GUI : : Widget > ( ) ;
cpu_graph_row . set_layout < GUI : : HorizontalBoxLayout > ( ) ;
cpu_graph_row . layout ( ) - > set_margins ( 6 ) ;
cpu_graph_row . set_fixed_height ( 108 ) ;
for ( auto i = 0u ; i < cpu_graphs_per_row ; + + i ) {
2022-03-18 19:56:04 -03:00
auto & cpu_graph = cpu_graph_row . add < SystemMonitor : : GraphWidget > ( ) ;
2022-01-13 19:55:58 -03:00
cpu_graph . set_max ( 100 ) ;
cpu_graph . set_value_format ( 0 , {
. graph_color_role = ColorRole : : SyntaxPreprocessorStatement ,
. text_formatter = [ ] ( int value ) {
return String : : formatted ( " Total: {}% " , value ) ;
} ,
} ) ;
cpu_graph . set_value_format ( 1 , {
. graph_color_role = ColorRole : : SyntaxPreprocessorValue ,
. text_formatter = [ ] ( int value ) {
return String : : formatted ( " Kernel: {}% " , value ) ;
} ,
} ) ;
cpu_graphs . append ( cpu_graph ) ;
}
2021-04-04 15:49:37 -03:00
}
2022-04-01 14:58:27 -03:00
ProcessModel : : the ( ) . on_cpu_info_change = [ cpu_graphs ] ( NonnullOwnPtrVector < ProcessModel : : CpuInfo > const & cpus ) mutable {
2021-04-04 17:18:59 -03:00
float sum_cpu = 0 ;
for ( size_t i = 0 ; i < cpus . size ( ) ; + + i ) {
2021-09-18 08:15:42 -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 ) } ) ;
2021-04-04 17:18:59 -03:00
sum_cpu + = cpus [ i ] . total_cpu_percent ;
}
float cpu_usage = sum_cpu / ( float ) cpus . size ( ) ;
statusbar - > set_text ( 2 , String : : formatted ( " CPU usage: {}% " , ( int ) roundf ( cpu_usage ) ) ) ;
2019-10-02 15:26:19 -03:00
} ;
2021-04-04 15:49:37 -03:00
2022-03-22 21:32:44 -03:00
auto & memory_graph = * graphs_container . find_descendant_of_type_named < SystemMonitor : : GraphWidget > ( " memory_graph " ) ;
2021-04-04 15:49:37 -03:00
memory_graph . set_value_format ( 0 , {
. graph_color_role = ColorRole : : SyntaxComment ,
2021-09-03 13:26:58 -03:00
. text_formatter = [ ] ( int bytes ) {
return String : : formatted ( " Committed: {} " , human_readable_size ( bytes ) ) ;
2021-04-04 15:49:37 -03:00
} ,
} ) ;
memory_graph . set_value_format ( 1 , {
. graph_color_role = ColorRole : : SyntaxPreprocessorStatement ,
2021-09-03 13:26:58 -03:00
. text_formatter = [ ] ( int bytes ) {
return String : : formatted ( " Allocated: {} " , human_readable_size ( bytes ) ) ;
2021-04-04 15:49:37 -03:00
} ,
} ) ;
memory_graph . set_value_format ( 2 , {
. graph_color_role = ColorRole : : SyntaxPreprocessorValue ,
2021-09-03 13:26:58 -03:00
. text_formatter = [ ] ( int bytes ) {
return String : : formatted ( " Kernel heap: {} " , human_readable_size ( bytes ) ) ;
2021-04-04 15:49:37 -03:00
} ,
} ) ;
2019-10-02 15:26:19 -03:00
}