@ -6,6 +6,8 @@
# include <condition_variable>
# include <condition_variable>
# include <mutex>
# include <mutex>
# include <optional>
# include <stop_token>
# include <thread>
# include <thread>
# include "core/core.h"
# include "core/core.h"
@ -18,59 +20,53 @@ namespace Kernel::Svc {
constexpr auto MAX_MSG_TIME = std : : chrono : : milliseconds ( 250 ) ;
constexpr auto MAX_MSG_TIME = std : : chrono : : milliseconds ( 250 ) ;
const auto MAX_MSG_SIZE = 0x1000 ;
const auto MAX_MSG_SIZE = 0x1000 ;
static std : : string msg_buffer ;
static std : : mutex msg_mutex ;
static std : : condition_variable msg_cv ;
static std : : chrono : : steady_clock : : time_point last_msg_time ;
static bool worker_running = true ;
static std : : unique_ptr < std : : thread > flush_thread ;
static std : : once_flag start_flag ;
static void FlushDbgLoop ( ) {
while ( true ) {
std : : unique_lock lock ( msg_mutex ) ;
msg_cv . wait ( lock , [ ] { return ! msg_buffer . empty ( ) | | ! worker_running ; } ) ;
if ( ! worker_running & & msg_buffer . empty ( ) ) break ;
auto timeout = last_msg_time + MAX_MSG_TIME ;
bool woke_early = msg_cv . wait_until ( lock , timeout , [ ] {
return msg_buffer . size ( ) > = MAX_MSG_SIZE | | ! worker_running ;
} ) ;
if ( ! woke_early | | msg_buffer . size ( ) > = MAX_MSG_SIZE | | ! worker_running ) {
if ( ! msg_buffer . empty ( ) ) {
// Remove trailing newline as LOG_INFO adds that anyways
if ( msg_buffer . back ( ) = = ' \n ' )
msg_buffer . pop_back ( ) ;
LOG_INFO ( Debug_Emulated , " \n {} " , msg_buffer ) ;
msg_buffer . clear ( ) ;
}
if ( ! worker_running ) break ;
}
}
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
Result OutputDebugString ( Core : : System & system , u64 address , u64 len ) {
Result OutputDebugString ( Core : : System & system , u64 address , u64 len ) {
static struct DebugFlusher {
std : : string msg_buffer ;
std : : mutex msg_mutex ;
std : : condition_variable msg_cv ;
std : : chrono : : steady_clock : : time_point last_msg_time ;
std : : optional < std : : jthread > thread ;
} flusher_data ;
R_SUCCEED_IF ( len = = 0 ) ;
R_SUCCEED_IF ( len = = 0 ) ;
// Only start the thread the very first time this function is called
// Only start the thread the very first time this function is called
std : : call_once ( start_flag , [ ] {
flush_thread = std : : make_unique < std : : thread > ( FlushDbgLoop ) ;
} ) ;
if ( ! flusher_data . thread ) {
flusher_data . thread . emplace ( [ ] ( std : : stop_token stop_token ) {
while ( ! stop_token . stop_requested ( ) ) {
std : : unique_lock lock ( flusher_data . msg_mutex ) ;
flusher_data . msg_cv . wait ( lock , [ & stop_token ] {
return ! flusher_data . msg_buffer . empty ( ) | | stop_token . stop_requested ( ) ;
} ) ;
if ( stop_token . stop_requested ( ) & & flusher_data . msg_buffer . empty ( ) )
break ;
auto timeout = flusher_data . last_msg_time + MAX_MSG_TIME ;
bool woke_early = flusher_data . msg_cv . wait_until ( lock , timeout , [ & stop_token ] {
return flusher_data . msg_buffer . size ( ) > = MAX_MSG_SIZE | | stop_token . stop_requested ( ) ;
} ) ;
if ( ! woke_early | | flusher_data . msg_buffer . size ( ) > = MAX_MSG_SIZE | | stop_token . stop_requested ( ) ) {
if ( ! flusher_data . msg_buffer . empty ( ) ) {
// Remove trailing newline as LOG_INFO adds that anyways
if ( flusher_data . msg_buffer . back ( ) = = ' \n ' )
flusher_data . msg_buffer . pop_back ( ) ;
LOG_INFO ( Debug_Emulated , " \n {} " , flusher_data . msg_buffer ) ;
flusher_data . msg_buffer . clear ( ) ;
}
if ( stop_token . stop_requested ( ) ) break ;
}
}
flusher_data . msg_cv . notify_all ( ) ;
} ) ;
}
{
{
std : : lock_guard lock ( msg_mutex ) ;
const auto old_size = msg_buffer . size ( ) ;
msg_buffer . resize ( old_size + len ) ;
GetCurrentMemory ( system . Kernel ( ) ) . ReadBlock ( address , msg_buffer . data ( ) + old_size , len ) ;
last_msg_time = std : : chrono : : steady_clock : : now ( ) ;
std : : lock_guard lock ( flusher_data . msg_mutex ) ;
const auto old_size = flusher_data . msg_buffer . size ( ) ;
flusher_data . msg_buffer . resize ( old_size + len ) ;
GetCurrentMemory ( system . Kernel ( ) ) . ReadBlock ( address , flusher_data . msg_buffer . data ( ) + old_size , len ) ;
flusher_data . last_msg_time = std : : chrono : : steady_clock : : now ( ) ;
}
}
msg_cv . notify_one ( ) ;
flusher_data . msg_cv . notify_one ( ) ;
R_SUCCEED ( ) ;
R_SUCCEED ( ) ;
}
}
@ -82,16 +78,4 @@ Result OutputDebugString64From32(Core::System& system, uint32_t debug_str, uint3
R_RETURN ( OutputDebugString ( system , debug_str , len ) ) ;
R_RETURN ( OutputDebugString ( system , debug_str , len ) ) ;
}
}
struct BufferAutoFlush {
~ BufferAutoFlush ( ) {
{
std : : lock_guard lock ( msg_mutex ) ;
worker_running = false ;
}
msg_cv . notify_all ( ) ;
if ( flush_thread & & flush_thread - > joinable ( ) ) flush_thread - > join ( ) ;
}
} ;
static BufferAutoFlush auto_flusher ;
} // namespace Kernel::Svc
} // namespace Kernel::Svc