#include "ds-rt-config.h" #ifdef ENABLE_PERFTRACING #if !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) #define DS_IMPL_SERVER_GETTER_SETTER #include "ds-server.h" #include "ds-ipc.h" #include "ds-protocol.h" #include "ds-process-protocol.h" #include "ds-eventpipe-protocol.h" #include "ds-dump-protocol.h" #include "ds-profiler-protocol.h" #include "ds-rt.h" /* * Globals and volatile access functions. */ static volatile uint32_t _server_shutting_down_state = 0; static ep_rt_wait_event_handle_t _server_resume_runtime_startup_event = { 0 }; static bool _server_disabled = false; static volatile bool _is_paused_for_startup = false; static inline bool server_volatile_load_shutting_down_state (void) { return (ep_rt_volatile_load_uint32_t (&_server_shutting_down_state) != 0) ? true : false; } static inline void server_volatile_store_shutting_down_state (bool state) { ep_rt_volatile_store_uint32_t (&_server_shutting_down_state, state ? 1 : 0); } /* * Forward declares of all static functions. */ static void server_error_callback_create ( const ep_char8_t *message, uint32_t code); static void server_error_callback_close ( const ep_char8_t *message, uint32_t code); static void server_warning_callback ( const ep_char8_t *message, uint32_t code); static bool server_protocol_helper_unknown_command ( DiagnosticsIpcMessage *message, DiagnosticsIpcStream *stream); /* * DiagnosticServer. */ static void server_error_callback_create ( const ep_char8_t *message, uint32_t code) { EP_ASSERT (message != NULL); DS_LOG_ERROR_2 ("Failed to create diagnostic IPC: error (%d): %s.", code, message); } static void server_error_callback_close ( const ep_char8_t *message, uint32_t code) { EP_ASSERT (message != NULL); DS_LOG_ERROR_2 ("Failed to close diagnostic IPC: error (%d): %s.", code, message); } static bool server_protocol_helper_unknown_command ( DiagnosticsIpcMessage *message, DiagnosticsIpcStream *stream) { DS_LOG_WARNING_1 ("Received unknown request type (%d)", ds_ipc_header_get_commandset (ds_ipc_message_get_header_ref (message))); ds_ipc_message_send_error (stream, DS_IPC_E_UNKNOWN_COMMAND); ds_ipc_stream_free (stream); return true; } static void server_warning_callback ( const ep_char8_t *message, uint32_t code) { EP_ASSERT (message != NULL); DS_LOG_WARNING_2 ("warning (%d): %s.", code, message); } EP_RT_DEFINE_THREAD_FUNC (server_thread) { EP_ASSERT (server_volatile_load_shutting_down_state () || ds_ipc_stream_factory_has_active_ports ()); if (!ds_ipc_stream_factory_has_active_ports ()) { #ifndef DS_IPC_DISABLE_LISTEN_PORTS DS_LOG_ERROR_0 ("Diagnostics IPC listener was undefined"); #endif return 1; } while (!server_volatile_load_shutting_down_state ()) { DiagnosticsIpcStream *stream = ds_ipc_stream_factory_get_next_available_stream (server_warning_callback); if (!stream) continue; ds_rt_auto_trace_signal (); DiagnosticsIpcMessage message; if (!ds_ipc_message_init (&message)) continue; if (!ds_ipc_message_initialize_stream (&message, stream)) { ds_ipc_message_send_error (stream, DS_IPC_E_BAD_ENCODING); ds_ipc_stream_free (stream); ds_ipc_message_fini (&message); continue; } if (ep_rt_utf8_string_compare ( (const ep_char8_t *)ds_ipc_header_get_magic_ref (ds_ipc_message_get_header_ref (&message)), (const ep_char8_t *)DOTNET_IPC_V1_MAGIC) != 0) { ds_ipc_message_send_error (stream, DS_IPC_E_UNKNOWN_MAGIC); ds_ipc_stream_free (stream); ds_ipc_message_fini (&message); continue; } DS_LOG_INFO_2 ("DiagnosticServer - received IPC message with command set (%d) and command id (%d)", ds_ipc_header_get_commandset (ds_ipc_message_get_header_ref (&message)), ds_ipc_header_get_commandid (ds_ipc_message_get_header_ref (&message))); switch ((DiagnosticsServerCommandSet)ds_ipc_header_get_commandset (ds_ipc_message_get_header_ref (&message))) { case DS_SERVER_COMMANDSET_EVENTPIPE: ds_eventpipe_protocol_helper_handle_ipc_message (&message, stream); break; case DS_SERVER_COMMANDSET_DUMP: ds_dump_protocol_helper_handle_ipc_message (&message, stream); break; case DS_SERVER_COMMANDSET_PROCESS: ds_process_protocol_helper_handle_ipc_message (&message, stream); break; case DS_SERVER_COMMANDSET_PROFILER: ds_profiler_protocol_helper_handle_ipc_message (&message, stream); break; default: server_protocol_helper_unknown_command (&message, stream); break; } ds_ipc_message_fini (&message); } return (ep_rt_thread_start_func_return_t)0; } void ds_server_disable (void) { _server_disabled = true; } bool ds_server_init (void) { if (!ds_ipc_stream_factory_init ()) return false; if (_server_disabled || !ds_rt_config_value_get_enable ()) return true; bool result = false; // Initialize PAL layer. if (!ds_ipc_pal_init ()) { DS_LOG_ERROR_1 ("Failed to initialize PAL layer (%d).", ep_rt_get_last_error ()); ep_raise_error (); } // Initialize the RuntimeIdentifier before use ds_ipc_advertise_cookie_v1_init (); // Ports can fail to be configured if (!ds_ipc_stream_factory_configure (server_error_callback_create)) DS_LOG_ERROR_0 ("At least one Diagnostic Port failed to be configured."); if (ds_ipc_stream_factory_any_suspended_ports ()) { ep_rt_wait_event_alloc (&_server_resume_runtime_startup_event, true, false); ep_raise_error_if_nok (ep_rt_wait_event_is_valid (&_server_resume_runtime_startup_event)); } if (ds_ipc_stream_factory_has_active_ports ()) { ds_rt_auto_trace_init (); ds_rt_auto_trace_launch (); ep_rt_thread_id_t thread_id = ep_rt_uint64_t_to_thread_id_t (0); if (!ep_rt_thread_create ((void *)server_thread, NULL, EP_THREAD_TYPE_SERVER, (void *)&thread_id)) { // Failed to create IPC thread. ds_ipc_stream_factory_close_ports (NULL); DS_LOG_ERROR_1 ("Failed to create diagnostic server thread (%d).", ep_rt_get_last_error ()); ep_raise_error (); } else { ds_rt_auto_trace_wait (); } } result = true; ep_on_exit: return result; ep_on_error: EP_ASSERT (!result); ep_exit_error_handler (); } bool ds_server_shutdown (void) { server_volatile_store_shutting_down_state (true); if (ds_ipc_stream_factory_has_active_ports ()) ds_ipc_stream_factory_shutdown (server_error_callback_close); ds_ipc_stream_factory_fini (); ds_ipc_pal_shutdown (); return true; } // This method will block runtime bring-up IFF DOTNET_DefaultDiagnosticPortSuspend != NULL and DOTNET_DiagnosticPorts != 0 (it's default state) // The _ds_resume_runtime_startup_event event will be signaled when the Diagnostics Monitor uses the ResumeRuntime Diagnostics IPC Command void ds_server_pause_for_diagnostics_monitor (void) { _is_paused_for_startup = true; if (ds_ipc_stream_factory_any_suspended_ports ()) { EP_ASSERT (ep_rt_wait_event_is_valid (&_server_resume_runtime_startup_event)); DS_LOG_ALWAYS_0 ("The runtime has been configured to pause during startup and is awaiting a Diagnostics IPC ResumeStartup command."); if (ep_rt_wait_event_wait (&_server_resume_runtime_startup_event, 5000, false) != 0) { ds_rt_server_log_pause_message (); DS_LOG_ALWAYS_0 ("The runtime has been configured to pause during startup and is awaiting a Diagnostics IPC ResumeStartup command and has waited 5 seconds."); ep_rt_wait_event_wait (&_server_resume_runtime_startup_event, EP_INFINITE_WAIT, false); } } // allow wait failures to fall through and the runtime to continue coming up } void ds_server_resume_runtime_startup (void) { ds_ipc_stream_factory_resume_current_port (); if (!ds_ipc_stream_factory_any_suspended_ports () && ep_rt_wait_event_is_valid (&_server_resume_runtime_startup_event)) { ep_rt_wait_event_set (&_server_resume_runtime_startup_event); _is_paused_for_startup = false; } } bool ds_server_is_paused_in_startup (void) { return _is_paused_for_startup; } #endif /* !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) */ #endif /* ENABLE_PERFTRACING */ #if !defined(ENABLE_PERFTRACING) || (defined(DS_INCLUDE_SOURCE_FILES) && !defined(DS_FORCE_INCLUDE_SOURCE_FILES)) extern const char quiet_linker_empty_file_warning_diagnostics_server; const char quiet_linker_empty_file_warning_diagnostics_server = 0; #endif