2020-01-18 05:38:21 -03:00
/*
2021-01-11 05:52:18 -03:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2021-12-18 08:28:27 -03:00
* Copyright ( c ) 2021 , Julius Heijmen < julius . heijmen @ gmail . com >
2023-02-02 09:00:40 -03:00
* Copyright ( c ) 2023 , Jelle Raaijmakers < jelle @ gmta . nl >
2023-08-25 18:47:42 -03:00
* Copyright ( c ) 2023 , Jakub Berkop < jakub . berkop @ gmail . com >
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
*/
2021-08-16 16:20:24 -03:00
# include "FlameGraphView.h"
2021-02-27 14:33:30 -03:00
# include "IndividualSampleModel.h"
2019-12-12 18:01:06 -03:00
# include "Profile.h"
2021-08-16 16:20:24 -03:00
# include "ProfileModel.h"
2021-05-06 14:50:18 -03:00
# include "TimelineContainer.h"
# include "TimelineHeader.h"
2021-05-06 14:00:48 -03:00
# include "TimelineTrack.h"
2021-05-06 13:59:51 -03:00
# include "TimelineView.h"
2020-07-01 16:07:53 -03:00
# include <LibCore/ArgsParser.h>
2020-07-01 15:49:51 -03:00
# include <LibCore/ElapsedTimer.h>
2020-12-26 08:58:50 -03:00
# include <LibCore/ProcessStatisticsReader.h>
2021-11-23 20:09:41 -03:00
# include <LibCore/System.h>
2020-07-01 15:49:51 -03:00
# include <LibCore/Timer.h>
2021-02-24 12:40:36 -03:00
# include <LibDesktop/Launcher.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Action.h>
# include <LibGUI/Application.h>
# include <LibGUI/BoxLayout.h>
2020-07-01 15:49:51 -03:00
# include <LibGUI/Button.h>
# include <LibGUI/Label.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Menu.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/Menubar.h>
2020-07-01 15:49:51 -03:00
# include <LibGUI/MessageBox.h>
2020-02-16 05:17:49 -03:00
# include <LibGUI/Model.h>
2020-07-28 07:09:19 -03:00
# include <LibGUI/ProcessChooser.h>
2020-04-12 05:57:44 -03:00
# include <LibGUI/Splitter.h>
2021-05-06 14:12:58 -03:00
# include <LibGUI/Statusbar.h>
2021-02-27 14:33:30 -03:00
# include <LibGUI/TabWidget.h>
2020-04-11 13:46:11 -03:00
# include <LibGUI/TableView.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/TreeView.h>
# include <LibGUI/Window.h>
2021-11-23 20:09:41 -03:00
# include <LibMain/Main.h>
2020-07-01 15:49:51 -03:00
# include <serenity.h>
# include <string.h>
2021-05-04 12:45:02 -03:00
using namespace Profiler ;
2021-01-11 05:52:18 -03:00
static bool generate_profile ( pid_t & pid ) ;
2019-12-12 18:01:06 -03:00
2021-11-23 20:09:41 -03:00
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
2019-12-12 18:01:06 -03:00
{
2020-07-01 16:07:53 -03:00
int pid = 0 ;
2023-02-28 17:41:43 -03:00
StringView perfcore_file_arg ;
2021-03-04 19:07:38 -03:00
Core : : ArgsParser args_parser ;
2020-07-01 16:07:53 -03:00
args_parser . add_option ( pid , " PID to profile " , " pid " , ' p ' , " PID " ) ;
2021-03-04 19:07:38 -03:00
args_parser . add_positional_argument ( perfcore_file_arg , " Path of perfcore file " , " perfcore-file " , Core : : ArgsParser : : Required : : No ) ;
2021-11-23 20:09:41 -03:00
args_parser . parse ( arguments ) ;
2021-03-04 19:07:38 -03:00
2023-02-28 17:41:43 -03:00
if ( pid & & ! perfcore_file_arg . is_empty ( ) ) {
2021-03-04 19:07:38 -03:00
warnln ( " -p/--pid option and perfcore-file argument must not be used together! " ) ;
return 1 ;
}
2020-07-01 16:07:53 -03:00
2023-05-05 01:24:53 -03:00
auto app = TRY ( GUI : : Application : : create ( arguments ) ) ;
2022-07-11 14:32:29 -03:00
auto app_icon = TRY ( GUI : : Icon : : try_create_default_icon ( " app-profiler " sv ) ) ;
2020-07-01 15:49:51 -03:00
2022-12-04 15:02:33 -03:00
DeprecatedString perfcore_file ;
2023-02-28 17:41:43 -03:00
if ( perfcore_file_arg . is_empty ( ) ) {
2020-07-01 16:07:53 -03:00
if ( ! generate_profile ( pid ) )
2020-07-01 15:49:51 -03:00
return 0 ;
2022-12-04 15:02:33 -03:00
perfcore_file = DeprecatedString : : formatted ( " /proc/{}/perf_events " , pid ) ;
2020-07-01 15:49:51 -03:00
} else {
2021-03-04 19:07:38 -03:00
perfcore_file = perfcore_file_arg ;
2019-12-12 18:01:06 -03:00
}
2021-03-04 19:07:38 -03:00
auto profile_or_error = Profile : : load_from_perfcore_file ( perfcore_file ) ;
2020-12-26 06:27:57 -03:00
if ( profile_or_error . is_error ( ) ) {
2022-12-04 15:02:33 -03:00
GUI : : MessageBox : : show ( nullptr , DeprecatedString : : formatted ( " {} " , profile_or_error . error ( ) ) , " Profiler " sv , GUI : : MessageBox : : Type : : Error ) ;
2020-12-26 06:27:57 -03:00
return 0 ;
2019-12-12 18:01:06 -03:00
}
2020-12-26 06:27:57 -03:00
auto & profile = profile_or_error . value ( ) ;
2023-09-16 08:51:33 -03:00
auto window = GUI : : Window : : construct ( ) ;
2021-02-24 12:40:36 -03:00
2023-04-08 06:48:22 -03:00
TRY ( Desktop : : Launcher : : add_allowed_handler_with_only_specific_urls ( " /bin/Help " , { URL : : create_with_file_scheme ( " /usr/share/man/man1/Applications/Profiler.md " ) } ) ) ;
2021-11-23 20:23:00 -03:00
TRY ( Desktop : : Launcher : : seal_allowlist ( ) ) ;
2021-02-24 12:40:36 -03:00
2020-07-01 14:43:17 -03:00
window - > set_title ( " Profiler " ) ;
2020-07-28 07:09:19 -03:00
window - > set_icon ( app_icon . bitmap_for_size ( 16 ) ) ;
2023-09-21 13:05:11 -03:00
window - > restore_size_and_position ( " Profiler " sv , " Window " sv , { { 800 , 600 } } ) ;
window - > save_size_and_position_on_close ( " Profiler " sv , " Window " sv ) ;
2019-12-12 18:01:06 -03:00
2023-09-18 21:13:48 -03:00
auto main_widget = window - > set_main_widget < GUI : : Widget > ( ) ;
2021-12-18 08:28:27 -03:00
main_widget - > set_fill_with_background_color ( true ) ;
main_widget - > set_layout < GUI : : VerticalBoxLayout > ( ) ;
2019-12-14 14:44:29 -03:00
2023-09-18 02:00:51 -03:00
auto timeline_header_container = GUI : : Widget : : construct ( ) ;
2021-05-06 14:50:18 -03:00
timeline_header_container - > set_layout < GUI : : VerticalBoxLayout > ( ) ;
timeline_header_container - > set_fill_with_background_color ( true ) ;
timeline_header_container - > set_shrink_to_fit ( true ) ;
2021-11-23 20:09:41 -03:00
auto timeline_view = TRY ( TimelineView : : try_create ( * profile ) ) ;
2021-12-22 12:37:50 -03:00
for ( auto const & process : profile - > processes ( ) ) {
2021-05-07 06:11:45 -03:00
bool matching_event_found = false ;
2021-12-22 12:37:50 -03:00
for ( auto const & event : profile - > events ( ) ) {
2021-06-02 16:16:57 -03:00
if ( event . pid = = process . pid & & process . valid_at ( event . serial ) ) {
2021-05-07 06:11:45 -03:00
matching_event_found = true ;
break ;
}
2021-05-06 13:44:31 -03:00
}
2021-05-07 06:11:45 -03:00
if ( ! matching_event_found )
2021-05-06 13:44:31 -03:00
continue ;
2023-09-22 18:28:59 -03:00
auto & timeline_header = timeline_header_container - > add < TimelineHeader > ( * profile , process ) ;
timeline_header . set_shrink_to_fit ( true ) ;
timeline_header . on_selection_change = [ & ] ( bool selected ) {
2021-06-02 16:16:57 -03:00
auto end_valid = process . end_valid = = EventSerialNumber { } ? EventSerialNumber : : max_valid_serial ( ) : process . end_valid ;
2021-05-07 20:33:43 -03:00
if ( selected )
profile - > add_process_filter ( process . pid , process . start_valid , end_valid ) ;
else
profile - > remove_process_filter ( process . pid , process . start_valid , end_valid ) ;
2021-05-07 08:47:12 -03:00
timeline_header_container - > for_each_child_widget ( [ ] ( auto & other_timeline_header ) {
static_cast < TimelineHeader & > ( other_timeline_header ) . update_selection ( ) ;
return IterationDecision : : Continue ;
} ) ;
} ;
2021-12-18 08:28:27 -03:00
2023-09-22 18:28:59 -03:00
timeline_view - > add < TimelineTrack > ( * timeline_view , * profile , process ) ;
2021-05-06 13:44:31 -03:00
}
2023-09-22 18:28:59 -03:00
auto & main_splitter = main_widget - > add < GUI : : VerticalSplitter > ( ) ;
2021-05-06 13:44:31 -03:00
2023-09-22 18:28:59 -03:00
[[maybe_unused]] auto & timeline_container = main_splitter . add < TimelineContainer > ( * timeline_header_container , * timeline_view ) ;
2021-05-07 20:10:48 -03:00
2023-09-22 18:28:59 -03:00
auto & tab_widget = main_splitter . add < GUI : : TabWidget > ( ) ;
2021-02-27 14:33:30 -03:00
2023-09-22 18:28:59 -03:00
auto & tree_tab = tab_widget . add_tab < GUI : : Widget > ( " Call Tree " _string ) ;
2023-09-14 04:01:53 -03:00
tree_tab . set_layout < GUI : : VerticalBoxLayout > ( 4 ) ;
2023-09-22 18:28:59 -03:00
auto & bottom_splitter = tree_tab . add < GUI : : VerticalSplitter > ( ) ;
2020-04-11 13:46:11 -03:00
2023-09-22 18:28:59 -03:00
auto & tree_view = bottom_splitter . add < GUI : : TreeView > ( ) ;
tree_view . set_should_fill_selected_rows ( true ) ;
tree_view . set_column_headers_visible ( true ) ;
tree_view . set_selection_behavior ( GUI : : TreeView : : SelectionBehavior : : SelectRows ) ;
tree_view . set_model ( profile - > model ( ) ) ;
2019-12-12 18:01:06 -03:00
2023-09-22 18:28:59 -03:00
auto & disassembly_view = bottom_splitter . add < GUI : : TableView > ( ) ;
disassembly_view . set_visible ( false ) ;
2020-04-11 13:46:11 -03:00
2021-07-29 06:27:13 -03:00
auto update_disassembly_model = [ & ] {
2023-09-22 18:28:59 -03:00
if ( disassembly_view . is_visible ( ) & & ! tree_view . selection ( ) . is_empty ( ) ) {
profile - > set_disassembly_index ( tree_view . selection ( ) . first ( ) ) ;
disassembly_view . set_model ( profile - > disassembly_model ( ) ) ;
2021-07-29 06:27:13 -03:00
} else {
2023-09-22 18:28:59 -03:00
disassembly_view . set_model ( nullptr ) ;
2021-07-29 06:27:13 -03:00
}
} ;
2023-09-22 18:28:59 -03:00
auto & source_view = bottom_splitter . add < GUI : : TableView > ( ) ;
source_view . set_visible ( false ) ;
2021-12-26 21:25:58 -03:00
auto update_source_model = [ & ] {
2023-09-22 18:28:59 -03:00
if ( source_view . is_visible ( ) & & ! tree_view . selection ( ) . is_empty ( ) ) {
profile - > set_source_index ( tree_view . selection ( ) . first ( ) ) ;
source_view . set_model ( profile - > source_model ( ) ) ;
2021-12-26 21:25:58 -03:00
} else {
2023-09-22 18:28:59 -03:00
source_view . set_model ( nullptr ) ;
2021-12-26 21:25:58 -03:00
}
} ;
2023-09-22 18:28:59 -03:00
tree_view . on_selection_change = [ & ] {
2021-07-29 06:27:13 -03:00
update_disassembly_model ( ) ;
2021-12-26 21:25:58 -03:00
update_source_model ( ) ;
2020-04-11 13:46:11 -03:00
} ;
2023-01-20 16:06:05 -03:00
auto disassembly_action = GUI : : Action : : create_checkable ( " Show &Disassembly " , { Mod_Ctrl , Key_D } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/x86.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( auto & action ) {
2023-09-22 18:28:59 -03:00
disassembly_view . set_visible ( action . is_checked ( ) ) ;
2021-07-29 06:27:13 -03:00
update_disassembly_model ( ) ;
2021-05-22 17:48:48 -03:00
} ) ;
2023-01-20 16:06:05 -03:00
auto source_action = GUI : : Action : : create_checkable ( " Show &Source " , { Mod_Ctrl , Key_S } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/x86.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( auto & action ) {
2023-09-22 18:28:59 -03:00
source_view . set_visible ( action . is_checked ( ) ) ;
2021-12-26 21:25:58 -03:00
update_source_model ( ) ;
} ) ;
2023-09-22 18:28:59 -03:00
auto & samples_tab = tab_widget . add_tab < GUI : : Widget > ( " Samples " _string ) ;
2023-09-14 04:01:53 -03:00
samples_tab . set_layout < GUI : : VerticalBoxLayout > ( 4 ) ;
2021-02-27 14:33:30 -03:00
2023-09-22 18:28:59 -03:00
auto & samples_splitter = samples_tab . add < GUI : : HorizontalSplitter > ( ) ;
auto & samples_table_view = samples_splitter . add < GUI : : TableView > ( ) ;
samples_table_view . set_model ( profile - > samples_model ( ) ) ;
2021-02-27 14:33:30 -03:00
2023-09-22 18:28:59 -03:00
auto & individual_sample_view = samples_splitter . add < GUI : : TableView > ( ) ;
samples_table_view . on_selection_change = [ & ] {
auto const & index = samples_table_view . selection ( ) . first ( ) ;
2021-02-27 14:33:30 -03:00
auto model = IndividualSampleModel : : create ( * profile , index . data ( GUI : : ModelRole : : Custom ) . to_integer < size_t > ( ) ) ;
2023-09-22 18:28:59 -03:00
individual_sample_view . set_model ( move ( model ) ) ;
2021-02-27 14:33:30 -03:00
} ;
2023-09-22 18:28:59 -03:00
auto & signposts_tab = tab_widget . add_tab < GUI : : Widget > ( " Signposts " _string ) ;
2023-09-14 04:01:53 -03:00
signposts_tab . set_layout < GUI : : VerticalBoxLayout > ( 4 ) ;
2021-08-13 19:41:38 -03:00
2023-09-22 18:28:59 -03:00
auto & signposts_splitter = signposts_tab . add < GUI : : HorizontalSplitter > ( ) ;
auto & signposts_table_view = signposts_splitter . add < GUI : : TableView > ( ) ;
signposts_table_view . set_model ( profile - > signposts_model ( ) ) ;
2021-08-13 19:41:38 -03:00
2023-09-22 18:28:59 -03:00
auto & individual_signpost_view = signposts_splitter . add < GUI : : TableView > ( ) ;
signposts_table_view . on_selection_change = [ & ] {
auto const & index = signposts_table_view . selection ( ) . first ( ) ;
2021-08-13 19:41:38 -03:00
auto model = IndividualSampleModel : : create ( * profile , index . data ( GUI : : ModelRole : : Custom ) . to_integer < size_t > ( ) ) ;
2023-09-22 18:28:59 -03:00
individual_signpost_view . set_model ( move ( model ) ) ;
2021-08-13 19:41:38 -03:00
} ;
2023-09-22 18:28:59 -03:00
auto & flamegraph_tab = tab_widget . add_tab < GUI : : Widget > ( " Flame Graph " _string ) ;
2023-09-14 04:01:53 -03:00
flamegraph_tab . set_layout < GUI : : VerticalBoxLayout > ( GUI : : Margins { 4 , 4 , 4 , 4 } ) ;
2021-08-16 16:20:24 -03:00
2023-09-22 18:28:59 -03:00
auto & flamegraph_view = flamegraph_tab . add < FlameGraphView > ( profile - > model ( ) , ProfileModel : : Column : : StackFrame , ProfileModel : : Column : : SampleCount ) ;
2021-08-16 16:20:24 -03:00
2021-12-28 09:35:08 -03:00
u64 const start_of_trace = profile - > first_timestamp ( ) ;
u64 const end_of_trace = start_of_trace + profile - > length_in_ms ( ) ;
auto const clamp_timestamp = [ start_of_trace , end_of_trace ] ( u64 timestamp ) - > u64 {
2021-05-06 14:12:58 -03:00
return min ( end_of_trace , max ( timestamp , start_of_trace ) ) ;
} ;
2022-04-26 17:59:03 -03:00
// FIXME: Make this constexpr once String is able to.
2023-02-02 09:00:40 -03:00
auto const format_sample_count = [ & profile ] ( auto const sample_count ) {
2022-04-26 16:39:50 -03:00
if ( profile - > show_percentages ( ) )
2023-02-02 09:00:40 -03:00
return DeprecatedString : : formatted ( " {}% " , sample_count . as_string ( ) ) ;
2022-12-04 15:02:33 -03:00
return DeprecatedString : : formatted ( " {} Samples " , sample_count . to_i32 ( ) ) ;
2022-04-26 16:39:50 -03:00
} ;
2023-09-22 18:28:59 -03:00
auto & statusbar = main_widget - > add < GUI : : Statusbar > ( ) ;
2021-08-16 16:20:24 -03:00
auto statusbar_update = [ & ] {
2021-05-06 14:12:58 -03:00
auto & view = * timeline_view ;
StringBuilder builder ;
2021-08-16 16:20:24 -03:00
2023-09-22 18:28:59 -03:00
auto flamegraph_hovered_index = flamegraph_view . hovered_index ( ) ;
2021-08-16 16:20:24 -03:00
if ( flamegraph_hovered_index . is_valid ( ) ) {
2022-12-05 22:12:49 -03:00
auto stack = profile - > model ( ) . data ( flamegraph_hovered_index . sibling_at_column ( ProfileModel : : Column : : StackFrame ) ) . to_deprecated_string ( ) ;
2022-04-26 16:39:50 -03:00
auto sample_count = profile - > model ( ) . data ( flamegraph_hovered_index . sibling_at_column ( ProfileModel : : Column : : SampleCount ) ) ;
auto self_count = profile - > model ( ) . data ( flamegraph_hovered_index . sibling_at_column ( ProfileModel : : Column : : SelfCount ) ) ;
2021-08-16 16:20:24 -03:00
builder . appendff ( " {}, " , stack ) ;
2022-04-26 16:39:50 -03:00
builder . appendff ( " Samples: {}, " , format_sample_count ( sample_count ) ) ;
builder . appendff ( " Self: {} " , format_sample_count ( self_count ) ) ;
2021-08-16 16:20:24 -03:00
} else {
u64 normalized_start_time = clamp_timestamp ( min ( view . select_start_time ( ) , view . select_end_time ( ) ) ) ;
u64 normalized_end_time = clamp_timestamp ( max ( view . select_start_time ( ) , view . select_end_time ( ) ) ) ;
u64 normalized_hover_time = clamp_timestamp ( view . hover_time ( ) ) ;
builder . appendff ( " Time: {} ms " , normalized_hover_time - start_of_trace ) ;
if ( normalized_start_time ! = normalized_end_time ) {
auto start = normalized_start_time - start_of_trace ;
auto end = normalized_end_time - start_of_trace ;
builder . appendff ( " , Selection: {} - {} ms " , start , end ) ;
builder . appendff ( " , Duration: {} ms " , end - start ) ;
}
2021-05-06 14:12:58 -03:00
}
2023-09-22 18:28:59 -03:00
statusbar . set_text ( builder . to_string ( ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2021-05-06 14:12:58 -03:00
} ;
2021-08-16 16:20:24 -03:00
timeline_view - > on_selection_change = [ & ] { statusbar_update ( ) ; } ;
2023-09-22 18:28:59 -03:00
flamegraph_view . on_hover_change = [ & ] { statusbar_update ( ) ; } ;
2021-05-06 14:12:58 -03:00
2023-09-22 18:28:59 -03:00
auto & filesystem_events_tab = tab_widget . add_tab < GUI : : Widget > ( " Filesystem events " _string ) ;
2023-09-14 04:01:53 -03:00
filesystem_events_tab . set_layout < GUI : : VerticalBoxLayout > ( 4 ) ;
2022-02-17 18:26:20 -03:00
2023-09-22 18:28:59 -03:00
auto & filesystem_events_tree_view = filesystem_events_tab . add < GUI : : TreeView > ( ) ;
filesystem_events_tree_view . set_should_fill_selected_rows ( true ) ;
filesystem_events_tree_view . set_column_headers_visible ( true ) ;
filesystem_events_tree_view . set_selection_behavior ( GUI : : TreeView : : SelectionBehavior : : SelectRows ) ;
filesystem_events_tree_view . set_model ( profile - > file_event_model ( ) ) ;
filesystem_events_tree_view . set_column_visible ( FileEventModel : : Column : : OpenDuration , false ) ;
filesystem_events_tree_view . set_column_visible ( FileEventModel : : Column : : CloseDuration , false ) ;
filesystem_events_tree_view . set_column_visible ( FileEventModel : : Column : : ReadvDuration , false ) ;
filesystem_events_tree_view . set_column_visible ( FileEventModel : : Column : : ReadDuration , false ) ;
filesystem_events_tree_view . set_column_visible ( FileEventModel : : Column : : PreadDuration , false ) ;
2022-02-17 18:26:20 -03:00
2023-08-14 05:44:42 -03:00
auto file_menu = window - > add_menu ( " &File " _string ) ;
2023-08-14 05:14:27 -03:00
file_menu - > add_action ( GUI : : CommonActions : : make_quit_action ( [ & ] ( auto & ) { app - > quit ( ) ; } ) ) ;
2019-12-15 18:55:11 -03:00
2023-08-14 05:44:42 -03:00
auto view_menu = window - > add_menu ( " &View " _string ) ;
2020-10-19 10:04:54 -03:00
2021-04-10 10:55:34 -03:00
auto invert_action = GUI : : Action : : create_checkable ( " &Invert Tree " , { Mod_Ctrl , Key_I } , [ & ] ( auto & action ) {
2019-12-16 14:21:05 -03:00
profile - > set_inverted ( action . is_checked ( ) ) ;
} ) ;
2019-12-17 01:46:39 -03:00
invert_action - > set_checked ( false ) ;
2023-08-14 05:14:27 -03:00
view_menu - > add_action ( invert_action ) ;
2019-12-16 14:21:05 -03:00
2021-04-10 10:55:34 -03:00
auto top_functions_action = GUI : : Action : : create_checkable ( " &Top Functions " , { Mod_Ctrl , Key_T } , [ & ] ( auto & action ) {
2020-10-19 10:04:54 -03:00
profile - > set_show_top_functions ( action . is_checked ( ) ) ;
} ) ;
top_functions_action - > set_checked ( false ) ;
2023-08-14 05:14:27 -03:00
view_menu - > add_action ( top_functions_action ) ;
2020-10-19 10:04:54 -03:00
2021-04-10 10:55:34 -03:00
auto percent_action = GUI : : Action : : create_checkable ( " Show &Percentages " , { Mod_Ctrl , Key_P } , [ & ] ( auto & action ) {
2020-03-02 17:56:03 -03:00
profile - > set_show_percentages ( action . is_checked ( ) ) ;
2023-09-22 18:28:59 -03:00
tree_view . update ( ) ;
disassembly_view . update ( ) ;
source_view . update ( ) ;
2020-03-02 17:56:03 -03:00
} ) ;
percent_action - > set_checked ( false ) ;
2023-08-14 05:14:27 -03:00
view_menu - > add_action ( percent_action ) ;
2019-12-16 14:21:05 -03:00
2023-08-14 05:14:27 -03:00
view_menu - > add_action ( disassembly_action ) ;
view_menu - > add_action ( source_action ) ;
2021-05-22 17:48:48 -03:00
2023-08-14 05:44:42 -03:00
auto help_menu = window - > add_menu ( " &Help " _string ) ;
2023-08-14 05:14:27 -03:00
help_menu - > add_action ( GUI : : CommonActions : : make_command_palette_action ( window ) ) ;
help_menu - > add_action ( GUI : : CommonActions : : make_help_action ( [ ] ( auto & ) {
2023-04-08 06:48:22 -03:00
Desktop : : Launcher : : open ( URL : : create_with_file_scheme ( " /usr/share/man/man1/Applications/Profiler.md " ) , " /bin/Help " ) ;
2023-08-14 05:14:27 -03:00
} ) ) ;
2023-09-13 16:34:46 -03:00
help_menu - > add_action ( GUI : : CommonActions : : make_about_action ( " Profiler " _string , app_icon , window ) ) ;
2020-07-28 07:09:19 -03:00
2019-12-12 18:01:06 -03:00
window - > show ( ) ;
2020-07-04 09:05:19 -03:00
return app - > exec ( ) ;
2019-12-12 18:01:06 -03:00
}
2020-07-01 15:49:51 -03:00
2022-12-04 15:02:33 -03:00
static bool prompt_to_stop_profiling ( pid_t pid , DeprecatedString const & process_name )
2020-07-01 15:49:51 -03:00
{
auto window = GUI : : Window : : construct ( ) ;
2022-12-04 15:02:33 -03:00
window - > set_title ( DeprecatedString : : formatted ( " Profiling {}({}) " , process_name , pid ) ) ;
2020-12-26 08:58:50 -03:00
window - > resize ( 240 , 100 ) ;
2023-01-20 16:06:05 -03:00
window - > set_icon ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-profiler.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2020-08-15 11:32:11 -03:00
window - > center_on_screen ( ) ;
2020-12-26 08:58:50 -03:00
2023-09-18 21:13:48 -03:00
auto widget = window - > set_main_widget < GUI : : Widget > ( ) ;
2023-01-06 13:48:37 -03:00
widget - > set_fill_with_background_color ( true ) ;
2023-02-16 18:07:06 -03:00
widget - > set_layout < GUI : : VerticalBoxLayout > ( GUI : : Margins { 0 , 0 , 16 } ) ;
2020-07-01 15:49:51 -03:00
2023-08-07 23:26:17 -03:00
auto & timer_label = widget - > add < GUI : : Label > ( " ... " _string ) ;
2020-07-01 15:49:51 -03:00
Core : : ElapsedTimer clock ;
clock . start ( ) ;
2023-01-11 17:00:46 -03:00
auto update_timer = Core : : Timer : : create_repeating ( 100 , [ & ] {
2023-04-29 11:41:48 -03:00
timer_label . set_text ( String : : formatted ( " {:.1} seconds " , static_cast < float > ( clock . elapsed ( ) ) / 1000.0f ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2023-01-11 17:00:46 -03:00
} ) . release_value_but_fixme_should_propagate_errors ( ) ;
2023-01-11 16:36:46 -03:00
update_timer - > start ( ) ;
2020-07-01 15:49:51 -03:00
2023-08-07 23:26:17 -03:00
auto & stop_button = widget - > add < GUI : : Button > ( " Stop " _string ) ;
2020-12-29 21:23:32 -03:00
stop_button . set_fixed_size ( 140 , 22 ) ;
2020-07-01 15:49:51 -03:00
stop_button . on_click = [ & ] ( auto ) {
2020-07-04 11:52:01 -03:00
GUI : : Application : : the ( ) - > quit ( ) ;
2020-07-01 15:49:51 -03:00
} ;
window - > show ( ) ;
2020-07-04 11:52:01 -03:00
return GUI : : Application : : the ( ) - > exec ( ) = = 0 ;
2020-07-01 15:49:51 -03:00
}
2021-01-11 05:52:18 -03:00
bool generate_profile ( pid_t & pid )
2020-07-01 15:49:51 -03:00
{
2020-07-01 16:07:53 -03:00
if ( ! pid ) {
2023-08-07 23:26:17 -03:00
auto process_chooser = GUI : : ProcessChooser : : construct ( " Profiler " sv , " Profile " _string , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-profiler.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2022-05-13 09:10:27 -03:00
if ( process_chooser - > exec ( ) = = GUI : : Dialog : : ExecResult : : Cancel )
2020-07-01 16:07:53 -03:00
return false ;
2020-07-02 07:12:16 -03:00
pid = process_chooser - > pid ( ) ;
2020-07-01 16:07:53 -03:00
}
2022-12-04 15:02:33 -03:00
DeprecatedString process_name ;
2020-12-26 08:58:50 -03:00
auto all_processes = Core : : ProcessStatisticsReader : : get_all ( ) ;
2022-12-08 10:50:31 -03:00
if ( ! all_processes . is_error ( ) ) {
auto & processes = all_processes . value ( ) . processes ;
2021-07-14 15:05:59 -03:00
if ( auto it = processes . find_if ( [ & ] ( auto & entry ) { return entry . pid = = pid ; } ) ; it ! = processes . end ( ) )
2021-05-23 06:08:32 -03:00
process_name = it - > name ;
2021-01-02 00:58:47 -03:00
else
process_name = " (unknown) " ;
} else {
2020-12-26 08:58:50 -03:00
process_name = " (unknown) " ;
2021-01-02 00:58:47 -03:00
}
2020-12-26 08:58:50 -03:00
2021-05-27 20:00:42 -03:00
static constexpr u64 event_mask = PERF_EVENT_SAMPLE | PERF_EVENT_MMAP | PERF_EVENT_MUNMAP | PERF_EVENT_PROCESS_CREATE
| PERF_EVENT_PROCESS_EXEC | PERF_EVENT_PROCESS_EXIT | PERF_EVENT_THREAD_CREATE | PERF_EVENT_THREAD_EXIT ;
if ( profiling_enable ( pid , event_mask ) < 0 ) {
2020-07-01 16:07:53 -03:00
int saved_errno = errno ;
2022-12-04 15:02:33 -03:00
GUI : : MessageBox : : show ( nullptr , DeprecatedString : : formatted ( " Unable to profile process {}({}): {} " , process_name , pid , strerror ( saved_errno ) ) , " Profiler " sv , GUI : : MessageBox : : Type : : Error ) ;
2020-07-01 15:49:51 -03:00
return false ;
2020-07-01 16:07:53 -03:00
}
2020-07-01 15:49:51 -03:00
2020-12-26 08:58:50 -03:00
if ( ! prompt_to_stop_profiling ( pid , process_name ) )
2020-07-01 15:49:51 -03:00
return false ;
2020-11-14 07:13:59 -03:00
if ( profiling_disable ( pid ) < 0 ) {
return false ;
}
2020-07-01 15:49:51 -03:00
return true ;
}