2020-07-07 16:13:04 -03:00
/*
* Copyright ( c ) 2020 , Andreas Kling < kling @ serenityos . org >
*
2021-04-22 05:24:48 -03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-07-07 16:13:04 -03:00
*/
# include "Emulator.h"
2020-10-13 13:34:27 -03:00
# include <AK/Format.h>
2020-07-27 13:56:19 -03:00
# include <AK/LexicalPath.h>
# include <AK/StringBuilder.h>
2020-09-24 15:07:07 -03:00
# include <LibCore/ArgsParser.h>
2020-09-16 14:19:30 -03:00
# include <LibCore/DirIterator.h>
2023-01-22 17:26:34 -03:00
# include <LibCore/Process.h>
2023-05-29 16:52:50 -03:00
# include <LibFileSystem/FileSystem.h>
2021-08-05 15:12:31 -03:00
# include <fcntl.h>
2020-07-27 13:56:19 -03:00
# include <pthread.h>
2021-03-12 13:29:37 -03:00
# include <serenity.h>
2020-07-27 13:56:19 -03:00
# include <string.h>
2020-07-07 16:13:04 -03:00
2020-10-24 04:45:51 -03:00
bool g_report_to_debug = false ;
2020-07-27 10:57:12 -03:00
int main ( int argc , char * * argv , char * * env )
2020-07-07 16:13:04 -03:00
{
2021-11-26 21:11:07 -03:00
Vector < StringView > arguments ;
2021-05-13 14:33:58 -03:00
bool pause_on_startup { false } ;
2022-12-04 15:02:33 -03:00
DeprecatedString profile_dump_path ;
2021-08-07 19:30:17 -03:00
bool enable_roi_mode { false } ;
2021-08-08 07:58:07 -03:00
bool dump_profile { false } ;
unsigned profile_instruction_interval { 0 } ;
2020-09-24 15:07:07 -03:00
Core : : ArgsParser parser ;
2021-06-07 16:51:08 -03:00
parser . set_stop_on_first_non_option ( true ) ;
2020-10-24 04:45:51 -03:00
parser . add_option ( g_report_to_debug , " Write reports to the debug log " , " report-to-debug " , 0 ) ;
2021-05-13 14:33:58 -03:00
parser . add_option ( pause_on_startup , " Pause on startup " , " pause " , ' p ' ) ;
2021-08-08 07:58:07 -03:00
parser . add_option ( dump_profile , " Generate a ProfileViewer-compatible profile " , " profile " , 0 ) ;
2021-11-01 14:02:35 -03:00
parser . add_option ( profile_instruction_interval , " Set the profile instruction capture interval, 128 by default " , " profile-interval " , ' i ' , " num_instructions " ) ;
2021-08-05 15:12:31 -03:00
parser . add_option ( profile_dump_path , " File path for profile dump " , " profile-file " , 0 , " path " ) ;
2021-08-07 19:30:17 -03:00
parser . add_option ( enable_roi_mode , " Enable Region-of-Interest mode for profiling " , " roi " , 0 ) ;
2021-05-13 14:33:58 -03:00
2021-05-17 09:41:59 -03:00
parser . add_positional_argument ( arguments , " Command to emulate " , " command " ) ;
2021-05-13 14:33:58 -03:00
2020-09-24 15:07:07 -03:00
parser . parse ( argc , argv ) ;
2020-07-07 16:13:04 -03:00
2021-08-08 07:58:07 -03:00
if ( dump_profile & & profile_instruction_interval = = 0 )
profile_instruction_interval = 128 ;
2021-08-07 19:30:17 -03:00
2023-05-29 16:52:50 -03:00
auto executable_path_or_error = arguments [ 0 ] . contains ( ' / ' )
? FileSystem : : real_path ( arguments [ 0 ] )
2023-05-29 17:20:05 -03:00
: Core : : System : : resolve_executable_from_environment ( arguments [ 0 ] ) ;
2023-05-29 16:52:50 -03:00
if ( executable_path_or_error . is_error ( ) ) {
2022-07-11 14:32:29 -03:00
reportln ( " Cannot find executable for '{}'. " sv , arguments [ 0 ] ) ;
2021-05-17 09:45:57 -03:00
return 1 ;
2021-01-23 11:12:24 -03:00
}
2023-05-29 16:52:50 -03:00
auto executable_path = executable_path_or_error . release_value ( ) . to_deprecated_string ( ) ;
2020-07-07 16:13:04 -03:00
2021-08-08 07:58:07 -03:00
if ( dump_profile & & profile_dump_path . is_empty ( ) )
2022-12-04 15:02:33 -03:00
profile_dump_path = DeprecatedString : : formatted ( " {}.{}.profile " , LexicalPath ( executable_path ) . basename ( ) , getpid ( ) ) ;
2021-08-05 15:12:31 -03:00
2023-02-09 21:00:18 -03:00
OwnPtr < Stream > profile_stream ;
2023-03-06 13:16:25 -03:00
OwnPtr < Vector < NonnullOwnPtr < DeprecatedString > > > profile_strings ;
2021-08-20 12:09:23 -03:00
OwnPtr < Vector < int > > profile_string_id_map ;
2021-08-08 07:58:07 -03:00
if ( dump_profile ) {
2023-02-08 23:02:46 -03:00
auto profile_stream_or_error = Core : : File : : open ( profile_dump_path , Core : : File : : OpenMode : : Write ) ;
2023-01-19 22:00:41 -03:00
if ( profile_stream_or_error . is_error ( ) ) {
warnln ( " Failed to open '{}' for writing: {} " , profile_dump_path , profile_stream_or_error . error ( ) ) ;
2021-08-05 15:12:31 -03:00
return 1 ;
}
2023-01-19 22:00:41 -03:00
profile_stream = profile_stream_or_error . release_value ( ) ;
2023-03-06 13:16:25 -03:00
profile_strings = make < Vector < NonnullOwnPtr < DeprecatedString > > > ( ) ;
2021-08-20 12:09:23 -03:00
profile_string_id_map = make < Vector < int > > ( ) ;
2021-08-05 15:12:31 -03:00
2023-03-01 11:37:45 -03:00
profile_stream - > write_until_depleted ( R " ({ " events " :[) " sv . bytes ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-08-05 15:12:31 -03:00
timeval tv { } ;
gettimeofday ( & tv , nullptr ) ;
2023-03-01 11:37:45 -03:00
profile_stream - > write_until_depleted (
2023-01-19 22:00:41 -03:00
DeprecatedString : : formatted (
R " ~({{ " type " : " process_create " , " parent_pid " : 1, " executable " : " { } " , " pid " : {}, " tid " : {}, " timestamp " : {}, " lost_samples " : 0, " stack " : []}})~ " ,
executable_path , getpid ( ) , gettid ( ) , tv . tv_sec * 1000 + tv . tv_usec / 1000 )
. bytes ( ) )
. release_value_but_fixme_should_propagate_errors ( ) ;
2021-08-05 15:12:31 -03:00
}
2022-12-04 15:02:33 -03:00
Vector < DeprecatedString > environment ;
2020-07-27 10:57:12 -03:00
for ( int i = 0 ; env [ i ] ; + + i ) {
environment . append ( env [ i ] ) ;
}
2020-11-14 19:39:44 -03:00
// FIXME: It might be nice to tear down the emulator properly.
2020-11-23 15:00:35 -03:00
auto & emulator = * new UserspaceEmulator : : Emulator ( executable_path , arguments , environment ) ;
2021-08-20 12:09:23 -03:00
emulator . set_profiling_details ( dump_profile , profile_instruction_interval , profile_stream , profile_strings , profile_string_id_map ) ;
2021-08-08 07:58:07 -03:00
emulator . set_in_region_of_interest ( ! enable_roi_mode ) ;
2020-07-11 12:18:07 -03:00
if ( ! emulator . load_elf ( ) )
2020-07-07 16:13:04 -03:00
return 1 ;
2020-07-27 13:56:19 -03:00
StringBuilder builder ;
2022-07-11 14:32:29 -03:00
builder . append ( " (UE) " sv ) ;
2021-06-29 11:46:16 -03:00
builder . append ( LexicalPath : : basename ( arguments [ 0 ] ) ) ;
2023-01-22 17:26:34 -03:00
if ( auto result = Core : : Process : : set_name ( builder . string_view ( ) , Core : : Process : : SetThreadName : : Yes ) ; result . is_error ( ) ) {
reportln ( " Core::Process::set_name: {} " sv , result . error ( ) ) ;
2020-07-27 13:56:19 -03:00
return 1 ;
}
2021-05-13 14:33:58 -03:00
if ( pause_on_startup )
emulator . pause ( ) ;
2023-01-22 17:26:34 -03:00
int rc = emulator . exec ( ) ;
2021-08-05 15:12:31 -03:00
2021-08-20 12:09:23 -03:00
if ( dump_profile ) {
2023-03-01 11:37:45 -03:00
emulator . profile_stream ( ) . write_until_depleted ( " ], \" strings \" : [ " sv . bytes ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-08-20 12:09:23 -03:00
if ( emulator . profiler_strings ( ) . size ( ) ) {
for ( size_t i = 0 ; i < emulator . profiler_strings ( ) . size ( ) - 1 ; + + i )
2023-03-01 11:37:45 -03:00
emulator . profile_stream ( ) . write_until_depleted ( DeprecatedString : : formatted ( " \" {} \" , " , emulator . profiler_strings ( ) . at ( i ) ) . bytes ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
emulator . profile_stream ( ) . write_until_depleted ( DeprecatedString : : formatted ( " \" {} \" " , emulator . profiler_strings ( ) . last ( ) ) . bytes ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-08-20 12:09:23 -03:00
}
2023-03-01 11:37:45 -03:00
emulator . profile_stream ( ) . write_until_depleted ( " ]} " sv . bytes ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-08-20 12:09:23 -03:00
}
2021-08-05 15:12:31 -03:00
return rc ;
2020-07-07 16:13:04 -03:00
}