diff --git a/src/mono/mono/eventpipe/Makefile.am b/src/mono/mono/eventpipe/Makefile.am index 899cf5044e3ec4..2c28b8c8d1e4f2 100644 --- a/src/mono/mono/eventpipe/Makefile.am +++ b/src/mono/mono/eventpipe/Makefile.am @@ -8,8 +8,32 @@ AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/mono $(GLIB_CFLAGS) $(SHARED_CFLAG noinst_LTLIBRARIES = libeventpipe.la eventpipe_sources = \ - ep.c \ - ep.h \ + ds-dump-protocol.c \ + ds-dump-protocol.h \ + ds-eventpipe-protocol.c \ + ds-eventpipe-protocol.h \ + ds-getter-setter.h \ + ds-ipc.c \ + ds-ipc.h \ + ds-ipc-posix.c \ + ds-ipc-posix.h \ + ds-process-protocol.c \ + ds-process-protocol.h \ + ds-profiler-protocol.c \ + ds-profiler-protocol.h \ + ds-protocol.c \ + ds-protocol.h \ + ds-rt.h \ + ds-rt-config.h \ + ds-rt-mono.c \ + ds-rt-mono.h \ + ds-rt-types.h \ + ds-rt-types-mono.h \ + ds-server.c \ + ds-server.h \ + ds-types.h \ + ep.c \ + ep.h \ ep-block.c \ ep-block.h \ ep-buffer.c \ diff --git a/src/mono/mono/eventpipe/ds-dump-protocol.c b/src/mono/mono/eventpipe/ds-dump-protocol.c new file mode 100644 index 00000000000000..b71c3065c1f276 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-dump-protocol.c @@ -0,0 +1,32 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#if !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) + +#define DS_IMPL_DUMP_PROTOCOL_GETTER_SETTER +#include "ds-protocol.h" +#include "ds-dump-protocol.h" +#include "ds-rt.h" + +void +ds_dump_protocol_helper_handle_ipc_message ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (stream != NULL); + + // TODO: Implement. + DS_LOG_WARNING_0 ("Generate Core Dump not implemented\n"); + ds_ipc_message_send_error (stream, DS_IPC_E_NOTSUPPORTED); + ds_ipc_stream_free (stream); +} + +#endif /* !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef DS_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_diagnostics_dump_protocol; +const char quiet_linker_empty_file_warning_diagnostics_dump_protocol = 0; +#endif diff --git a/src/mono/mono/eventpipe/ds-dump-protocol.h b/src/mono/mono/eventpipe/ds-dump-protocol.h new file mode 100644 index 00000000000000..17b86a8b999c08 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-dump-protocol.h @@ -0,0 +1,27 @@ +#ifndef __DIAGNOSTICS_DUMP_PROTOCOL_H__ +#define __DIAGNOSTICS_DUMP_PROTOCOL_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#include "ds-types.h" +#include "ds-ipc.h" + +#undef DS_IMPL_GETTER_SETTER +#ifdef DS_IMPL_DUMP_PROTOCOL_GETTER_SETTER +#define DS_IMPL_GETTER_SETTER +#endif +#include "ds-getter-setter.h" + +/* + * DiagnosticsDumpProtocolHelper. + */ + +void +ds_dump_protocol_helper_handle_ipc_message ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_DUMP_PROTOCOL_H__ */ diff --git a/src/mono/mono/eventpipe/ds-eventpipe-protocol.c b/src/mono/mono/eventpipe/ds-eventpipe-protocol.c new file mode 100644 index 00000000000000..7dc8098b8ef224 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-eventpipe-protocol.c @@ -0,0 +1,528 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#if !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) + +#define DS_IMPL_EVENTPIPE_PROTOCOL_GETTER_SETTER +#include "ds-protocol.h" +#include "ds-eventpipe-protocol.h" +#include "ep.h" +#include "ds-rt.h" + +/* + * Forward declares of all static functions. + */ + +static +bool +eventpipe_protocol_helper_send_stop_tracing_success ( + DiagnosticsIpcStream *stream, + EventPipeSessionID session_id); + +static +bool +eventpipe_protocol_helper_send_start_tracing_success ( + DiagnosticsIpcStream *stream, + EventPipeSessionID session_id); + +static +bool +eventpipe_collect_tracing_command_try_parse_serialization_format ( + uint8_t **buffer, + uint32_t *buffer_len, + EventPipeSerializationFormat *format); + +static +bool +eventpipe_collect_tracing_command_try_parse_circular_buffer_size ( + uint8_t **buffer, + uint32_t *buffer_len, + uint32_t *circular_buffer); + +static +bool +eventpipe_collect_tracing_command_try_parse_rundown_requested ( + uint8_t **buffer, + uint32_t *buffer_len, + bool *rundown_requested); + +static +bool +eventpipe_collect_tracing_command_try_parse_config ( + uint8_t **buffer, + uint32_t *buffer_len, + ep_rt_provider_config_array_t *result); + +static +uint8_t * +eventpipe_collect_tracing_command_try_parse_payload ( + uint8_t *buffer, + uint16_t buffer_len); + +static +uint8_t * +eventpipe_collect_tracing2_command_try_parse_payload ( + uint8_t *buffer, + uint16_t buffer_len); + +static +void +eventpipe_protocol_helper_stop_tracing ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +static +void +eventpipe_protocol_helper_collect_tracing ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +static +void +eventpipe_protocol_helper_collect_tracing_2 ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +static +void +eventpipe_protocol_helper_unknown_command ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +/* +* EventPipeCollectTracingCommandPayload +*/ + +static +inline +bool +eventpipe_collect_tracing_command_try_parse_serialization_format ( + uint8_t **buffer, + uint32_t *buffer_len, + EventPipeSerializationFormat *format) +{ + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len != NULL); + EP_ASSERT (format != NULL); + + uint32_t serialization_format; + bool can_parse = ds_ipc_message_try_parse_uint32_t (buffer, buffer_len, &serialization_format); + + *format = (EventPipeSerializationFormat)serialization_format; + return can_parse && (0 <= (int32_t)serialization_format) && ((int32_t)serialization_format < (int32_t)EP_SERIALIZATION_FORMAT_COUNT); +} + +static +inline +bool +eventpipe_collect_tracing_command_try_parse_circular_buffer_size ( + uint8_t **buffer, + uint32_t *buffer_len, + uint32_t *circular_buffer) +{ + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len != NULL); + EP_ASSERT (circular_buffer != NULL); + + bool can_parse = ds_ipc_message_try_parse_uint32_t (buffer, buffer_len, circular_buffer); + return can_parse && (*circular_buffer > 0); +} + +static +inline +bool +eventpipe_collect_tracing_command_try_parse_rundown_requested ( + uint8_t **buffer, + uint32_t *buffer_len, + bool *rundown_requested) +{ + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len != NULL); + EP_ASSERT (rundown_requested != NULL); + + return ds_ipc_message_try_parse_value (buffer, buffer_len, (uint8_t *)rundown_requested, sizeof (bool)); +} + +static +bool +eventpipe_collect_tracing_command_try_parse_config ( + uint8_t **buffer, + uint32_t *buffer_len, + ep_rt_provider_config_array_t *result) +{ + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len != NULL); + EP_ASSERT (result != NULL); + + // Picking an arbitrary upper bound, + // This should be larger than any reasonable client request. + // TODO: This might be too large. + const uint32_t max_count_configs = 1000; + uint32_t count_configs = 0; + + ep_char8_t *provider_name_utf8 = NULL; + ep_char8_t *filter_data_utf8 = NULL; + + ep_raise_error_if_nok (ds_ipc_message_try_parse_uint32_t (buffer, buffer_len, &count_configs) == true); + ep_raise_error_if_nok (count_configs <= max_count_configs); + + ep_rt_provider_config_array_alloc_capacity (result, count_configs); + + for (uint32_t i = 0; i < count_configs; ++i) { + uint64_t keywords = 0; + ep_raise_error_if_nok (ds_ipc_message_try_parse_uint64_t (buffer, buffer_len, &keywords) == true); + + uint32_t log_level = 0; + ep_raise_error_if_nok (ds_ipc_message_try_parse_uint32_t (buffer, buffer_len, &log_level) == true); + ep_raise_error_if_nok (log_level <= EP_EVENT_LEVEL_VERBOSE); + + const ep_char16_t *provider_name = NULL; + ep_raise_error_if_nok (ds_ipc_message_try_parse_string_utf16_t (buffer, buffer_len, &provider_name) == true); + + provider_name_utf8 = ep_rt_utf16_to_utf8_string (provider_name, -1); + ep_raise_error_if_nok (provider_name_utf8 != NULL); + + ep_raise_error_if_nok (ep_rt_utf8_string_is_null_or_empty (provider_name_utf8) == false); + + const ep_char16_t *filter_data = NULL; // This parameter is optional. + ds_ipc_message_try_parse_string_utf16_t (buffer, buffer_len, &filter_data); + + if (filter_data) { + filter_data_utf8 = ep_rt_utf16_to_utf8_string (filter_data, -1); + ep_raise_error_if_nok (filter_data_utf8 != NULL); + } + + EventPipeProviderConfiguration provider_config; + ep_provider_config_init (&provider_config, provider_name_utf8, keywords, (EventPipeEventLevel)log_level, filter_data_utf8); + ep_rt_provider_config_array_append (result, provider_config); + + provider_name_utf8 = NULL; + filter_data_utf8 = NULL; + } + +ep_on_exit: + return (count_configs > 0); + +ep_on_error: + count_configs = 0; + ep_rt_utf8_string_free (provider_name_utf8); + ep_rt_utf8_string_free (filter_data_utf8); + ep_exit_error_handler (); +} + +static +uint8_t * +eventpipe_collect_tracing_command_try_parse_payload ( + uint8_t *buffer, + uint16_t buffer_len) +{ + EP_ASSERT (buffer != NULL); + + uint8_t * buffer_cursor = buffer; + uint32_t buffer_cursor_len = buffer_len; + + EventPipeCollectTracingCommandPayload * instance = ds_eventpipe_collect_tracing_command_payload_alloc (); + ep_raise_error_if_nok (instance != NULL); + + instance->incoming_buffer = buffer; + + if (!eventpipe_collect_tracing_command_try_parse_circular_buffer_size (&buffer_cursor, &buffer_cursor_len, &instance->circular_buffer_size_in_mb ) || + !eventpipe_collect_tracing_command_try_parse_serialization_format (&buffer_cursor, &buffer_cursor_len, &instance->serialization_format) || + !eventpipe_collect_tracing_command_try_parse_config (&buffer_cursor, &buffer_cursor_len, &instance->provider_configs)) + ep_raise_error (); + +ep_on_exit: + return (uint8_t *)instance; + +ep_on_error: + ds_eventpipe_collect_tracing_command_payload_free (instance); + instance = NULL; + ep_exit_error_handler (); +} + +EventPipeCollectTracingCommandPayload * +ds_eventpipe_collect_tracing_command_payload_alloc (void) +{ + return ep_rt_object_alloc (EventPipeCollectTracingCommandPayload); +} + +void +ds_eventpipe_collect_tracing_command_payload_free (EventPipeCollectTracingCommandPayload *payload) +{ + ep_return_void_if_nok (payload != NULL); + ep_rt_byte_array_free (payload->incoming_buffer); + + EventPipeProviderConfiguration *config = ep_rt_provider_config_array_data (&payload->provider_configs); + size_t config_len = ep_rt_provider_config_array_size (&payload->provider_configs); + for (size_t i = 0; i < config_len; ++i) { + ep_rt_utf8_string_free ((ep_char8_t *)ep_provider_config_get_provider_name (&config [i])); + ep_rt_utf8_string_free ((ep_char8_t *)ep_provider_config_get_filter_data (&config [i])); + } + + ep_rt_object_free (payload); +} + +/* +* EventPipeCollectTracing2CommandPayload +*/ + +static +uint8_t * +eventpipe_collect_tracing2_command_try_parse_payload ( + uint8_t *buffer, + uint16_t buffer_len) +{ + EP_ASSERT (buffer != NULL); + + uint8_t * buffer_cursor = buffer; + uint32_t buffer_cursor_len = buffer_len; + + EventPipeCollectTracing2CommandPayload *instance = ds_eventpipe_collect_tracing2_command_payload_alloc (); + ep_raise_error_if_nok (instance != NULL); + + instance->incoming_buffer = buffer; + + if (!eventpipe_collect_tracing_command_try_parse_circular_buffer_size (&buffer_cursor, &buffer_cursor_len, &instance->circular_buffer_size_in_mb ) || + !eventpipe_collect_tracing_command_try_parse_serialization_format (&buffer_cursor, &buffer_cursor_len, &instance->serialization_format) || + !eventpipe_collect_tracing_command_try_parse_rundown_requested (&buffer_cursor, &buffer_cursor_len, &instance->rundown_requested) || + !eventpipe_collect_tracing_command_try_parse_config (&buffer_cursor, &buffer_cursor_len, &instance->provider_configs)) + ep_raise_error (); + +ep_on_exit: + return (uint8_t *)instance; + +ep_on_error: + ds_eventpipe_collect_tracing2_command_payload_free (instance); + instance = NULL; + ep_exit_error_handler (); +} + +EventPipeCollectTracing2CommandPayload * +ds_eventpipe_collect_tracing2_command_payload_alloc (void) +{ + return ep_rt_object_alloc (EventPipeCollectTracing2CommandPayload); +} + +void +ds_eventpipe_collect_tracing2_command_payload_free (EventPipeCollectTracing2CommandPayload *payload) +{ + ep_return_void_if_nok (payload != NULL); + ep_rt_byte_array_free (payload->incoming_buffer); + + EventPipeProviderConfiguration *config = ep_rt_provider_config_array_data (&payload->provider_configs); + size_t config_len = ep_rt_provider_config_array_size (&payload->provider_configs); + for (size_t i = 0; i < config_len; ++i) { + ep_rt_utf8_string_free ((ep_char8_t *)ep_provider_config_get_provider_name (&config [i])); + ep_rt_utf8_string_free ((ep_char8_t *)ep_provider_config_get_filter_data (&config [i])); + } + + ep_rt_object_free (payload); +} + +/* +* EventPipeProtocolHelper +*/ + +static +bool +eventpipe_protocol_helper_send_stop_tracing_success ( + DiagnosticsIpcStream *stream, + EventPipeSessionID session_id) +{ + EP_ASSERT (stream != NULL); + + DiagnosticsIpcMessage success_message; + ds_ipc_message_init (&success_message); + bool success = ds_ipc_message_initialize_header_uint64_t_payload (&success_message, ds_ipc_header_get_generic_success (), (uint64_t)session_id); + if (success) + ds_ipc_message_send (&success_message, stream); + ds_ipc_message_fini (&success_message); + return success; +} + +static +bool +eventpipe_protocol_helper_send_start_tracing_success ( + DiagnosticsIpcStream *stream, + EventPipeSessionID session_id) +{ + EP_ASSERT (stream != NULL); + + DiagnosticsIpcMessage success_message; + ds_ipc_message_init (&success_message); + bool success = ds_ipc_message_initialize_header_uint64_t_payload (&success_message, ds_ipc_header_get_generic_success (), (uint64_t)session_id); + if (success) + ds_ipc_message_send (&success_message, stream); + ds_ipc_message_fini (&success_message); + return success; +} + +static +void +eventpipe_protocol_helper_stop_tracing ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + ep_return_void_if_nok (message != NULL && stream != NULL); + + EventPipeStopTracingCommandPayload *payload; + payload = (EventPipeStopTracingCommandPayload *)ds_ipc_message_try_parse_payload (message, NULL); + + if (!payload) { + ds_ipc_message_send_error (stream, DS_IPC_E_BAD_ENCODING); + ep_raise_error (); + } + + ep_disable (payload->session_id); + + eventpipe_protocol_helper_send_stop_tracing_success (stream, payload->session_id); + ds_ipc_stream_flush (stream); + +ep_on_exit: + ds_eventpipe_stop_tracing_command_payload_free (payload); + ds_ipc_stream_free (stream); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +static +void +eventpipe_protocol_helper_collect_tracing ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + ep_return_void_if_nok (message != NULL && stream != NULL); + + EventPipeCollectTracingCommandPayload *payload; + payload = (EventPipeCollectTracingCommandPayload *)ds_ipc_message_try_parse_payload (message, eventpipe_collect_tracing_command_try_parse_payload); + + if (!payload) { + ds_ipc_message_send_error (stream, DS_IPC_E_BAD_ENCODING); + ep_raise_error (); + } + + EventPipeSessionID session_id; + session_id = ep_enable ( + NULL, + payload->circular_buffer_size_in_mb, + ep_rt_provider_config_array_data (&payload->provider_configs), + (uint32_t)ep_rt_provider_config_array_size (&payload->provider_configs), + EP_SESSION_TYPE_IPCSTREAM, + payload->serialization_format, + true, + ds_ipc_stream_get_stream_ref (stream), + NULL); + + if (session_id == 0) { + ds_ipc_message_send_error (stream, DS_IPC_E_FAIL); + ep_raise_error (); + } else { + eventpipe_protocol_helper_send_start_tracing_success (stream, session_id); + ep_start_streaming (session_id); + } + +ep_on_exit: + ds_eventpipe_collect_tracing_command_payload_free (payload); + return; + +ep_on_error: + ds_ipc_stream_free (stream); + ep_exit_error_handler (); +} + +static +void +eventpipe_protocol_helper_collect_tracing_2 ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + ep_return_void_if_nok (message != NULL && stream != NULL); + + EventPipeCollectTracing2CommandPayload *payload; + payload = (EventPipeCollectTracing2CommandPayload *)ds_ipc_message_try_parse_payload (message, eventpipe_collect_tracing2_command_try_parse_payload); + + if (!payload) { + ds_ipc_message_send_error (stream, DS_IPC_E_BAD_ENCODING); + ep_raise_error (); + } + + EventPipeSessionID session_id; + session_id = ep_enable ( + NULL, + payload->circular_buffer_size_in_mb, + ep_rt_provider_config_array_data (&payload->provider_configs), + (uint32_t)ep_rt_provider_config_array_size (&payload->provider_configs), + EP_SESSION_TYPE_IPCSTREAM, + payload->serialization_format, + payload->rundown_requested, + ds_ipc_stream_get_stream_ref (stream), + NULL); + + if (session_id == 0) { + ds_ipc_message_send_error (stream, DS_IPC_E_FAIL); + ep_raise_error (); + } else { + eventpipe_protocol_helper_send_start_tracing_success (stream, session_id); + ep_start_streaming (session_id); + } + +ep_on_exit: + ds_eventpipe_collect_tracing2_command_payload_free (payload); + return; + +ep_on_error: + ds_ipc_stream_free (stream); + ep_exit_error_handler (); +} + +static +void +eventpipe_protocol_helper_unknown_command ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + DS_LOG_WARNING_1 ("Received unknown request type (%d)\n", ds_ipc_header_get_commandset (ds_ipc_message_get_header_cref (message))); + ds_ipc_message_send_error (stream, DS_IPC_E_UNKNOWN_COMMAND); + ds_ipc_stream_free (stream); +} + +void +ds_eventpipe_stop_tracing_command_payload_free (EventPipeStopTracingCommandPayload *payload) +{ + ep_return_void_if_nok (payload != NULL); + ep_rt_byte_array_free ((uint8_t *)payload); +} + +void +ds_eventpipe_protocol_helper_handle_ipc_message ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + ep_return_void_if_nok (message != NULL && stream != NULL); + + switch ((EventPipeCommandId)ds_ipc_header_get_commandid (ds_ipc_message_get_header_cref (message))) { + case EP_COMMANDID_COLLECT_TRACING: + eventpipe_protocol_helper_collect_tracing (message, stream); + break; + case EP_COMMANDID_COLLECT_TRACING_2: + eventpipe_protocol_helper_collect_tracing_2 (message, stream); + break; + case EP_COMMANDID_STOP_TRACING: + eventpipe_protocol_helper_stop_tracing (message, stream); + break; + default: + eventpipe_protocol_helper_unknown_command (message, stream); + break; + } +} + +#endif /* !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef DS_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_diagnostics_eventpipe_protocol; +const char quiet_linker_empty_file_warning_diagnostics_eventpipe_protocol = 0; +#endif diff --git a/src/mono/mono/eventpipe/ds-eventpipe-protocol.h b/src/mono/mono/eventpipe/ds-eventpipe-protocol.h new file mode 100644 index 00000000000000..2e48da282d10cd --- /dev/null +++ b/src/mono/mono/eventpipe/ds-eventpipe-protocol.h @@ -0,0 +1,124 @@ +#ifndef __DIAGNOSTICS_EVENTPIPE_PROTOCOL_H__ +#define __DIAGNOSTICS_EVENTPIPE_PROTOCOL_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#include "ds-types.h" +#include "ds-ipc.h" + +#undef DS_IMPL_GETTER_SETTER +#ifdef DS_IMPL_EVENTPIPE_PROTOCOL_GETTER_SETTER +#define DS_IMPL_GETTER_SETTER +#endif +#include "ds-getter-setter.h" + +/* +* EventPipeCollectTracingCommandPayload +*/ + +// Command = 0x0202 +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_EVENTPIPE_PROTOCOL_GETTER_SETTER) +struct _EventPipeCollectTracingCommandPayload { +#else +struct _EventPipeCollectTracingCommandPayload_Internal { +#endif + // The protocol buffer is defined as: + // X, Y, Z means encode bytes for X followed by bytes for Y followed by bytes for Z + // message = uint circularBufferMB, uint format, array providers + // uint = 4 little endian bytes + // wchar = 2 little endian bytes, UTF16 encoding + // array = uint length, length # of Ts + // string = (array where the last char must = 0) or (length = 0) + // provider_config = ulong keywords, uint logLevel, string provider_name, string filter_data + + uint8_t *incoming_buffer; + ep_rt_provider_config_array_t provider_configs; + uint32_t circular_buffer_size_in_mb; + EventPipeSerializationFormat serialization_format; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_EVENTPIPE_PROTOCOL_GETTER_SETTER) +struct _EventPipeCollectTracingCommandPayload { + uint8_t _internal [sizeof (struct _EventPipeCollectTracingCommandPayload_Internal)]; +}; +#endif + +EventPipeCollectTracingCommandPayload * +ds_eventpipe_collect_tracing_command_payload_alloc (void); + +void +ds_eventpipe_collect_tracing_command_payload_free (EventPipeCollectTracingCommandPayload *payload); + +/* +* EventPipeCollectTracing2CommandPayload +*/ + +// Command = 0x0202 +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_EVENTPIPE_PROTOCOL_GETTER_SETTER) +struct _EventPipeCollectTracing2CommandPayload { +#else +struct _EventPipeCollectTracing2CommandPayload_Internal { +#endif + // The protocol buffer is defined as: + // X, Y, Z means encode bytes for X followed by bytes for Y followed by bytes for Z + // message = uint circularBufferMB, uint format, array providers + // uint = 4 little endian bytes + // wchar = 2 little endian bytes, UTF16 encoding + // array = uint length, length # of Ts + // string = (array where the last char must = 0) or (length = 0) + // provider_config = ulong keywords, uint logLevel, string provider_name, string filter_data + + uint8_t *incoming_buffer; + ep_rt_provider_config_array_t provider_configs; + uint32_t circular_buffer_size_in_mb; + EventPipeSerializationFormat serialization_format; + bool rundown_requested; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_EVENTPIPE_PROTOCOL_GETTER_SETTER) +struct _EventPipeCollectTracing2CommandPayload { + uint8_t _internal [sizeof (struct _EventPipeCollectTracing2CommandPayload_Internal)]; +}; +#endif + +EventPipeCollectTracing2CommandPayload * +ds_eventpipe_collect_tracing2_command_payload_alloc (void); + +void +ds_eventpipe_collect_tracing2_command_payload_free (EventPipeCollectTracing2CommandPayload *payload); + +/* +* EventPipeStopTracingCommandPayload +*/ + +// Command = 0x0201 +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_EVENTPIPE_PROTOCOL_GETTER_SETTER) +struct _EventPipeStopTracingCommandPayload { +#else +struct _EventPipeStopTracingCommandPayload_Internal { +#endif + EventPipeSessionID session_id; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_EVENTPIPE_PROTOCOL_GETTER_SETTER) +struct _EventPipeStopTracingCommandPayload { + uint8_t _internal [sizeof (struct _EventPipeStopTracingCommandPayload_Internal)]; +}; +#endif + +void +ds_eventpipe_stop_tracing_command_payload_free (EventPipeStopTracingCommandPayload *payload); + +/* +* EventPipeProtocolHelper +*/ + +void +ds_eventpipe_protocol_helper_handle_ipc_message ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_EVENTPIPE_PROTOCOL_H__ */ diff --git a/src/mono/mono/eventpipe/ds-getter-setter.h b/src/mono/mono/eventpipe/ds-getter-setter.h new file mode 100644 index 00000000000000..80e11ea733fa01 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-getter-setter.h @@ -0,0 +1,83 @@ +#include "ep-getter-setter.h" + +#ifndef DS_DEFINE_INLINE_GETTER +#define DS_DEFINE_INLINE_GETTER(instance_type, instance_type_name, return_type, instance_field_name) \ + EP_DEFINE_INLINE_GETTER_PREFIX(ds, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef DS_DEFINE_INLINE_GETTER_REF +#define DS_DEFINE_INLINE_GETTER_REF(instance_type, instance_type_name, return_type, instance_field_name) \ + EP_DEFINE_INLINE_GETTER_REF_PREFIX(ds, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef DS_DEFINE_INLINE_GETTER_ARRAY_REF +#define DS_DEFINE_INLINE_GETTER_ARRAY_REF(instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) \ + EP_DEFINE_INLINE_GETTER_ARRAY_REF_PREFIX(ds, instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) +#endif + +#ifndef DS_DEFINE_INLINE_SETTER +#define DS_DEFINE_INLINE_SETTER(instance_type, instance_type_name, instance_field_type, instance_field_name) \ + EP_DEFINE_INLINE_SETTER_PREFIX(ds, instance_type, instance_type_name, instance_field_type, instance_field_name) +#endif + +#ifndef DS_DEFINE_NOINLINE_GETTER +#define DS_DEFINE_NOINLINE_GETTER(instance_type, instance_type_name, return_type, instance_field_name) \ + EP_DEFINE_NOINLINE_GETTER_PREFIX(ds, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef DS_DEFINE_NOINLINE_GETTER_REF +#define DS_DEFINE_NOINLINE_GETTER_REF(instance_type, instance_type_name, return_type, instance_field_name) \ + EP_DEFINE_NOINLINE_GETTER_REF_PREFIX(ds, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef DS_DEFINE_NOINLINE_GETTER_ARRAY_REF +#define DS_DEFINE_NOINLINE_GETTER_ARRAY_REF(instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) \ + EP_DEFINE_NOINLINE_GETTER_ARRAY_REF_PREFIX(ds, instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) +#endif + +#ifndef DS_DEFINE_NOINLINE_SETTER +#define DS_DEFINE_NOINLINE_SETTER(instance_type, instance_type_name, instance_field_type, instance_field_name) \ + EP_DEFINE_NOINLINE_SETTER_PREFIX(ds, instance_type, instance_type_name, instance_field_type, instance_field_name) +#endif + +#ifndef DS_IMPL_GETTER +#define DS_IMPL_GETTER(instance_type, instance_type_name, return_type, instance_field_name) \ + EP_IMPL_GETTER_PREFIX(ds, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef DS_IMPL_GETTER_REF +#define DS_IMPL_GETTER_REF(instance_type, instance_type_name, return_type, instance_field_name) \ + EP_IMPL_GETTER_REF_PREFIX(ds, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef DS_IMPL_GETTER_ARRAY_REF +#define DS_IMPL_GETTER_ARRAY_REF(instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) \ + EP_IMPL_GETTER_ARRAY_REF_PREFIX(ds, instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) +#endif + +#ifndef DS_IMPL_SETTER +#define DS_IMPL_SETTER(instance_type, instance_type_name, instance_field_type, instance_field_name) \ + EP_IMPL_SETTER_PREFIX(ds, instance_type, instance_type_name, instance_field_type, instance_field_name) +#endif + +#undef DS_DEFINE_GETTER +#undef DS_DEFINE_GETTER_REF +#undef DS_DEFINE_GETTER_ARRAY_REF +#undef DS_DEFINE_SETTER + +#if defined(DS_INLINE_GETTER_SETTER) +#define DS_DEFINE_GETTER DS_DEFINE_INLINE_GETTER +#define DS_DEFINE_GETTER_REF DS_DEFINE_INLINE_GETTER_REF +#define DS_DEFINE_GETTER_ARRAY_REF DS_DEFINE_INLINE_GETTER_ARRAY_REF +#define DS_DEFINE_SETTER DS_DEFINE_INLINE_SETTER +#elif defined(DS_IMPL_GETTER_SETTER) +#define DS_DEFINE_GETTER DS_IMPL_GETTER +#define DS_DEFINE_GETTER_REF DS_IMPL_GETTER_REF +#define DS_DEFINE_GETTER_ARRAY_REF DS_IMPL_GETTER_ARRAY_REF +#define DS_DEFINE_SETTER DS_IMPL_SETTER +#else +#define DS_DEFINE_GETTER DS_DEFINE_NOINLINE_GETTER +#define DS_DEFINE_GETTER_REF DS_DEFINE_NOINLINE_GETTER_REF +#define DS_DEFINE_GETTER_ARRAY_REF DS_DEFINE_NOINLINE_GETTER_ARRAY_REF +#define DS_DEFINE_SETTER DS_DEFINE_NOINLINE_SETTER +#endif diff --git a/src/mono/mono/eventpipe/ds-ipc-posix.c b/src/mono/mono/eventpipe/ds-ipc-posix.c new file mode 100644 index 00000000000000..979c7915c83b99 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-ipc-posix.c @@ -0,0 +1,890 @@ +#include + +#ifdef ENABLE_PERFTRACING +#ifndef HOST_WIN32 +#include "ds-rt-config.h" +#if !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) + +#define DS_IMPL_IPC_POSIX_GETTER_SETTER +#include "ds-ipc-posix.h" +#include "ds-protocol.h" +#include "ds-rt.h" + +#include +#include +#include +#include +#include +#include + +#if __GNUC__ +#include +#else +#include +#endif // __GNUC__ + +#ifdef __APPLE__ +#define APPLICATION_CONTAINER_BASE_PATH_SUFFIX "/Library/Group Containers/" + +// Not much to go with, but Max semaphore length on Mac is 31 characters. In a sandbox, the semaphore name +// must be prefixed with an application group ID. This will be 10 characters for developer ID and extra 2 +// characters for group name. For example ABCDEFGHIJ.MS. We still need some characters left +// for the actual semaphore names. +#define MAX_APPLICATION_GROUP_ID_LENGTH 13 +#endif // __APPLE__ + +/* + * Forward declares of all static functions. + */ + +static +void +ipc_stream_free_func (void *object); + +static +bool +ipc_stream_read_func ( + void *object, + uint8_t *buffer, + uint32_t bytes_to_read, + uint32_t *bytes_read, + uint32_t timeout_ms); + +static +bool +ipc_stream_write_func ( + void *object, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written, + uint32_t timeout_ms); + +static +bool +ipc_stream_flush_func (void *object); + +static +bool +ipc_stream_close_func (void *object); + +static +DiagnosticsIpcStream * +ipc_stream_alloc ( + int client_socket, + DiagnosticsIpcConnectionMode mode); + +static +bool +ipc_init_listener ( + DiagnosticsIpc *ipc, + struct sockaddr *server_address, + size_t server_address_len, + ds_ipc_error_callback_func callback); + +/* + * DiagnosticsIpc. + */ + +#ifdef __APPLE__ +static +bool +ipc_init_listener ( + DiagnosticsIpc *ipc, + struct sockaddr *server_address, + size_t server_address_len, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (ipc != NULL); + EP_ASSERT (ipc->mode == DS_IPC_CONNECTION_MODE_LISTEN); + + bool success = false; + + // This will set the default permission bit to 600 + mode_t prev_mask = umask (~(S_IRUSR | S_IWUSR)); + + int server_socket; + DS_ENTER_BLOCKING_PAL_SECTION; + server_socket = socket (AF_UNIX, SOCK_STREAM, 0 ); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (server_socket == -1) { + if (callback) + callback (strerror (errno), errno); + umask(prev_mask); + EP_ASSERT (!"Failed to create diagnostics IPC socket."); + ep_raise_error (); + } + + int result_bind; + DS_ENTER_BLOCKING_PAL_SECTION; + result_bind = bind (server_socket, server_address, server_address_len); + DS_EXIT_BLOCKING_PAL_SECTION; + + EP_ASSERT (result_bind != -1); + if (result_bind == -1) { + if (callback) + callback (strerror (errno), errno); + + int result_close; + DS_ENTER_BLOCKING_PAL_SECTION; + result_close = close (server_socket); + DS_EXIT_BLOCKING_PAL_SECTION; + + EP_ASSERT (result_close != -1); + if (result_close == -1) { + if (callback) + callback (strerror (errno), errno); + } + + umask (prev_mask); + ep_raise_error (); + } + + umask (prev_mask); + + ipc->server_socket = server_socket; + success = true; + +ep_on_exit: + return success; + +ep_on_error: + success = false; + ep_exit_error_handler (); +} +#else +static +bool +ipc_init_listener ( + DiagnosticsIpc *ipc, + struct sockaddr *server_address, + size_t server_address_len, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (ipc != NULL); + EP_ASSERT (ipc->mode == DS_IPC_CONNECTION_MODE_LISTEN); + + bool success = false; + + int server_socket; + DS_ENTER_BLOCKING_PAL_SECTION; + server_socket = socket (AF_UNIX, SOCK_STREAM, 0); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (server_socket == -1) { + if (callback) + callback (strerror (errno), errno); + EP_ASSERT (!"Failed to create diagnostics IPC socket."); + ep_raise_error (); + } + + int result_fchmod; + DS_ENTER_BLOCKING_PAL_SECTION; + result_fchmod = fchmod (server_socket, S_IRUSR | S_IWUSR); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (result_fchmod == -1) { + if (callback) + callback (strerror (errno), errno); + EP_ASSERT (!"Failed to set permissions on diagnostics IPC socket."); + ep_raise_error (); + } + + int result_bind; + DS_ENTER_BLOCKING_PAL_SECTION; + result_bind = bind (server_socket, server_address, server_address_len); + DS_EXIT_BLOCKING_PAL_SECTION; + + EP_ASSERT (result_bind != -1); + if (result_bind == -1) { + if (callback) + callback (strerror (errno), errno); + + int result_close; + DS_ENTER_BLOCKING_PAL_SECTION; + result_close = close (server_socket); + DS_EXIT_BLOCKING_PAL_SECTION; + + EP_ASSERT (result_close != -1); + if (result_close == -1) { + if (callback) + callback (strerror (errno), errno); + } + + ep_raise_error (); + } + + ipc->server_socket = server_socket; + success = true; + +ep_on_exit: + return success; + +ep_on_error: + success = false; + ep_exit_error_handler (); +} +#endif + +DiagnosticsIpc * +ds_ipc_alloc ( + const ep_char8_t *pipe_name, + DiagnosticsIpcConnectionMode mode, + ds_ipc_error_callback_func callback) +{ + DiagnosticsIpc *instance = NULL; + struct sockaddr_un *server_address = ep_rt_object_alloc (struct sockaddr_un); + ep_raise_error_if_nok (server_address != NULL); + + server_address->sun_family = AF_UNIX; + + if (pipe_name) { + ep_rt_utf8_string_snprintf ( + server_address->sun_path, + sizeof (server_address->sun_path), + "%s", + pipe_name); + } else { + // generate the default socket name + ds_rt_transport_get_default_name ( + server_address->sun_path, + sizeof (server_address->sun_path), + "dotnet-diagnostic", + ep_rt_current_process_get_id (), + NULL, + "socket"); + } + + instance = ep_rt_object_alloc (DiagnosticsIpc); + ep_raise_error_if_nok (instance != NULL); + + instance->mode = mode; + instance->server_socket = -1; + instance->server_address = server_address; + instance->is_closed = false; + instance->is_listening = false; + + // Ownership transfered. + server_address = NULL; + + if (mode == DS_IPC_CONNECTION_MODE_LISTEN) + ep_raise_error_if_nok (ipc_init_listener (instance, (struct sockaddr *)instance->server_address, sizeof (*instance->server_address), callback) == true); + +ep_on_exit: + return instance; + +ep_on_error: + ep_rt_object_free (server_address); + ds_ipc_free (instance); + instance = NULL; + ep_exit_error_handler (); +} + +void +ds_ipc_free (DiagnosticsIpc *ipc) +{ + ep_return_void_if_nok (ipc != NULL); + + ds_ipc_close (ipc, false, NULL); + ep_rt_object_free (ipc->server_address); + ep_rt_object_free (ipc); +} + +int32_t +ds_ipc_poll ( + ds_rt_ipc_poll_handle_array_t *poll_handles, + uint32_t timeout_ms, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (poll_handles); + + int32_t result = -1; + DiagnosticsIpcPollHandle * poll_handles_data = ds_rt_ipc_poll_handle_array_data (poll_handles); + size_t poll_handles_data_len = ds_rt_ipc_poll_handle_array_size (poll_handles); + + // prepare the pollfd structs + struct pollfd *poll_fds = ep_rt_object_array_alloc (struct pollfd, poll_handles_data_len); + ep_raise_error_if_nok (poll_fds != NULL); + + for (uint32_t i = 0; i < poll_handles_data_len; ++i) { + ds_ipc_poll_handle_set_events (&poll_handles_data [i], 0); // ignore any input on events. + int fd = -1; + if (ds_ipc_poll_handle_get_ipc (&poll_handles_data [i])) { + // SERVER + EP_ASSERT (poll_handles_data [i].ipc->mode == DS_IPC_CONNECTION_MODE_LISTEN); + fd = ds_ipc_poll_handle_get_ipc (&poll_handles_data [i])->server_socket; + } else { + // CLIENT + EP_ASSERT (poll_handles_data [i].stream != NULL); + fd = ds_ipc_poll_handle_get_stream (&poll_handles_data [i])->client_socket; + } + + poll_fds [i].fd = fd; + poll_fds [i].events = POLLIN; + } + + int result_poll; + DS_ENTER_BLOCKING_PAL_SECTION; + result_poll = poll (poll_fds, poll_handles_data_len, timeout_ms); + DS_EXIT_BLOCKING_PAL_SECTION; + + // Check results + if (result_poll < 0) { + // If poll() returns with an error, including one due to an interrupted call, the fds + // array will be unmodified and the global variable errno will be set to indicate the error. + // - POLL(2) + if (callback) + callback (strerror (errno), errno); + ep_raise_error (); + } else if (result_poll == 0) { + // we timed out + result = 0; + ep_raise_error (); + } + + for (uint32_t i = 0; i < poll_handles_data_len; ++i) { + if (poll_fds [i].revents != 0) { + if (callback) + callback ("IpcStream::DiagnosticsIpc::Poll - poll revents", (uint32_t)poll_fds [i].revents); + // error check FIRST + if (poll_fds [i].revents & POLLHUP) { + // check for hangup first because a closed socket + // will technically meet the requirements for POLLIN + // i.e., a call to recv/read won't block + ds_ipc_poll_handle_set_events (&poll_handles_data [i], (uint8_t)DS_IPC_POLL_EVENTS_HANGUP); + } else if ((poll_fds [i].revents & (POLLERR|POLLNVAL))) { + if (callback) + callback ("Poll error", (uint32_t)poll_fds [i].revents); + ds_ipc_poll_handle_set_events (&poll_handles_data [i], (uint8_t)DS_IPC_POLL_EVENTS_ERR); + } else if (poll_fds [i].revents & (POLLIN|POLLPRI)) { + ds_ipc_poll_handle_set_events (&poll_handles_data [i], (uint8_t)DS_IPC_POLL_EVENTS_SIGNALED); + } else { + ds_ipc_poll_handle_set_events (&poll_handles_data [i], (uint8_t)DS_IPC_POLL_EVENTS_UNKNOWN); + if (callback) + callback ("unkown poll response", (uint32_t)poll_fds [i].revents); + } + } + } + + result = 1; + +ep_on_exit: + ep_rt_object_array_free (poll_fds); + return result; + +ep_on_error: + if (result != 0) + result = -1; + + ep_exit_error_handler (); +} + +bool +ds_ipc_listen ( + DiagnosticsIpc *ipc, + ds_ipc_error_callback_func callback) +{ + bool result = false; + EP_ASSERT (ipc != NULL); + + EP_ASSERT (ipc->mode == DS_IPC_CONNECTION_MODE_LISTEN); + if (ipc->mode != DS_IPC_CONNECTION_MODE_LISTEN) { + if (callback) + callback ("Cannot call Listen on a client connection", -1); + return false; + } + + if (ipc->is_listening) + return true; + + EP_ASSERT (ipc->server_socket != -1); + + int result_listen; + DS_ENTER_BLOCKING_PAL_SECTION; + result_listen = listen (ipc->server_socket, /* backlog */ 255); + DS_EXIT_BLOCKING_PAL_SECTION; + + EP_ASSERT (result_listen != -1); + if (result_listen == -1) { + if (callback) + callback (strerror (errno), errno); + + int result_unlink; + DS_ENTER_BLOCKING_PAL_SECTION; + result_unlink = unlink (ipc->server_address->sun_path); + DS_EXIT_BLOCKING_PAL_SECTION; + + EP_ASSERT (result_unlink != -1); + if (result_unlink == -1) { + if (callback) + callback (strerror (errno), errno); + } + + int result_close; + DS_ENTER_BLOCKING_PAL_SECTION; + result_close = close (ipc->server_socket); + DS_EXIT_BLOCKING_PAL_SECTION; + + EP_ASSERT (result_close != -1); + if (result_close == -1) { + if (callback) + callback (strerror (errno), errno); + } + + ep_raise_error (); + } + + ipc->is_listening = true; + result = true; + +ep_on_exit: + return result; + +ep_on_error: + result = false; + ep_exit_error_handler (); +} + +DiagnosticsIpcStream * +ds_ipc_accept ( + DiagnosticsIpc *ipc, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (ipc != NULL); + DiagnosticsIpcStream *stream = NULL; + + EP_ASSERT (ipc->mode == DS_IPC_CONNECTION_MODE_LISTEN); + EP_ASSERT (ipc->is_listening); + + struct sockaddr_un from; + socklen_t from_len = sizeof (from); + + int client_socket; + DS_ENTER_BLOCKING_PAL_SECTION; + client_socket = accept (ipc->server_socket, (struct sockaddr *)&from, &from_len); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (client_socket == -1) { + if (callback) + callback (strerror (errno), errno); + ep_raise_error (); + } + + stream = ipc_stream_alloc (client_socket, ipc->mode); + +ep_on_exit: + return stream; + +ep_on_error: + ds_ipc_stream_free (stream); + stream = NULL; + ep_exit_error_handler (); +} + +DiagnosticsIpcStream * +ds_ipc_connect ( + DiagnosticsIpc *ipc, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (ipc != NULL); + DiagnosticsIpcStream *stream = NULL; + + EP_ASSERT (ipc->mode == DS_IPC_CONNECTION_MODE_CONNECT); + + struct sockaddr_un client_address; + client_address.sun_family = AF_UNIX; + + int client_socket; + DS_ENTER_BLOCKING_PAL_SECTION; + client_socket = socket (AF_UNIX, SOCK_STREAM, 0); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (client_socket == -1) { + if (callback) + callback (strerror (errno), errno); + ep_raise_error (); + } + + // We don't expect this to block since this is a Unix Domain Socket. `connect` may block until the + // TCP handshake is complete for TCP/IP sockets, but UDS don't use TCP. `connect` will return even if + // the server hasn't called `accept`. + int result_connect; + DS_ENTER_BLOCKING_PAL_SECTION; + result_connect = connect (client_socket, (struct sockaddr *)ipc->server_address, sizeof(*(ipc->server_address))); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (result_connect < 0) { + if (callback) + callback (strerror (errno), errno); + + int result_close; + DS_ENTER_BLOCKING_PAL_SECTION; + result_close = close (client_socket); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (result_close < 0 && callback) + callback (strerror (errno), errno); + + ep_raise_error (); + } + + stream = ipc_stream_alloc (client_socket, DS_IPC_CONNECTION_MODE_CONNECT); + +ep_on_exit: + return stream; + +ep_on_error: + ds_ipc_stream_free (stream); + stream = NULL; + + ep_exit_error_handler (); +} + +void +ds_ipc_close ( + DiagnosticsIpc *ipc, + bool is_shutdown, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (ipc != NULL); + ep_return_void_if_nok (ipc->is_closed == false); + + ipc->is_closed = true; + + if (ipc->server_socket != -1) { + // only close the socket if not shutting down, let the OS handle it in that case + if (!is_shutdown) { + int close_result; + DS_ENTER_BLOCKING_PAL_SECTION; + close_result = close (ipc->server_socket); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (close_result == -1) { + if (callback) + callback (strerror (errno), errno); + EP_ASSERT (!"Failed to close unix domain socket."); + } + } + + // N.B. - it is safe to unlink the unix domain socket file while the server + // is still alive: + // "The usual UNIX close-behind semantics apply; the socket can be unlinked + // at any time and will be finally removed from the file system when the last + // reference to it is closed." - unix(7) man page + int result_unlink; + DS_ENTER_BLOCKING_PAL_SECTION; + result_unlink = unlink (ipc->server_address->sun_path); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (result_unlink == -1) { + if (callback) + callback (strerror (errno), errno); + EP_ASSERT (!"Failed to unlink server address."); + } + } +} + +int32_t +ds_ipc_to_string ( + DiagnosticsIpc *ipc, + ep_char8_t *buffer, + uint32_t buffer_len) +{ + EP_ASSERT (ipc != NULL); + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len <= DS_IPC_MAX_TO_STRING_LEN); + + return ep_rt_utf8_string_snprintf (buffer, buffer_len, "{ server_socket = %d }", ipc->server_socket); +} + +/* + * DiagnosticsIpcStream. + */ + +static +void +ipc_stream_free_func (void *object) +{ + EP_ASSERT (object != NULL); + DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object; + ds_ipc_stream_free (ipc_stream); +} + +static +bool +ipc_stream_read_func ( + void *object, + uint8_t *buffer, + uint32_t bytes_to_read, + uint32_t *bytes_read, + uint32_t timeout_ms) +{ + EP_ASSERT (object != NULL); + EP_ASSERT (buffer != NULL); + EP_ASSERT (bytes_read != NULL); + + bool success = false; + DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object; + uint8_t *buffer_cursor = (uint8_t*)buffer; + ssize_t current_bytes_read = 0; + ssize_t total_bytes_read = 0; + bool continue_recv = true; + + if (timeout_ms != DS_IPC_STREAM_TIMEOUT_INFINITE) { + struct pollfd pfd; + pfd.fd = ipc_stream->client_socket; + pfd.events = POLLIN; + + int result_poll; + DS_ENTER_BLOCKING_PAL_SECTION; + result_poll = poll (&pfd, 1, timeout_ms); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (result_poll <= 0 || !(pfd.revents & POLLIN)) { + // timeout or error + ep_raise_error (); + } + // else fallthrough + } + + DS_ENTER_BLOCKING_PAL_SECTION; + while (continue_recv && bytes_to_read - total_bytes_read > 0) { + current_bytes_read = recv ( + ipc_stream->client_socket, + buffer_cursor, + bytes_to_read - total_bytes_read, + 0); + continue_recv = current_bytes_read > 0; + if (!continue_recv) + break; + total_bytes_read += current_bytes_read; + buffer_cursor += current_bytes_read; + } + DS_EXIT_BLOCKING_PAL_SECTION; + + ep_raise_error_if_nok (continue_recv == true); + success = true; + +ep_on_exit: + *bytes_read = (uint32_t)total_bytes_read; + return success; + +ep_on_error: + total_bytes_read = 0; + success = false; + ep_exit_error_handler (); +} + +static +bool +ipc_stream_write_func ( + void *object, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written, + uint32_t timeout_ms) +{ + EP_ASSERT (object != NULL); + EP_ASSERT (buffer != NULL); + EP_ASSERT (bytes_written != NULL); + + bool success = false; + DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object; + uint8_t *buffer_cursor = (uint8_t*)buffer; + ssize_t current_bytes_written = 0; + ssize_t total_bytes_written = 0; + bool continue_send = true; + + if (timeout_ms != DS_IPC_STREAM_TIMEOUT_INFINITE) { + struct pollfd pfd; + pfd.fd = ipc_stream->client_socket; + pfd.events = POLLOUT; + + int result_poll; + DS_ENTER_BLOCKING_PAL_SECTION; + result_poll = poll (&pfd, 1, timeout_ms); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (result_poll <= 0 || !(pfd.revents & POLLOUT)) { + // timeout or error + ep_raise_error (); + } + // else fallthrough + } + + DS_ENTER_BLOCKING_PAL_SECTION; + while (continue_send && bytes_to_write - total_bytes_written > 0) { + current_bytes_written = send ( + ipc_stream->client_socket, + buffer_cursor, + bytes_to_write - total_bytes_written, + 0); + continue_send = current_bytes_written != -1; + if (!continue_send) + break; + total_bytes_written += current_bytes_written; + buffer_cursor += current_bytes_written; + } + DS_EXIT_BLOCKING_PAL_SECTION; + + ep_raise_error_if_nok (continue_send == true); + success = true; + +ep_on_exit: + *bytes_written = (uint32_t)total_bytes_written; + return success; + +ep_on_error: + total_bytes_written = 0; + success = false; + ep_exit_error_handler (); +} + +static +bool +ipc_stream_flush_func (void *object) +{ + // fsync - http://man7.org/linux/man-pages/man2/fsync.2.html ??? + return true; +} + +static +bool +ipc_stream_close_func (void *object) +{ + EP_ASSERT (object != NULL); + DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object; + return ds_ipc_stream_close (ipc_stream, NULL); +} + +static IpcStreamVtable ipc_stream_vtable = { + ipc_stream_free_func, + ipc_stream_read_func, + ipc_stream_write_func, + ipc_stream_flush_func, + ipc_stream_close_func }; + +static +DiagnosticsIpcStream * +ipc_stream_alloc ( + int client_socket, + DiagnosticsIpcConnectionMode mode) +{ + DiagnosticsIpcStream *instance = ep_rt_object_alloc (DiagnosticsIpcStream); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ep_ipc_stream_init (&instance->stream, &ipc_stream_vtable) != NULL); + + instance->client_socket = client_socket; + instance->mode = mode; + +ep_on_exit: + return instance; + +ep_on_error: + ds_ipc_stream_free (instance); + instance = NULL; + ep_exit_error_handler (); +} + +IpcStream * +ds_ipc_stream_get_stream_ref (DiagnosticsIpcStream *ipc_stream) +{ + return &ipc_stream->stream; +} + +void +ds_ipc_stream_free (DiagnosticsIpcStream *ipc_stream) +{ + ep_return_void_if_nok (ipc_stream != NULL); + ds_ipc_stream_close (ipc_stream, NULL); + ep_rt_object_free (ipc_stream); +} + +bool +ds_ipc_stream_read ( + DiagnosticsIpcStream *ipc_stream, + uint8_t *buffer, + uint32_t bytes_to_read, + uint32_t *bytes_read, + uint32_t timeout_ms) +{ + return ipc_stream_read_func ( + ipc_stream, + buffer, + bytes_to_read, + bytes_read, + timeout_ms); +} + +bool +ds_ipc_stream_write ( + DiagnosticsIpcStream *ipc_stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written, + uint32_t timeout_ms) +{ + return ipc_stream_write_func ( + ipc_stream, + buffer, + bytes_to_write, + bytes_written, + timeout_ms); +} + +bool +ds_ipc_stream_flush (DiagnosticsIpcStream *ipc_stream) +{ + return ipc_stream_flush_func (ipc_stream); +} + +bool +ds_ipc_stream_close ( + DiagnosticsIpcStream *ipc_stream, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (ipc_stream != NULL); + + if (ipc_stream->client_socket != -1) { + ds_ipc_stream_flush (ipc_stream); + + int result_close; + DS_ENTER_BLOCKING_PAL_SECTION; + result_close = close (ipc_stream->client_socket); + DS_EXIT_BLOCKING_PAL_SECTION; + + EP_ASSERT (result_close != -1); + if (result_close == -1) { + if (callback) + callback (strerror (errno), errno); + } + + ipc_stream->client_socket = -1; + } + + return true; +} + +int32_t +ds_ipc_stream_to_string ( + DiagnosticsIpcStream *ipc_stream, + ep_char8_t *buffer, + uint32_t buffer_len) +{ + EP_ASSERT (ipc_stream != NULL); + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len <= DS_IPC_MAX_TO_STRING_LEN); + + //TODO: Implement. + return ep_rt_utf8_string_snprintf (buffer, buffer_len, "{ client_socket = %d }", ipc_stream->client_socket); +} + +#endif /* !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* !HOST_WIN32 */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef DS_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_diagnostics_ipc_posix; +const char quiet_linker_empty_file_warning_diagnostics_ipc_posix = 0; +#endif diff --git a/src/mono/mono/eventpipe/ds-ipc-posix.h b/src/mono/mono/eventpipe/ds-ipc-posix.h new file mode 100644 index 00000000000000..ccc0b60111a4a4 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-ipc-posix.h @@ -0,0 +1,63 @@ +#ifndef __DIAGNOSTICS_IPC_POSIX_H__ +#define __DIAGNOSTICS_IPC_POSIX_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#ifndef HOST_WIN32 +#include "ds-rt-config.h" +#include "ds-types.h" +#include "ds-ipc.h" +#include "ep-stream.h" + +#undef DS_IMPL_GETTER_SETTER +#ifdef DS_IMPL_IPC_POSIX_GETTER_SETTER +#define DS_IMPL_GETTER_SETTER +#endif +#include "ds-getter-setter.h" + +/* + * DiagnosticsIpc. + */ + +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_IPC_POSIX_GETTER_SETTER) +struct _DiagnosticsIpc { +#else +struct _DiagnosticsIpc_Internal { +#endif + struct sockaddr_un *server_address; + int server_socket; + bool is_listening; + bool is_closed; + DiagnosticsIpcConnectionMode mode; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_IPC_POSIX_GETTER_SETTER) +struct _DiagnosticsIpc { + uint8_t _internal [sizeof (struct _DiagnosticsIpc_Internal)]; +}; +#endif + +/* + * DiagnosticsIpcStream. + */ + +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_IPC_POSIX_GETTER_SETTER) +struct _DiagnosticsIpcStream { +#else +struct _DiagnosticsIpcStream_Internal { +#endif + IpcStream stream; + int client_socket; + DiagnosticsIpcConnectionMode mode; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_IPC_POSIX_GETTER_SETTER) +struct _DiagnosticsIpcStream { + uint8_t _internal [sizeof (struct _DiagnosticsIpcStream_Internal)]; +}; +#endif + +#endif /* !HOST_WIN32 */ +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_IPC_POSIX_H__ */ diff --git a/src/mono/mono/eventpipe/ds-ipc-win32.c b/src/mono/mono/eventpipe/ds-ipc-win32.c new file mode 100644 index 00000000000000..c1c6db591a4dfa --- /dev/null +++ b/src/mono/mono/eventpipe/ds-ipc-win32.c @@ -0,0 +1,839 @@ +#include + +#ifdef ENABLE_PERFTRACING +#ifdef HOST_WIN32 +#include "ds-rt-config.h" +#if !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) + +#define DS_IMPL_IPC_WIN32_GETTER_SETTER +#include "ds-ipc-win32.h" +#include "ds-protocol.h" +#include "ds-rt.h" + +/* + * Forward declares of all static functions. + */ + +static +void +ipc_stream_free_func (void *object); + +static +bool +ipc_stream_read_func ( + void *object, + uint8_t *buffer, + uint32_t bytes_to_read, + uint32_t *bytes_read, + uint32_t timeout_ms); + +static +bool +ipc_stream_write_func ( + void *object, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written, + uint32_t timeout_ms); + +static +bool +ipc_stream_flush_func (void *object); + +static +bool +ipc_stream_close_func (void *object); + +static +DiagnosticsIpcStream * +ipc_stream_alloc ( + HANDLE pipe, + DiagnosticsIpcConnectionMode mode); + +/* + * DiagnosticsIpc. + */ + +DiagnosticsIpc * +ds_ipc_alloc ( + const ep_char8_t *pipe_name, + DiagnosticsIpcConnectionMode mode, + ds_ipc_error_callback_func callback) +{ + int32_t characters_written = -1; + + DiagnosticsIpc *instance = ep_rt_object_alloc (DiagnosticsIpc); + ep_raise_error_if_nok (instance != NULL); + + instance->mode = mode; + instance->is_listening = false; + + // All memory zeroed on alloc. + //memset (&instance->overlap, 0, sizeof (instance->overlap)); + + instance->overlap.hEvent = INVALID_HANDLE_VALUE; + instance->pipe = INVALID_HANDLE_VALUE; + + if (pipe_name) { + characters_written = ep_rt_utf8_string_snprintf ( + &instance->pipe_name, + DS_IPC_WIN32_MAX_NAMED_PIPE_LEN, + "\\\\.\\pipe\\%s", + pipe_name); + } else { + characters_written = ep_rt_utf8_string_snprintf ( + &instance->pipe_name, + DS_IPC_WIN32_MAX_NAMED_PIPE_LEN, + "\\\\.\\pipe\\dotnet-diagnostic-%d", + ep_rt_current_process_get_id ()); + } + + if (characters_written == -1) { + if (callback) + callback ("Failed to generate the named pipe name", characters_written); + ep_raise_error (); + } + +ep_on_exit: + return instance; + +ep_on_error: + ds_ipc_free (instance); + instance = NULL; + ep_exit_error_handler (); +} + +void +ds_ipc_free (DiagnosticsIpc *ipc) +{ + ep_return_void_if_nok (ipc != NULL); + + ds_ipc_close (ipc, false, NULL); + ep_rt_object_free (ipc); +} + +int32_t +ds_ipc_poll ( + ds_rt_ipc_poll_handle_array_t *poll_handles, + uint32_t timeout_ms, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (poll_handles); + + int32_t result = 1; + DiagnosticsIpcPollHandle * poll_handles_data = ds_rt_ipc_poll_handle_array_data (poll_handles); + size_t poll_handles_data_len = ds_rt_ipc_poll_handle_array_size (poll_handles); + + HANDLE handles [MAXIMUM_WAIT_OBJECTS]; + for (size_t i = 0; i < poll_handles_data_len; ++i) { + ds_ipc_poll_handle_set_events (&poll_handles_data [i], 0); // ignore any input on events. + if (ds_ipc_poll_handle_get_ipc (&poll_handles_data [i])) { + // SERVER + EP_ASSERT (poll_handles_data [i].ipc->mode == DS_IPC_CONNECTION_MODE_LISTEN); + handles [i] = ds_ipc_poll_handle_get_ipc (&poll_handles_data [i])->overlap.hEvent; + } else { + // CLIENT + bool success = true; + DWORD bytes_read = 1; + if (!ds_ipc_poll_handle_get_stream (&poll_handles_data [i])->is_test_reading) { + // check for data by doing an asynchronous 0 byte read. + // This will signal if the pipe closes (hangup) or the server + // sends new data + success = ReadFile ( + ds_ipc_poll_handle_get_stream (&poll_handles_data [i])->pipe, // handle + NULL, // null buffer + 0, // read 0 bytesd + &bytes_read, // dummy variable + &ds_ipc_poll_handle_get_stream (&poll_handles_data [i])->overlap); // overlap object to use + + ds_ipc_poll_handle_get_stream (&poll_handles_data [i])->is_test_reading = true; + if (!success) { + DWORD error = GetLastError (); + switch (error) { + case ERROR_IO_PENDING: + handles [i] = ds_ipc_poll_handle_get_stream (&poll_handles_data [i])->overlap.hEvent; + break; + case ERROR_PIPE_NOT_CONNECTED: + ds_ipc_poll_handle_set_events (&poll_handles_data [i], (uint8_t)DS_IPC_POLL_EVENTS_HANGUP); + result = -1; + ep_raise_error (); + default: + if (callback) + callback ("0 byte async read on client connection failed", error); + result = -1; + ep_raise_error (); + } + } else { + // there's already data to be read + handles [i] = ds_ipc_poll_handle_get_stream (&poll_handles_data [i])->overlap.hEvent; + } + } else { + handles [i] = ds_ipc_poll_handle_get_stream (&poll_handles_data [i])->overlap.hEvent; + } + } + } + + // call wait for multiple obj + DWORD wait = WAIT_FAILED; + DS_ENTER_BLOCKING_PAL_SECTION; + wait = WaitForMultipleObjects ( + poll_handles_data_len, // count + handles, // handles + false, // don't wait all + timeout_ms); + DS_EXIT_BLOCKING_PAL_SECTION; + + if (wait == WAIT_TIMEOUT) { + // we timed out + result = 0; + ep_raise_error (); + } + + if (wait == WAIT_FAILED) { + // we errored + if (callback) + callback ("WaitForMultipleObjects failed", GetLastError()); + result = -1; + ep_raise_error (); + } + + // determine which of the streams signaled + DWORD index = wait - WAIT_OBJECT_0; + // error check the index + if (index < 0 || index > (poll_handles_data_len - 1)) { + // check if we abandoned something + DWORD abandonedIndex = wait - WAIT_ABANDONED_0; + if (abandonedIndex > 0 || abandonedIndex < (poll_handles_data_len - 1)) { + ds_ipc_poll_handle_set_events( &poll_handles_data [abandonedIndex], (uint8_t)DS_IPC_POLL_EVENTS_HANGUP); + result = -1; + ep_raise_error (); + } else { + if (callback) + callback ("WaitForMultipleObjects failed", GetLastError()); + result = -1; + ep_raise_error (); + } + } + + // Set revents depending on what signaled the stream + if (!ds_ipc_poll_handle_get_ipc (&poll_handles_data [index])) { + // CLIENT + // check if the connection got hung up + // Start with quick none blocking completion check. + DWORD dummy = 0; + BOOL success = GetOverlappedResult( + ds_ipc_poll_handle_get_stream(&poll_handles_data [index])->pipe, + &ds_ipc_poll_handle_get_stream(&poll_handles_data [index])->overlap, + &dummy, + false); + if (!success && GetLastError () == ERROR_IO_INCOMPLETE) { + // IO still incomplete, wait for completion. + dummy = 0; + DS_ENTER_BLOCKING_PAL_SECTION; + success = GetOverlappedResult( + ds_ipc_poll_handle_get_stream(&poll_handles_data [index])->pipe, + &ds_ipc_poll_handle_get_stream(&poll_handles_data [index])->overlap, + &dummy, + true); + DS_EXIT_BLOCKING_PAL_SECTION; + } + ds_ipc_poll_handle_get_stream(&poll_handles_data [index])->is_test_reading = false; + if (!success) { + DWORD error = GetLastError(); + if (error == ERROR_PIPE_NOT_CONNECTED || error == ERROR_BROKEN_PIPE) { + ds_ipc_poll_handle_set_events(&poll_handles_data [index], (uint8_t)DS_IPC_POLL_EVENTS_HANGUP); + } else { + if (callback) + callback ("Client connection error", error); + ds_ipc_poll_handle_set_events (&poll_handles_data [index], (uint8_t)DS_IPC_POLL_EVENTS_ERR); + result = -1; + ep_raise_error (); + } + } else { + ds_ipc_poll_handle_set_events (&poll_handles_data [index], (uint8_t)DS_IPC_POLL_EVENTS_SIGNALED); + } + } else { + // SERVER + ds_ipc_poll_handle_set_events (&poll_handles_data [index], (uint8_t)DS_IPC_POLL_EVENTS_SIGNALED); + } + + result = 1; + +ep_on_exit: + return result; + +ep_on_error: + + if (result == 1) + result = -1; + + ep_exit_error_handler (); +} + +bool +ds_ipc_listen ( + DiagnosticsIpc *ipc, + ds_ipc_error_callback_func callback) +{ + bool result = false; + + EP_ASSERT (ipc != NULL); + EP_ASSERT (ipc->mode == DS_IPC_CONNECTION_MODE_LISTEN); + if (ipc->mode != DS_IPC_CONNECTION_MODE_LISTEN) { + if (callback) + callback ("Cannot call Listen on a client connection", -1); + return false; + } + + if (ipc->is_listening) + return true; + + EP_ASSERT (ipc->pipe == INVALID_HANDLE_VALUE); + + const uint32_t in_buffer_size = 16 * 1024; + const uint32_t out_buffer_size = 16 * 1024; + + DS_ENTER_BLOCKING_PAL_SECTION; + ipc->pipe = CreateNamedPipeA ( + ipc->pipe_name, // pipe name + PIPE_ACCESS_DUPLEX | // read/write access + FILE_FLAG_OVERLAPPED, // async listening + PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, // message type pipe, message-read and blocking mode + PIPE_UNLIMITED_INSTANCES, // max. instances + out_buffer_size, // output buffer size + in_buffer_size, // input buffer size + 0, // default client time-out + NULL); // default security attribute + DS_EXIT_BLOCKING_PAL_SECTION; + + if (ipc->pipe == INVALID_HANDLE_VALUE) { + if (callback) + callback ("Failed to create an instance of a named pipe.", GetLastError()); + ep_raise_error (); + } + + EP_ASSERT (ipc->overlap.hEvent == INVALID_HANDLE_VALUE); + + ipc->overlap.hEvent = CreateEvent (NULL, true, false, NULL); + if (!ipc->overlap.hEvent) { + if (callback) + callback ("Failed to create overlap event", GetLastError()); + ep_raise_error (); + } + + if (ConnectNamedPipe (ipc->pipe, &ipc->overlap) == FALSE) { + const DWORD error_code = GetLastError (); + switch (error_code) { + case ERROR_IO_PENDING: + // There was a pending connection that can be waited on (will happen in poll) + case ERROR_PIPE_CONNECTED: + // Occurs when a client connects before the function is called. + // In this case, there is a connection between client and + // server, even though the function returned zero. + break; + + default: + if (callback) + callback ("A client process failed to connect.", error_code); + ep_raise_error (); + } + } + + ipc->is_listening = true; + result = true; + +ep_on_exit: + return result; + +ep_on_error: + ds_ipc_close (ipc, false, callback); + result = false; + ep_exit_error_handler (); +} + +DiagnosticsIpcStream * +ds_ipc_accept ( + DiagnosticsIpc *ipc, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (ipc != NULL); + EP_ASSERT (ipc->mode == DS_IPC_CONNECTION_MODE_LISTEN); + + DiagnosticsIpcStream *stream = NULL; + + // Start with quick none blocking completion check. + DWORD dummy = 0; + BOOL success = GetOverlappedResult ( + ipc->pipe, // handle + &ipc->overlap, // overlapped + &dummy, // throw-away dword + false); // wait till event signals + + if (!success && GetLastError () == ERROR_IO_INCOMPLETE) { + // IO still incomplete, wait for completion. + dummy = 0; + DS_ENTER_BLOCKING_PAL_SECTION; + success = GetOverlappedResult ( + ipc->pipe, // handle + &ipc->overlap, // overlapped + &dummy, // throw-away dword + true); // wait till event signals + DS_EXIT_BLOCKING_PAL_SECTION; + } + + if (!success) { + if (callback) + callback ("Failed to GetOverlappedResults for NamedPipe server", GetLastError()); + ep_raise_error (); + } + + // create new IpcStream using handle and reset the Server object so it can listen again + stream = ipc_stream_alloc (ipc->pipe, DS_IPC_CONNECTION_MODE_LISTEN); + ep_raise_error_if_nok (stream != NULL); + + // reset the server + ipc->pipe = INVALID_HANDLE_VALUE; + ipc->is_listening = false; + CloseHandle (ipc->overlap.hEvent); + memset(&ipc->overlap, 0, sizeof(OVERLAPPED)); // clear the overlapped objects state + ipc->overlap.hEvent = INVALID_HANDLE_VALUE; + + ep_raise_error_if_nok (ds_ipc_listen (ipc, callback) == true); + +ep_on_exit: + return stream; + +ep_on_error: + ds_ipc_stream_free (stream); + stream = NULL; + ep_exit_error_handler (); +} + +DiagnosticsIpcStream * +ds_ipc_connect ( + DiagnosticsIpc *ipc, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (ipc != NULL); + EP_ASSERT (ipc->mode == DS_IPC_CONNECTION_MODE_CONNECT); + + DiagnosticsIpcStream *stream = NULL; + HANDLE pipe = INVALID_HANDLE_VALUE; + + if (ipc->mode != DS_IPC_CONNECTION_MODE_CONNECT) { + if (callback) + callback ("Cannot call connect on a server connection", 0); + ep_raise_error (); + } + + DS_ENTER_BLOCKING_PAL_SECTION; + pipe = CreateFileA( + ipc->pipe_name, // pipe name + PIPE_ACCESS_DUPLEX, // read/write access + 0, // no sharing + NULL, // default security attributes + OPEN_EXISTING, // opens existing pipe + FILE_FLAG_OVERLAPPED, // overlapped + NULL); // no template file + DS_EXIT_BLOCKING_PAL_SECTION; + + if (pipe == INVALID_HANDLE_VALUE) { + if (callback) + callback ("Failed to connect to named pipe.", GetLastError ()); + ep_raise_error (); + } + + stream = ipc_stream_alloc (pipe, ipc->mode); + ep_raise_error_if_nok (stream); + + pipe = INVALID_HANDLE_VALUE; + +ep_on_exit: + return stream; + +ep_on_error: + ds_ipc_stream_free (stream); + stream = NULL; + + if (pipe != INVALID_HANDLE_VALUE) { + CloseHandle (pipe); + } + + ep_exit_error_handler (); +} + +void +ds_ipc_close ( + DiagnosticsIpc *ipc, + bool is_shutdown, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (ipc != NULL); + + // don't attempt cleanup on shutdown and let the OS handle it + if (is_shutdown) { + if (callback) + callback ("Closing without cleaning underlying handles", 100); + return; + } + + if (ipc->pipe != INVALID_HANDLE_VALUE) { + if (ipc->mode == DS_IPC_CONNECTION_MODE_LISTEN) { + BOOL success_disconnect = FALSE; + DS_ENTER_BLOCKING_PAL_SECTION; + success_disconnect = DisconnectNamedPipe (ipc->pipe); + DS_EXIT_BLOCKING_PAL_SECTION; + if (success_disconnect != TRUE && callback) + callback ("Failed to disconnect NamedPipe", GetLastError()); + } + + const BOOL success_close_pipe = CloseHandle (ipc->pipe); + if (success_close_pipe != TRUE && callback) + callback ("Failed to close pipe handle", GetLastError()); + ipc->pipe = INVALID_HANDLE_VALUE; + } + + if (ipc->overlap.hEvent != INVALID_HANDLE_VALUE) { + const BOOL success_close_event = CloseHandle (ipc->overlap.hEvent); + if (success_close_event != TRUE && callback) + callback ("Failed to close overlap event handle", GetLastError()); + memset(&ipc->overlap, 0, sizeof(OVERLAPPED)); // clear the overlapped objects state + ipc->overlap.hEvent = INVALID_HANDLE_VALUE; + } +} + +int32_t +ds_ipc_to_string ( + DiagnosticsIpc *ipc, + ep_char8_t *buffer, + uint32_t buffer_len) +{ + EP_ASSERT (ipc != NULL); + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len <= DS_IPC_MAX_TO_STRING_LEN); + return ep_rt_utf8_string_snprintf (buffer, buffer_len, "{ _hPipe = %d, _oOverlap.hEvent = %d }", ipc->pipe, ipc->overlap.hEvent); +} + +/* + * DiagnosticsIpcStream. + */ + +static +void +ipc_stream_free_func (void *object) +{ + EP_ASSERT (object != NULL); + DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object; + ds_ipc_stream_free (ipc_stream); +} + +static +bool +ipc_stream_read_func ( + void *object, + uint8_t *buffer, + uint32_t bytes_to_read, + uint32_t *bytes_read, + uint32_t timeout_ms) +{ + EP_ASSERT (object != NULL); + EP_ASSERT (buffer != NULL); + EP_ASSERT (bytes_read != NULL); + + DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object; + DWORD read = 0; + LPOVERLAPPED overlap = &ipc_stream->overlap; + + bool success = ReadFile ( + ipc_stream->pipe, // handle to pipe + buffer, // buffer to receive data + bytes_to_read, // size of buffer + &read, // number of bytes read + overlap) != FALSE; // overlapped I/O + + if (!success) { + DWORD error = GetLastError (); + if (error == ERROR_IO_PENDING) { + // if we're waiting infinitely, only make one syscall + if (timeout_ms == DS_IPC_WIN32_INFINITE_TIMEOUT) { + DS_ENTER_BLOCKING_PAL_SECTION; + success = GetOverlappedResult ( + ipc_stream->pipe, // pipe + overlap, // overlapped + &read, // out actual number of bytes read + true) != FALSE; // block until async IO completes + DS_EXIT_BLOCKING_PAL_SECTION; + } else { + // Wait on overlapped IO event (triggers when async IO is complete regardless of success) + // or timeout + DS_ENTER_BLOCKING_PAL_SECTION; + DWORD wait = WaitForSingleObject (ipc_stream->overlap.hEvent, (DWORD)timeout_ms); + if (wait == WAIT_OBJECT_0) { + // async IO compelted, get the result + success = GetOverlappedResult ( + ipc_stream->pipe, // pipe + overlap, // overlapped + &read, // out actual number of bytes read + true) != FALSE; // block until async IO completes + } else { + // We either timed out or something else went wrong. + // For any error, attempt to cancel IO and ensure the cancel happened + if (CancelIoEx (ipc_stream->pipe, overlap) != FALSE) { + // check if the async write beat the cancellation + success = GetOverlappedResult ( + ipc_stream->pipe, // pipe + overlap, // overlapped + &read, // out actual number of bytes read + true) != FALSE; // block until async IO completes + // Failure here isn't recoverable, so return as such + } + } + DS_EXIT_BLOCKING_PAL_SECTION; + } + } + } + + *bytes_read = (uint32_t)read; + return success; +} + +static +bool +ipc_stream_write_func ( + void *object, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written, + uint32_t timeout_ms) +{ + EP_ASSERT (object != NULL); + EP_ASSERT (buffer != NULL); + EP_ASSERT (bytes_written != NULL); + + DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object; + DWORD written = 0; + LPOVERLAPPED overlap = &ipc_stream->overlap; + + bool success = WriteFile ( + ipc_stream->pipe, // handle to pipe + buffer, // buffer to write from + bytes_to_write, // number of bytes to write + &written, // number of bytes written + overlap) != FALSE; // overlapped I/O + + if (!success) { + DWORD error = GetLastError (); + if (error == ERROR_IO_PENDING) { + // if we're waiting infinitely, only make one syscall + if (timeout_ms == DS_IPC_WIN32_INFINITE_TIMEOUT) { + DS_ENTER_BLOCKING_PAL_SECTION; + success = GetOverlappedResult ( + ipc_stream->pipe, // pipe + overlap, // overlapped + &written, // out actual number of bytes written + true) != FALSE; // block until async IO completes + DS_EXIT_BLOCKING_PAL_SECTION; + } else { + // Wait on overlapped IO event (triggers when async IO is complete regardless of success) + // or timeout + DS_ENTER_BLOCKING_PAL_SECTION; + DWORD wait = WaitForSingleObject (ipc_stream->overlap.hEvent, (DWORD)timeout_ms); + if (wait == WAIT_OBJECT_0) { + // async IO compelted, get the result + success = GetOverlappedResult ( + ipc_stream->pipe, // pipe + overlap, // overlapped + &written, // out actual number of bytes written + true) != FALSE; // block until async IO completes + } else { + // We either timed out or something else went wrong. + // For any error, attempt to cancel IO and ensure the cancel happened + if (CancelIoEx (ipc_stream->pipe, overlap) != FALSE) { + // check if the async write beat the cancellation + success = GetOverlappedResult ( + ipc_stream->pipe, // pipe + overlap, // overlapped + &written, // out actual number of bytes written + true) != FALSE; // block until async IO completes + // Failure here isn't recoverable, so return as such + } + } + DS_EXIT_BLOCKING_PAL_SECTION; + } + } + } + + *bytes_written = (uint32_t)written; + return success; +} + +static +bool +ipc_stream_flush_func (void *object) +{ + EP_ASSERT (object != NULL); + + DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object; + bool success = false; + + DS_ENTER_BLOCKING_PAL_SECTION; + success = FlushFileBuffers (ipc_stream->pipe) != FALSE; + DS_EXIT_BLOCKING_PAL_SECTION; + + // TODO: Add error handling. + return success; +} + +static +bool +ipc_stream_close_func (void *object) +{ + EP_ASSERT (object != NULL); + DiagnosticsIpcStream *ipc_stream = (DiagnosticsIpcStream *)object; + return ds_ipc_stream_close (ipc_stream, NULL); +} + +static IpcStreamVtable ipc_stream_vtable = { + ipc_stream_free_func, + ipc_stream_read_func, + ipc_stream_write_func, + ipc_stream_flush_func, + ipc_stream_close_func }; + +static +DiagnosticsIpcStream * +ipc_stream_alloc ( + HANDLE pipe, + DiagnosticsIpcConnectionMode mode) +{ + DiagnosticsIpcStream *instance = ep_rt_object_alloc (DiagnosticsIpcStream); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ep_ipc_stream_init (&instance->stream, &ipc_stream_vtable) != NULL); + + instance->pipe = pipe; + instance->mode = mode; + + // All memory zeroed on alloc. + //memset (&instance->overlap, 0, sizeof (OVERLAPPED)); + + instance->overlap.hEvent = CreateEvent (NULL, true, false, NULL); + +ep_on_exit: + return instance; + +ep_on_error: + ds_ipc_stream_free (instance); + instance = NULL; + ep_exit_error_handler (); +} + +IpcStream * +ds_ipc_stream_get_stream_ref (DiagnosticsIpcStream *ipc_stream) +{ + return &ipc_stream->stream; +} + +void +ds_ipc_stream_free (DiagnosticsIpcStream *ipc_stream) +{ + ep_return_void_if_nok (ipc_stream != NULL); + ds_ipc_stream_close (ipc_stream, NULL); + ep_rt_object_free (ipc_stream); +} + +bool +ds_ipc_stream_read ( + DiagnosticsIpcStream *ipc_stream, + uint8_t *buffer, + uint32_t bytes_to_read, + uint32_t *bytes_read, + uint32_t timeout_ms) +{ + return ipc_stream_read_func ( + ipc_stream, + buffer, + bytes_to_read, + bytes_read, + timeout_ms); +} + +bool +ds_ipc_stream_write ( + DiagnosticsIpcStream *ipc_stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written, + uint32_t timeout_ms) +{ + return ipc_stream_write_func ( + ipc_stream, + buffer, + bytes_to_write, + bytes_written, + timeout_ms); +} + +bool +ds_ipc_stream_flush (DiagnosticsIpcStream *ipc_stream) +{ + return ipc_stream_flush_func (ipc_stream); +} + +bool +ds_ipc_stream_close ( + DiagnosticsIpcStream *ipc_stream, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (ipc_stream != NULL); + + if (ipc_stream->pipe != INVALID_HANDLE_VALUE) { + ds_ipc_stream_flush (ipc_stream); + if (ipc_stream->mode == DS_IPC_CONNECTION_MODE_LISTEN) { + BOOL success_disconnect = FALSE; + DS_ENTER_BLOCKING_PAL_SECTION; + success_disconnect = DisconnectNamedPipe (ipc_stream->pipe); + DS_EXIT_BLOCKING_PAL_SECTION; + if (success_disconnect != TRUE && callback) + callback ("Failed to disconnect NamedPipe", GetLastError()); + } + + const BOOL success_close_pipe = CloseHandle (ipc_stream->pipe); + if (success_close_pipe != TRUE && callback) + callback ("Failed to close pipe handle", GetLastError()); + ipc_stream->pipe = INVALID_HANDLE_VALUE; + } + + if (ipc_stream->overlap.hEvent != INVALID_HANDLE_VALUE) { + const BOOL success_close_event = CloseHandle (ipc_stream->overlap.hEvent); + if (success_close_event != TRUE && callback) + callback ("Failed to close overlapped event handle", GetLastError()); + memset(&ipc_stream->overlap, 0, sizeof(OVERLAPPED)); // clear the overlapped objects state + ipc_stream->overlap.hEvent = INVALID_HANDLE_VALUE; + } + + ipc_stream->is_test_reading = false; + + return true; +} + +int32_t +ds_ipc_stream_to_string ( + DiagnosticsIpcStream *ipc_stream, + ep_char8_t *buffer, + uint32_t buffer_len) +{ + EP_ASSERT (ipc_stream != NULL); + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len <= DS_IPC_MAX_TO_STRING_LEN); + return ep_rt_utf8_string_snprintf (buffer, buffer_len, "{ _hPipe = %d, _oOverlap.hEvent = %d }", ipc_stream->pipe, ipc_stream->overlap.hEvent); +} + +#endif /* !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* HOST_WIN32 */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef DS_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_diagnostics_ipc_win32; +const char quiet_linker_empty_file_warning_diagnostics_ipc_win32 = 0; +#endif diff --git a/src/mono/mono/eventpipe/ds-ipc-win32.h b/src/mono/mono/eventpipe/ds-ipc-win32.h new file mode 100644 index 00000000000000..35f157cb684ef0 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-ipc-win32.h @@ -0,0 +1,70 @@ +#ifndef __DIAGNOSTICS_IPC_WIN32_H__ +#define __DIAGNOSTICS_IPC_WIN32_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#ifdef HOST_WIN32 +#include "ds-rt-config.h" +#include "ds-types.h" +#include "ds-ipc.h" +#include "ep-stream.h" + +#undef DS_IMPL_GETTER_SETTER +#ifdef DS_IMPL_IPC_WIN32_GETTER_SETTER +#define DS_IMPL_GETTER_SETTER +#endif +#include "ds-getter-setter.h" + +#include + +/* + * DiagnosticsIpc. + */ + +#define DS_IPC_WIN32_MAX_NAMED_PIPE_LEN 256 +#define DS_IPC_WIN32_INFINITE_TIMEOUT INFINITE + +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_IPC_WIN32_GETTER_SETTER) +struct _DiagnosticsIpc { +#else +struct _DiagnosticsIpc_Internal { +#endif + ep_char8_t pipe_name [DS_IPC_WIN32_MAX_NAMED_PIPE_LEN]; + OVERLAPPED overlap; + HANDLE pipe; + bool is_listening; + DiagnosticsIpcConnectionMode mode; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_IPC_WIN32_GETTER_SETTER) +struct _DiagnosticsIpc { + uint8_t _internal [sizeof (struct _DiagnosticsIpc_Internal)]; +}; +#endif + +/* + * DiagnosticsIpcStream. + */ + +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_IPC_WIN32_GETTER_SETTER) +struct _DiagnosticsIpcStream { +#else +struct _DiagnosticsIpcStream_Internal { +#endif + IpcStream stream; + OVERLAPPED overlap; + HANDLE pipe; + bool is_test_reading; + DiagnosticsIpcConnectionMode mode; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_IPC_WIN32_GETTER_SETTER) +struct _DiagnosticsIpcStream { + uint8_t _internal [sizeof (struct _DiagnosticsIpcStream_Internal)]; +}; +#endif + +#endif /* HOST_WIN32 */ +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_IPC_WIN32_H__ */ diff --git a/src/mono/mono/eventpipe/ds-ipc.c b/src/mono/mono/eventpipe/ds-ipc.c new file mode 100644 index 00000000000000..02da2e5fb71310 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-ipc.c @@ -0,0 +1,830 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#if !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) + +#define DS_IMPL_IPC_GETTER_SETTER +#include "ds-ipc.h" +#include "ds-protocol.h" +#include "ds-rt.h" + +/* + * Globals and volatile access functions. + */ + +static volatile uint32_t _ds_shutting_down_state = 0; +static ds_rt_port_array_t _ds_port_array = { 0 }; + +// set this in get_next_available_stream, and then expose a callback that +// allows us to track which connections have sent their ResumeRuntime commands +static DiagnosticsPort *_ds_current_port = NULL; + +static +inline +bool +load_shutting_down_state (void) +{ + return (ep_rt_volatile_load_uint32_t (&_ds_shutting_down_state) != 0) ? true : false; +} + +static +inline +void +store_shutting_down_state (bool state) +{ + ep_rt_volatile_store_uint32_t (&_ds_shutting_down_state, state ? 1 : 0); +} + +/* + * Forward declares of all static functions. + */ + +static +uint32_t +ipc_stream_factory_get_next_timeout (uint32_t current_timout_ms); + +static +void +ipc_stream_factory_split_port_config ( + ep_char8_t *config, + const ep_char8_t *delimiters, + ds_rt_port_config_array_t *config_array); + +static +bool +ipc_stream_factory_build_and_add_port ( + DiagnosticsPortBuilder *builder, + ds_ipc_error_callback_func callback); + +static +void +ipc_log_poll_handles (ds_rt_ipc_poll_handle_array_t *ipc_poll_handles); + +static +void +connect_port_free_func (void *object); + +static +bool +connect_port_get_ipc_poll_handle_func ( + void *object, + DiagnosticsIpcPollHandle *handle, + ds_ipc_error_callback_func callback); + +static +DiagnosticsIpcStream * +connect_port_get_connected_stream_func ( + void *object, + ds_ipc_error_callback_func callback); + +static +void +connect_port_reset ( + void *object, + ds_ipc_error_callback_func callback); + +static +void +listen_port_free_func (void *object); + +static +bool +listen_port_get_ipc_poll_handle_func ( + void *object, + DiagnosticsIpcPollHandle *handle, + ds_ipc_error_callback_func callback); + +static +DiagnosticsIpcStream * +listen_port_get_connected_stream_func ( + void *object, + ds_ipc_error_callback_func callback); + +static +void +listen_port_reset ( + void *object, + ds_ipc_error_callback_func callback); + +/* + * IpcStreamFactory. + */ + +static +inline +uint32_t +ipc_stream_factory_get_next_timeout (uint32_t current_timeout_ms) +{ + if (current_timeout_ms == DS_IPC_POLL_TIMEOUT_INFINITE) + return DS_IPC_POLL_TIMEOUT_MIN_MS; + else + return (current_timeout_ms >= DS_IPC_POLL_TIMEOUT_MAX_MS) ? + DS_IPC_POLL_TIMEOUT_MAX_MS : + (uint32_t)((float)current_timeout_ms * DS_IPC_POLL_TIMEOUT_FALLOFF_FACTOR); +} + +static +void +ipc_stream_factory_split_port_config ( + ep_char8_t *config, + const ep_char8_t *delimiters, + ds_rt_port_config_array_t *config_array) +{ + ep_char8_t *part = NULL; + ep_char8_t *context = NULL; + ep_char8_t *cursor = config; + + EP_ASSERT (config != NULL); + EP_ASSERT (context != NULL); + EP_ASSERT (cursor != NULL); + + part = ep_rt_utf8_string_strtok (cursor, delimiters, &context); + while (part) { + ds_rt_port_config_array_append (config_array, part); + part = ep_rt_utf8_string_strtok (NULL, delimiters, &context); + } +} + +static +bool +ipc_stream_factory_build_and_add_port ( + DiagnosticsPortBuilder *builder, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (builder != NULL); + EP_ASSERT (callback != NULL); + + bool success = false; + DiagnosticsIpc *ipc = NULL; + + if (builder->type == DS_PORT_TYPE_LISTEN) { + ipc = ds_ipc_alloc (builder->path, DS_IPC_CONNECTION_MODE_LISTEN, callback); + ep_raise_error_if_nok (ipc != NULL); + ep_raise_error_if_nok (ds_ipc_listen (ipc, callback) == true); + ds_rt_port_array_append (&_ds_port_array, (DiagnosticsPort *)ds_listen_port_alloc (ipc, builder)); + success = true; + } else if (builder->type == DS_PORT_TYPE_CONNECT) { + ipc = ds_ipc_alloc (builder->path, DS_IPC_CONNECTION_MODE_CONNECT, callback); + ep_raise_error_if_nok (ipc != NULL); + ep_raise_error_if_nok (ds_ipc_listen (ipc, callback) == true); + ds_rt_port_array_append (&_ds_port_array, (DiagnosticsPort *)ds_connect_port_alloc (ipc, builder)); + success = true; + } + +ep_on_exit: + return success; + +ep_on_error: + ds_ipc_free (ipc); + success = false; + ep_exit_error_handler (); +} + +static +void +ipc_log_poll_handles (ds_rt_ipc_poll_handle_array_t *ipc_poll_handles) +{ + // TODO: Should this be debug only? + ds_rt_ipc_poll_handle_array_iterator_t ipc_poll_handles_iterator; + DiagnosticsIpcPollHandle ipc_poll_handle; + ep_char8_t buffer [DS_IPC_MAX_TO_STRING_LEN]; + uint32_t connection_id = 0; + + ds_rt_ipc_poll_handle_array_iterator_begin (ipc_poll_handles, &ipc_poll_handles_iterator); + while (!ds_rt_ipc_poll_handle_array_iterator_end (ipc_poll_handles, &ipc_poll_handles_iterator)) { + ipc_poll_handle = ds_rt_ipc_poll_handle_array_iterator_value (&ipc_poll_handles_iterator); + if (ipc_poll_handle.ipc) { + if (!(ds_ipc_to_string (ipc_poll_handle.ipc, buffer, EP_ARRAY_SIZE (buffer)) > 0)) + buffer [0] = '\0'; + DS_LOG_INFO_2 ("\tSERVER IpcPollHandle[%d] = %s\n", connection_id, buffer); + } else { + if (!(ds_ipc_stream_to_string (ipc_poll_handle.stream, buffer, EP_ARRAY_SIZE (buffer)) > 0)) + buffer [0] = '\0'; + DS_LOG_INFO_2 ("\tCLIENT IpcPollHandle[%d] = %s\n", connection_id, buffer); + } + ds_rt_ipc_poll_handle_array_iterator_next (ipc_poll_handles, &ipc_poll_handles_iterator); + connection_id++; + } +} + +void +ds_ipc_stream_factory_init (void) +{ + ds_rt_port_array_alloc (&_ds_port_array); +} + +void +ds_ipc_stream_factory_fini (void) +{ + ds_rt_port_array_free (&_ds_port_array); +} + +bool +ds_ipc_stream_factory_configure (ds_ipc_error_callback_func callback) +{ + bool success = true; + + ep_char8_t *ports = ds_rt_config_value_get_ports (); + if (ports) { + ds_rt_port_config_array_t port_configs; + ds_rt_port_config_array_t port_config_parts; + + ds_rt_port_array_alloc (&port_configs); + ds_rt_port_array_alloc (&port_config_parts); + + ipc_stream_factory_split_port_config (ports, ";", &port_configs); + + ep_char8_t **port_configs_data = ds_rt_port_config_array_data (&port_configs); + size_t port_configs_size = ds_rt_port_config_array_size (&port_configs); + for (size_t port_configs_index = 0; port_configs_index < port_configs_size; ++port_configs_index) { + ep_char8_t *port_config = port_configs_data [port_configs_index]; + DS_LOG_INFO_1 ("ds_ipc_stream_factory_configure - Attempted to create Diagnostic Port from \"%s\".\n", port_config ? port_config : ""); + if (port_config) { + ds_rt_port_config_array_clear (&port_config_parts, NULL); + ipc_stream_factory_split_port_config (port_config, ",", &port_config_parts); + + ep_char8_t **port_config_parts_data = ds_rt_port_config_array_data (&port_config_parts); + size_t port_config_parts_size = ds_rt_port_config_array_size (&port_config_parts); + if (port_config_parts_size != 0) { + DiagnosticsPortBuilder port_builder; + ds_port_builder_init (&port_builder); + for (size_t port_config_parts_index = 0; port_config_parts_index < port_config_parts_size; ++port_config_parts_index) { + if (port_config_parts_index == 0) + port_builder.path = port_config_parts_data [port_config_parts_index]; + else + ds_port_builder_set_tag (&port_builder, port_config_parts_data [port_config_parts_index]); + } + if (!ep_rt_utf8_string_is_null_or_empty (port_builder.path)) { + // Ignore listen type (see conversation in https://github.com/dotnet/runtime/pull/40499 for details) + if (port_builder.type != DS_PORT_TYPE_LISTEN) { + const bool build_success = ipc_stream_factory_build_and_add_port (&port_builder, callback); + DS_LOG_INFO_1 ("ds_ipc_stream_factory_configure - Diagnostic Port creation succeeded? %d \n", build_success); + success &= build_success; + } else { + DS_LOG_INFO_0 ("ds_ipc_stream_factory_configure - Ignoring LISTEN port configuration \n"); + } + } else { + DS_LOG_INFO_0("ds_ipc_stream_factory_configure - Ignoring port configuration with empty address\n"); + } + ds_port_builder_fini (&port_builder); + } else { + success &= false; + } + } + } + + ds_rt_port_array_free (&port_config_parts); + ds_rt_port_array_free (&port_configs); + } + + // create the default listen port + int32_t port_suspend = ds_rt_config_value_get_default_port_suspend (); + + DiagnosticsPortBuilder default_port_builder; + ds_port_builder_init (&default_port_builder); + + default_port_builder.path = NULL; + default_port_builder.suspend_mode = port_suspend > 0 ? DS_PORT_SUSPEND_MODE_SUSPEND : DS_PORT_SUSPEND_MODE_NOSUSPEND; + default_port_builder.type = DS_PORT_TYPE_LISTEN; + + success &= ipc_stream_factory_build_and_add_port (&default_port_builder, callback); + + ds_port_builder_fini (&default_port_builder); + + ep_rt_utf8_string_free (ports); + + return success; +} + +DiagnosticsIpcStream * +ds_ipc_stream_factory_get_next_available_stream (ds_ipc_error_callback_func callback) +{ + DS_LOG_INFO_0 ("ds_ipc_stream_factory_get_next_available_stream - ENTER"); + + DiagnosticsIpcStream *stream = NULL; + ds_rt_ipc_poll_handle_array_t ipc_poll_handles; + DiagnosticsIpcPollHandle ipc_poll_handle; + ds_rt_port_array_t *ports = &_ds_port_array; + DiagnosticsPort *port = NULL; + + int32_t poll_timeout_ms = DS_IPC_POLL_TIMEOUT_INFINITE; + bool connect_success = true; + uint32_t poll_attempts = 0; + + ds_rt_ipc_poll_handle_array_alloc (&ipc_poll_handles); + + while (!stream) { + connect_success = true; + ds_rt_port_array_iterator_t ports_iterator; + ds_rt_port_array_iterator_begin (ports, &ports_iterator); + while (!ds_rt_port_array_iterator_end (ports, &ports_iterator)) { + port = ds_rt_port_array_iterator_value (&ports_iterator); + if (ds_port_get_ipc_poll_handle_vcall (port, &ipc_poll_handle, callback)) + ds_rt_ipc_poll_handle_array_append (&ipc_poll_handles, ipc_poll_handle); + else + connect_success = false; + + ds_rt_port_array_iterator_next (ports, &ports_iterator); + } + + poll_timeout_ms = connect_success ? + DS_IPC_POLL_TIMEOUT_INFINITE : + ipc_stream_factory_get_next_timeout (poll_timeout_ms); + + poll_attempts++; + DS_LOG_INFO_2 ("ds_ipc_stream_factory_get_next_available_stream - Poll attempt: %d, timeout: %dms.\n", poll_attempts, poll_timeout_ms); + + ipc_log_poll_handles (&ipc_poll_handles); + + int32_t ret_val = ds_ipc_poll (&ipc_poll_handles, poll_timeout_ms, callback); + bool saw_error = false; + + if (ret_val != 0) { + uint32_t connection_id = 0; + ds_rt_ipc_poll_handle_array_iterator_t ipc_poll_handles_iterator; + ds_rt_ipc_poll_handle_array_iterator_begin (&ipc_poll_handles, &ipc_poll_handles_iterator); + while (!ds_rt_ipc_poll_handle_array_iterator_end (&ipc_poll_handles, &ipc_poll_handles_iterator)) { + ipc_poll_handle = ds_rt_ipc_poll_handle_array_iterator_value (&ipc_poll_handles_iterator); + port = (DiagnosticsPort *)ipc_poll_handle.user_data; + switch (ipc_poll_handle.events) { + case DS_IPC_POLL_EVENTS_HANGUP: + EP_ASSERT (port != NULL); + ds_port_reset_vcall (port, callback); + DS_LOG_INFO_2 ("ds_ipc_stream_factory_get_next_available_stream - HUP :: Poll attempt: %d, connection %d hung up. Connect is reset.\n", nPollAttempts, connection_id); + poll_timeout_ms = DS_IPC_POLL_TIMEOUT_MIN_MS; + break; + case DS_IPC_POLL_EVENTS_SIGNALED: + EP_ASSERT (port != NULL); + if (!stream) { // only use first signaled stream; will get others on subsequent calls + stream = ds_port_get_connected_stream_vcall (port, callback); + _ds_current_port = port; + } + DS_LOG_INFO_2 ("ds_ipc_stream_factory_get_next_available_stream - SIG :: Poll attempt: %d, connection %d signalled.\n", nPollAttempts, connection_id); + break; + case DS_IPC_POLL_EVENTS_ERR: + ds_port_reset_vcall ((DiagnosticsPort *)ipc_poll_handle.user_data, callback); + DS_LOG_INFO_2 ("ds_ipc_stream_factory_get_next_available_stream - ERR :: Poll attempt: %d, connection %d errored. Connection is reset.\n", nPollAttempts, connection_id); + saw_error = true; + break; + case DS_IPC_POLL_EVENTS_NONE: + DS_LOG_INFO_2 ("ds_ipc_stream_factory_get_next_available_stream - NON :: Poll attempt: %d, connection %d had no events.\n", nPollAttempts, connection_id); + break; + default: + DS_LOG_INFO_2 ("ds_ipc_stream_factory_get_next_available_stream - UNK :: Poll attempt: %d, connection %d had invalid PollEvent.\n", nPollAttempts, connection_id); + saw_error = true; + break; + } + + ds_rt_ipc_poll_handle_array_iterator_next (&ipc_poll_handles, &ipc_poll_handles_iterator); + connection_id++; + } + } + + if (!stream && saw_error) { + _ds_current_port = NULL; + ep_raise_error (); + } + + // clear the view. + ds_rt_ipc_poll_handle_array_clear (&ipc_poll_handles, NULL); + } + +ep_on_exit: + DS_LOG_INFO_2 ("ds_ipc_stream_factory_get_next_available_stream - EXIT :: Poll attempt: %d, stream using handle %d.\n", poll_attempts, ds_ipc_stream_get_handle_int32_t (stream)); + return stream; + +ep_on_error: + stream = NULL; + ep_exit_error_handler (); +} + +void +ds_ipc_stream_factory_resume_current_port (void) +{ + if (_ds_current_port != NULL) + _ds_current_port->has_resumed_runtime = true; +} + +bool +ds_ipc_stream_factory_any_suspended_ports (void) +{ + bool any_suspended_ports = false; + ds_rt_port_array_iterator_t iterator; + ds_rt_port_array_iterator_begin (&_ds_port_array, &iterator); + while (!ds_rt_port_array_iterator_end (&_ds_port_array, &iterator)) { + DiagnosticsPort *port = ds_rt_port_array_iterator_value (&iterator); + any_suspended_ports |= !(port->suspend_mode == DS_PORT_SUSPEND_MODE_NOSUSPEND || port->has_resumed_runtime); + ds_rt_port_array_iterator_next (&_ds_port_array, &iterator); + } + return any_suspended_ports; +} + +bool +ds_ipc_stream_factory_has_active_ports (void) +{ + return !load_shutting_down_state () && + ds_rt_port_array_size (&_ds_port_array) > 0; +} + +void +ds_ipc_stream_factory_close_ports (ds_ipc_error_callback_func callback) +{ + ds_rt_port_array_iterator_t iterator; + ds_rt_port_array_iterator_begin (&_ds_port_array, &iterator); + while (!ds_rt_port_array_iterator_end (&_ds_port_array, &iterator)) { + ds_port_close (ds_rt_port_array_iterator_value (&iterator), false, callback); + ds_rt_port_array_iterator_next (&_ds_port_array, &iterator); + } +} + +void +ds_ipc_stream_factory_shutdown (ds_ipc_error_callback_func callback) +{ + if (load_shutting_down_state ()) + return; + + store_shutting_down_state (true); + + ds_rt_port_array_iterator_t iterator; + ds_rt_port_array_iterator_begin (&_ds_port_array, &iterator); + while (!ds_rt_port_array_iterator_end (&_ds_port_array, &iterator)) { + ds_port_close (ds_rt_port_array_iterator_value (&iterator), true, callback); + ds_port_free_vcall (ds_rt_port_array_iterator_value (&iterator)); + ds_rt_port_array_iterator_next (&_ds_port_array, &iterator); + } + + _ds_current_port = NULL; +} + +/* + * DiagnosticsPort. + */ + +DiagnosticsPort * +ds_port_init ( + DiagnosticsPort *port, + DiagnosticsPortVtable *vtable, + DiagnosticsIpc *ipc, + DiagnosticsPortBuilder *builder) +{ + EP_ASSERT (port != NULL); + EP_ASSERT (vtable != NULL); + EP_ASSERT (ipc != NULL); + EP_ASSERT (builder != NULL); + + port->vtable = vtable; + port->suspend_mode = builder->suspend_mode; + port->type = builder->type; + port->ipc = ipc; + port->stream = NULL; + port->has_resumed_runtime = false; + + return port; +} + +void +ds_port_fini (DiagnosticsPort *port) +{ + return; +} + +void +ds_port_free_vcall (DiagnosticsPort *port) +{ + ep_return_void_if_nok (port != NULL); + + EP_ASSERT (port->vtable != NULL); + DiagnosticsPortVtable *vtable = port->vtable; + + EP_ASSERT (vtable->free_func != NULL); + vtable->free_func (port); +} + +bool +ds_port_get_ipc_poll_handle_vcall ( + DiagnosticsPort *port, + DiagnosticsIpcPollHandle *handle, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (port != NULL); + EP_ASSERT (port->vtable != NULL); + + DiagnosticsPortVtable *vtable = port->vtable; + + EP_ASSERT (vtable->get_ipc_poll_handle_func != NULL); + return vtable->get_ipc_poll_handle_func (port, handle, callback); +} + +DiagnosticsIpcStream * +ds_port_get_connected_stream_vcall ( + DiagnosticsPort *port, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (port != NULL); + EP_ASSERT (port->vtable != NULL); + + DiagnosticsPortVtable *vtable = port->vtable; + + EP_ASSERT (vtable->get_connected_stream_func != NULL); + return vtable->get_connected_stream_func (port, callback); +} + +void +ds_port_reset_vcall ( + DiagnosticsPort *port, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (port != NULL); + EP_ASSERT (port->vtable != NULL); + + DiagnosticsPortVtable *vtable = port->vtable; + + EP_ASSERT (vtable->reset_func != NULL); + vtable->reset_func (port, callback); +} + +void +ds_port_close ( + DiagnosticsPort *port, + bool is_shutdown, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (port != NULL); + if (port->ipc) + ds_ipc_close (port->ipc, is_shutdown, callback); + if (port->stream && !is_shutdown) + ds_ipc_stream_close (port->stream, callback); +} + +DiagnosticsPortBuilder * +ds_port_builder_init (DiagnosticsPortBuilder *builder) +{ + EP_ASSERT (builder != NULL); + builder->path = NULL; + builder->suspend_mode = DS_PORT_SUSPEND_MODE_SUSPEND; + builder->type = DS_PORT_TYPE_CONNECT; + + return builder; +} + +void +ds_port_builder_fini (DiagnosticsPortBuilder *builder) +{ + return; +} + +void +ds_port_builder_set_tag ( + DiagnosticsPortBuilder *builder, + ep_char8_t *tag) +{ + EP_ASSERT (builder != NULL); + EP_ASSERT (tag != NULL); + + if (ep_rt_utf8_string_compare_ignore_case (tag, "listen") == 0) + builder->type = DS_PORT_TYPE_LISTEN; + else if (ep_rt_utf8_string_compare_ignore_case (tag, "connect") == 0) + builder->type = DS_PORT_TYPE_CONNECT; + else if (ep_rt_utf8_string_compare_ignore_case (tag, "nosuspend") == 0) + builder->suspend_mode = DS_PORT_SUSPEND_MODE_NOSUSPEND; + else if (ep_rt_utf8_string_compare_ignore_case (tag, "suspend") == 0) + builder->suspend_mode = DS_PORT_SUSPEND_MODE_SUSPEND; + else + // don't mutate if it's not a valid option + DS_LOG_INFO_1 ("ds_port_builder_set_tag - Unknown tag '%s'.\n", tag); +} + +/* + * DiagnosticsConnectPort. + */ + +static +void +connect_port_free_func (void *object) +{ + EP_ASSERT (object != NULL); + ds_connect_port_free ((DiagnosticsConnectPort *)object); +} + +static +bool +connect_port_get_ipc_poll_handle_func ( + void *object, + DiagnosticsIpcPollHandle *handle, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (object != NULL); + EP_ASSERT (handle != NULL); + + bool success = false; + DiagnosticsConnectPort *connect_port = (DiagnosticsConnectPort *)object; + DiagnosticsIpcStream *connection = NULL; + + DS_LOG_INFO_0 ("connect_port_get_ipc_poll_handle - ENTER.\n"); + + if (!connect_port->port.stream) { + DS_LOG_INFO_0 ("connect_port_get_ipc_poll_handle - cache was empty!\n"); + // cache is empty, reconnect, e.g., there was a disconnect + connection = ds_ipc_connect (connect_port->port.ipc, callback); + if (!connection) { + if (callback) + callback("Failed to connect to client connection", -1); + ep_raise_error (); + } + + ep_char8_t buffer [DS_IPC_MAX_TO_STRING_LEN]; + if (!(ds_ipc_stream_to_string (connection, buffer, EP_ARRAY_SIZE (buffer) > 0))) + buffer [0] = '\0'; + DS_LOG_INFO_1 ("connect_port_get_ipc_poll_handle - returned connection %s\n", buffer); + + if (!ds_icp_advertise_v1_send (connection)) { + if (callback) + callback("Failed to send advertise message", -1); + ep_raise_error (); + } + + //Transfer ownership. + connect_port->port.stream = connection; + connection = NULL; + } + + handle->ipc = NULL; + handle->stream = connect_port->port.stream; + handle->events = 0; + handle->user_data = object; + + success = true; + +ep_on_exit: + DS_LOG_INFO_0 ("connect_port_get_ipc_poll_handle - EXIT.\n"); + return success; + +ep_on_error: + ds_ipc_stream_free (connection); + success = false; + ep_exit_error_handler (); +} + +static +DiagnosticsIpcStream * +connect_port_get_connected_stream_func ( + void *object, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (object != NULL); + + DiagnosticsConnectPort *connect_port = (DiagnosticsConnectPort *)object; + DiagnosticsIpcStream *stream = connect_port->port.stream; + connect_port->port.stream = NULL; + return stream; +} + +static +void +connect_port_reset ( + void *object, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (object != NULL); + + DiagnosticsConnectPort *connect_port = (DiagnosticsConnectPort *)object; + ds_ipc_stream_free (connect_port->port.stream); + connect_port->port.stream = NULL; +} + +static DiagnosticsPortVtable connect_port_vtable = { + connect_port_free_func, + connect_port_get_ipc_poll_handle_func, + connect_port_get_connected_stream_func, + connect_port_reset }; + +DiagnosticsConnectPort * +ds_connect_port_alloc ( + DiagnosticsIpc *ipc, + DiagnosticsPortBuilder *builder) +{ + DiagnosticsConnectPort * instance = ep_rt_object_alloc (DiagnosticsConnectPort); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ds_port_init ( + (DiagnosticsPort *)instance, + &connect_port_vtable, + ipc, + builder) != NULL); + +ep_on_exit: + return instance; + +ep_on_error: + ds_connect_port_free (instance); + instance = NULL; + ep_exit_error_handler (); +} + +void +ds_connect_port_free (DiagnosticsConnectPort *connect_port) +{ + ep_return_void_if_nok (connect_port != NULL); + ds_port_fini (&connect_port->port); + ep_rt_object_free (connect_port); +} + +/* + * DiagnosticsListenPort. + */ + +static +void +listen_port_free_func (void *object) +{ + EP_ASSERT (object != NULL); + ds_listen_port_free ((DiagnosticsListenPort *)object); +} + +static +bool +listen_port_get_ipc_poll_handle_func ( + void *object, + DiagnosticsIpcPollHandle *handle, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (object != NULL); + EP_ASSERT (handle != NULL); + + DiagnosticsListenPort *listen_port = (DiagnosticsListenPort *)object; + + handle->ipc = listen_port->port.ipc; + handle->stream = NULL; + handle->events = 0; + handle->user_data = object; + + return true; +} + +static +DiagnosticsIpcStream * +listen_port_get_connected_stream_func ( + void *object, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (object != NULL); + + DiagnosticsListenPort *listen_port = (DiagnosticsListenPort *)object; + return ds_ipc_accept (listen_port->port.ipc, callback); +} + +static +void +listen_port_reset ( + void *object, + ds_ipc_error_callback_func callback) +{ + EP_ASSERT (object != NULL); + return; +} + +static DiagnosticsPortVtable listen_port_vtable = { + listen_port_free_func, + listen_port_get_ipc_poll_handle_func, + listen_port_get_connected_stream_func, + listen_port_reset }; + +DiagnosticsListenPort * +ds_listen_port_alloc ( + DiagnosticsIpc *ipc, + DiagnosticsPortBuilder *builder) +{ + DiagnosticsListenPort * instance = ep_rt_object_alloc (DiagnosticsListenPort); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ds_port_init ( + (DiagnosticsPort *)instance, + &listen_port_vtable, + ipc, + builder) != NULL); + +ep_on_exit: + return instance; + +ep_on_error: + ds_listen_port_free (instance); + instance = NULL; + ep_exit_error_handler (); +} + +void +ds_listen_port_free (DiagnosticsListenPort *listen_port) +{ + ep_return_void_if_nok (listen_port != NULL); + ds_port_fini (&listen_port->port); + ep_rt_object_free (listen_port); +} + +#endif /* !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef DS_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_diagnostics_ipc; +const char quiet_linker_empty_file_warning_diagnostics_ipc = 0; +#endif diff --git a/src/mono/mono/eventpipe/ds-ipc.h b/src/mono/mono/eventpipe/ds-ipc.h new file mode 100644 index 00000000000000..225bf878b220ee --- /dev/null +++ b/src/mono/mono/eventpipe/ds-ipc.h @@ -0,0 +1,321 @@ +#ifndef __DIAGNOSTICS_IPC_H__ +#define __DIAGNOSTICS_IPC_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#include "ds-types.h" + +#undef DS_IMPL_GETTER_SETTER +#ifdef DS_IMPL_IPC_GETTER_SETTER +#define DS_IMPL_GETTER_SETTER +#endif +#include "ds-getter-setter.h" + +#define DS_IPC_MAX_TO_STRING_LEN 128 + +/* + * IpcStreamFactory. + */ + +void +ds_ipc_stream_factory_init (void); + +void +ds_ipc_stream_factory_fini (void); + +bool +ds_ipc_stream_factory_configure (ds_ipc_error_callback_func callback); + +DiagnosticsIpcStream * +ds_ipc_stream_factory_get_next_available_stream (ds_ipc_error_callback_func callback); + +void +ds_ipc_stream_factory_resume_current_port (void); + +bool +ds_ipc_stream_factory_any_suspended_ports (void); + +bool +ds_ipc_stream_factory_has_active_ports (void); + +void +ds_ipc_stream_factory_close_ports (ds_ipc_error_callback_func callback); + +void +ds_ipc_stream_factory_shutdown (ds_ipc_error_callback_func callback); + +/* + * DiagnosticsPort. + */ + +typedef void (*DiagnosticsPortFreeFunc)(void *object); +typedef bool (*DiagnosticsPortGetIPCPollHandleFunc)(void *object, DiagnosticsIpcPollHandle *handle, ds_ipc_error_callback_func callback); +typedef DiagnosticsIpcStream *(*DiagnosticsPortGetConnectedStreamFunc)(void *object, ds_ipc_error_callback_func callback); +typedef void (*DiagnosticsPortResetFunc)(void *object, ds_ipc_error_callback_func callback); + +struct _DiagnosticsPortVtable { + DiagnosticsPortFreeFunc free_func; + DiagnosticsPortGetIPCPollHandleFunc get_ipc_poll_handle_func; + DiagnosticsPortGetConnectedStreamFunc get_connected_stream_func; + DiagnosticsPortResetFunc reset_func; +}; + +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_IPC_GETTER_SETTER) +struct _DiagnosticsPort { +#else +struct _DiagnosticsPort_Internal { +#endif + DiagnosticsPortVtable *vtable; + DiagnosticsIpc *ipc; + DiagnosticsIpcStream *stream; + bool has_resumed_runtime; + DiagnosticsPortSuspendMode suspend_mode; + DiagnosticsPortType type; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_IPC_GETTER_SETTER) +struct _DiagnosticsPort { + uint8_t _internal [sizeof (struct _DiagnosticsPort_Internal)]; +}; +#endif + +DiagnosticsPort * +ds_port_init ( + DiagnosticsPort *port, + DiagnosticsPortVtable *vtable, + DiagnosticsIpc *ipc, + DiagnosticsPortBuilder *builder); + +void +ds_port_fini (DiagnosticsPort *port); + +void +ds_port_free_vcall (DiagnosticsPort *port); + +// returns a pollable handle and performs any preparation required +// e.g., as a side-effect, will connect and advertise on reverse connections +bool +ds_port_get_ipc_poll_handle_vcall ( + DiagnosticsPort *port, + DiagnosticsIpcPollHandle *handle, + ds_ipc_error_callback_func callback); + +// Returns the signaled stream in a usable state +DiagnosticsIpcStream * +ds_port_get_connected_stream_vcall ( + DiagnosticsPort * port, + ds_ipc_error_callback_func callback); + +// Resets the connection in the event of a hangup +void +ds_port_reset_vcall ( + DiagnosticsPort * port, + ds_ipc_error_callback_func callback); + +// closes the underlying connections +// only performs minimal cleanup if isShutdown==true +void +ds_port_close ( + DiagnosticsPort * port, + bool is_shutdown, + ds_ipc_error_callback_func callback); + +/* + * DiagnosticsPortBuilder. + */ + +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_IPC_GETTER_SETTER) +struct _DiagnosticsPortBuilder { +#else +struct _DiagnosticsPortBuilder_Internal { +#endif + ep_char8_t *path; + DiagnosticsPortSuspendMode suspend_mode; + DiagnosticsPortType type; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_IPC_GETTER_SETTER) +struct _DiagnosticsPortBuilder { + uint8_t _internal [sizeof (struct _DiagnosticsPortBuilder_Internal)]; +}; +#endif + +DiagnosticsPortBuilder * +ds_port_builder_init (DiagnosticsPortBuilder *builder); + +void +ds_port_builder_fini (DiagnosticsPortBuilder *builder); + +void +ds_port_builder_set_tag ( + DiagnosticsPortBuilder *builder, + ep_char8_t *tag); + +/* + * DiagnosticsConnectPort. + */ + +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_IPC_GETTER_SETTER) +struct _DiagnosticsConnectPort { +#else +struct _DiagnosticsConnectPort_Internal { +#endif + DiagnosticsPort port; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_IPC_GETTER_SETTER) +struct _DiagnosticsConnectPort { + uint8_t _internal [sizeof (struct _DiagnosticsConnectPort_Internal)]; +}; +#endif + +DiagnosticsConnectPort * +ds_connect_port_alloc ( + DiagnosticsIpc *ipc, + DiagnosticsPortBuilder *builder); + +void +ds_connect_port_free (DiagnosticsConnectPort *listen_port); + +/* + * DiagnosticsListenPort. + */ + +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_IPC_GETTER_SETTER) +struct _DiagnosticsListenPort { +#else +struct _DiagnosticsListenPort_Internal { +#endif + DiagnosticsPort port; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_IPC_GETTER_SETTER) +struct _DiagnosticsListenPort { + uint8_t _internal [sizeof (struct _DiagnosticsListenPort_Internal)]; +}; +#endif + +DiagnosticsListenPort * +ds_listen_port_alloc ( + DiagnosticsIpc *ipc, + DiagnosticsPortBuilder *builder); + +void +ds_listen_port_free (DiagnosticsListenPort *listen_port); + +// PAL. + +/* + * DiagnosticsIpc. + */ + +int32_t +ds_ipc_get_handle_int32_t (DiagnosticsIpc *ipc); + +DiagnosticsIpc * +ds_ipc_alloc ( + const ep_char8_t *ipc_name, + DiagnosticsIpcConnectionMode mode, + ds_ipc_error_callback_func callback); + +void +ds_ipc_free (DiagnosticsIpc *ipc); + +// Poll +// Parameters: +// - IpcPollHandle * poll_handles: Array of IpcPollHandles to poll +// - uint32_t timeout_ms: The timeout in milliseconds for the poll (-1 == infinite) +// Returns: +// int32_t: -1 on error, 0 on timeout, >0 on successful poll +// Remarks: +// Check the events returned in revents for each IpcPollHandle to find the signaled handle. +// Signaled DiagnosticsIpcs can call accept() without blocking. +// Signaled IpcStreams can call read(...) without blocking. +// The caller is responsible for cleaning up "hung up" connections. +int32_t +ds_ipc_poll ( + ds_rt_ipc_poll_handle_array_t *poll_handles, + uint32_t timeout_ms, + ds_ipc_error_callback_func callback); + +// puts the DiagnosticsIpc into Listening Mode +// Re-entrant safe +bool +ds_ipc_listen ( + DiagnosticsIpc *ipc, + ds_ipc_error_callback_func callback); + +// produces a connected stream from a server-mode DiagnosticsIpc. +// Blocks until a connection is available. +DiagnosticsIpcStream * +ds_ipc_accept ( + DiagnosticsIpc *ipc, + ds_ipc_error_callback_func callback); + +// Connect to a server and returns a connected stream +DiagnosticsIpcStream * +ds_ipc_connect ( + DiagnosticsIpc *ipc, + ds_ipc_error_callback_func callback); + +// Closes an open IPC. +// Only attempts minimal cleanup if is_shutdown==true, i.e., +// unlinks Unix Domain Socket on Linux, no-op on Windows +void +ds_ipc_close ( + DiagnosticsIpc *ipc, + bool is_shutdown, + ds_ipc_error_callback_func callback); + +int32_t +ds_ipc_to_string ( + DiagnosticsIpc *ipc, + ep_char8_t *buffer, + uint32_t buffer_len); +/* + * DiagnosticsIpcStream. + */ + +int32_t +ds_ipc_stream_get_handle_int32_t (DiagnosticsIpcStream *ipc_stream); + +IpcStream * +ds_ipc_stream_get_stream_ref (DiagnosticsIpcStream *ipc_stream); + +void +ds_ipc_stream_free (DiagnosticsIpcStream *ipc_stream); + +bool +ds_ipc_stream_read ( + DiagnosticsIpcStream *ipc_stream, + uint8_t *buffer, + uint32_t bytes_to_read, + uint32_t *bytes_read, + uint32_t timeout_ms); + +bool +ds_ipc_stream_write ( + DiagnosticsIpcStream *ipc_stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written, + uint32_t timeout_ms); + +bool +ds_ipc_stream_flush (DiagnosticsIpcStream *ipc_stream); + +bool +ds_ipc_stream_close ( + DiagnosticsIpcStream *ipc_stream, + ds_ipc_error_callback_func callback); + +int32_t +ds_ipc_stream_to_string ( + DiagnosticsIpcStream *ipc_stream, + ep_char8_t *buffer, + uint32_t buffer_len); + +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_IPC_H__ */ diff --git a/src/mono/mono/eventpipe/ds-process-protocol.c b/src/mono/mono/eventpipe/ds-process-protocol.c new file mode 100644 index 00000000000000..1e2dfd66fe0abc --- /dev/null +++ b/src/mono/mono/eventpipe/ds-process-protocol.c @@ -0,0 +1,306 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#if !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) + +#define DS_IMPL_PROCESS_PROTOCOL_GETTER_SETTER +#include "ds-protocol.h" +#include "ds-process-protocol.h" +#include "ds-server.h" +#include "ep.h" +#include "ds-rt.h" +#include "ep-event-source.h" + +/* + * Forward declares of all static functions. + */ + +static +uint16_t +process_info_payload_get_size (DiagnosticsProcessInfoPayload *payload); + +static +bool +process_info_payload_flatten ( + void *payload, + uint8_t **buffer, + uint16_t *size); + +static +void +process_protocol_helper_get_process_info ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +static +void +process_protocol_helper_get_process_env ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +static +void +process_protocol_helper_resume_runtime_startup ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +static +void +process_protocol_helper_unknown_command ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +/* + * DiagnosticsProcessInfoPayload. + */ + +static +uint16_t +process_info_payload_get_size (DiagnosticsProcessInfoPayload *payload) +{ + // see IPC spec @ https://github.com/dotnet/diagnostics/blob/master/documentation/design-docs/ipc-protocol.md + // for definition of serialization format + + // uint64_t ProcessId; -> 8 bytes + // GUID RuntimeCookie; -> 16 bytes + // LPCWSTR CommandLine; -> 4 bytes + strlen * sizeof(WCHAR) + // LPCWSTR OS; -> 4 bytes + strlen * sizeof(WCHAR) + // LPCWSTR Arch; -> 4 bytes + strlen * sizeof(WCHAR) + + EP_ASSERT (payload != NULL); + + size_t size = 0; + size += sizeof(payload->process_id); + size += sizeof(payload->runtime_cookie); + + size += sizeof(uint32_t); + size += (payload->command_line != NULL) ? + (ep_rt_utf16_string_len (payload->command_line) + 1) * sizeof(ep_char16_t) : 0; + + size += sizeof(uint32_t); + size += (payload->os != NULL) ? + (ep_rt_utf16_string_len (payload->os) + 1) * sizeof(ep_char16_t) : 0; + + size += sizeof(uint32_t); + size += (payload->arch != NULL) ? + (ep_rt_utf16_string_len (payload->arch) + 1) * sizeof(ep_char16_t) : 0; + + EP_ASSERT (size <= UINT16_MAX); + return (uint16_t)size; +} + +static +bool +process_info_payload_flatten ( + void *payload, + uint8_t **buffer, + uint16_t *size) +{ + DiagnosticsProcessInfoPayload *process_info = (DiagnosticsProcessInfoPayload*)payload; + + EP_ASSERT (payload != NULL); + EP_ASSERT (buffer != NULL); + EP_ASSERT (*buffer != NULL); + EP_ASSERT (size != NULL); + EP_ASSERT (process_info_payload_get_size (process_info) == *size); + + // see IPC spec @ https://github.com/dotnet/diagnostics/blob/master/documentation/design-docs/ipc-protocol.md + // for definition of serialization format + + bool success = true; + + // uint64_t ProcessId; + memcpy (*buffer, &process_info->process_id, sizeof (process_info->process_id)); + *buffer += sizeof (process_info->process_id); + *size -= sizeof (process_info->process_id); + + // GUID RuntimeCookie; + memcpy(*buffer, &process_info->runtime_cookie, sizeof (process_info->runtime_cookie)); + *buffer += sizeof (process_info->runtime_cookie); + *size -= sizeof (process_info->runtime_cookie); + + // LPCWSTR CommandLine; + success &= ds_ipc_message_try_write_string_utf16_t (buffer, size, process_info->command_line); + + // LPCWSTR OS; + if (success) + success &= ds_ipc_message_try_write_string_utf16_t (buffer, size, process_info->os); + + // LPCWSTR Arch; + if (success) + success &= ds_ipc_message_try_write_string_utf16_t (buffer, size, process_info->arch); + + // Assert we've used the whole buffer we were given + EP_ASSERT(*size == 0); + + return success; +} + +DiagnosticsProcessInfoPayload * +ds_process_info_payload_init ( + DiagnosticsProcessInfoPayload *payload, + const ep_char16_t *command_line, + const ep_char16_t *os, + const ep_char16_t *arch, + uint32_t process_id, + const uint8_t *runtime_cookie) +{ + ep_return_null_if_nok (payload != NULL); + + payload->command_line = command_line; + payload->os = os; + payload->arch = arch; + payload->process_id = process_id; + + if (runtime_cookie) + memcpy (&payload->runtime_cookie, runtime_cookie, EP_ACTIVITY_ID_SIZE); + + return payload; +} + +void +ds_process_info_payload_fini (DiagnosticsProcessInfoPayload *payload) +{ + ; +} + +/* + * DiagnosticsProcessProtocolHelper. + */ + +static +void +process_protocol_helper_get_process_info ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (stream != NULL); + + ep_char16_t *command_line = NULL; + ep_char16_t *os_info = NULL; + ep_char16_t *arch_info = NULL; + + if (ep_rt_managed_command_line_get ()) + command_line = ep_rt_utf8_to_utf16_string (ep_rt_managed_command_line_get (), -1); + + // Checkout https://github.com/dotnet/coreclr/pull/24433 for more information about this fall back. + if (!command_line) + // Use the result from ep_rt_os_command_line_get() instead + command_line = ep_rt_utf8_to_utf16_string (ep_rt_os_command_line_get (), -1); + + // get OS + Arch info + os_info = ep_rt_utf8_to_utf16_string (ep_event_source_get_os_info (), -1); + arch_info = ep_rt_utf8_to_utf16_string (ep_event_source_get_arch_info (), -1); + + DiagnosticsProcessInfoPayload payload; + ds_process_info_payload_init ( + &payload, + command_line, + os_info, + arch_info, + ep_rt_current_process_get_id (), + ds_ipc_advertise_cookie_v1_get ()); + + ep_raise_error_if_nok (ds_ipc_message_initialize_buffer ( + message, + ds_ipc_header_get_generic_success (), + (void *)&payload, + process_info_payload_get_size (&payload), + process_info_payload_flatten) == true); + + ds_ipc_message_send (message, stream); + +ep_on_exit: + ds_process_info_payload_fini (&payload); + ep_rt_utf16_string_free (arch_info); + ep_rt_utf16_string_free (os_info); + ep_rt_utf16_string_free (command_line); + ds_ipc_stream_free (stream); + return; + +ep_on_error: + ds_ipc_message_send_error (stream, DS_IPC_E_FAIL); + DS_LOG_WARNING_0 ("Failed to send DiagnosticsIPC response"); + ep_exit_error_handler (); +} + +static +void +process_protocol_helper_get_process_env ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (stream != NULL); + + // TODO: Implement. + ds_ipc_message_send_error (stream, DS_IPC_E_NOTSUPPORTED); + DS_LOG_WARNING_0 ("Get Process Environmnet not implemented\n"); + + ds_ipc_stream_free (stream); +} + +static +void +process_protocol_helper_resume_runtime_startup ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (stream != NULL); + + // no payload + ds_server_resume_runtime_startup (); + bool success = ds_ipc_message_send_success (stream, DS_IPC_S_OK); + if (!success) { + ds_ipc_message_send_error (stream, DS_IPC_E_FAIL); + DS_LOG_WARNING_0 ("Failed to send DiagnosticsIPC response"); + } + + ds_ipc_stream_free (stream); +} + +static +void +process_protocol_helper_unknown_command ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + DS_LOG_WARNING_1 ("Received unknown request type (%d)\n", ds_ipc_message_header_get_commandset (ds_ipc_message_get_header (&message))); + ds_ipc_message_send_error (stream, DS_IPC_E_UNKNOWN_COMMAND); + ds_ipc_stream_free (stream); +} + +void +ds_process_protocol_helper_handle_ipc_message ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (stream != NULL); + + switch ((DiagnosticsProcessCommandId)ds_ipc_header_get_commandid (ds_ipc_message_get_header_ref (message))) { + case DS_PROCESS_COMMANDID_GET_PROCESS_INFO: + process_protocol_helper_get_process_info (message, stream); + break; + case DS_PROCESS_COMMANDID_RESUME_RUNTIME: + process_protocol_helper_resume_runtime_startup (message, stream); + break; + case DS_PROCESS_COMMANDID_GET_PROCESS_ENV: + process_protocol_helper_get_process_env (message, stream); + break; + default: + process_protocol_helper_unknown_command (message, stream); + break; + } +} + +#endif /* !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef DS_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_diagnostics_process_protocol; +const char quiet_linker_empty_file_warning_diagnostics_process_protocol = 0; +#endif diff --git a/src/mono/mono/eventpipe/ds-process-protocol.h b/src/mono/mono/eventpipe/ds-process-protocol.h new file mode 100644 index 00000000000000..ef07330d6dc370 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-process-protocol.h @@ -0,0 +1,72 @@ +#ifndef __DIAGNOSTICS_PROCESS_PROTOCOL_H__ +#define __DIAGNOSTICS_PROCESS_PROTOCOL_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#include "ds-types.h" +#include "ds-ipc.h" + +#undef DS_IMPL_GETTER_SETTER +#ifdef DS_IMPL_PROCESS_PROTOCOL_GETTER_SETTER +#define DS_IMPL_GETTER_SETTER +#endif +#include "ds-getter-setter.h" + +/* +* DiagnosticsProcessInfoPayload +*/ + +// command = 0x0400 +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_PROCESS_PROTOCOL_GETTER_SETTER) +struct _DiagnosticsProcessInfoPayload { +#else +struct _DiagnosticsProcessInfoPayload_Internal { +#endif + // The protocol buffer is defined as: + // X, Y, Z means encode bytes for X followed by bytes for Y followed by bytes for Z + // uint = 4 little endian bytes + // long = 8 little endian bytes + // GUID = 16 little endian bytes + // wchar = 2 little endian bytes, UTF16 encoding + // array = uint length, length # of Ts + // string = (array where the last char must = 0) or (length = 0) + + // ProcessInfo = long pid, string cmdline, string OS, string arch, GUID runtimeCookie + uint64_t process_id; + const ep_char16_t *command_line; + const ep_char16_t *os; + const ep_char16_t *arch; + uint8_t runtime_cookie [EP_ACTIVITY_ID_SIZE]; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_PROCESS_PROTOCOL_GETTER_SETTER) +struct _DiagnosticsProcessInfoPayload { + uint8_t _internal [sizeof (struct _DiagnosticsProcessInfoPayload_Internal)]; +}; +#endif + +DiagnosticsProcessInfoPayload * +ds_process_info_payload_init ( + DiagnosticsProcessInfoPayload *payload, + const ep_char16_t *command_line, + const ep_char16_t *os, + const ep_char16_t *arch, + uint32_t process_id, + const uint8_t *runtime_cookie); + +void +ds_process_info_payload_fini (DiagnosticsProcessInfoPayload *payload); + +/* + * DiagnosticsProcessProtocolHelper. + */ + +void +ds_process_protocol_helper_handle_ipc_message ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_PROCESS_PROTOCOL_H__ */ diff --git a/src/mono/mono/eventpipe/ds-profiler-protocol.c b/src/mono/mono/eventpipe/ds-profiler-protocol.c new file mode 100644 index 00000000000000..1f63c8a342967c --- /dev/null +++ b/src/mono/mono/eventpipe/ds-profiler-protocol.c @@ -0,0 +1,32 @@ +#include + +#if defined(ENABLE_PERFTRACING) && defined(FEATURE_PROFAPI_ATTACH_DETACH) +#include "ds-rt-config.h" +#if !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) + +#define DS_IMPL_PROFILER_PROTOCOL_GETTER_SETTER +#include "ds-protocol.h" +#include "ds-profiler-protocol.h" +#include "ds-rt.h" + +void +ds_profiler_protocol_helper_handle_ipc_message ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (stream != NULL); + + // TODO: Implement. + DS_LOG_WARNING_0 ("Attach profiler not implemented\n"); + ds_ipc_message_send_error (stream, DS_IPC_E_NOTSUPPORTED); + ds_ipc_stream_free (stream); +} + +#endif /* !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* #if defined(ENABLE_PERFTRACING) && defined(FEATURE_PROFAPI_ATTACH_DETACH) */ + +#ifndef DS_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_diagnostics_profiler_protocol; +const char quiet_linker_empty_file_warning_diagnostics_profiler_protocol = 0; +#endif diff --git a/src/mono/mono/eventpipe/ds-profiler-protocol.h b/src/mono/mono/eventpipe/ds-profiler-protocol.h new file mode 100644 index 00000000000000..3cd5055fdcb529 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-profiler-protocol.h @@ -0,0 +1,27 @@ +#ifndef __DIAGNOSTICS_PROFILER_PROTOCOL_H__ +#define __DIAGNOSTICS_PROFILER_PROTOCOL_H__ + +#include + +#if defined(ENABLE_PERFTRACING) && defined(FEATURE_PROFAPI_ATTACH_DETACH) +#include "ds-rt-config.h" +#include "ds-types.h" +#include "ds-ipc.h" + +#undef DS_IMPL_GETTER_SETTER +#ifdef DS_IMPL_PROFILER_PROTOCOL_GETTER_SETTER +#define DS_IMPL_GETTER_SETTER +#endif +#include "ds-getter-setter.h" + +/* + * DiagnosticsProfilerProtocolHelper. + */ + +void +ds_profiler_protocol_helper_handle_ipc_message ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +#endif /* defined(ENABLE_PERFTRACING) && defined(FEATURE_PROFAPI_ATTACH_DETACH) */ +#endif /* __DIAGNOSTICS_PROFILER_PROTOCOL_H__ */ diff --git a/src/mono/mono/eventpipe/ds-protocol.c b/src/mono/mono/eventpipe/ds-protocol.c new file mode 100644 index 00000000000000..b69a05e1f0a3de --- /dev/null +++ b/src/mono/mono/eventpipe/ds-protocol.c @@ -0,0 +1,574 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#if !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) + +#define DS_IMPL_PROTOCOL_GETTER_SETTER +#include "ds-protocol.h" +#include "ds-server.h" +#include "ep.h" +#include "ep-stream.h" +#include "ep-event-source.h" + +const DiagnosticsIpcHeader _ds_ipc_generic_success_header = { + { DOTNET_IPC_V1_MAGIC }, + (uint16_t)sizeof (DiagnosticsIpcHeader), + (uint8_t)DS_SERVER_COMMANDSET_SERVER, + (uint8_t)DS_SERVER_RESPONSEID_OK, + (uint16_t)0x0000 +}; + +const DiagnosticsIpcHeader _ds_ipc_generic_error_header = { + { DOTNET_IPC_V1_MAGIC }, + (uint16_t)sizeof (DiagnosticsIpcHeader), + (uint8_t)DS_SERVER_COMMANDSET_SERVER, + (uint8_t)DS_SERVER_RESPONSEID_ERROR, + (uint16_t)0x0000 +}; + +static uint8_t _ds_ipc_advertise_cooike_v1 [EP_ACTIVITY_ID_SIZE] = { 0 }; + +/* + * Forward declares of all static functions. + */ + +static +bool +ipc_message_flatten_blitable_type ( + DiagnosticsIpcMessage *message, + uint8_t *payload, + size_t payload_len); + +static +bool +ipc_message_try_parse ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +static +bool +ipc_message_try_send_string_utf16_t ( + DiagnosticsIpcStream *stream, + const ep_char16_t *value); + +static +bool +ipc_message_flatten ( + DiagnosticsIpcMessage *message, + void *payload, + uint16_t payload_len, + ds_ipc_flatten_payload_func flatten_payload); + +/* +* DiagnosticsIpc +*/ + +uint8_t * +ds_ipc_advertise_cookie_v1_get (void) +{ + return _ds_ipc_advertise_cooike_v1; +} + +void +ds_ipc_advertise_cookie_v1_init (void) +{ + ep_rt_create_activity_id ((uint8_t *)&_ds_ipc_advertise_cooike_v1, EP_ACTIVITY_ID_SIZE); +} + +/** +* ==ADVERTISE PROTOCOL== +* Before standard IPC Protocol communication can occur on a client-mode connection +* the runtime must advertise itself over the connection. ALL SUBSEQUENT COMMUNICATION +* IS STANDARD DIAGNOSTICS IPC PROTOCOL COMMUNICATION. +* +* See spec in: dotnet/diagnostics@documentation/design-docs/ipc-spec.md +* +* The flow for Advertise is a one-way burst of 34 bytes consisting of +* 8 bytes - "ADVR_V1\0" (ASCII chars + null byte) +* 16 bytes - random 128 bit number cookie (little-endian) +* 8 bytes - PID (little-endian) +* 2 bytes - unused 2 byte field for futureproofing +*/ +bool +ds_icp_advertise_v1_send (DiagnosticsIpcStream *stream) +{ + uint8_t advertise_buffer [DOTNET_IPC_V1_ADVERTISE_SIZE]; + uint8_t *cookie = ds_ipc_advertise_cookie_v1_get (); + uint64_t pid = DS_VAL64 (ep_rt_current_process_get_id ()); + uint64_t *buffer = (uint64_t *)advertise_buffer; + bool result = false; + + ep_return_false_if_nok (stream != NULL); + + memcpy (buffer, DOTNET_IPC_V1_ADVERTISE_MAGIC, sizeof (uint64_t)); + buffer++; + + // fills buffer[1] and buffer[2] + memcpy (buffer, cookie, EP_ACTIVITY_ID_SIZE); + buffer +=2; + + memcpy (buffer, &pid, sizeof (uint64_t)); + buffer++; + + // zero out unused filed + memset (buffer, 0, sizeof (uint16_t)); + + uint32_t bytes_written = 0; + ep_raise_error_if_nok (ds_ipc_stream_write (stream, advertise_buffer, sizeof (advertise_buffer), &bytes_written, 100 /*ms*/) == true); + + EP_ASSERT (bytes_written == sizeof (advertise_buffer)); + result = (bytes_written == sizeof (advertise_buffer)); + +ep_on_exit: + return result; + +ep_on_error: + result = false; + ep_exit_error_handler (); +} + +/* +* DiagnosticsIpcMessage +*/ + +static +bool +ipc_message_try_send_string_utf16_t ( + DiagnosticsIpcStream *stream, + const ep_char16_t *value) +{ + uint32_t string_len = (uint32_t)(ep_rt_utf16_string_len (value) + 1); + uint32_t string_bytes = (uint32_t)(string_len * sizeof (ep_char16_t)); + uint32_t total_bytes = (uint32_t)(string_bytes + sizeof (uint32_t)); + + uint32_t total_written = 0; + uint32_t written = 0; + + bool success = ds_ipc_stream_write (stream, (const uint8_t *)&string_len, (uint32_t)sizeof (string_len), &written, EP_INFINITE_WAIT); + total_written += written; + + if (success) { + success &= ds_ipc_stream_write (stream, (const uint8_t *)value, string_bytes, &written, EP_INFINITE_WAIT); + total_written += written; + } + + EP_ASSERT (total_bytes == total_written); + return success && (total_bytes == total_written); +} + +static +bool +ipc_message_flatten_blitable_type ( + DiagnosticsIpcMessage *message, + uint8_t *payload, + size_t payload_len) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (payload != NULL); + + if (message->data != NULL) + return true; + + bool result = false; + uint8_t *buffer = NULL; + uint8_t *buffer_cursor = NULL; + + EP_ASSERT (sizeof (message->header) + payload_len <= UINT16_MAX); + message->size = sizeof (message->header) + payload_len; + + buffer = ep_rt_byte_array_alloc (message->size); + ep_raise_error_if_nok (buffer != NULL); + + buffer_cursor = buffer; + message->header.size = message->size; + + memcpy (buffer_cursor, &message->header, sizeof (message->header)); + buffer_cursor += sizeof (message->header); + + memcpy (buffer_cursor, payload, payload_len); + + EP_ASSERT (message->data == NULL); + message->data = buffer; + + buffer = NULL; + result = true; + +ep_on_exit: + return result; + +ep_on_error: + ep_rt_byte_array_free (buffer); + result = false; + ep_exit_error_handler (); +} + +// Attempt to populate header and payload from a buffer. +// Payload is left opaque as a flattened buffer in m_pData +static +bool +ipc_message_try_parse ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (stream != NULL); + + uint8_t *buffer = NULL; + bool success = false; + + // Read out header first + uint32_t bytes_read; + success = ds_ipc_stream_read (stream, (uint8_t *)&message->header, sizeof (message->header), &bytes_read, EP_INFINITE_WAIT); + if (!success || (bytes_read < sizeof (message->header))) + ep_raise_error (); + + if (message->header.size < sizeof (message->header)) + ep_raise_error (); + + message->size = message->header.size; + + // Then read out payload to buffer. + uint16_t payload_len; + payload_len = message->header.size - sizeof (message->header); + if (payload_len != 0) { + uint8_t *buffer = ep_rt_byte_array_alloc (payload_len); + ep_raise_error_if_nok (buffer != NULL); + + success = ds_ipc_stream_read (stream, buffer, payload_len, &bytes_read, EP_INFINITE_WAIT); + if (!success || (bytes_read < payload_len)) + ep_raise_error (); + + message->data = buffer; + buffer = NULL; + } + +ep_on_exit: + return success; + +ep_on_error: + ep_rt_byte_array_free (buffer); + success = false; + ep_exit_error_handler (); +} + +static +bool +ipc_message_flatten ( + DiagnosticsIpcMessage *message, + void *payload, + uint16_t payload_len, + ds_ipc_flatten_payload_func flatten_payload) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (payload != NULL); + + if (message->data) + return true; + + bool result = true; + uint8_t *buffer = NULL; + + uint16_t total_len = 0; + total_len += sizeof (DiagnosticsIpcHeader) + payload_len; + + uint16_t remaining_len = total_len; + message->size = total_len; + + buffer = ep_rt_byte_array_alloc (message->size); + ep_raise_error_if_nok (buffer != NULL); + + uint8_t * buffer_cursor; + buffer_cursor = buffer; + message->header.size = message->size; + + memcpy (buffer_cursor, &message->header, sizeof (DiagnosticsIpcHeader)); + buffer_cursor += sizeof (DiagnosticsIpcHeader); + remaining_len -= sizeof (DiagnosticsIpcHeader); + + if (flatten_payload) + result = flatten_payload (payload, &buffer_cursor, &remaining_len); + else + memcpy (buffer_cursor, payload, payload_len); + + EP_ASSERT (message->data == NULL); + + //Transfer ownership. + message->data = buffer; + buffer = NULL; + +ep_on_exit: + ep_rt_byte_array_free (buffer); + return result; + +ep_on_error: + result = false; + ep_exit_error_handler (); +} + +DiagnosticsIpcMessage * +ds_ipc_message_init (DiagnosticsIpcMessage *message) +{ + ep_return_null_if_nok (message != NULL); + + message->data = NULL; + message->size = 0; + memset (&message->header, 0 , sizeof (message->header)); + + return message; +} + +void +ds_ipc_message_fini (DiagnosticsIpcMessage *message) +{ + ep_return_void_if_nok (message != NULL); + ep_rt_byte_array_free (message->data); +} + +bool +ds_ipc_message_initialize_stream ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + return ipc_message_try_parse (message, stream); +} + +bool +ds_ipc_message_try_parse_value ( + uint8_t **buffer, + uint32_t *buffer_len, + uint8_t *value, + size_t value_len) +{ + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len != NULL); + EP_ASSERT (value != NULL); + EP_ASSERT ((buffer_len - value_len) >= 0); + + memcpy (value, *buffer, value_len); + *buffer = *buffer + value_len; + *buffer_len = *buffer_len - value_len; + return true; +} + +bool +ds_ipc_message_try_parse_uint64_t ( + uint8_t **buffer, + uint32_t *buffer_len, + uint64_t *value) +{ + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len != NULL); + EP_ASSERT (value != NULL); + + bool result = ds_ipc_message_try_parse_value (buffer, buffer_len, (uint8_t *)value, sizeof (uint64_t)); + if (result) + value = DS_VAL64 (value); + return result; +} + +bool +ds_ipc_message_try_parse_uint32_t ( + uint8_t **buffer, + uint32_t *buffer_len, + uint32_t *value) +{ + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len != NULL); + EP_ASSERT (value != NULL); + + bool result = ds_ipc_message_try_parse_value (buffer, buffer_len, (uint8_t*)value, sizeof (uint32_t)); + if (result) + value = DS_VAL32 (value); + return result; +} + +// TODO: Strings are in little endian format in buffer. +bool +ds_ipc_message_try_parse_string_utf16_t ( + uint8_t **buffer, + uint32_t *buffer_len, + const ep_char16_t **value) +{ + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len != NULL); + EP_ASSERT (value != NULL); + + bool result = false; + + uint32_t string_len = 0; + ep_raise_error_if_nok (ds_ipc_message_try_parse_uint32_t (buffer, buffer_len, &string_len) == true); + + if (string_len != 0) { + if (string_len > (*buffer_len / sizeof (ep_char16_t))) + ep_raise_error (); + + if (((const ep_char16_t *)*buffer) [string_len - 1] != 0) + ep_raise_error (); + + *value = (ep_char16_t *)*buffer; + + } else { + *value = NULL; + } + + *buffer = *buffer + (string_len * sizeof (ep_char16_t)); + *buffer_len = *buffer_len + (string_len * sizeof (ep_char16_t)); + + result = true; + +ep_on_exit: + return result; + +ep_on_error: + EP_ASSERT (result == false); + ep_exit_error_handler (); +} + +bool +ds_ipc_message_initialize_header_uint32_t_payload ( + DiagnosticsIpcMessage *message, + const DiagnosticsIpcHeader *header, + uint32_t payload) +{ + EP_ASSERT (message); + EP_ASSERT (header); + + message->header = *header; + return ipc_message_flatten_blitable_type (message, (uint8_t *)&payload, sizeof (payload)); +} + +bool +ds_ipc_message_initialize_header_uint64_t_payload ( + DiagnosticsIpcMessage *message, + const DiagnosticsIpcHeader *header, + uint64_t payload) +{ + EP_ASSERT (message); + EP_ASSERT (header); + + message->header = *header; + return ipc_message_flatten_blitable_type (message, (uint8_t *)&payload, sizeof (payload)); +} + +bool +ds_ipc_message_initialize_buffer ( + DiagnosticsIpcMessage *message, + const DiagnosticsIpcHeader *header, + void *payload, + uint16_t payload_len, + ds_ipc_flatten_payload_func flatten_payload) +{ + message->header = *header; + return ipc_message_flatten (message, payload, payload_len, flatten_payload); +} + +uint8_t * +ds_ipc_message_try_parse_payload ( + DiagnosticsIpcMessage *message, + ds_ipc_parse_payload_func parse_func) +{ + ep_return_null_if_nok (message != NULL); + + EP_ASSERT (message->data); + + uint8_t *payload = NULL; + + if (parse_func) + payload = parse_func (message->data, message->size - sizeof (message->header)); + else + payload = message->data; + + message->data = NULL; // user is expected to clean up buffer when finished with it + return payload; +} + +bool +ds_ipc_message_try_write_string_utf16_t ( + uint8_t **buffer, + uint16_t *buffer_len, + const ep_char16_t *value) +{ + EP_ASSERT (buffer != NULL); + EP_ASSERT (*buffer != NULL); + EP_ASSERT (buffer_len != NULL); + EP_ASSERT (value != NULL); + + bool result = true; + uint32_t string_len = (uint32_t)(ep_rt_utf16_string_len (value) + 1); + size_t total_bytes = (string_len * sizeof (ep_char16_t)) + sizeof(uint32_t); + + EP_ASSERT (total_bytes <= UINT16_MAX); + EP_ASSERT (*buffer_len >= (uint16_t)total_bytes); + if (*buffer_len < (uint16_t)total_bytes || total_bytes > UINT16_MAX) + ep_raise_error (); + + memcpy (*buffer, &string_len, sizeof (string_len)); + *buffer += sizeof (string_len); + + memcpy (*buffer, value, string_len * sizeof (ep_char16_t)); + *buffer += (string_len * sizeof (ep_char16_t)); + + *buffer_len -= (uint16_t)total_bytes; + +ep_on_exit: + return result; + +ep_on_error: + result = false; + ep_exit_error_handler (); +} + +bool +ds_ipc_message_send ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (message->data != NULL); + EP_ASSERT (stream != NULL); + + uint32_t bytes_written; + bool success = ds_ipc_stream_write (stream, message->data, message->size, &bytes_written, EP_INFINITE_WAIT); + return (bytes_written == message->size) && success; +} + +bool +ds_ipc_message_send_error ( + DiagnosticsIpcStream *stream, + uint32_t error) +{ + ep_return_false_if_nok (stream != NULL); + + DiagnosticsIpcMessage error_message; + ds_ipc_message_init (&error_message); + bool success = ds_ipc_message_initialize_header_uint32_t_payload (&error_message, ds_ipc_header_get_generic_error (), error); + if (success) + ds_ipc_message_send (&error_message, stream); + ds_ipc_message_fini (&error_message); + return success; +} + +bool +ds_ipc_message_send_success ( + DiagnosticsIpcStream *stream, + uint32_t code) +{ + ep_return_false_if_nok (stream != NULL); + + DiagnosticsIpcMessage success_message; + ds_ipc_message_init (&success_message); + bool success = ds_ipc_message_initialize_header_uint32_t_payload (&success_message, ds_ipc_header_get_generic_success (), code); + if (success) + ds_ipc_message_send (&success_message, stream); + ds_ipc_message_fini (&success_message); + return success; +} + +#endif /* !defined(DS_INCLUDE_SOURCE_FILES) || defined(DS_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef DS_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_diagnostics_protocol; +const char quiet_linker_empty_file_warning_diagnostics_protocol = 0; +#endif diff --git a/src/mono/mono/eventpipe/ds-protocol.h b/src/mono/mono/eventpipe/ds-protocol.h new file mode 100644 index 00000000000000..eac340c485182d --- /dev/null +++ b/src/mono/mono/eventpipe/ds-protocol.h @@ -0,0 +1,204 @@ +#ifndef __DIAGNOSTICS_PROTOCOL_H__ +#define __DIAGNOSTICS_PROTOCOL_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#include "ds-types.h" +#include "ds-ipc.h" + +#undef DS_IMPL_GETTER_SETTER +#ifdef DS_IMPL_PROTOCOL_GETTER_SETTER +#define DS_IMPL_GETTER_SETTER +#endif +#include "ds-getter-setter.h" + +typedef bool (ds_ipc_flatten_payload_func)(void *payload, uint8_t **buffer, uint16_t *buffer_len); +typedef uint8_t * (*ds_ipc_parse_payload_func)(uint8_t *buffer, uint16_t buffer_len); + +/* +* DiagnosticsIpc +*/ + +uint8_t * +ds_ipc_advertise_cookie_v1_get (void); + +void +ds_ipc_advertise_cookie_v1_init (void); + +bool +ds_icp_advertise_v1_send (DiagnosticsIpcStream *stream); + +/* +* DiagnosticsIpcHeader +*/ + +// The header to be associated with every command and response +// to/from the diagnostics server +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_PROTOCOL_GETTER_SETTER) +struct _DiagnosticsIpcHeader { +#else +struct _DiagnosticsIpcHeader_Internal { +#endif + // Magic Version number; a 0 terminated char array + uint8_t magic [14]; + // The size of the incoming packet, size = header + payload size + uint16_t size; + // The scope of the Command. + uint8_t commandset; + // The command being sent + uint8_t commandid; + // reserved for future use + uint16_t reserved; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_PROTOCOL_GETTER_SETTER) +struct _DiagnosticsIpcHeader { + uint8_t _internal [sizeof (struct _DiagnosticsIpcHeader_Internal)]; +}; +#endif + +DS_DEFINE_GETTER_ARRAY_REF(DiagnosticsIpcHeader *, ipc_header, uint8_t *, const uint8_t *, magic, magic[0]) +DS_DEFINE_GETTER(DiagnosticsIpcHeader *, ipc_header, uint8_t, commandset) +DS_DEFINE_GETTER(DiagnosticsIpcHeader *, ipc_header, uint8_t, commandid) + +/* +* DiagnosticsIpcMessage +*/ + +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_PROTOCOL_GETTER_SETTER) +struct _DiagnosticsIpcMessage { +#else +struct _DiagnosticsIpcMessage_Internal { +#endif + // header associated with this message + DiagnosticsIpcHeader header; + // Pointer to flattened buffer filled with: + // incoming message: payload (could be empty which would be NULL) + // outgoing message: header + payload + uint8_t *data; + // The total size of the message (header + payload) + uint16_t size; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_PROTOCOL_GETTER_SETTER) +struct _DiagnosticsIpcMessage { + uint8_t _internal [sizeof (struct _DiagnosticsIpcMessage_Internal)]; +}; +#endif + +DS_DEFINE_GETTER_REF(DiagnosticsIpcMessage *, ipc_message, DiagnosticsIpcHeader *, header) + +DiagnosticsIpcMessage * +ds_ipc_message_init (DiagnosticsIpcMessage *message); + +void +ds_ipc_message_fini (DiagnosticsIpcMessage *message); + +// Initialize an incoming IpcMessage from a stream by parsing +// the header and payload. +// +// If either fail, this returns false, true otherwise +bool +ds_ipc_message_initialize_stream ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +bool +ds_ipc_message_try_parse_value ( + uint8_t **buffer, + uint32_t *buffer_len, + uint8_t *value, + size_t value_len); + +bool +ds_ipc_message_try_parse_uint64_t ( + uint8_t **buffer, + uint32_t *buffer_len, + uint64_t *value); + +bool +ds_ipc_message_try_parse_uint32_t ( + uint8_t **buffer, + uint32_t *buffer_len, + uint32_t *value); + +bool +ds_ipc_message_try_parse_string_utf16_t ( + uint8_t **buffer, + uint32_t *buffer_len, + const ep_char16_t **value); + +bool +ds_ipc_message_initialize_header_uint32_t_payload ( + DiagnosticsIpcMessage *message, + const DiagnosticsIpcHeader *header, + uint32_t payload); + +bool +ds_ipc_message_initialize_header_uint64_t_payload ( + DiagnosticsIpcMessage *message, + const DiagnosticsIpcHeader *header, + uint64_t payload); + +bool +ds_ipc_message_initialize_buffer ( + DiagnosticsIpcMessage *message, + const DiagnosticsIpcHeader *header, + void *payload, + uint16_t payload_len, + ds_ipc_flatten_payload_func flatten_payload); + +uint8_t * +ds_ipc_message_try_parse_payload ( + DiagnosticsIpcMessage *message, + ds_ipc_parse_payload_func parse_func); + +bool +ds_ipc_message_try_write_string_utf16_t ( + uint8_t **buffer, + uint16_t *buffer_len, + const ep_char16_t *value); + +bool +ds_ipc_message_send ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +// Send an Error message across the pipe. +// Will return false on failure of any step (init or send). +// Regardless of success of this function, the spec +// dictates that the connection be closed on error, +// so the user is expected to delete the IpcStream +// after handling error cases. +bool +ds_ipc_message_send_error ( + DiagnosticsIpcStream *stream, + uint32_t error); + +bool +ds_ipc_message_send_success ( + DiagnosticsIpcStream *stream, + uint32_t code); + +static +inline +const DiagnosticsIpcHeader * +ds_ipc_header_get_generic_success (void) +{ + extern const DiagnosticsIpcHeader _ds_ipc_generic_success_header; + return &_ds_ipc_generic_success_header; +} + +static +inline +const DiagnosticsIpcHeader * +ds_ipc_header_get_generic_error (void) +{ + extern const DiagnosticsIpcHeader _ds_ipc_generic_error_header; + return &_ds_ipc_generic_error_header; +} + +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_PROTOCOL_H__ */ diff --git a/src/mono/mono/eventpipe/ds-rt-config.h b/src/mono/mono/eventpipe/ds-rt-config.h new file mode 100644 index 00000000000000..b01333bf457bf0 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-rt-config.h @@ -0,0 +1,14 @@ +#ifndef __DIAGNOSTICS_RT_CONFIG_H__ +#define __DIAGNOSTICS_RT_CONFIG_H__ + +#include "ep-rt-config.h" + +#ifdef EP_INLINE_GETTER_SETTER +#define DS_INLINE_GETTER_SETTER +#endif + +#ifdef EP_INCLUDE_SOURCE_FILES +#define DS_INCLUDE_SOURCE_FILES +#endif + +#endif /* __DIAGNOSTICS_RT_CONFIG_H__ */ diff --git a/src/mono/mono/eventpipe/ds-rt-mono.c b/src/mono/mono/eventpipe/ds-rt-mono.c new file mode 100644 index 00000000000000..0e1813dd8e75b5 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-rt-mono.c @@ -0,0 +1,278 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#include "ds-types.h" +#include "ds-rt.h" + +#ifdef __APPLE__ +#define APPLICATION_CONTAINER_BASE_PATH_SUFFIX "/Library/Group Containers/" + +// Not much to go with, but Max semaphore length on Mac is 31 characters. In a sandbox, the semaphore name +// must be prefixed with an application group ID. This will be 10 characters for developer ID and extra 2 +// characters for group name. For example ABCDEFGHIJ.MS. We still need some characters left +// for the actual semaphore names. +#define MAX_APPLICATION_GROUP_ID_LENGTH 13 +#endif // __APPLE__ + +#if defined (__linux__) && !defined (HAVE_PROCFS_STAT) +#define HAVE_PROCFS_STAT +#endif + +/* + * Forward declares of all static functions. + */ + +#ifndef HOST_WIN32 +static +bool +ipc_get_process_id_disambiguation_key ( + uint32_t process_id, + uint64_t *key); +#endif /* !HOST_WIN32 */ + +void ipc_transport_get_default_name ( + ep_char8_t *name, + uint32_t name_len, + const ep_char8_t *prefix, + int32_t id, + const ep_char8_t *group_id, + const ep_char8_t *suffix); + +#ifndef HOST_WIN32 + +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#endif + +#ifdef __NetBSD__ +#include +#include +#include +#include +#endif + +/* +Get a numeric value that can be used to disambiguate between processes with the same PID, +provided that one of them is still running. The numeric value can mean different things +on different platforms, so it should not be used for any other purpose. Under the hood, +it is implemented based on the creation time of the process. +*/ +static +bool +ipc_get_process_id_disambiguation_key ( + uint32_t process_id, + uint64_t *key) +{ + if (!key) { + EP_ASSERT (!"key argument cannot be null!"); + return false; + } + + *key = 0; + +#if defined (__APPLE__) + // On OS X, we return the process start time expressed in Unix time (the number of seconds + // since the start of the Unix epoch). + struct kinfo_proc info = {}; + size_t size = sizeof (info); + int mib [4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_id }; + + const int result_sysctl = sysctl (mib, sizeof(mib)/sizeof(*mib), &info, &size, NULL, 0); + if (result_sysctl == 0) { + struct timeval proc_starttime = info.kp_proc.p_starttime; + long seconds_since_epoch = proc_starttime.tv_sec; + *key = seconds_since_epoch; + return true; + } else { + EP_ASSERT (!"Failed to get start time of a process."); + return false; + } +#elif defined (__NetBSD__) + // On NetBSD, we return the process start time expressed in Unix time (the number of seconds + // since the start of the Unix epoch). + kvm_t *kd; + int cnt; + struct kinfo_proc2 *info; + + kd = kvm_open (NULL, NULL, NULL, KVM_NO_FILES, "kvm_open"); + if (!kd) { + EP_ASSERT (!"Failed to get start time of a process."); + return false; + } + + info = kvm_getproc2 (kd, KERN_PROC_PID, process_id, sizeof (struct kinfo_proc2), &cnt); + if (!info || cnt < 1) { + kvm_close (kd); + EP_ASSERT (!"Failed to get start time of a process."); + return false; + } + + kvm_close (kd); + + long seconds_since_epoch = info->p_ustart_sec; + *key = seconds_since_epoch; + + return true; +#elif defined (HAVE_PROCFS_STAT) + // Here we read /proc//stat file to get the start time for the process. + // We return this value (which is expressed in jiffies since boot time). + + // Making something like: /proc/123/stat + char stat_file_name [64]; + snprintf (stat_file_name, sizeof (stat_file_name), "/proc/%d/stat", process_id); + + FILE *stat_file = fopen (stat_file_name, "r"); + if (!stat_file) { + EP_ASSERT (!"Failed to get start time of a process, fopen failed."); + return false; + } + + char *line = NULL; + size_t line_len = 0; + if (getline (&line, &line_len, stat_file) == -1) + { + EP_ASSERT (!"Failed to get start time of a process, getline failed."); + return false; + } + + unsigned long long start_time; + + // According to `man proc`, the second field in the stat file is the filename of the executable, + // in parentheses. Tokenizing the stat file using spaces as separators breaks when that name + // has spaces in it, so we start using sscanf_s after skipping everything up to and including the + // last closing paren and the space after it. + char *scan_start_position = strrchr (line, ')'); + if (!scan_start_position || scan_start_position [1] == '\0') { + EP_ASSERT (!"Failed to parse stat file contents with strrchr."); + return false; + } + + scan_start_position += 2; + + // All the format specifiers for the fields in the stat file are provided by 'man proc'. + int result_sscanf = sscanf (scan_start_position, + "%*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %*lu %*lu %*ld %*ld %*ld %*ld %*ld %*ld %llu \n", + &start_time); + + if (result_sscanf != 1) { + EP_ASSERT (!"Failed to parse stat file contents with sscanf."); + return false; + } + + free (line); + fclose (stat_file); + + *key = (uint64_t)start_time; + return true; +#else + // If we don't have /proc, we just return false. + DS_LOG_WARNING_0 ("ipc_get_process_id_disambiguation_key was called but is not implemented on this platform!"); + return false; +#endif +} + +void +ipc_transport_get_default_name ( + ep_char8_t *name, + uint32_t name_len, + const ep_char8_t *prefix, + int32_t id, + const ep_char8_t *group_id, + const ep_char8_t *suffix) +{ + EP_ASSERT (name != NULL); + EP_ASSERT (name > 0); + + int32_t result = 0; + uint64_t disambiguation_key = 0; + ep_char8_t *format_buffer = NULL; + + *name = '\0'; + + format_buffer = (ep_char8_t *)malloc (name_len + 1); + ep_raise_error_if_nok (format_buffer != NULL); + + *format_buffer = '\0'; + + // If ipc_get_process_id_disambiguation_key failed for some reason, it should set the value + // to 0. We expect that anyone else making the pipe name will also fail and thus will + // also try to use 0 as the value. + if (!ipc_get_process_id_disambiguation_key (id, &disambiguation_key)) + EP_ASSERT (disambiguation_key == 0); +#ifdef __APPLE__ + if (group_id) { + // Verify the length of the group id + size_t group_id_len = strlen (group_id); + if (group_id_len > MAX_APPLICATION_GROUP_ID_LENGTH) { + DS_LOG_ERROR_0 ("The length of group_id is larger than MAX_APPLICATION_GROUP_ID_LENGTH"); + ep_raise_error (); + } + + // In sandbox, all IPC files (locks, pipes) should be written to the application group + // container. The path returned by GetTempPathA will be unique for each process and cannot + // be used for IPC between two different processes + const char *home_dir = getpwuid (getuid ())->pw_dir; + size_t home_dir_len = strlen (home_dir); + + // Verify the size of the path won't exceed maximum allowed size + if ((home_dir_len + strlen (APPLICATION_CONTAINER_BASE_PATH_SUFFIX) + group_id_len + 1) >= name_len) { + DS_LOG_ERROR_0 ("Application container folder path is larger than name_len"); + ep_raise_error (); + } + + result = snprintf (format_buffer, name_len, "%s%s%s/", home_dir, APPLICATION_CONTAINER_BASE_PATH_SUFFIX, group_id); + if (result <= 0 || (uint32_t)result > name_len) { + DS_LOG_ERROR_0 ("format_buffer to small"); + ep_raise_error (); + } + + } else +#endif // __APPLE__ + { + // Get a temp file location + result = ep_rt_temp_path_get (format_buffer, name_len); + if (result == 0) { + DS_LOG_ERROR_0 ("ep_rt_temp_path_get failed"); + ep_raise_error (); + } + + EP_ASSERT (result <= name_len); + } + + result = snprintf(name, name_len, "%s%s-%d-%llu-%s", format_buffer, prefix, id, (unsigned long long)disambiguation_key, suffix); + if (result <= 0 || (uint32_t)result > name_len) { + DS_LOG_ERROR_0 ("name buffer to small"); + ep_raise_error (); + } + +ep_on_exit: + free (format_buffer); + return; + +ep_on_error: + name [0] = '\0'; + ep_exit_error_handler (); +} +#else /* !HOST_WIN32 */ +void +ipc_transport_get_default_name ( + ep_char8_t *name, + uint32_t name_len, + const ep_char8_t *prefix, + int32_t id, + const ep_char8_t *group_id, + const ep_char8_t *suffix) +{ + // Currently not used on Windows. + g_assert_not_reached (); +} +#endif /* !HOST_WIN32 */ +#endif /* ENABLE_PERFTRACING */ + +MONO_EMPTY_SOURCE_FILE(diagnostics_rt_mono); diff --git a/src/mono/mono/eventpipe/ds-rt-mono.h b/src/mono/mono/eventpipe/ds-rt-mono.h new file mode 100644 index 00000000000000..2d9fcbf3198067 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-rt-mono.h @@ -0,0 +1,138 @@ +// Implementation of ds-rt.h targeting Mono runtime. +#ifndef __DIAGNOSTICS_RT_MONO_H__ +#define __DIAGNOSTICS_RT_MONO_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-mono.h" + +#undef DS_LOG_ALWAYS_0 +#define DS_LOG_ALWAYS_0(msg) + +#undef DS_LOG_ALWAYS_1 +#define DS_LOG_ALWAYS_1(msg, data1) + +#undef DS_LOG_ALWAYS_2 +#define DS_LOG_ALWAYS_2(msg, data1, data2) + +#undef DS_LOG_INFO_0 +#define DS_LOG_INFO_0(msg) + +#undef DS_LOG_INFO_1 +#define DS_LOG_INFO_1(msg, data1) + +#undef DS_LOG_INFO_2 +#define DS_LOG_INFO_2(msg, data1, data2) + +#undef DS_LOG_ERROR_0 +#define DS_LOG_ERROR_0(msg) + +#undef DS_LOG_ERROR_1 +#define DS_LOG_ERROR_1(msg, data1) + +#undef DS_LOG_ERROR_2 +#define DS_LOG_ERROR_2(msg, data1, data2) + +#undef DS_LOG_WARNING_0 +#define DS_LOG_WARNING_0(msg) + +#undef DS_LOG_WARNING_1 +#define DS_LOG_WARNING_1(msg, data1) + +#undef DS_LOG_WARNING_2 +#define DS_LOG_WARNING_2(msg, data1, data2) + +#undef DS_ENTER_BLOCKING_PAL_SECTION +#define DS_ENTER_BLOCKING_PAL_SECTION \ + MONO_REQ_GC_UNSAFE_MODE \ + MONO_ENTER_GC_SAFE + +#undef DS_EXIT_BLOCKING_PAL_SECTION +#define DS_EXIT_BLOCKING_PAL_SECTION \ + MONO_REQ_GC_SAFE_MODE \ + MONO_EXIT_GC_SAFE \ + MONO_REQ_GC_UNSAFE_MODE + +#define DS_RT_DEFINE_ARRAY(array_name, array_type, iterator_type, item_type) \ + EP_RT_DEFINE_ARRAY_PREFIX(ds, array_name, array_type, iterator_type, item_type) + +#define DS_RT_DEFINE_ARRAY_ITERATOR(array_name, array_type, iterator_type, item_type) \ + EP_RT_DEFINE_ARRAY_ITERATOR_PREFIX(ds, array_name, array_type, iterator_type, item_type) + +/* + * DiagnosticsConfiguration. + */ + +static +inline +bool +ds_rt_config_value_get_enable (void) +{ + bool enable = true; + gchar *value = g_getenv ("COMPlus_EnableDiagnostics"); + if (value && atoi (value) == 0) + enable = false; + g_free (value); + return enable; +} + +static +inline +ep_char8_t * +ds_rt_config_value_get_ports (void) +{ + return g_getenv ("DOTNET_DiagnosticPorts"); +} + +static +inline +int32_t +ds_rt_config_value_get_default_port_suspend (void) +{ + int32_t value_int32_t = 0; + gchar *value = g_getenv ("DOTNET_DefaultDiagnosticPortSuspend"); + if (value) + value_int32_t = atoi (value); + g_free (value); + return value_int32_t; +} + +/* + * DiagnosticsIpc. + */ + +static +inline +void +ds_rt_transport_get_default_name ( + ep_char8_t *name, + int32_t name_len, + const ep_char8_t *prefix, + int32_t id, + const ep_char8_t *group_id, + const ep_char8_t *suffix) +{ + extern void ipc_transport_get_default_name (ep_char8_t *name, uint32_t name_len, const ep_char8_t *prefix, int32_t id, const ep_char8_t *group_id, const ep_char8_t *suffix); + ipc_transport_get_default_name (name, name_len, prefix, id, group_id, suffix); +} + +/* + * DiagnosticsIpcPollHandle. + */ + +DS_RT_DEFINE_ARRAY (ipc_poll_handle_array, ds_rt_ipc_poll_handle_array_t, ds_rt_ipc_poll_handle_array_iterator_t, DiagnosticsIpcPollHandle) +DS_RT_DEFINE_ARRAY_ITERATOR (ipc_poll_handle_array, ds_rt_ipc_poll_handle_array_t, ds_rt_ipc_poll_handle_array_iterator_t, DiagnosticsIpcPollHandle) + +/* + * DiagnosticsPort. + */ + +DS_RT_DEFINE_ARRAY (port_array, ds_rt_port_array_t, ds_rt_port_array_iterator_t, DiagnosticsPort *) +DS_RT_DEFINE_ARRAY_ITERATOR (port_array, ds_rt_port_array_t, ds_rt_port_array_iterator_t, DiagnosticsPort *) + +DS_RT_DEFINE_ARRAY (port_config_array, ds_rt_port_config_array_t, ds_rt_port_config_array_iterator_t, ep_char8_t *) +DS_RT_DEFINE_ARRAY_ITERATOR (port_config_array, ds_rt_port_config_array_t, ds_rt_port_config_array_iterator_t, ep_char8_t *) + +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_RT_MONO_H__ */ diff --git a/src/mono/mono/eventpipe/ds-rt-types-mono.h b/src/mono/mono/eventpipe/ds-rt-types-mono.h new file mode 100644 index 00000000000000..9b6778f5a1d67e --- /dev/null +++ b/src/mono/mono/eventpipe/ds-rt-types-mono.h @@ -0,0 +1,21 @@ +// Implementation of ds-rt-types.h targeting Mono runtime. +#ifndef __DIAGNOSTICS_RT_TYPES_MONO_H__ +#define __DIAGNOSTICS_RT_TYPES_MONO_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#include "ep-rt-types-mono.h" + +typedef struct _rt_mono_array_internal_t ds_rt_port_array_t; +typedef struct _rt_mono_array_iterator_internal_t ds_rt_port_array_iterator_t; + +typedef struct _rt_mono_array_internal_t ds_rt_port_config_array_t; +typedef struct _rt_mono_array_iterator_internal_t ds_rt_port_config_array_iterator_t; + +typedef struct _rt_mono_array_internal_t ds_rt_ipc_poll_handle_array_t; +typedef struct _rt_mono_array_iterator_internal_t ds_rt_ipc_poll_handle_array_iterator_t; + +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_RT_TYPES_MONO_H__ */ diff --git a/src/mono/mono/eventpipe/ds-rt-types.h b/src/mono/mono/eventpipe/ds-rt-types.h new file mode 100644 index 00000000000000..a01f5b4b759c42 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-rt-types.h @@ -0,0 +1,11 @@ +#ifndef __DIAGNOSTICS_RT_TYPES_H__ +#define __DIAGNOSTICS_RT_TYPES_H__ + +#include + +#ifdef ENABLE_PERFTRACING + +#include "ds-rt-types-mono.h" + +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_RT_TYPES_H__ */ diff --git a/src/mono/mono/eventpipe/ds-rt.h b/src/mono/mono/eventpipe/ds-rt.h new file mode 100644 index 00000000000000..54ca5b0ae6e0fd --- /dev/null +++ b/src/mono/mono/eventpipe/ds-rt.h @@ -0,0 +1,83 @@ +#ifndef __DIAGNOSTICS_RT_H__ +#define __DIAGNOSTICS_RT_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt.h" +#include "ds-rt-config.h" +#include "ds-types.h" + +#define DS_LOG_ALWAYS_0(msg) ds_rt_redefine +#define DS_LOG_ALWAYS_1(msg, data1) ds_rt_redefine +#define DS_LOG_ALWAYS_2(msg, data1, data2) ds_rt_redefine +#define DS_LOG_INFO_0(msg) ds_rt_redefine +#define DS_LOG_INFO_1(msg, data1) ds_rt_redefine +#define DS_LOG_INFO_2(msg, data1, data2) ds_rt_redefine +#define DS_LOG_ERROR_0(msg) ds_rt_redefine +#define DS_LOG_ERROR_1(msg, data1) ds_rt_redefine +#define DS_LOG_ERROR_2(msg, data1, data2) ds_rt_redefine +#define DS_LOG_WARNING_0(msg) ds_rt_redefine +#define DS_LOG_WARNING_1(msg, data1) ds_rt_redefine +#define DS_LOG_WARNING_2(msg, data1, data2) ds_rt_redefine + +#define DS_ENTER_BLOCKING_PAL_SECTION ds_rt_redefine +#define DS_EXIT_BLOCKING_PAL_SECTION ds_rt_redefine + +#define DS_RT_DECLARE_ARRAY(array_name, array_type, iterator_type, item_type) \ + EP_RT_DECLARE_ARRAY_PREFIX(ds, array_name, array_type, iterator_type, item_type) + +#define DS_RT_DECLARE_ARRAY_ITERATOR(array_name, array_type, iterator_type, item_type) \ + EP_RT_DECLARE_ARRAY_ITERATOR_PREFIX(ds, array_name, array_type, iterator_type, item_type) + +/* + * DiagnosticsConfiguration. + */ + +static +bool +ds_rt_config_value_get_enable (void); + +static +ep_char8_t * +ds_rt_config_value_get_ports (void); + +static +int32_t +ds_rt_config_value_get_default_port_suspend (void); + +/* + * DiagnosticsIpc. + */ + +static +void +ds_rt_transport_get_default_name ( + ep_char8_t *name, + int32_t name_len, + const ep_char8_t *prefix, + int32_t id, + const ep_char8_t *group_id, + const ep_char8_t *suffix); + +/* + * DiagnosticsIpcPollHandle. + */ + +DS_RT_DECLARE_ARRAY (ipc_poll_handle_array, ds_rt_ipc_poll_handle_array_t, ds_rt_ipc_poll_handle_array_iterator_t, DiagnosticsIpcPollHandle) +DS_RT_DECLARE_ARRAY_ITERATOR (ipc_poll_handle_array, ds_rt_ipc_poll_handle_array_t, ds_rt_ipc_poll_handle_array_iterator_t, DiagnosticsIpcPollHandle) + +/* + * DiagnosticsPort. + */ + +DS_RT_DECLARE_ARRAY (port_array, ds_rt_port_array_t, ds_rt_port_array_iterator_t, DiagnosticsPort *) +DS_RT_DECLARE_ARRAY_ITERATOR (port_array, ds_rt_port_array_t, ds_rt_port_array_iterator_t, DiagnosticsPort *) + +DS_RT_DECLARE_ARRAY (port_config_array, ds_rt_port_config_array_t, ds_rt_port_array_iterator_t, ep_char8_t *) +DS_RT_DECLARE_ARRAY_ITERATOR (port_config_array, ds_rt_port_config_array_t, ds_rt_port_array_iterator_t, ep_char8_t *) + +#include "ds-rt-mono.h" + +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_RT_H__ */ diff --git a/src/mono/mono/eventpipe/ds-server.c b/src/mono/mono/eventpipe/ds-server.c new file mode 100644 index 00000000000000..6ed3635c805330 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-server.c @@ -0,0 +1,317 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" + +// Option to include all internal source files into ds-server.c. +#ifdef DS_INCLUDE_SOURCE_FILES +#define DS_FORCE_INCLUDE_SOURCE_FILES +#include "ds-ipc.c" +#ifdef HOST_WIN32 +#include "ds-ipc-win32.c" +#else +#include "ds-ipc-posix.c" +#endif +#include "ds-protocol.c" +#include "ds-eventpipe-protocol.c" +#include "ds-process-protocol.c" +#include "ds-dump-protocol.c" +#include "ds-profiler-protocol.c" +#else +#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 "ep-stream.h" +#endif + +#ifdef FEATURE_AUTO_TRACE +// TODO: Implement +#include "ds-autotrace.h" +#endif + +/* + * 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 +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 +void +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.\n", 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.\n", code, message); +} + +static +void +server_protocol_helper_unknown_command ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + DS_LOG_WARNING_1 ("Received unknown request type (%d)\n", ds_ipc_message_header_get_commandset (ds_ipc_message_get_header (&message))); + ds_ipc_message_send_error (stream, DS_IPC_E_UNKNOWN_COMMAND); + ds_ipc_stream_free (stream); +} + +static +void +server_warning_callback ( + const ep_char8_t *message, + uint32_t code) +{ + EP_ASSERT (message != NULL); + DS_LOG_WARNING_2 ("warning (%d): %s.\n", code, message); +} + +EP_RT_DEFINE_THREAD_FUNC (server_thread) +{ + EP_ASSERT (server_volatile_load_shutting_down_state () == true || ds_ipc_stream_factory_has_active_ports () == true); + + if (!ds_ipc_stream_factory_has_active_ports ()) { + DS_LOG_ERROR_0 ("Diagnostics IPC listener was undefined\n"); + return 1; + } + + ep_rt_thread_setup (true); + + while (!server_volatile_load_shutting_down_state ()) { + DiagnosticsIpcStream *stream = ds_ipc_stream_factory_get_next_available_stream (server_warning_callback); + if (!stream) + continue; + +#ifdef FEATURE_AUTO_TRACE + // TODO: Implement + auto_trace_signal(); +#endif + + DiagnosticsIpcMessage message; + ds_ipc_message_init (&message); + 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)\n", ds_ipc_message_header_get_commandset (ds_ipc_message_get_header (&message)), ds_ipc_header_get_commandid (ds_ipc_message_get_header (&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; +#ifdef FEATURE_PROFAPI_ATTACH_DETACH + case DS_SERVER_COMMANDSET_PROFILER: + ds_profiler_protocol_helper_handle_ipc_message (&message, stream); + break; +#endif // FEATURE_PROFAPI_ATTACH_DETACH + default: + server_protocol_helper_unknown_command (&message, stream); + break; + } + + ds_ipc_message_fini (&message); + } + + ep_rt_thread_teardown (); + + return (ep_rt_thread_start_func_return_t)0; +} + +void +ds_server_disable (void) +{ + _server_disabled = true; +} + +bool +ds_server_init (void) +{ + ds_ipc_stream_factory_init (); + + if (_server_disabled || !ds_rt_config_value_get_enable ()) + return true; + + bool success = false; + + // Initialize the RuntimeIndentifier before use + ds_ipc_advertise_cookie_v1_init (); + + // Ports can fail to be configured + bool any_errors = !ds_ipc_stream_factory_configure (server_error_callback_create); + if (any_errors) + DS_LOG_ERROR_0 ("At least one Diagnostic Port failed to be configured.\n"); + + if (ds_ipc_stream_factory_any_suspended_ports ()) + ep_rt_wait_event_alloc (&_server_resume_runtime_startup_event, true, false); + + if (ds_ipc_stream_factory_has_active_ports ()) { +#ifdef FEATURE_AUTO_TRACE + // TODO: Implement. + auto_trace_init(); + auto_trace_launch(); +#endif + ep_rt_thread_id_t thread_id = 0; + + if (!ep_rt_thread_create ((void *)server_thread, NULL, (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).\n", ep_rt_get_last_error ()); + ep_raise_error (); + } else { +#ifdef FEATURE_AUTO_TRACE + // TODO: Implement. + auto_trace_wait(); +#endif + success = true; + } + } + +ep_on_exit: + return success; + +ep_on_error: + success = false; + 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 (); + 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) +{ + ep_char8_t *ports = NULL; + wchar_t *ports_wcs = NULL; + int32_t port_suspended = 0; + + 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) { + ports = ds_rt_config_value_get_ports (); + ports_wcs = ports ? ep_rt_utf8_to_wcs_string (ports, -1) : NULL; + port_suspended = ds_rt_config_value_get_default_port_suspend (); + + printf ("The runtime has been configured to pause during startup and is awaiting a Diagnostics IPC ResumeStartup command from a Diagnostic Port.\n"); + printf ("DOTNET_DiagnosticPorts=\"%ls\"\n", ports_wcs == NULL ? L"" : ports_wcs); + printf("DOTNET_DefaultDiagnosticPortSuspend=%d\n", port_suspended); + fflush (stdout); + + 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 + + ep_rt_wcs_string_free (ports_wcs); + ep_rt_utf8_string_free (ports); +} + +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); +} + +#endif /* ENABLE_PERFTRACING */ + +extern const char quiet_linker_empty_file_warning_diagnostics_server; +const char quiet_linker_empty_file_warning_diagnostics_server = 0; diff --git a/src/mono/mono/eventpipe/ds-server.h b/src/mono/mono/eventpipe/ds-server.h new file mode 100644 index 00000000000000..d9f6e7680e4464 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-server.h @@ -0,0 +1,39 @@ +#ifndef __DIAGNOSTICS_SERVER_H__ +#define __DIAGNOSTICS_SERVER_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ds-rt-config.h" +#include "ds-types.h" +#include "ds-rt.h" + +/* + * DiagnosticsServer. + */ + +void +ds_server_disable (void); + +// Initialize the event pipe (Creates the EventPipe IPC server). +bool +ds_server_init (void); + +// Shutdown the event pipe. +bool +ds_server_shutdown (void); + +// Pauses runtime startup after the Diagnostics Server has been started +// allowing a Diagnostics Monitor to attach perform tasks before +// Startup is completed +EP_NEVER_INLINE +void +ds_server_pause_for_diagnostics_monitor (void); + +// Sets event to resume startup in runtime +// This is a no-op if not configured to pause or runtime has already resumed +void +ds_server_resume_runtime_startup (void); + +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_SERVER_H__ */ diff --git a/src/mono/mono/eventpipe/ds-types.h b/src/mono/mono/eventpipe/ds-types.h new file mode 100644 index 00000000000000..887cad1a838be0 --- /dev/null +++ b/src/mono/mono/eventpipe/ds-types.h @@ -0,0 +1,178 @@ +#ifndef __DIAGNOSTICS_TYPES_H__ +#define __DIAGNOSTICS_TYPES_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-types.h" +#include "ds-rt-types.h" + +#undef DS_IMPL_GETTER_SETTER +#ifdef DS_IMPL_IPC_GETTER_SETTER +#define DS_IMPL_GETTER_SETTER +#endif +#include "ds-getter-setter.h" + +/* + * Diagnostics Structs. + */ + +typedef struct _DiagnosticsConnectPort DiagnosticsConnectPort; +typedef struct _DiagnosticsIpc DiagnosticsIpc; +typedef struct _DiagnosticsIpcHeader DiagnosticsIpcHeader; +typedef struct _DiagnosticsIpcMessage DiagnosticsIpcMessage; +typedef struct _DiagnosticsIpcPollHandle DiagnosticsIpcPollHandle; +typedef struct _DiagnosticsIpcStream DiagnosticsIpcStream; +typedef struct _DiagnosticsListenPort DiagnosticsListenPort; +typedef struct _DiagnosticsPort DiagnosticsPort; +typedef struct _DiagnosticsPortBuilder DiagnosticsPortBuilder; +typedef struct _DiagnosticsPortVtable DiagnosticsPortVtable; +typedef struct _DiagnosticsProcessInfoPayload DiagnosticsProcessInfoPayload; +typedef struct _EventPipeCollectTracingCommandPayload EventPipeCollectTracingCommandPayload; +typedef struct _EventPipeCollectTracing2CommandPayload EventPipeCollectTracing2CommandPayload; +typedef struct _EventPipeStopTracingCommandPayload EventPipeStopTracingCommandPayload; + +/* + * Diagnostics Enums. + */ + +typedef enum { + DS_IPC_MAGIC_VERSION_DOTNET_IPC_V1 = 0x01, + // FUTURE +} DiagnosticsIpcMagicVersion; + +typedef enum { + // reserved = 0x00, + DS_SERVER_COMMANDSET_DUMP = 0x01, + DS_SERVER_COMMANDSET_EVENTPIPE = 0x02, + DS_SERVER_COMMANDSET_PROFILER = 0x03, + DS_SERVER_COMMANDSET_PROCESS = 0x04, + DS_SERVER_COMMANDSET_SERVER = 0xFF +} DiagnosticsServerCommandSet; + +// The event pipe command set is 0x02 +// see ds-ipc.h and ds-server.h for more details +typedef enum { + DS_PROCESS_COMMANDID_GET_PROCESS_INFO = 0x00, + DS_PROCESS_COMMANDID_RESUME_RUNTIME = 0x01, + DS_PROCESS_COMMANDID_GET_PROCESS_ENV = 0x02, + // future +} DiagnosticsProcessCommandId; + +// Overlaps with DiagnosticsServerCommandId +// DON'T create overlapping values +typedef enum { + DS_SERVER_RESPONSEID_OK = 0x00, + // future + DS_SERVER_RESPONSEID_ERROR = 0xFF, +} DiagnosticsServerResponseId; + +// The event pipe command set is 0x02 +// see ds-ipc.h and ds-server.h for more details +typedef enum { + EP_COMMANDID_STOP_TRACING = 0x01, + EP_COMMANDID_COLLECT_TRACING = 0x02, + EP_COMMANDID_COLLECT_TRACING_2 = 0x03, + // future +} EventPipeCommandId; + +typedef enum { + DS_IPC_CONNECTION_MODE_CONNECT, + DS_IPC_CONNECTION_MODE_LISTEN +} DiagnosticsIpcConnectionMode; + +typedef enum { + DS_IPC_POLL_EVENTS_NONE = 0x00, // no events + DS_IPC_POLL_EVENTS_SIGNALED = 0x01, // ready for use + DS_IPC_POLL_EVENTS_HANGUP = 0x02, // connection remotely closed + DS_IPC_POLL_EVENTS_ERR = 0x04, // error + DS_IPC_POLL_EVENTS_UNKNOWN = 0x80 // unknown state +} DiagnosticsIpcPollEvents; + +typedef enum { + DS_PORT_TYPE_LISTEN = 0, + DS_PORT_TYPE_CONNECT = 1 +} DiagnosticsPortType; + +typedef enum { + DS_PORT_SUSPEND_MODE_NOSUSPEND = 0, + DS_PORT_SUSPEND_MODE_SUSPEND = 1 +} DiagnosticsPortSuspendMode; + +#define DOTNET_IPC_V1_MAGIC "DOTNET_IPC_V1" +#define DOTNET_IPC_V1_ADVERTISE_MAGIC "ADVR_V1" +#define DOTNET_IPC_V1_ADVERTISE_SIZE 34 + +#if BIGENDIAN +#define DS_VAL16(x) (((x) >> 8) | ((x) << 8)) +#define DS_VAL32(y) (((y) >> 24) | (((y) >> 8) & 0x0000FF00L) | (((y) & 0x0000FF00L) << 8) | ((y) << 24)) +#define DS_VAL64(z) (((uint64_t)DS_VAL32(z) << 32) | DS_VAL32((z) >> 32)) +#else +#define DS_VAL16(x) x +#define DS_VAL32(x) x +#define DS_VAL64(x) x +#endif // BIGENDIAN + +#define DS_IPC_S_OK ((uint32_t)(0L)) +#define DS_IPC_E_BAD_ENCODING ((uint32_t)(0x80131384L)) +#define DS_IPC_E_UNKNOWN_COMMAND ((uint32_t)(0x80131385L)) +#define DS_IPC_E_UNKNOWN_MAGIC ((uint32_t)(0x80131386L)) +#define DS_IPC_E_NOTSUPPORTED ((uint32_t)(0x80131515L)) +#define DS_IPC_E_FAIL ((uint32_t)(0x80004005L)) + +// Polling timeout semantics +// If client connection is opted in +// and connection succeeds => set timeout to infinite +// and connection fails => set timeout to minimum and scale by falloff factor +// else => set timeout to -1 (infinite) +// +// If an agent closes its socket while we're still connected, +// Poll will return and let us know which connection hung up +#define DS_IPC_POLL_TIMEOUT_FALLOFF_FACTOR (float)1.25 +#define DS_IPC_STREAM_TIMEOUT_INFINITE (int32_t)-1 +#define DS_IPC_POLL_TIMEOUT_INFINITE (int32_t)-1 +#define DS_IPC_POLL_TIMEOUT_MIN_MS (int32_t)10 +#define DS_IPC_POLL_TIMEOUT_MAX_MS (int32_t)500 + +typedef void (*ds_ipc_error_callback_func)( + const ep_char8_t *message, + uint32_t code); + +/* + * DiagnosticsIpcPollHandle. + */ + +// The bookeeping struct used for polling on server and client structs +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_IPC_GETTER_SETTER) +struct _DiagnosticsIpcPollHandle { +#else +struct _DiagnosticsIpcPollHandle_Internal { +#endif + // Only one of these will be non-null, treat as a union + DiagnosticsIpc *ipc; + DiagnosticsIpcStream *stream; + + // contains some set of PollEvents + // will be set by Poll + // Any values here are ignored by Poll + uint8_t events; + + // a cookie assignable by upstream users for additional bookkeeping + void *user_data; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_IPC_GETTER_SETTER) +struct _DiagnosticsIpcPollHandle { + uint8_t _internal [sizeof (struct _DiagnosticsIpcPollHandle_Internal)]; +}; +#endif + +DS_DEFINE_GETTER(DiagnosticsIpcPollHandle *, ipc_poll_handle, DiagnosticsIpc *, ipc) +DS_DEFINE_GETTER(DiagnosticsIpcPollHandle *, ipc_poll_handle, DiagnosticsIpcStream *, stream) +DS_DEFINE_GETTER(DiagnosticsIpcPollHandle *, ipc_poll_handle, uint8_t, events) +DS_DEFINE_SETTER(DiagnosticsIpcPollHandle *, ipc_poll_handle, uint8_t, events) +DS_DEFINE_GETTER(DiagnosticsIpcPollHandle *, ipc_poll_handle, void *, user_data) +DS_DEFINE_SETTER(DiagnosticsIpcPollHandle *, ipc_poll_handle, void *, user_data) + +#endif /* ENABLE_PERFTRACING */ +#endif /* __DIAGNOSTICS_TYPES_H__ */ diff --git a/src/mono/mono/eventpipe/ep-block.c b/src/mono/mono/eventpipe/ep-block.c index 62d7faafb519f7..50f23c60e76c11 100644 --- a/src/mono/mono/eventpipe/ep-block.c +++ b/src/mono/mono/eventpipe/ep-block.c @@ -14,6 +14,34 @@ * Forward declares of all static functions. */ +static +int32_t +block_get_file_version (EventPipeSerializationFormat format); + +static +int32_t +block_get_file_minimum_version (EventPipeSerializationFormat format); + +static +void +block_fast_serialize_func ( + void *object, + FastSerializer *fast_serializer); + +static +void +block_clear_func (void *object); + +static +void +block_serialize_header_func ( + void *object, + FastSerializer *fast_serializer); + +static +uint32_t +block_get_header_size_func (void *object); + static void block_base_fast_serialize_func ( @@ -102,6 +130,74 @@ stack_block_serialize_header_func (void *object, FastSerializer *fast_serializer * EventPipeBlock */ +static +int32_t +block_get_block_version (EventPipeSerializationFormat format) +{ + switch (format) { + case EP_SERIALIZATION_FORMAT_NETPERF_V3 : + return 1; + case EP_SERIALIZATION_FORMAT_NETTRACE_V4 : + return 2; + default : + EP_ASSERT (!"Unrecognized EventPipeSerializationFormat"); + return 0; + } +} + +static +int32_t +block_get_block_minimum_version (EventPipeSerializationFormat format) +{ + switch (format) { + case EP_SERIALIZATION_FORMAT_NETPERF_V3 : + return 0; + case EP_SERIALIZATION_FORMAT_NETTRACE_V4 : + return 2; + default : + EP_ASSERT (!"Unrecognized EventPipeSerializationFormat"); + return 0; + } +} + +static +void +block_fast_serialize_func ( + void *object, + FastSerializer *fast_serializer) +{ + EP_ASSERT (object != NULL); + EP_ASSERT (fast_serializer != NULL); + + ep_block_fast_serialize ((EventPipeBlock *)object, fast_serializer); +} + +static +void +block_clear_func (void *object) +{ + EP_ASSERT (object != NULL); + ep_block_clear ((EventPipeBlock *)object); +} + +static +void +block_serialize_header_func ( + void *object, + FastSerializer *fast_serializer) +{ + EP_ASSERT (object != NULL); + EP_ASSERT (fast_serializer != NULL); +} + +static +uint32_t +block_get_header_size_func (void *object) +{ + EP_ASSERT (object != NULL); + return 0; +} + EventPipeBlock * ep_block_init ( EventPipeBlock *block, @@ -115,8 +211,8 @@ ep_block_init ( ep_raise_error_if_nok (ep_fast_serializable_object_init ( &block->fast_serializer_object, (FastSerializableObjectVtable *)vtable, - ep_file_get_file_version (format), - ep_file_get_file_minimum_version (format), + block_get_block_version (format), + block_get_block_minimum_version (format), format >= EP_SERIALIZATION_FORMAT_NETTRACE_V4) != NULL); block->block = ep_rt_byte_array_alloc (max_block_size); @@ -241,7 +337,7 @@ ep_block_fast_serialize ( EP_ASSERT (required_padding <= FAST_SERIALIZER_ALIGNMENT_SIZE - 1); ep_fast_serializer_write_buffer (fast_serializer, max_padding, required_padding); // we write zeros here, the reader is going to always read from the first aligned address of the serialized content - EP_ASSERT (ep_fast_serializer_get_write_error_encountered (fast_serializer) || ep_fast_serializer_get_required_padding (fast_serializer)); + EP_ASSERT (ep_fast_serializer_get_write_error_encountered (fast_serializer) || (ep_fast_serializer_get_required_padding (fast_serializer) == 0)); } ep_block_serialize_header_vcall (block, fast_serializer); @@ -545,7 +641,7 @@ ep_event_block_base_write_event ( uint32_t total_size = 1 + bytes_written + data_len; if (write_pointer + total_size >= block->end_of_the_buffer) { - //TODO: Orignal EP updates blocks write pointer continiously, doing the same here before + // TODO: Orignal EP updates blocks write pointer continiously, doing the same here before //bailing out. Question is if that is intentional or just a side effect of directly updating //the member. block->write_pointer = write_pointer; @@ -746,9 +842,9 @@ sequence_point_get_block_size (EventPipeSequencePoint *sequence_point) const uint32_t thread_count = ep_rt_thread_sequence_number_map_count (ep_sequence_point_get_thread_sequence_numbers_cref (sequence_point)); - return sizeof (ep_sequence_point_sizeof_timestamp (sequence_point)) + + return (int32_t)(ep_sequence_point_sizeof_timestamp (sequence_point) + sizeof (uint32_t) + //thread count - thread_count * size_of_sequence_number; + thread_count * size_of_sequence_number); } static @@ -771,17 +867,17 @@ void sequence_point_block_fini (EventPipeSequencePointBlock *sequence_point_block) { EP_ASSERT (sequence_point_block != NULL); - ep_event_block_base_fini (&sequence_point_block->event_block_base); + ep_block_fini (&sequence_point_block->block); } static EventPipeBlockVtable sequence_point_block_vtable = { { sequence_point_block_free_func, - block_base_fast_serialize_func, + block_fast_serialize_func, sequence_point_block_get_type_name_func }, - block_base_clear_func, - block_base_get_header_size_func, - block_base_serialize_header_func }; + block_clear_func, + block_get_header_size_func, + block_serialize_header_func }; EventPipeSequencePointBlock * ep_sequence_point_block_alloc (EventPipeSequencePoint *sequence_point) @@ -807,20 +903,19 @@ ep_sequence_point_block_init ( EP_ASSERT (sequence_point_block != NULL); EP_ASSERT (sequence_point != NULL); - ep_return_null_if_nok (ep_event_block_base_init ( - &sequence_point_block->event_block_base, + ep_return_null_if_nok (ep_block_init ( + &sequence_point_block->block, &sequence_point_block_vtable, sequence_point_get_block_size (sequence_point), - EP_SERIALIZATION_FORMAT_NETTRACE_V4, - true) != NULL); + EP_SERIALIZATION_FORMAT_NETTRACE_V4) != NULL); const ep_timestamp_t timestamp = ep_sequence_point_get_timestamp (sequence_point); - memcpy (sequence_point_block->event_block_base.block.write_pointer, ×tamp, sizeof (timestamp)); - sequence_point_block->event_block_base.block.write_pointer += sizeof (timestamp); + memcpy (sequence_point_block->block.write_pointer, ×tamp, sizeof (timestamp)); + sequence_point_block->block.write_pointer += sizeof (timestamp); const uint32_t thread_count = ep_rt_thread_sequence_number_map_count (ep_sequence_point_get_thread_sequence_numbers_cref (sequence_point)); - memcpy (sequence_point_block->event_block_base.block.write_pointer, &thread_count, sizeof (thread_count)); - sequence_point_block->event_block_base.block.write_pointer += sizeof (thread_count); + memcpy (sequence_point_block->block.write_pointer, &thread_count, sizeof (thread_count)); + sequence_point_block->block.write_pointer += sizeof (thread_count); ep_rt_thread_sequence_number_hash_map_iterator_t iterator; for (ep_rt_thread_sequence_number_map_iterator_begin (ep_sequence_point_get_thread_sequence_numbers_cref (sequence_point), &iterator); @@ -830,12 +925,12 @@ ep_sequence_point_block_init ( const EventPipeThreadSessionState *key = ep_rt_thread_sequence_number_map_iterator_key (&iterator); const uint64_t thread_id = ep_thread_get_os_thread_id (ep_thread_session_state_get_thread (key)); - memcpy (sequence_point_block->event_block_base.block.write_pointer, &thread_id, sizeof (thread_id)); - sequence_point_block->event_block_base.block.write_pointer += sizeof (thread_id); + memcpy (sequence_point_block->block.write_pointer, &thread_id, sizeof (thread_id)); + sequence_point_block->block.write_pointer += sizeof (thread_id); const uint32_t sequence_number = ep_rt_thread_sequence_number_map_iterator_value (&iterator); - memcpy (sequence_point_block->event_block_base.block.write_pointer, &sequence_number, sizeof (sequence_number)); - sequence_point_block->event_block_base.block.write_pointer += sizeof (sequence_number); + memcpy (sequence_point_block->block.write_pointer, &sequence_number, sizeof (sequence_number)); + sequence_point_block->block.write_pointer += sizeof (sequence_number); } return sequence_point_block; @@ -888,7 +983,7 @@ stack_block_clear_func (void *object) stack_block->has_initial_index = 0; stack_block->count = 0; - ep_block_clear (&stack_block->event_block_base.block); + ep_block_clear (&stack_block->block); } static @@ -917,7 +1012,7 @@ stack_block_serialize_header_func ( static EventPipeBlockVtable stack_block_vtable = { { stack_block_free_func, - block_base_fast_serialize_func, + block_fast_serialize_func, stack_block_get_type_name_func }, stack_block_clear_func, stack_block_get_header_size_func, @@ -929,12 +1024,11 @@ ep_stack_block_alloc (uint32_t max_block_size) EventPipeStackBlock *instance = ep_rt_object_alloc (EventPipeStackBlock); ep_raise_error_if_nok (instance != NULL); - ep_raise_error_if_nok (ep_event_block_base_init ( - &instance->event_block_base, + ep_raise_error_if_nok (ep_block_init ( + &instance->block, &stack_block_vtable, max_block_size, - EP_SERIALIZATION_FORMAT_NETTRACE_V4, - true) != NULL); + EP_SERIALIZATION_FORMAT_NETTRACE_V4) != NULL); stack_block_clear_func (instance); @@ -952,7 +1046,7 @@ ep_stack_block_free (EventPipeStackBlock *stack_block) { ep_return_void_if_nok (stack_block != NULL); - ep_event_block_base_fini (&stack_block->event_block_base); + ep_block_fini (&stack_block->block); ep_rt_object_free (stack_block); } @@ -968,7 +1062,7 @@ ep_stack_block_write_stack ( uint32_t stack_size = ep_stack_contents_get_size (stack); uint32_t total_size = sizeof (stack_size) + stack_size; - EventPipeBlock *block = &stack_block->event_block_base.block; + EventPipeBlock *block = &stack_block->block; uint8_t *write_pointer = block->write_pointer; ep_raise_error_if_nok (write_pointer + total_size < block->end_of_the_buffer); diff --git a/src/mono/mono/eventpipe/ep-block.h b/src/mono/mono/eventpipe/ep-block.h index 29d22125040eba..59d9f3f1e9a4fc 100644 --- a/src/mono/mono/eventpipe/ep-block.h +++ b/src/mono/mono/eventpipe/ep-block.h @@ -297,7 +297,7 @@ struct _EventPipeSequencePointBlock { #else struct _EventPipeSequencePointBlock_Internal { #endif - EventPipeEventBlockBase event_block_base; + EventPipeBlock block; }; #if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_BLOCK_GETTER_SETTER) @@ -329,7 +329,7 @@ struct _EventPipeStackBlock { #else struct _EventPipeStackBlock_Internal { #endif - EventPipeEventBlockBase event_block_base; + EventPipeBlock block; uint32_t initial_index; uint32_t count; bool has_initial_index; @@ -378,4 +378,4 @@ ep_stack_block_clear (EventPipeStackBlock *stack_block) } #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_BLOCK_H__ **/ +#endif /* __EVENTPIPE_BLOCK_H__ */ diff --git a/src/mono/mono/eventpipe/ep-buffer-manager.c b/src/mono/mono/eventpipe/ep-buffer-manager.c index 07c5c5f03d0aa8..720d58a4bb3b23 100644 --- a/src/mono/mono/eventpipe/ep-buffer-manager.c +++ b/src/mono/mono/eventpipe/ep-buffer-manager.c @@ -362,7 +362,7 @@ buffer_manager_init_sequence_point_thread_list ( // This needs to come after querying the thread sequence numbers to ensure that any recorded // sequence number is <= the actual sequence number at this timestamp ep_buffer_manager_requires_lock_held (buffer_manager); - ep_sequence_point_set_timestamp (sequence_point, ep_rt_perf_counter_query ()); + ep_sequence_point_set_timestamp (sequence_point, ep_perf_timestamp_get ()); } static @@ -555,7 +555,7 @@ buffer_manager_move_next_event_any_thread ( ep_rt_buffer_array_t buffer_array; ep_rt_buffer_list_array_t buffer_list_array; - //TODO: Init on stack instead of alloc? + // TODO: Init on stack instead of alloc? ep_rt_buffer_array_alloc (&buffer_array); ep_rt_buffer_list_array_alloc (&buffer_list_array); @@ -1256,7 +1256,7 @@ ep_buffer_manager_get_next_event (EventPipeBufferManager *buffer_manager) // to accumulate in the write buffer before we converted it and forced the writer to allocate another. Other more // sophisticated approaches would probably build a low overhead synchronization mechanism to read and write the // buffer at the same time. - ep_timestamp_t stop_timetamp = ep_rt_perf_counter_query (); + ep_timestamp_t stop_timetamp = ep_perf_timestamp_get (); buffer_manager_move_next_event_any_thread (buffer_manager, stop_timetamp); return buffer_manager->current_event; } diff --git a/src/mono/mono/eventpipe/ep-buffer-manager.h b/src/mono/mono/eventpipe/ep-buffer-manager.h index 18ea00e377af2a..3674cb437dda5d 100644 --- a/src/mono/mono/eventpipe/ep-buffer-manager.h +++ b/src/mono/mono/eventpipe/ep-buffer-manager.h @@ -241,4 +241,4 @@ ep_buffer_manager_ensure_consistency (EventPipeBufferManager *buffer_manager); #endif #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_BUFFERMANAGER_H__ **/ +#endif /* __EVENTPIPE_BUFFERMANAGER_H__ */ diff --git a/src/mono/mono/eventpipe/ep-buffer.c b/src/mono/mono/eventpipe/ep-buffer.c index ca13ef066e3f7f..879785d5f2c3ab 100644 --- a/src/mono/mono/eventpipe/ep-buffer.c +++ b/src/mono/mono/eventpipe/ep-buffer.c @@ -34,7 +34,7 @@ ep_buffer_alloc ( instance->limit = instance->buffer + buffer_size; instance->current = ep_buffer_get_next_aligned_address (instance, instance->buffer); - instance->creation_timestamp = ep_perf_counter_query (); + instance->creation_timestamp = ep_perf_timestamp_get (); EP_ASSERT (instance->creation_timestamp > 0); instance->current_read_event = NULL; diff --git a/src/mono/mono/eventpipe/ep-buffer.h b/src/mono/mono/eventpipe/ep-buffer.h index b8bfe309ff6b1f..3aa4f635a8f15f 100644 --- a/src/mono/mono/eventpipe/ep-buffer.h +++ b/src/mono/mono/eventpipe/ep-buffer.h @@ -157,4 +157,4 @@ ep_buffer_ensure_consistency (const EventPipeBuffer *buffer); #endif #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_BUFFER_H__ **/ +#endif /* __EVENTPIPE_BUFFER_H__ */ diff --git a/src/mono/mono/eventpipe/ep-config-internals.h b/src/mono/mono/eventpipe/ep-config-internals.h index 672051148c165e..42a72318452e28 100644 --- a/src/mono/mono/eventpipe/ep-config-internals.h +++ b/src/mono/mono/eventpipe/ep-config-internals.h @@ -53,4 +53,4 @@ config_enable_disable ( bool enable); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_CONFIGURATION_INTERNALS_H__ **/ +#endif /* __EVENTPIPE_CONFIGURATION_INTERNALS_H__ */ diff --git a/src/mono/mono/eventpipe/ep-config.c b/src/mono/mono/eventpipe/ep-config.c index d649317418efad..d912b0fabdf0f3 100644 --- a/src/mono/mono/eventpipe/ep-config.c +++ b/src/mono/mono/eventpipe/ep-config.c @@ -340,8 +340,8 @@ ep_config_build_event_metadata_event ( EventPipeEvent *source_event = ep_event_instance_get_ep_event (source_instance); EventPipeProvider *provider = ep_event_get_provider (source_event); const ep_char16_t *provider_name_utf16 = ep_provider_get_provider_name_utf16 (provider); - const uint8_t *payload_data = ep_event_instance_get_data (source_instance); - uint32_t payload_data_len = ep_event_instance_get_data_len (source_instance); + const uint8_t *payload_data = ep_event_get_metadata (source_event); + uint32_t payload_data_len = ep_event_get_metadata_len (source_event); uint32_t provider_name_len = (ep_rt_utf16_string_len (provider_name_utf16) + 1) * sizeof (ep_char16_t); uint32_t instance_payload_size = sizeof (metadata_id) + provider_name_len + payload_data_len; diff --git a/src/mono/mono/eventpipe/ep-config.h b/src/mono/mono/eventpipe/ep-config.h index 7a7fc89f5c01fe..139866ee44ae49 100644 --- a/src/mono/mono/eventpipe/ep-config.h +++ b/src/mono/mono/eventpipe/ep-config.h @@ -144,4 +144,4 @@ void ep_event_metdata_event_free (EventPipeEventMetadataEvent *metadata_event); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_CONFIGURATION_H__ **/ +#endif /* __EVENTPIPE_CONFIGURATION_H__ */ diff --git a/src/mono/mono/eventpipe/ep-event-instance.c b/src/mono/mono/eventpipe/ep-event-instance.c index 0f0e9be55ab886..1b486aaffbf877 100644 --- a/src/mono/mono/eventpipe/ep-event-instance.c +++ b/src/mono/mono/eventpipe/ep-event-instance.c @@ -85,7 +85,7 @@ ep_event_instance_init ( event_instance->data = data; event_instance->data_len = data_len; - event_instance->timestamp = ep_perf_counter_query (); + event_instance->timestamp = ep_perf_timestamp_get (); EP_ASSERT (event_instance->timestamp > 0); ep_event_instance_ensure_consistency (event_instance); diff --git a/src/mono/mono/eventpipe/ep-event-instance.h b/src/mono/mono/eventpipe/ep-event-instance.h index c872433ec1e733..d67757048f1aeb 100644 --- a/src/mono/mono/eventpipe/ep-event-instance.h +++ b/src/mono/mono/eventpipe/ep-event-instance.h @@ -133,4 +133,4 @@ void ep_sequence_point_free (EventPipeSequencePoint *sequence_point); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_EVENT_INSTANCE_H__ **/ +#endif /* __EVENTPIPE_EVENT_INSTANCE_H__ */ diff --git a/src/mono/mono/eventpipe/ep-event-payload.c b/src/mono/mono/eventpipe/ep-event-payload.c index cffe7b2d370c0f..b09e0eed7d7977 100644 --- a/src/mono/mono/eventpipe/ep-event-payload.c +++ b/src/mono/mono/eventpipe/ep-event-payload.c @@ -140,11 +140,11 @@ ep_event_payload_copy_data ( if (event_payload->size > 0) { if (ep_event_payload_is_flattened (event_payload)) { memcpy (dst, event_payload->data, event_payload->size); - } else if (event_payload->data != NULL) { + } else if (event_payload->event_data != NULL) { uint32_t offset = 0; EventData *event_data = event_payload->event_data; for (uint32_t i = 0; i < event_payload->event_data_len; ++i) { - EP_ASSERT ((offset + ep_event_data_get_size (&event_data[i])) < event_payload->size); + EP_ASSERT ((offset + ep_event_data_get_size (&event_data[i])) <= event_payload->size); memcpy (dst + offset, (uint8_t *)ep_event_data_get_ptr (&event_data[i]), ep_event_data_get_size (&event_data[i])); offset += ep_event_data_get_size (&event_data[i]); } diff --git a/src/mono/mono/eventpipe/ep-event-payload.h b/src/mono/mono/eventpipe/ep-event-payload.h index 0ad000bf0b1978..00065f30f59fc6 100644 --- a/src/mono/mono/eventpipe/ep-event-payload.h +++ b/src/mono/mono/eventpipe/ep-event-payload.h @@ -125,4 +125,4 @@ uint8_t * ep_event_payload_get_flat_data (EventPipeEventPayload *event_payload); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_EVENT_PAYLOAD_H__ **/ +#endif /* __EVENTPIPE_EVENT_PAYLOAD_H__ */ diff --git a/src/mono/mono/eventpipe/ep-event-source.c b/src/mono/mono/eventpipe/ep-event-source.c index 10b8b47aaf749f..3d8a2c7abbcc2c 100644 --- a/src/mono/mono/eventpipe/ep-event-source.c +++ b/src/mono/mono/eventpipe/ep-event-source.c @@ -14,21 +14,21 @@ #include "ep-rt.h" #if defined(HOST_WINDOWS) || defined(HOST_WIN32) -const ep_char8_t* _ep_os_info = "windows"; -#elif defined(HOST_DARWIN) -const ep_char8_t* _ep_os_info = "osx"; +const ep_char8_t* _ep_os_info = "Windows"; #elif defined(HOST_IOS) -const ep_char8_t* _ep_os_info = "ios"; +const ep_char8_t* _ep_os_info = "iOS"; #elif defined(HOST_WATCHOS) -const ep_char8_t* _ep_os_info = "watchos"; +const ep_char8_t* _ep_os_info = "WatchOS"; #elif defined(HOST_TVOS) -const ep_char8_t* _ep_os_info = "tvos"; +const ep_char8_t* _ep_os_info = "tvOS"; +#elif defined(__APPLE__) +const ep_char8_t* _ep_os_info = "macOS"; #elif defined(HOST_ANDROID) -const ep_char8_t* _ep_os_info = "android"; +const ep_char8_t* _ep_os_info = "Android"; #elif defined(__linux__) -const ep_char8_t* _ep_os_info = "linux"; +const ep_char8_t* _ep_os_info = "Linux"; #else -const ep_char8_t* _ep_os_info = "unknown"; +const ep_char8_t* _ep_os_info = "Unknown"; #endif #if defined(TARGET_X86) @@ -40,7 +40,7 @@ const ep_char8_t* _ep_arch_info = "arm32"; #elif defined(TARGET_ARM64) const ep_char8_t* _ep_arch_info = "arm64"; #else -const ep_char8_t* _ep_arch_info = "unknown"; +const ep_char8_t* _ep_arch_info = "Unknown"; #endif EventPipeEventSource _ep_event_source_instance = { 0 }; diff --git a/src/mono/mono/eventpipe/ep-event-source.h b/src/mono/mono/eventpipe/ep-event-source.h index 350853ce81c33c..9eb95b24055de2 100644 --- a/src/mono/mono/eventpipe/ep-event-source.h +++ b/src/mono/mono/eventpipe/ep-event-source.h @@ -36,19 +36,19 @@ struct _EventPipeEventSource { static inline -const char * +const ep_char8_t * ep_event_source_get_os_info (void) { - extern const char *_ep_os_info; + extern const ep_char8_t *_ep_os_info; return _ep_os_info; } static inline -const char * +const ep_char8_t * ep_event_source_get_arch_info (void) { - extern const char *_ep_arch_info; + extern const ep_char8_t *_ep_arch_info; return _ep_arch_info; } @@ -81,4 +81,4 @@ ep_event_source_get (void) } #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_EVENT_SOURCE_H__ **/ +#endif /* __EVENTPIPE_EVENT_SOURCE_H__ */ diff --git a/src/mono/mono/eventpipe/ep-event.h b/src/mono/mono/eventpipe/ep-event.h index 40874166b1248c..ac3a90f63effbb 100644 --- a/src/mono/mono/eventpipe/ep-event.h +++ b/src/mono/mono/eventpipe/ep-event.h @@ -94,4 +94,4 @@ void ep_event_free (EventPipeEvent * ep_event); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_EVENT_H__ **/ +#endif /* __EVENTPIPE_EVENT_H__ */ diff --git a/src/mono/mono/eventpipe/ep-file.c b/src/mono/mono/eventpipe/ep-file.c index 6ee792baa69bce..863096e2e8366d 100644 --- a/src/mono/mono/eventpipe/ep-file.c +++ b/src/mono/mono/eventpipe/ep-file.c @@ -74,6 +74,14 @@ file_save_metadata_id ( EventPipeEvent *ep_event, uint32_t metadata_id); +static +int32_t +file_get_file_version (EventPipeSerializationFormat format); + +static +int32_t +file_get_file_minimum_version (EventPipeSerializationFormat format); + /* * EventPipeFile. */ @@ -290,6 +298,36 @@ file_save_metadata_id ( ep_rt_metadata_labels_add (&file->metadata_ids, ep_event, metadata_id); } +static +int32_t +file_get_file_version (EventPipeSerializationFormat format) +{ + switch (format) { + case EP_SERIALIZATION_FORMAT_NETPERF_V3 : + return 3; + case EP_SERIALIZATION_FORMAT_NETTRACE_V4 : + return 4; + default : + EP_ASSERT (!"Unrecognized EventPipeSerializationFormat"); + return 0; + } +} + +static +int32_t +file_get_file_minimum_version (EventPipeSerializationFormat format) +{ + switch (format) { + case EP_SERIALIZATION_FORMAT_NETPERF_V3 : + return 0; + case EP_SERIALIZATION_FORMAT_NETTRACE_V4 : + return 4; + default : + EP_ASSERT (!"Unrecognized EventPipeSerializationFormat"); + return 0; + } +} + EventPipeFile * ep_file_alloc ( StreamWriter *stream_writer, @@ -301,8 +339,8 @@ ep_file_alloc ( ep_raise_error_if_nok (ep_fast_serializable_object_init ( &instance->fast_serializable_object, &file_vtable, - ep_file_get_file_version (format), - ep_file_get_file_minimum_version (format), + file_get_file_version (format), + file_get_file_minimum_version (format), format >= EP_SERIALIZATION_FORMAT_NETTRACE_V4) != NULL); instance->stream_writer = stream_writer; @@ -318,8 +356,8 @@ ep_file_alloc ( ep_raise_error_if_nok (instance->stack_block != NULL); // File start time information. - instance->file_open_system_time = ep_rt_system_time_get (); - instance->file_open_timestamp = ep_perf_counter_query (); + ep_system_time_get (&instance->file_open_system_time); + instance->file_open_timestamp = ep_perf_timestamp_get (); instance->timestamp_frequency = ep_perf_frequency_query (); instance->pointer_size = SIZEOF_VOID_P; @@ -343,7 +381,7 @@ ep_file_alloc ( ep_rt_volatile_store_uint32_t (&instance->initialized, 0); #ifdef EP_CHECKED_BUILD - instance->last_sorted_timestamp = ep_perf_counter_query (); + instance->last_sorted_timestamp = ep_perf_timestamp_get (); #endif ep_on_exit: @@ -518,34 +556,6 @@ ep_file_flush ( } } -int32_t -ep_file_get_file_version (EventPipeSerializationFormat format) -{ - switch (format) { - case EP_SERIALIZATION_FORMAT_NETPERF_V3 : - return 3; - case EP_SERIALIZATION_FORMAT_NETTRACE_V4 : - return 4; - default : - EP_ASSERT (!"Unrecognized EventPipeSerializationFormat"); - return 0; - } -} - -int32_t -ep_file_get_file_minimum_version (EventPipeSerializationFormat format) -{ - switch (format) { - case EP_SERIALIZATION_FORMAT_NETPERF_V3 : - return 0; - case EP_SERIALIZATION_FORMAT_NETTRACE_V4 : - return 4; - default : - EP_ASSERT (!"Unrecognized EventPipeSerializationFormat"); - return 0; - } -} - /* * StackHashEntry. */ diff --git a/src/mono/mono/eventpipe/ep-file.h b/src/mono/mono/eventpipe/ep-file.h index 62940be46934db..01865d4eb56ad8 100644 --- a/src/mono/mono/eventpipe/ep-file.h +++ b/src/mono/mono/eventpipe/ep-file.h @@ -24,6 +24,8 @@ struct _EventPipeFile { struct _EventPipeFile_Internal { #endif FastSerializableObject fast_serializable_object; + // The system time when the file was opened. + EventPipeSystemTime file_open_system_time; // The frequency of the timestamps used for this file. int64_t timestamp_frequency; StreamWriter *stream_writer; @@ -35,8 +37,6 @@ struct _EventPipeFile_Internal { // Hashtable of metadata labels. ep_rt_metadata_labels_hash_map_t metadata_ids; ep_rt_stack_hash_map_t stack_hash; - // The system time when the file was opened. - ep_systemtime_t file_open_system_time; // The timestamp when the file was opened. Used for calculating file-relative timestamps. ep_timestamp_t file_open_timestamp; #ifdef EP_CHECKED_BUILD @@ -99,12 +99,6 @@ ep_file_flush ( EventPipeFile *file, EventPipeFileFlushFlags flags); -int32_t -ep_file_get_file_version (EventPipeSerializationFormat format); - -int32_t -ep_file_get_file_minimum_version (EventPipeSerializationFormat format); - /* * StackHashKey. */ @@ -169,4 +163,4 @@ void ep_stack_hash_entry_free (StackHashEntry *stack_hash_entry); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_FILE_H__ **/ +#endif /* __EVENTPIPE_FILE_H__ */ diff --git a/src/mono/mono/eventpipe/ep-getter-setter.h b/src/mono/mono/eventpipe/ep-getter-setter.h index 718aab19ddd2c1..b4a5f59f71bdc4 100644 --- a/src/mono/mono/eventpipe/ep-getter-setter.h +++ b/src/mono/mono/eventpipe/ep-getter-setter.h @@ -1,70 +1,155 @@ +#ifndef EP_BUILD_GETTER_GET_NAME +#define EP_BUILD_GETTER_GET_NAME(prefix_name, instance_type_name, instance_field_name) \ +prefix_name ## _ ## instance_type_name ## _get_ ## instance_field_name +#endif + +#ifndef EP_BUILD_GETTER_GET_REF_NAME +#define EP_BUILD_GETTER_GET_REF_NAME(prefix_name, instance_type_name, instance_field_name) \ +prefix_name ## _ ## instance_type_name ## _get_ ## instance_field_name ## _ref +#endif + +#ifndef EP_BUILD_GETTER_GET_CREF_NAME +#define EP_BUILD_GETTER_GET_CREF_NAME(prefix_name, instance_type_name, instance_field_name) \ +prefix_name ## _ ## instance_type_name ## _get_ ## instance_field_name ## _cref +#endif + +#ifndef EP_BUILD_GETTER_SIZEOF_NAME +#define EP_BUILD_GETTER_SIZEOF_NAME(prefix_name, instance_type_name, instance_field_name) \ +prefix_name ## _ ## instance_type_name ## _sizeof_ ## instance_field_name +#endif + +#ifndef EP_BUILD_SETTER_NAME +#define EP_BUILD_SETTER_NAME(prefix_name, instance_type_name, instance_field_name) \ +prefix_name ## _ ## instance_type_name ## _set_ ## instance_field_name +#endif + +#ifndef EP_DEFINE_INLINE_GETTER_PREFIX +#define EP_DEFINE_INLINE_GETTER_PREFIX(prefix_name, instance_type, instance_type_name, return_type, instance_field_name) \ + static inline return_type EP_BUILD_GETTER_GET_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance) { return instance-> instance_field_name; } \ + static inline size_t EP_BUILD_GETTER_SIZEOF_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance) { return sizeof (instance-> instance_field_name); } +#endif #ifndef EP_DEFINE_INLINE_GETTER #define EP_DEFINE_INLINE_GETTER(instance_type, instance_type_name, return_type, instance_field_name) \ - static inline return_type ep_ ## instance_type_name ## _get_ ## instance_field_name (const instance_type instance) { return instance-> instance_field_name; } \ - static inline size_t ep_ ## instance_type_name ## _sizeof_ ## instance_field_name (const instance_type instance) { return sizeof (instance-> instance_field_name); } + EP_DEFINE_INLINE_GETTER_PREFIX(ep, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef EP_DEFINE_INLINE_GETTER_REF_PREFIX +#define EP_DEFINE_INLINE_GETTER_REF_PREFIX(prefix_name, instance_type, instance_type_name, return_type, instance_field_name) \ + static inline return_type EP_BUILD_GETTER_GET_REF_NAME(prefix_name, instance_type_name, instance_field_name) (instance_type instance) { return &(instance-> instance_field_name); } \ + static inline const return_type EP_BUILD_GETTER_GET_CREF_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance) { return &(instance-> instance_field_name); } #endif #ifndef EP_DEFINE_INLINE_GETTER_REF #define EP_DEFINE_INLINE_GETTER_REF(instance_type, instance_type_name, return_type, instance_field_name) \ - static inline return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _ref (instance_type instance) { return &(instance-> instance_field_name); } \ - static inline const return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _cref (const instance_type instance) { return &(instance-> instance_field_name); } + EP_DEFINE_INLINE_GETTER_REF_PREFIX(ep, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef EP_DEFINE_INLINE_GETTER_ARRAY_REF_PREFIX +#define EP_DEFINE_INLINE_GETTER_ARRAY_REF_PREFIX(prefix_name, instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) \ + static inline return_type EP_BUILD_GETTER_GET_REF_NAME(prefix_name, instance_type_name, instance_field_name) (instance_type instance) { return &(instance-> instance_field); } \ + static inline const_return_type EP_BUILD_GETTER_GET_CREF_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance) { return &(instance-> instance_field); } #endif #ifndef EP_DEFINE_INLINE_GETTER_ARRAY_REF #define EP_DEFINE_INLINE_GETTER_ARRAY_REF(instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) \ - static inline return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _ref (instance_type instance) { return &(instance-> instance_field); } \ - static inline const_return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _cref (const instance_type instance) { return &(instance-> instance_field); } + EP_DEFINE_INLINE_GETTER_ARRAY_REF_PREFIX(ep, instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) +#endif + +#ifndef EP_DEFINE_INLINE_SETTER_PREFIX +#define EP_DEFINE_INLINE_SETTER_PREFIX(prefix_name, instance_type, instance_type_name, instance_field_type, instance_field_name) \ + static inline void EP_BUILD_SETTER_NAME(prefix_name, instance_type_name, instance_field_name) (instance_type instance, instance_field_type instance_field_name) { instance-> instance_field_name = instance_field_name; } #endif #ifndef EP_DEFINE_INLINE_SETTER -#define EP_DEFINE_INLINE_SETTER(instance_type, instance_type_name, instance_field_type, instance_field_name) static inline void ep_ ## instance_type_name ## _set_ ## instance_field_name (instance_type instance, instance_field_type instance_field_name) { instance-> instance_field_name = instance_field_name; } +#define EP_DEFINE_INLINE_SETTER(instance_type, instance_type_name, instance_field_type, instance_field_name) \ + EP_DEFINE_INLINE_SETTER_PREFIX(ep, instance_type, instance_type_name, instance_field_type, instance_field_name) +#endif + +#ifndef EP_DEFINE_NOINLINE_GETTER_PREFIX +#define EP_DEFINE_NOINLINE_GETTER_PREFIX(prefix_name, instance_type, instance_type_name, return_type, instance_field_name) \ + return_type EP_BUILD_GETTER_GET_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance); \ + size_t EP_BUILD_GETTER_SIZEOF_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance); #endif #ifndef EP_DEFINE_NOINLINE_GETTER #define EP_DEFINE_NOINLINE_GETTER(instance_type, instance_type_name, return_type, instance_field_name) \ - return_type ep_ ## instance_type_name ## _get_ ## instance_field_name (const instance_type instance); \ - size_t ep_ ## instance_type_name ## _sizeof_ ## instance_field_name (const instance_type instance); + EP_DEFINE_NOINLINE_GETTER_PREFIX(ep, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef EP_DEFINE_NOINLINE_GETTER_REF_PREFIX +#define EP_DEFINE_NOINLINE_GETTER_REF_PREFIX(prefix_name, instance_type, instance_type_name, return_type, instance_field_name) \ + return_type EP_BUILD_GETTER_GET_REF_NAME(prefix_name, instance_type_name, instance_field_name) (instance_type instance); \ + const return_type EP_BUILD_GETTER_GET_CREF_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance); #endif #ifndef EP_DEFINE_NOINLINE_GETTER_REF #define EP_DEFINE_NOINLINE_GETTER_REF(instance_type, instance_type_name, return_type, instance_field_name) \ - return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _ref (instance_type instance); \ - const return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _cref (const instance_type instance); + EP_DEFINE_NOINLINE_GETTER_REF_PREFIX(ep, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef EP_DEFINE_NOINLINE_GETTER_ARRAY_REF_PREFIX +#define EP_DEFINE_NOINLINE_GETTER_ARRAY_REF_PREFIX(prefix_name, instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) \ + return_type EP_BUILD_GETTER_GET_REF_NAME(prefix_name, instance_type_name, instance_field_name) (instance_type instance); \ + const_return_type EP_BUILD_GETTER_GET_CREF_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance); #endif #ifndef EP_DEFINE_NOINLINE_GETTER_ARRAY_REF #define EP_DEFINE_NOINLINE_GETTER_ARRAY_REF(instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) \ - return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _ref (instance_type instance); \ - const_return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _cref (const instance_type instance); + EP_DEFINE_NOINLINE_GETTER_ARRAY_REF_PREFIX(ep, instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) +#endif + +#ifndef EP_DEFINE_NOINLINE_SETTER_PREFIX +#define EP_DEFINE_NOINLINE_SETTER_PREFIX(prefix_name, instance_type, instance_type_name, instance_field_type, instance_field_name) \ + void EP_BUILD_SETTER_NAME(prefix_name, instance_type_name, instance_field_name) (instance_type instance, instance_field_type instance_field_name); #endif #ifndef EP_DEFINE_NOINLINE_SETTER #define EP_DEFINE_NOINLINE_SETTER(instance_type, instance_type_name, instance_field_type, instance_field_name) \ - void ep_ ## instance_type_name ## _set_ ## instance_field_name (instance_type instance, instance_field_type instance_field_name); + EP_DEFINE_NOINLINE_SETTER_PREFIX(ep, instance_type, instance_type_name, instance_field_type, instance_field_name) +#endif + +#ifndef EP_IMPL_GETTER_PREFIX +#define EP_IMPL_GETTER_PREFIX(prefix_name, instance_type, instance_type_name, return_type, instance_field_name) \ + return_type EP_BUILD_GETTER_GET_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance) { return instance-> instance_field_name; } \ + size_t EP_BUILD_GETTER_SIZEOF_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance) { return sizeof (instance-> instance_field_name); } #endif #ifndef EP_IMPL_GETTER #define EP_IMPL_GETTER(instance_type, instance_type_name, return_type, instance_field_name) \ - return_type ep_ ## instance_type_name ## _get_ ## instance_field_name (const instance_type instance) { return instance-> instance_field_name; } \ - size_t ep_ ## instance_type_name ## _sizeof_ ## instance_field_name (const instance_type instance) { return sizeof (instance-> instance_field_name); } + EP_IMPL_GETTER_PREFIX(ep, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef EP_IMPL_GETTER_REF_PREFIX +#define EP_IMPL_GETTER_REF_PREFIX(prefix_name, instance_type, instance_type_name, return_type, instance_field_name) \ + return_type EP_BUILD_GETTER_GET_REF_NAME(prefix_name, instance_type_name, instance_field_name) (instance_type instance) { return &(instance-> instance_field_name); } \ + const return_type EP_BUILD_GETTER_GET_CREF_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance) { return &(instance-> instance_field_name); } #endif #ifndef EP_IMPL_GETTER_REF #define EP_IMPL_GETTER_REF(instance_type, instance_type_name, return_type, instance_field_name) \ - return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _ref (instance_type instance) { return &(instance-> instance_field_name); } \ - const return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _cref (const instance_type instance) { return &(instance-> instance_field_name); } + EP_IMPL_GETTER_REF_PREFIX(ep, instance_type, instance_type_name, return_type, instance_field_name) +#endif + +#ifndef EP_IMPL_GETTER_ARRAY_REF_PREFIX +#define EP_IMPL_GETTER_ARRAY_REF_PREFIX(prefix_name, instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) \ + return_type EP_BUILD_GETTER_GET_REF_NAME(prefix_name, instance_type_name, instance_field_name) (instance_type instance) { return &(instance-> instance_field); } \ + const_return_type EP_BUILD_GETTER_GET_CREF_NAME(prefix_name, instance_type_name, instance_field_name) (const instance_type instance) { return &(instance-> instance_field); } #endif #ifndef EP_IMPL_GETTER_ARRAY_REF #define EP_IMPL_GETTER_ARRAY_REF(instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) \ - return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _ref (instance_type instance) { return &(instance-> instance_field); } \ - const_return_type ep_ ## instance_type_name ## _get_ ## instance_field_name ## _cref (const instance_type instance) { return &(instance-> instance_field); } + EP_IMPL_GETTER_ARRAY_REF_PREFIX(ep, instance_type, instance_type_name, return_type, const_return_type, instance_field_name, instance_field) +#endif + +#ifndef EP_IMPL_SETTER_PREFIX +#define EP_IMPL_SETTER_PREFIX(prefix_name, instance_type, instance_type_name, instance_field_type, instance_field_name) \ + void EP_BUILD_SETTER_NAME(prefix_name, instance_type_name, instance_field_name) (instance_type instance, instance_field_type instance_field_name) { instance-> instance_field_name = instance_field_name; } #endif #ifndef EP_IMPL_SETTER #define EP_IMPL_SETTER(instance_type, instance_type_name, instance_field_type, instance_field_name) \ - void ep_ ## instance_type_name ## _set_ ## instance_field_name (instance_type instance, instance_field_type instance_field_name) { instance-> instance_field_name = instance_field_name; } + EP_IMPL_SETTER_PREFIX(ep, instance_type, instance_type_name, instance_field_type, instance_field_name) #endif #undef EP_DEFINE_GETTER diff --git a/src/mono/mono/eventpipe/ep-metadata-generator.c b/src/mono/mono/eventpipe/ep-metadata-generator.c index 03ef9b35abd43f..79dca3fd86ca08 100644 --- a/src/mono/mono/eventpipe/ep-metadata-generator.c +++ b/src/mono/mono/eventpipe/ep-metadata-generator.c @@ -351,7 +351,7 @@ ep_parameter_desc_init ( EP_ASSERT (parameter_desc != NULL); parameter_desc->type = type; - parameter_desc->element_type = 0; + parameter_desc->element_type = EP_PARAMETER_TYPE_EMPTY; parameter_desc->name = name; return parameter_desc; diff --git a/src/mono/mono/eventpipe/ep-metadata-generator.h b/src/mono/mono/eventpipe/ep-metadata-generator.h index 8bbf21c759d1a2..51b100122ffeb3 100644 --- a/src/mono/mono/eventpipe/ep-metadata-generator.h +++ b/src/mono/mono/eventpipe/ep-metadata-generator.h @@ -62,4 +62,4 @@ void ep_parameter_desc_fini (EventPipeParameterDesc *parameter_desc); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_METADATA_GENERATOR_H__ **/ +#endif /* __EVENTPIPE_METADATA_GENERATOR_H__ */ diff --git a/src/mono/mono/eventpipe/ep-provider-internals.h b/src/mono/mono/eventpipe/ep-provider-internals.h index 5d38fb8f009b6e..83de57823c807d 100644 --- a/src/mono/mono/eventpipe/ep-provider-internals.h +++ b/src/mono/mono/eventpipe/ep-provider-internals.h @@ -41,4 +41,4 @@ void provider_invoke_callback (EventPipeProviderCallbackData *provider_callback_data); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_PROVIDER_INTERNALS_H__ **/ +#endif /* __EVENTPIPE_PROVIDER_INTERNALS_H__ */ diff --git a/src/mono/mono/eventpipe/ep-provider.h b/src/mono/mono/eventpipe/ep-provider.h index 645c4f78eb844b..f0cfa7e3bb3f39 100644 --- a/src/mono/mono/eventpipe/ep-provider.h +++ b/src/mono/mono/eventpipe/ep-provider.h @@ -121,4 +121,4 @@ ep_provider_set_delete_deferred ( bool deferred); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_PROVIDER_H__ **/ +#endif /* __EVENTPIPE_PROVIDER_H__ */ diff --git a/src/mono/mono/eventpipe/ep-rt-mono.c b/src/mono/mono/eventpipe/ep-rt-mono.c index 3b8b935c9cef2f..c9e7bf5a5066e5 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.c +++ b/src/mono/mono/eventpipe/ep-rt-mono.c @@ -4,11 +4,234 @@ #include "ep-rt-config.h" #include "ep-types.h" #include "ep-rt.h" +#include ep_rt_spin_lock_handle_t _ep_rt_mono_config_lock = {0}; EventPipeMonoFuncTable _ep_rt_mono_func_table = {0}; +mono_lazy_init_t _ep_rt_mono_os_cmd_line_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; +char *_ep_rt_mono_os_cmd_line = NULL; + +mono_lazy_init_t _ep_rt_mono_managed_cmd_line_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; +char *_ep_rt_mono_managed_cmd_line = NULL; + +#ifdef HOST_WIN32 +int64_t +ep_rt_mono_perf_counter_query (void) +{ + LARGE_INTEGER value; + if (QueryPerformanceCounter (&value)) + return (int64_t)value.QuadPart; + else + return 0; +} + +int64_t +ep_rt_mono_perf_frequency_query (void) +{ + LARGE_INTEGER value; + if (QueryPerformanceFrequency (&value)) + return (int64_t)value.QuadPart; + else + return 0; +} + +void +ep_rt_mono_system_time_get (EventPipeSystemTime *system_time) +{ + SYSTEMTIME value; + GetSystemTime (&value); + + EP_ASSERT (system_time != NULL); + ep_system_time_set ( + system_time, + value.wYear, + value.wMonth, + value.wDayOfWeek, + value.wDay, + value.wHour, + value.wMinute, + value.wSecond, + value.wMilliseconds); +} + +int64_t +ep_rt_mono_system_timestamp_get (void) +{ + FILETIME value; + GetSystemTimeAsFileTime (&value); + return (int64_t)((((uint64_t)value.dwHighDateTime) << 32) | (uint64_t)value.dwLowDateTime); +} +#else +#include +#include +#include +#include + +#if HAVE_SYS_TIME_H +#include +#endif // HAVE_SYS_TIME_H + +#if HAVE_MACH_ABSOLUTE_TIME +#include +static mono_lazy_init_t _ep_rt_mono_time_base_info_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; +static mach_timebase_info_data_t _ep_rt_mono_time_base_info = {0}; +#endif + +#ifdef HAVE_LOCALTIME_R +#define HAVE_GMTIME_R 1 +#endif + +static const int64_t SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL; +static const int64_t SECS_TO_100NS = 10000000; +static const int64_t SECS_TO_NS = 1000000000; +static const int64_t MSECS_TO_MIS = 1000; + +#ifndef HAVE_CLOCK_MONOTONIC +static const int64_t MISECS_TO_NS = 1000; +#endif + +static +void +time_base_info_lazy_init (void); + +static +int64_t +system_time_to_int64 ( + time_t sec, + long nsec); + +#if HAVE_MACH_ABSOLUTE_TIME +static +void +time_base_info_lazy_init (void) +{ + kern_return_t result = mach_timebase_info (&_ep_rt_mono_time_base_info); + if (result != KERN_SUCCESS) + memset (&_ep_rt_mono_time_base_info, 0, sizeof (_ep_rt_mono_time_base_info)); +} +#endif + +int64_t +ep_rt_mono_perf_counter_query (void) +{ +#if HAVE_MACH_ABSOLUTE_TIME + return (int64_t)mach_absolute_time (); +#elif HAVE_CLOCK_MONOTONIC + struct timespec ts; + int result = clock_gettime (CLOCK_MONOTONIC, &ts); + if (result == 0) + return ((int64_t)(ts.tv_sec) * (int64_t)(SECS_TO_NS)) + (int64_t)(ts.tv_nsec); +#else + #error "ep_rt_mono_perf_counter_get requires either mach_absolute_time () or clock_gettime (CLOCK_MONOTONIC) to be supported." +#endif + return 0; +} + +int64_t +ep_rt_mono_perf_frequency_query (void) +{ +#if HAVE_MACH_ABSOLUTE_TIME + // (numer / denom) gives you the nanoseconds per tick, so the below code + // computes the number of ticks per second. We explicitly do the multiplication + // first in order to help minimize the error that is produced by integer division. + mono_lazy_initialize (&_ep_rt_mono_time_base_info_init, time_base_info_lazy_init); + if (_ep_rt_mono_time_base_info.denom == 0 || _ep_rt_mono_time_base_info.numer == 0) + return 0; + return ((int64_t)(SECS_TO_NS) * (int64_t)(_ep_rt_mono_time_base_info.denom)) / (int64_t)(_ep_rt_mono_time_base_info.numer); +#elif HAVE_CLOCK_MONOTONIC + // clock_gettime () returns a result in terms of nanoseconds rather than a count. This + // means that we need to either always scale the result by the actual resolution (to + // get a count) or we need to say the resolution is in terms of nanoseconds. We prefer + // the latter since it allows the highest throughput and should minimize error propagated + // to the user. + return (int64_t)(SECS_TO_NS); +#else + #error "ep_rt_mono_perf_frequency_query requires either mach_absolute_time () or clock_gettime (CLOCK_MONOTONIC) to be supported." +#endif + return 0; +} + +void +ep_rt_mono_system_time_get (EventPipeSystemTime *system_time) +{ + time_t tt; +#if HAVE_GMTIME_R + struct tm ut; +#endif /* HAVE_GMTIME_R */ + struct tm *ut_ptr; + struct timeval time_val; + int timeofday_retval; + + EP_ASSERT (system_time != NULL); + + tt = time (NULL); + + /* We can't get millisecond resolution from time (), so we get it from gettimeofday () */ + timeofday_retval = gettimeofday (&time_val, NULL); + +#if HAVE_GMTIME_R + ut_ptr = &ut; + if (gmtime_r (&tt, ut_ptr) == NULL) +#else /* HAVE_GMTIME_R */ + if ((ut_ptr = gmtime (&tt)) == NULL) +#endif /* HAVE_GMTIME_R */ + g_assert_not_reached (); + + uint16_t milliseconds = 0; + if (timeofday_retval != -1) { + int old_seconds; + int new_seconds; + + milliseconds = time_val.tv_usec / MSECS_TO_MIS; + + old_seconds = ut_ptr->tm_sec; + new_seconds = time_val.tv_sec % 60; + + /* just in case we reached the next second in the interval between time () and gettimeofday () */ + if (old_seconds != new_seconds) + milliseconds = 999; + } + + ep_system_time_set ( + system_time, + 1900 + ut_ptr->tm_year, + ut_ptr->tm_mon + 1, + ut_ptr->tm_wday, + ut_ptr->tm_mday, + ut_ptr->tm_hour, + ut_ptr->tm_min, + ut_ptr->tm_sec, + milliseconds); +} + +static +inline +int64_t +system_time_to_int64 ( + time_t sec, + long nsec) +{ + return ((int64_t)sec + SECS_BETWEEN_1601_AND_1970_EPOCHS) * SECS_TO_100NS + (nsec / 100); +} + +int64_t +ep_rt_mono_system_timestamp_get (void) +{ +#if HAVE_CLOCK_MONOTONIC + struct timespec time; + if (clock_gettime (CLOCK_REALTIME, &time) == 0) + return system_time_to_int64 (time.tv_sec, time.tv_nsec); +#else + struct timeval time; + if (gettimeofday (&time, NULL) == 0) + return system_time_to_int64 (time.tv_sec, time.tv_usec * MISECS_TO_NS); +#endif + else + return system_time_to_int64 (0, 0); +} +#endif + #endif /* ENABLE_PERFTRACING */ -extern const char quiet_linker_empty_file_warning_eventpipe_rt_mono; -const char quiet_linker_empty_file_warning_eventpipe_rt_mono = 0; +MONO_EMPTY_SOURCE_FILE(eventpipe_rt_mono); diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h index 04808f23b9768f..d2d18fdebe30be 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-mono.h @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #undef EP_ARRAY_SIZE #define EP_ARRAY_SIZE(expr) G_N_ELEMENTS(expr) @@ -35,11 +38,19 @@ #undef EP_ALWAYS_INLINE #define EP_ALWAYS_INLINE MONO_ALWAYS_INLINE +#undef EP_NEVER_INLINE +#define EP_NEVER_INLINE MONO_NEVER_INLINE + #undef EP_ALIGN_UP #define EP_ALIGN_UP(val,align) ALIGN_TO(val,align) -#define EP_RT_DEFINE_LIST(list_name, list_type, item_type) \ - static inline void ep_rt_ ## list_name ## _free (list_type *list, void (*callback)(void *)) { \ +#ifndef EP_RT_BUILD_TYPE_FUNC_NAME +#define EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, type_name, func_name) \ +prefix_name ## _rt_ ## type_name ## _ ## func_name +#endif + +#define EP_RT_DEFINE_LIST_PREFIX(prefix_name, list_name, list_type, item_type) \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, free) (list_type *list, void (*callback)(void *)) { \ if (callback) { \ for (GSList *l = list->list; l; l = l->next) { \ callback (l->data); \ @@ -48,107 +59,132 @@ g_slist_free (list->list); \ list->list = NULL; \ } \ - static inline void ep_rt_ ## list_name ## _clear (list_type *list, void (*callback)(void *)) { ep_rt_ ## list_name ## _free (list, callback); } \ - static inline void ep_rt_ ## list_name ## _append (list_type *list, item_type item) { list->list = g_slist_append (list->list, ((gpointer)(gsize)item)); } \ - static inline void ep_rt_ ## list_name ## _remove (list_type *list, const item_type item) { list->list = g_slist_remove (list->list, ((gconstpointer)(const gsize)item)); } \ - static inline bool ep_rt_ ## list_name ## _find (const list_type *list, const item_type item_to_find, item_type *found_item) { \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, clear) (list_type *list, void (*callback)(void *)) { ep_rt_ ## list_name ## _free (list, callback); } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, append) (list_type *list, item_type item) { list->list = g_slist_append (list->list, ((gpointer)(gsize)item)); } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, remove) (list_type *list, const item_type item) { list->list = g_slist_remove (list->list, ((gconstpointer)(const gsize)item)); } \ + static inline bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, find) (const list_type *list, const item_type item_to_find, item_type *found_item) { \ GSList *found_glist_item = g_slist_find (list->list, ((gconstpointer)(const gsize)item_to_find)); \ *found_item = (found_glist_item != NULL) ? ((item_type)(gsize)(found_glist_item->data)) : ((item_type)(gsize)NULL); \ return *found_item != NULL; \ } \ - static inline bool ep_rt_ ## list_name ## _is_empty (const list_type *list) { return list->list == NULL; } + static inline bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, is_empty) (const list_type *list) { return list->list == NULL; } + +#define EP_RT_DEFINE_LIST(list_name, list_type, item_type) \ + EP_RT_DEFINE_LIST_PREFIX(ep, list_name, list_type, item_type) + +#define EP_RT_DEFINE_LIST_ITERATOR_PREFIX(prefix_name, list_name, list_type, iterator_type, item_type) \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, iterator_begin) (const list_type *list, iterator_type *iterator) { iterator->iterator = list->list; } \ + static inline bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, iterator_end) (const list_type *list, const iterator_type *iterator) { return iterator->iterator == NULL; } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, iterator_next) (const list_type *list, iterator_type *iterator) { iterator->iterator = iterator->iterator->next; } \ + static inline item_type EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, iterator_value) (const iterator_type *iterator) { return ((item_type)(gsize)(iterator->iterator->data)); } #define EP_RT_DEFINE_LIST_ITERATOR(list_name, list_type, iterator_type, item_type) \ - static inline void ep_rt_ ## list_name ## _iterator_begin (const list_type *list, iterator_type *iterator) { iterator->iterator = list->list; } \ - static inline bool ep_rt_ ## list_name ## _iterator_end (const list_type *list, const iterator_type *iterator) { return iterator->iterator == NULL; } \ - static inline void ep_rt_ ## list_name ## _iterator_next (const list_type *list, iterator_type *iterator) { iterator->iterator = iterator->iterator->next; } \ - static inline item_type ep_rt_ ## list_name ## _iterator_value (const iterator_type *iterator) { return ((item_type)(gsize)(iterator->iterator->data)); } + EP_RT_DEFINE_LIST_ITERATOR_PREFIX(ep, list_name, list_type, iterator_type, item_type) + +#define EP_RT_DEFINE_QUEUE_PREFIX(prefix_name, queue_name, queue_type, item_type) \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, alloc) (queue_type *queue) { queue->queue = g_queue_new (); } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, free) (queue_type *queue) { g_queue_free (queue->queue); queue->queue = NULL; } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, pop_head) (queue_type *queue, item_type *item) { *item = ((item_type)(gsize)g_queue_pop_head (queue->queue)); } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, push_head) (queue_type *queue, item_type item) { g_queue_push_head (queue->queue, ((gpointer)(gsize)item)); } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, push_tail) (queue_type *queue, item_type item) { g_queue_push_tail (queue->queue, ((gpointer)(gsize)item)); } \ + static inline bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, is_empty) (const queue_type *queue) { return g_queue_is_empty (queue->queue); } #define EP_RT_DEFINE_QUEUE(queue_name, queue_type, item_type) \ - static inline void ep_rt_ ## queue_name ## _alloc (queue_type *queue) { queue->queue = g_queue_new (); } \ - static inline void ep_rt_ ## queue_name ## _free (queue_type *queue) { g_queue_free (queue->queue); queue->queue = NULL; } \ - static inline void ep_rt_ ## queue_name ## _pop_head (queue_type *queue, item_type *item) { *item = ((item_type)(gsize)g_queue_pop_head (queue->queue)); } \ - static inline void ep_rt_ ## queue_name ## _push_head (queue_type *queue, item_type item) { g_queue_push_head (queue->queue, ((gpointer)(gsize)item)); } \ - static inline void ep_rt_ ## queue_name ## _push_tail (queue_type *queue, item_type item) { g_queue_push_tail (queue->queue, ((gpointer)(gsize)item)); } \ - static inline bool ep_rt_ ## queue_name ## _is_empty (const queue_type *queue) { return g_queue_is_empty (queue->queue); } - -#define EP_RT_DEFINE_ARRAY(array_name, array_type, item_type) \ - static inline void ep_rt_ ## array_name ## _alloc (array_type *ep_array) { ep_array->array = g_array_new (FALSE, FALSE, sizeof (item_type)); } \ - static inline void ep_rt_ ## array_name ## _free (array_type *ep_array) { g_array_free (ep_array->array, TRUE); } \ - static inline void ep_rt_ ## array_name ## _clear (array_type *ep_array) { g_array_set_size (ep_array->array, 0); } \ - static inline void ep_rt_ ## array_name ## _append (array_type *ep_array, item_type item) { g_array_append_val (ep_array->array, item); } \ - static inline bool ep_rt_ ## array_name ## _remove (array_type *ep_array, const item_type item) { \ - for (gint i = 0; i < ep_array->array->len; ++i ) { \ - if (g_array_index (ep_array->array, item_type, i) == item) { \ - ep_array->array = g_array_remove_index_fast (ep_array->array, i); \ - return true; \ - } \ - } \ - return false; \ + EP_RT_DEFINE_QUEUE_PREFIX(ep, queue_name, queue_type, item_type) + +#define EP_RT_DEFINE_ARRAY_PREFIX(prefix_name, array_name, array_type, iterator_type, item_type) \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, alloc) (array_type *ep_array) { ep_array->array = g_array_new (FALSE, FALSE, sizeof (item_type)); } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, alloc_capacity) (array_type *ep_array, size_t capacity) { ep_array->array = g_array_sized_new (FALSE, FALSE, sizeof (item_type), capacity); } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, free) (array_type *ep_array) { g_array_free (ep_array->array, TRUE); } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, append) (array_type *ep_array, item_type item) { g_array_append_val (ep_array->array, item); } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, clear) (array_type *ep_array, void (*callback)(void *)) { g_array_set_size (ep_array->array, 0); } \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, remove) (array_type *ep_array, iterator_type *pos) { \ + EP_ASSERT (pos->index < ep_array->array->len); \ + ep_array->array = g_array_remove_index_fast (ep_array->array, pos->index); \ } \ - static inline size_t ep_rt_ ## array_name ## _size (const array_type *ep_array) { return ep_array->array->len; } + static inline size_t EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, size) (const array_type *ep_array) { return ep_array->array->len; } \ + static inline item_type * EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, data) (const array_type *ep_array) { return (item_type *)ep_array->array->data; } + +#define EP_RT_DEFINE_ARRAY(array_name, array_type, iterator_type, item_type) \ + EP_RT_DEFINE_ARRAY_PREFIX(ep, array_name, array_type, iterator_type, item_type) + +#define EP_RT_DEFINE_ARRAY_ITERATOR_PREFIX(prefix_name, array_name, array_type, iterator_type, item_type) \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, iterator_begin) (const array_type *ep_array, iterator_type *iterator) { iterator->array = ep_array->array; iterator->index = 0; } \ + static inline bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, iterator_end) (const array_type *ep_array, const iterator_type *iterator) { return iterator->index >= iterator->array->len; } \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, iterator_next) (const array_type *ep_array, iterator_type *iterator) { iterator->index++; } \ + static item_type EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, iterator_value) (const iterator_type *iterator) { return g_array_index(iterator->array, item_type, iterator->index); } #define EP_RT_DEFINE_ARRAY_ITERATOR(array_name, array_type, iterator_type, item_type) \ - static inline void ep_rt_ ## array_name ## _iterator_begin (const array_type *ep_array, iterator_type *iterator) { iterator->array = ep_array->array; iterator->index = 0; } \ - static inline bool ep_rt_ ## array_name ## _iterator_end (const array_type *ep_array, const iterator_type *iterator) { return iterator->index >= iterator->array->len; } \ - static void ep_rt_ ## array_name ## _iterator_next (const array_type *ep_array, iterator_type *iterator) { iterator->index++; } \ - static item_type ep_rt_ ## array_name ## _iterator_value (const iterator_type *iterator) { return g_array_index(iterator->array, item_type, iterator->index); } + EP_RT_DEFINE_ARRAY_ITERATOR_PREFIX(ep, array_name, array_type, iterator_type, item_type) -#define EP_RT_DEFINE_HASH_MAP(hash_map_name, hash_map_type, key_type, value_type) \ - static inline void ep_rt_ ## hash_map_name ## _alloc (hash_map_type *hash_map, uint32_t (*hash_callback)(const void *), bool (*eq_callback)(const void *, const void *), void (*key_free_callback)(void *), void (*value_free_callback)(void *)) { \ +#define EP_RT_DEFINE_HASH_MAP_PREFIX(prefix_name, hash_map_name, hash_map_type, key_type, value_type) \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, alloc) (hash_map_type *hash_map, uint32_t (*hash_callback)(const void *), bool (*eq_callback)(const void *, const void *), void (*key_free_callback)(void *), void (*value_free_callback)(void *)) { \ hash_map->table = g_hash_table_new_full ((GHashFunc)hash_callback, (GEqualFunc)eq_callback, (GDestroyNotify)key_free_callback, (GDestroyNotify)value_free_callback); \ hash_map->count = 0;\ } \ - static inline void ep_rt_ ## hash_map_name ## _free (hash_map_type *hash_map) { \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, free) (hash_map_type *hash_map) { \ g_hash_table_destroy (hash_map->table); \ hash_map->table = NULL; \ hash_map->count = 0; \ } \ - static inline void ep_rt_ ## hash_map_name ## _add (hash_map_type *hash_map, key_type key, value_type value) { \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, add) (hash_map_type *hash_map, key_type key, value_type value) { \ g_hash_table_replace (hash_map->table, (gpointer)key, ((gpointer)(gsize)value)); \ hash_map->count++; \ } \ - static inline void ep_rt_ ## hash_map_name ## _remove (hash_map_type *hash_map, const key_type key) { \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, remove) (hash_map_type *hash_map, const key_type key) { \ if (g_hash_table_remove (hash_map->table, (gconstpointer)key)) \ hash_map->count--; \ } \ - static inline void ep_rt_ ## hash_map_name ## _remove_all (hash_map_type *hash_map) { \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, remove_all) (hash_map_type *hash_map) { \ g_hash_table_remove_all (hash_map->table); \ hash_map->count = 0; \ } \ - static inline bool ep_rt_ ## hash_map_name ## _lookup (const hash_map_type *hash_map, const key_type key, value_type *value) { \ + static inline bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, lookup) (const hash_map_type *hash_map, const key_type key, value_type *value) { \ gpointer _value = NULL; \ bool result = g_hash_table_lookup_extended (hash_map->table, (gconstpointer)key, NULL, &_value); \ *value = ((value_type)(gsize)_value); \ return result; \ } \ - static inline uint32_t ep_rt_ ## hash_map_name ## _count (const hash_map_type *hash_map) { \ + static inline uint32_t EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, count) (const hash_map_type *hash_map) { \ return hash_map->count; \ } -#define EP_RT_DEFINE_HASH_MAP_ITERATOR(hash_map_name, hash_map_type, iterator_type, key_type, value_type) \ - static inline void ep_rt_ ## hash_map_name ## _iterator_begin (const hash_map_type *hash_map, iterator_type *iterator) { \ +#define EP_RT_DEFINE_HASH_MAP(hash_map_name, hash_map_type, key_type, value_type) \ + EP_RT_DEFINE_HASH_MAP_PREFIX(ep, hash_map_name, hash_map_type, key_type, value_type) + +#define EP_RT_DEFINE_HASH_MAP_ITERATOR_PREFIX(prefix_name, hash_map_name, hash_map_type, iterator_type, key_type, value_type) \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, iterator_begin) (const hash_map_type *hash_map, iterator_type *iterator) { \ g_hash_table_iter_init (&iterator->iterator, hash_map->table); \ if (hash_map->table && hash_map->count > 0) \ iterator->end = !g_hash_table_iter_next (&iterator->iterator, &iterator->key, &iterator->value); \ else \ iterator->end = true; \ } \ - static inline bool ep_rt_ ## hash_map_name ## _iterator_end (const hash_map_type *hash_map, const iterator_type *iterator) { \ + static inline bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, iterator_end) (const hash_map_type *hash_map, const iterator_type *iterator) { \ return iterator->end; \ } \ - static inline void ep_rt_ ## hash_map_name ## _iterator_next (const hash_map_type *hash_map, iterator_type *iterator) { \ + static inline void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, iterator_next) (const hash_map_type *hash_map, iterator_type *iterator) { \ iterator->end = !g_hash_table_iter_next (&iterator->iterator, &iterator->key, &iterator->value); \ } \ - static inline key_type ep_rt_ ## hash_map_name ## _iterator_key (const iterator_type *iterator) { \ + static inline key_type EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, iterator_key) (const iterator_type *iterator) { \ return ((key_type)(gsize)iterator->key); \ } \ - static inline value_type ep_rt_ ## hash_map_name ## _iterator_value (const iterator_type *iterator) { \ + static inline value_type EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, iterator_value) (const iterator_type *iterator) { \ return ((value_type)(gsize)iterator->value); \ } -typedef gint64 (*ep_rt_mono_100ns_ticks_func)(void); -typedef gint64 (*ep_rt_mono_100ns_datetime_func)(void); +#define EP_RT_DEFINE_HASH_MAP_ITERATOR(hash_map_name, hash_map_type, iterator_type, key_type, value_type) \ + EP_RT_DEFINE_HASH_MAP_ITERATOR_PREFIX(ep, hash_map_name, hash_map_type, iterator_type, key_type, value_type) + +typedef MonoThreadStart ep_rt_thread_start_func; +typedef mono_thread_start_return_t ep_rt_thread_start_func_return_t; +typedef MonoNativeThreadId ep_rt_thread_id_t; + +typedef EventPipeThreadHolder * (*ep_rt_thread_holder_alloc_func)(void); +typedef void (*ep_rt_thread_holder_free_func)(EventPipeThreadHolder *thread_holder); + +#define EP_RT_DEFINE_THREAD_FUNC(name) static mono_thread_start_return_t WINAPI name (gpointer data) + typedef int (*ep_rt_mono_cpu_count_func)(void); typedef int (*ep_rt_mono_process_current_pid_func)(void); typedef MonoNativeThreadId (*ep_rt_mono_native_thread_id_get_func)(void); @@ -170,10 +206,13 @@ typedef MonoW32HandleWaitRet (*ep_rt_mono_w32handle_wait_one_func)(gpointer hand typedef void* (*ep_rt_mono_valloc_func)(void *addr, size_t length, int flags, MonoMemAccountType type); typedef int (*ep_rt_mono_vfree_func)(void *addr, size_t length, MonoMemAccountType type); typedef int (*ep_rt_mono_valloc_granule_func)(void); +typedef gboolean (*ep_rt_mono_thread_platform_create_thread_func)(ep_rt_thread_start_func thread_func, gpointer thread_data, gsize * const stack_size, ep_rt_thread_id_t *thread_id); +typedef gpointer (*ep_rt_mono_thread_attach_func)(gboolean); +typedef void (*ep_rt_mono_thread_detach_func)(void); +typedef char* (*ep_rt_mono_get_os_cmd_line_func)(void); +typedef char* (*ep_rt_mono_get_managed_cmd_line_func)(void); typedef struct _EventPipeMonoFuncTable { - ep_rt_mono_100ns_ticks_func ep_rt_mono_100ns_ticks; - ep_rt_mono_100ns_datetime_func ep_rt_mono_100ns_datetime; ep_rt_mono_process_current_pid_func ep_rt_mono_process_current_pid; ep_rt_mono_cpu_count_func ep_rt_mono_cpu_count; ep_rt_mono_native_thread_id_get_func ep_rt_mono_native_thread_id_get; @@ -195,19 +234,24 @@ typedef struct _EventPipeMonoFuncTable { ep_rt_mono_valloc_func ep_rt_mono_valloc; ep_rt_mono_vfree_func ep_rt_mono_vfree; ep_rt_mono_valloc_granule_func ep_rt_mono_valloc_granule; + ep_rt_mono_thread_platform_create_thread_func ep_rt_mono_thread_platform_create_thread; + ep_rt_mono_thread_attach_func ep_rt_mono_thread_attach; + ep_rt_mono_thread_detach_func ep_rt_mono_thread_detach; + ep_rt_mono_get_os_cmd_line_func ep_rt_mono_get_os_cmd_line; + ep_rt_mono_get_managed_cmd_line_func ep_rt_mono_get_managed_cmd_line; } EventPipeMonoFuncTable; -typedef EventPipeThreadHolder * (*ep_rt_thread_holder_alloc_func)(void); -typedef void (*ep_rt_thread_holder_free_func)(EventPipeThreadHolder *thread_holder); +int64_t +ep_rt_mono_perf_counter_query (void); -static -inline -ep_rt_spin_lock_handle_t * -ep_rt_mono_config_lock_get (void) -{ - extern ep_rt_spin_lock_handle_t _ep_rt_mono_config_lock; - return &_ep_rt_mono_config_lock; -} +int64_t +ep_rt_mono_perf_frequency_query (void); + +void +ep_rt_mono_system_time_get (EventPipeSystemTime *system_time); + +int64_t +ep_rt_mono_system_timestamp_get (void); #ifndef EP_RT_MONO_USE_STATIC_RUNTIME static @@ -220,6 +264,111 @@ ep_rt_mono_func_table_get (void) } #endif +static +inline +char * +os_command_line_get (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return mono_get_os_cmd_line (); +#else + return ep_rt_mono_func_table_get ()->ep_rt_mono_get_os_cmd_line (); +#endif +} + +static +inline +char ** +os_command_line_get_ref (void) +{ + extern char *_ep_rt_mono_os_cmd_line; + return &_ep_rt_mono_os_cmd_line; +} + +static +inline +mono_lazy_init_t * +os_command_line_get_init (void) +{ + extern mono_lazy_init_t _ep_rt_mono_os_cmd_line_init; + return &_ep_rt_mono_os_cmd_line_init; +} + +static +inline +void +os_command_line_lazy_init (void) +{ + if (!*os_command_line_get_ref ()) + *os_command_line_get_ref () = os_command_line_get (); +} + +static +inline +void +os_command_line_lazy_clean (void) +{ + g_free (*os_command_line_get_ref ()); + *os_command_line_get_ref () = NULL; +} + +static +inline +char * +managed_command_line_get (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return mono_runtime_get_managed_cmd_line (); +#else + return ep_rt_mono_func_table_get ()->ep_rt_mono_get_managed_cmd_line (); +#endif +} + +static +inline +char ** +managed_command_line_get_ref (void) +{ + extern char *_ep_rt_mono_managed_cmd_line; + return &_ep_rt_mono_managed_cmd_line; +} + +static +inline +mono_lazy_init_t * +managed_command_line_get_init (void) +{ + extern mono_lazy_init_t _ep_rt_mono_managed_cmd_line_init; + return &_ep_rt_mono_managed_cmd_line_init; +} + +static +inline +void +managed_command_line_lazy_init (void) +{ + if (!*managed_command_line_get_ref ()) + *managed_command_line_get_ref () = managed_command_line_get (); +} + +static +inline +void +managed_command_line_lazy_clean (void) +{ + g_free (*managed_command_line_get_ref ()); + *managed_command_line_get_ref () = NULL; +} + +static +inline +ep_rt_spin_lock_handle_t * +ep_rt_mono_config_lock_get (void) +{ + extern ep_rt_spin_lock_handle_t _ep_rt_mono_config_lock; + return &_ep_rt_mono_config_lock; +} + MONO_PROFILER_API void mono_eventpipe_init ( @@ -384,7 +533,7 @@ ep_rt_atomic_dec_int64_t (volatile int64_t *value) * EventPipe. */ -EP_RT_DEFINE_ARRAY (session_id_array, ep_rt_session_id_array_t, EventPipeSessionID) +EP_RT_DEFINE_ARRAY (session_id_array, ep_rt_session_id_array_t, ep_rt_session_id_array_iterator_t, EventPipeSessionID) EP_RT_DEFINE_ARRAY_ITERATOR (session_id_array, ep_rt_session_id_array_t, ep_rt_session_id_array_iterator_t, EventPipeSessionID) static @@ -421,6 +570,9 @@ inline void ep_rt_shutdown (void) { + mono_lazy_cleanup (managed_command_line_get_init (), managed_command_line_lazy_clean); + mono_lazy_cleanup (os_command_line_get_init (), os_command_line_lazy_clean); + ep_rt_spin_lock_free (ep_rt_mono_config_lock_get ()); mono_eventpipe_fini (); } @@ -465,7 +617,7 @@ inline bool ep_rt_walk_managed_stack_for_current_thread (EventPipeStackContents *stack_contents) { - //TODO: Implement. + // TODO: Implement. return true; } @@ -489,15 +641,15 @@ ep_rt_init_providers_and_events (void) * EventPipeBuffer. */ -EP_RT_DEFINE_ARRAY (buffer_array, ep_rt_buffer_array_t, EventPipeBuffer *) +EP_RT_DEFINE_ARRAY (buffer_array, ep_rt_buffer_array_t, ep_rt_buffer_array_iterator_t, EventPipeBuffer *) EP_RT_DEFINE_ARRAY_ITERATOR (buffer_array, ep_rt_buffer_array_t, ep_rt_buffer_array_iterator_t, EventPipeBuffer *) /* * EventPipeBufferList. */ -EP_RT_DEFINE_ARRAY (buffer_list_array, ep_rt_buffer_list_array_t, EventPipeBufferList *) -EP_RT_DEFINE_ARRAY_ITERATOR (buffer_list_array, ep_rt_buffer_list_array_t, ep_rt_buffer_array_iterator_t, EventPipeBufferList *) +EP_RT_DEFINE_ARRAY (buffer_list_array, ep_rt_buffer_list_array_t, ep_rt_buffer_list_array_iterator_t, EventPipeBufferList *) +EP_RT_DEFINE_ARRAY_ITERATOR (buffer_list_array, ep_rt_buffer_list_array_t, ep_rt_buffer_list_array_iterator_t, EventPipeBufferList *) /* * EventPipeEvent. @@ -548,6 +700,9 @@ ep_rt_provider_list_find_by_name ( * EventPipeProviderConfiguration. */ +EP_RT_DEFINE_ARRAY (provider_config_array, ep_rt_provider_config_array_t, ep_rt_provider_config_array_iterator_t, EventPipeProviderConfiguration) +EP_RT_DEFINE_ARRAY_ITERATOR (provider_config_array, ep_rt_provider_config_array_t, ep_rt_provider_config_array_iterator_t, EventPipeProviderConfiguration) + static inline bool @@ -599,7 +754,7 @@ inline void ep_rt_sample_profiler_init (EventPipeProviderCallbackDataQueue *provider_callback_data_queue) { - //TODO: Not supported. + // TODO: Not supported. } static @@ -607,7 +762,7 @@ inline void ep_rt_sample_profiler_enable (void) { - //TODO: Not supported. + // TODO: Not supported. } static @@ -615,7 +770,7 @@ inline void ep_rt_sample_profiler_disable (void) { - //TODO: Not supported. + // TODO: Not supported. } static @@ -623,7 +778,7 @@ inline uint32_t ep_rt_sample_profiler_get_sampling_rate (void) { - //TODO: Not supported. + // TODO: Not supported. return 0; } @@ -632,21 +787,21 @@ inline void ep_rt_sample_profiler_set_sampling_rate (uint32_t nanoseconds) { - //TODO: Not supported. + // TODO: Not supported. } static void ep_rt_sample_profiler_can_start_sampling (void) { - //TODO: Not supported. + // TODO: Not supported. } static void ep_rt_notify_profiler_provider_created (EventPipeProvider *provider) { - //TODO: Not supported. + // TODO: Not supported. } /* @@ -667,7 +822,7 @@ EP_RT_DEFINE_LIST_ITERATOR (sequence_point_list, ep_rt_sequence_point_list_t, ep * EventPipeThread. */ -EP_RT_DEFINE_ARRAY (thread_array, ep_rt_thread_array_t, EventPipeThread *) +EP_RT_DEFINE_ARRAY (thread_array, ep_rt_thread_array_t, ep_rt_thread_array_iterator_t, EventPipeThread *) EP_RT_DEFINE_ARRAY_ITERATOR (thread_array, ep_rt_thread_array_t, ep_rt_thread_array_iterator_t, EventPipeThread *) /* @@ -677,7 +832,7 @@ EP_RT_DEFINE_ARRAY_ITERATOR (thread_array, ep_rt_thread_array_t, ep_rt_thread_ar EP_RT_DEFINE_LIST (thread_session_state_list, ep_rt_thread_session_state_list_t, EventPipeThreadSessionState *) EP_RT_DEFINE_LIST_ITERATOR (thread_session_state_list, ep_rt_thread_session_state_list_t, ep_rt_thread_session_state_list_iterator_t, EventPipeThreadSessionState *) -EP_RT_DEFINE_ARRAY (thread_session_state_array, ep_rt_thread_session_state_array_t, EventPipeThreadSessionState *) +EP_RT_DEFINE_ARRAY (thread_session_state_array, ep_rt_thread_session_state_array_t, ep_rt_thread_session_state_array_iterator_t, EventPipeThreadSessionState *) EP_RT_DEFINE_ARRAY_ITERATOR (thread_session_state_array, ep_rt_thread_session_state_array_t, ep_rt_thread_session_state_array_iterator_t, EventPipeThreadSessionState *) static @@ -795,10 +950,33 @@ ep_rt_wait_event_get_wait_handle (ep_rt_wait_event_handle_t *wait_event) return (EventPipeWaitHandle)wait_event; } +static +inline +bool +ep_rt_wait_event_is_valid (ep_rt_wait_event_handle_t *wait_event) +{ + if (wait_event == NULL || wait_event->event == NULL || wait_event->event == INVALID_HANDLE_VALUE) + return false; + else + return true; +} + /* * Misc. */ +static +inline +int +ep_rt_get_last_error (void) +{ +#ifdef HOST_WIN32 + return GetLastError (); +#else + return errno; +#endif +} + static inline bool @@ -878,6 +1056,29 @@ ep_rt_object_free (void *ptr) * PAL. */ +static +inline +bool +ep_rt_thread_create ( + void *thread_func, + void *params, + void *id) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return (bool)mono_thread_platform_create_thread ((ep_rt_thread_start_func)thread_func, params, NULL, (ep_rt_thread_id_t *)id); +#else + return (bool)ep_rt_mono_func_table_get ()->ep_rt_mono_thread_platform_create_thread ((ep_rt_thread_start_func)thread_func, params, NULL, (ep_rt_thread_id_t *)id); +#endif +} + +static +inline +void +ep_rt_thread_sleep (uint64_t ns) +{ + g_usleep (ns / 1000); +} + static inline uint32_t @@ -927,11 +1128,7 @@ inline int64_t ep_rt_perf_counter_query (void) { -#ifdef EP_RT_MONO_USE_STATIC_RUNTIME - return (int64_t)mono_100ns_ticks (); -#else - return (int64_t)ep_rt_mono_func_table_get ()->ep_rt_mono_100ns_ticks (); -#endif + return ep_rt_mono_perf_counter_query (); } static @@ -939,20 +1136,23 @@ inline int64_t ep_rt_perf_frequency_query (void) { - //Counter uses resolution of 100ns ticks. - return 10 * 1000 * 1000; + return ep_rt_mono_perf_frequency_query (); } static inline -ep_systemtime_t -ep_rt_system_time_get (void) +void +ep_rt_system_time_get (EventPipeSystemTime *system_time) { -#ifdef EP_RT_MONO_USE_STATIC_RUNTIME - return (ep_systemtime_t)mono_100ns_datetime (); -#else - return (ep_systemtime_t)ep_rt_mono_func_table_get ()->ep_rt_mono_100ns_datetime (); -#endif + ep_rt_mono_system_time_get (system_time); +} + +static +inline +int64_t +ep_rt_system_timestamp_get (void) +{ + return ep_rt_mono_system_timestamp_get (); } static @@ -970,10 +1170,18 @@ ep_rt_system_get_alloc_granularity (void) static inline const ep_char8_t * -ep_rt_command_line_get (void) +ep_rt_os_command_line_get (void) { - //TODO: Implement. - return ""; + if (!mono_lazy_is_initialized (os_command_line_get_init ())) { + char *cmd_line = os_command_line_get (); + if (!cmd_line) + return NULL; + g_free (cmd_line); + } + + mono_lazy_initialize (os_command_line_get_init (), os_command_line_lazy_init); + EP_ASSERT (*os_command_line_get_ref () != NULL); + return *os_command_line_get_ref (); } static @@ -1037,7 +1245,9 @@ ep_rt_valloc0 (size_t buffer_size) static inline void -ep_rt_vfree (uint8_t *buffer, size_t buffer_size) +ep_rt_vfree ( + uint8_t *buffer, + size_t buffer_size) { if (buffer) #ifdef EP_RT_MONO_USE_STATIC_RUNTIME @@ -1047,6 +1257,34 @@ ep_rt_vfree (uint8_t *buffer, size_t buffer_size) #endif } +static +inline +uint32_t +ep_rt_temp_path_get ( + ep_char8_t *buffer, + uint32_t buffer_len) +{ + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len > 0); + + const ep_char8_t *path = g_get_tmp_dir (); + int32_t result = snprintf (buffer, buffer_len, "%s", path); + if (result <= 0 || result > buffer_len) + ep_raise_error (); + + if (buffer [result - 1] != G_DIR_SEPARATOR) { + buffer [result++] = G_DIR_SEPARATOR; + buffer [result] = '\0'; + } + +ep_on_exit: + return result; + +ep_on_error: + result = 0; + ep_exit_error_handler (); +} + /* * SpinLock. */ @@ -1098,6 +1336,7 @@ ep_rt_spin_lock_release (ep_rt_spin_lock_handle_t *spin_lock) if (spin_lock && spin_lock->lock) { #ifdef EP_CHECKED_BUILD spin_lock->lock_is_held = false; + spin_lock->owning_thread_id = MONO_UINT_TO_NATIVE_THREAD_ID (0); #endif mono_coop_mutex_unlock (spin_lock->lock); } @@ -1143,6 +1382,32 @@ ep_rt_utf8_string_compare ( return strcmp ((const char *)str1, (const char *)str2); } +static +inline +int +ep_rt_utf8_string_compare_ignore_case ( + const ep_char8_t *str1, + const ep_char8_t *str2) +{ + return g_strcasecmp ((const char *)str1, (const char *)str2); +} + +static +inline +bool +ep_rt_utf8_string_is_null_or_empty (const ep_char8_t *str) +{ + if (str == NULL) + return true; + + while (*str) { + if (!isspace(*str)) + return false; + str++; + } + return true; +} + static inline ep_char16_t * @@ -1161,6 +1426,24 @@ ep_rt_utf8_string_dup (const ep_char8_t *str) return g_strdup (str); } +static +inline +ep_char8_t * +ep_rt_utf8_string_strtok ( + ep_char8_t *str, + const ep_char8_t *delimiter, + ep_char8_t **context) +{ + return strtok_r (str, delimiter, context); +} + +#undef ep_rt_utf8_string_snprintf +#define ep_rt_utf8_string_snprintf( \ + str, \ + str_len, \ + format, ...) \ +g_snprintf ((gchar *)str, (gulong)str_len, (const gchar *)format, __VA_ARGS__) + static inline void @@ -1195,13 +1478,43 @@ ep_rt_utf16_string_free (ep_char16_t *str) g_free (str); } +static +inline +wchar_t * +ep_rt_utf8_to_wcs_string ( + const ep_char8_t *str, + size_t len) +{ +#if WCHAR_MAX == 0xFFFF + return (wchar_t *)ep_rt_utf8_to_utf16_string (str, len); +#else + return (wchar_t *)g_utf8_to_ucs4 (str, len, NULL, NULL, NULL); +#endif +} + +static +inline +void +ep_rt_wcs_string_free (wchar_t *str) +{ + g_free (str); +} + static inline const ep_char8_t * ep_rt_managed_command_line_get (void) { - //TODO: Implement. - return ""; + if (!mono_lazy_is_initialized (managed_command_line_get_init ())) { + char *cmd_line = managed_command_line_get (); + if (!cmd_line) + return NULL; + g_free (cmd_line); + } + + mono_lazy_initialize (managed_command_line_get_init (), managed_command_line_lazy_init); + EP_ASSERT (*managed_command_line_get_ref () != NULL); + return *managed_command_line_get_ref (); } /* @@ -1210,11 +1523,34 @@ ep_rt_managed_command_line_get (void) static inline void -ep_rt_thread_setup (void) +ep_rt_thread_setup (bool background_thread) { +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME // NOTE, under netcore, only root domain exists. - if (!mono_thread_current ()) - mono_thread_attach (mono_get_root_domain ()); + if (!mono_thread_current ()) { + MonoThread *thread = mono_thread_attach (mono_get_root_domain ()); + if (background_thread && thread) { + mono_thread_set_state (thread, ThreadState_Background); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_SAMPLE); + } + } +#else + ep_rt_mono_func_table_get ()->ep_rt_mono_thread_attach (background_thread); +#endif +} + +static +inline +void +ep_rt_thread_teardown (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + MonoThread *current_thread = mono_thread_current (); + if (current_thread) + mono_thread_detach (current_thread); +#else + ep_rt_mono_func_table_get ()->ep_rt_mono_thread_detach (); +#endif } static diff --git a/src/mono/mono/eventpipe/ep-rt-types-mono.h b/src/mono/mono/eventpipe/ep-rt-types-mono.h index fbea8005d6666e..f0a5a995b7961d 100644 --- a/src/mono/mono/eventpipe/ep-rt-types-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-types-mono.h @@ -112,12 +112,13 @@ typedef struct _rt_mono_array_iterator_internal_t ep_rt_thread_array_iterator_t; typedef struct _rt_mono_array_internal_t ep_rt_session_id_array_t; typedef struct _rt_mono_array_iterator_internal_t ep_rt_session_id_array_iterator_t; +typedef struct _rt_mono_array_internal_t ep_rt_provider_config_array_t; +typedef struct _rt_mono_array_iterator_internal_t ep_rt_provider_config_array_iterator_t; + typedef MonoThreadHandle ep_rt_thread_handle_t; typedef gpointer ep_rt_file_handle_t; -typedef gpointer ep_rt_ipc_handle_t; - typedef MonoMethod ep_rt_method_desc_t; typedef struct _rt_mono_event_internal_t ep_rt_wait_event_handle_t; diff --git a/src/mono/mono/eventpipe/ep-rt.h b/src/mono/mono/eventpipe/ep-rt.h index 89b73ff47d3dae..1d3d980bba0cd7 100644 --- a/src/mono/mono/eventpipe/ep-rt.h +++ b/src/mono/mono/eventpipe/ep-rt.h @@ -16,59 +16,88 @@ #define EP_YIELD_WHILE(condition) ep_rt_redefine #define EP_ALWAYS_INLINE ep_rt_redefine +#define EP_NEVER_INLINE ep_rt_redefine #define EP_ALIGN_UP(val,align) ep_rt_redefine +#ifndef EP_RT_BUILD_TYPE_FUNC_NAME +#define EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, type_name, func_name) \ +prefix_name ## _rt_ ## type_name ## _ ## func_name +#endif + +#define EP_RT_DECLARE_LIST_PREFIX(prefix_name, list_name, list_type, item_type) \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, free) (list_type *list, void (*callback)(void *)); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, clear) (list_type *list, void (*callback)(void *)); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, append) (list_type *list, item_type item); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, remove) (list_type *list, const item_type item); \ + static bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, find) (const list_type *list, const item_type item_to_find, item_type *found_item); \ + static bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, is_empty) (const list_type *list); + #define EP_RT_DECLARE_LIST(list_name, list_type, item_type) \ - static void ep_rt_ ## list_name ## _free (list_type *list, void (*callback)(void *)); \ - static void ep_rt_ ## list_name ## _clear (list_type *list, void (*callback)(void *)); \ - static void ep_rt_ ## list_name ## _append (list_type *list, item_type item); \ - static void ep_rt_ ## list_name ## _remove (list_type *list, const item_type item); \ - static bool ep_rt_ ## list_name ## _find (const list_type *list, const item_type item_to_find, item_type *found_item); \ - static bool ep_rt_ ## list_name ## _is_empty (const list_type *list); + EP_RT_DECLARE_LIST_PREFIX(ep, list_name, list_type, item_type) + +#define EP_RT_DECLARE_LIST_ITERATOR_PREFIX(prefix_name, list_name, list_type, iterator_type, item_type) \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, iterator_begin) (const list_type *list, iterator_type *iterator); \ + static bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, iterator_end) (const list_type *list, const iterator_type *iterator); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, iterator_next) (const list_type *list, iterator_type *iterator); \ + static item_type EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, list_name, iterator_value) (const iterator_type *iterator); #define EP_RT_DECLARE_LIST_ITERATOR(list_name, list_type, iterator_type, item_type) \ - static void ep_rt_ ## list_name ## _iterator_begin (const list_type *list, iterator_type *iterator); \ - static bool ep_rt_ ## list_name ## _iterator_end (const list_type *list, const iterator_type *iterator); \ - static void ep_rt_ ## list_name ## _iterator_next (const list_type *list, iterator_type *iterator); \ - static item_type ep_rt_ ## list_name ## _iterator_value (const iterator_type *iterator); + EP_RT_DECLARE_LIST_ITERATOR_PREFIX(ep, list_name, list_type, iterator_type, item_type) \ + +#define EP_RT_DECLARE_QUEUE_PREFIX(prefix_name, queue_name, queue_type, item_type) \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, alloc) (queue_type *queue); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, free) (queue_type *queue); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, pop_head) (queue_type *queue, item_type *item); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, push_head) (queue_type *queue, item_type item); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, push_tail) (queue_type *queue, item_type item); \ + static bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, queue_name, is_empty) (const queue_type *queue); #define EP_RT_DECLARE_QUEUE(queue_name, queue_type, item_type) \ - static void ep_rt_ ## queue_name ## _alloc (queue_type *queue); \ - static void ep_rt_ ## queue_name ## _free (queue_type *queue); \ - static void ep_rt_ ## queue_name ## _pop_head (queue_type *queue, item_type *item); \ - static void ep_rt_ ## queue_name ## _push_head (queue_type *queue, item_type item); \ - static void ep_rt_ ## queue_name ## _push_tail (queue_type *queue, item_type item); \ - static bool ep_rt_ ## queue_name ## _is_empty (const queue_type *queue); - -#define EP_RT_DECLARE_ARRAY(array_name, array_type, item_type) \ - static void ep_rt_ ## array_name ## _alloc (array_type *ep_array); \ - static void ep_rt_ ## array_name ## _free (array_type *ep_array); \ - static void ep_rt_ ## array_name ## _clear (array_type *ep_array); \ - static void ep_rt_ ## array_name ## _append (array_type *ep_array, item_type item); \ - static bool ep_rt_ ## array_name ## _remove (array_type *ep_array, const item_type item); \ - static size_t ep_rt_ ## array_name ## _size (const array_type *ep_array); + EP_RT_DECLARE_QUEUE_PREFIX(ep, queue_name, queue_type, item_type) + +#define EP_RT_DECLARE_ARRAY_PREFIX(prefix_name, array_name, array_type, iterator_type, item_type) \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, alloc) (array_type *ep_array); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, alloc_capacity) (array_type *ep_array, size_t capacity); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, free) (array_type *ep_array); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, append) (array_type *ep_array, item_type item); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, clear) (array_type *ep_array, void (*callback)(void *)); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, remove) (array_type *ep_array, iterator_type *pos); \ + static size_t EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, size) (const array_type *ep_array); \ + static item_type * EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, data) (const array_type *ep_array); + +#define EP_RT_DECLARE_ARRAY(array_name, array_type, iterator_type, item_type) \ + EP_RT_DECLARE_ARRAY_PREFIX(ep, array_name, array_type, iterator_type, item_type) + +#define EP_RT_DECLARE_ARRAY_ITERATOR_PREFIX(prefix_name, array_name, array_type, iterator_type, item_type) \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, iterator_begin) (const array_type *ep_array, iterator_type *iterator); \ + static bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, iterator_end) (const array_type *ep_array, const iterator_type *iterator); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, iterator_next) (const array_type *ep_array, iterator_type *iterator); \ + static item_type EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, array_name, iterator_value) (const iterator_type *iterator); #define EP_RT_DECLARE_ARRAY_ITERATOR(array_name, array_type, iterator_type, item_type) \ - static void ep_rt_ ## array_name ## _iterator_begin (const array_type *ep_array, iterator_type *iterator); \ - static bool ep_rt_ ## array_name ## _iterator_end (const array_type *ep_array, const iterator_type *iterator); \ - static void ep_rt_ ## array_name ## _iterator_next (const array_type *ep_array, iterator_type *iterator); \ - static item_type ep_rt_ ## array_name ## _iterator_value (const iterator_type *iterator); + EP_RT_DECLARE_ARRAY_ITERATOR_PREFIX(ep, array_name, array_type, iterator_type, item_type) \ + +#define EP_RT_DECLARE_HASH_MAP_PREFIX(prefix_name, hash_map_name, hash_map_type, key_type, value_type) \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, alloc) (hash_map_type *hash_map, uint32_t (*hash_callback)(const void *), bool (*eq_callback)(const void *, const void *), void (*key_free_callback)(void *), void (*value_free_callback)(void *)); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, free) (hash_map_type *hash_map); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, add) (hash_map_type *hash_map, key_type key, value_type value); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, remove) (hash_map_type *hash_map, const key_type key); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, remove_all) (hash_map_type *hash_map); \ + static bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, lookup) (const hash_map_type *hash_map, const key_type key, value_type *value); \ + static uint32_t EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, count) (const hash_map_type *hash_map); #define EP_RT_DECLARE_HASH_MAP(hash_map_name, hash_map_type, key_type, value_type) \ - static void ep_rt_ ## hash_map_name ## _alloc (hash_map_type *hash_map, uint32_t (*hash_callback)(const void *), bool (*eq_callback)(const void *, const void *), void (*key_free_callback)(void *), void (*value_free_callback)(void *)); \ - static void ep_rt_ ## hash_map_name ## _free (hash_map_type *hash_map); \ - static void ep_rt_ ## hash_map_name ## _add (hash_map_type *hash_map, key_type key, value_type value); \ - static void ep_rt_ ## hash_map_name ## _remove (hash_map_type *hash_map, const key_type key); \ - static void ep_rt_ ## hash_map_name ## _remove_all (hash_map_type *hash_map); \ - static bool ep_rt_ ## hash_map_name ## _lookup (const hash_map_type *hash_map, const key_type key, value_type *value); \ - static uint32_t ep_rt_ ## hash_map_name ## _count (const hash_map_type *hash_map); + EP_RT_DECLARE_HASH_MAP_PREFIX(ep, hash_map_name, hash_map_type, key_type, value_type) + +#define EP_RT_DECLARE_HASH_MAP_ITERATOR_PREFIX(prefix_name, hash_map_name, hash_map_type, iterator_type, key_type, value_type) \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, iterator_begin) (const hash_map_type *hash_map, iterator_type *iterator); \ + static bool EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, iterator_end) (const hash_map_type *hash_map, const iterator_type *iterator); \ + static void EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, iterator_next) (const hash_map_type *hash_map, iterator_type *iterator); \ + static key_type EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, iterator_key) (const iterator_type *iterator); \ + static value_type EP_RT_BUILD_TYPE_FUNC_NAME(prefix_name, hash_map_name, iterator_value) (const iterator_type *iterator); #define EP_RT_DECLARE_HASH_MAP_ITERATOR(hash_map_name, hash_map_type, iterator_type, key_type, value_type) \ - static void ep_rt_ ## hash_map_name ## _iterator_begin (const hash_map_type *hash_map, iterator_type *iterator); \ - static bool ep_rt_ ## hash_map_name ## _iterator_end (const hash_map_type *hash_map, const iterator_type *iterator); \ - static void ep_rt_ ## hash_map_name ## _iterator_next (const hash_map_type *hash_map, iterator_type *iterator); \ - static key_type ep_rt_ ## hash_map_name ## _iterator_key (const iterator_type *iterator); \ - static value_type ep_rt_ ## hash_map_name ## _iterator_value (const iterator_type *iterator); + EP_RT_DECLARE_HASH_MAP_ITERATOR_PREFIX(ep, hash_map_name, hash_map_type, iterator_type, key_type, value_type) /* * Atomics. @@ -102,7 +131,7 @@ ep_rt_atomic_dec_int64_t (volatile int64_t *value); * EventPipe. */ -EP_RT_DECLARE_ARRAY (session_id_array, ep_rt_session_id_array_t, EventPipeSessionID) +EP_RT_DECLARE_ARRAY (session_id_array, ep_rt_session_id_array_t, ep_rt_session_id_array_iterator_t, EventPipeSessionID) EP_RT_DECLARE_ARRAY_ITERATOR (session_id_array, ep_rt_session_id_array_t, ep_rt_session_id_array_iterator_t, EventPipeSessionID) static @@ -150,15 +179,15 @@ ep_rt_init_providers_and_events (void); * EventPipeBuffer. */ -EP_RT_DECLARE_ARRAY (buffer_array, ep_rt_buffer_array_t, EventPipeBuffer *) +EP_RT_DECLARE_ARRAY (buffer_array, ep_rt_buffer_array_t, ep_rt_buffer_array_iterator_t, EventPipeBuffer *) EP_RT_DECLARE_ARRAY_ITERATOR (buffer_array, ep_rt_buffer_array_t, ep_rt_buffer_array_iterator_t, EventPipeBuffer *) /* * EventPipeBufferList. */ -EP_RT_DECLARE_ARRAY (buffer_list_array, ep_rt_buffer_list_array_t, EventPipeBufferList *) -EP_RT_DECLARE_ARRAY_ITERATOR (buffer_list_array, ep_rt_buffer_list_array_t, ep_rt_buffer_array_iterator_t, EventPipeBufferList *) +EP_RT_DECLARE_ARRAY (buffer_list_array, ep_rt_buffer_list_array_t, ep_rt_buffer_list_array_iterator_t, EventPipeBufferList *) +EP_RT_DECLARE_ARRAY_ITERATOR (buffer_list_array, ep_rt_buffer_list_array_t, ep_rt_buffer_list_array_iterator_t, EventPipeBufferList *) /* * EventPipeEvent. @@ -194,6 +223,9 @@ ep_rt_provider_list_find_by_name ( * EventPipeProviderConfiguration. */ +EP_RT_DECLARE_ARRAY (provider_config_array, ep_rt_provider_config_array_t, ep_rt_provider_config_array_iterator_t, EventPipeProviderConfiguration) +EP_RT_DECLARE_ARRAY_ITERATOR (provider_config_array, ep_rt_provider_config_array_t, ep_rt_provider_config_array_iterator_t, EventPipeProviderConfiguration) + static bool ep_rt_config_value_get_enable (void); @@ -266,7 +298,7 @@ EP_RT_DECLARE_LIST_ITERATOR (sequence_point_list, ep_rt_sequence_point_list_t, e * EventPipeThread. */ -EP_RT_DECLARE_ARRAY (thread_array, ep_rt_thread_array_t, EventPipeThread *) +EP_RT_DECLARE_ARRAY (thread_array, ep_rt_thread_array_t, ep_rt_thread_array_iterator_t, EventPipeThread *) EP_RT_DECLARE_ARRAY_ITERATOR (thread_array, ep_rt_thread_array_t, ep_rt_thread_array_iterator_t, EventPipeThread *) /* @@ -276,7 +308,7 @@ EP_RT_DECLARE_ARRAY_ITERATOR (thread_array, ep_rt_thread_array_t, ep_rt_thread_a EP_RT_DECLARE_LIST (thread_session_state_list, ep_rt_thread_session_state_list_t, EventPipeThreadSessionState *) EP_RT_DECLARE_LIST_ITERATOR (thread_session_state_list, ep_rt_thread_session_state_list_t, ep_rt_thread_session_state_list_iterator_t, EventPipeThreadSessionState *) -EP_RT_DECLARE_ARRAY (thread_session_state_array, ep_rt_thread_session_state_array_t, EventPipeThreadSessionState *) +EP_RT_DECLARE_ARRAY (thread_session_state_array, ep_rt_thread_session_state_array_t, ep_rt_thread_session_state_array_iterator_t, EventPipeThreadSessionState *) EP_RT_DECLARE_ARRAY_ITERATOR (thread_session_state_array, ep_rt_thread_session_state_array_t, ep_rt_thread_session_state_array_iterator_t, EventPipeThreadSessionState *) /* @@ -321,10 +353,18 @@ static EventPipeWaitHandle ep_rt_wait_event_get_handle (ep_rt_wait_event_handle_t *wait_event); +static +bool +ep_rt_wait_event_is_valid (ep_rt_wait_event_handle_t *wait_event); + /* * Misc. */ +static +int +ep_rt_get_last_error (void); + static bool ep_rt_process_detach (void); @@ -355,6 +395,17 @@ ep_rt_object_free (void *ptr); * PAL. */ +static +bool +ep_rt_thread_create ( + void *thread_func, + void *params, + void *id); + +static +void +ep_rt_thread_sleep (uint64_t ns); + static uint32_t ep_rt_current_process_get_id (void); @@ -380,8 +431,12 @@ int64_t ep_rt_perf_frequency_query (void); static -ep_systemtime_t -ep_rt_system_time_get (void); +void +ep_rt_system_time_get (EventPipeSystemTime *system_time); + +static +int64_t +ep_rt_system_timestamp_get (void); static int32_t @@ -389,7 +444,7 @@ ep_rt_system_get_alloc_granularity (void); static const ep_char8_t * -ep_rt_command_line_get (void); +ep_rt_os_command_line_get (void); static ep_rt_file_handle_t @@ -413,7 +468,15 @@ ep_rt_valloc0 (size_t buffer_size); static void -ep_rt_vfree (uint8_t *buffer, size_t buffer_size); +ep_rt_vfree ( + uint8_t *buffer, + size_t buffer_size); + +static +uint32_t +ep_rt_temp_path_get ( + ep_char8_t *buffer, + uint32_t buffer_len); /* * SpinLock. @@ -462,10 +525,32 @@ ep_rt_utf8_string_compare ( const ep_char8_t *str1, const ep_char8_t *str2); +static +int +ep_rt_utf8_string_compare_ignore_case ( + const ep_char8_t *str1, + const ep_char8_t *str2); + +static +bool +ep_rt_utf8_string_is_null_or_empty (const ep_char8_t *str); + static ep_char8_t * ep_rt_utf8_string_dup (const ep_char8_t *str); +static +ep_char8_t * +ep_rt_utf8_string_strtok ( + ep_char8_t *str, + const ep_char8_t *delimiter, + ep_char8_t **context); + +#define ep_rt_utf8_string_snprintf( \ + str, \ + str_len, \ + format, ...) ep_redefine + static ep_char16_t * ep_rt_utf8_to_utf16_string ( @@ -490,6 +575,12 @@ static void ep_rt_utf16_string_free (ep_char16_t *str); +static +wchar_t * +ep_rt_utf8_to_wcs_string ( + const ep_char8_t *str, + size_t len); + static const ep_char8_t * ep_rt_managed_command_line_get (void); @@ -497,9 +588,14 @@ ep_rt_managed_command_line_get (void); /* * Thread. */ + +static +void +ep_rt_thread_setup (bool background_thread); + static void -ep_rt_thread_setup (void); +ep_rt_thread_teardown (void); static EventPipeThread * diff --git a/src/mono/mono/eventpipe/ep-session.c b/src/mono/mono/eventpipe/ep-session.c index cd7b4aeb4c2064..074fe6917f2151 100644 --- a/src/mono/mono/eventpipe/ep-session.c +++ b/src/mono/mono/eventpipe/ep-session.c @@ -31,11 +31,65 @@ session_create_ipc_streaming_thread (EventPipeSession *session); * EventPipeSession. */ +EP_RT_DEFINE_THREAD_FUNC (streaming_thread) +{ + EP_ASSERT (data != NULL); + if (data == NULL) + return 1; + + EventPipeSession *const session = (EventPipeSession *)data; + if (session->session_type != EP_SESSION_TYPE_IPCSTREAM) + return 1; + + ep_rt_thread_setup (true); + session->ipc_streaming_thread = ep_thread_get_or_create (); + + bool success = true; + ep_rt_wait_event_handle_t *wait_event = (ep_rt_wait_event_handle_t *)ep_session_get_wait_event (session); + + while (ep_session_get_ipc_streaming_enabled (session)) { + bool events_written = false; + if (!ep_session_write_all_buffers_to_file (session, &events_written)) { + success = false; + break; + } + + if (!events_written) { + // No events were available, sleep until more are available + ep_rt_wait_event_wait (wait_event, EP_INFINITE_WAIT, false); + } + + // Wait until it's time to sample again. + const uint32_t timeout_ns = 100000000; // 100 msec. + ep_rt_thread_sleep (timeout_ns); + } + + ep_rt_wait_event_set (&session->rt_thread_shutdown_event); + + if (!success) + ep_disable ((EventPipeSessionID)session); + + session->ipc_streaming_thread = NULL; + ep_rt_thread_teardown (); + + return (ep_rt_thread_start_func_return_t)0; +} + static void session_create_ipc_streaming_thread (EventPipeSession *session) { - //TODO: Implement. + EP_ASSERT (session != NULL); + EP_ASSERT (session->session_type == EP_SESSION_TYPE_IPCSTREAM); + + ep_requires_lock_held (); + + ep_session_set_ipc_streaming_enabled (session, true); + ep_rt_wait_event_alloc (&session->rt_thread_shutdown_event, true, false); + + ep_rt_thread_id_t thread_id = 0; + if (!ep_rt_thread_create ((void *)streaming_thread, (void *)session, &thread_id)) + EP_ASSERT (!"Unable to create IPC stream flushing thread."); } static @@ -55,7 +109,7 @@ session_disable_ipc_streaming_thread (EventPipeSession *session) // Thread could be waiting on the event that there is new data to read. ep_rt_wait_event_set (ep_buffer_manager_get_rt_wait_event_ref (session->buffer_manager)); - // Wait for the sampling thread to clean itself up. + // Wait for the streaming thread to clean itself up. ep_rt_wait_event_handle_t *rt_thread_shutdown_event = &session->rt_thread_shutdown_event; ep_rt_wait_event_wait (rt_thread_shutdown_event, EP_INFINITE_WAIT, false /* bAlertable */); ep_rt_wait_event_free (rt_thread_shutdown_event); @@ -136,10 +190,8 @@ ep_session_alloc ( break; } - instance->session_start_time = ep_rt_system_time_get (); - instance->session_start_timestamp = ep_perf_counter_query (); - - ep_rt_wait_event_alloc (&instance->rt_thread_shutdown_event, true, false); + instance->session_start_time = ep_system_timestamp_get (); + instance->session_start_timestamp = ep_perf_timestamp_get (); ep_on_exit: ep_requires_lock_held (); @@ -240,7 +292,7 @@ ep_session_enable_rundown (EventPipeSession *session) void ep_session_execute_rundown (EventPipeSession *session) { - //TODO: Implement. This is mainly runtime specific implementation + // TODO: Implement. This is mainly runtime specific implementation //since it will emit native trace events into the pipe (using CoreCLR's ETW support). } @@ -356,9 +408,9 @@ ep_session_write_all_buffers_to_file (EventPipeSession *session, bool *events_wr // Get the current time stamp. // ep_buffer_manager_write_all_buffer_to_file will use this to ensure that no events after // the current timestamp are written into the file. - ep_timestamp_t stop_timestamp = ep_rt_perf_counter_query (); + ep_timestamp_t stop_timestamp = ep_perf_timestamp_get (); ep_buffer_manager_write_all_buffers_to_file (session->buffer_manager, session->file, stop_timestamp, events_written); - return ep_file_has_errors (session->file); + return !ep_file_has_errors (session->file); } bool diff --git a/src/mono/mono/eventpipe/ep-session.h b/src/mono/mono/eventpipe/ep-session.h index 6755a19a436e1c..d26ca4dd75e300 100644 --- a/src/mono/mono/eventpipe/ep-session.h +++ b/src/mono/mono/eventpipe/ep-session.h @@ -24,8 +24,8 @@ struct _EventPipeSession { #else struct _EventPipeSession_Internal { #endif - // When the session is of IPC type, this becomes a handle to the streaming thread. - EventPipeThread ipc_streaming_thread; + // When the session is of IPC type, this becomes a reference to the streaming thread. + EventPipeThread *ipc_streaming_thread; // Event object used to signal Disable that the IPC streaming thread is done. ep_rt_wait_event_handle_t rt_thread_shutdown_event; // The set of configurations for each provider in the session. @@ -37,7 +37,7 @@ struct _EventPipeSession_Internal { // For synchoronous sessions. EventPipeSessionSynchronousCallback synchronous_callback; // Start date and time in UTC. - ep_systemtime_t session_start_time; + ep_system_timestamp_t session_start_time; // Start timestamp. ep_timestamp_t session_start_timestamp; uint32_t index; @@ -66,7 +66,7 @@ EP_DEFINE_GETTER(EventPipeSession *, session, EventPipeSessionProviderList *, pr EP_DEFINE_GETTER(EventPipeSession *, session, EventPipeBufferManager *, buffer_manager) EP_DEFINE_GETTER_REF(EventPipeSession *, session, volatile uint32_t *, rundown_enabled) EP_DEFINE_GETTER(EventPipeSession *, session, bool, rundown_requested) -EP_DEFINE_GETTER(EventPipeSession *, session, ep_systemtime_t, session_start_time) +EP_DEFINE_GETTER(EventPipeSession *, session, ep_timestamp_t, session_start_time) EP_DEFINE_GETTER(EventPipeSession *, session, ep_timestamp_t, session_start_timestamp) EP_DEFINE_GETTER(EventPipeSession *, session, EventPipeFile *, file) @@ -180,4 +180,4 @@ ep_session_set_ipc_streaming_enabled ( bool enabled); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_SESSION_H__ **/ +#endif /* __EVENTPIPE_SESSION_H__ */ diff --git a/src/mono/mono/eventpipe/ep-stack-contents.h b/src/mono/mono/eventpipe/ep-stack-contents.h index af933d87224b37..08c4ae08b0b8e0 100644 --- a/src/mono/mono/eventpipe/ep-stack-contents.h +++ b/src/mono/mono/eventpipe/ep-stack-contents.h @@ -175,4 +175,4 @@ ep_stack_contents_get_size (const EventPipeStackContents *stack_contents) } #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_STACK_CONTENTS_H__ **/ +#endif /* __EVENTPIPE_STACK_CONTENTS_H__ */ diff --git a/src/mono/mono/eventpipe/ep-stream.c b/src/mono/mono/eventpipe/ep-stream.c index ae0524482898b6..cf5447e341a3b2 100644 --- a/src/mono/mono/eventpipe/ep-stream.c +++ b/src/mono/mono/eventpipe/ep-stream.c @@ -214,7 +214,7 @@ ep_fast_serializer_write_buffer ( bool result = ep_stream_writer_write (fast_serializer->stream_writer, buffer, buffer_len, &bytes_written); uint32_t required_padding = fast_serializer->required_padding; - required_padding = (FAST_SERIALIZER_ALIGNMENT_SIZE + required_padding - (bytes_written & FAST_SERIALIZER_ALIGNMENT_SIZE)) % FAST_SERIALIZER_ALIGNMENT_SIZE; + required_padding = (FAST_SERIALIZER_ALIGNMENT_SIZE + required_padding - (bytes_written % FAST_SERIALIZER_ALIGNMENT_SIZE)) % FAST_SERIALIZER_ALIGNMENT_SIZE; fast_serializer->required_padding = required_padding; // This will cause us to stop writing to the file. @@ -425,64 +425,94 @@ ep_file_stream_writer_write ( */ IpcStream * -ep_ipc_stream_alloc (ep_rt_ipc_handle_t rt_ipc) +ep_ipc_stream_init ( + IpcStream *ipc_stream, + IpcStreamVtable *vtable) { - IpcStream *instance = ep_rt_object_alloc (IpcStream); - ep_raise_error_if_nok (instance != NULL); - - //Transfer ownership. - instance->rt_ipc = rt_ipc; + EP_ASSERT (ipc_stream != NULL); + EP_ASSERT (vtable != NULL); -ep_on_exit: - return instance; + ipc_stream->vtable = vtable; + return ipc_stream; +} -ep_on_error: - ep_ipc_stream_free (instance); - instance = NULL; - ep_exit_error_handler (); +void +ep_ipc_stream_fini (IpcStream *ipc_stream) +{ + return; } void -ep_ipc_stream_free (IpcStream *ipc_stream) +ep_ipc_stream_free_vcall (IpcStream *ipc_stream) { ep_return_void_if_nok (ipc_stream != NULL); - ep_ipc_stream_flush (ipc_stream); - ep_ipc_stream_disconnect (ipc_stream); - ep_ipc_stream_close (ipc_stream); - ep_rt_object_free (ipc_stream); + EP_ASSERT (ipc_stream->vtable != NULL); + IpcStreamVtable *vtable = ipc_stream->vtable; + + ep_ipc_stream_flush_vcall (ipc_stream); + ep_ipc_stream_close_vcall (ipc_stream); + + EP_ASSERT (vtable->free_func != NULL); + vtable->free_func (ipc_stream); } bool -ep_ipc_stream_flush (IpcStream *ipc_stream) +ep_ipc_stream_read_vcall ( + IpcStream *ipc_stream, + uint8_t *buffer, + uint32_t bytes_to_read, + uint32_t *bytes_read, + uint32_t timeout_ms) { - //TODO: Implement. - return false; + EP_ASSERT (ipc_stream != NULL); + + EP_ASSERT (ipc_stream->vtable != NULL); + IpcStreamVtable *vtable = ipc_stream->vtable; + + EP_ASSERT (vtable->read_func != NULL); + return vtable->read_func (ipc_stream, buffer, bytes_to_read, bytes_read, timeout_ms); } bool -ep_ipc_stream_disconnect (IpcStream *ipc_stream) +ep_ipc_stream_write_vcall ( + IpcStream *ipc_stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written, + uint32_t timeout_ms) { - //TODO: Implement. - return false; + EP_ASSERT (ipc_stream != NULL); + + EP_ASSERT (ipc_stream->vtable != NULL); + IpcStreamVtable *vtable = ipc_stream->vtable; + + EP_ASSERT (vtable->write_func != NULL); + return vtable->write_func (ipc_stream, buffer, bytes_to_write, bytes_written, timeout_ms); } bool -ep_ipc_stream_close (IpcStream *ipc_stream) +ep_ipc_stream_flush_vcall (IpcStream *ipc_stream) { - //TODO: Implement. - return false; + EP_ASSERT (ipc_stream != NULL); + + EP_ASSERT (ipc_stream->vtable != NULL); + IpcStreamVtable *vtable = ipc_stream->vtable; + + EP_ASSERT (vtable->flush_func != NULL); + return vtable->flush_func (ipc_stream); } bool -ep_ipc_stream_write ( - IpcStream *ipc_stream, - const uint8_t *buffer, - uint32_t bytes_to_write, - uint32_t *bytes_written) +ep_ipc_stream_close_vcall (IpcStream *ipc_stream) { - //TODO: Implement. - return false; + EP_ASSERT (ipc_stream != NULL); + + EP_ASSERT (ipc_stream->vtable != NULL); + IpcStreamVtable *vtable = ipc_stream->vtable; + + EP_ASSERT (vtable->close_func != NULL); + return vtable->close_func (ipc_stream); } /* @@ -546,7 +576,7 @@ ep_ipc_stream_writer_free (IpcStreamWriter *ipc_stream_writer) { ep_return_void_if_nok (ipc_stream_writer != NULL); - ep_ipc_stream_free (ipc_stream_writer->ipc_stream); + ep_ipc_stream_free_vcall (ipc_stream_writer->ipc_stream); ep_stream_writer_fini (&ipc_stream_writer->stream_writer); ep_rt_object_free (ipc_stream_writer); } @@ -566,7 +596,7 @@ ep_ipc_stream_writer_write ( bool result = false; ep_raise_error_if_nok (ep_ipc_stream_writer_get_ipc_stream (ipc_stream_writer) != NULL); - result = ep_ipc_stream_write (ep_ipc_stream_writer_get_ipc_stream (ipc_stream_writer), buffer, bytes_to_write, bytes_written); + result = ep_ipc_stream_write_vcall (ep_ipc_stream_writer_get_ipc_stream (ipc_stream_writer), buffer, bytes_to_write, bytes_written, EP_INFINITE_WAIT); ep_on_exit: return result; diff --git a/src/mono/mono/eventpipe/ep-stream.h b/src/mono/mono/eventpipe/ep-stream.h index f1e91648b02270..687d55f61d3c49 100644 --- a/src/mono/mono/eventpipe/ep-stream.h +++ b/src/mono/mono/eventpipe/ep-stream.h @@ -286,13 +286,26 @@ ep_file_stream_writer_write ( * IpcStream. */ +typedef void (*IpcStreamFreeFunc)(void *object); +typedef bool (*IpcStreamReadFunc)(void *object, uint8_t *buffer, uint32_t bytes_to_read, uint32_t *bytes_read, uint32_t timeout_ms); +typedef bool (*IpcStreamWriteFunc)(void *object, const uint8_t *buffer, uint32_t bytes_to_write, uint32_t *bytes_written, uint32_t timeout_ms); +typedef bool (*IpcStreamFlushFunc)(void *object); +typedef bool (*IpcStreamCloseFunc)(void *object); + +struct _IpcStreamVtable { + IpcStreamFreeFunc free_func; + IpcStreamReadFunc read_func; + IpcStreamWriteFunc write_func; + IpcStreamFlushFunc flush_func; + IpcStreamCloseFunc close_func; +}; + #if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_STREAM_GETTER_SETTER) -//TODO: Implement. struct _IpcStream { #else struct _IpcStream_Internal { #endif - ep_rt_ipc_handle_t rt_ipc; + IpcStreamVtable *vtable; }; #if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_STREAM_GETTER_SETTER) @@ -301,29 +314,38 @@ struct _IpcStream { }; #endif -EP_DEFINE_GETTER(IpcStream *, ipc_stream, ep_rt_ipc_handle_t, rt_ipc) - IpcStream * -ep_ipc_stream_alloc (ep_rt_ipc_handle_t rt_ipc); +ep_ipc_stream_init ( + IpcStream *ipc_stream, + IpcStreamVtable *vtable); + +void +ep_ipc_stream_fini (IpcStream *ipc_stream); void -ep_ipc_stream_free (IpcStream *ipc_stream); +ep_ipc_stream_free_vcall (IpcStream *ipc_stream); bool -ep_ipc_stream_flush (IpcStream *stream); +ep_ipc_stream_read_vcall ( + IpcStream *ipc_stream, + uint8_t *buffer, + uint32_t bytes_to_read, + uint32_t *bytes_read, + uint32_t timeout_ms); bool -ep_ipc_stream_disconnect (IpcStream *stream); +ep_ipc_stream_write_vcall ( + IpcStream *ipc_stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written, + uint32_t timeout_ms); bool -ep_ipc_stream_close (IpcStream *stream); +ep_ipc_stream_flush_vcall (IpcStream *ipc_stream); bool -ep_ipc_stream_write ( - IpcStream *stream, - const uint8_t *buffer, - uint32_t bytes_to_write, - uint32_t *bytes_written); +ep_ipc_stream_close_vcall (IpcStream *ipc_stream); /* * IpcStreamWriter. @@ -363,4 +385,4 @@ ep_ipc_stream_writer_write ( uint32_t *bytes_written); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_STREAM_H__ **/ +#endif /* __EVENTPIPE_STREAM_H__ */ diff --git a/src/mono/mono/eventpipe/ep-thread.c b/src/mono/mono/eventpipe/ep-thread.c index 8f2573badcbb3d..39da171ecaf087 100644 --- a/src/mono/mono/eventpipe/ep-thread.c +++ b/src/mono/mono/eventpipe/ep-thread.c @@ -66,10 +66,21 @@ ep_thread_free (EventPipeThread *thread) ep_rt_spin_lock_aquire (&_ep_threads_lock); // Remove ourselves from the global list - if (EP_UNLIKELY (!ep_rt_thread_array_remove (&_ep_threads, thread))) - EP_ASSERT (!"We couldn't find ourselves in the global thread list"); + ep_rt_thread_array_iterator_t iterator; + bool found = false; + ep_rt_thread_array_iterator_begin (&_ep_threads, &iterator); + while (!ep_rt_thread_array_iterator_end (&_ep_threads, &iterator)) { + if (ep_rt_thread_array_iterator_value (&iterator) == thread) { + ep_rt_thread_array_remove (&_ep_threads, &iterator); + found = true; + break; + } + ep_rt_thread_array_iterator_next (&_ep_threads, &iterator); + } ep_rt_spin_lock_release (&_ep_threads_lock); + EP_ASSERT (found || !"We couldn't find ourselves in the global thread list"); + ep_rt_spin_lock_free (&thread->rt_lock); ep_rt_object_free (thread); } diff --git a/src/mono/mono/eventpipe/ep-thread.h b/src/mono/mono/eventpipe/ep-thread.h index dc2aa27f64dd4b..7948d955432782 100644 --- a/src/mono/mono/eventpipe/ep-thread.h +++ b/src/mono/mono/eventpipe/ep-thread.h @@ -297,4 +297,4 @@ void ep_thread_session_state_increment_sequence_number (EventPipeThreadSessionState *thread_session_state); #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_THREAD_H__ **/ +#endif /* __EVENTPIPE_THREAD_H__ */ diff --git a/src/mono/mono/eventpipe/ep-types.h b/src/mono/mono/eventpipe/ep-types.h index 1a0c729a9c162a..2d2b3044e964df 100644 --- a/src/mono/mono/eventpipe/ep-types.h +++ b/src/mono/mono/eventpipe/ep-types.h @@ -48,6 +48,7 @@ typedef struct _EventPipeSequencePoint EventPipeSequencePoint; typedef struct _EventPipeSequencePointBlock EventPipeSequencePointBlock; typedef struct _EventPipeStackBlock EventPipeStackBlock; typedef struct _EventPipeStackContents EventPipeStackContents; +typedef struct _EventPipeSystemTime EventPipeSystemTime; typedef struct _EventPipeThread EventPipeThread; typedef struct _EventPipeThreadHolder EventPipeThreadHolder; typedef struct _EventPipeThreadSessionState EventPipeThreadSessionState; @@ -57,6 +58,7 @@ typedef struct _FastSerializer FastSerializer; typedef struct _FileStream FileStream; typedef struct _FileStreamWriter FileStreamWriter; typedef struct _IpcStream IpcStream; +typedef struct _IpcStreamVtable IpcStreamVtable; typedef struct _IpcStreamWriter IpcStreamWriter; typedef struct _StackHashEntry StackHashEntry; typedef struct _StackHashKey StackHashKey; @@ -164,7 +166,7 @@ typedef uint64_t EventPipeSessionID; typedef char ep_char8_t; typedef unsigned short ep_char16_t; typedef int64_t ep_timestamp_t; -typedef int64_t ep_systemtime_t; +typedef int64_t ep_system_timestamp_t; /* * EventPipe Callbacks. @@ -375,5 +377,51 @@ ep_provider_config_init ( void ep_provider_config_fini (EventPipeProviderConfiguration *provider_config); +/* + * EventPipeSystemTime. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_EP_GETTER_SETTER) +struct _EventPipeSystemTime { +#else +struct _EventPipeSystemTime_Internal { +#endif + uint16_t year; + uint16_t month; + uint16_t day_of_week; + uint16_t day; + uint16_t hour; + uint16_t minute; + uint16_t second; + uint16_t milliseconds; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_EP_GETTER_SETTER) +struct _EventPipeSystemTime { + uint8_t _internal [sizeof (struct _EventPipeSystemTime_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeSystemTime *, system_time, uint16_t, year); +EP_DEFINE_GETTER(EventPipeSystemTime *, system_time, uint16_t, month); +EP_DEFINE_GETTER(EventPipeSystemTime *, system_time, uint16_t, day_of_week); +EP_DEFINE_GETTER(EventPipeSystemTime *, system_time, uint16_t, day); +EP_DEFINE_GETTER(EventPipeSystemTime *, system_time, uint16_t, hour); +EP_DEFINE_GETTER(EventPipeSystemTime *, system_time, uint16_t, minute); +EP_DEFINE_GETTER(EventPipeSystemTime *, system_time, uint16_t, second); +EP_DEFINE_GETTER(EventPipeSystemTime *, system_time, uint16_t, milliseconds); + +void +ep_system_time_set ( + EventPipeSystemTime *system_time, + uint16_t year, + uint16_t month, + uint16_t day_of_week, + uint16_t day, + uint16_t hour, + uint16_t minute, + uint16_t second, + uint16_t milliseconds); + #endif /* ENABLE_PERFTRACING */ #endif /* __EVENTPIPE_TYPES_H__ */ diff --git a/src/mono/mono/eventpipe/ep.c b/src/mono/mono/eventpipe/ep.c index b29813ad140828..27e2646618eab5 100644 --- a/src/mono/mono/eventpipe/ep.c +++ b/src/mono/mono/eventpipe/ep.c @@ -495,7 +495,7 @@ log_process_info_event (EventPipeEventSource *event_source) const ep_char8_t *cmd_line = ep_rt_managed_command_line_get (); if (cmd_line == NULL) - cmd_line = ep_rt_command_line_get (); + cmd_line = ep_rt_os_command_line_get (); // Log the process information event. ep_event_source_send_process_info (event_source, cmd_line); @@ -583,7 +583,7 @@ disable (EventPipeSessionID id) ep_requires_lock_not_held (); if (_ep_can_start_threads) - ep_rt_thread_setup (); + ep_rt_thread_setup (false); if (id == 0) return; @@ -632,7 +632,7 @@ write_event ( ep_return_void_if_nok (ep_event_is_enabled (ep_event) == true); // Get current thread. - EventPipeThread *const thread = ep_thread_get (); + EventPipeThread *const thread = ep_thread_get_or_create (); // If the activity id isn't specified AND we are in a eventpipe thread, pull it from the current thread. // If pThread is NULL (we aren't in writing from a managed thread) then pActivityId can be NULL @@ -1288,7 +1288,7 @@ ep_finish_init (void) ep_session_start_streaming ((EventPipeSession *)session_id); ep_rt_session_id_array_iterator_next (&_ep_deferred_enable_session_ids, &deferred_session_ids_iterator); } - ep_rt_session_id_array_clear (&_ep_deferred_enable_session_ids); + ep_rt_session_id_array_clear (&_ep_deferred_enable_session_ids, NULL); } ep_rt_sample_profiler_can_start_sampling (); @@ -1306,7 +1306,7 @@ ep_finish_init (void) disable (session_id); ep_rt_session_id_array_iterator_next (&_ep_deferred_disable_session_ids, &deferred_session_ids_iterator); } - ep_rt_session_id_array_clear (&_ep_deferred_disable_session_ids); + ep_rt_session_id_array_clear (&_ep_deferred_disable_session_ids, NULL); } ep_on_exit: @@ -1405,22 +1405,6 @@ ep_get_wait_handle (EventPipeSessionID session_id) return session ? ep_session_get_wait_event (session) : 0; } -/* - * EventPipePerf. - */ - -int64_t -ep_perf_counter_query (void) -{ - return ep_rt_perf_counter_query (); -} - -int64_t -ep_perf_frequency_query (void) -{ - return ep_rt_perf_frequency_query (); -} - /* * EventPipeProviderCallbackDataQueue. */ @@ -1451,6 +1435,33 @@ ep_provider_callback_data_queue_try_dequeue ( return true; } +/* + * EventPipeSystemTime. + */ + +void +ep_system_time_set ( + EventPipeSystemTime *system_time, + uint16_t year, + uint16_t month, + uint16_t day_of_week, + uint16_t day, + uint16_t hour, + uint16_t minute, + uint16_t second, + uint16_t milliseconds) +{ + EP_ASSERT (system_time != NULL); + system_time->year = year; + system_time->month = month; + system_time->day_of_week = day_of_week; + system_time->day = day; + system_time->hour = hour; + system_time->minute = minute; + system_time->second = second; + system_time->milliseconds = milliseconds; +} + #endif /* ENABLE_PERFTRACING */ extern const char quiet_linker_empty_file_warning_eventpipe; diff --git a/src/mono/mono/eventpipe/ep.h b/src/mono/mono/eventpipe/ep.h index 31e38137125020..4d9693521f9819 100644 --- a/src/mono/mono/eventpipe/ep.h +++ b/src/mono/mono/eventpipe/ep.h @@ -261,7 +261,7 @@ inline bool ep_walk_managed_stack_for_current_thread (EventPipeStackContents *stack_contents) { - //TODO: Implement. + // TODO: Implement. ep_stack_contents_reset (stack_contents); return ep_rt_walk_managed_stack_for_current_thread (stack_contents); } @@ -270,11 +270,41 @@ ep_walk_managed_stack_for_current_thread (EventPipeStackContents *stack_contents * EventPipePerf. */ -int64_t -ep_perf_counter_query (void); +static +inline +ep_timestamp_t +ep_perf_timestamp_get (void) +{ + return (ep_timestamp_t)ep_rt_perf_counter_query (); +} +static +inline int64_t -ep_perf_frequency_query (void); +ep_perf_frequency_query (void) +{ + return ep_rt_perf_frequency_query (); +} + +/* + * EventPipeSystemTime. + */ + +static +inline +ep_system_timestamp_t +ep_system_timestamp_get (void) +{ + return (ep_system_timestamp_t)ep_rt_system_timestamp_get (); +} + +static +inline +void +ep_system_time_get (EventPipeSystemTime *system_time) +{ + ep_rt_system_time_get (system_time); +} #else /* ENABLE_PERFTRACING */ @@ -303,4 +333,4 @@ ep_shutdown (void) } #endif /* ENABLE_PERFTRACING */ -#endif /** __EVENTPIPE_H__ **/ +#endif /* __EVENTPIPE_H__ */ diff --git a/src/mono/mono/eventpipe/test/Makefile.am b/src/mono/mono/eventpipe/test/Makefile.am index 6e0c0e92237c11..04317b0315b144 100644 --- a/src/mono/mono/eventpipe/test/Makefile.am +++ b/src/mono/mono/eventpipe/test/Makefile.am @@ -38,6 +38,7 @@ ep-fake-tests.c \ ep-fastserializer-tests.c \ ep-file-tests.c \ ep-provider-callback-dataqueue-tests.c \ +ep-rt-tests.c \ ep-session-tests.c \ ep-setup-tests.c \ ep-teardown-tests.c \ diff --git a/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c b/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c index f1bd7c24c43c11..ce0106164fd28c 100644 --- a/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c +++ b/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c @@ -401,7 +401,7 @@ test_buffer_manager_write_events_to_file (EventPipeSerializationFormat format) test_location = 4; - ep_buffer_manager_write_all_buffers_to_file (buffer_manager, ep_session_get_file (session), ep_perf_counter_query (), &events_written); + ep_buffer_manager_write_all_buffers_to_file (buffer_manager, ep_session_get_file (session), ep_perf_timestamp_get (), &events_written); ep_raise_error_if_nok (events_written == true); @@ -506,9 +506,9 @@ test_buffer_manager_perf (void) test_location = 3; while (!done) { - int64_t start = ep_perf_counter_query (); + int64_t start = ep_perf_timestamp_get (); write_result = write_events (buffer_manager, thread, session, ep_event, 10 * 1000 * 1000, &events_written); - int64_t stop = ep_perf_counter_query (); + int64_t stop = ep_perf_timestamp_get (); accumulted_buffer_manager_write_time_ticks += stop - start; total_events_written += events_written; @@ -516,9 +516,9 @@ test_buffer_manager_perf (void) done = true; } else { bool ignore_events_written; - int64_t start = ep_perf_counter_query (); - ep_buffer_manager_write_all_buffers_to_file (buffer_manager, null_file, ep_perf_counter_query (), &ignore_events_written); - int64_t stop = ep_perf_counter_query (); + int64_t start = ep_perf_timestamp_get (); + ep_buffer_manager_write_all_buffers_to_file (buffer_manager, null_file, ep_perf_timestamp_get (), &ignore_events_written); + int64_t stop = ep_perf_timestamp_get (); accumulted_buffer_to_null_file_time_ticks += stop - start; } @@ -540,7 +540,7 @@ test_buffer_manager_perf (void) float total_events_written_per_sec = (float)total_events_written / (total_accumulted_time_sec ? total_accumulted_time_sec : 1.0); // Measured number of events/second for one thread. - //TODO: Setup acceptable pass/failure metrics. + // TODO: Setup acceptable pass/failure metrics. printf ("\n\tPerformance stats:\n"); printf ("\t\tTotal number of events: %i\n", total_events_written); printf ("\t\tTotal time in sec: %.2f\n\t\tTotal number of events written per sec/core: %.2f\n", total_accumulted_time_sec, total_events_written_per_sec); diff --git a/src/mono/mono/eventpipe/test/ep-buffer-tests.c b/src/mono/mono/eventpipe/test/ep-buffer-tests.c index 4d1dd3ebb3ec24..f6b818113710a9 100644 --- a/src/mono/mono/eventpipe/test/ep-buffer-tests.c +++ b/src/mono/mono/eventpipe/test/ep-buffer-tests.c @@ -600,9 +600,9 @@ test_check_buffer_perf (void) bool done = false; while (!done) { - int64_t start = ep_perf_counter_query (); + int64_t start = ep_perf_timestamp_get (); load_result = load_buffer (buffer, session, ep_event, 10 * 1000 * 1000, true, &events_written); - int64_t stop = ep_perf_counter_query (); + int64_t stop = ep_perf_timestamp_get (); accumulted_time_ticks += stop - start; total_events_written += events_written; @@ -622,7 +622,7 @@ test_check_buffer_perf (void) // Measured number of events/second for one thread. // Only measure loading data into pre-allocated buffer. - //TODO: Setup acceptable pass/failure metrics. + // TODO: Setup acceptable pass/failure metrics. printf ("\n\tPerformance stats:\n"); printf ("\t\tTotal number of events: %i\n", total_events_written); printf ("\t\tTotal time in sec: %.2f\n", accumulted_time_sec); diff --git a/src/mono/mono/eventpipe/test/ep-fastserializer-tests.c b/src/mono/mono/eventpipe/test/ep-fastserializer-tests.c index 36153f664d37a4..a4abdcb5f20136 100644 --- a/src/mono/mono/eventpipe/test/ep-fastserializer-tests.c +++ b/src/mono/mono/eventpipe/test/ep-fastserializer-tests.c @@ -331,8 +331,8 @@ test_fast_serializer_stack_block_get_type_name (void) ep_exit_error_handler (); } -//TODO: Add perf test just doing write into fast serializer with different event types (no event alloc/instancing). Write into void -//stream but still write into same memory buffer to do something. +// TODO: Add perf test just doing write into fast serializer with different event types (no event alloc/instancing). Write into void +// stream but still write into same memory buffer to do something. static Test ep_fastserializer_tests [] = { {"fast_serializer_object_fast_serialize", test_fast_serializer_object_fast_serialize}, diff --git a/src/mono/mono/eventpipe/test/ep-rt-tests.c b/src/mono/mono/eventpipe/test/ep-rt-tests.c new file mode 100644 index 00000000000000..5956237982c5e7 --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-rt-tests.c @@ -0,0 +1,170 @@ +#include "mono/eventpipe/ep.h" +#include "eglib/test/test.h" + +#ifdef _CRTDBG_MAP_ALLOC +static _CrtMemState eventpipe_memory_start_snapshot; +static _CrtMemState eventpipe_memory_end_snapshot; +static _CrtMemState eventpipe_memory_diff_snapshot; +#endif + +static RESULT +test_rt_setup (void) +{ +#ifdef _CRTDBG_MAP_ALLOC + _CrtMemCheckpoint (&eventpipe_memory_start_snapshot); +#endif + return NULL; +} + +static RESULT +test_rt_perf_frequency (void) +{ + return (ep_perf_frequency_query () > 0) ? NULL : FAILED ("Frequency to low"); +} + +static RESULT +test_rt_perf_timestamp (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + int64_t frequency = 0; + double elapsed_time_ms = 0; + + ep_timestamp_t start = ep_perf_timestamp_get (); + g_usleep (10 * 1000); + ep_timestamp_t stop = ep_perf_timestamp_get (); + + test_location = 1; + + ep_raise_error_if_nok (stop > start); + + test_location = 2; + + frequency = ep_perf_frequency_query (); + ep_raise_error_if_nok (frequency > 0); + + test_location = 3; + + elapsed_time_ms = ((double)(stop - start) / (double)frequency) * 1000; + + ep_raise_error_if_nok (elapsed_time_ms > 0); + + test_location = 4; + + ep_raise_error_if_nok (elapsed_time_ms > 10); + +ep_on_exit: + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_rt_system_time (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + EventPipeSystemTime time1; + EventPipeSystemTime time2; + bool time_diff = false; + + ep_system_time_get (&time1); + + ep_raise_error_if_nok (ep_system_time_get_year (&time1) > 1600 && ep_system_time_get_year (&time1) < 30828); + test_location = 1; + + ep_raise_error_if_nok (ep_system_time_get_month (&time1) > 0 && ep_system_time_get_month (&time1) < 13); + test_location = 2; + + ep_raise_error_if_nok (ep_system_time_get_day (&time1) > 0 && ep_system_time_get_day (&time1) < 32); + test_location = 3; + + ep_raise_error_if_nok (ep_system_time_get_day_of_week (&time1) >= 0 && ep_system_time_get_day_of_week (&time1) < 7); + test_location = 4; + + ep_raise_error_if_nok (ep_system_time_get_hour (&time1) >= 0 && ep_system_time_get_hour (&time1) < 24); + test_location = 5; + + ep_raise_error_if_nok (ep_system_time_get_minute (&time1) >= 0 && ep_system_time_get_minute (&time1) < 60); + test_location = 6; + + ep_raise_error_if_nok (ep_system_time_get_second (&time1) >= 0 && ep_system_time_get_second (&time1) < 60); + test_location = 7; + + ep_raise_error_if_nok (ep_system_time_get_milliseconds (&time1) >= 0 && ep_system_time_get_milliseconds (&time1) < 1000); + test_location = 8; + + g_usleep (1000 * 1000); + + ep_system_time_get (&time2); + + time_diff |= ep_system_time_get_year (&time1) != ep_system_time_get_year (&time2); + time_diff |= ep_system_time_get_month (&time1) != ep_system_time_get_month (&time2); + time_diff |= ep_system_time_get_day (&time1) != ep_system_time_get_day (&time2); + time_diff |= ep_system_time_get_day_of_week (&time1) != ep_system_time_get_day_of_week (&time2); + time_diff |= ep_system_time_get_hour (&time1) != ep_system_time_get_hour (&time2); + time_diff |= ep_system_time_get_minute (&time1) != ep_system_time_get_minute (&time2); + time_diff |= ep_system_time_get_second (&time1) != ep_system_time_get_second (&time2); + time_diff |= ep_system_time_get_milliseconds (&time1) != ep_system_time_get_milliseconds (&time2); + + ep_raise_error_if_nok (time_diff == true); + +ep_on_exit: + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_rt_system_timestamp (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + ep_system_timestamp_t start = ep_system_timestamp_get (); + g_usleep (10 * 1000); + ep_system_timestamp_t stop = ep_system_timestamp_get (); + + test_location = 1; + + ep_raise_error_if_nok (stop > start); + +ep_on_exit: + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_rt_teardown (void) +{ +#ifdef _CRTDBG_MAP_ALLOC + _CrtMemCheckpoint (&eventpipe_memory_end_snapshot); + if ( _CrtMemDifference( &eventpipe_memory_diff_snapshot, &eventpipe_memory_start_snapshot, &eventpipe_memory_end_snapshot) ) { + _CrtMemDumpStatistics( &eventpipe_memory_diff_snapshot ); + return FAILED ("Memory leak detected!"); + } +#endif + return NULL; +} + +static Test ep_rt_tests [] = { + {"test_rt_setup", test_rt_setup}, + {"test_rt_perf_frequency", test_rt_perf_frequency}, + {"test_rt_perf_timestamp", test_rt_perf_timestamp}, + {"test_rt_system_time", test_rt_system_time}, + {"test_rt_system_timestamp", test_rt_system_timestamp}, + {"test_rt_teardown", test_rt_teardown}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(ep_rt_tests_init, ep_rt_tests) diff --git a/src/mono/mono/eventpipe/test/ep-test.vcxproj b/src/mono/mono/eventpipe/test/ep-test.vcxproj index ae59c918fa95f6..7c1544b7533272 100644 --- a/src/mono/mono/eventpipe/test/ep-test.vcxproj +++ b/src/mono/mono/eventpipe/test/ep-test.vcxproj @@ -28,6 +28,7 @@ + diff --git a/src/mono/mono/eventpipe/test/ep-test.vcxproj.filters b/src/mono/mono/eventpipe/test/ep-test.vcxproj.filters index b3958e68729236..432204b213ff4c 100644 --- a/src/mono/mono/eventpipe/test/ep-test.vcxproj.filters +++ b/src/mono/mono/eventpipe/test/ep-test.vcxproj.filters @@ -40,6 +40,9 @@ Source Files\tests + + Source Files\tests + diff --git a/src/mono/mono/eventpipe/test/ep-tests.c b/src/mono/mono/eventpipe/test/ep-tests.c index 5ae0b222155932..8b8541db489a5e 100644 --- a/src/mono/mono/eventpipe/test/ep-tests.c +++ b/src/mono/mono/eventpipe/test/ep-tests.c @@ -20,6 +20,10 @@ static _CrtMemState eventpipe_memory_diff_snapshot; static RESULT test_eventpipe_setup (void) { + // Lazy initialized, force now to not show up as leak. + ep_rt_os_command_line_get (); + ep_rt_managed_command_line_get (); + #ifdef _CRTDBG_MAP_ALLOC _CrtMemCheckpoint (&eventpipe_memory_start_snapshot); #endif @@ -177,7 +181,7 @@ test_enable_disable (void) EventPipeSessionID session_id = 0; EventPipeProviderConfiguration provider_config; - EventPipeProviderConfiguration *current_provider_config =ep_provider_config_init (&provider_config, TEST_PROVIDER_NAME, 1, EP_EVENT_LEVEL_LOG_ALWAYS, ""); + EventPipeProviderConfiguration *current_provider_config = ep_provider_config_init (&provider_config, TEST_PROVIDER_NAME, 1, EP_EVENT_LEVEL_LOG_ALWAYS, ""); ep_raise_error_if_nok (current_provider_config != NULL); test_location = 1; @@ -844,8 +848,8 @@ test_session_write_get_next_event (void) ep_start_streaming (session_id); - //Starts as signaled. - //TODO: Is this expected behavior, just a way to notify observer that we are up and running? + // Starts as signaled. + // TODO: Is this expected behavior, just a way to notify observer that we are up and running? uint32_t test = ep_rt_wait_event_wait ((ep_rt_wait_event_handle_t *)ep_session_get_wait_event ((EventPipeSession *)session_id), 0, false); ep_raise_error_if_nok (test == 0); @@ -860,8 +864,8 @@ test_session_write_get_next_event (void) test_location = 6; - //TODO: Is this really the correct behavior, first write signals event, meaning that buffer will converted to read only - //with just one event in it. + // TODO: Is this really the correct behavior, first write signals event, meaning that buffer will converted to read only + // with just one event in it. test = ep_rt_wait_event_wait ((ep_rt_wait_event_handle_t *)ep_session_get_wait_event ((EventPipeSession *)session_id), 0, false); ep_raise_error_if_nok (test == 0); @@ -944,9 +948,9 @@ test_session_write_suspend_event (void) ep_exit_error_handler (); } -//TODO: Add test setting rundown and write events. +// TODO: Add test setting rundown and write events. -//TODO: Suspend write and write events. +// TODO: Suspend write and write events. static RESULT test_write_event (void) @@ -1091,8 +1095,8 @@ test_write_wait_get_next_event (void) ep_start_streaming (session_id); - //Starts as signaled. - //TODO: Is this expected behavior, just a way to notify observer that we are up and running? + // Starts as signaled. + // TODO: Is this expected behavior, just a way to notify observer that we are up and running? uint32_t test = ep_rt_wait_event_wait ((ep_rt_wait_event_handle_t *)ep_get_wait_handle (session_id), 0, false); ep_raise_error_if_nok (test == 0); @@ -1173,10 +1177,10 @@ test_write_event_perf (void) // Write in chunks of 1000 events, all should fit into buffer manager. for (events_written = 0; events_written < 10 * 1000 * 1000; events_written += 1000) { - int64_t start = ep_perf_counter_query (); + int64_t start = ep_perf_timestamp_get (); for (uint32_t i = 0; i < 1000; i++) ep_write_event (ep_event, data, EP_ARRAY_SIZE (data), NULL, NULL); - int64_t stop = ep_perf_counter_query (); + int64_t stop = ep_perf_timestamp_get (); accumulted_write_time_ticks += stop - start; // Drain events to not end up in having buffer manager OOM. @@ -1189,7 +1193,7 @@ test_write_event_perf (void) float events_written_per_sec = (float)events_written / (accumulted_write_time_sec ? accumulted_write_time_sec : 1.0); // Measured number of events/second for one thread. - //TODO: Setup acceptable pass/failure metrics. + // TODO: Setup acceptable pass/failure metrics. printf ("\n\tPerformance stats:\n"); printf ("\t\tTotal number of events: %i\n", events_written); printf ("\t\tTotal time in sec: %.2f\n\t\tTotal number of events written per sec/core: %.2f\n\t", accumulted_write_time_sec, events_written_per_sec); @@ -1206,9 +1210,9 @@ test_write_event_perf (void) ep_exit_error_handler (); } -//TODO: Add multithreaded test writing into private/shared sessions. +// TODO: Add multithreaded test writing into private/shared sessions. -//TODO: Add consumer thread test, flushing file buffers/session, acting on signal. +// TODO: Add consumer thread test, flushing file buffers/session, acting on signal. static RESULT test_eventpipe_teardown (void) diff --git a/src/mono/mono/eventpipe/test/ep-tests.h b/src/mono/mono/eventpipe/test/ep-tests.h index ed2dfa206f07f1..e3511fa7ffa5c8 100644 --- a/src/mono/mono/eventpipe/test/ep-tests.h +++ b/src/mono/mono/eventpipe/test/ep-tests.h @@ -4,6 +4,7 @@ #include "eglib/test/test.h" DEFINE_TEST_GROUP_INIT_H(ep_setup_tests_init); +DEFINE_TEST_GROUP_INIT_H(ep_rt_tests_init); DEFINE_TEST_GROUP_INIT_H(ep_fastserializer_tests_init); DEFINE_TEST_GROUP_INIT_H(ep_provider_callback_data_queue_tests_init); DEFINE_TEST_GROUP_INIT_H(ep_file_tests_init); @@ -18,6 +19,7 @@ DEFINE_TEST_GROUP_INIT_H(ep_teardown_tests_init); const static Group test_groups [] = { {"setup", ep_setup_tests_init}, + {"rt", ep_rt_tests_init}, {"fastserialzier", ep_fastserializer_tests_init}, {"provider-callback-dataqueue", ep_provider_callback_data_queue_tests_init}, {"file", ep_file_tests_init}, diff --git a/src/mono/mono/metadata/Makefile.am b/src/mono/mono/metadata/Makefile.am index aa593365235cfe..85a4f014ff4671 100644 --- a/src/mono/mono/metadata/Makefile.am +++ b/src/mono/mono/metadata/Makefile.am @@ -280,6 +280,7 @@ common_sources = \ domain-internals.h \ environment.c \ environment.h \ + environment-internals.h \ icall-eventpipe.c \ exception.c \ exception.h \ diff --git a/src/mono/mono/metadata/appdomain.c b/src/mono/mono/metadata/appdomain.c index 0c3e89d98641b1..685b1278d66f69 100644 --- a/src/mono/mono/metadata/appdomain.c +++ b/src/mono/mono/metadata/appdomain.c @@ -74,6 +74,11 @@ #include #include #include + +#ifdef ENABLE_PERFTRACING +#include +#endif + #ifdef HOST_WIN32 #include #endif @@ -346,6 +351,11 @@ mono_runtime_init_checked (MonoDomain *domain, MonoThreadStartCB start_cb, MonoT mono_thread_attach (domain); +#if defined(ENABLE_PERFTRACING) && !defined(DISABLE_EVENTPIPE) + ds_server_init (); + ds_server_pause_for_diagnostics_monitor (); +#endif + mono_type_initialization_init (); if (!mono_runtime_get_no_exec ()) diff --git a/src/mono/mono/metadata/environment-internals.h b/src/mono/mono/metadata/environment-internals.h new file mode 100644 index 00000000000000..cfd13afc4d53b6 --- /dev/null +++ b/src/mono/mono/metadata/environment-internals.h @@ -0,0 +1,16 @@ +/** + * \file + * + * Copyright 2020 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef _MONO_METADATA_ENVIRONMENT_INTERNALS_H_ +#define _MONO_METADATA_ENVIRONMENT_INTERNALS_H_ + +void +mono_set_os_args (int argc, char **argv); + +char * +mono_get_os_cmd_line (void); + +#endif /* _MONO_METADATA_ENVIRONMENT_INTERNALS_H_ */ diff --git a/src/mono/mono/metadata/environment.c b/src/mono/mono/metadata/environment.c index f0a5ae8229629a..350249594b127b 100644 --- a/src/mono/mono/metadata/environment.c +++ b/src/mono/mono/metadata/environment.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -45,6 +46,22 @@ mono_environment_exitcode_set (gint32 value) exitcode=value; } +static int mini_argc = 0; +static char **mini_argv = NULL; + +void +mono_set_os_args (int argc, char **argv) +{ + mini_argc = argc; + mini_argv = argv; +} + +char * +mono_get_os_cmd_line (void) +{ + return mono_runtime_get_cmd_line (mini_argc, mini_argv); +} + #ifndef ENABLE_NETCORE /* note: we better manipulate the string in managed code (easier and safer) */ MonoStringHandle diff --git a/src/mono/mono/metadata/icall-decl.h b/src/mono/mono/metadata/icall-decl.h index 858a7b8830a6a0..909621cf44dc60 100644 --- a/src/mono/mono/metadata/icall-decl.h +++ b/src/mono/mono/metadata/icall-decl.h @@ -253,7 +253,7 @@ ICALL_EXPORT MonoBoolean ves_icall_System_Diagnostics_Tracing_EventPipeInternal_ ICALL_EXPORT intptr_t ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetProvider (const_gunichar2_ptr provider_name); ICALL_EXPORT MonoBoolean ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetSessionInfo (uint64_t session_id, void *session_info); ICALL_EXPORT intptr_t ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetWaitHandle (uint64_t session_id); -ICALL_EXPORT void ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WriteEventData (intptr_t event_handle, const void *event_data, uint32_t data_count, const uint8_t *activity_id, const uint8_t *related_activity_id); +ICALL_EXPORT void ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WriteEventData (intptr_t event_handle, void *event_data, uint32_t event_data_len, const uint8_t *activity_id, const uint8_t *related_activity_id); #endif ICALL_EXPORT void ves_icall_Mono_RuntimeGPtrArrayHandle_GPtrArrayFree (GPtrArray *ptr_array); diff --git a/src/mono/mono/metadata/icall-eventpipe.c b/src/mono/mono/metadata/icall-eventpipe.c index ae86f89ac42650..15882204fde4a8 100644 --- a/src/mono/mono/metadata/icall-eventpipe.c +++ b/src/mono/mono/metadata/icall-eventpipe.c @@ -34,12 +34,6 @@ typedef struct _EventPipeProviderConfigurationNative { gunichar2 *filter_data; } EventPipeProviderConfigurationNative; -typedef struct _EventProviderEventData { - uint64_t ptr; - uint32_t size; - uint32_t reserved; -} EventProviderEventData; - typedef struct _EventPipeSessionInfo { int64_t starttime_as_utc_filetime; int64_t start_timestamp; @@ -64,9 +58,6 @@ gpointer ep_rt_mono_rand_provider; static ep_rt_thread_holder_alloc_func thread_holder_alloc_callback_func; static ep_rt_thread_holder_free_func thread_holder_free_callback_func; -void -mono_eventpipe_raise_thread_exited (uint64_t); - static gboolean rand_try_get_bytes_func (guchar *buffer, gssize buffer_size, MonoError *error) @@ -114,6 +105,33 @@ profiler_eventpipe_thread_exited (MonoProfiler *prof, uintptr_t tid) eventpipe_thread_exited (); } +static +gpointer +eventpipe_thread_attach (gboolean background_thread) +{ + MonoThread *thread = NULL; + + // NOTE, under netcore, only root domain exists. + if (!mono_thread_current ()) { + thread = mono_thread_attach (mono_get_root_domain ()); + if (background_thread) { + mono_thread_set_state (thread, ThreadState_Background); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_SAMPLE); + } + } + + return thread; +} + +static +void +eventpipe_thread_detach (void) +{ + MonoThread *current_thread = mono_thread_current (); + if (current_thread) + mono_thread_detach (current_thread); +} + void mono_eventpipe_init ( EventPipeMonoFuncTable *table, @@ -121,8 +139,6 @@ mono_eventpipe_init ( ep_rt_thread_holder_free_func thread_holder_free_func) { if (table != NULL) { - table->ep_rt_mono_100ns_datetime = mono_100ns_datetime; - table->ep_rt_mono_100ns_ticks = mono_100ns_ticks; table->ep_rt_mono_cpu_count = mono_cpu_count; table->ep_rt_mono_process_current_pid = mono_process_current_pid; table->ep_rt_mono_native_thread_id_get = mono_native_thread_id_get; @@ -144,6 +160,11 @@ mono_eventpipe_init ( table->ep_rt_mono_valloc = mono_valloc; table->ep_rt_mono_vfree = mono_vfree; table->ep_rt_mono_valloc_granule = mono_valloc_granule; + table->ep_rt_mono_thread_platform_create_thread = mono_thread_platform_create_thread; + table->ep_rt_mono_thread_attach = eventpipe_thread_attach; + table->ep_rt_mono_thread_detach = eventpipe_thread_detach; + table->ep_rt_mono_get_os_cmd_line = mono_get_os_cmd_line; + table->ep_rt_mono_get_managed_cmd_line = mono_runtime_get_managed_cmd_line; } thread_holder_alloc_callback_func = thread_holder_alloc_func; @@ -447,12 +468,14 @@ ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetWaitHandle (uint64_t s void ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WriteEventData ( intptr_t event_handle, - /* EventProviderEventData[] */const void *event_data, - uint32_t data_len, + /* EventData[] */void *event_data, + uint32_t event_data_len, /* GUID * */const uint8_t *activity_id, /* GUID * */const uint8_t *related_activity_id) { - ; + g_assert (event_handle); + EventPipeEvent *ep_event = (EventPipeEvent *)event_handle; + ep_write_event (ep_event, (EventData *)event_data, event_data_len, activity_id, related_activity_id); } #else /* ENABLE_PERFTRACING */ @@ -567,8 +590,8 @@ ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetWaitHandle (uint64_t s void ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WriteEventData ( intptr_t event_handle, - /* EventProviderEventData[] */const void *event_data, - uint32_t data_len, + /* EventData[] */void *event_data, + uint32_t event_data_len, /* GUID * */const uint8_t *activity_id, /* GUID * */const uint8_t *related_activity_id) { @@ -580,4 +603,4 @@ ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WriteEventData ( #endif /* ENABLE_PERFTRACING */ #endif /* ENABLE_NETCORE */ -MONO_EMPTY_SOURCE_FILE (eventpipe_rt_mono); +MONO_EMPTY_SOURCE_FILE (icall_eventpipe); diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index 5f1f5338070ca1..3a591bde45fce4 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -2421,4 +2421,10 @@ mono_gc_wbarrier_value_copy_internal (void* dest, const void* src, int count, Mo void mono_gc_wbarrier_object_copy_internal (MonoObject* obj, MonoObject *src); +char * +mono_runtime_get_managed_cmd_line (void); + +char * +mono_runtime_get_cmd_line (int argc, char **argv); + #endif /* __MONO_OBJECT_INTERNALS_H__ */ diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index 59481ff5ef17e5..0423ee94cc44e8 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -105,6 +105,11 @@ static GENERATE_GET_CLASS_WITH_CACHE (asyncresult, "System.Runtime.Remoting.Mess #define ldstr_unlock() mono_coop_mutex_unlock (&ldstr_section) static MonoCoopMutex ldstr_section; +static GString * +quote_escape_and_append_string (char *src_str, GString *target_str); + +static GString * +format_cmd_line (int argc, char **argv, gboolean add_host); /** * mono_runtime_object_init: @@ -4536,6 +4541,7 @@ free_main_args (void) for (i = 0; i < num_main_args; ++i) g_free (main_args [i]); g_free (main_args); + num_main_args = 0; main_args = NULL; } @@ -9467,6 +9473,121 @@ mono_vtype_get_field_addr (gpointer vtype, MonoClassField *field) return ((char*)vtype) + field->offset - MONO_ABI_SIZEOF (MonoObject); } +static GString * +quote_escape_and_append_string (char *src_str, GString *target_str) +{ +#ifdef HOST_WIN32 + char quote_char = '\"'; + char escape_chars[] = "\"\\"; +#else + char quote_char = '\''; + char escape_chars[] = "\'\\"; +#endif + + gboolean need_quote = FALSE; + gboolean need_escape = FALSE; + + for (char *pos = src_str; *pos; ++pos) { + if (isspace (*pos)) + need_quote = TRUE; + if (strchr (escape_chars, *pos)) + need_escape = TRUE; + } + + if (need_quote) + target_str = g_string_append_c (target_str, quote_char); + + if (need_escape) { + for (char *pos = src_str; *pos; ++pos) { + if (strchr (escape_chars, *pos)) + target_str = g_string_append_c (target_str, '\\'); + target_str = g_string_append_c (target_str, *pos); + } + } else { + target_str = g_string_append (target_str, src_str); + } + + if (need_quote) + target_str = g_string_append_c (target_str, quote_char); + + return target_str; +} + +static GString * +format_cmd_line (int argc, char **argv, gboolean add_host) +{ + size_t total_size = 0; + char *host_path = NULL; + GString *cmd_line = NULL; + + if (add_host) { +#if !defined(HOST_WIN32) && defined(HAVE_UNISTD_H) + host_path = mono_w32process_get_path (getpid ()); +#elif defined(HOST_WIN32) + gunichar2 *host_path_ucs2 = NULL; + guint32 host_path_ucs2_len = 0; + if (mono_get_module_filename (NULL, &host_path_ucs2, &host_path_ucs2_len)) { + host_path = g_utf16_to_utf8 (host_path_ucs2, -1, NULL, NULL, NULL); + g_free (host_path_ucs2); + } +#endif + } + + if (host_path) + // quote + string + quote + total_size += strlen (host_path) + 2; + + for (int i = 0; i < argc; ++i) { + if (argv [i]) { + if (total_size > 0) { + // add space + total_size++; + } + // quote + string + quote + total_size += strlen (argv [i]) + 2; + } + } + + // String will grow if needed, so not over allocating + // to handle case of escaped characters in arguments, if + // that happens string will automatically grow. + cmd_line = g_string_sized_new (total_size + 1); + + if (cmd_line) { + if (host_path) + cmd_line = quote_escape_and_append_string (host_path, cmd_line); + + for (int i = 0; i < argc; ++i) { + if (argv [i]) { + if (cmd_line->len > 0) { + // add space + cmd_line = g_string_append_c (cmd_line, ' '); + } + cmd_line = quote_escape_and_append_string (argv [i], cmd_line); + } + } + } + + g_free (host_path); + + return cmd_line; +} + +char * +mono_runtime_get_cmd_line (int argc, char **argv) +{ + MONO_REQ_GC_NEUTRAL_MODE; + GString *cmd_line = format_cmd_line (num_main_args, main_args, FALSE); + return cmd_line ? g_string_free (cmd_line, FALSE) : NULL; +} + +char * +mono_runtime_get_managed_cmd_line (void) +{ + MONO_REQ_GC_NEUTRAL_MODE; + GString *cmd_line = format_cmd_line (num_main_args, main_args, TRUE); + return cmd_line ? g_string_free (cmd_line, FALSE) : NULL; +} #if NEVER_DEFINED /* diff --git a/src/mono/mono/mini/driver.c b/src/mono/mono/mini/driver.c index 9f96cc2abfd887..2f3cfcbf51e646 100644 --- a/src/mono/mono/mini/driver.c +++ b/src/mono/mono/mini/driver.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -2644,6 +2645,8 @@ mono_main (int argc, char* argv[]) } mono_set_defaults (mini_verbose_level, opt); + mono_set_os_args (argc, argv); + domain = mini_init (argv [i], forced_version); mono_gc_set_stack_end (&domain); diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 6711c9a1afa30f..c8cc526285f590 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -76,6 +76,7 @@ #ifdef ENABLE_PERFTRACING #include +#include #endif #include "mini.h" @@ -4578,8 +4579,10 @@ mini_init (const char *filename, const char *runtime_version) domain = mono_init_from_assembly (filename, filename); #if defined(ENABLE_PERFTRACING) && !defined(DISABLE_EVENTPIPE) + if (mono_compile_aot) + ds_server_disable (); + ep_init (); - ep_finish_init (); #endif if (mono_aot_only) { @@ -4651,6 +4654,10 @@ mini_init (const char *filename, const char *runtime_version) #endif mono_threads_set_runtime_startup_finished (); +#if defined(ENABLE_PERFTRACING) && !defined(DISABLE_EVENTPIPE) + ep_finish_init (); +#endif + #ifdef ENABLE_EXPERIMENT_TIERED if (!mono_compile_aot) { /* create compilation thread in background */ @@ -5035,6 +5042,7 @@ mini_cleanup (MonoDomain *domain) mini_get_interp_callbacks ()->cleanup (); #if defined(ENABLE_PERFTRACING) && !defined(DISABLE_EVENTPIPE) ep_shutdown (); + ds_server_shutdown (); #endif } #else diff --git a/src/mono/mono/mini/simd-intrinsics-netcore.c b/src/mono/mono/mini/simd-intrinsics-netcore.c index c8594ef4e7008f..5d13fda1242997 100644 --- a/src/mono/mono/mini/simd-intrinsics-netcore.c +++ b/src/mono/mono/mini/simd-intrinsics-netcore.c @@ -455,7 +455,7 @@ emit_sys_numerics_vector_t (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSig one->inst_c0 = 1; break; } - one->dreg = alloc_dreg (cfg, one->type); + one->dreg = alloc_dreg (cfg, (MonoStackType)one->type); MONO_ADD_INS (cfg->cbb, one); return emit_simd_ins (cfg, klass, expand_opcode, one->dreg, -1); } diff --git a/src/mono/msvc/libeventpipe.targets b/src/mono/msvc/libeventpipe.targets index c37a89070ec8f4..fca6c7e08775eb 100644 --- a/src/mono/msvc/libeventpipe.targets +++ b/src/mono/msvc/libeventpipe.targets @@ -5,6 +5,48 @@ true + + $(ExcludeEventPipeFromBuild) + + + + $(ExcludeEventPipeFromBuild) + + + + + $(ExcludeEventPipeFromBuild) + + + + $(ExcludeEventPipeFromBuild) + + + + $(ExcludeEventPipeFromBuild) + + + + $(ExcludeEventPipeFromBuild) + + + + $(ExcludeEventPipeFromBuild) + + + + + + $(ExcludeEventPipeFromBuild) + + + + + + $(ExcludeEventPipeFromBuild) + + + $(ExcludeEventPipeFromBuild) diff --git a/src/mono/msvc/libeventpipe.targets.filters b/src/mono/msvc/libeventpipe.targets.filters index 71e1b382ce17b0..07ff710c166188 100644 --- a/src/mono/msvc/libeventpipe.targets.filters +++ b/src/mono/msvc/libeventpipe.targets.filters @@ -1,6 +1,78 @@ + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe diff --git a/src/mono/msvc/libmonoruntime-common.targets b/src/mono/msvc/libmonoruntime-common.targets index 6006f7870096ed..e02f1f20cf7c4b 100644 --- a/src/mono/msvc/libmonoruntime-common.targets +++ b/src/mono/msvc/libmonoruntime-common.targets @@ -38,6 +38,7 @@ + diff --git a/src/mono/msvc/libmonoruntime-common.targets.filters b/src/mono/msvc/libmonoruntime-common.targets.filters index 26ae534a071123..abfcda22a5709a 100644 --- a/src/mono/msvc/libmonoruntime-common.targets.filters +++ b/src/mono/msvc/libmonoruntime-common.targets.filters @@ -103,6 +103,9 @@ Source Files$(MonoRuntimeFilterSubFolder)\common + + Header Files$(MonoRuntimeFilterSubFolder)\common + Source Files$(MonoRuntimeFilterSubFolder)\common