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