diff --git a/src/coreclr/inc/configuration.h b/src/coreclr/inc/configuration.h index eac7fb7212445e..530e4665b3f8db 100644 --- a/src/coreclr/inc/configuration.h +++ b/src/coreclr/inc/configuration.h @@ -14,11 +14,21 @@ #ifndef __configuration_h__ #define __configuration_h__ +typedef HRESULT (*config_knob_fn)( + const LPCWSTR& name, + const LPCWSTR& value, + void* context + ); + class Configuration { public: static void InitializeConfigurationKnobs(int numberOfConfigs, LPCWSTR *configNames, LPCWSTR *configValues); + // Invokes callback with given context for each configuration knob. + // Returns S_OK for successful enumeration; otherwise HRESULT from failed callback. + static HRESULT EnumerateKnobs(config_knob_fn callback, void* context); + // Returns (in priority order): // - The value of the ConfigDWORDInfo if it's set // - The value of the ConfigurationKnob (searched by name) if it's set (performs a u16_strtoul). diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h index df255f7186e193..6b0b10ce4b0434 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h @@ -272,6 +272,13 @@ ds_rt_disable_perfmap (void) return DS_IPC_E_NOTSUPPORTED; } +static +uint32_t +ds_rt_appcontext_properties_get (dn_vector_ptr_t *props_array) +{ + return DS_IPC_E_NOTSUPPORTED; +} + /* * DiagnosticServer. */ diff --git a/src/coreclr/utilcode/configuration.cpp b/src/coreclr/utilcode/configuration.cpp index 5949ea880662f1..a10a23ac173f9b 100644 --- a/src/coreclr/utilcode/configuration.cpp +++ b/src/coreclr/utilcode/configuration.cpp @@ -29,6 +29,20 @@ void Configuration::InitializeConfigurationKnobs(int numberOfConfigs, LPCWSTR *n knobValues = values; } +HRESULT Configuration::EnumerateKnobs(config_knob_fn callback, void* context) +{ + HRESULT hr = S_OK; + + for (int i = 0; i < numberOfKnobs; ++i) + { + _ASSERT(knobNames[i] != nullptr); + if (FAILED(hr = callback(knobNames[i], knobValues[i], context))) + return hr; + } + + return S_OK; +} + static LPCWSTR GetConfigurationValue(LPCWSTR name) { _ASSERT(name != nullptr); diff --git a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h index 18d2ce62bdfc69..efc537129599a9 100644 --- a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h @@ -10,6 +10,7 @@ #ifdef ENABLE_PERFTRACING #include "ep-rt-coreclr.h" #include +#include #include #include #include @@ -329,6 +330,45 @@ ds_rt_disable_perfmap (void) #endif // FEATURE_PERFMAP } +static +uint32_t +ds_rt_appcontext_properties_get (dn_vector_ptr_t *props_array) +{ + STATIC_CONTRACT_NOTHROW; + EP_ASSERT (props_array != NULL); + + return Configuration::EnumerateKnobs([](const LPCWSTR& name, const LPCWSTR& value, void* context) { + dn_vector_ptr_t * props_array = static_cast(context); + + const LPCWSTR separator = W("="); + size_t name_len = u16_strlen(name); + size_t name_size = name_len * sizeof (ep_char16_t); // no null terminator + size_t separator_len = u16_strlen(separator); + size_t separator_size = separator_len * sizeof (ep_char16_t); // no null terminator + size_t value_len = u16_strlen(value); + size_t value_size = value_len * sizeof (ep_char16_t); // no null terminator + + // =\0 + size_t str_size = name_size + separator_size + value_size + sizeof (ep_char16_t); // includes null terminator + ep_char16_t *str_entry = reinterpret_cast(malloc (str_size)); + if (!str_entry) + return E_OUTOFMEMORY; + + ep_char16_t * dst = str_entry; + memcpy(dst, reinterpret_cast(name), name_size); + dst += name_len; + memcpy(dst, reinterpret_cast(separator), separator_size); + dst += separator_len; + memcpy(dst, reinterpret_cast(value), value_size); + dst += value_len; + dst[0] = (ep_char16_t)'\0'; + + dn_vector_ptr_push_back (props_array, str_entry); + + return S_OK; + }, props_array); +} + /* * DiagnosticServer. */ diff --git a/src/mono/mono/eventpipe/ds-rt-mono.h b/src/mono/mono/eventpipe/ds-rt-mono.h index 3eea7f1ebe04cd..741ee078aa55c3 100644 --- a/src/mono/mono/eventpipe/ds-rt-mono.h +++ b/src/mono/mono/eventpipe/ds-rt-mono.h @@ -239,6 +239,14 @@ ds_rt_disable_perfmap (void) return DS_IPC_E_NOTSUPPORTED; } +static +uint32_t +ds_rt_appcontext_properties_get (dn_vector_ptr_t *props_array) +{ + // TODO: Implement. + return DS_IPC_E_NOTSUPPORTED; +} + /* * DiagnosticServer. */ diff --git a/src/native/eventpipe/ds-process-protocol.c b/src/native/eventpipe/ds-process-protocol.c index a40403a03f7897..8222e22733c684 100644 --- a/src/native/eventpipe/ds-process-protocol.c +++ b/src/native/eventpipe/ds-process-protocol.c @@ -89,6 +89,12 @@ process_protocol_helper_disable_perfmap ( DiagnosticsIpcMessage *message, DiagnosticsIpcStream *stream); +static +bool +process_protocol_helper_get_appcontext_properties ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + static bool process_protocol_helper_unknown_command ( @@ -475,6 +481,131 @@ ds_env_info_payload_fini (DiagnosticsEnvironmentInfoPayload *payload) payload->env_array = NULL; } +/* + * DiagnosticsAppContextPropertiesPayload. + */ + +static +uint16_t +ds_appcontext_properties_payload_get_size (DiagnosticsAppContextPropertiesPayload *payload) +{ + EP_ASSERT (payload != NULL); + + size_t size = 0; + size += sizeof (payload->incoming_bytes); + size += sizeof (payload->future); + + EP_ASSERT (size <= UINT16_MAX); + return (uint16_t)size; +} + +static +uint32_t +ds_appcontext_properties_get_size (DiagnosticsAppContextPropertiesPayload *payload) +{ + EP_ASSERT (payload != NULL); + + size_t size = 0; + + size += sizeof (uint32_t); + size += (sizeof (uint32_t) * dn_vector_ptr_size (payload->props_array)); + + DN_VECTOR_PTR_FOREACH_BEGIN (ep_char16_t *, prop_value, payload->props_array) { + size += ((ep_rt_utf16_string_len (prop_value) + 1) * sizeof (ep_char16_t)); + } DN_VECTOR_PTR_FOREACH_END; + + EP_ASSERT (size <= UINT32_MAX); + return (uint32_t)size; +} + +static +bool +ds_appcontext_properties_payload_flatten ( + void *payload, + uint8_t **buffer, + uint16_t *size) +{ + DiagnosticsAppContextPropertiesPayload *props_payload = (DiagnosticsAppContextPropertiesPayload*)payload; + + EP_ASSERT (payload != NULL); + EP_ASSERT (buffer != NULL); + EP_ASSERT (*buffer != NULL); + EP_ASSERT (size != NULL); + EP_ASSERT (ds_appcontext_properties_payload_get_size (props_payload) == *size); + + // see IPC spec @ https://github.com/dotnet/diagnostics/blob/main/documentation/design-docs/ipc-protocol.md + // for definition of serialization format + + bool success = true; + + // uint32_t incoming_bytes; + memcpy (*buffer, &props_payload->incoming_bytes, sizeof (props_payload->incoming_bytes)); + *buffer += sizeof (props_payload->incoming_bytes); + *size -= sizeof (props_payload->incoming_bytes); + + // uint16_t future; + memcpy(*buffer, &props_payload->future, sizeof (props_payload->future)); + *buffer += sizeof (props_payload->future); + *size -= sizeof (props_payload->future); + + // Assert we've used the whole buffer we were given + EP_ASSERT(*size == 0); + + return success; +} + +static +bool +ds_appcontext_properties_stream_properties ( + DiagnosticsAppContextPropertiesPayload *payload, + DiagnosticsIpcStream *stream) +{ + DiagnosticsAppContextPropertiesPayload *props_payload = (DiagnosticsAppContextPropertiesPayload*)payload; + + EP_ASSERT (payload != NULL); + EP_ASSERT (stream != NULL); + + // see IPC spec @ https://github.com/dotnet/diagnostics/blob/main/documentation/design-docs/ipc-protocol.md + // for definition of serialization format + + bool success = true; + uint32_t bytes_written = 0; + + // Array> + uint32_t props_len = dn_vector_ptr_size (props_payload->props_array); + props_len = ep_rt_val_uint32_t (props_len); + success &= ds_ipc_stream_write (stream, (const uint8_t *)&props_len, sizeof (props_len), &bytes_written, EP_INFINITE_WAIT); + + DN_VECTOR_PTR_FOREACH_BEGIN (ep_char16_t *, prop_value, props_payload->props_array) { + success &= ds_ipc_message_try_write_string_utf16_t_to_stream (stream, prop_value); + } DN_VECTOR_PTR_FOREACH_END; + + return success; +} + +DiagnosticsAppContextPropertiesPayload * +ds_appcontext_properties_payload_init (DiagnosticsAppContextPropertiesPayload *payload, const dn_vector_ptr_t *props_array) +{ + ep_return_null_if_nok (payload != NULL); + ep_return_null_if_nok (props_array != NULL); + + payload->props_array = props_array; + payload->incoming_bytes = ds_appcontext_properties_get_size (payload); + payload->future = 0; + + return payload; +} + +void +ds_appcontext_properties_payload_fini (DiagnosticsAppContextPropertiesPayload *payload) +{ + DN_VECTOR_PTR_FOREACH_BEGIN (ep_char16_t *, prop_value, payload->props_array) { + ep_rt_utf16_string_free (prop_value); + } DN_VECTOR_PTR_FOREACH_END; + + payload->props_array = NULL; +} + /* * DiagnosticsProcessProtocolHelper. */ @@ -872,6 +1003,58 @@ process_protocol_helper_disable_perfmap ( ep_exit_error_handler (); } +static +bool +process_protocol_helper_get_appcontext_properties ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (stream != NULL); + + bool result = false; + + DiagnosticsAppContextPropertiesPayload payload; + DiagnosticsAppContextPropertiesPayload *props_payload = NULL; + + dn_vector_ptr_t *props_array = dn_vector_ptr_alloc (); + ep_raise_error_if_nok (props_array); + + ds_ipc_result_t ipc_result; + ipc_result = ds_rt_appcontext_properties_get (props_array); + if (ipc_result != DS_IPC_S_OK) { + ds_ipc_message_send_error (stream, ipc_result); + ep_raise_error (); + } else { + props_payload = ds_appcontext_properties_payload_init (&payload, props_array); + ep_raise_error_if_nok (props_payload); + + ep_raise_error_if_nok (ds_ipc_message_initialize_buffer ( + message, + ds_ipc_header_get_generic_success (), + (void *)props_payload, + ds_appcontext_properties_payload_get_size (props_payload), + ds_appcontext_properties_payload_flatten)); + + ep_raise_error_if_nok (ds_ipc_message_send (message, stream)); + ep_raise_error_if_nok (ds_appcontext_properties_stream_properties (props_payload, stream)); + } + + result = true; + +ep_on_exit: + ds_appcontext_properties_payload_fini (props_payload); + dn_vector_ptr_free (props_array); + ds_ipc_stream_free (stream); + return result; + +ep_on_error: + EP_ASSERT (!result); + ds_ipc_message_send_error (stream, DS_IPC_E_FAIL); + DS_LOG_WARNING_0 ("Failed to send DiagnosticsIPC response"); + ep_exit_error_handler (); +} + static bool process_protocol_helper_unknown_command ( @@ -916,6 +1099,9 @@ ds_process_protocol_helper_handle_ipc_message ( case DS_PROCESS_COMMANDID_DISABLE_PERFMAP: result = process_protocol_helper_disable_perfmap (message, stream); break; + case DS_PROCESS_COMMANDID_GET_APPCONTEXT_PROPERTIES: + result = process_protocol_helper_get_appcontext_properties (message, stream); + break; default: result = process_protocol_helper_unknown_command (message, stream); break; diff --git a/src/native/eventpipe/ds-process-protocol.h b/src/native/eventpipe/ds-process-protocol.h index 04430bbb72b85e..f5b6212725d639 100644 --- a/src/native/eventpipe/ds-process-protocol.h +++ b/src/native/eventpipe/ds-process-protocol.h @@ -190,6 +190,35 @@ ds_enable_perfmap_payload_alloc (void); void ds_enable_perfmap_payload_free (DiagnosticsEnablePerfmapPayload *payload); +/* +* DiagnosticsAppContextPropertiesPayload +*/ + +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_PROCESS_PROTOCOL_GETTER_SETTER) +struct _DiagnosticsAppContextPropertiesPayload { +#else +struct _DiagnosticsAppContextPropertiesPayload_Internal { +#endif + // The appcontext properties are sent back as an optional continuation stream of data. + // It is encoded in the typical length-prefixed array format as defined in + // the Diagnostics IPC Spec: https://github.com/dotnet/diagnostics/blob/main/documentation/design-docs/ipc-protocol.md + uint32_t incoming_bytes; + uint16_t future; + const dn_vector_ptr_t *props_array; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_PROCESS_PROTOCOL_GETTER_SETTER) +struct _DiagnosticsAppContextPropertiesPayload { + uint8_t _internal [sizeof (struct _DiagnosticsAppContextPropertiesPayload_Internal)]; +}; +#endif + +DiagnosticsAppContextPropertiesPayload * +ds_appcontext_properties_payload_init (DiagnosticsAppContextPropertiesPayload *payload, const dn_vector_ptr_t *props_array); + +void +ds_appcontext_properties_payload_fini (DiagnosticsAppContextPropertiesPayload *payload); + /* * DiagnosticsProcessProtocolHelper. */ diff --git a/src/native/eventpipe/ds-rt.h b/src/native/eventpipe/ds-rt.h index f4a54b3d52df5d..19e303f2d656d4 100644 --- a/src/native/eventpipe/ds-rt.h +++ b/src/native/eventpipe/ds-rt.h @@ -118,6 +118,10 @@ static uint32_t ds_rt_disable_perfmap (void); +static +uint32_t +ds_rt_appcontext_properties_get (dn_vector_ptr_t *props_array); + /* * DiagnosticServer. */ diff --git a/src/native/eventpipe/ds-types.h b/src/native/eventpipe/ds-types.h index 85363f499e4046..1beb54d864f93b 100644 --- a/src/native/eventpipe/ds-types.h +++ b/src/native/eventpipe/ds-types.h @@ -15,6 +15,7 @@ * Diagnostics Structs. */ +typedef struct _DiagnosticsAppContextPropertiesPayload DiagnosticsAppContextPropertiesPayload; typedef struct _DiagnosticsAttachProfilerCommandPayload DiagnosticsAttachProfilerCommandPayload; typedef struct _DiagnosticsStartupProfilerCommandPayload DiagnosticsStartupProfilerCommandPayload; typedef struct _DiagnosticsConnectPort DiagnosticsConnectPort; @@ -74,7 +75,8 @@ typedef enum { DS_PROCESS_COMMANDID_SET_ENV_VAR = 0x03, DS_PROCESS_COMMANDID_GET_PROCESS_INFO_2 = 0x04, DS_PROCESS_COMMANDID_ENABLE_PERFMAP = 0x05, - DS_PROCESS_COMMANDID_DISABLE_PERFMAP = 0x06 + DS_PROCESS_COMMANDID_DISABLE_PERFMAP = 0x06, + DS_PROCESS_COMMANDID_GET_APPCONTEXT_PROPERTIES = 0x07 // future } DiagnosticsProcessCommandId;