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"
2021-08-05 15:12:31 -03:00
# include <AK/FileStream.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>
2021-05-09 01:50:57 -03:00
# include <LibCore/File.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 } ;
2021-08-05 15:12:31 -03:00
String profile_dump_path ;
FILE * profile_output_file { nullptr } ;
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
2021-05-17 09:45:57 -03:00
String executable_path ;
if ( arguments [ 0 ] . contains ( " / " sv ) )
2021-05-17 09:41:59 -03:00
executable_path = Core : : File : : real_path_for ( arguments [ 0 ] ) ;
2021-05-17 09:45:57 -03:00
else
2022-08-20 13:31:03 -03:00
executable_path = Core : : File : : resolve_executable_from_environment ( arguments [ 0 ] ) . value_or ( { } ) ;
2021-05-17 09:45:57 -03:00
if ( executable_path . is_empty ( ) ) {
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
}
2020-07-07 16:13:04 -03:00
2021-08-08 07:58:07 -03:00
if ( dump_profile & & profile_dump_path . is_empty ( ) )
2021-09-22 11:22:14 -03:00
profile_dump_path = String : : formatted ( " {}.{}.profile " , LexicalPath ( executable_path ) . basename ( ) , getpid ( ) ) ;
2021-08-05 15:12:31 -03:00
2021-08-08 07:58:07 -03:00
OwnPtr < OutputFileStream > profile_stream ;
2021-08-20 12:09:23 -03:00
OwnPtr < NonnullOwnPtrVector < String > > profile_strings ;
OwnPtr < Vector < int > > profile_string_id_map ;
2021-08-08 07:58:07 -03:00
if ( dump_profile ) {
2021-08-05 15:12:31 -03:00
profile_output_file = fopen ( profile_dump_path . characters ( ) , " w+ " ) ;
if ( profile_output_file = = nullptr ) {
2021-12-22 12:20:22 -03:00
char const * error_string = strerror ( errno ) ;
2021-08-05 15:12:31 -03:00
warnln ( " Failed to open '{}' for writing: {} " , profile_dump_path , error_string ) ;
return 1 ;
}
2021-08-08 07:58:07 -03:00
profile_stream = make < OutputFileStream > ( profile_output_file ) ;
2021-08-20 12:09:23 -03:00
profile_strings = make < NonnullOwnPtrVector < String > > ( ) ;
profile_string_id_map = make < Vector < int > > ( ) ;
2021-08-05 15:12:31 -03:00
2021-08-08 07:58:07 -03:00
profile_stream - > write_or_error ( R " ({ " events " :[) " sv . bytes ( ) ) ;
2021-08-05 15:12:31 -03:00
timeval tv { } ;
gettimeofday ( & tv , nullptr ) ;
2021-08-08 07:58:07 -03:00
profile_stream - > write_or_error (
2021-08-05 15:12:31 -03:00
String : : 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 ( ) ) ;
}
2020-07-27 10:57:12 -03:00
Vector < String > environment ;
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 ] ) ) ;
2020-07-27 13:56:19 -03:00
if ( set_process_name ( builder . string_view ( ) . characters_without_null_termination ( ) , builder . string_view ( ) . length ( ) ) < 0 ) {
perror ( " set_process_name " ) ;
return 1 ;
}
int rc = pthread_setname_np ( pthread_self ( ) , builder . to_string ( ) . characters ( ) ) ;
if ( rc ! = 0 ) {
2022-07-11 14:32:29 -03:00
reportln ( " pthread_setname_np: {} " sv , strerror ( rc ) ) ;
2020-07-27 13:56:19 -03:00
return 1 ;
}
2021-05-13 14:33:58 -03:00
if ( pause_on_startup )
emulator . pause ( ) ;
2021-08-05 15:12:31 -03:00
rc = emulator . exec ( ) ;
2021-08-20 12:09:23 -03:00
if ( dump_profile ) {
2022-02-21 18:55:53 -03:00
emulator . profile_stream ( ) . write_or_error ( " ], \" strings \" : [ " sv . bytes ( ) ) ;
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 )
emulator . profile_stream ( ) . write_or_error ( String : : formatted ( " \" {} \" , " , emulator . profiler_strings ( ) . at ( i ) ) . bytes ( ) ) ;
emulator . profile_stream ( ) . write_or_error ( String : : formatted ( " \" {} \" " , emulator . profiler_strings ( ) . last ( ) ) . bytes ( ) ) ;
}
emulator . profile_stream ( ) . write_or_error ( " ]} " sv . bytes ( ) ) ;
}
2021-08-05 15:12:31 -03:00
return rc ;
2020-07-07 16:13:04 -03:00
}