From f63b6f0a1b3ffd35dc1253226984a8d20bd4d91b Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Mon, 5 Oct 2020 09:23:38 +0200 Subject: [PATCH] PR handling port of Diagnostic Server C++ library from CoreCLR into a C (#41872) library that can be shared between Mono as well as CoreCLR runtime. Port follows same guidelines setup for event pipe port #34600. Diagnostic server library is currently hosted as part of event pipe library but hosting its own runtime shim as well as source files (so could be split into separate library if ever make sense). Diagnostic Server have dependencies on event pipe library (and reuse part of event pipe runtime shim from its how shim). This is the first PR getting the code from diagnostic server codebase over to C library. --- src/mono/mono/eventpipe/Makefile.am | 28 +- src/mono/mono/eventpipe/ds-dump-protocol.c | 32 + src/mono/mono/eventpipe/ds-dump-protocol.h | 27 + src/mono/mono/eventpipe/ds-eventpipe-protocol.c | 528 ++++++++++++ src/mono/mono/eventpipe/ds-eventpipe-protocol.h | 124 +++ src/mono/mono/eventpipe/ds-getter-setter.h | 83 ++ src/mono/mono/eventpipe/ds-ipc-posix.c | 890 +++++++++++++++++++++ src/mono/mono/eventpipe/ds-ipc-posix.h | 63 ++ src/mono/mono/eventpipe/ds-ipc-win32.c | 839 +++++++++++++++++++ src/mono/mono/eventpipe/ds-ipc-win32.h | 70 ++ src/mono/mono/eventpipe/ds-ipc.c | 830 +++++++++++++++++++ src/mono/mono/eventpipe/ds-ipc.h | 321 ++++++++ src/mono/mono/eventpipe/ds-process-protocol.c | 306 +++++++ src/mono/mono/eventpipe/ds-process-protocol.h | 72 ++ src/mono/mono/eventpipe/ds-profiler-protocol.c | 32 + src/mono/mono/eventpipe/ds-profiler-protocol.h | 27 + src/mono/mono/eventpipe/ds-protocol.c | 574 +++++++++++++ src/mono/mono/eventpipe/ds-protocol.h | 204 +++++ src/mono/mono/eventpipe/ds-rt-config.h | 14 + src/mono/mono/eventpipe/ds-rt-mono.c | 278 +++++++ src/mono/mono/eventpipe/ds-rt-mono.h | 138 ++++ src/mono/mono/eventpipe/ds-rt-types-mono.h | 21 + src/mono/mono/eventpipe/ds-rt-types.h | 11 + src/mono/mono/eventpipe/ds-rt.h | 83 ++ src/mono/mono/eventpipe/ds-server.c | 317 ++++++++ src/mono/mono/eventpipe/ds-server.h | 39 + src/mono/mono/eventpipe/ds-types.h | 178 +++++ src/mono/mono/eventpipe/ep-block.c | 156 +++- src/mono/mono/eventpipe/ep-block.h | 6 +- src/mono/mono/eventpipe/ep-buffer-manager.c | 6 +- src/mono/mono/eventpipe/ep-buffer-manager.h | 2 +- src/mono/mono/eventpipe/ep-buffer.c | 2 +- src/mono/mono/eventpipe/ep-buffer.h | 2 +- src/mono/mono/eventpipe/ep-config-internals.h | 2 +- src/mono/mono/eventpipe/ep-config.c | 4 +- src/mono/mono/eventpipe/ep-config.h | 2 +- src/mono/mono/eventpipe/ep-event-instance.c | 2 +- src/mono/mono/eventpipe/ep-event-instance.h | 2 +- src/mono/mono/eventpipe/ep-event-payload.c | 4 +- src/mono/mono/eventpipe/ep-event-payload.h | 2 +- src/mono/mono/eventpipe/ep-event-source.c | 20 +- src/mono/mono/eventpipe/ep-event-source.h | 10 +- src/mono/mono/eventpipe/ep-event.h | 2 +- src/mono/mono/eventpipe/ep-file.c | 76 +- src/mono/mono/eventpipe/ep-file.h | 12 +- src/mono/mono/eventpipe/ep-getter-setter.h | 127 ++- src/mono/mono/eventpipe/ep-metadata-generator.c | 2 +- src/mono/mono/eventpipe/ep-metadata-generator.h | 2 +- src/mono/mono/eventpipe/ep-provider-internals.h | 2 +- src/mono/mono/eventpipe/ep-provider.h | 2 +- src/mono/mono/eventpipe/ep-rt-mono.c | 227 +++++- src/mono/mono/eventpipe/ep-rt-mono.h | 538 ++++++++++--- src/mono/mono/eventpipe/ep-rt-types-mono.h | 5 +- src/mono/mono/eventpipe/ep-rt.h | 198 +++-- src/mono/mono/eventpipe/ep-session.c | 70 +- src/mono/mono/eventpipe/ep-session.h | 10 +- src/mono/mono/eventpipe/ep-stack-contents.h | 2 +- src/mono/mono/eventpipe/ep-stream.c | 102 ++- src/mono/mono/eventpipe/ep-stream.h | 52 +- src/mono/mono/eventpipe/ep-thread.c | 15 +- src/mono/mono/eventpipe/ep-thread.h | 2 +- src/mono/mono/eventpipe/ep-types.h | 50 +- src/mono/mono/eventpipe/ep.c | 53 +- src/mono/mono/eventpipe/ep.h | 40 +- src/mono/mono/eventpipe/test/Makefile.am | 1 + .../mono/eventpipe/test/ep-buffer-manager-tests.c | 14 +- src/mono/mono/eventpipe/test/ep-buffer-tests.c | 6 +- .../mono/eventpipe/test/ep-fastserializer-tests.c | 4 +- src/mono/mono/eventpipe/test/ep-rt-tests.c | 170 ++++ src/mono/mono/eventpipe/test/ep-test.vcxproj | 1 + .../mono/eventpipe/test/ep-test.vcxproj.filters | 3 + src/mono/mono/eventpipe/test/ep-tests.c | 32 +- src/mono/mono/eventpipe/test/ep-tests.h | 2 + src/mono/mono/metadata/Makefile.am | 1 + src/mono/mono/metadata/appdomain.c | 10 + src/mono/mono/metadata/environment-internals.h | 16 + src/mono/mono/metadata/environment.c | 17 + src/mono/mono/metadata/icall-decl.h | 2 +- src/mono/mono/metadata/icall-eventpipe.c | 57 +- src/mono/mono/metadata/object-internals.h | 6 + src/mono/mono/metadata/object.c | 121 +++ src/mono/mono/mini/driver.c | 3 + src/mono/mono/mini/mini-runtime.c | 10 +- src/mono/mono/mini/simd-intrinsics-netcore.c | 2 +- src/mono/msvc/libeventpipe.targets | 42 + src/mono/msvc/libeventpipe.targets.filters | 72 ++ src/mono/msvc/libmonoruntime-common.targets | 1 + .../msvc/libmonoruntime-common.targets.filters | 3 + 88 files changed, 8104 insertions(+), 432 deletions(-) create mode 100644 src/mono/mono/eventpipe/ds-dump-protocol.c create mode 100644 src/mono/mono/eventpipe/ds-dump-protocol.h create mode 100644 src/mono/mono/eventpipe/ds-eventpipe-protocol.c create mode 100644 src/mono/mono/eventpipe/ds-eventpipe-protocol.h create mode 100644 src/mono/mono/eventpipe/ds-getter-setter.h create mode 100644 src/mono/mono/eventpipe/ds-ipc-posix.c create mode 100644 src/mono/mono/eventpipe/ds-ipc-posix.h create mode 100644 src/mono/mono/eventpipe/ds-ipc-win32.c create mode 100644 src/mono/mono/eventpipe/ds-ipc-win32.h create mode 100644 src/mono/mono/eventpipe/ds-ipc.c create mode 100644 src/mono/mono/eventpipe/ds-ipc.h create mode 100644 src/mono/mono/eventpipe/ds-process-protocol.c create mode 100644 src/mono/mono/eventpipe/ds-process-protocol.h create mode 100644 src/mono/mono/eventpipe/ds-profiler-protocol.c create mode 100644 src/mono/mono/eventpipe/ds-profiler-protocol.h create mode 100644 src/mono/mono/eventpipe/ds-protocol.c create mode 100644 src/mono/mono/eventpipe/ds-protocol.h create mode 100644 src/mono/mono/eventpipe/ds-rt-config.h create mode 100644 src/mono/mono/eventpipe/ds-rt-mono.c create mode 100644 src/mono/mono/eventpipe/ds-rt-mono.h create mode 100644 src/mono/mono/eventpipe/ds-rt-types-mono.h create mode 100644 src/mono/mono/eventpipe/ds-rt-types.h create mode 100644 src/mono/mono/eventpipe/ds-rt.h create mode 100644 src/mono/mono/eventpipe/ds-server.c create mode 100644 src/mono/mono/eventpipe/ds-server.h create mode 100644 src/mono/mono/eventpipe/ds-types.h create mode 100644 src/mono/mono/eventpipe/test/ep-rt-tests.c create mode 100644 src/mono/mono/metadata/environment-internals.h diff --git a/src/mono/mono/eventpipe/Makefile.am b/src/mono/mono/eventpipe/Makefile.am index 899cf50..2c28b8c 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 0000000..b71c306 --- /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 0000000..17b86a8 --- /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 0000000..7dc8098 --- /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 0000000..2e48da2 --- /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 0000000..80e11ea --- /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 0000000..979c791 --- /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 0000000..ccc0b60 --- /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 0000000..c1c6db5 --- /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 0000000..35f157c --- /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 0000000..02da2e5 --- /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 0000000..225bf87 --- /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 0000000..1e2dfd6 --- /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 0000000..ef07330 --- /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 0000000..1f63c8a --- /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 0000000..3cd5055 --- /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 0000000..b69a05e --- /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 0000000..eac340c --- /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 0000000..b01333b --- /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 0000000..0e1813d --- /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 0000000..2d9fcbf --- /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 0000000..9b6778f --- /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 0000000..a01f5b4 --- /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 0000000..54ca5b0 --- /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 0000000..6ed3635 --- /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 0000000..d9f6e76 --- /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 0000000..887cad1 --- /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 62d7faa..50f23c6 100644 --- a/src/mono/mono/eventpipe/ep-block.c +++ b/src/mono/mono/eventpipe/ep-block.c @@ -15,6 +15,34 @@ */ 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 ( void *object, @@ -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 29d2212..59d9f3f 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 07c5c5f..720d58a 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 18ea00e..3674cb4 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 ca13ef0..879785d 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 b8bfe30..3aa4f63 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 6720511..42a7231 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 d649317..d912b0f 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 7a7fc89..139866e 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 0f0e9be..1b486aa 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 c872433..d677570 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 cffe7b2..b09e0ee 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 0ad000b..00065f3 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 10b8b47..3d8a2c7 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 350853c..9eb95b2 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 4087416..ac3a90f 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 6ee792b..863096e 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 62940be..01865d4 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 718aab1..b4a5f59 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 03ef9b3..79dca3f 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 8bbf21c..51b1001 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 5d38fb8..83de578 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 645c4f7..f0cfa7e 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 3b8b935..c9e7bf5 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 04808f2..d2d18fd 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,12 +950,35 @@ 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 ep_rt_process_detach (void) { @@ -880,6 +1058,29 @@ ep_rt_object_free (void *ptr) 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 ep_rt_current_process_get_id (void) { @@ -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); } @@ -1145,6 +1384,32 @@ ep_rt_utf8_string_compare ( 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 * ep_rt_utf8_to_utf16_string ( const ep_char8_t *str, @@ -1163,6 +1428,24 @@ ep_rt_utf8_string_dup (const ep_char8_t *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 ep_rt_utf8_string_free (ep_char8_t *str) { @@ -1197,11 +1480,41 @@ ep_rt_utf16_string_free (ep_char16_t *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 fbea800..f0a5a99 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 89b73ff..1d3d980 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,11 +353,19 @@ 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); @@ -356,6 +396,17 @@ ep_rt_object_free (void *ptr); */ 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. @@ -463,10 +526,32 @@ ep_rt_utf8_string_compare ( 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 ( const ep_char8_t *str, @@ -491,15 +576,26 @@ 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); /* * 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 cd7b4ae..074fe69 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 6755a19..d26ca4d 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 af933d8..08c4ae0 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 ae05244..cf5447e 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_on_error: */ 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 f1e9164..687d55f 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 8f2573b..39da171 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 dc2aa27..7948d95 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 1a0c729..2d2b304 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 b29813a..27e2646 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: @@ -1406,22 +1406,6 @@ ep_get_wait_handle (EventPipeSessionID session_id) } /* - * 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 31e3813..4d96935 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 6e0c0e92..04317b0 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 f1bd7c2..ce01061 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 4d1dd3e..f6b8181 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 36153f6..a4abdcb 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 @@ ep_on_error: 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 0000000..5956237 --- /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 ae59c91..7c1544b 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 b3958e6..432204b 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 5ae0b22..8b8541d 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 @@ ep_on_error: 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 @@ ep_on_error: 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 ed2dfa2..e3511fa 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 aa59336..85a4f01 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 0c3e89d..685b127 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 0000000..cfd13af --- /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 f0a5ae8..3502495 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 858a7b8..909621c 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 ae86f89..1588220 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 5f1f533..3a591bd 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 59481ff..0423ee9 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 9f96cc2..2f3cfcb 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 6711c9a..c8cc526 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 c8594ef..5d13fda 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 c37a890..fca6c7e 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 71e1b382..07ff710 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 6006f78..e02f1f2 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 26ae534..abfcda2 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 -- 2.7.4