From c5b55f06d6d65c4673734198a87a31f7ace789c2 Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Tue, 26 May 2020 15:28:53 +0200 Subject: [PATCH] Initial work of porting native diagnostic eventpipe library to C. (#34600) First bulk of changes related to porting EventPipe C++ library from CoreCLR into a C library that can be shared between Mono as well as CoreCLR runtime. Due to dependencies and current time line it has been decided to initially target Mono runtime (since it doesn't have event pipe functionality at the moment), but library is still designed to integrate into other runtimes, replacing current C++ library implementation, when time permits. Until that happens, we will need to make sure changes done in C++ code base gets ported over to C library. The amount of changes going into the native library should however be small and if major changes needs to be done, we should first make sure to move over to use the shared version of event pipe C-library in CoreCLR and then only do changes into shared version of the library. Port is currently mapping existing C++ code base, no focus on rewrite or change current implementation (unless needed to port code over to C). Library source files are currently placed into mono/eventpipe folder and build as a separate library, linked into runtime, but will be moved when ready to be shared between runtimes. We might also move all code into a loadable library and p/invokes, depending on size, at least for Mono runtime targeting mobile devices. There is also a unit test runner under mono/eventpipe/tests using eglib test runner, running through a set of unit tests covering the eventpipe library implementation. Library is split up over a couple of source files closely mapping corresponding coreclr source files. Most code could be shared between runtimes and artifacts that are runtime specific, like locks/lists/maps/strings/pal layer etc, a shim API is defined in ep-rt.h and ep-rt-types.h that gets implemented by the targeted runtime consuming the event pipe C library, like ep-rt-mono.h and ep-rt-types-mono.h. All function in the shim API is small and should be inlinable. For example, locking the configuration lock is declared like this in ep-rt.h: bool ep_rt_config_aquire (void); and in ep-rt-mono.h it is implemented like this: static inline bool ep_rt_config_aquire (void) { ep_rt_spin_lock_aquire (ep_rt_mono_config_lock_get ()); return true; } and CoreCLR should implement the function using its corresponding locking primitives. NOTE, since we primarily target Mono in initial port there might smaller adjustments needed to the shim API once bringing it over to CoreCLR, but those changes/adjustments can be done when needed. Since library could be shared, it tries to standardize on common C99 data types when available. If there are runtime specific types needed, they are declared in ep-rt-types.h and implemented by each runtime in corresponding file. Most of library is currently following Mono coding guidelines. For shared types naming is done like this, EventPipe[Type], like EventPipeProvider. This naming follow naming of other types in Mono as well as CoreCLR. There is one exception to this rule, types that are runtime specific implementation, naming is following function naming with a _t prefix, like ep_rt_session_provider_list_t, that is a list specific type, implemented by each runtime. This is done to make it easier to see difference of types implemented in the runtime specific shim and types implemented in the shared library API surface. Since library shim implemented by targeted runtime will be called by C code, it can't throw exceptions, the C library should however compile under C++, meaning that CoreCLR's shim should be able to use C++ constructions as long as it don't throw any exceptions. Library function naming follows the following pattern, for "public" functions included in ep-*.h , ep_ + object + action, so for example, ep_config_create_provider, corresponds to EventPipe::Configuration::CreateProvider in C++ library. For "private/internal" functions not, ep_ prefix is dropped, but object + action is still preserved, for example config_create_provider and function is marked as being static when possible. If a function assumes a lock is held when getting called, it is using _lock_held (similar to SAL annotations) postfix to indicate that function doesn't claim lock and if function needs lock to be held by caller, it will assert that condition on enter (similar to PRECONDITION(EventPipe::IsLockOwnedByCurrentThread())). Library uses asserts internally to validate state and conditions. This is a debug/checked build assert that should be excluded in release builds. It is defined in EP_ASSERT and should be redefined by targeted runtime. Private/internal functions asserts incorrect input parameters and state, while "public" functions return back error to caller when detecting incorrect input parameters. Incorrect state can still be assert in all kinds of functions. Getters/Setters are setup for accessing type members. All functions except a type's own alloc/init/fini/free implemented in ep-*.c are not allowed to use direct type member access and should go through available inlinable getter/setter functions. There is also a way to build the library that catches incorrect use of members at compile time (not in ep-*-internals.c since it include alloc/init/fini/free implementations, but in other source files). Types that can be stack allocated offers a init/fini function pairs. Types that should only be heap allocated, alloc/free, and types supporting both stack/heap, implements alloc/init/fini/free. Since C can't prevent incorrect usage patterns, the implementation needs to follow correct life time management patterns. Types allocated using alloc should be freed with corresponding free function. For example, ep_event_alloc should be freed by ep_event_free, the init/fini methods are then called by the alloc/free implementations. For types supporting stack allocation and allocated on stack or aggregated within other types, init must be called before first use and fini before going out of scope. Ownership of allocated objects is only considered complete on successful function calls. If a function fails, caller still owns resource and need to take actions. Library uses UTF8 strings internally, mainly since Mono runtime already uses that and it is more portable. Number of strings in the library is limited, and in cases where stream protocol uses UTF16 strings, a prealloc string is stored on the type, reducing need to do multiple UTF8 -> UTF16 conversions over the lifetime of the type. NOTE, this is first commit setting up most of the direction of the library and (as part of doing that) ported a great deal of eventpipe code and added ~40 native unittests. It is however not complete, or fully working, so there will be several follow up commit building on top of this work in order to get complete library. --- src/mono/cmake/config.h.in | 3 + src/mono/configure.ac | 10 + src/mono/m4/mono-output.m4 | 2 + src/mono/mono.proj | 1 + src/mono/mono/Makefile.am | 17 +- src/mono/mono/eventpipe/Makefile.am | 71 ++ src/mono/mono/eventpipe/ep-block-internals.c | 595 +++++++++ src/mono/mono/eventpipe/ep-block.c | 398 ++++++ src/mono/mono/eventpipe/ep-block.h | 375 ++++++ .../eventpipe/ep-buffer-manager-internals.c | 38 + src/mono/mono/eventpipe/ep-buffer-manager.c | 28 + src/mono/mono/eventpipe/ep-buffer-manager.h | 69 + src/mono/mono/eventpipe/ep-buffer.h | 40 + src/mono/mono/eventpipe/ep-config-internals.c | 68 + src/mono/mono/eventpipe/ep-config.c | 553 ++++++++ src/mono/mono/eventpipe/ep-config.h | 184 +++ .../eventpipe/ep-event-instance-internals.c | 143 +++ src/mono/mono/eventpipe/ep-event-instance.c | 93 ++ src/mono/mono/eventpipe/ep-event-instance.h | 122 ++ src/mono/mono/eventpipe/ep-event-internals.c | 111 ++ .../eventpipe/ep-event-payload-internals.c | 70 ++ src/mono/mono/eventpipe/ep-event-payload.h | 54 + .../eventpipe/ep-event-source-internals.c | 99 ++ src/mono/mono/eventpipe/ep-event-source.c | 52 + src/mono/mono/eventpipe/ep-event-source.h | 51 + src/mono/mono/eventpipe/ep-event.h | 64 + src/mono/mono/eventpipe/ep-file-internals.c | 295 +++++ src/mono/mono/eventpipe/ep-file.c | 331 +++++ src/mono/mono/eventpipe/ep-file.h | 164 +++ src/mono/mono/eventpipe/ep-internals.c | 242 ++++ .../ep-metadata-generator-internals.c | 40 + .../mono/eventpipe/ep-metadata-generator.c | 106 ++ .../mono/eventpipe/ep-metadata-generator.h | 57 + .../mono/eventpipe/ep-provider-internals.c | 86 ++ src/mono/mono/eventpipe/ep-provider.c | 317 +++++ src/mono/mono/eventpipe/ep-provider.h | 123 ++ src/mono/mono/eventpipe/ep-rt-config-mono.h | 7 + src/mono/mono/eventpipe/ep-rt-config.h | 9 + src/mono/mono/eventpipe/ep-rt-mono.c | 14 + src/mono/mono/eventpipe/ep-rt-mono.h | 1108 +++++++++++++++++ src/mono/mono/eventpipe/ep-rt-types-mono.h | 96 ++ src/mono/mono/eventpipe/ep-rt-types.h | 22 + src/mono/mono/eventpipe/ep-rt.h | 482 +++++++ .../mono/eventpipe/ep-session-internals.c | 123 ++ .../eventpipe/ep-session-provider-internals.c | 135 ++ src/mono/mono/eventpipe/ep-session-provider.c | 60 + src/mono/mono/eventpipe/ep-session-provider.h | 86 ++ src/mono/mono/eventpipe/ep-session.c | 255 ++++ src/mono/mono/eventpipe/ep-session.h | 130 ++ src/mono/mono/eventpipe/ep-stack-contents.h | 172 +++ src/mono/mono/eventpipe/ep-stream-internals.c | 392 ++++++ src/mono/mono/eventpipe/ep-stream.c | 280 +++++ src/mono/mono/eventpipe/ep-stream.h | 364 ++++++ src/mono/mono/eventpipe/ep-thread-internals.c | 219 ++++ src/mono/mono/eventpipe/ep-thread.c | 157 +++ src/mono/mono/eventpipe/ep-thread.h | 233 ++++ src/mono/mono/eventpipe/ep-types.h | 388 ++++++ src/mono/mono/eventpipe/ep.c | 711 +++++++++++ src/mono/mono/eventpipe/ep.h | 272 ++++ .../mono/eventpipe/test/Directory.Build.props | 3 + .../eventpipe/test/Directory.Build.targets | 3 + src/mono/mono/eventpipe/test/Makefile.am | 56 + src/mono/mono/eventpipe/test/ep-fake-tests.c | 1 + .../eventpipe/test/ep-fastserializer-tests.c | 346 +++++ src/mono/mono/eventpipe/test/ep-file-tests.c | 172 +++ .../ep-provider-callback-dataqueue-tests.c | 98 ++ .../mono/eventpipe/test/ep-session-tests.c | 244 ++++ src/mono/mono/eventpipe/test/ep-setup-tests.c | 27 + .../mono/eventpipe/test/ep-teardown-tests.c | 25 + src/mono/mono/eventpipe/test/ep-test-driver.c | 5 + src/mono/mono/eventpipe/test/ep-test-runner.c | 1 + src/mono/mono/eventpipe/test/ep-test.sln | 127 ++ src/mono/mono/eventpipe/test/ep-test.vcxproj | 186 +++ .../eventpipe/test/ep-test.vcxproj.filters | 63 + src/mono/mono/eventpipe/test/ep-tests-debug.h | 8 + src/mono/mono/eventpipe/test/ep-tests.c | 399 ++++++ src/mono/mono/eventpipe/test/ep-tests.h | 30 + .../mono/eventpipe/test/ep-thread-tests.c | 478 +++++++ src/mono/mono/metadata/Makefile.am | 9 +- src/mono/mono/metadata/icall-eventpipe.c | 489 ++++++++ src/mono/mono/metadata/icall.c | 109 -- src/mono/mono/mini/Makefile.am.in | 5 + src/mono/mono/mini/mini-runtime.c | 11 + src/mono/msvc/build-init.vcxproj | 1 + src/mono/msvc/libeventpipe.targets | 149 +++ src/mono/msvc/libeventpipe.targets.filters | 172 +++ src/mono/msvc/libmono-dynamic.vcxproj | 4 - src/mono/msvc/libmonoruntime-common.targets | 1 + .../libmonoruntime-common.targets.filters | 3 + src/mono/msvc/libmonoruntime.targets | 1 + src/mono/msvc/libmonoruntime.targets.filters | 1 + src/mono/msvc/mono.props | 8 +- src/mono/msvc/mono.winconfig.targets | 3 +- 93 files changed, 13904 insertions(+), 121 deletions(-) create mode 100644 src/mono/mono/eventpipe/Makefile.am create mode 100644 src/mono/mono/eventpipe/ep-block-internals.c create mode 100644 src/mono/mono/eventpipe/ep-block.c create mode 100644 src/mono/mono/eventpipe/ep-block.h create mode 100644 src/mono/mono/eventpipe/ep-buffer-manager-internals.c create mode 100644 src/mono/mono/eventpipe/ep-buffer-manager.c create mode 100644 src/mono/mono/eventpipe/ep-buffer-manager.h create mode 100644 src/mono/mono/eventpipe/ep-buffer.h create mode 100644 src/mono/mono/eventpipe/ep-config-internals.c create mode 100644 src/mono/mono/eventpipe/ep-config.c create mode 100644 src/mono/mono/eventpipe/ep-config.h create mode 100644 src/mono/mono/eventpipe/ep-event-instance-internals.c create mode 100644 src/mono/mono/eventpipe/ep-event-instance.c create mode 100644 src/mono/mono/eventpipe/ep-event-instance.h create mode 100644 src/mono/mono/eventpipe/ep-event-internals.c create mode 100644 src/mono/mono/eventpipe/ep-event-payload-internals.c create mode 100644 src/mono/mono/eventpipe/ep-event-payload.h create mode 100644 src/mono/mono/eventpipe/ep-event-source-internals.c create mode 100644 src/mono/mono/eventpipe/ep-event-source.c create mode 100644 src/mono/mono/eventpipe/ep-event-source.h create mode 100644 src/mono/mono/eventpipe/ep-event.h create mode 100644 src/mono/mono/eventpipe/ep-file-internals.c create mode 100644 src/mono/mono/eventpipe/ep-file.c create mode 100644 src/mono/mono/eventpipe/ep-file.h create mode 100644 src/mono/mono/eventpipe/ep-internals.c create mode 100644 src/mono/mono/eventpipe/ep-metadata-generator-internals.c create mode 100644 src/mono/mono/eventpipe/ep-metadata-generator.c create mode 100644 src/mono/mono/eventpipe/ep-metadata-generator.h create mode 100644 src/mono/mono/eventpipe/ep-provider-internals.c create mode 100644 src/mono/mono/eventpipe/ep-provider.c create mode 100644 src/mono/mono/eventpipe/ep-provider.h create mode 100644 src/mono/mono/eventpipe/ep-rt-config-mono.h create mode 100644 src/mono/mono/eventpipe/ep-rt-config.h create mode 100644 src/mono/mono/eventpipe/ep-rt-mono.c create mode 100644 src/mono/mono/eventpipe/ep-rt-mono.h create mode 100644 src/mono/mono/eventpipe/ep-rt-types-mono.h create mode 100644 src/mono/mono/eventpipe/ep-rt-types.h create mode 100644 src/mono/mono/eventpipe/ep-rt.h create mode 100644 src/mono/mono/eventpipe/ep-session-internals.c create mode 100644 src/mono/mono/eventpipe/ep-session-provider-internals.c create mode 100644 src/mono/mono/eventpipe/ep-session-provider.c create mode 100644 src/mono/mono/eventpipe/ep-session-provider.h create mode 100644 src/mono/mono/eventpipe/ep-session.c create mode 100644 src/mono/mono/eventpipe/ep-session.h create mode 100644 src/mono/mono/eventpipe/ep-stack-contents.h create mode 100644 src/mono/mono/eventpipe/ep-stream-internals.c create mode 100644 src/mono/mono/eventpipe/ep-stream.c create mode 100644 src/mono/mono/eventpipe/ep-stream.h create mode 100644 src/mono/mono/eventpipe/ep-thread-internals.c create mode 100644 src/mono/mono/eventpipe/ep-thread.c create mode 100644 src/mono/mono/eventpipe/ep-thread.h create mode 100644 src/mono/mono/eventpipe/ep-types.h create mode 100644 src/mono/mono/eventpipe/ep.c create mode 100644 src/mono/mono/eventpipe/ep.h create mode 100644 src/mono/mono/eventpipe/test/Directory.Build.props create mode 100644 src/mono/mono/eventpipe/test/Directory.Build.targets create mode 100644 src/mono/mono/eventpipe/test/Makefile.am create mode 100644 src/mono/mono/eventpipe/test/ep-fake-tests.c create mode 100644 src/mono/mono/eventpipe/test/ep-fastserializer-tests.c create mode 100644 src/mono/mono/eventpipe/test/ep-file-tests.c create mode 100644 src/mono/mono/eventpipe/test/ep-provider-callback-dataqueue-tests.c create mode 100644 src/mono/mono/eventpipe/test/ep-session-tests.c create mode 100644 src/mono/mono/eventpipe/test/ep-setup-tests.c create mode 100644 src/mono/mono/eventpipe/test/ep-teardown-tests.c create mode 100644 src/mono/mono/eventpipe/test/ep-test-driver.c create mode 100644 src/mono/mono/eventpipe/test/ep-test-runner.c create mode 100644 src/mono/mono/eventpipe/test/ep-test.sln create mode 100644 src/mono/mono/eventpipe/test/ep-test.vcxproj create mode 100644 src/mono/mono/eventpipe/test/ep-test.vcxproj.filters create mode 100644 src/mono/mono/eventpipe/test/ep-tests-debug.h create mode 100644 src/mono/mono/eventpipe/test/ep-tests.c create mode 100644 src/mono/mono/eventpipe/test/ep-tests.h create mode 100644 src/mono/mono/eventpipe/test/ep-thread-tests.c create mode 100644 src/mono/mono/metadata/icall-eventpipe.c create mode 100644 src/mono/msvc/libeventpipe.targets create mode 100644 src/mono/msvc/libeventpipe.targets.filters diff --git a/src/mono/cmake/config.h.in b/src/mono/cmake/config.h.in index b5754a613a877..e89b59c5f7100 100644 --- a/src/mono/cmake/config.h.in +++ b/src/mono/cmake/config.h.in @@ -1647,6 +1647,9 @@ /* Enable private types checked build */ #cmakedefine ENABLE_CHECKED_BUILD_CRASH_REPORTING 1 +/* Enable EventPipe library support */ +#cmakedefine ENABLE_PERFTRACING 1 + /* Define to 1 if you have /usr/include/malloc.h. */ #cmakedefine HAVE_USR_INCLUDE_MALLOC_H 1 diff --git a/src/mono/configure.ac b/src/mono/configure.ac index 98f8e86b254ff..6eb961f9dda00 100644 --- a/src/mono/configure.ac +++ b/src/mono/configure.ac @@ -943,6 +943,16 @@ if test x$with_core = xonly; then fi AM_CONDITIONAL(ENABLE_NETCORE, test x$with_core = xonly) +if test x$with_core = xonly; then + if test -f $srcdir/mono/eventpipe/ep.h; then + enable_perftracing=yes + fi + if test x$enable_perftracing = xyes; then + AC_DEFINE(ENABLE_PERFTRACING,1,[Enables support for eventpipe library]) + fi +fi +AM_CONDITIONAL(ENABLE_PERFTRACING, test x$enable_perftracing = xyes) + # # A sanity check to catch cases where the package was unpacked # with an ancient tar program (Solaris) diff --git a/src/mono/m4/mono-output.m4 b/src/mono/m4/mono-output.m4 index 40d72ea7d6d14..128c16f33633e 100644 --- a/src/mono/m4/mono-output.m4 +++ b/src/mono/m4/mono-output.m4 @@ -12,6 +12,8 @@ AC_DEFUN([AC_MONO_OUTPUT], [ mono/utils/Makefile mono/metadata/Makefile mono/zlib/Makefile + mono/eventpipe/Makefile + mono/eventpipe/test/Makefile mono/arch/Makefile mono/arch/x86/Makefile mono/arch/amd64/Makefile diff --git a/src/mono/mono.proj b/src/mono/mono.proj index 20b28c5303cf6..446b216b77c93 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -728,6 +728,7 @@ <_MonoBuildParams Include="/p:MONO_BUILD_DIR_PREFIX=""$(MonoObjDir)""" /> <_MonoBuildParams Include="/p:MONO_ENABLE_NETCORE=true" /> + <_MonoBuildParams Include="/p:MONO_ENABLE_PERFTRACING=true" /> <_MonoBuildParams Include="/p:MONO_USE_STATIC_C_RUNTIME=true" /> <_MonoBuildParams Include="/p:CL_MPCount=$([System.Environment]::ProcessorCount)" /> <_MonoBuildParams Include="/v:minimal" /> diff --git a/src/mono/mono/Makefile.am b/src/mono/mono/Makefile.am index 226b8cab86854..fa19a4c0018dc 100644 --- a/src/mono/mono/Makefile.am +++ b/src/mono/mono/Makefile.am @@ -13,11 +13,20 @@ native_dirs = native endif if ENABLE_NETCORE -btls_dirs = +btls_dirs = +managed_unit_test_dirs = +native_unit_test_dirs = +else +managed_unit_test_dirs = tests +native_unit_test_dirs = unit-tests +endif + +if ENABLE_PERFTRACING +eventpipe_dirs = eventpipe endif if ENABLE_NETCORE -SUBDIRS = eglib arch utils sgen zlib metadata mini profiler +SUBDIRS = eglib arch utils sgen zlib $(eventpipe_dirs) metadata mini profiler $(native_unit_test_dirs) else if CROSS_COMPILING @@ -26,9 +35,9 @@ else if INSTALL_MONOTOUCH SUBDIRS = $(btls_dirs) eglib arch utils zlib $(sgen_dirs) metadata mini profiler $(native_dirs) else -SUBDIRS = $(btls_dirs) eglib arch utils cil zlib $(sgen_dirs) metadata mini dis tests unit-tests benchmark profiler $(native_dirs) +SUBDIRS = $(btls_dirs) eglib arch utils cil zlib $(sgen_dirs) metadata mini dis $(managed_unit_test_dirs) $(native_unit_test_dirs) benchmark profiler $(native_dirs) endif endif endif -DIST_SUBDIRS = btls native eglib arch utils cil zlib $(sgen_dirs) metadata mini dis tests unit-tests benchmark profiler +DIST_SUBDIRS = btls native eglib arch utils cil zlib $(sgen_dirs) metadata mini dis $(managed_unit_test_dirs) $(native_unit_test_dirs) benchmark profiler diff --git a/src/mono/mono/eventpipe/Makefile.am b/src/mono/mono/eventpipe/Makefile.am new file mode 100644 index 0000000000000..63d2edd8cf9dd --- /dev/null +++ b/src/mono/mono/eventpipe/Makefile.am @@ -0,0 +1,71 @@ +MAKEFLAGS := $(MAKEFLAGS) --no-builtin-rules + +if !ENABLE_MSVC_ONLY +if ENABLE_PERFTRACING + +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/mono $(GLIB_CFLAGS) $(SHARED_CFLAGS) + +noinst_LTLIBRARIES = libeventpipe.la + +eventpipe_sources = \ + ep.c \ + ep.h \ + ep-block.c \ + ep-block.h \ + ep-block-internals.c \ + ep-buffer.h \ + ep-buffer-manager.c \ + ep-buffer-manager.h \ + ep-buffer-manager-internals.c \ + ep-config.c \ + ep-config.h \ + ep-config-internals.c \ + ep-event.h \ + ep-event-instance.h \ + ep-event-instance.c \ + ep-event-instance-internals.c \ + ep-event-internals.c \ + ep-event-payload-internals.c \ + ep-event-payload.h \ + ep-event-source.c \ + ep-event-source.h \ + ep-event-source-internals.c \ + ep-file.c \ + ep-file.h \ + ep-file-internals.c \ + ep-internals.c \ + ep-metadata-generator.c \ + ep-metadata-generator.h \ + ep-metadata-generator-internals.c \ + ep-provider.c \ + ep-provider.h \ + ep-provider-internals.c \ + ep-rt.h \ + ep-rt-config.h \ + ep-rt-config-mono.h \ + ep-rt-mono.c \ + ep-rt-mono.h \ + ep-rt-types.h \ + ep-rt-types-mono.h \ + ep-thread.c \ + ep-thread.h \ + ep-thread-internals.c \ + ep-types.h \ + ep-session.c \ + ep-session.h \ + ep-session-internals.c \ + ep-session-provider.c \ + ep-session-provider-internals.h \ + ep-session-provider-internals.c \ + ep-stream.c \ + ep-stream.h \ + ep-stream-internals.c \ + ep-stack-contents.h + + +libeventpipe_la_SOURCES = $(eventpipe_sources) + +endif # ENABLE_PERFTRACING +endif # !ENABLE_MSVC_ONLY + +CFLAGS := $(filter-out @CXX_REMOVE_CFLAGS@, @CFLAGS@) @CXX_ADD_CFLAGS@ diff --git a/src/mono/mono/eventpipe/ep-block-internals.c b/src/mono/mono/eventpipe/ep-block-internals.c new file mode 100644 index 0000000000000..3d3f647722671 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-block-internals.c @@ -0,0 +1,595 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +void +block_base_fast_serialize_func ( + void *object, + FastSerializer *fast_serializer); + +static +void +block_base_clear_func (void *object); + +static +void +block_base_serialize_header_func ( + void *object, + FastSerializer *fast_serializer); + +static +uint32_t +block_base_get_header_size_func (void *object); + +static +const ep_char8_t * +event_block_get_type_name_func (void *object); + +static +void +event_block_free_func (void *object); + +static +void +metadata_block_free_func (void *object); + +static +const ep_char8_t * +metadata_block_get_type_name_func (void *object); + +static +int32_t +sequence_point_get_block_size (EventPipeSequencePoint *sequence_point); + +static +void +sequence_point_block_free_func (void *object); + +static +const ep_char8_t * +sequence_point_block_get_type_name_func (void *object); + +static +void +stack_block_free_func (void *object); + +static +const ep_char8_t * +stack_block_get_type_name_func (void *object); + +static +void +stack_block_clear_func (void *object); + +static +uint32_t +stack_block_get_header_size_func (void *object); + +static +void +stack_block_serialize_header_func (void *object, FastSerializer *fast_serializer); + +/* + * EventPipeBlock + */ + +EventPipeBlock * +ep_block_init ( + EventPipeBlock *block, + EventPipeBlockVtable *vtable, + uint32_t max_block_size, + EventPipeSerializationFormat format) +{ + EP_ASSERT (block != NULL && vtable != NULL); + + 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), + format >= EP_SERIALIZATION_FORMAT_NETTRACE_V4) != NULL); + + block->block = ep_rt_byte_array_alloc (max_block_size); + ep_raise_error_if_nok (block->block != NULL); + + memset (block->block, 0, max_block_size); + block->write_pointer = block->block; + block->end_of_the_buffer = block->block + max_block_size; + block->format = format; + +ep_on_exit: + return block; + +ep_on_error: + block = NULL; + ep_exit_error_handler (); +} + +void +ep_block_fini (EventPipeBlock *block) +{ + ep_return_void_if_nok (block != NULL); + ep_rt_byte_array_free (block->block); +} + +void +ep_block_clear_vcall (EventPipeBlock *block) +{ + EP_ASSERT (block != NULL && block->fast_serializer_object.vtable != NULL); + EventPipeBlockVtable *vtable = (EventPipeBlockVtable *)block->fast_serializer_object.vtable; + + EP_ASSERT (vtable->clear_func != NULL); + vtable->clear_func (block); +} + +uint32_t +ep_block_get_header_size_vcall (EventPipeBlock *block) +{ + EP_ASSERT (block != NULL && block->fast_serializer_object.vtable != NULL); + EventPipeBlockVtable *vtable = (EventPipeBlockVtable *)block->fast_serializer_object.vtable; + + EP_ASSERT (vtable->get_header_size_func != NULL); + return vtable->get_header_size_func (block); +} + +void +ep_block_serialize_header_vcall ( + EventPipeBlock *block, + FastSerializer *fast_serializer) +{ + EP_ASSERT (block != NULL && block->fast_serializer_object.vtable != NULL); + EventPipeBlockVtable *vtable = (EventPipeBlockVtable *)block->fast_serializer_object.vtable; + + EP_ASSERT (vtable->serialize_header_func != NULL); + vtable->serialize_header_func (block, fast_serializer); +} + +void +ep_block_fast_serialize_vcall ( + EventPipeBlock *block, + FastSerializer *fast_serializer) +{ + EP_ASSERT (block != NULL); + ep_fast_serializable_object_fast_serialize_vcall (&block->fast_serializer_object, fast_serializer); +} + +/* + * EventPipeEventBlockBase + */ + +static +void +block_base_fast_serialize_func ( + void *object, + FastSerializer *fast_serializer) +{ + EP_ASSERT (object != NULL && fast_serializer != NULL); + ep_block_fast_serialize (&((EventPipeEventBlockBase *)object)->block, fast_serializer); +} + +static +void +block_base_clear_func (void *object) +{ + EP_ASSERT (object != NULL); + ep_event_block_base_clear ((EventPipeEventBlockBase *)object); +} + +static +void +block_base_serialize_header_func ( + void *object, + FastSerializer *fast_serializer) +{ + EP_ASSERT (object != NULL && fast_serializer != NULL); + ep_event_block_base_serialize_header ((EventPipeEventBlockBase *)object, fast_serializer); +} + +static +uint32_t +block_base_get_header_size_func (void *object) +{ + EP_ASSERT (object != NULL); + return ep_event_block_base_get_header_size ((EventPipeEventBlockBase *)object); +} + +EventPipeEventBlockBase * +ep_event_block_base_init ( + EventPipeEventBlockBase *event_block_base, + EventPipeBlockVtable *vtable, + uint32_t max_block_size, + EventPipeSerializationFormat format, + bool use_header_compression) +{ + EP_ASSERT (event_block_base != NULL && vtable != NULL); + + ep_raise_error_if_nok (ep_block_init ( + &event_block_base->block, + vtable, + max_block_size, + format) != NULL); + + event_block_base->use_header_compression = use_header_compression; + + memset (event_block_base->compressed_header, 0, EP_ARRAY_SIZE (event_block_base->compressed_header)); + ep_event_block_base_clear (event_block_base); + +ep_on_exit: + return event_block_base; + +ep_on_error: + event_block_base = NULL; + ep_exit_error_handler (); +} + +void +ep_event_block_base_fini (EventPipeEventBlockBase *event_block_base) +{ + ep_return_void_if_nok (event_block_base != NULL); + ep_block_fini (&event_block_base->block); +} + +/* + * EventPipeEventBlock + */ + +static +const ep_char8_t * +event_block_get_type_name_func (void *object) +{ + EP_ASSERT (object != NULL); + return "EventBlock"; +} + +static +void +event_block_free_func (void *object) +{ + ep_event_block_free ((EventPipeEventBlock *)object); +} + +static EventPipeBlockVtable event_block_vtable = { + { + event_block_free_func, + block_base_fast_serialize_func, + event_block_get_type_name_func }, + block_base_clear_func, + block_base_get_header_size_func, + block_base_serialize_header_func }; + +EventPipeEventBlock * +ep_event_block_alloc ( + uint32_t max_block_size, + EventPipeSerializationFormat format) +{ + EventPipeEventBlock *instance = ep_rt_object_alloc (EventPipeEventBlock); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ep_event_block_base_init ( + &instance->event_block_base, + &event_block_vtable, + max_block_size, + format, + format >= EP_SERIALIZATION_FORMAT_NETTRACE_V4) != NULL); + +ep_on_exit: + return instance; + +ep_on_error: + ep_event_block_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_event_block_free (EventPipeEventBlock *event_block) +{ + ep_return_void_if_nok (event_block != NULL); + + ep_event_block_base_fini (&event_block->event_block_base); + ep_rt_object_free (event_block); +} + +/* + * EventPipeMetadataBlock + */ + +static +void +metadata_block_free_func (void *object) +{ + ep_metadata_block_free ((EventPipeMetadataBlock *)object); +} + +static +const ep_char8_t * +metadata_block_get_type_name_func (void *object) +{ + EP_ASSERT (object != NULL); + return "MetadataBlock"; +} + +static EventPipeBlockVtable metadata_block_vtable = { + { + metadata_block_free_func, + block_base_fast_serialize_func, + metadata_block_get_type_name_func }, + block_base_clear_func, + block_base_get_header_size_func, + block_base_serialize_header_func }; + +EventPipeMetadataBlock * +ep_metadata_block_alloc (uint32_t max_block_size) +{ + EventPipeMetadataBlock *instance = ep_rt_object_alloc (EventPipeMetadataBlock); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ep_event_block_base_init ( + &instance->event_block_base, + &metadata_block_vtable, + max_block_size, + EP_SERIALIZATION_FORMAT_NETTRACE_V4, + true) != NULL); + +ep_on_exit: + return instance; + +ep_on_error: + ep_metadata_block_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_metadata_block_free (EventPipeMetadataBlock *metadata_block) +{ + ep_return_void_if_nok (metadata_block != NULL); + + ep_event_block_base_fini (&metadata_block->event_block_base); + ep_rt_object_free (metadata_block); +} + +/* + * EventPipeSequencePointBlock. + */ + +static +int32_t +sequence_point_get_block_size (EventPipeSequencePoint *sequence_point) +{ + EP_ASSERT (sequence_point != NULL); + + const uint32_t size_of_sequence_number = + sizeof (uint64_t) + //thread id + sizeof (uint32_t); //sequence number + + const uint32_t thread_count = ep_rt_thread_sequence_number_map_count (ep_sequence_point_get_thread_sequence_numbers_cref (sequence_point)); + + return sizeof (sequence_point->timestamp) + + sizeof (uint32_t) + //thread count + thread_count * size_of_sequence_number; +} + +static +void +sequence_point_block_free_func (void *object) +{ + ep_sequence_point_block_free ((EventPipeSequencePointBlock *)object); +} + +static +const ep_char8_t * +sequence_point_block_get_type_name_func (void *object) +{ + EP_ASSERT (object != NULL); + return "SPBlock"; +} + +static EventPipeBlockVtable sequence_point_block_vtable = { + { + sequence_point_block_free_func, + block_base_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 }; + +EventPipeSequencePointBlock * +ep_sequence_point_block_alloc (EventPipeSequencePoint *sequence_point) +{ + ep_return_null_if_nok (sequence_point != NULL); + + EventPipeSequencePointBlock *instance = ep_rt_object_alloc (EventPipeSequencePointBlock); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ep_sequence_point_block_init (instance, sequence_point) != NULL); + +ep_on_exit: + return instance; + +ep_on_error: + ep_sequence_point_block_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +EventPipeSequencePointBlock * +ep_sequence_point_block_init ( + EventPipeSequencePointBlock *sequence_point_block, + EventPipeSequencePoint *sequence_point) +{ + EP_ASSERT (sequence_point_block != NULL); + ep_return_null_if_nok (sequence_point != NULL); + + ep_raise_error_if_nok (ep_event_block_base_init ( + &sequence_point_block->event_block_base, + &sequence_point_block_vtable, + sequence_point_get_block_size (sequence_point), + EP_SERIALIZATION_FORMAT_NETTRACE_V4, + true) != NULL); + + const int64_t timestamp = sequence_point->timestamp; + memcpy (sequence_point_block->event_block_base.block.write_pointer, ×tamp, sizeof (timestamp)); + sequence_point_block->event_block_base.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); + + ep_rt_thread_sequence_number_hash_map_iterator_t iterator; + for (ep_rt_thread_sequence_number_map_iterator_begin (&sequence_point->thread_sequence_numbers, &iterator); + !ep_rt_thread_sequence_number_map_iterator_end (&sequence_point->thread_sequence_numbers, &iterator); + ep_rt_thread_sequence_number_map_iterator_next (&sequence_point->thread_sequence_numbers, &iterator)) { + + 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); + + 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); + } + +ep_on_exit: + return sequence_point_block; + +ep_on_error: + sequence_point_block = NULL; + ep_exit_error_handler (); +} + +void +ep_sequence_point_block_fini (EventPipeSequencePointBlock *sequence_point_block) +{ + ep_return_void_if_nok (sequence_point_block != NULL); + ep_event_block_base_fini (&sequence_point_block->event_block_base); +} + +void +ep_sequence_point_block_free (EventPipeSequencePointBlock *sequence_point_block) +{ + ep_return_void_if_nok (sequence_point_block != NULL); + + ep_sequence_point_block_fini (sequence_point_block); + ep_rt_object_free (sequence_point_block); +} + +/* + * EventPipeStackBlock. + */ + +static +void +stack_block_free_func (void *object) +{ + ep_stack_block_free ((EventPipeStackBlock *)object); +} + +static +const ep_char8_t * +stack_block_get_type_name_func (void *object) +{ + EP_ASSERT (object != NULL); + return "StackBlock"; +} + +static +void +stack_block_clear_func (void *object) +{ + EP_ASSERT (object != NULL); + + EventPipeStackBlock *stack_block = (EventPipeStackBlock *)object; + + stack_block->has_initial_index = false; + stack_block->has_initial_index = 0; + stack_block->count = 0; + + ep_block_clear (&stack_block->event_block_base.block); +} + +static +uint32_t +stack_block_get_header_size_func (void *object) +{ + EP_ASSERT (object != NULL); + return sizeof (uint32_t) + // start index + sizeof (uint32_t); // count of indices +} + +static +void +stack_block_serialize_header_func (void *object, FastSerializer *fast_serializer) +{ + EP_ASSERT (object != NULL && fast_serializer != NULL); + + EventPipeStackBlock *stack_block = (EventPipeStackBlock *)object; + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&stack_block->initial_index, sizeof (stack_block->initial_index)); + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&stack_block->count, sizeof (stack_block->count)); +} + +static EventPipeBlockVtable stack_block_vtable = { + { + stack_block_free_func, + block_base_fast_serialize_func, + stack_block_get_type_name_func }, + stack_block_clear_func, + stack_block_get_header_size_func, + stack_block_serialize_header_func }; + +EventPipeStackBlock * +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, + &stack_block_vtable, + max_block_size, + EP_SERIALIZATION_FORMAT_NETTRACE_V4, + true) != NULL); + + stack_block_clear_func (instance); + +ep_on_exit: + return instance; + +ep_on_error: + ep_stack_block_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +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_rt_object_free (stack_block); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_block_internals; +const char quiet_linker_empty_file_warning_eventpipe_block_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-block.c b/src/mono/mono/eventpipe/ep-block.c new file mode 100644 index 0000000000000..214d3817fa645 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-block.c @@ -0,0 +1,398 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +uint8_t * +event_block_base_write_var_uint32 ( + uint8_t * write_pointer, + uint32_t value); + +static +uint8_t * +event_block_base_write_var_uint64 ( + uint8_t * write_pointer, + uint64_t value); + +/* + * EventPipeBlock + */ + +void +ep_block_clear (EventPipeBlock *block) +{ + ep_return_void_if_nok (block != NULL); + ep_return_void_if_nok (ep_block_get_block (block) != NULL); + + EP_ASSERT (ep_block_get_write_pointer (block) <= ep_block_get_end_of_the_buffer (block)); + + memset (ep_block_get_block (block), 0, ep_block_get_end_of_the_buffer (block) - ep_block_get_block (block)); + ep_block_set_write_pointer (block, ep_block_get_block (block)); +} + +uint32_t +ep_block_get_header_size (EventPipeBlock *block) +{ + return 0; +} + +void +ep_block_serialize_header ( + EventPipeBlock *block, + FastSerializer *fast_serializer) +{ + ; +} + +void +ep_block_fast_serialize ( + EventPipeBlock *block, + FastSerializer *fast_serializer) +{ + ep_return_void_if_nok (block != NULL && fast_serializer != NULL && ep_block_get_block (block) != NULL); + + uint32_t data_size = ep_block_get_bytes_written (block); + EP_ASSERT (data_size != 0); + + uint32_t header_size = ep_block_get_header_size_vcall (block); + uint32_t total_size = data_size + header_size; + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&total_size, sizeof (total_size)); + + uint32_t required_padding = ep_fast_serializer_get_required_padding (fast_serializer); + if (required_padding != 0) { + uint8_t max_padding [FAST_SERIALIZER_ALIGNMENT_SIZE - 1] = { 0 }; // it's longest possible padding, we are going to use only part of it + + 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_block_serialize_header_vcall (block, fast_serializer); + ep_fast_serializer_write_buffer (fast_serializer, ep_block_get_block (block), data_size); +} + +/* + * EventPipeEventBlockBase + */ + +static +uint8_t * +event_block_base_write_var_uint32 ( + uint8_t * write_pointer, + uint32_t value) +{ + while (value >= 0x80) { + *write_pointer = (uint8_t)(value | 0x80); + write_pointer++; + value >>= 7; + } + *write_pointer = (uint8_t)value; + write_pointer++; + return write_pointer; +} + +static +uint8_t * +event_block_base_write_var_uint64 ( + uint8_t * write_pointer, + uint64_t value) +{ + while (value >= 0x80) { + *write_pointer = (uint8_t)(value | 0x80); + write_pointer++; + value >>= 7; + } + *write_pointer = (uint8_t)value; + write_pointer++; + return write_pointer; +} + +void +ep_event_block_base_clear (EventPipeEventBlockBase *event_block_base) +{ + ep_return_void_if_nok (event_block_base != NULL); + + ep_block_clear (ep_event_block_base_get_block_ref (event_block_base)); + memset (ep_event_block_base_get_last_header_ref (event_block_base), 0, sizeof (EventPipeEventHeader)); + ep_event_block_base_set_min_timestamp (event_block_base, INT64_MAX); + ep_event_block_base_set_max_timestamp (event_block_base, INT64_MIN); +} + +uint32_t +ep_event_block_base_get_header_size (const EventPipeEventBlockBase *event_block_base) +{ + ep_return_zero_if_nok (event_block_base != NULL && ep_block_get_format ((EventPipeBlock *)event_block_base) != EP_SERIALIZATION_FORMAT_NETPERF_V3); + + return sizeof(uint16_t) + // header size + sizeof(uint16_t) + // flags + sizeof(int64_t) + // min timestamp + sizeof(int64_t); // max timestamp +} + +void +ep_event_block_base_serialize_header ( + EventPipeEventBlockBase *event_block_base, + FastSerializer *fast_serializer) +{ + ep_return_void_if_nok (event_block_base != NULL && ep_block_get_format ((EventPipeBlock *)event_block_base) != EP_SERIALIZATION_FORMAT_NETPERF_V3 && fast_serializer != NULL); + + const uint16_t header_size = (uint16_t)ep_block_get_header_size_vcall ((EventPipeBlock *)event_block_base); + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&header_size, sizeof (header_size)); + + const uint16_t flags = ep_event_block_base_get_use_header_compression (event_block_base) ? 1 : 0; + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&flags, sizeof (flags)); + + uint64_t min_timestamp = ep_event_block_base_get_min_timestamp (event_block_base); + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&min_timestamp, sizeof (min_timestamp)); + + uint64_t max_timestamp = ep_event_block_base_get_max_timestamp (event_block_base); + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&max_timestamp, sizeof (max_timestamp)); +} + +bool +ep_event_block_base_write_event ( + EventPipeEventBlockBase *event_block_base, + EventPipeEventInstance *event_instance, + uint64_t capture_thread_id, + uint32_t sequence_number, + int32_t stack_id, + bool is_sorted_event) +{ + ep_return_false_if_nok (event_block_base != NULL && event_instance != NULL); + + EventPipeBlock *block = ep_event_block_base_get_block_ref (event_block_base); + ep_raise_error_if_nok (ep_block_get_block (block) != NULL); + + uint32_t data_len = 0; + uint8_t * aligned_end = NULL; + uint32_t capture_proc_number = ep_event_instance_get_proc_num (event_instance); + uint8_t * write_pointer = ep_block_get_write_pointer (block); + + if (!ep_event_block_base_get_use_header_compression (event_block_base)) { + uint32_t total_size = ep_event_instance_get_aligned_total_size (event_instance, ep_block_get_format (block)); + ep_raise_error_if_nok (write_pointer + total_size < ep_block_get_end_of_the_buffer (block)); + + aligned_end = write_pointer + total_size + sizeof (total_size); + + memcpy (write_pointer, &total_size, sizeof (total_size)); + write_pointer += sizeof (total_size); + + uint32_t metadata_id = ep_event_instance_get_metadata_id (event_instance); + EP_ASSERT ((metadata_id & (1 << 31)) == 0); + + metadata_id |= (!is_sorted_event ? 1 << 31 : 0); + memcpy (write_pointer, &metadata_id, sizeof (metadata_id)); + write_pointer += sizeof (metadata_id); + + if (ep_block_get_format (block) == EP_SERIALIZATION_FORMAT_NETPERF_V3) { + int32_t thread_id = (int32_t)ep_event_instance_get_thread_id (event_instance); + memcpy (write_pointer, &thread_id, sizeof (thread_id)); + write_pointer += sizeof (thread_id); + } else if (ep_block_get_format (block) == EP_SERIALIZATION_FORMAT_NETTRACE_V4) { + memcpy (write_pointer, &sequence_number, sizeof (sequence_number)); + write_pointer += sizeof (sequence_number); + + uint64_t thread_id = ep_event_instance_get_thread_id (event_instance); + memcpy (write_pointer, &thread_id, sizeof (thread_id)); + write_pointer += sizeof (thread_id); + + memcpy (write_pointer, &capture_thread_id, sizeof (capture_thread_id)); + write_pointer += sizeof (capture_thread_id); + + memcpy (write_pointer, &capture_proc_number, sizeof (capture_proc_number)); + write_pointer += sizeof (capture_proc_number); + + memcpy (write_pointer, &stack_id, sizeof (stack_id)); + write_pointer += sizeof (stack_id); + } + + int64_t timestamp = ep_event_instance_get_timestamp (event_instance); + memcpy (write_pointer, ×tamp, sizeof (timestamp)); + write_pointer += sizeof (timestamp); + + const uint8_t *activity_id = ep_event_instance_get_activity_id_cref (event_instance); + memcpy (write_pointer, activity_id, EP_ACTIVITY_ID_SIZE); + write_pointer += EP_ACTIVITY_ID_SIZE; + + const uint8_t *relative_activity_id = ep_event_instance_get_related_activity_id_cref (event_instance); + memcpy (write_pointer, relative_activity_id, EP_ACTIVITY_ID_SIZE); + write_pointer += EP_ACTIVITY_ID_SIZE; + + data_len = ep_event_instance_get_data_len (event_instance); + memcpy (write_pointer, &data_len, sizeof (data_len)); + write_pointer += sizeof (data_len); + } else { // using header compression + uint8_t flags = 0; + uint8_t *header_write_pointer = ep_event_block_base_get_compressed_header_ref (event_block_base); + EventPipeEventHeader *last_header = ep_event_block_base_get_last_header_ref (event_block_base); + + if (ep_event_instance_get_metadata_id (event_instance) != last_header->metadata_id) { + header_write_pointer = event_block_base_write_var_uint32 (header_write_pointer, ep_event_instance_get_metadata_id (event_instance)); + flags |= 1; + } + + if (is_sorted_event) { + flags |= (1 << 6); + } + + if (last_header->sequence_number + (ep_event_instance_get_metadata_id (event_instance) != 0 ? 1 : 0) != sequence_number || + last_header->capture_thread_id != capture_thread_id || last_header->capture_proc_number != capture_proc_number) { + header_write_pointer = event_block_base_write_var_uint32 (header_write_pointer, sequence_number - last_header->sequence_number - 1); + header_write_pointer = event_block_base_write_var_uint32 (header_write_pointer, (uint32_t)capture_thread_id); + header_write_pointer = event_block_base_write_var_uint32 (header_write_pointer, capture_proc_number); + flags |= (1 << 1); + } + + if (last_header->thread_id != ep_event_instance_get_thread_id (event_instance)) { + header_write_pointer = event_block_base_write_var_uint64 (header_write_pointer, ep_event_instance_get_thread_id (event_instance)); + flags |= (1 << 2); + } + + if (last_header->stack_id != stack_id) { + header_write_pointer = event_block_base_write_var_uint32 (header_write_pointer, stack_id); + flags |= (1 << 3); + } + + int64_t timestamp = ep_event_instance_get_timestamp (event_instance); + header_write_pointer = event_block_base_write_var_uint64 (header_write_pointer, timestamp - last_header->timestamp); + + if (memcmp (&last_header->activity_id, ep_event_instance_get_activity_id_cref (event_instance), EP_ACTIVITY_ID_SIZE) != 0) { + memcpy (header_write_pointer, ep_event_instance_get_activity_id_cref (event_instance), EP_ACTIVITY_ID_SIZE ); + header_write_pointer += EP_ACTIVITY_ID_SIZE; + flags |= (1 << 4); + } + + if (memcmp (&last_header->related_activity_id, ep_event_instance_get_related_activity_id_cref (event_instance), EP_ACTIVITY_ID_SIZE) != 0) { + memcpy (header_write_pointer, ep_event_instance_get_related_activity_id_cref (event_instance), EP_ACTIVITY_ID_SIZE ); + header_write_pointer += EP_ACTIVITY_ID_SIZE; + flags |= (1 << 5); + } + + data_len = ep_event_instance_get_data_len (event_instance); + if (last_header->data_len != data_len) { + header_write_pointer = event_block_base_write_var_uint32 (header_write_pointer, data_len); + flags |= (1 << 7); + } + + uint32_t bytes_written = (uint32_t)(header_write_pointer - ep_event_block_base_get_compressed_header_cref (event_block_base)); + uint32_t total_size = 1 + bytes_written + data_len; + + if (write_pointer + total_size >= ep_block_get_end_of_the_buffer (block)) { + //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. + ep_block_set_write_pointer (block, write_pointer); + ep_raise_error (); + } + + last_header->metadata_id = ep_event_instance_get_metadata_id (event_instance); + last_header->sequence_number = sequence_number; + last_header->thread_id = ep_event_instance_get_thread_id (event_instance); + last_header->capture_thread_id = capture_thread_id; + last_header->capture_proc_number = capture_proc_number; + last_header->stack_id = stack_id; + last_header->timestamp = timestamp; + memcpy (&last_header->activity_id, ep_event_instance_get_activity_id_cref (event_instance), EP_ACTIVITY_ID_SIZE); + memcpy (&last_header->related_activity_id, ep_event_instance_get_related_activity_id_cref (event_instance), EP_ACTIVITY_ID_SIZE); + last_header->data_len = data_len; + + aligned_end = write_pointer + total_size; + *write_pointer = flags; + write_pointer++; + memcpy (write_pointer, ep_event_block_base_get_compressed_header_cref (event_block_base), bytes_written); + write_pointer += bytes_written; + } + + if (data_len > 0) { + memcpy (write_pointer, ep_event_instance_get_data (event_instance), data_len); + write_pointer += data_len; + } + + if (ep_block_get_format (block) == EP_SERIALIZATION_FORMAT_NETPERF_V3) { + uint32_t stack_size = ep_stack_contents_get_size (ep_event_instance_get_stack_contents_ref (event_instance)); + memcpy (write_pointer, &stack_size, sizeof (stack_size)); + write_pointer += sizeof (stack_size); + + if (stack_size > 0) { + memcpy (write_pointer, ep_event_instance_get_stack_contents_ref (event_instance), stack_size); + write_pointer += stack_size; + } + } + + while (write_pointer < aligned_end) + *write_pointer++ = (uint8_t)0; // put padding at the end to get 4 bytes alignment of the payload + + EP_ASSERT (write_pointer == aligned_end); + + int64_t instance_timestamp = ep_event_instance_get_timestamp (event_instance); + if (ep_event_block_base_get_min_timestamp (event_block_base) > instance_timestamp) + ep_event_block_base_set_min_timestamp (event_block_base, instance_timestamp); + if (ep_event_block_base_get_max_timestamp (event_block_base) > instance_timestamp) + ep_event_block_base_set_max_timestamp (event_block_base, instance_timestamp); + + ep_block_set_write_pointer (block, write_pointer); + return true; + +ep_on_error: + return false; +} + +/* + * EventPipeStackBlock. + */ + +bool +ep_stack_block_write_stack ( + EventPipeStackBlock *stack_block, + int32_t stack_id, + EventPipeStackContents *stack) +{ + ep_return_false_if_nok (stack_block != NULL); + + EventPipeBlock *block = ep_event_block_base_get_block_ref (ep_stack_block_get_event_block_base_ref (stack_block)); + ep_raise_error_if_nok (block != NULL && ep_block_get_block (block) != NULL); + + uint32_t stack_size = ep_stack_contents_get_size (stack); + uint32_t total_size = sizeof (stack_size) + stack_size; + uint8_t *write_pointer = ep_block_get_write_pointer (block); + + ep_raise_error_if_nok (write_pointer + total_size < ep_block_get_end_of_the_buffer (block)); + + if (!ep_stack_block_get_has_initial_index (stack_block)) { + ep_stack_block_set_has_initial_index (stack_block, true); + ep_stack_block_set_initial_index (stack_block, stack_id); + } + + ep_stack_block_set_count (stack_block, ep_stack_block_get_count (stack_block) + 1); + + memcpy (write_pointer, &stack_size, sizeof (stack_size)); + write_pointer += sizeof (stack_size); + + if (stack_size > 0) { + memcpy (write_pointer, ep_stack_contents_get_pointer (stack), stack_size); + write_pointer += stack_size; + } + + ep_block_set_write_pointer (block, write_pointer); + return true; + +ep_on_error: + return false; +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_block; +const char quiet_linker_empty_file_warning_eventpipe_block = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-block.h b/src/mono/mono/eventpipe/ep-block.h new file mode 100644 index 0000000000000..f114cfd4eabba --- /dev/null +++ b/src/mono/mono/eventpipe/ep-block.h @@ -0,0 +1,375 @@ +#ifndef __EVENTPIPE_BLOCK_H__ +#define __EVENTPIPE_BLOCK_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeBlock + */ + +typedef void (*EventPipeBlockClearFunc)(void *object); +typedef uint32_t (*EventPipeBlockGetHeaderSizeFunc)(void *object); +typedef void (*EventPipeBlockSerializeHeaderFunc)(void *object, FastSerializer *fast_serializer); + +struct _EventPipeBlockVtable { + FastSerializableObjectVtable fast_serializable_object_vtable; + EventPipeBlockClearFunc clear_func; + EventPipeBlockGetHeaderSizeFunc get_header_size_func; + EventPipeBlockSerializeHeaderFunc serialize_header_func; +}; + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeBlock { +#else +struct _EventPipeBlock_Internal { +#endif + FastSerializableObject fast_serializer_object; + uint8_t *block; + uint8_t *write_pointer; + uint8_t *end_of_the_buffer; + EventPipeSerializationFormat format; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeBlock { + uint8_t _internal [sizeof (struct _EventPipeBlock_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeBlock *, block, uint8_t*, block) +EP_DEFINE_GETTER(EventPipeBlock *, block, uint8_t*, write_pointer) +EP_DEFINE_SETTER(EventPipeBlock *, block, uint8_t*, write_pointer) +EP_DEFINE_GETTER(EventPipeBlock *, block, uint8_t*, end_of_the_buffer) +EP_DEFINE_GETTER(EventPipeBlock *, block, EventPipeSerializationFormat, format) + +static +inline +uint32_t +ep_block_get_bytes_written (const EventPipeBlock *block) +{ + return block == NULL ? 0 : (uint32_t)(ep_block_get_write_pointer (block) - ep_block_get_block (block)); +} + +EventPipeBlock * +ep_block_init ( + EventPipeBlock *block, + EventPipeBlockVtable *vtable, + uint32_t max_block_size, + EventPipeSerializationFormat format); + +void +ep_block_fini (EventPipeBlock *block); + +void +ep_block_clear (EventPipeBlock *block); + +uint32_t +ep_block_get_header_size (EventPipeBlock *block); + +void +ep_block_serialize_header ( + EventPipeBlock *block, + FastSerializer *fast_serializer); + +void +ep_block_fast_serialize ( + EventPipeBlock *block, + FastSerializer *fast_serializer); + +void +ep_block_clear_vcall (EventPipeBlock *block); + +uint32_t +ep_block_get_header_size_vcall (EventPipeBlock *block); + +void +ep_block_serialize_header_vcall ( + EventPipeBlock *block, + FastSerializer *fast_serializer); + +void +ep_block_fast_serialize_vcall ( + EventPipeBlock *block, + FastSerializer *fast_serializer); + +/* + * EventPipeEventHeader. + */ + +struct _EventPipeEventHeader { + int32_t metadata_id; + int32_t sequence_number; + uint64_t thread_id; + uint64_t capture_thread_id; + int32_t capture_proc_number; + int32_t stack_id; + uint64_t timestamp; + uint8_t activity_id [EP_ACTIVITY_ID_SIZE]; + uint8_t related_activity_id [EP_ACTIVITY_ID_SIZE]; + int32_t data_len; +}; + +/* + * EventPipeEventBlockBase + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEventBlockBase { +#else +struct _EventPipeEventBlockBase_Internal { +#endif + EventPipeBlock block; + EventPipeEventHeader last_header; + uint8_t compressed_header [100]; + bool use_header_compression; + uint64_t min_timestamp; + uint64_t max_timestamp; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEventBlockBase { + uint8_t _internal [sizeof (struct _EventPipeEventBlockBase_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_REF(EventPipeEventBlockBase *, event_block_base, EventPipeBlock *, block) +EP_DEFINE_GETTER_REF(EventPipeEventBlockBase *, event_block_base, EventPipeEventHeader *, last_header) +EP_DEFINE_GETTER(EventPipeEventBlockBase *, event_block_base, bool, use_header_compression) +EP_DEFINE_GETTER_ARRAY_REF(EventPipeEventBlockBase *, event_block_base, uint8_t *, const uint8_t *, compressed_header, compressed_header[0]) +EP_DEFINE_GETTER(EventPipeEventBlockBase *, event_block_base, uint64_t, min_timestamp) +EP_DEFINE_SETTER(EventPipeEventBlockBase *, event_block_base, uint64_t, min_timestamp) +EP_DEFINE_GETTER(EventPipeEventBlockBase *, event_block_base, uint64_t, max_timestamp) +EP_DEFINE_SETTER(EventPipeEventBlockBase *, event_block_base, uint64_t, max_timestamp) + +EventPipeEventBlockBase * +ep_event_block_base_init ( + EventPipeEventBlockBase *event_block_base, + EventPipeBlockVtable *vtable, + uint32_t max_block_size, + EventPipeSerializationFormat format, + bool use_header_compression); + +void +ep_event_block_base_fini (EventPipeEventBlockBase *event_block_base); + +void +ep_event_block_base_clear (EventPipeEventBlockBase *event_block_base); + +uint32_t +ep_event_block_base_get_header_size (const EventPipeEventBlockBase *event_block_base); + +void +ep_event_block_base_serialize_header ( + EventPipeEventBlockBase *event_block_base, + FastSerializer *fast_serializer); + +bool +ep_event_block_base_write_event ( + EventPipeEventBlockBase *event_block_base, + EventPipeEventInstance *event_instance, + uint64_t capture_thread_id, + uint32_t sequence_number, + int32_t stack_id, + bool is_sorted_event); + +/* + * EventPipeEventBlock. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEventBlock { +#else +struct _EventPipeEventBlock_Internal { +#endif + EventPipeEventBlockBase event_block_base; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEventBlock { + uint8_t _internal [sizeof (struct _EventPipeEventBlock_Internal)]; +}; +#endif + +EventPipeEventBlock * +ep_event_block_alloc ( + uint32_t max_block_size, + EventPipeSerializationFormat format); + +void +ep_event_block_free (EventPipeEventBlock *event_block); + +static +inline +uint32_t +ep_event_block_get_bytes_written (EventPipeEventBlock *event_block) +{ + return ep_block_get_bytes_written ((const EventPipeBlock *)event_block); +} + +static +inline +void +ep_event_block_serialize (EventPipeEventBlock *event_block, FastSerializer *fast_serializer) +{ + ep_fast_serializer_write_object (fast_serializer, (FastSerializableObject*)event_block); +} + +static +inline +void +ep_event_block_clear (EventPipeEventBlock *event_block) +{ + ep_block_clear_vcall ((EventPipeBlock *)event_block); +} + +/* + * EventPipeMetadataBlock. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeMetadataBlock { +#else +struct _EventPipeMetadataBlock_Internal { +#endif + EventPipeEventBlockBase event_block_base; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeMetadataBlock { + uint8_t _internal [sizeof (struct _EventPipeMetadataBlock_Internal)]; +}; +#endif + +EventPipeMetadataBlock * +ep_metadata_block_alloc (uint32_t max_block_size); + +void +ep_metadata_block_free (EventPipeMetadataBlock *metadata_block); + +static +inline +uint32_t +ep_metadata_block_get_bytes_written (EventPipeMetadataBlock *metadata_block) +{ + return ep_block_get_bytes_written ((const EventPipeBlock *)metadata_block); +} + +static +inline +void +ep_metadata_block_serialize (EventPipeMetadataBlock *metadata_block, FastSerializer *fast_serializer) +{ + ep_fast_serializer_write_object (fast_serializer, (FastSerializableObject *)metadata_block); +} + +static +inline +void +ep_metadata_block_clear (EventPipeMetadataBlock *metadata_block) +{ + ep_block_clear_vcall ((EventPipeBlock *)metadata_block); +} + +/* + * EventPipeSequencePointBlock. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeSequencePointBlock { +#else +struct _EventPipeSequencePointBlock_Internal { +#endif + EventPipeEventBlockBase event_block_base; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeSequencePointBlock { + uint8_t _internal [sizeof (struct _EventPipeSequencePointBlock_Internal)]; +}; +#endif + +EventPipeSequencePointBlock * +ep_sequence_point_block_alloc (EventPipeSequencePoint *sequence_point); + +EventPipeSequencePointBlock * +ep_sequence_point_block_init ( + EventPipeSequencePointBlock *sequence_point_block, + EventPipeSequencePoint *sequence_point); + +void +ep_sequence_point_block_fini (EventPipeSequencePointBlock *sequence_point_block); + +void +ep_sequence_point_block_free (EventPipeSequencePointBlock *sequence_point_block); + +/* + * EventPipeStackBlock. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeStackBlock { +#else +struct _EventPipeStackBlock_Internal { +#endif + EventPipeEventBlockBase event_block_base; + uint32_t initial_index; + uint32_t count; + bool has_initial_index; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeStackBlock { + uint8_t _internal [sizeof (struct _EventPipeStackBlock_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_REF(EventPipeStackBlock *, stack_block, EventPipeEventBlockBase *, event_block_base) +EP_DEFINE_GETTER(EventPipeStackBlock *, stack_block, uint32_t, initial_index) +EP_DEFINE_SETTER(EventPipeStackBlock *, stack_block, uint32_t, initial_index) +EP_DEFINE_GETTER(EventPipeStackBlock *, stack_block, uint32_t, count) +EP_DEFINE_SETTER(EventPipeStackBlock *, stack_block, uint32_t, count) +EP_DEFINE_GETTER(EventPipeStackBlock *, stack_block, bool, has_initial_index) +EP_DEFINE_SETTER(EventPipeStackBlock *, stack_block, bool, has_initial_index) + +EventPipeStackBlock * +ep_stack_block_alloc (uint32_t max_block_size); + +void +ep_stack_block_free (EventPipeStackBlock *stack_block); + +bool +ep_stack_block_write_stack ( + EventPipeStackBlock *stack_block, + int32_t stack_id, + EventPipeStackContents *stack); + +static +inline +uint32_t +ep_stack_block_get_bytes_written (EventPipeStackBlock *stack_block) +{ + return ep_block_get_bytes_written ((const EventPipeBlock *)stack_block); +} + +static +inline +void +ep_stack_block_serialize (EventPipeStackBlock *stack_block, FastSerializer *fast_serializer) +{ + ep_fast_serializer_write_object (fast_serializer, (FastSerializableObject *)stack_block); +} + +static +inline +void +ep_stack_block_clear (EventPipeStackBlock *stack_block) +{ + ep_block_clear_vcall ((EventPipeBlock *)stack_block); +} + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_BLOCK_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-buffer-manager-internals.c b/src/mono/mono/eventpipe/ep-buffer-manager-internals.c new file mode 100644 index 0000000000000..aabec9ad60379 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-buffer-manager-internals.c @@ -0,0 +1,38 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * EventPipeBufferManager. + */ + +EventPipeBufferManager * +ep_buffer_manager_alloc ( + EventPipeSession *session, + size_t max_size_of_all_buffers, + size_t sequence_point_allocation_budget) +{ + //TODO: Implement. + return ep_rt_object_alloc (EventPipeBufferManager); +} + +void +ep_buffer_manager_free (EventPipeBufferManager * buffer_manager) +{ + //TODO: Implement. + ep_return_void_if_nok (buffer_manager != NULL); + ep_rt_object_free (buffer_manager); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_buffer_manager_internals; +const char quiet_linker_empty_file_warning_eventpipe_buffer_manager_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-buffer-manager.c b/src/mono/mono/eventpipe/ep-buffer-manager.c new file mode 100644 index 0000000000000..3e151790d6afb --- /dev/null +++ b/src/mono/mono/eventpipe/ep-buffer-manager.c @@ -0,0 +1,28 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * EventPipeBufferManager. + */ + +#ifdef EP_CHECKED_BUILD +void +ep_buffer_manager_requires_lock_held (const EventPipeBufferManager *buffer_manager) +{ + EP_ASSERT (buffer_manager != NULL); + ep_rt_spin_lock_requires_lock_held (ep_buffer_manager_get_rt_lock_cref (buffer_manager)); +} +#endif + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_buffer_manager; +const char quiet_linker_empty_file_warning_eventpipe_buffer_manager = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-buffer-manager.h b/src/mono/mono/eventpipe/ep-buffer-manager.h new file mode 100644 index 0000000000000..2676114076bf4 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-buffer-manager.h @@ -0,0 +1,69 @@ +#ifndef __EVENTPIPE_BUFFERMANAGER_H__ +#define __EVENTPIPE_BUFFERMANAGER_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeBufferList. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +//TODO: Implement. +struct _EventPipeBufferList { +#else +struct _EventPipeBufferList_Internal { +#endif + uint8_t x; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeBufferList { + uint8_t _internal [sizeof (struct _EventPipeBufferList_Internal)]; +}; +#endif + +/* + * EventPipeBufferManager. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +//TODO: Implement. +struct _EventPipeBufferManager { +#else +struct _EventPipeBufferManager_Internal { +#endif + ep_rt_wait_event_handle_t rt_wait_event; + ep_rt_spin_lock_handle_t rt_lock; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeBufferManager { + uint8_t _internal [sizeof (struct _EventPipeBufferManager_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_REF(EventPipeBufferManager *, buffer_manager, ep_rt_wait_event_handle_t *, rt_wait_event) +EP_DEFINE_GETTER_REF(EventPipeBufferManager *, buffer_manager, ep_rt_spin_lock_handle_t *, rt_lock); + +EventPipeBufferManager * +ep_buffer_manager_alloc ( + EventPipeSession *session, + size_t max_size_of_all_buffers, + size_t sequence_point_allocation_budget); + +void +ep_buffer_manager_free (EventPipeBufferManager *buffer_manager); + +#ifdef EP_CHECKED_BUILD +void +ep_buffer_manager_requires_lock_held (const EventPipeBufferManager *buffer_manager); +#else +#define ep_buffer_manager_requires_lock_held(x) +#endif + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_BUFFERMANAGER_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-buffer.h b/src/mono/mono/eventpipe/ep-buffer.h new file mode 100644 index 0000000000000..cfc5f66c3e193 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-buffer.h @@ -0,0 +1,40 @@ +#ifndef __EVENTPIPE_BUFFER_H__ +#define __EVENTPIPE_BUFFER_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeBuffer. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +//TODO: Implement. +struct _EventPipeBuffer { +#else +struct _EventPipeBuffer_Internal { +#endif + volatile uint32_t state; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeBuffer { + uint8_t _internal [sizeof (struct _EventPipeBuffer_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_REF(EventPipeBuffer *, buffer, volatile uint32_t *, state) + +static +inline +void +ep_buffer_convert_to_read_only (EventPipeBuffer *buffer) +{ + //TODO: Implement. +} + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_BUFFER_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-config-internals.c b/src/mono/mono/eventpipe/ep-config-internals.c new file mode 100644 index 0000000000000..a26d76a7c0ac9 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-config-internals.c @@ -0,0 +1,68 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +EventPipeConfiguration _ep_config = { { 0 }, 0 }; + +/* + * EventPipeEventMetadataEvent. + */ + +EventPipeEventMetadataEvent * +ep_event_metdata_event_alloc ( + EventPipeEvent *ep_event, + uint32_t proc_num, + uint64_t thread_id, + uint8_t *data, + uint32_t data_len, + const uint8_t *activity_id, + const uint8_t *related_activity_id) +{ + EventPipeEventMetadataEvent *instance = ep_rt_object_alloc (EventPipeEventMetadataEvent); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ep_event_instance_init ( + &instance->event_instance, + ep_event, + proc_num, + thread_id, + data, + data_len, + activity_id, + related_activity_id) != NULL); + + instance->payload_buffer = data; + instance->payload_buffer_len = data_len; + +ep_on_exit: + return instance; + +ep_on_error: + ep_event_metdata_event_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_event_metdata_event_free (EventPipeEventMetadataEvent *metadata_event) +{ + ep_return_void_if_nok (metadata_event != NULL); + + ep_event_instance_fini (&metadata_event->event_instance); + ep_rt_byte_array_free (metadata_event->payload_buffer); + ep_rt_object_free (metadata_event); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_configuration_internals; +const char quiet_linker_empty_file_warning_eventpipe_configuration_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-config.c b/src/mono/mono/eventpipe/ep-config.c new file mode 100644 index 0000000000000..24b6461adfed0 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-config.c @@ -0,0 +1,553 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +void +config_compute_keyword_and_level_lock_held ( + const EventPipeConfiguration *config, + const EventPipeProvider *provider, + int64_t *keyword_for_all_sessions, + EventPipeEventLevel *level_for_all_sessions); + +static +bool +config_register_provider_lock_held ( + EventPipeConfiguration *config, + EventPipeProvider *provider, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + +static +bool +config_unregister_provider_lock_held ( + EventPipeConfiguration *config, + EventPipeProvider *provider); + +/* + * EventPipeConfiguration. + */ + +static +void +config_compute_keyword_and_level_lock_held ( + const EventPipeConfiguration *config, + const EventPipeProvider *provider, + int64_t *keyword_for_all_sessions, + EventPipeEventLevel *level_for_all_sessions) +{ + ep_rt_config_requires_lock_held (); + EP_ASSERT (provider != NULL); + EP_ASSERT (keyword_for_all_sessions != NULL); + EP_ASSERT (level_for_all_sessions != NULL); + + *keyword_for_all_sessions = 0; + *level_for_all_sessions = EP_EVENT_LEVEL_LOG_ALWAYS; + + for (int i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; i++) { + // Entering EventPipe lock gave us a barrier, we don't need more of them. + EventPipeSession *session = ep_volatile_load_session_without_barrier (i); + if (session) { + EventPipeSessionProviderList *providers = ep_session_get_providers (session); + EP_ASSERT (providers != NULL); + + EventPipeSessionProvider *session_provider = ep_rt_session_provider_list_find_by_name (ep_session_provider_list_get_providers_cref (providers), ep_provider_get_provider_name (provider)); + if (session_provider) { + *keyword_for_all_sessions = *keyword_for_all_sessions | ep_session_provider_get_keywords (session_provider); + *level_for_all_sessions = (ep_session_provider_get_logging_level (session_provider) > *level_for_all_sessions) ? ep_session_provider_get_logging_level (session_provider) : *level_for_all_sessions; + } + } + } + + ep_rt_config_requires_lock_held (); + return; +} + +static +bool +config_register_provider_lock_held ( + EventPipeConfiguration *config, + EventPipeProvider *provider, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +{ + ep_rt_config_requires_lock_held (); + EP_ASSERT (config != NULL); + EP_ASSERT (provider != NULL); + + // See if we've already registered this provider. + EventPipeProvider *existing_provider = ep_config_get_provider_lock_held (config, ep_provider_get_provider_name (provider)); + if (existing_provider) + return false; + + // The provider has not been registered, so register it. + ep_rt_provider_list_append (ep_config_get_provider_list_ref (config), provider); + + int64_t keyword_for_all_sessions; + EventPipeEventLevel level_for_all_sessions; + config_compute_keyword_and_level_lock_held (config, provider, &keyword_for_all_sessions, &level_for_all_sessions); + + for (int i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; i++) { + // Entering EventPipe lock gave us a barrier, we don't need more of them. + EventPipeSession *session = ep_volatile_load_session_without_barrier (i); + if (session) { + EventPipeSessionProviderList *providers = ep_session_get_providers (session); + EP_ASSERT (providers != NULL); + + EventPipeSessionProvider *session_provider = ep_rt_session_provider_list_find_by_name (ep_session_provider_list_get_providers_cref (providers), ep_provider_get_provider_name (provider)); + if (session_provider) { + EventPipeProviderCallbackData provider_callback_data; + ep_provider_set_config_lock_held ( + provider, + keyword_for_all_sessions, + level_for_all_sessions, + ((uint64_t)1 << ep_session_get_index (session)), + ep_session_provider_get_keywords (session_provider), + ep_session_provider_get_logging_level (session_provider), + ep_session_provider_get_filter_data (session_provider), + &provider_callback_data); + ep_provider_callback_data_queue_enqueue (provider_callback_data_queue, &provider_callback_data); + } + } + } + + ep_rt_config_requires_lock_held (); + return true; +} + +static +bool +config_unregister_provider_lock_held ( + EventPipeConfiguration *config, + EventPipeProvider *provider) +{ + ep_rt_config_requires_lock_held (); + EP_ASSERT (config != NULL); + + EventPipeProvider *existing_provider = NULL; + ep_rt_provider_list_t *provider_list = ep_config_get_provider_list_ref (config); + + // The provider list should be non-NULL, but can be NULL on shutdown. + if (!ep_rt_provider_list_is_empty (provider_list)) { + // If we found the provider, remove it. + if (ep_rt_provider_list_find (provider_list, provider, &existing_provider)) + ep_rt_provider_list_remove (provider_list, existing_provider); + } + + ep_rt_config_requires_lock_held (); + return (existing_provider != NULL); +} + +EventPipeConfiguration * +ep_config_init (EventPipeConfiguration *config) +{ + ep_rt_config_requires_lock_not_held (); + + ep_return_false_if_nok (config != NULL); + + ep_config_set_config_provider (config, ep_create_provider (ep_config_get_default_provider_name_utf8 (), NULL, NULL)); + ep_raise_error_if_nok (ep_config_get_config_provider (config) != NULL); + + // Create the metadata event. + ep_config_set_metadata_event (config, ep_provider_add_event ( + ep_config_get_config_provider (config), + 0, /* event_id */ + 0, /* keywords */ + 0, /* event_version */ + EP_EVENT_LEVEL_LOG_ALWAYS, + false, /* need_stack */ + NULL, /* meatadata */ + 0)); /* metadata_len */ + ep_raise_error_if_nok (ep_config_get_metadata_event (config) != NULL); + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return config; + +ep_on_error: + ep_config_shutdown (config); + + config = NULL; + ep_exit_error_handler (); +} + +void +ep_config_shutdown (EventPipeConfiguration *config) +{ + ep_rt_config_requires_lock_not_held (); + + ep_event_free (ep_config_get_metadata_event (config)); + ep_config_set_metadata_event (config, NULL); + + ep_delete_provider (ep_config_get_config_provider (config)); + ep_config_set_config_provider (config, NULL); + + // Take the lock before manipulating the list. + EP_CONFIG_LOCK_ENTER + // We don't delete provider itself because it can be in-use + ep_rt_provider_list_free (ep_config_get_provider_list_ref (config), NULL); + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +EventPipeProvider * +ep_config_create_provider ( + EventPipeConfiguration *config, + const ep_char8_t *provider_name, + EventPipeCallback callback_func, + void *callback_data, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +{ + ep_rt_config_requires_lock_not_held (); + + ep_return_null_if_nok (config != NULL && provider_name != NULL); + + EventPipeProvider *provider = NULL; + EP_CONFIG_LOCK_ENTER + provider = ep_config_create_provider_lock_held (config, provider_name, callback_func, callback_data, provider_callback_data_queue); + ep_raise_error_if_nok_holding_lock (provider != NULL); + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return provider; + +ep_on_error: + ep_config_delete_provider (config, provider); + + provider = NULL; + ep_exit_error_handler (); +} + +void +ep_config_delete_provider ( + EventPipeConfiguration *config, + EventPipeProvider *provider) +{ + ep_rt_config_requires_lock_not_held (); + + ep_return_void_if_nok (config != NULL && provider != NULL); + + EP_CONFIG_LOCK_ENTER + ep_config_delete_provider_lock_held (config, provider); + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +void +ep_config_enable ( + EventPipeConfiguration *config, + const EventPipeSession *session, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +{ + ep_rt_config_requires_lock_not_held (); + + ep_return_void_if_nok (config != NULL && session != NULL); + + EP_CONFIG_LOCK_ENTER + ep_config_enable_disable_lock_held (config, session, provider_callback_data_queue, true); + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +void +ep_config_disable ( + EventPipeConfiguration *config, + const EventPipeSession *session, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +{ + ep_rt_config_requires_lock_not_held (); + + ep_return_void_if_nok (config != NULL && session != NULL); + + EP_CONFIG_LOCK_ENTER + ep_config_enable_disable_lock_held (config, session, provider_callback_data_queue, false); + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +EventPipeEventMetadataEvent * +ep_config_build_event_metadata_event ( + EventPipeConfiguration *config, + const EventPipeEventInstance *source_instance, + uint32_t metadata_id) +{ + ep_return_null_if_nok (config != NULL && source_instance != NULL); + + // The payload of the event should contain: + // - Metadata ID + // - GUID ProviderID. + // - Optional event description payload. + + uint8_t *instance_payload = NULL; + + // Calculate the size of the 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); + 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; + + // Allocate the payload. + instance_payload = ep_rt_byte_array_alloc (instance_payload_size); + ep_raise_error_if_nok (instance_payload != NULL); + + // Fill the buffer with the payload. + uint8_t *current = instance_payload; + + memcpy(current, &metadata_id, sizeof(metadata_id)); + current += sizeof(metadata_id); + + memcpy(current, provider_name_utf16, provider_name_len); + current += provider_name_len; + + // Write the incoming payload data. + memcpy(current, payload_data, payload_data_len); + + // Construct the metadata event instance. + EventPipeEventMetadataEvent *instance = ep_event_metdata_event_alloc ( + ep_config_get_metadata_event (config), + ep_rt_current_processor_get_number (), + ep_rt_current_thread_get_id (), + instance_payload, + instance_payload_size, + NULL /* pActivityId */, + NULL /* pRelatedActivityId */); + + ep_raise_error_if_nok (instance != NULL); + instance_payload = NULL; + + EP_ASSERT (ep_event_get_need_stack (ep_config_get_metadata_event (config)) == false); + + // Set the timestamp to match the source event, because the metadata event + // will be emitted right before the source event. + ep_event_instance_set_timestamp ((EventPipeEventInstance *)instance, ep_event_instance_get_timestamp (source_instance)); + +ep_on_exit: + return instance; + +ep_on_error: + ep_rt_byte_array_free (instance_payload); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_config_delete_deferred_providers (EventPipeConfiguration *config) +{ + ep_rt_config_requires_lock_not_held (); + + ep_return_void_if_nok (config != NULL); + + EP_CONFIG_LOCK_ENTER + ep_config_delete_deferred_providers_lock_held (config); + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +EventPipeSessionProvider * +ep_config_get_session_provider_lock_held ( + const EventPipeConfiguration *config, + const EventPipeSession *session, + const EventPipeProvider *provider) +{ + ep_rt_config_requires_lock_held (); + + ep_return_null_if_nok (config != NULL && session != NULL); + + EventPipeSessionProvider *existing_provider = ep_session_get_session_provider_lock_held (session, provider); + + ep_rt_config_requires_lock_held (); + return existing_provider; +} + +EventPipeProvider * +ep_config_get_provider_lock_held ( + EventPipeConfiguration *config, + const ep_char8_t *name) +{ + ep_rt_config_requires_lock_held (); + + ep_return_null_if_nok (config != NULL && name != NULL); + + // The provider list should be non-NULL, but can be NULL on shutdown. + ep_return_null_if_nok (ep_rt_provider_list_is_empty (ep_config_get_provider_list_cref (config)) != true); + EventPipeProvider *provider = ep_rt_provider_list_find_by_name (ep_config_get_provider_list_cref (config), name); + + ep_rt_config_requires_lock_held (); + return provider; +} + +EventPipeProvider * +ep_config_create_provider_lock_held ( + EventPipeConfiguration *config, + const ep_char8_t *provider_name, + EventPipeCallback callback_func, + void *callback_data, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +{ + ep_rt_config_requires_lock_held (); + + ep_return_null_if_nok (config != NULL && provider_name != NULL); + + EventPipeProvider *provider = ep_provider_alloc (config, provider_name, callback_func, callback_data); + ep_raise_error_if_nok (provider != NULL); + ep_raise_error_if_nok (config_register_provider_lock_held (config, provider, provider_callback_data_queue) == true); + +ep_on_exit: + ep_rt_config_requires_lock_held (); + return provider; + +ep_on_error: + ep_config_delete_provider_lock_held (config, provider); + + provider = NULL; + ep_exit_error_handler (); +} + +void +ep_config_delete_provider_lock_held ( + EventPipeConfiguration *config, + EventPipeProvider *provider) +{ + ep_rt_config_requires_lock_held (); + + ep_return_void_if_nok (config != NULL); + + config_unregister_provider_lock_held (config, provider); + ep_provider_free (provider); + + ep_rt_config_requires_lock_held (); + return; +} + +void +ep_config_delete_deferred_providers_lock_held (EventPipeConfiguration *config) +{ + ep_rt_config_requires_lock_held (); + + ep_return_void_if_nok (config != NULL); + + // The provider list should be non-NULL, but can be NULL on shutdown. + const ep_rt_provider_list_t *provider_list = ep_config_get_provider_list_ref (config); + if (!ep_rt_provider_list_is_empty (provider_list)) { + ep_rt_provider_list_iterator_t iterator; + ep_rt_provider_list_iterator_begin (provider_list, &iterator); + + while (!ep_rt_provider_list_iterator_end (provider_list, &iterator)) { + EventPipeProvider *provider = ep_rt_provider_list_iterator_value (&iterator); + EP_ASSERT (provider != NULL); + + // Get next item before deleting current. + ep_rt_provider_list_iterator_next (provider_list, &iterator); + if (ep_provider_get_delete_deferred (provider)) + ep_config_delete_provider_lock_held (config, provider); + } + } + + ep_rt_config_requires_lock_held (); + return; +} + +void +ep_config_enable_disable_lock_held ( + EventPipeConfiguration *config, + const EventPipeSession *session, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue, + bool enable) +{ + ep_rt_config_requires_lock_held (); + + ep_return_void_if_nok (config != NULL && session != NULL); + + // The provider list should be non-NULL, but can be NULL on shutdown. + const ep_rt_provider_list_t *provider_list = ep_config_get_provider_list_cref (config); + if (!ep_rt_provider_list_is_empty (provider_list)) { + ep_rt_provider_list_iterator_t iterator; + for (ep_rt_provider_list_iterator_begin (provider_list, &iterator); !ep_rt_provider_list_iterator_end (provider_list, &iterator); ep_rt_provider_list_iterator_next (provider_list, &iterator)) { + EventPipeProvider *provider = ep_rt_provider_list_iterator_value (&iterator); + if (provider) { + // Enable/Disable the provider if it has been configured. + EventPipeSessionProvider *session_provider = ep_config_get_session_provider_lock_held (config, session, provider); + if (session_provider) { + int64_t keyword_for_all_sessions; + EventPipeEventLevel level_for_all_sessions; + EventPipeProviderCallbackData provider_callback_data; + config_compute_keyword_and_level_lock_held (config, provider, &keyword_for_all_sessions, &level_for_all_sessions); + if (enable) { + ep_provider_set_config_lock_held ( + provider, + keyword_for_all_sessions, + level_for_all_sessions, + ep_session_get_mask (session), + ep_session_provider_get_keywords (session_provider), + ep_session_provider_get_logging_level (session_provider), + ep_session_provider_get_filter_data (session_provider), + &provider_callback_data); + } else { + ep_provider_unset_config_lock_held ( + provider, + keyword_for_all_sessions, + level_for_all_sessions, + ep_session_get_mask (session), + ep_session_provider_get_keywords (session_provider), + ep_session_provider_get_logging_level (session_provider), + ep_session_provider_get_filter_data (session_provider), + &provider_callback_data); + } + ep_provider_callback_data_queue_enqueue (provider_callback_data_queue, &provider_callback_data); + } + } + } + } + + ep_rt_config_requires_lock_held (); + return; +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_configuration; +const char quiet_linker_empty_file_warning_eventpipe_configuration = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-config.h b/src/mono/mono/eventpipe/ep-config.h new file mode 100644 index 0000000000000..577b78f8643bc --- /dev/null +++ b/src/mono/mono/eventpipe/ep-config.h @@ -0,0 +1,184 @@ +#ifndef __EVENTPIPE_CONFIGURATION_H__ +#define __EVENTPIPE_CONFIGURATION_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeConfiguration. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeConfiguration { +#else +struct _EventPipeConfiguration_Internal { +#endif + ep_rt_provider_list_t provider_list; + EventPipeProvider *config_provider; + EventPipeEvent *metadata_event; + ep_char8_t *config_provider_name; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeConfiguration { + uint8_t _internal [sizeof (struct _EventPipeConfiguration_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_REF(EventPipeConfiguration *, config, ep_rt_provider_list_t *, provider_list) +EP_DEFINE_GETTER(EventPipeConfiguration *, config, EventPipeProvider *, config_provider) +EP_DEFINE_SETTER(EventPipeConfiguration *, config, EventPipeProvider *, config_provider) +EP_DEFINE_GETTER(EventPipeConfiguration *, config, EventPipeEvent *, metadata_event) +EP_DEFINE_SETTER(EventPipeConfiguration *, config, EventPipeEvent *, metadata_event) +EP_DEFINE_GETTER(EventPipeConfiguration *, config, const ep_char8_t *, config_provider_name) + +static +inline +const ep_char8_t * +ep_config_get_default_provider_name_utf8 (void) +{ + return "Microsoft-DotNETCore-EventPipeConfiguration"; +} + +static +inline +const ep_char8_t * +ep_config_get_public_provider_name_utf8 (void) +{ + return "Microsoft-Windows-DotNETRuntime"; +} + +static +inline +const ep_char8_t * +ep_config_get_rundown_provider_name_utf8 (void) +{ + return "Microsoft-Windows-DotNETRuntimeRundown"; +} + +static +inline +EventPipeConfiguration * +ep_config_get (void) +{ + // Singelton. + extern EventPipeConfiguration _ep_config; + return &_ep_config; +} + +EventPipeConfiguration * +ep_config_init (EventPipeConfiguration *config); + +void +ep_config_shutdown (EventPipeConfiguration *config); + +EventPipeProvider * +ep_config_create_provider ( + EventPipeConfiguration *config, + const ep_char8_t *provider_name, + EventPipeCallback callback_func, + void *callback_data, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + +void +ep_config_delete_provider ( + EventPipeConfiguration *config, + EventPipeProvider *provider); + +void +ep_config_enable ( + EventPipeConfiguration *config, + const EventPipeSession *session, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + +void +ep_config_disable ( + EventPipeConfiguration *config, + const EventPipeSession *session, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + +EventPipeEventMetadataEvent * +ep_config_build_event_metadata_event ( + EventPipeConfiguration *config, + const EventPipeEventInstance *source_instance, + uint32_t metadata_id); + +void +ep_config_delete_deferred_providers (EventPipeConfiguration *config); + +EventPipeSessionProvider * +ep_config_get_session_provider_lock_held ( + const EventPipeConfiguration *config, + const EventPipeSession *session, + const EventPipeProvider *provider); + +EventPipeProvider * +ep_config_get_provider_lock_held ( + EventPipeConfiguration *config, + const ep_char8_t *name); + +EventPipeProvider * +ep_config_create_provider_lock_held ( + EventPipeConfiguration *config, + const ep_char8_t *provider_name, + EventPipeCallback callback_func, + void *callback_data, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + +void +ep_config_delete_provider_lock_held ( + EventPipeConfiguration *config, + EventPipeProvider *provider); + +void +ep_config_delete_deferred_providers_lock_held (EventPipeConfiguration *config); + +void +ep_config_enable_disable_lock_held ( + EventPipeConfiguration *config, + const EventPipeSession *session, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue, + bool enable); + +/* + * EventPipeEventMetadataEvent. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEventMetadataEvent { +#else +struct _EventPipeEventMetadataEvent_Internal { +#endif + EventPipeEventInstance event_instance; + uint8_t *payload_buffer; + uint32_t payload_buffer_len; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEventMetadataEvent { + uint8_t _internal [sizeof (struct _EventPipeEventMetadataEvent_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeEventMetadataEvent *, event_metadata_event, uint8_t *, payload_buffer) +EP_DEFINE_GETTER(EventPipeEventMetadataEvent *, event_instance, uint32_t, payload_buffer_len) + + +EventPipeEventMetadataEvent * +ep_event_metdata_event_alloc ( + EventPipeEvent *ep_event, + uint32_t proc_num, + uint64_t thread_id, + uint8_t *data, + uint32_t data_len, + const uint8_t *activity_id, + const uint8_t *related_activity_id); + +void +ep_event_metdata_event_free (EventPipeEventMetadataEvent *metadata_event); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_CONFIGURATION_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-event-instance-internals.c b/src/mono/mono/eventpipe/ep-event-instance-internals.c new file mode 100644 index 0000000000000..123675616d8f9 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-event-instance-internals.c @@ -0,0 +1,143 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * EventPipeEventInstance. + */ + +EventPipeEventInstance * +ep_event_instance_alloc ( + EventPipeEvent *ep_event, + uint32_t proc_num, + uint64_t thread_id, + const uint8_t *data, + uint32_t data_len, + const uint8_t *activity_id, + const uint8_t *related_activity_id) +{ + EventPipeEventInstance *instance = ep_rt_object_alloc (EventPipeEventInstance); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ep_event_instance_init ( + instance, + ep_event, + proc_num, + thread_id, + data, + data_len, + activity_id, + related_activity_id) != NULL); + +ep_on_exit: + return instance; + +ep_on_error: + ep_event_instance_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +EventPipeEventInstance * +ep_event_instance_init ( + EventPipeEventInstance *event_instance, + EventPipeEvent *ep_event, + uint32_t proc_num, + uint64_t thread_id, + const uint8_t *data, + uint32_t data_len, + const uint8_t *activity_id, + const uint8_t *related_activity_id) +{ + EP_ASSERT (event_instance != NULL); + ep_return_null_if_nok (ep_event != NULL); + +#ifdef EP_CHECKED_BUILD + event_instance->debug_event_start = 0xDEADBEEF; + event_instance->debug_event_end = 0xCAFEBABE; +#endif + + event_instance->ep_event = ep_event; + event_instance->proc_num = proc_num; + event_instance->thread_id = thread_id; + + if (activity_id) + memcpy (&(event_instance->activity_id), activity_id, EP_ACTIVITY_ID_SIZE); + + if (related_activity_id) + memcpy (&(event_instance->related_activity_id), related_activity_id, EP_ACTIVITY_ID_SIZE); + + event_instance->data = data; + event_instance->data_len = data_len; + + event_instance->timestamp = ep_perf_counter_query (); + EP_ASSERT (event_instance->timestamp > 0); + + ep_event_instance_ensure_consistency (event_instance); + + return event_instance; +} + +void +ep_event_instance_fini (EventPipeEventInstance *ep_event_instance) +{ + ; +} + +void +ep_event_instance_free (EventPipeEventInstance *ep_event_instance) +{ + ep_return_void_if_nok (ep_event_instance != NULL); + + ep_event_instance_fini (ep_event_instance); + ep_rt_object_free (ep_event_instance); +} + +/* + * EventPipeSequencePoint. + */ + +EventPipeSequencePoint * +ep_sequence_point_init (EventPipeSequencePoint *sequence_point) +{ + EP_ASSERT (sequence_point != NULL); + + sequence_point->timestamp = 0; + sequence_point->thread_sequence_numbers.table = NULL; + sequence_point->thread_sequence_numbers.count = 0; + + return sequence_point; +} + +void +ep_sequence_point_fini (EventPipeSequencePoint *sequence_point) +{ + ep_return_void_if_nok (sequence_point != NULL); + + // Each entry in the map owns a ref-count on the corresponding thread + if (sequence_point->thread_sequence_numbers.table) { + ep_rt_thread_sequence_number_hash_map_iterator_t iterator; + for ( + ep_rt_thread_sequence_number_map_iterator_begin (&sequence_point->thread_sequence_numbers, &iterator); + !ep_rt_thread_sequence_number_map_iterator_end (&sequence_point->thread_sequence_numbers, &iterator); + ep_rt_thread_sequence_number_map_iterator_next (&sequence_point->thread_sequence_numbers, &iterator)) { + + EventPipeThreadSessionState *key = ep_rt_thread_sequence_number_map_iterator_key (&iterator); + ep_thread_release (ep_thread_session_state_get_thread (key)); + } + } +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_event_instance_internals; +const char quiet_linker_empty_file_warning_eventpipe_event_instance_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-event-instance.c b/src/mono/mono/eventpipe/ep-event-instance.c new file mode 100644 index 0000000000000..1702b5d922c87 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-event-instance.c @@ -0,0 +1,93 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * EventPipeEventInstance. + */ + +bool +ep_event_instance_ensure_consistency (const EventPipeEventInstance *ep_event_instance) +{ +#ifdef EP_CHECKED_BUILD + EP_ASSERT (ep_event_instance_get_debug_event_start (ep_event_instance) == 0xDEADBEEF); + EP_ASSERT (ep_event_instance_get_debug_event_end (ep_event_instance) == 0xCAFEBABE); +#endif + + return true; +} + +uint32_t +ep_event_instance_get_aligned_total_size ( + const EventPipeEventInstance *ep_event_instance, + EventPipeSerializationFormat format) +{ + // Calculate the size of the total payload so that it can be written to the file. + uint32_t payload_len = 0; + + if (format == EP_SERIALIZATION_FORMAT_NETPERF_V3) { + payload_len = + // Metadata ID + ep_event_instance_sizeof_metadata_id (ep_event_instance) + + // Thread ID + sizeof (int32_t) + + // TimeStamp + ep_event_instance_sizeof_timestamp (ep_event_instance) + + // Activity ID + EP_ACTIVITY_ID_SIZE + + // Related Activity ID + EP_ACTIVITY_ID_SIZE + + // Data payload length + ep_event_instance_sizeof_data_len (ep_event_instance) + + // Event payload data + ep_event_instance_get_data_len (ep_event_instance) + + // Prepended stack payload size in bytes + sizeof (uint32_t) + + // Stack payload size + ep_stack_contents_get_size (ep_event_instance_get_stack_contents_cref (ep_event_instance)); + } else if (format == EP_SERIALIZATION_FORMAT_NETTRACE_V4) { + payload_len = + // Metadata ID + ep_event_instance_sizeof_metadata_id (ep_event_instance) + + // Sequence number (implied by the buffer containing the event instance) + sizeof (uint32_t) + + // Thread ID + sizeof (int32_t) + + // Capture Thread ID (implied by the buffer containing the event instance) + sizeof (uint64_t) + + // ProcNumber + ep_event_instance_sizeof_proc_num (ep_event_instance) + + // Stack intern table id + sizeof (uint32_t) + + // TimeStamp + ep_event_instance_sizeof_timestamp (ep_event_instance) + + // Activity ID + EP_ACTIVITY_ID_SIZE + + // Related Activity ID + EP_ACTIVITY_ID_SIZE + + // Data payload length + ep_event_instance_sizeof_data_len (ep_event_instance) + + // Event payload data + ep_event_instance_get_data_len (ep_event_instance); + } else { + EP_ASSERT (!"Unrecognized format"); + } + + // round up to FAST_SERIALIZER_ALIGNMENT_SIZE bytes + if (payload_len % FAST_SERIALIZER_ALIGNMENT_SIZE != 0) + payload_len += FAST_SERIALIZER_ALIGNMENT_SIZE - (payload_len % FAST_SERIALIZER_ALIGNMENT_SIZE); + + return payload_len; +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_event_instance; +const char quiet_linker_empty_file_warning_eventpipe_event_instance = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-event-instance.h b/src/mono/mono/eventpipe/ep-event-instance.h new file mode 100644 index 0000000000000..391e7d36f3774 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-event-instance.h @@ -0,0 +1,122 @@ +#ifndef __EVENTPIPE_EVENTINSTANCE_H__ +#define __EVENTPIPE_EVENTINSTANCE_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeEventInstance. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEventInstance { +#else +struct _EventPipeEventInstance_Internal { +#endif +#ifdef EP_CHECKED_BUILD + uint32_t debug_event_start; + uint32_t debug_event_end; +#endif + EventPipeEvent *ep_event; + uint32_t metadata_id; + uint32_t proc_num; + uint64_t thread_id; + uint64_t timestamp; + uint8_t activity_id [EP_ACTIVITY_ID_SIZE]; + uint8_t related_activity_id [EP_ACTIVITY_ID_SIZE]; + const uint8_t *data; + uint32_t data_len; + EventPipeStackContents stack_contents; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEventInstance { + uint8_t _internal [sizeof (struct _EventPipeEventInstance_Internal)]; +}; +#endif + +#ifdef EP_CHECKED_BUILD +EP_DEFINE_GETTER(EventPipeEventInstance *, event_instance, uint32_t, debug_event_start) +EP_DEFINE_GETTER(EventPipeEventInstance *, event_instance, uint32_t, debug_event_end) +#endif +EP_DEFINE_GETTER(EventPipeEventInstance *, event_instance, EventPipeEvent *, ep_event) +EP_DEFINE_GETTER(EventPipeEventInstance *, event_instance, uint32_t, metadata_id) +EP_DEFINE_SETTER(EventPipeEventInstance *, event_instance, uint32_t, metadata_id) +EP_DEFINE_GETTER(EventPipeEventInstance *, event_instance, uint32_t, proc_num) +EP_DEFINE_GETTER(EventPipeEventInstance *, event_instance, uint64_t, thread_id) +EP_DEFINE_GETTER(EventPipeEventInstance *, event_instance, uint64_t, timestamp) +EP_DEFINE_SETTER(EventPipeEventInstance *, event_instance, uint64_t, timestamp) +EP_DEFINE_GETTER_ARRAY_REF(EventPipeEventInstance *, event_instance, uint8_t *, const uint8_t *, activity_id, activity_id[0]) +EP_DEFINE_GETTER_ARRAY_REF(EventPipeEventInstance *, event_instance, uint8_t *, const uint8_t *, related_activity_id, related_activity_id[0]) +EP_DEFINE_GETTER(EventPipeEventInstance *, event_instance, const uint8_t *, data) +EP_DEFINE_GETTER(EventPipeEventInstance *, event_instance, uint32_t, data_len) +EP_DEFINE_GETTER_REF(EventPipeEventInstance *, event_instance, EventPipeStackContents *, stack_contents) + +EventPipeEventInstance * +ep_event_instance_alloc ( + EventPipeEvent *ep_event, + uint32_t proc_num, + uint64_t thread_id, + const uint8_t *data, + uint32_t data_len, + const uint8_t *activity_id, + const uint8_t *related_activity_id); + +EventPipeEventInstance * +ep_event_instance_init ( + EventPipeEventInstance *ep_event_instance, + EventPipeEvent *ep_event, + uint32_t proc_num, + uint64_t thread_id, + const uint8_t *data, + uint32_t data_len, + const uint8_t *activity_id, + const uint8_t *related_activity_id); + +void +ep_event_instance_fini (EventPipeEventInstance *ep_event_instance); + +void +ep_event_instance_free (EventPipeEventInstance *ep_event_instance); + +bool +ep_event_instance_ensure_consistency (const EventPipeEventInstance *ep_event_instance); + +uint32_t +ep_event_instance_get_aligned_total_size ( + const EventPipeEventInstance *ep_event_instance, + EventPipeSerializationFormat format); + +/* + * EventPipeSequencePoint. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeSequencePoint { +#else +struct _EventPipeSequencePoint_Internal { +#endif + uint64_t timestamp; + ep_rt_thread_sequence_number_hash_map_t thread_sequence_numbers; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeSequencePoint { + uint8_t _internal [sizeof (struct _EventPipeSequencePoint_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeSequencePoint *, sequence_point, uint64_t, timestamp) +EP_DEFINE_GETTER_REF(EventPipeSequencePoint *, sequence_point, ep_rt_thread_sequence_number_hash_map_t *, thread_sequence_numbers) + +EventPipeSequencePoint * +ep_sequence_point_init (EventPipeSequencePoint *sequence_point); + +void +ep_sequence_point_fini (EventPipeSequencePoint *sequence_point); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_EVENTINSTANCE_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-event-internals.c b/src/mono/mono/eventpipe/ep-event-internals.c new file mode 100644 index 0000000000000..02c9469d28f59 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-event-internals.c @@ -0,0 +1,111 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +void +event_build_minimum_metadata ( + EventPipeEvent *ep_event, + uint8_t **metadata, + uint32_t *metadata_len); + +/* + * EventPipeEvent. + */ + +static +void +event_build_minimum_metadata ( + EventPipeEvent *ep_event, + uint8_t **metadata, + uint32_t *metadata_len) +{ + EP_ASSERT (ep_event != NULL); + EP_ASSERT (metadata != NULL); + EP_ASSERT (metadata_len != NULL); + + size_t output_len = 0; + ep_char16_t empty_string [1] = { 0 }; + *metadata = ep_metadata_generator_generate_event_metadata ( + ep_event->event_id, + empty_string, + ep_event->keywords, + ep_event->event_version, + ep_event->level, + NULL, + 0, + &output_len); + + *metadata_len = (uint32_t)output_len; +} + +EventPipeEvent * +ep_event_alloc ( + EventPipeProvider *provider, + uint64_t keywords, + uint32_t event_id, + uint32_t event_version, + EventPipeEventLevel level, + bool need_stack, + const uint8_t *metadata, + uint32_t metadata_len) +{ + ep_return_null_if_nok (provider != NULL); + + EventPipeEvent *instance = ep_rt_object_alloc (EventPipeEvent); + ep_raise_error_if_nok (instance != NULL); + + instance->provider = provider; + instance->keywords = keywords; + instance->event_id = event_id; + instance->event_version = event_version; + instance->level = level; + instance->need_stack = need_stack; + instance->enabled_mask = 0; + + if (metadata != NULL) { + instance->metadata = ep_rt_byte_array_alloc (metadata_len); + ep_raise_error_if_nok (instance->metadata != NULL); + + memcpy (instance->metadata, metadata, metadata_len); + instance->metadata_len = metadata_len; + } else { + // if metadata is not provided, we have to build the minimum version. It's required by the serialization contract. + event_build_minimum_metadata (instance, &(instance->metadata), &(instance->metadata_len)); + } + +ep_on_exit: + return instance; + +ep_on_error: + ep_event_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_event_free (EventPipeEvent *ep_event) +{ + ep_return_void_if_nok (ep_event != NULL); + + ep_rt_byte_array_free (ep_event->metadata); + ep_rt_object_free (ep_event); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_event_internals; +const char quiet_linker_empty_file_warning_eventpipe_event_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-event-payload-internals.c b/src/mono/mono/eventpipe/ep-event-payload-internals.c new file mode 100644 index 0000000000000..1be0b43138e75 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-event-payload-internals.c @@ -0,0 +1,70 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * EventData. + */ + +EventData * +ep_event_data_alloc ( + uint64_t ptr, + uint32_t size, + uint32_t reserved) +{ + EventData *instance = ep_rt_object_alloc (EventData); + ep_raise_error_if_nok (ep_event_data_init (instance, ptr, size,reserved)); + +ep_on_exit: + return instance; + +ep_on_error: + ep_event_data_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +EventData * +ep_event_data_init ( + EventData *event_data, + uint64_t ptr, + uint32_t size, + uint32_t reserved) +{ + EP_ASSERT (event_data != NULL); + + event_data->ptr = ptr; + event_data->size = size; + event_data->reserved = reserved; + + return event_data; +} + +void +ep_event_data_fini (EventData *event_data) +{ + ; +} + +void +ep_event_data_free (EventData *event_data) +{ + ep_return_void_if_nok (event_data != NULL); + + ep_event_data_fini (event_data); + ep_rt_object_free (event_data); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_event_payload_internals; +const char quiet_linker_empty_file_warning_eventpipe_event_payload_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-event-payload.h b/src/mono/mono/eventpipe/ep-event-payload.h new file mode 100644 index 0000000000000..58532d7b1416a --- /dev/null +++ b/src/mono/mono/eventpipe/ep-event-payload.h @@ -0,0 +1,54 @@ +#ifndef __EVENTPIPE_EVENT_PAYLOAD_H__ +#define __EVENTPIPE_EVENT_PAYLOAD_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventData. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventData { +#else +struct _EventData_Internal { +#endif + uint64_t ptr; + uint32_t size; + uint32_t reserved; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventData { + uint8_t _internal [sizeof (struct _EventData_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventData *, event_data, uint64_t, ptr) +EP_DEFINE_GETTER(EventData *, event_data, uint32_t, size) +EP_DEFINE_GETTER(EventData *, event_data, uint32_t, reserved) + +EventData * +ep_event_data_alloc ( + uint64_t ptr, + uint32_t size, + uint32_t reserved); + +EventData * +ep_event_data_init ( + EventData *event_data, + uint64_t ptr, + uint32_t size, + uint32_t reserved); + +void +ep_event_data_fini (EventData *event_data); + +void +ep_event_data_free (EventData *event_data); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_EVENT_PAYLOAD_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-event-source-internals.c b/src/mono/mono/eventpipe/ep-event-source-internals.c new file mode 100644 index 0000000000000..e283a37030a34 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-event-source-internals.c @@ -0,0 +1,99 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * EventPipeEventSource. + */ + +EventPipeEventSource * +ep_event_source_alloc (void) +{ + ep_char16_t *command_line_arg_utf16 = NULL; + ep_char16_t *event_name_utf16 = NULL; + uint8_t *metadata = NULL; + + EventPipeEventSource *instance = ep_rt_object_alloc (EventPipeEventSource); + ep_raise_error_if_nok (instance != NULL); + + instance->provider = ep_create_provider (ep_provider_get_default_name_utf8 (), NULL, NULL); + ep_raise_error_if_nok (instance->provider != NULL); + + // Generate metadata. + EventPipeParameterDesc params [1]; + const uint32_t params_len = EP_ARRAY_SIZE (params); + + command_line_arg_utf16 = ep_rt_utf8_to_utf16_string ("CommandLine", -1); + ep_raise_error_if_nok (command_line_arg_utf16 != NULL); + + ep_parameter_desc_init (params, EP_PARAMETER_TYPE_STRING, command_line_arg_utf16); + + event_name_utf16 = ep_rt_utf8_to_utf16_string ("ProcessInfo", -1); + ep_raise_error_if_nok (event_name_utf16 != NULL); + + size_t metadata_len = 0; + metadata = ep_metadata_generator_generate_event_metadata ( + 1, /* eventID */ + event_name_utf16, + 0, /* keywords */ + 0, /* version */ + EP_EVENT_LEVEL_LOG_ALWAYS, + params, + params_len, + &metadata_len); + + ep_raise_error_if_nok (metadata != NULL); + + // Add the event. + instance->process_info_event = ep_provider_add_event ( + instance->provider, + 1, /* eventID */ + 0, /* keywords */ + 0, /* eventVersion */ + EP_EVENT_LEVEL_LOG_ALWAYS, + false, /* needStack */ + metadata, + (uint32_t)metadata_len); + + ep_raise_error_if_nok (instance->process_info_event); + + // Delete the metadata after the event is created. + // The metadata blob will be copied into EventPipe-owned memory. + ep_rt_byte_array_free (metadata); + + // Delete the strings after the event is created. + // The strings will be copied into EventPipe-owned memory. + ep_rt_utf16_string_free (event_name_utf16); + ep_rt_utf16_string_free (command_line_arg_utf16); + +ep_on_exit: + return instance; + +ep_on_error: + ep_rt_byte_array_free (metadata); + ep_rt_utf16_string_free (event_name_utf16); + ep_rt_utf16_string_free (command_line_arg_utf16); + ep_event_source_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_event_source_free (EventPipeEventSource *event_source) +{ + ep_provider_free (event_source->provider); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_event_source_internals; +const char quiet_linker_empty_file_warning_eventpipe_event_source_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-event-source.c b/src/mono/mono/eventpipe/ep-event-source.c new file mode 100644 index 0000000000000..94b1283e3b414 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-event-source.c @@ -0,0 +1,52 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * EventPipeEventSource. + */ + +static EventPipeEventSource *event_source_instance; + +void +ep_event_source_enable ( + EventPipeEventSource *event_source, + EventPipeSession *session) +{ + ep_return_void_if_nok (event_source != NULL); + ep_return_void_if_nok (session != NULL); + + EventPipeSessionProvider *session_provider = ep_session_provider_alloc (ep_event_source_get_provider_name (event_source), (uint64_t)-1, EP_EVENT_LEVEL_LOG_ALWAYS, NULL); + if (session_provider != NULL) + ep_session_add_session_provider (session, session_provider); +} + +void +ep_event_source_send_process_info ( + EventPipeEventSource * event_source, + const ep_char16_t *command_line) +{ + ep_return_void_if_nok (event_source != NULL); + + EventData data [1]; + ep_event_data_init (data, (uint64_t)command_line, (uint32_t)((ep_rt_utf16_string_len (command_line) + 1) * sizeof (ep_char16_t)), 0); + ep_write_event (ep_event_source_get_process_info_event (event_source), data, EP_ARRAY_SIZE (data), NULL, NULL); +} + +EventPipeEventSource * +ep_event_source_get (void) +{ + return event_source_instance; +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_event_source; +const char quiet_linker_empty_file_warning_eventpipe_event_source = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-event-source.h b/src/mono/mono/eventpipe/ep-event-source.h new file mode 100644 index 0000000000000..3a2195c27778f --- /dev/null +++ b/src/mono/mono/eventpipe/ep-event-source.h @@ -0,0 +1,51 @@ +#ifndef __EVENTPIPE_EVENT_SOURCE_H__ +#define __EVENTPIPE_EVENT_SOURCE_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeEventSource. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEventSource { +#else +struct _EventPipeEventSource_Internal { +#endif + const ep_char8_t *provider_name; + EventPipeProvider *provider; + const ep_char8_t *process_info_event_name; + EventPipeEvent *process_info_event; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEventSource { + uint8_t _internal [sizeof (struct _EventPipeEventSource_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeEventSource *, event_source, const ep_char8_t *, provider_name) +EP_DEFINE_GETTER(EventPipeEventSource *, event_source, const ep_char8_t *, process_info_event_name) +EP_DEFINE_GETTER(EventPipeEventSource *, event_source, EventPipeEvent *, process_info_event) + +EventPipeEventSource * +ep_event_source_alloc (void); + +void +ep_event_source_free (EventPipeEventSource *event_source); + +void +ep_event_source_enable (EventPipeEventSource *event_source, EventPipeSession *session); + +void +ep_event_source_send_process_info (EventPipeEventSource *event_source, const ep_char16_t *command_line); + +EventPipeEventSource * +ep_event_source_get (void); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_EVENT_SOURCE_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-event.h b/src/mono/mono/eventpipe/ep-event.h new file mode 100644 index 0000000000000..8f0418b638ab9 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-event.h @@ -0,0 +1,64 @@ +#ifndef __EVENTPIPE_EVENT_H__ +#define __EVENTPIPE_EVENT_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeEvent. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEvent { +#else +struct _EventPipeEvent_Internal { +#endif + EventPipeProvider *provider; + uint64_t keywords; + uint32_t event_id; + uint32_t event_version; + EventPipeEventLevel level; + bool need_stack; + volatile int64_t enabled_mask; + uint8_t *metadata; + uint32_t metadata_len; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeEvent { + uint8_t _internal [sizeof (struct _EventPipeEvent_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeEvent *, event, EventPipeProvider *, provider) +EP_DEFINE_GETTER(EventPipeEvent *, event, uint64_t, keywords) +EP_DEFINE_GETTER(EventPipeEvent *, event, uint32_t, event_id) +EP_DEFINE_GETTER(EventPipeEvent *, event, uint32_t, event_version) +EP_DEFINE_GETTER(EventPipeEvent *, event, EventPipeEventLevel, level) +EP_DEFINE_GETTER(EventPipeEvent *, event, bool, need_stack) +EP_DEFINE_GETTER(EventPipeEvent *, event, int64_t, enabled_mask) +EP_DEFINE_SETTER(EventPipeEvent *, event, int64_t, enabled_mask) +EP_DEFINE_GETTER(EventPipeEvent *, event, uint8_t *, metadata) +EP_DEFINE_SETTER(EventPipeEvent *, event, uint8_t *, metadata) +EP_DEFINE_GETTER(EventPipeEvent *, event, uint32_t, metadata_len) +EP_DEFINE_SETTER(EventPipeEvent *, event, uint32_t, metadata_len) + +EventPipeEvent * +ep_event_alloc ( + EventPipeProvider *provider, + uint64_t keywords, + uint32_t event_id, + uint32_t event_version, + EventPipeEventLevel level, + bool need_stack, + const uint8_t *metadata, + uint32_t metadata_len); + +void +ep_event_free (EventPipeEvent * ep_event); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_EVENT_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-file-internals.c b/src/mono/mono/eventpipe/ep-file-internals.c new file mode 100644 index 0000000000000..b10351d792ea3 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-file-internals.c @@ -0,0 +1,295 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +void +file_fast_serialize_func (void *object, FastSerializer *fast_serializer); + +static +const ep_char8_t * +file_get_type_name_func (void *object); + +static +void +file_free_func (void *object); + +static +uint32_t +stack_hash_key_hash_func (const void *key); + +static +bool +stack_hash_key_eq_func (const void *key1, const void *key2); + +static +void +stack_hash_value_free_func (void *entry); + +/* + * EventPipeFile. + */ + +static +void +file_free_func (void *object) +{ + ep_file_free ((EventPipeFile*)object); +} + +static +void +file_fast_serialize_func (void *object, FastSerializer *fast_serializer) +{ + EP_ASSERT (object != NULL && fast_serializer != NULL); + + EventPipeFile *file = (EventPipeFile *)object; + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&file->file_open_system_time, sizeof (file->file_open_system_time)); + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&file->file_open_timestamp, sizeof (file->file_open_timestamp)); + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&file->timestamp_frequency, sizeof (file->timestamp_frequency)); + + // the beginning of V3 + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&file->pointer_size, sizeof (file->pointer_size)); + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&file->current_process_id, sizeof (file->current_process_id)); + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&file->number_of_processors, sizeof (file->number_of_processors)); + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&file->sampling_rate_in_ns, sizeof (file->sampling_rate_in_ns)); +} + +static +const ep_char8_t * +file_get_type_name_func (void *object) +{ + EP_ASSERT (object != NULL); + return "Trace"; +} + +static +FastSerializableObjectVtable +file_vtable = { + file_free_func, + file_fast_serialize_func, + file_get_type_name_func }; + +static +uint32_t +stack_hash_key_hash_func (const void *key) +{ + EP_ASSERT (key != NULL); + return ((const StackHashKey *)key)->hash; +} + +static +bool +stack_hash_key_eq_func (const void *key1, const void *key2) +{ + EP_ASSERT (key1 != NULL && key2 != NULL); + + const StackHashKey * stack_hash_key1 = (const StackHashKey *)key1; + const StackHashKey * stack_hash_key2 = (const StackHashKey *)key2; + + return stack_hash_key1->stack_size_in_bytes == stack_hash_key2->stack_size_in_bytes && + !memcmp (stack_hash_key1->stack_bytes, stack_hash_key2->stack_bytes, stack_hash_key1->stack_size_in_bytes); +} + +static +void +stack_hash_value_free_func (void *entry) +{ + ep_stack_hash_entry_free ((StackHashEntry *)entry); +} + +static +void +file_write_end (EventPipeFile *file) +{ + EP_ASSERT (file != NULL && ep_file_get_fast_serializer (file) != NULL); + + ep_file_flush (file, EP_FILE_FLUSH_FLAGS_ALL_BLOCKS); + + // "After the last EventBlock is emitted, the stream is ended by emitting a NullReference Tag which indicates that there are no more objects in the stream to read." + // see https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/EventPipe/EventPipeFormat.md for more + ep_fast_serializer_write_tag (ep_file_get_fast_serializer (file), FAST_SERIALIZER_TAGS_NULL_REFERENCE, NULL, 0); +} + +EventPipeFile * +ep_file_alloc ( + StreamWriter *stream_writer, + EventPipeSerializationFormat format) +{ + EventPipeFile *instance = ep_rt_object_alloc (EventPipeFile); + ep_raise_error_if_nok (instance != NULL); + + ep_fast_serializable_object_init ( + &instance->fast_serializable_object, + &file_vtable, + ep_file_get_file_version (format), + ep_file_get_file_minimum_version (format), + format >= EP_SERIALIZATION_FORMAT_NETTRACE_V4); + + instance->stream_writer = stream_writer; + instance->format = format; + + instance->event_block = ep_event_block_alloc (100 * 1024, format); + ep_raise_error_if_nok (instance->event_block != NULL); + + instance->metadata_block = ep_metadata_block_alloc (100 * 1024); + ep_raise_error_if_nok (instance->metadata_block); + + instance->stack_block = ep_stack_block_alloc (100 * 1024); + 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 (); + instance->timestamp_frequency = ep_perf_frequency_query (); + + instance->pointer_size = SIZEOF_VOID_P; + instance->current_process_id = ep_rt_current_process_get_id (); + instance->number_of_processors = ep_rt_processors_get_count (); + + instance->sampling_rate_in_ns = ep_rt_sample_profiler_get_sampling_rate (); + + ep_rt_metadata_labels_alloc (&instance->metadata_ids, NULL, NULL, NULL, NULL); + ep_raise_error_if_nok (instance->metadata_ids.table); + + ep_rt_stack_hash_alloc (&instance->stack_hash, stack_hash_key_hash_func, stack_hash_key_eq_func, NULL, stack_hash_value_free_func); + ep_raise_error_if_nok (instance->stack_hash.table); + + // Start at 0 - The value is always incremented prior to use, so the first ID will be 1. + ep_rt_volatile_store_uint32_t (&instance->metadata_id_counter, 0); + + // Start at 0 - The value is always incremented prior to use, so the first ID will be 1. + instance->stack_id_counter = 0; + +#ifdef EP_CHECKED_BUILD + instance->last_sorted_timestamp = ep_perf_counter_query (); +#endif + +ep_on_exit: + return instance; + +ep_on_error: + ep_file_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_file_free (EventPipeFile *file) +{ + ep_return_void_if_nok (file != NULL); + + if (file->event_block != NULL && file->fast_serializer != NULL) + file_write_end (file); + + ep_event_block_free (file->event_block); + ep_metadata_block_free (file->metadata_block); + ep_stack_block_free (file->stack_block); + ep_fast_serializer_free (file->fast_serializer); + ep_rt_metadata_labels_free (&file->metadata_ids); + ep_rt_stack_hash_free (&file->stack_hash); + + // If there's no fast_serializer, stream_writer ownership + // have not been passed along and needs to be freed by file. + if (!file->fast_serializer) + ep_stream_writer_free_vcall (file->stream_writer); + + ep_fast_serializable_object_fini (&file->fast_serializable_object); + ep_rt_object_free (file); +} + +/* + * StackHashEntry. + */ + +StackHashEntry * +ep_stack_hash_entry_alloc ( + const EventPipeStackContents *stack_contents, + uint32_t id, + uint32_t hash) +{ + ep_return_null_if_nok (stack_contents != NULL); + + uint32_t stack_size = ep_stack_contents_get_size (stack_contents); + StackHashEntry *entry = (StackHashEntry *)ep_rt_byte_array_alloc (offsetof (StackHashEntry, stack_bytes) + stack_size); + ep_raise_error_if_nok (entry != NULL); + + entry->id = id; + entry->key.hash = hash; + entry->key.stack_size_in_bytes = stack_size; + entry->key.stack_bytes = entry->stack_bytes; + memcpy (entry->stack_bytes, ep_stack_contents_get_pointer (stack_contents), stack_size); + +ep_on_exit: + return entry; + +ep_on_error: + ep_stack_hash_entry_free (entry); + + entry = NULL; + ep_exit_error_handler (); +} + +void +ep_stack_hash_entry_free (StackHashEntry *stack_hash_entry) +{ + ep_return_void_if_nok (stack_hash_entry != NULL); + ep_rt_byte_array_free ((uint8_t *)stack_hash_entry); +} + +/* + * StackHashKey. + */ + +static +inline +uint32_t +hash_bytes (const uint8_t *data, size_t data_len) +{ + EP_ASSERT (data != NULL); + + uint32_t hash = 5381; + const uint8_t *data_end = data + data_len; + for (/**/ ; data < data_end; data++) + hash = ((hash << 5) + hash) ^ *data; + return hash; +} + +StackHashKey * +ep_stack_hash_key_init ( + StackHashKey *key, + const EventPipeStackContents *stack_contents) +{ + EP_ASSERT (key != NULL); + ep_return_null_if_nok (stack_contents != NULL); + + key->stack_bytes = ep_stack_contents_get_pointer (stack_contents); + key->stack_size_in_bytes = ep_stack_contents_get_size (stack_contents); + key->hash = hash_bytes (key->stack_bytes, key->stack_size_in_bytes); + + return key; +} + +void +ep_stack_hash_key_fini (StackHashKey *key) +{ + ; +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_file_internals; +const char quiet_linker_empty_file_warning_eventpipe_file_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-file.c b/src/mono/mono/eventpipe/ep-file.c new file mode 100644 index 0000000000000..e16acabc0b0d2 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-file.c @@ -0,0 +1,331 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +uint32_t +file_get_stack_id ( + EventPipeFile *file, + EventPipeEventInstance *event_instance); + +static +uint32_t +file_generate_metadata_id (EventPipeFile *file); + +static +uint32_t +file_get_metadata_id ( + EventPipeFile *file, + EventPipeEvent *ep_event); + +static +void +file_write_event_to_block ( + EventPipeFile *file, + EventPipeEventInstance *event_instance, + uint32_t metadata_id, + uint64_t capture_thread_id, + uint32_t sequence_number, + uint32_t stack_id, + bool is_sotred_event); + +static +void +file_save_metadata_id ( + EventPipeFile *file, + EventPipeEvent *ep_event, + uint32_t metadata_id); + +/* + * EventPipeFile. + */ + +static +uint32_t +file_get_stack_id ( + EventPipeFile *file, + EventPipeEventInstance * event_instance) +{ + EP_ASSERT (file != NULL && event_instance != NULL); + EP_ASSERT (ep_file_get_format (file) >= EP_SERIALIZATION_FORMAT_NETTRACE_V4); + EP_ASSERT (ep_file_get_stack_block (file) != NULL); + + uint32_t stack_id = 0; + EventPipeStackContents *stack_contents = ep_event_instance_get_stack_contents_ref (event_instance); + EventPipeStackBlock *stack_block = ep_file_get_stack_block (file); + ep_rt_stack_hash_map_t *stack_hash = ep_file_get_stack_hash_ref (file); + StackHashEntry *entry = NULL; + StackHashKey key; + ep_stack_hash_key_init (&key, stack_contents); + if (!ep_rt_stack_hash_lookup (stack_hash, &key, &entry)) { + stack_id = ep_file_get_stack_id_counter (file) + 1; + ep_file_set_stack_id_counter (file, stack_id); + entry = ep_stack_hash_entry_alloc (stack_contents, stack_id, ep_stack_hash_key_get_hash (&key)); + if (entry) + ep_rt_stack_hash_add (stack_hash, ep_stack_hash_entry_get_key_ref (entry), entry); + + if (!ep_stack_block_write_stack (stack_block, stack_id, stack_contents)) { + // we can't write this stack to the current block (it's full) + // so we write what we have in the block to the serializer + ep_file_flush (file, EP_FILE_FLUSH_FLAGS_STACK_BLOCK); + bool result = ep_stack_block_write_stack (stack_block, stack_id, stack_contents); + EP_ASSERT (result == true); // we should never fail to add event to a clear block (if we do the max size is too small) + } + } else { + stack_id = ep_stack_hash_entry_get_id (entry); + } + + ep_stack_hash_key_fini (&key); + return stack_id; +} + +static +uint32_t +file_get_metadata_id ( + EventPipeFile *file, + EventPipeEvent *ep_event) +{ + EP_ASSERT (file != NULL && ep_event != NULL); + EP_ASSERT (ep_file_get_metadata_ids_cref (file) != NULL); + + uint32_t metadata_ids; + if (ep_rt_metadata_labels_lookup (ep_file_get_metadata_ids_cref (file), ep_event, &metadata_ids)) { + EP_ASSERT (metadata_ids != 0); + return metadata_ids; + } + + return 0; +} + +static +uint32_t +file_generate_metadata_id (EventPipeFile *file) +{ + return ep_rt_atomic_inc_uint32_t (ep_file_get_metadata_id_counter_ref (file)); +} + +static +void +file_write_event_to_block ( + EventPipeFile *file, + EventPipeEventInstance *event_instance, + uint32_t metadata_id, + uint64_t capture_thread_id, + uint32_t sequence_number, + uint32_t stack_id, + bool is_sotred_event) +{ + EP_ASSERT (file != NULL && event_instance != NULL); + EP_ASSERT (ep_file_get_event_block (file) != NULL); + EP_ASSERT (ep_file_get_metadata_block (file) != NULL); + + ep_event_instance_set_metadata_id (event_instance, metadata_id); + + // If we are flushing events we need to flush metadata and stacks as well + // to ensure referenced metadata/stacks were written to the file before the + // event which referenced them. + EventPipeFileFlushFlags flags = EP_FILE_FLUSH_FLAGS_ALL_BLOCKS; + EventPipeEventBlockBase *block = (EventPipeEventBlockBase *)ep_file_get_event_block (file); + if(metadata_id == 0 && ep_file_get_format (file) >= EP_SERIALIZATION_FORMAT_NETTRACE_V4) { + flags = EP_FILE_FLUSH_FLAGS_METADATA_BLOCK; + block = (EventPipeEventBlockBase *)ep_file_get_metadata_block (file); + } + + if (ep_event_block_base_write_event (block, event_instance, capture_thread_id, sequence_number, stack_id, is_sotred_event)) + return; // the block is not full, we added the event and continue + + // we can't write this event to the current block (it's full) + // so we write what we have in the block to the serializer + ep_file_flush (file, flags); + + bool result = ep_event_block_base_write_event (block, event_instance, capture_thread_id, sequence_number, stack_id, is_sotred_event); + EP_ASSERT (result == true); // we should never fail to add event to a clear block (if we do the max size is too small) +} + +static +void +file_save_metadata_id ( + EventPipeFile *file, + EventPipeEvent *ep_event, + uint32_t metadata_id) +{ + EP_ASSERT (file != NULL && ep_event != NULL); + EP_ASSERT (metadata_id > 0); + EP_ASSERT (ep_file_get_metadata_ids_cref (file) != NULL); + + // If a pre-existing metadata label exists, remove it. + uint32_t old_id; + if (ep_rt_metadata_labels_lookup (ep_file_get_metadata_ids_cref (file), ep_event, &old_id)) + ep_rt_metadata_labels_remove (ep_file_get_metadata_ids_ref (file), ep_event); + + // Add the metadata label. + ep_rt_metadata_labels_add (ep_file_get_metadata_ids_ref (file), ep_event, metadata_id); +} + +bool +ep_file_initialize_file (EventPipeFile *file) +{ + ep_return_false_if_nok (file != NULL); + + EP_ASSERT (ep_file_get_stream_writer (file) != NULL); + EP_ASSERT (ep_file_get_fast_serializer (file) == NULL); + + bool success = true; + if (ep_file_get_format (file) >= EP_SERIALIZATION_FORMAT_NETTRACE_V4) { + const ep_char8_t header[] = "Nettrace"; + const uint32_t bytes_to_write = EP_ARRAY_SIZE (header) - 1; + uint32_t bytes_written = 0; + success = ep_stream_writer_write (ep_file_get_stream_writer (file), (const uint8_t *)header, bytes_to_write, &bytes_written) && bytes_written == bytes_to_write; + } + + if (success) { + // Create the file stream and write the FastSerialization header. + ep_file_set_fast_serializer (file, ep_fast_serializer_alloc (ep_file_get_stream_writer (file))); + + // Write the first object to the file. + if (ep_file_get_fast_serializer (file)) + ep_fast_serializer_write_object (ep_file_get_fast_serializer (file), (FastSerializableObject *)file); + } + + return success; +} + +void +ep_file_write_event ( + EventPipeFile *file, + EventPipeEventInstance *event_instance, + uint64_t capture_thread_id, + uint32_t sequence_number, + bool is_sorted_event) +{ + ep_return_void_if_nok (file != NULL && event_instance != NULL); + EP_ASSERT (ep_file_get_fast_serializer (file) != NULL); + + EventPipeEventMetadataEvent *metadata_instance = NULL; + +#ifdef EP_CHECKED_BUILD + EP_ASSERT (ep_event_instance_get_timestamp (event_instance) >= ep_file_get_last_sorted_timestamp (file)); + if (is_sorted_event) + ep_file_set_last_sorted_timestamp (file, ep_event_instance_get_timestamp (event_instance)); +#endif + + uint32_t stack_id = 0; + if (ep_file_get_format (file) >= EP_SERIALIZATION_FORMAT_NETTRACE_V4) + stack_id = file_get_stack_id (file, event_instance); + + // Check to see if we've seen this event type before. + // If not, then write the event metadata to the event stream first. + unsigned int metadata_id = file_get_metadata_id (file, ep_event_instance_get_ep_event (event_instance)); + if(metadata_id == 0) { + metadata_id = file_generate_metadata_id (file); + + metadata_instance = ep_build_event_metadata_event (event_instance, metadata_id); + ep_raise_error_if_nok (metadata_instance != NULL); + + file_write_event_to_block (file, (EventPipeEventInstance *)metadata_instance, 0, 0, 0, 0, true); // metadataId=0 breaks recursion and represents the metadata event. + file_save_metadata_id (file, ep_event_instance_get_ep_event (event_instance), metadata_id); + } + + file_write_event_to_block (file, event_instance, metadata_id, capture_thread_id, sequence_number, stack_id, is_sorted_event); + +ep_on_exit: + ep_event_metdata_event_free (metadata_instance); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +void +ep_file_write_sequence_point ( + EventPipeFile *file, + EventPipeSequencePoint *sequence_point) +{ + ep_return_void_if_nok (file != NULL && sequence_point != NULL); + EP_ASSERT (ep_file_get_fast_serializer (file) != NULL); + + if (ep_file_get_format (file) < EP_SERIALIZATION_FORMAT_NETTRACE_V4) + return; // sequence points aren't used in NetPerf format + + ep_file_flush (file, EP_FILE_FLUSH_FLAGS_ALL_BLOCKS); + EventPipeSequencePointBlock sequence_point_block; + + ep_sequence_point_block_init (&sequence_point_block, sequence_point); + ep_fast_serializer_write_object (ep_file_get_fast_serializer (file), (FastSerializableObject *)&sequence_point_block); + ep_sequence_point_block_fini (&sequence_point_block); + + // stack cache resets on sequence points + ep_file_set_stack_id_counter (file, 0); + ep_rt_stack_hash_remove_all (ep_file_get_stack_hash_ref (file)); +} + +void +ep_file_flush ( + EventPipeFile *file, + EventPipeFileFlushFlags flags) +{ + // Write existing buffer to the stream/file regardless of whether it is full or not. + ep_return_void_if_nok (file != NULL && ep_file_get_fast_serializer (file) != NULL && ep_file_get_metadata_block (file) != NULL && + ep_file_get_stack_block (file) != NULL && ep_file_get_event_block (file) != NULL); + + if ((ep_metadata_block_get_bytes_written (ep_file_get_metadata_block (file)) != 0) && ((flags & EP_FILE_FLUSH_FLAGS_METADATA_BLOCK) != 0)) { + EP_ASSERT (ep_file_get_format (file) >= EP_SERIALIZATION_FORMAT_NETTRACE_V4); + ep_metadata_block_serialize (ep_file_get_metadata_block (file), ep_file_get_fast_serializer (file)); + ep_metadata_block_clear (ep_file_get_metadata_block (file)); + } + + if ((ep_stack_block_get_bytes_written (ep_file_get_stack_block (file)) != 0) && ((flags & EP_FILE_FLUSH_FLAGS_STACK_BLOCK) != 0)) { + EP_ASSERT (ep_file_get_format (file) >= EP_SERIALIZATION_FORMAT_NETTRACE_V4); + ep_stack_block_serialize (ep_file_get_stack_block (file), ep_file_get_fast_serializer (file)); + ep_stack_block_clear (ep_file_get_stack_block (file)); + } + + if ((ep_event_block_get_bytes_written (ep_file_get_event_block (file)) != 0) && ((flags & EP_FILE_FLUSH_FLAGS_EVENT_BLOCK) != 0)) { + ep_event_block_serialize (ep_file_get_event_block (file), ep_file_get_fast_serializer (file)); + ep_event_block_clear (ep_file_get_event_block (file)); + } +} + +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; + } +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_file; +const char quiet_linker_empty_file_warning_eventpipe_file = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-file.h b/src/mono/mono/eventpipe/ep-file.h new file mode 100644 index 0000000000000..7c78e405e9464 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-file.h @@ -0,0 +1,164 @@ +#ifndef __EVENTPIPE_FILE_H__ +#define __EVENTPIPE_FILE_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeFile. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeFile { +#else +struct _EventPipeFile_Internal { +#endif + FastSerializableObject fast_serializable_object; + StreamWriter *stream_writer; + FastSerializer *fast_serializer; + EventPipeEventBlock *event_block; + EventPipeMetadataBlock *metadata_block; + EventPipeStackBlock *stack_block; + ep_rt_metadata_labels_hash_map_t metadata_ids; + ep_rt_stack_hash_map_t stack_hash; + uint64_t file_open_system_time; + uint64_t file_open_timestamp; + uint64_t timestamp_frequency; +#ifdef EP_CHECKED_BUILD + uint64_t last_sorted_timestamp; +#endif + uint32_t pointer_size; + uint32_t current_process_id; + uint32_t number_of_processors; + uint32_t sampling_rate_in_ns; + uint32_t stack_id_counter; + volatile uint32_t metadata_id_counter; + EventPipeSerializationFormat format; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeFile { + uint8_t _internal [sizeof (struct _EventPipeFile_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeFile *, file, StreamWriter *, stream_writer) +EP_DEFINE_GETTER(EventPipeFile *, file, FastSerializer *, fast_serializer) +EP_DEFINE_SETTER(EventPipeFile *, file, FastSerializer *, fast_serializer) +EP_DEFINE_GETTER(EventPipeFile *, file, EventPipeEventBlock *, event_block) +EP_DEFINE_GETTER(EventPipeFile *, file, EventPipeMetadataBlock *, metadata_block) +EP_DEFINE_GETTER_REF(EventPipeFile *, file, ep_rt_metadata_labels_hash_map_t *, metadata_ids) +EP_DEFINE_GETTER_REF(EventPipeFile *, file, ep_rt_stack_hash_map_t *, stack_hash) +EP_DEFINE_GETTER(EventPipeFile *, file, EventPipeStackBlock *, stack_block) +EP_DEFINE_GETTER(EventPipeFile *, file, EventPipeSerializationFormat, format) +EP_DEFINE_GETTER(EventPipeFile *, file, uint32_t, stack_id_counter); +EP_DEFINE_SETTER(EventPipeFile *, file, uint32_t, stack_id_counter); +EP_DEFINE_GETTER_REF(EventPipeFile *, file, volatile uint32_t *, metadata_id_counter) +#ifdef EP_CHECKED_BUILD +EP_DEFINE_GETTER(EventPipeFile *, file, uint64_t, last_sorted_timestamp) +EP_DEFINE_SETTER(EventPipeFile *, file, uint64_t, last_sorted_timestamp) +#endif + +EventPipeFile * +ep_file_alloc ( + StreamWriter *stream_writer, + EventPipeSerializationFormat format); + +void +ep_file_free (EventPipeFile *file); + +bool +ep_file_initialize_file (EventPipeFile *file); + +void +ep_file_write_event ( + EventPipeFile *file, + EventPipeEventInstance * event_instance, + uint64_t capture_thread_id, + uint32_t sequence_number, + bool is_sorted_event); + +void +ep_file_write_sequence_point ( + EventPipeFile *file, + EventPipeSequencePoint *sequence_point); + +void +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. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _StackHashKey { +#else +struct _StackHashKey_Internal { +#endif + uint8_t *stack_bytes; + uint32_t hash; + uint32_t stack_size_in_bytes; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _StackHashKey { + uint8_t _internal [sizeof (struct _StackHashKey_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(StackHashKey *, stack_hash_key, uint32_t, hash) + +StackHashKey * +ep_stack_hash_key_init ( + StackHashKey *key, + const EventPipeStackContents *stack_contents); + +void +ep_stack_hash_key_fini (StackHashKey *key); + +/* + * StackHashEntry. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _StackHashEntry { +#else +struct _StackHashEntry_Internal { +#endif + StackHashKey key; + uint32_t id; + // This is the first byte of StackSizeInBytes bytes of stack data + uint8_t stack_bytes[1]; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _StackHashEntry { + uint8_t _internal [sizeof (struct _StackHashEntry_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_REF(StackHashEntry *, stack_hash_entry, StackHashKey *, key) +EP_DEFINE_GETTER(StackHashEntry *, stack_hash_entry, uint32_t, id) + +StackHashEntry * +ep_stack_hash_entry_alloc ( + const EventPipeStackContents *stack_contents, + uint32_t id, + uint32_t hash); + +void +ep_stack_hash_entry_free (StackHashEntry *stack_hash_entry); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_FILE_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-internals.c b/src/mono/mono/eventpipe/ep-internals.c new file mode 100644 index 0000000000000..58bc70df0e89b --- /dev/null +++ b/src/mono/mono/eventpipe/ep-internals.c @@ -0,0 +1,242 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" + +#ifndef EP_INLINE_GETTER_SETTER +#define EP_IMPL_GETTER_SETTER +#define EP_DEFINE_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); } +#define EP_DEFINE_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); } +#define EP_DEFINE_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); } +#define EP_DEFINE_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; } +#endif + +#include "ep.h" + +// Option to include all internal source files into ep-internals.c. +#ifdef EP_INCLUDE_SOURCE_FILES +#define EP_FORCE_INCLUDE_SOURCE_FILES +#include "ep-block-internals.c" +#include "ep-buffer-manager-internals.c" +#include "ep-config-internals.c" +#include "ep-event-internals.c" +#include "ep-event-instance-internals.c" +#include "ep-event-payload-internals.c" +#include "ep-event-source-internals.c" +#include "ep-file-internals.c" +#include "ep-metadata-generator-internals.c" +#include "ep-provider-internals.c" +#include "ep-session-internals.c" +#include "ep-session-provider-internals.c" +#include "ep-stream-internals.c" +#include "ep-thread-internals.c" +#endif + +/* + * EventFilterDescriptor. + */ + +EventFilterDescriptor * +ep_event_filter_desc_alloc ( + uint64_t ptr, + uint32_t size, + uint32_t type) +{ + EventFilterDescriptor *instance = ep_rt_object_alloc (EventFilterDescriptor); + ep_raise_error_if_nok (ep_event_filter_desc_init (instance, ptr, size, type) != NULL); + +ep_on_exit: + return instance; + +ep_on_error: + ep_event_filter_desc_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +EventFilterDescriptor * +ep_event_filter_desc_init ( + EventFilterDescriptor *event_filter_desc, + uint64_t ptr, + uint32_t size, + uint32_t type) +{ + EP_ASSERT (event_filter_desc != NULL); + + event_filter_desc->ptr = ptr; + event_filter_desc->size = size; + event_filter_desc->type = type; + + return event_filter_desc; +} + +void +ep_event_filter_desc_fini (EventFilterDescriptor * filter_desc) +{ + ; +} + +void +ep_event_filter_desc_free (EventFilterDescriptor * filter_desc) +{ + ep_return_void_if_nok (filter_desc != NULL); + + ep_event_filter_desc_fini (filter_desc); + ep_rt_object_free (filter_desc); +} + +/* + * EventPipeProviderCallbackDataQueue. + */ + +EventPipeProviderCallbackDataQueue * +ep_provider_callback_data_queue_init (EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +{ + ep_return_null_if_nok (provider_callback_data_queue != NULL); + ep_rt_provider_callback_data_queue_alloc (&provider_callback_data_queue->queue); + return provider_callback_data_queue; +} + +void +ep_provider_callback_data_queue_fini (EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +{ + ep_return_void_if_nok (provider_callback_data_queue != NULL); + ep_rt_provider_callback_data_queue_free (&provider_callback_data_queue->queue); +} + +/* + * EventPipeProviderCallbackData. + */ + +EventPipeProviderCallbackData * +ep_provider_callback_data_alloc ( + const ep_char8_t *filter_data, + EventPipeCallback callback_function, + void *callback_data, + int64_t keywords, + EventPipeEventLevel provider_level, + bool enabled) +{ + EventPipeProviderCallbackData *instance = ep_rt_object_alloc (EventPipeProviderCallbackData); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ep_provider_callback_data_init ( + instance, + filter_data, + callback_function, + callback_data, + keywords, + provider_level, + enabled) != NULL); + +ep_on_exit: + return instance; + +ep_on_error: + instance = NULL; + ep_exit_error_handler (); +} + +EventPipeProviderCallbackData * +ep_provider_callback_data_alloc_copy (EventPipeProviderCallbackData *provider_callback_data_src) +{ + EventPipeProviderCallbackData *instance = ep_rt_object_alloc (EventPipeProviderCallbackData); + ep_raise_error_if_nok (instance != NULL); + + if (provider_callback_data_src) + *instance = *provider_callback_data_src; + +ep_on_exit: + return instance; + +ep_on_error: + instance = NULL; + ep_exit_error_handler (); +} + +EventPipeProviderCallbackData * +ep_provider_callback_data_init ( + EventPipeProviderCallbackData *provider_callback_data, + const ep_char8_t *filter_data, + EventPipeCallback callback_function, + void *callback_data, + int64_t keywords, + EventPipeEventLevel provider_level, + bool enabled) +{ + ep_return_null_if_nok (provider_callback_data != NULL); + + provider_callback_data->filter_data = filter_data; + provider_callback_data->callback_function = callback_function; + provider_callback_data->callback_data = callback_data; + provider_callback_data->keywords = keywords; + provider_callback_data->provider_level = provider_level; + provider_callback_data->enabled = enabled; + + return provider_callback_data; +} + +EventPipeProviderCallbackData * +ep_provider_callback_data_init_copy ( + EventPipeProviderCallbackData *provider_callback_data_dst, + EventPipeProviderCallbackData *provider_callback_data_src) +{ + ep_return_null_if_nok (provider_callback_data_dst != NULL && provider_callback_data_src != NULL); + *provider_callback_data_dst = *provider_callback_data_src; + return provider_callback_data_dst; +} + +void +ep_provider_callback_data_fini (EventPipeProviderCallbackData *provider_callback_data) +{ + ; +} + +void +ep_provider_callback_data_free (EventPipeProviderCallbackData *provider_callback_data) +{ + ep_return_void_if_nok (provider_callback_data != NULL); + ep_rt_object_free (provider_callback_data); +} + +/* + * EventPipeProviderConfiguration. + */ + +EventPipeProviderConfiguration * +ep_provider_config_init ( + EventPipeProviderConfiguration *provider_config, + const ep_char8_t *provider_name, + uint64_t keywords, + uint32_t logging_level, + const ep_char8_t *filter_data) +{ + EP_ASSERT (provider_config != NULL); + ep_return_null_if_nok (provider_name != NULL); + + provider_config->provider_name = provider_name; + provider_config->keywords = keywords; + provider_config->logging_level = logging_level; + provider_config->filter_data = filter_data; + + return provider_config; +} + +void +ep_provider_config_fini (EventPipeProviderConfiguration *provider_config) +{ + ; +} + +#endif /* ENABLE_PERFTRACING */ + +extern const char quiet_linker_empty_file_warning_eventpipe_internals; +const char quiet_linker_empty_file_warning_eventpipe_internals = 0; diff --git a/src/mono/mono/eventpipe/ep-metadata-generator-internals.c b/src/mono/mono/eventpipe/ep-metadata-generator-internals.c new file mode 100644 index 0000000000000..e2c8e76421d16 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-metadata-generator-internals.c @@ -0,0 +1,40 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * EventPipeParameterDesc. + */ + +EventPipeParameterDesc * +ep_parameter_desc_init ( + EventPipeParameterDesc *parameter_desc, + EventPipeParameterType type, + const ep_char16_t *name) +{ + EP_ASSERT (parameter_desc != NULL); + + parameter_desc->type = type; + parameter_desc->name = name; + + return parameter_desc; +} + +void +ep_parameter_desc_fini (EventPipeParameterDesc *parameter_desc) +{ + ; +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_metadata_generator_internals; +const char quiet_linker_empty_file_warning_eventpipe_metadata_generator_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-metadata-generator.c b/src/mono/mono/eventpipe/ep-metadata-generator.c new file mode 100644 index 0000000000000..4fd80356e9649 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-metadata-generator.c @@ -0,0 +1,106 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * EventPipeMetadataGenerator. + */ + +uint8_t * +ep_metadata_generator_generate_event_metadata ( + uint32_t event_id, + const ep_char16_t *event_name, + uint64_t keywords, + uint32_t version, + EventPipeEventLevel level, + EventPipeParameterDesc *params, + uint32_t params_len, + size_t *metadata_len) +{ + ep_return_null_if_nok (event_name != NULL); + ep_return_null_if_nok (params_len == 0 || params != NULL); + ep_return_null_if_nok (metadata_len != NULL); + + // The order of fields is defined in coreclr\src\mscorlib\shared\System\Diagnostics\Tracing\EventSource.cs DefineEventPipeEvents method + // eventID : 4 bytes + // eventName : (eventName.Length + 1) * 2 bytes + // keywords : 8 bytes + // eventVersion : 4 bytes + // level : 4 bytes + // parameterCount : 4 bytes + size_t event_name_len = ep_rt_utf16_string_len (event_name); + *metadata_len = 24 + ((event_name_len + 1) * sizeof (ep_char16_t)); + + // Each parameter has a 4 byte TypeCode + (parameterName.Length + 1) * 2 bytes. + for (uint32_t i = 0; i < params_len; ++i) { + EP_ASSERT (ep_parameter_desc_get_name (¶ms [i]) != NULL); + *metadata_len += (4 + ((ep_rt_utf16_string_len (ep_parameter_desc_get_name (¶ms [i])) + 1) * sizeof (ep_char16_t))); + } + + // Allocate a metadata blob. + uint8_t *buffer = ep_rt_byte_array_alloc (*metadata_len); + ep_raise_error_if_nok (buffer != NULL); + + uint8_t * current = buffer; + + // Write the event ID. + memcpy (current, &event_id, sizeof (event_id)); + current += sizeof (event_id); + + // Write the event name. + memcpy (current, event_name, (event_name_len + 1) * sizeof (ep_char16_t)); + current += (event_name_len + 1) * sizeof (ep_char16_t); + + // Write the keywords. + memcpy (current, &keywords, sizeof (keywords)); + current += sizeof (keywords); + + // Write the version. + memcpy (current, &version, sizeof (version)); + current += sizeof (version); + + // Write the level. + memcpy (current, &level, sizeof (level)); + current += sizeof (level); + + // Write the parameter count. + memcpy(current, ¶ms_len, sizeof (params_len)); + current += sizeof (params_len); + + // Write the parameter descriptions. + for (uint32_t i = 0; i < params_len; ++i) { + EventPipeParameterType const param_type = ep_parameter_desc_get_type (¶ms [i]); + const ep_char16_t *const param_name = ep_parameter_desc_get_name (¶ms [i]); + size_t const param_name_len = ep_rt_utf16_string_len (param_name); + + memcpy (current, ¶m_type, sizeof (param_type)); + current += sizeof (param_type); + + memcpy (current, param_name, (param_name_len + 1) * sizeof (ep_char16_t)); + current += (param_name_len + 1) * sizeof (ep_char16_t); + } + + EP_ASSERT (*metadata_len == (size_t)(current - buffer)); + +ep_on_exit: + return buffer; + +ep_on_error: + ep_rt_byte_array_free (buffer); + *metadata_len = 0; + + buffer = NULL; + ep_exit_error_handler (); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_metadata_generator; +const char quiet_linker_empty_file_warning_eventpipe_metadata_generator = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-metadata-generator.h b/src/mono/mono/eventpipe/ep-metadata-generator.h new file mode 100644 index 0000000000000..5f1d90a7ca27b --- /dev/null +++ b/src/mono/mono/eventpipe/ep-metadata-generator.h @@ -0,0 +1,57 @@ +#ifndef __EVENTPIPE_METADATA_GENERATOR_H__ +#define __EVENTPIPE_METADATA_GENERATOR_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeMetadataGenerator. + */ + +uint8_t * +ep_metadata_generator_generate_event_metadata ( + uint32_t event_id, + const ep_char16_t *event_name, + uint64_t keywords, + uint32_t version, + EventPipeEventLevel level, + EventPipeParameterDesc *params, + uint32_t params_len, + size_t *metadata_len); + +/* + * EventPipeParameterDesc. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeParameterDesc { +#else +struct _EventPipeParameterDesc_Internal { +#endif + EventPipeParameterType type; + const ep_char16_t *name; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeParameterDesc { + uint8_t _internal [sizeof (struct _EventPipeParameterDesc_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeParameterDesc *, parameter_desc, EventPipeParameterType, type) +EP_DEFINE_GETTER(EventPipeParameterDesc *, parameter_desc, const ep_char16_t *, name) + +EventPipeParameterDesc * +ep_parameter_desc_init ( + EventPipeParameterDesc *parameter_desc, + EventPipeParameterType type, + const ep_char16_t *name); + +void +ep_parameter_desc_fini (EventPipeParameterDesc *parameter_desc); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_METADATA_GENERATOR_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-provider-internals.c b/src/mono/mono/eventpipe/ep-provider-internals.c new file mode 100644 index 0000000000000..a93197ae76663 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-provider-internals.c @@ -0,0 +1,86 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +void +event_free_func (void *ep_event); + +/* + * EventPipeProvider. + */ + +static +void +event_free_func (void *ep_event) +{ + ep_event_free ((EventPipeEvent *)ep_event); +} + +EventPipeProvider * +ep_provider_alloc ( + EventPipeConfiguration *config, + const ep_char8_t *provider_name, + EventPipeCallback callback_func, + void *callback_data) +{ + ep_return_false_if_nok (config != NULL && provider_name != NULL); + + EventPipeProvider *instance = ep_rt_object_alloc (EventPipeProvider); + ep_raise_error_if_nok (instance != NULL); + + instance->provider_name = ep_rt_utf8_string_dup (provider_name); + ep_raise_error_if_nok (instance->provider_name != NULL); + + instance->provider_name_utf16 = ep_rt_utf8_to_utf16_string (provider_name, ep_rt_utf8_string_len (provider_name)); + ep_raise_error_if_nok (instance->provider_name_utf16 != NULL); + + instance->keywords = 0; + instance->provider_level = EP_EVENT_LEVEL_CRITICAL; + instance->callback_func = callback_func; + instance->callback_data = callback_data; + instance->config = config; + instance->delete_deferred = false; + instance->sessions = 0; + +ep_on_exit: + return instance; + +ep_on_error: + ep_provider_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_provider_free (EventPipeProvider * provider) +{ + ep_return_void_if_nok (provider != NULL); + + //TODO: CoreCLR takes the lock before manipulating the list, but since complete object is + // going away and list is only owned by provider, meaning that if we had a race related + // to the list, it will crash anyways once lock is released and list is gone. + ep_rt_event_list_free (&provider->event_list, event_free_func); + + ep_rt_utf16_string_free (provider->provider_name_utf16); + ep_rt_utf8_string_free (provider->provider_name); + ep_rt_object_free (provider); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_provider_internals; +const char quiet_linker_empty_file_warning_eventpipe_provider_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-provider.c b/src/mono/mono/eventpipe/ep-provider.c new file mode 100644 index 0000000000000..5e87cfbf7619d --- /dev/null +++ b/src/mono/mono/eventpipe/ep-provider.c @@ -0,0 +1,317 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +const EventPipeProviderCallbackData * +provider_prepare_callback_data ( + EventPipeProvider *provider, + int64_t keywords, + EventPipeEventLevel provider_level, + const ep_char8_t *filter_data, + EventPipeProviderCallbackData *provider_callback_data); + +static +void +provider_refresh_all_events (EventPipeProvider *provider); + +static +void +provider_refresh_event_state_lock_held (EventPipeEvent *ep_event); + +static +int64_t +provider_compute_event_enable_mask_lock_held ( + const EventPipeConfiguration *config, + const EventPipeProvider *provider, + int64_t keywords, + EventPipeEventLevel event_level); + +/* + * EventPipeProvider. + */ + +static +const EventPipeProviderCallbackData * +provider_prepare_callback_data ( + EventPipeProvider *provider, + int64_t keywords, + EventPipeEventLevel provider_level, + const ep_char8_t *filter_data, + EventPipeProviderCallbackData *provider_callback_data) +{ + EP_ASSERT (provider != NULL && provider_callback_data != NULL); + + return ep_provider_callback_data_init ( + provider_callback_data, + filter_data, + ep_provider_get_callback_func (provider), + ep_provider_get_callback_data (provider), + keywords, + provider_level, + (ep_provider_get_sessions (provider) != 0)); +} + +static +void +provider_refresh_all_events (EventPipeProvider *provider) +{ + EP_ASSERT (provider != NULL); + ep_rt_config_requires_lock_held (); + + const ep_rt_event_list_t *event_list = ep_provider_get_event_list_cref (provider); + EP_ASSERT (event_list != NULL); + + ep_rt_event_list_iterator_t iterator; + for (ep_rt_event_list_iterator_begin (event_list, &iterator); !ep_rt_provider_list_iterator_end (event_list, &iterator); ep_rt_provider_list_iterator_next (event_list, &iterator)) + provider_refresh_event_state_lock_held(ep_rt_event_list_iterator_value (&iterator)); + + ep_rt_config_requires_lock_held (); + return; +} + +static +void +provider_refresh_event_state_lock_held (EventPipeEvent *ep_event) +{ + ep_rt_config_requires_lock_held (); + EP_ASSERT (ep_event != NULL); + + EventPipeProvider *provider = ep_event_get_provider (ep_event); + EP_ASSERT (provider != NULL); + + EventPipeConfiguration *config = ep_provider_get_config (provider); + EP_ASSERT (config != NULL); + + int64_t enable_mask = provider_compute_event_enable_mask_lock_held (config, provider, ep_event_get_keywords (ep_event), ep_event_get_level (ep_event)); + ep_event_set_enabled_mask (ep_event, enable_mask); + + ep_rt_config_requires_lock_held (); + return; +} + +static +int64_t +provider_compute_event_enable_mask_lock_held ( + const EventPipeConfiguration *config, + const EventPipeProvider *provider, + int64_t keywords, + EventPipeEventLevel event_level) +{ + ep_rt_config_requires_lock_held (); + EP_ASSERT (provider != NULL); + + int64_t result = 0; + bool provider_enabled = ep_provider_get_enabled (provider); + for (int i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; i++) { + // Entering EventPipe lock gave us a barrier, we don't need more of them. + EventPipeSession *session = ep_volatile_load_session_without_barrier (i); + if (session) { + EventPipeSessionProvider *session_provider = ep_config_get_session_provider_lock_held (config, session, provider); + if (session_provider) { + int64_t session_keyword = ep_session_provider_get_keywords (session_provider); + EventPipeEventLevel session_level = ep_session_provider_get_logging_level (session_provider); + // The event is enabled if: + // - The provider is enabled. + // - The event keywords are unspecified in the manifest (== 0) or when masked with the enabled config are != 0. + // - The event level is LogAlways or the provider's verbosity level is set to greater than the event's verbosity level in the manifest. + bool keyword_enabled = (keywords == 0) || ((session_keyword & keywords) != 0); + bool level_enabled = ((event_level == EP_EVENT_LEVEL_LOG_ALWAYS) || (session_level >= event_level)); + if (provider_enabled && keyword_enabled && level_enabled) + result = result | ep_session_get_mask (session); + } + } + } + + ep_rt_config_requires_lock_held (); + return result; +} + +EventPipeEvent * +ep_provider_add_event ( + EventPipeProvider *provider, + uint32_t event_id, + uint64_t keywords, + uint32_t event_version, + EventPipeEventLevel level, + bool need_stack, + const uint8_t *metadata, + uint32_t metadata_len) +{ + ep_rt_config_requires_lock_not_held (); + + ep_return_null_if_nok (provider != NULL); + + EventPipeEvent *instance = ep_event_alloc ( + provider, + keywords, + event_id, + event_version, + level, + need_stack, + metadata, + metadata_len); + + ep_return_null_if_nok (instance != NULL); + + // Take the config lock before inserting a new event. + EP_CONFIG_LOCK_ENTER + ep_rt_event_list_append (ep_provider_get_event_list_ref (provider), instance); + provider_refresh_event_state_lock_held (instance); + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return instance; + +ep_on_error: + instance = NULL; + ep_exit_error_handler (); +} + +const EventPipeProviderCallbackData * +ep_provider_set_config_lock_held ( + EventPipeProvider *provider, + int64_t keywords_for_all_sessions, + EventPipeEventLevel level_for_all_sessions, + uint64_t session_mask, + int64_t keywords, + EventPipeEventLevel level, + const ep_char8_t *filter_data, + EventPipeProviderCallbackData *callback_data) +{ + ep_rt_config_requires_lock_held (); + + ep_return_null_if_nok (provider != NULL); + + EP_ASSERT ((ep_provider_get_sessions (provider) & session_mask) == 0); + ep_provider_set_sessions (provider, (ep_provider_get_sessions (provider) | session_mask)); + ep_provider_set_keywords (provider, keywords_for_all_sessions); + ep_provider_set_provider_level (provider, level_for_all_sessions); + + provider_refresh_all_events (provider); + provider_prepare_callback_data (provider, ep_provider_get_keywords (provider), ep_provider_get_provider_level (provider), filter_data, callback_data); + + ep_rt_config_requires_lock_held (); + return callback_data; +} + +const EventPipeProviderCallbackData * +ep_provider_unset_config_lock_held ( + EventPipeProvider *provider, + int64_t keywords_for_all_sessions, + EventPipeEventLevel level_for_all_sessions, + uint64_t session_mask, + int64_t keywords, + EventPipeEventLevel level, + const ep_char8_t *filter_data, + EventPipeProviderCallbackData *callback_data) +{ + ep_rt_config_requires_lock_held (); + + ep_return_null_if_nok (provider != NULL); + + EP_ASSERT ((ep_provider_get_sessions (provider) & session_mask) != 0); + if (ep_provider_get_sessions (provider) & session_mask) + ep_provider_set_sessions (provider, (ep_provider_get_sessions (provider) & ~session_mask)); + + ep_provider_set_keywords (provider, keywords_for_all_sessions); + ep_provider_set_provider_level (provider, level_for_all_sessions); + + provider_refresh_all_events (provider); + provider_prepare_callback_data (provider, ep_provider_get_keywords (provider), ep_provider_get_provider_level (provider), filter_data, callback_data); + + ep_rt_config_requires_lock_held (); + return callback_data; +} + +void +ep_provider_invoke_callback (EventPipeProviderCallbackData *provider_callback_data) +{ + // Lock should not be held when invoking callback. + ep_rt_config_requires_lock_not_held (); + + ep_return_void_if_nok (provider_callback_data != NULL); + + const ep_char8_t *filter_data = ep_provider_callback_data_get_filter_data (provider_callback_data); + EventPipeCallback callback_function = ep_provider_callback_data_get_callback_function (provider_callback_data); + bool enabled = ep_provider_callback_data_get_enabled (provider_callback_data); + int64_t keywords = ep_provider_callback_data_get_keywords (provider_callback_data); + EventPipeEventLevel provider_level = ep_provider_callback_data_get_provider_level (provider_callback_data); + void *callback_data = ep_provider_callback_data_get_callback_data (provider_callback_data); + + bool is_event_filter_desc_init = false; + EventFilterDescriptor event_filter_desc; + uint8_t *buffer = NULL; + + if (filter_data) { + // The callback is expecting that filter data to be a concatenated list + // of pairs of null terminated strings. The first member of the pair is + // the key and the second is the value. + // To convert to this format we need to convert all '=' and ';' + // characters to '\0', except when in a quoted string. + const uint32_t filter_data_len = ep_rt_utf8_string_len (filter_data); + uint32_t buffer_size = filter_data_len + 1; + + buffer = ep_rt_byte_array_alloc (buffer_size); + ep_raise_error_if_nok (buffer != NULL); + + bool is_quoted_value = false; + uint32_t j = 0; + + for (uint32_t i = 0; i < buffer_size; ++i) { + // if a value is a quoted string, leave the quotes out from the destination + // and don't replace `=` or `;` characters until leaving the quoted section + // e.g., key="a;value=";foo=bar --> { key\0a;value=\0foo\0bar\0 } + if (filter_data [i] == '"') { + is_quoted_value = !is_quoted_value; + continue; + } + buffer [j++] = ((filter_data [i] == '=' || filter_data [i] == ';') && !is_quoted_value) ? '\0' : filter_data [i]; + } + + // In case we skipped over quotes in the filter string, shrink the buffer size accordingly + if (j < filter_data_len) + buffer_size = j + 1; + + ep_event_filter_desc_init (&event_filter_desc, (uint64_t)buffer, buffer_size, 0); + is_event_filter_desc_init = true; + } + + if (callback_function && !ep_rt_process_detach ()) { + (*callback_function)( + NULL, /* provider_id */ + enabled ? 1 : 0, + (uint8_t)provider_level, + (uint64_t)keywords, + 0, /* match_all_keywords */ + is_event_filter_desc_init ? &event_filter_desc : NULL, + callback_data /* CallbackContext */); + } + +ep_on_exit: + if (is_event_filter_desc_init) + ep_event_filter_desc_fini (&event_filter_desc); + + ep_rt_byte_array_free (buffer); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_provider; +const char quiet_linker_empty_file_warning_eventpipe_provider = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-provider.h b/src/mono/mono/eventpipe/ep-provider.h new file mode 100644 index 0000000000000..3d95ec8122382 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-provider.h @@ -0,0 +1,123 @@ +#ifndef __EVENTPIPE_PROVIDER_H__ +#define __EVENTPIPE_PROVIDER_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeProvider. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeProvider { +#else +struct _EventPipeProvider_Internal { +#endif + ep_char8_t *provider_name; + ep_char16_t *provider_name_utf16; + int64_t keywords; + EventPipeEventLevel provider_level; + ep_rt_event_list_t event_list; + EventPipeCallback callback_func; + void *callback_data; + EventPipeConfiguration *config; + bool delete_deferred; + uint64_t sessions; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeProvider { + uint8_t _internal [sizeof (struct _EventPipeProvider_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeProvider *, provider, const ep_char8_t *, provider_name) +EP_DEFINE_GETTER(EventPipeProvider *, provider, const ep_char16_t *, provider_name_utf16) +EP_DEFINE_GETTER(EventPipeProvider *, provider, uint64_t, keywords) +EP_DEFINE_SETTER(EventPipeProvider *, provider, uint64_t, keywords) +EP_DEFINE_GETTER(EventPipeProvider *, provider, EventPipeEventLevel, provider_level) +EP_DEFINE_SETTER(EventPipeProvider *, provider, EventPipeEventLevel, provider_level) +EP_DEFINE_GETTER_REF(EventPipeProvider *, provider, ep_rt_event_list_t *, event_list) +EP_DEFINE_GETTER(EventPipeProvider *, provider, EventPipeCallback, callback_func) +EP_DEFINE_GETTER(EventPipeProvider *, provider, void *, callback_data) +EP_DEFINE_GETTER(EventPipeProvider *, provider, EventPipeConfiguration *, config) +EP_DEFINE_GETTER(EventPipeProvider *, provider, bool, delete_deferred) +EP_DEFINE_SETTER(EventPipeProvider *, provider, bool, delete_deferred) +EP_DEFINE_GETTER(EventPipeProvider *, provider, uint64_t, sessions) +EP_DEFINE_SETTER(EventPipeProvider *, provider, uint64_t, sessions) + +static +inline +bool +ep_provider_get_enabled (const EventPipeProvider *provider) +{ + return ep_provider_get_sessions (provider) != 0; +} + +static +inline +const ep_char8_t * +ep_provider_get_wildcard_name_utf8 (void) +{ + return "*"; +} + +static +inline +const ep_char8_t * +ep_provider_get_default_name_utf8 (void) +{ + return "Microsoft-DotNETCore-EventPipe"; +} + +EventPipeProvider * +ep_provider_alloc ( + EventPipeConfiguration *config, + const ep_char8_t *provider_name, + EventPipeCallback callback_func, + void *callback_data); + +void +ep_provider_free (EventPipeProvider * provider); + +EventPipeEvent * +ep_provider_add_event ( + EventPipeProvider *provider, + uint32_t event_id, + uint64_t keywords, + uint32_t event_version, + EventPipeEventLevel level, + bool need_stack, + const uint8_t *metadata, + uint32_t metadata_len); + +const EventPipeProviderCallbackData * +ep_provider_set_config_lock_held ( + EventPipeProvider *provider, + int64_t keywords_for_all_sessions, + EventPipeEventLevel level_for_all_sessions, + uint64_t session_mask, + int64_t keywords, + EventPipeEventLevel level, + const ep_char8_t *filter_data, + EventPipeProviderCallbackData *callback_data); + +const EventPipeProviderCallbackData * +ep_provider_unset_config_lock_held ( + EventPipeProvider *provider, + int64_t keywords_for_all_sessions, + EventPipeEventLevel level_for_all_sessions, + uint64_t session_mask, + int64_t keywords, + EventPipeEventLevel level, + const ep_char8_t *filter_data, + EventPipeProviderCallbackData *callback_data); + +void +ep_provider_invoke_callback (EventPipeProviderCallbackData *provider_callback_data); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_PROVIDER_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-rt-config-mono.h b/src/mono/mono/eventpipe/ep-rt-config-mono.h new file mode 100644 index 0000000000000..0e5eb527c2f78 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-rt-config-mono.h @@ -0,0 +1,7 @@ +#ifndef __EVENTPIPE_RT_CONFIG_MONO_H__ +#define __EVENTPIPE_RT_CONFIG_MONO_H__ + +#include +#define EP_RT_MONO_USE_STATIC_RUNTIME + +#endif /* __EVENTPIPE_RT_CONFIG_MONO_H__ */ diff --git a/src/mono/mono/eventpipe/ep-rt-config.h b/src/mono/mono/eventpipe/ep-rt-config.h new file mode 100644 index 0000000000000..5c1499ca72c89 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-rt-config.h @@ -0,0 +1,9 @@ +#ifndef __EVENTPIPE_RT_CONFIG_H__ +#define __EVENTPIPE_RT_CONFIG_H__ + +#include "ep-rt-config-mono.h" + +#define EP_INLINE_GETTER_SETTER +#define EP_INCLUDE_SOURCE_FILES + +#endif /* __EVENTPIPE_RT_CONFIG_H__ */ diff --git a/src/mono/mono/eventpipe/ep-rt-mono.c b/src/mono/mono/eventpipe/ep-rt-mono.c new file mode 100644 index 0000000000000..26d3df4a2fda6 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-rt-mono.c @@ -0,0 +1,14 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" + +#include "ep.h" + +ep_rt_spin_lock_handle_t _ep_rt_mono_config_lock = {0}; +EventPipeMonoFuncTable _ep_rt_mono_func_table = {0}; + +#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; diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h new file mode 100644 index 0000000000000..8bcb8a1b39505 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-rt-mono.h @@ -0,0 +1,1108 @@ +// Implementation of ep-rt.h targeting Mono runtime. +#ifndef __EVENTPIPE_RT_MONO_H__ +#define __EVENTPIPE_RT_MONO_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "ep.h" + +#undef EP_ARRAY_SIZE +#define EP_ARRAY_SIZE(expr) G_N_ELEMENTS(expr) + +#undef EP_INFINITE_WAIT +#define EP_INFINITE_WAIT MONO_INFINITE_WAIT + +//TODO: Should make sure block is executed in safe mode. +#undef EP_GCX_PREEMP_ENTER +#define EP_GCX_PREEMP_ENTER { + +//TODO: Should make sure block is returned back to previous mode. +#undef EP_GCX_PREEMP_EXIT +#define EP_GCX_PREEMP_EXIT } + +#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 *)) { \ + for (GSList *l = list->list; l; l = l->next) { \ + if (callback != NULL) \ + callback (l->data); \ + } \ + 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) { \ + 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; } + +#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)); } + +#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_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 *)) { \ + 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) { \ + 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) { \ + 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) { \ + 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) { \ + 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) { \ + 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) { \ + 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) { \ + g_hash_table_iter_init (&iterator->iterator, hash_map->table); \ + if (hash_map->table) \ + 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) { \ + return iterator->end; \ + } \ + static inline void ep_rt_ ## 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) { \ + return ((key_type)(gsize)iterator->key); \ + } \ + static inline value_type ep_rt_ ## 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); +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); +typedef gboolean (*ep_rt_mono_native_thread_id_equals_func)(MonoNativeThreadId, MonoNativeThreadId); +typedef mono_bool (*ep_rt_mono_runtime_is_shutting_down_func)(void); +typedef gboolean (*ep_rt_mono_rand_try_get_bytes_func)(guchar *buffer, gssize buffer_size, MonoError *error); +typedef EventPipeThread * (*ep_rt_mono_thread_get_func)(void); +typedef EventPipeThread * (*ep_rt_mono_thread_get_or_create_func)(void); +typedef void (*ep_rt_mono_thread_exited_func)(void); +typedef gpointer (*ep_rt_mono_w32file_create_func)(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs); +typedef gboolean (*ep_rt_mono_w32file_write_func)(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten, gint32 *win32error); +typedef gboolean (*ep_rt_mono_w32file_close_func)(gpointer handle); + +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; + ep_rt_mono_native_thread_id_equals_func ep_rt_mono_native_thread_id_equals; + ep_rt_mono_runtime_is_shutting_down_func ep_rt_mono_runtime_is_shutting_down; + ep_rt_mono_rand_try_get_bytes_func ep_rt_mono_rand_try_get_bytes; + ep_rt_mono_thread_get_func ep_rt_mono_thread_get; + ep_rt_mono_thread_get_or_create_func ep_rt_mono_thread_get_or_create; + ep_rt_mono_thread_exited_func ep_rt_mono_thread_exited; + ep_rt_mono_w32file_create_func ep_rt_mono_w32file_create; + ep_rt_mono_w32file_write_func ep_rt_mono_w32file_write; + ep_rt_mono_w32file_close_func ep_rt_mono_w32file_close; +} EventPipeMonoFuncTable; + +typedef EventPipeThreadHolder * (*ep_rt_thread_holder_alloc_func)(void); +typedef void (*ep_rt_thread_holder_free_func)(EventPipeThreadHolder *thread_holder); + +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; +} + +static +inline +EventPipeMonoFuncTable * +ep_rt_mono_func_table_get (void) +{ + extern EventPipeMonoFuncTable _ep_rt_mono_func_table; + return &_ep_rt_mono_func_table; +} + +MONO_PROFILER_API +void +mono_eventpipe_init ( + EventPipeMonoFuncTable *table, + ep_rt_thread_holder_alloc_func thread_holder_alloc_func, + ep_rt_thread_holder_free_func thread_holder_free_func); + +MONO_PROFILER_API +void +mono_eventpipe_fini (void); + +/* +* Helpers +*/ + +static +inline +MonoNativeThreadId +ep_rt_mono_native_thread_id_get (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return mono_native_thread_id_get (); +#else + return ep_rt_mono_func_table_get ()->ep_rt_mono_native_thread_id_get (); +#endif +} + +static +inline +gboolean +ep_rt_mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return mono_native_thread_id_equals (id1, id2); +#else + return ep_rt_mono_func_table_get ()->ep_rt_mono_native_thread_id_equals (id1, id2); +#endif +} + +static +inline +gboolean +ep_rt_mono_rand_try_get_bytes (guchar *buffer, gssize buffer_size, MonoError *error) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + extern gpointer ep_rt_mono_rand_provider; + g_assert (ep_rt_mono_rand_provider != NULL); + return mono_rand_try_get_bytes (&ep_rt_mono_rand_provider, buffer, buffer_size, error); +#else + return ep_rt_mono_func_table_get ()->ep_rt_mono_rand_try_get_bytes (buffer, buffer_size, error); +#endif +} + +static +inline +void +ep_rt_mono_thread_exited (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + extern gboolean ep_rt_mono_initialized; + extern MonoNativeTlsKey ep_rt_mono_thread_holder_tls_id; + if (ep_rt_mono_initialized) { + EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (ep_rt_mono_thread_holder_tls_id); + if (thread_holder) + ep_thread_holder_free (thread_holder); + mono_native_tls_set_value (ep_rt_mono_thread_holder_tls_id, NULL); + } +#else + ep_rt_mono_func_table_get ()->ep_rt_mono_thread_exited (); +#endif +} + +static +inline +gpointer +ep_rt_mono_w32file_create (const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return mono_w32file_create (name, fileaccess, sharemode, createmode, attrs); +#else + return ep_rt_mono_func_table_get ()->ep_rt_mono_w32file_create (name, fileaccess, sharemode, createmode, attrs); +#endif +} + +static +inline +gboolean +ep_rt_mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten, gint32 *win32error) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return mono_w32file_write (handle, buffer, numbytes, byteswritten, win32error); +#else + return ep_rt_mono_func_table_get ()->ep_rt_mono_w32file_write (handle, buffer, numbytes, byteswritten, win32error); +#endif +} + +static +inline +gboolean +ep_rt_mono_w32file_close (gpointer handle) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return mono_w32file_close (handle); +#else + return ep_rt_mono_func_table_get ()->ep_rt_mono_w32file_close (handle); +#endif +} + +/* +* Atomics. +*/ + +static +inline +uint32_t +ep_rt_atomic_inc_uint32_t (volatile uint32_t *value) +{ + return (uint32_t)mono_atomic_inc_i32 ((volatile gint32 *)value); +} + +static +inline +uint32_t +ep_rt_atomic_dec_uint32_t (volatile uint32_t *value) +{ + return (uint32_t)mono_atomic_dec_i32 ((volatile gint32 *)value); +} + +static +inline +int32_t +ep_rt_atomic_inc_int32_t (volatile int32_t *value) +{ + return (int32_t)mono_atomic_inc_i32 ((volatile gint32 *)value); +} + +static +inline +int32_t +ep_rt_atomic_dec_int32_t (volatile int32_t *value) +{ + return (int32_t)mono_atomic_dec_i32 ((volatile gint32 *)value); +} + +/* + * EventPipe. + */ + +static +EventPipeThreadHolder * +thread_holder_alloc_func (void) +{ + return ep_thread_holder_alloc (ep_thread_alloc()); +} + +static +void +thread_holder_free_func (EventPipeThreadHolder * thread_holder) +{ + ep_thread_holder_free (thread_holder); +} + +static +inline +void +ep_rt_init (void) +{ + mono_eventpipe_init (ep_rt_mono_func_table_get (), thread_holder_alloc_func, thread_holder_free_func); + ep_rt_spin_lock_alloc (ep_rt_mono_config_lock_get ()); +} + +static +inline +void +ep_rt_shutdown (void) +{ + ep_rt_spin_lock_free (ep_rt_mono_config_lock_get ()); + mono_eventpipe_fini (); +} + +static +inline +bool +ep_rt_config_aquire (void) +{ + ep_rt_spin_lock_aquire (ep_rt_mono_config_lock_get ()); + return true; +} + +static +inline +void +ep_rt_config_release (void) +{ + ep_rt_spin_lock_release (ep_rt_mono_config_lock_get ()); +} + +#ifdef EP_CHECKED_BUILD +static +inline +void +ep_rt_config_requires_lock_held (void) +{ + ep_rt_spin_lock_requires_lock_held (ep_rt_mono_config_lock_get ()); +} + +static +inline +void +ep_rt_config_requires_lock_not_held (void) +{ + ep_rt_spin_lock_requires_lock_not_held (ep_rt_mono_config_lock_get ()); +} +#endif + +/* + * EventPipeEvent. + */ + +EP_RT_DEFINE_LIST (event_list, ep_rt_event_list_t, EventPipeEvent *) +EP_RT_DEFINE_LIST_ITERATOR (event_list, ep_rt_event_list_t, ep_rt_event_list_iterator_t, EventPipeEvent *) + +/* + * EventPipeFile. + */ + +EP_RT_DEFINE_HASH_MAP(metadata_labels, ep_rt_metadata_labels_hash_map_t, EventPipeEvent *, uint32_t) +EP_RT_DEFINE_HASH_MAP(stack_hash, ep_rt_stack_hash_map_t, StackHashKey *, StackHashEntry *) +EP_RT_DEFINE_HASH_MAP_ITERATOR(stack_hash, ep_rt_stack_hash_map_t, ep_rt_stack_hash_map_iterator_t, StackHashKey *, StackHashEntry *) + +/* + * EventPipeProvider. + */ + +EP_RT_DEFINE_LIST (provider_list, ep_rt_provider_list_t, EventPipeProvider *) +EP_RT_DEFINE_LIST_ITERATOR (provider_list, ep_rt_provider_list_t, ep_rt_provider_list_iterator_t, EventPipeProvider *) + +EP_RT_DEFINE_QUEUE (provider_callback_data_queue, ep_rt_provider_callback_data_queue_t, EventPipeProviderCallbackData *) + +static +inline +int +compare_provider_name ( + gconstpointer a, + gconstpointer b) +{ + return (a) ? ep_rt_utf8_string_compare (ep_provider_get_provider_name ((EventPipeProvider *)a), (const ep_char8_t *)b) : 1; +} + +static +inline +EventPipeProvider * +ep_rt_provider_list_find_by_name ( + const ep_rt_provider_list_t *list, + const ep_char8_t *name) +{ + GSList *item = g_slist_find_custom (list->list, name, compare_provider_name); + return (item != NULL) ? (EventPipeProvider *)item->data : NULL; +} + +/* + * EventPipeSampleProfiler. + */ + +static +inline +void +ep_rt_sample_profiler_init (EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +{ + //TODO: Not supported. +} + +static +inline +void +ep_rt_sample_profiler_enable (void) +{ + //TODO: Not supported. +} + +static +inline +void +ep_rt_sample_profiler_disable (void) +{ + //TODO: Not supported. +} + +static +uint32_t +ep_rt_sample_profiler_get_sampling_rate (void) +{ + //TODO: Not supported. + return 0; +} + +/* + * EvetPipeSessionProvider. + */ + +EP_RT_DEFINE_LIST (session_provider_list, ep_rt_session_provider_list_t, EventPipeSessionProvider *) +EP_RT_DEFINE_LIST_ITERATOR (session_provider_list, ep_rt_session_provider_list_t, ep_rt_session_provider_list_iterator_t, EventPipeSessionProvider *) + +static +inline +int +compare_session_provider_name ( + gconstpointer a, + gconstpointer b) +{ + return (a) ? ep_rt_utf8_string_compare (ep_session_provider_get_provider_name ((EventPipeSessionProvider *)a), (const ep_char8_t *)b) : 1; +} + +static +inline +EventPipeSessionProvider * +ep_rt_session_provider_list_find_by_name ( + const ep_rt_session_provider_list_t *list, + const ep_char8_t *name) +{ + GSList *item = g_slist_find_custom (list->list, name, compare_provider_name); + return (item != NULL) ? (EventPipeSessionProvider *)item->data : NULL; +} + +/* + * Arrays. + */ + +static +inline +uint8_t * +ep_rt_byte_array_alloc (size_t len) +{ + return g_new(uint8_t, len); +} + +static +inline +void +ep_rt_byte_array_free (uint8_t *ptr) +{ + g_free (ptr); +} + +/* + * Event. + */ + +static +inline +void +ep_rt_wait_event_alloc (ep_rt_wait_event_handle_t *wait_event) +{ + EP_ASSERT (wait_event != NULL); + wait_event->event = g_new0 (MonoOSEvent, 1); + if (wait_event->event) + mono_os_event_init (wait_event->event, false); +} + +static +inline +void +ep_rt_wait_event_free (ep_rt_wait_event_handle_t *wait_event) +{ + if (wait_event != NULL && wait_event->event != NULL) { + mono_os_event_destroy (wait_event->event); + g_free (wait_event->event); + wait_event->event = NULL; + } +} + +static +inline +bool +ep_rt_wait_event_set (ep_rt_wait_event_handle_t *wait_event) +{ + EP_ASSERT (wait_event != NULL && wait_event->event != NULL); + mono_os_event_set (wait_event->event); + return true; +} + +static +inline +int32_t +ep_rt_wait_event_wait ( + ep_rt_wait_event_handle_t *wait_event, + uint32_t timeout, + bool alertable) +{ + EP_ASSERT (wait_event != NULL && wait_event->event != NULL); + return mono_os_event_wait_one (wait_event->event, timeout, alertable); +} + +static +inline +EventPipeWaitHandle +ep_rt_wait_event_get_wait_handle (ep_rt_wait_event_handle_t *wait_event) +{ + EP_ASSERT (wait_event != NULL); + return (EventPipeWaitHandle)wait_event; +} + +/* + * Misc. + */ + +static +inline +bool +ep_rt_process_detach (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return (bool)mono_runtime_is_shutting_down (); +#else + return (bool)ep_rt_mono_func_table_get ()->ep_rt_mono_runtime_is_shutting_down (); +#endif +} + +static +inline +void +ep_rt_create_activity_id ( + uint8_t *activity_id, + uint32_t activity_id_len) +{ + EP_ASSERT (activity_id_len == EP_ACTIVITY_ID_SIZE); + + ERROR_DECL (error); + ep_rt_mono_rand_try_get_bytes ((guchar *)activity_id, EP_ACTIVITY_ID_SIZE, error); + + const uint16_t version_mask = 0xF000; + const uint16_t random_guid_version = 0x4000; + const uint8_t clock_seq_hi_and_reserved_mask = 0xC0; + const uint8_t clock_seq_hi_and_reserved_value = 0x80; + + // Modify bits indicating the type of the GUID + uint8_t *activity_id_c = activity_id + sizeof (uint32_t) + sizeof (uint16_t); + uint8_t *activity_id_d = activity_id + sizeof (uint32_t) + sizeof (uint16_t) + sizeof (uint16_t); + + uint16_t c; + memcpy (&c, activity_id_c, sizeof (c)); + + uint8_t d; + memcpy (&d, activity_id_d, sizeof (d)); + + // time_hi_and_version + c = ((c & ~version_mask) | random_guid_version); + // clock_seq_hi_and_reserved + d = ((d & ~clock_seq_hi_and_reserved_mask) | clock_seq_hi_and_reserved_value); + + memcpy (activity_id_c, &c, sizeof (c)); + memcpy (activity_id_d, &d, sizeof (d)); +} + +/* + * Objects. + */ + +#undef ep_rt_object_alloc +#define ep_rt_object_alloc(obj_type) (g_new0 (obj_type, 1)) + +static +inline +void +ep_rt_object_free (void *ptr) +{ + g_free (ptr); +} + +/* + * PAL. + */ + +static +inline +uint32_t +ep_rt_current_process_get_id (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return (uint32_t)mono_process_current_pid (); +#else + return (uint32_t)ep_rt_mono_func_table_get ()->ep_rt_mono_process_current_pid (); +#endif +} + +static +inline +uint32_t +ep_rt_current_processor_get_number (void) +{ + return 0xFFFFFFFF; +} + +static +inline +uint32_t +ep_rt_processors_get_count (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return (uint32_t)mono_cpu_count (); +#else + return (uint32_t)ep_rt_mono_func_table_get ()->ep_rt_mono_cpu_count (); +#endif +} + +static +inline +size_t +ep_rt_current_thread_get_id (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()); +#else + return MONO_NATIVE_THREAD_ID_TO_UINT (ep_rt_mono_func_table_get ()->ep_rt_mono_native_thread_id_get ()); +#endif +} + +static +inline +uint64_t +ep_rt_perf_counter_query (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return (uint64_t)mono_100ns_ticks (); +#else + return (uint64_t)ep_rt_mono_func_table_get ()->ep_rt_mono_100ns_ticks (); +#endif +} + +static +inline +uint64_t +ep_rt_perf_frequency_query (void) +{ + //Counter uses resolution of 100ns ticks. + return 100 * 1000 * 1000; +} + +static +inline +uint64_t +ep_rt_system_time_get (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + return (uint64_t)mono_100ns_datetime (); +#else + return (uint64_t)ep_rt_mono_func_table_get ()->ep_rt_mono_100ns_datetime (); +#endif +} + +static +inline +const ep_char8_t * +ep_rt_command_line_get (void) +{ + //TODO: Implement. + return ""; +} + +static +inline +ep_rt_file_handle_t +ep_rt_file_open_write (const ep_char8_t *path) +{ + ep_char16_t *path_utf16 = ep_rt_utf8_to_utf16_string (path, -1); + ep_return_null_if_nok (path_utf16 != NULL); + + gpointer file_handle = ep_rt_mono_w32file_create (path_utf16, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, FileAttributes_Normal); + ep_rt_utf16_string_free (path_utf16); + + return file_handle; +} + +static +bool +ep_rt_file_close (ep_rt_file_handle_t file_handle) +{ + ep_return_false_if_nok (file_handle != NULL); + return ep_rt_mono_w32file_close (file_handle); +} + +static +bool +ep_rt_file_write ( + ep_rt_file_handle_t file_handle, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written) +{ + ep_return_false_if_nok (file_handle != NULL); + EP_ASSERT (buffer != NULL); + + gint32 win32_error; + bool result = ep_rt_mono_w32file_write (file_handle, buffer, bytes_to_write, bytes_written, &win32_error); + if (result) + *bytes_written = bytes_to_write; + + return result; +} + +/* +* SpinLock. +*/ + +static +inline +void +ep_rt_spin_lock_alloc (ep_rt_spin_lock_handle_t *spin_lock) +{ +#ifdef EP_CHECKED_BUILD + spin_lock->lock_is_held = false; +#endif + spin_lock->lock = g_new0 (MonoCoopMutex, 1); + if (spin_lock->lock) + mono_coop_mutex_init (spin_lock->lock); +} + +static +inline +void +ep_rt_spin_lock_free (ep_rt_spin_lock_handle_t *spin_lock) +{ + if (spin_lock && spin_lock->lock) { + mono_coop_mutex_destroy (spin_lock->lock); + g_free (spin_lock->lock); + spin_lock->lock = NULL; + } +} + +static +inline +void +ep_rt_spin_lock_aquire (ep_rt_spin_lock_handle_t *spin_lock) +{ + if (spin_lock && spin_lock->lock) { + mono_coop_mutex_lock (spin_lock->lock); +#ifdef EP_CHECKED_BUILD + spin_lock->owning_thread_id = ep_rt_mono_native_thread_id_get (); + spin_lock->lock_is_held = true; +#endif + } +} + +static +inline +void +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; +#endif + mono_coop_mutex_unlock (spin_lock->lock); + } +} + +#ifdef EP_CHECKED_BUILD +static +inline +void +ep_rt_spin_lock_requires_lock_held (const ep_rt_spin_lock_handle_t *spin_lock) +{ + g_assert (spin_lock->lock_is_held && ep_rt_mono_native_thread_id_equals (spin_lock->owning_thread_id, ep_rt_mono_native_thread_id_get ())); +} + +static +inline +void +ep_rt_spin_lock_requires_lock_not_held (const ep_rt_spin_lock_handle_t *spin_lock) +{ + g_assert (!spin_lock->lock_is_held || (spin_lock->lock_is_held && !ep_rt_mono_native_thread_id_equals (spin_lock->owning_thread_id, ep_rt_mono_native_thread_id_get ()))); +} +#endif + +/* + * String. + */ + +static +inline +size_t +ep_rt_utf8_string_len (const ep_char8_t *str) +{ + return g_utf8_strlen ((const gchar *)str, -1); +} + +static +inline +int +ep_rt_utf8_string_compare ( + const ep_char8_t *str1, + const ep_char8_t *str2) +{ + return strcmp ((const char *)str1, (const char *)str2); +} + +static +inline +ep_char16_t * +ep_rt_utf8_to_utf16_string ( + const ep_char8_t *str, + size_t len) +{ + return (ep_char16_t *)(g_utf8_to_utf16 ((const gchar *)str, len, NULL, NULL, NULL)); +} + +static +inline +ep_char8_t * +ep_rt_utf8_string_dup (const ep_char8_t *str) +{ + return g_strdup (str); +} + +static +inline +void +ep_rt_utf8_string_free (ep_char8_t *str) +{ + g_free (str); +} + +static +inline +size_t +ep_rt_utf16_string_len (const ep_char16_t *str) +{ + return g_utf16_len ((const gunichar2 *)str); +} + +static +inline +ep_char8_t * +ep_rt_utf16_to_utf8_string ( + const ep_char16_t *str, + size_t len) +{ + return g_utf16_to_utf8 ((const gunichar2 *)str, len, NULL, NULL, NULL); +} + +static +inline +void +ep_rt_utf16_string_free (ep_char16_t *str) +{ + g_free (str); +} + +static +inline +const ep_char8_t * +ep_rt_managed_command_line_get (void) +{ + //TODO: Implement. + return ""; +} + +/* + * Thread. + */ +static +inline +void +ep_rt_thread_setup (void) +{ + //TODO: Is this needed on Mono runtime? Looks like a thread attach, making sure thread is attached to runtime. +} + +static +inline +EventPipeThread * +ep_rt_thread_get (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + extern MonoNativeTlsKey ep_rt_mono_thread_holder_tls_id; + EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (ep_rt_mono_thread_holder_tls_id); + return thread_holder ? ep_thread_holder_get_thread (thread_holder) : NULL; +#else + return ep_rt_mono_func_table_get ()->ep_rt_mono_thread_get (); +#endif +} + +static +inline +EventPipeThread * +ep_rt_thread_get_or_create (void) +{ +#ifdef EP_RT_MONO_USE_STATIC_RUNTIME + extern MonoNativeTlsKey ep_rt_mono_thread_holder_tls_id; + EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (ep_rt_mono_thread_holder_tls_id); + if (!thread_holder) { + thread_holder = thread_holder_alloc_func (); + mono_native_tls_set_value (ep_rt_mono_thread_holder_tls_id, thread_holder); + } + return ep_thread_holder_get_thread (thread_holder); +#else + return ep_rt_mono_func_table_get ()->ep_rt_mono_thread_get_or_create (); +#endif +} + +/* + * ThreadSequenceNumberMap. + */ + +EP_RT_DEFINE_HASH_MAP(thread_sequence_number_map, ep_rt_thread_sequence_number_hash_map_t, EventPipeThreadSessionState *, uint32_t) +EP_RT_DEFINE_HASH_MAP_ITERATOR(thread_sequence_number_map, ep_rt_thread_sequence_number_hash_map_t, ep_rt_thread_sequence_number_hash_map_iterator_t, EventPipeThreadSessionState *, uint32_t) + +/* + * Volatile. + */ + +static +inline +uint32_t +ep_rt_volatile_load_uint32_t (const volatile uint32_t *ptr) +{ + return mono_atomic_load_i32 ((volatile gint32 *)ptr); +} + +static +inline +uint32_t +ep_rt_volatile_load_uint32_t_without_barrier (const volatile uint32_t *ptr) +{ + uint32_t value = *ptr; + return value; +} + +static +inline +void +ep_rt_volatile_store_uint32_t ( + volatile uint32_t *ptr, + uint32_t value) +{ + mono_atomic_store_i32 ((volatile gint32 *)ptr, (gint32)value); +} + +static +inline +void +ep_rt_volatile_store_uint32_t_without_barrier ( + volatile uint32_t *ptr, + uint32_t value) +{ + *ptr = value; +} + +static +inline +uint64_t +ep_rt_volatile_load_uint64_t (const volatile uint64_t *ptr) +{ + return mono_atomic_load_i64 ((volatile gint64 *)ptr); +} + +static +inline +uint64_t +ep_rt_volatile_load_uint64_t_without_barrier (const volatile uint64_t *ptr) +{ + uint64_t value = *ptr; + return value; +} + +static +inline +void +ep_rt_volatile_store_uint64_t ( + volatile uint64_t *ptr, + uint64_t value) +{ + mono_atomic_store_i64 ((volatile gint64 *)ptr, (gint64)value); +} + +static +inline +void +ep_rt_volatile_store_uint64_t_without_barrier ( + volatile uint64_t *ptr, + uint64_t value) +{ + *ptr = value; +} + +static +inline +void * +ep_rt_volatile_load_ptr (volatile void **ptr) +{ + return mono_atomic_load_ptr ((volatile gpointer *)ptr); +} + +static +inline +void * +ep_rt_volatile_load_ptr_without_barrier (volatile void **ptr) +{ + void *value = (void *)(*ptr); + return value; +} + +static +inline +void +ep_rt_volatile_store_ptr ( + volatile void **ptr, + void *value) +{ + mono_atomic_store_ptr ((volatile gpointer *)ptr, (gpointer)value); +} + +static +inline +void +ep_rt_volatile_store_ptr_without_barrier ( + volatile void **ptr, + void *value) +{ + *ptr = value; +} + +#endif /* ENABLE_PERFTRACING */ +#endif /* __EVENTPIPE_RT_MONO_H__ */ diff --git a/src/mono/mono/eventpipe/ep-rt-types-mono.h b/src/mono/mono/eventpipe/ep-rt-types-mono.h new file mode 100644 index 0000000000000..266f1ee25cd1f --- /dev/null +++ b/src/mono/mono/eventpipe/ep-rt-types-mono.h @@ -0,0 +1,96 @@ +// Implementation of ep-rt-types.h targeting Mono runtime. +#ifndef __EVENTPIPE_RT_TYPES_MONO_H__ +#define __EVENTPIPE_RT_TYPES_MONO_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include +#include +#include +#include + +//#ifdef ENABLE_CHECKED_BUILD +#define EP_CHECKED_BUILD +//#endif + +#undef EP_ASSERT +//#define EP_ASSERT(expr) g_assert_checked(expr) +#define EP_ASSERT(expr) g_assert(expr) + +#undef EP_LIKELY +#define EP_LIKELY(expr) G_LIKELY(expr) + +#undef EP_UNLIKELY +#define EP_UNLIKELY(expr) G_UNLIKELY(expr) + +struct _rt_mono_list_internal_t { + GSList *list; +}; + +struct _rt_mono_list_iterator_internal_t { + GSList *iterator; +}; + +typedef struct _rt_mono_list_internal_t ep_rt_provider_list_t; +typedef struct _rt_mono_list_iterator_internal_t ep_rt_provider_list_iterator_t; + +typedef struct _rt_mono_list_internal_t ep_rt_event_list_t; +typedef struct _rt_mono_list_iterator_internal_t ep_rt_event_list_iterator_t; + +typedef struct _rt_mono_list_internal_t ep_rt_session_provider_list_t; +typedef struct _rt_mono_list_iterator_internal_t ep_rt_session_provider_list_iterator_t; + +struct _rt_mono_queue_internal_t { + GQueue *queue; +}; + +typedef struct _rt_mono_queue_internal_t ep_rt_provider_callback_data_queue_t; +typedef struct _rt_mono_queue_iterator_internal_t ep_rt_provider_callback_data_queue_iterator_t; + +struct _rt_mono_table_internal_t { + GHashTable *table; + uint32_t count; +}; + +struct _rt_mono_table_iterator_internal_t { + GHashTableIter iterator; + gpointer key; + gpointer value; + bool end; +}; + +typedef struct _rt_mono_table_internal_t ep_rt_metadata_labels_hash_map_t; +typedef struct _rt_mono_iterator_table_internal_t ep_rt_metadata_labels_hash_map_iterator_t; + +typedef struct _rt_mono_table_internal_t ep_rt_stack_hash_map_t; +typedef struct _rt_mono_table_iterator_internal_t ep_rt_stack_hash_map_iterator_t; + +typedef struct _rt_mono_table_internal_t ep_rt_thread_sequence_number_hash_map_t; +typedef struct _rt_mono_table_iterator_internal_t ep_rt_thread_sequence_number_hash_map_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; + +struct _rt_mono_event_internal_t { + MonoOSEvent *event; +}; + +typedef struct _rt_mono_event_internal_t ep_rt_wait_event_handle_t; + +struct _rt_mono_lock_internal_t { + MonoCoopMutex *lock; +#ifdef EP_CHECKED_BUILD + MonoNativeThreadId owning_thread_id; + bool lock_is_held; +#endif +}; + +typedef struct _rt_mono_lock_internal_t ep_rt_lock_handle_t; +typedef ep_rt_lock_handle_t ep_rt_spin_lock_handle_t; + +#endif /* ENABLE_PERFTRACING */ +#endif /* __EVENTPIPE_RT_TYPES_MONO_H__ */ diff --git a/src/mono/mono/eventpipe/ep-rt-types.h b/src/mono/mono/eventpipe/ep-rt-types.h new file mode 100644 index 0000000000000..cb0a9af82fa6d --- /dev/null +++ b/src/mono/mono/eventpipe/ep-rt-types.h @@ -0,0 +1,22 @@ +#ifndef __EVENTPIPE_RT_TYPES_H__ +#define __EVENTPIPE_RT_TYPES_H__ + +#define EP_ASSERT(expr) ep_rt_redefine +#define EP_LIKELY(expr) ep_rt_redefine +#define EP_UNLIKELY(expr) ep_rt_redefine + +/* + * ErrorHandling. + */ + +#define ep_raise_error_if_nok(expr) do { if (EP_UNLIKELY(!(expr))) goto ep_on_error; } while (0) +#define ep_raise_error() do { goto ep_on_error; } while (0) +#define ep_exit_error_handler() do { goto ep_on_exit; } while (0) +#define ep_return_null_if_nok(expr) do { if (EP_UNLIKELY(!(expr))) return NULL; } while (0) +#define ep_return_void_if_nok(expr) do { if (EP_UNLIKELY(!(expr))) return; } while (0) +#define ep_return_false_if_nok(expr) do { if (EP_UNLIKELY(!(expr))) return false; } while (0) +#define ep_return_zero_if_nok(expr) do { if (EP_UNLIKELY(!(expr))) return 0; } while (0) + +#include "ep-rt-types-mono.h" + +#endif /* __EVENTPIPE_RT_TYPES_H__ */ diff --git a/src/mono/mono/eventpipe/ep-rt.h b/src/mono/mono/eventpipe/ep-rt.h new file mode 100644 index 0000000000000..0bcdbc8cd3624 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-rt.h @@ -0,0 +1,482 @@ +#ifndef __EVENTPIPE_RT_H__ +#define __EVENTPIPE_RT_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +#define EP_ARRAY_SIZE(expr) ep_rt_redefine +#define EP_INFINITE_WAIT ep_rt_redefine + +#define EP_GCX_PREEMP_ENTER ep_rt_redefine +#define EP_GCX_PREEMP_EXIT ep_rt_redefine + +#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); + +#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); + +#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_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); + +#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); + +/* +* Atomics. +*/ + +static +uint32_t +ep_rt_atomic_inc_uint32_t (volatile uint32_t *value); + +static +uint32_t +ep_rt_atomic_dec_uint32_t (volatile uint32_t *value); + +static +int32_t +ep_rt_atomic_inc_int32_t (volatile int32_t *value); + +static +int32_t +ep_rt_atomic_dec_int32_t (volatile int32_t *value); + +/* + * EventPipe. + */ + +static +void +ep_rt_init (void); + +static +void +ep_rt_shutdown (void); + +static +bool +ep_rt_config_aquire (void); + +static +void +ep_rt_config_release (void); + +#ifdef EP_CHECKED_BUILD +static +void +ep_rt_config_requires_lock_held (void); + +static +void +ep_rt_config_requires_lock_not_held (void); +#else +#define ep_rt_config_requires_lock_held() +#define ep_rt_config_requires_lock_not_held() +#endif + +/* + * EventPipeEvent. + */ + +EP_RT_DECLARE_LIST (event_list, ep_rt_event_list_t, EventPipeEvent *) +EP_RT_DECLARE_LIST_ITERATOR (event_list, ep_rt_event_list_t, ep_rt_event_list_iterator_t, EventPipeEvent *) + +/* + * EventPipeFile. + */ + +EP_RT_DECLARE_HASH_MAP(metadata_labels, ep_rt_metadata_labels_hash_map_t, EventPipeEvent *, uint32_t) +EP_RT_DECLARE_HASH_MAP(stack_hash, ep_rt_stack_hash_map_t, StackHashKey *, StackHashEntry *) +EP_RT_DECLARE_HASH_MAP_ITERATOR(stack_hash, ep_rt_stack_hash_map_t, ep_rt_stack_hash_map_iterator_t, StackHashKey *, StackHashEntry *) + +/* + * EventPipeProvider. + */ + +EP_RT_DECLARE_LIST (provider_list, ep_rt_provider_list_t, EventPipeProvider *) +EP_RT_DECLARE_LIST_ITERATOR (provider_list, ep_rt_provider_list_t, ep_rt_provider_list_iterator_t, EventPipeProvider *) + +EP_RT_DECLARE_QUEUE (provider_callback_data_queue, ep_rt_provider_callback_data_queue_t, EventPipeProviderCallbackData *) + +static +EventPipeProvider * +ep_rt_provider_list_find_by_name ( + const ep_rt_provider_list_t *list, + const ep_char8_t *name); + +/* + * EventPipeSampleProfiler. + */ + +static +void +ep_rt_sample_profiler_init (EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + +static +void +ep_rt_sample_profiler_enable (void); + +static +void +ep_rt_sample_profiler_disable (void); + +static +uint32_t +ep_rt_sample_profiler_get_sampling_rate (void); + +/* + * EventPipeSessionProvider. + */ + +EP_RT_DECLARE_LIST (session_provider_list, ep_rt_session_provider_list_t, EventPipeSessionProvider *) +EP_RT_DECLARE_LIST_ITERATOR (session_provider_list, ep_rt_session_provider_list_t, ep_rt_session_provider_list_iterator_t, EventPipeSessionProvider *) + +static +EventPipeSessionProvider * +ep_rt_session_provider_list_find_by_name ( + const ep_rt_session_provider_list_t *list, + const ep_char8_t *name); + +/* + * Arrays. + */ + +static +uint8_t * +ep_rt_byte_array_alloc (size_t len); + +static +void +ep_rt_byte_array_free (uint8_t *ptr); + +/* + * Event. + */ + +static +void +ep_rt_wait_event_alloc (ep_rt_wait_event_handle_t *wait_event); + +static +void +ep_rt_wait_event_free (ep_rt_wait_event_handle_t *wait_event); + +static +bool +ep_rt_wait_event_set (ep_rt_wait_event_handle_t *wait_event); + +static +int32_t +ep_rt_wait_event_wait ( + ep_rt_wait_event_handle_t *wait_event, + uint32_t timeout, + bool alertable); + +static +EventPipeWaitHandle +ep_rt_wait_event_get_handle (ep_rt_wait_event_handle_t *wait_event); + +/* + * Misc. + */ + +static +bool +ep_rt_process_detach (void); + +static +void +ep_rt_create_activity_id ( + uint8_t *activity_id, + uint32_t activity_id_len); + +/* + * Objects. + */ + +#define ep_rt_object_alloc(obj_type) ep_rt_redefine + +static +void +ep_rt_object_free (void *ptr); + +/* + * PAL. + */ + +static +uint32_t +ep_rt_current_process_get_id (void); + +static +uint32_t +ep_rt_current_processor_get_number (void); + +static +uint32_t +ep_rt_processors_get_count (void); + +static +size_t +ep_rt_current_thread_get_id (void); + +static +uint64_t +ep_rt_perf_counter_query (void); + +static +uint64_t +ep_rt_perf_frequency_query (void); + +static +uint64_t +ep_rt_system_time_get (void); + +static +const ep_char8_t * +ep_rt_command_line_get (void); + +static +ep_rt_file_handle_t +ep_rt_file_open_write (const ep_char8_t *path); + +static +bool +ep_rt_file_close (ep_rt_file_handle_t file_handle); + +static +bool +ep_rt_file_write ( + ep_rt_file_handle_t file_handle, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written); + +/* +* SpinLock. +*/ + +static +void +ep_rt_spin_lock_alloc (ep_rt_spin_lock_handle_t *spin_lock); + +static +void +ep_rt_spin_lock_free (ep_rt_spin_lock_handle_t *spin_lock); + +static +void +ep_rt_spin_lock_aquire (ep_rt_spin_lock_handle_t *spin_lock); + +static +void +ep_rt_spin_lock_release (ep_rt_spin_lock_handle_t *spin_lock); + +#ifdef EP_CHECKED_BUILD +static +void +ep_rt_spin_lock_requires_lock_held (const ep_rt_spin_lock_handle_t *spin_lock); + +static +void +ep_rt_spin_lock_requires_lock_not_held (const ep_rt_spin_lock_handle_t *spin_lock); +#else +#define ep_rt_spin_lock_requires_lock_held(spin_lock) +#define ep_rt_spin_lock_requires_lock_not_held(spin_lock) +#endif + +/* + * String. + */ + +static +size_t +ep_rt_utf8_string_len (const ep_char8_t *str); + +static +int +ep_rt_utf8_string_compare ( + const ep_char8_t *str1, + const ep_char8_t *str2); + +static +ep_char8_t * +ep_rt_utf8_string_dup (const ep_char8_t *str); + +static +ep_char16_t * +ep_rt_utf8_to_utf16_string ( + const ep_char8_t *str, + size_t len); + +static +void +ep_rt_utf8_string_free (ep_char8_t *str); + +static +size_t +ep_rt_utf16_string_len (const ep_char16_t *str); + +static +ep_char8_t * +ep_rt_utf16_to_utf8_string ( + const ep_char16_t *str, + size_t len); + +static +void +ep_rt_utf16_string_free (ep_char16_t *str); + +static +const ep_char8_t * +ep_rt_managed_command_line_get (void); + +/* + * Thread. + */ +static +void +ep_rt_thread_setup (void); + +static +EventPipeThread * +ep_rt_thread_get (void); + +static +EventPipeThread * +ep_rt_thread_get_or_create (void); + + +/* + * ThreadSequenceNumberMap. + */ + +EP_RT_DECLARE_HASH_MAP(thread_sequence_number_map, ep_rt_thread_sequence_number_hash_map_t, EventPipeThreadSessionState *, uint32_t) +EP_RT_DECLARE_HASH_MAP_ITERATOR(thread_sequence_number_map, ep_rt_thread_sequence_number_hash_map_t, ep_rt_thread_sequence_number_hash_map_iterator_t, EventPipeThreadSessionState *, uint32_t) + + +/* + * Volatile. + */ + +static +uint32_t +ep_rt_volatile_load_uint32_t (const volatile uint32_t *ptr); + +static +uint32_t +ep_rt_volatile_load_uint32_t_without_barrier (const volatile uint32_t *ptr); + +static +void +ep_rt_volatile_store_uint32_t ( + volatile uint32_t *ptr, + uint32_t value); + +static +void +ep_rt_volatile_store_uint32_t_without_barrier ( + volatile uint32_t *ptr, + uint32_t value); + +static +uint64_t +ep_rt_volatile_load_uint64_t (const volatile uint64_t *ptr); + +static +uint64_t +ep_rt_volatile_load_uint64_t_without_barrier (const volatile uint64_t *ptr); + +static +void +ep_rt_volatile_store_uint64_t ( + volatile uint64_t *ptr, + uint64_t value); + +static +void +ep_rt_volatile_store_uint64_t_without_barrier ( + volatile uint64_t *ptr, + uint64_t value); + +static +void * +ep_rt_volatile_load_ptr (volatile void **ptr); + +static +void * +ep_rt_volatile_load_ptr_without_barrier (volatile void **ptr); + +static +void +ep_rt_volatile_store_ptr ( + volatile void **ptr, + void *value); + +static +void +ep_rt_volatile_store_ptr_without_barrier ( + volatile void **ptr, + void *value); + +/* + * Enter/Exit config lock helper used with error handling macros. + */ + +#define EP_CONFIG_LOCK_ENTER \ +{ \ + ep_rt_config_requires_lock_not_held (); \ + bool _owns_lock = ep_rt_config_aquire (); \ + bool _no_error = false; \ + if (EP_UNLIKELY((!_owns_lock))) \ + goto _ep_on_lock_exit; + +#define EP_CONFIG_LOCK_EXIT \ + _no_error = true; \ +_ep_on_lock_exit: \ + if (EP_UNLIKELY((!_owns_lock))) \ + goto ep_on_error; \ + ep_rt_config_requires_lock_held (); \ + ep_rt_config_release (); \ + if (EP_UNLIKELY((!_no_error))) \ + goto ep_on_error; \ + ep_rt_config_requires_lock_not_held (); \ +} + +#define ep_raise_error_if_nok_holding_lock(expr) do { if (EP_UNLIKELY(!(expr))) goto _ep_on_lock_exit; } while (0) +#define ep_raise_error_holding_lock() do { goto _ep_on_lock_exit; } while (0) + +#include "ep-rt-mono.h" + +#endif /* ENABLE_PERFTRACING */ +#endif /* __EVENTPIPE_RT_H__ */ diff --git a/src/mono/mono/eventpipe/ep-session-internals.c b/src/mono/mono/eventpipe/ep-session-internals.c new file mode 100644 index 0000000000000..3ed2a1101cc22 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-session-internals.c @@ -0,0 +1,123 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * EventPipeSession. + */ + +EventPipeSession * +ep_session_alloc ( + uint32_t index, + const ep_char8_t *output_path, + IpcStream *stream, + EventPipeSessionType session_type, + EventPipeSerializationFormat format, + bool rundown_requested, + uint32_t circular_buffer_size_in_mb, + const EventPipeProviderConfiguration *providers, + uint32_t providers_len, + bool rundown_enabled) +{ + ep_rt_config_requires_lock_held (); + + ep_return_null_if_nok (index < EP_MAX_NUMBER_OF_SESSIONS && format < EP_SERIALIZATION_FORMAT_COUNT && circular_buffer_size_in_mb > 0 && providers_len > 0 && providers != NULL); + + FileStreamWriter *file_stream_writer = NULL; + IpcStreamWriter *ipc_stream_writer = NULL; + + EventPipeSession *instance = ep_rt_object_alloc (EventPipeSession); + ep_raise_error_if_nok (instance != NULL); + + instance->providers = ep_session_provider_list_alloc (providers, providers_len); + ep_raise_error_if_nok (instance->providers != NULL); + + instance->index = index; + instance->rundown_enabled = rundown_enabled ? 1 : 0; + instance->session_type = session_type; + instance->format = format; + instance->rundown_requested = rundown_requested; + + size_t sequence_point_alloc_budget = 0; + + // Hard coded 10MB for now, we'll probably want to make + // this configurable later. + if (instance->session_type != EP_SESSION_TYPE_LISTENER && instance->format >= EP_SERIALIZATION_FORMAT_NETTRACE_V4) { + sequence_point_alloc_budget = 10 * 1024 * 1024; + } + + instance->buffer_manager = ep_buffer_manager_alloc (instance, ((size_t)circular_buffer_size_in_mb) << 20, sequence_point_alloc_budget); + ep_raise_error_if_nok (instance->buffer_manager != NULL); + + // Create the event pipe file. + // A NULL output path means that we should not write the results to a file. + // This is used in the EventListener case. + switch (session_type) { + case EP_SESSION_TYPE_FILE : + if (output_path) { + file_stream_writer = ep_file_stream_writer_alloc (output_path); + instance->file = ep_file_alloc (ep_file_stream_writer_get_stream_writer_ref (file_stream_writer), format); + ep_raise_error_if_nok (instance->file != NULL); + file_stream_writer = NULL; + } + break; + + case EP_SESSION_TYPE_IPCSTREAM: + ipc_stream_writer = ep_ipc_stream_writer_alloc ((uint64_t)instance, stream); + ep_raise_error_if_nok (ipc_stream_writer != NULL); + instance->file = ep_file_alloc (ep_ipc_stream_writer_get_stream_writer_ref (ipc_stream_writer), format); + ep_raise_error_if_nok (instance->file != NULL); + ipc_stream_writer = NULL; + break; + + default: + 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); + +ep_on_exit: + ep_rt_config_requires_lock_held (); + return instance; + +ep_on_error: + ep_file_stream_writer_free (file_stream_writer); + ep_ipc_stream_writer_free (ipc_stream_writer); + ep_session_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_session_free (EventPipeSession *session) +{ + ep_return_void_if_nok (session != NULL); + + EP_ASSERT (ep_session_get_ipc_streaming_enabled (session) == false); + + ep_rt_wait_event_free (&session->rt_thread_shutdown_event); + + ep_session_provider_list_free (session->providers); + + ep_buffer_manager_free (session->buffer_manager); + ep_file_free (session->file); + + ep_rt_object_free (session); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_session_internals; +const char quiet_linker_empty_file_warning_eventpipe_session_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-session-provider-internals.c b/src/mono/mono/eventpipe/ep-session-provider-internals.c new file mode 100644 index 0000000000000..5c71bb7ef7af3 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-session-provider-internals.c @@ -0,0 +1,135 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +void +session_provider_free_func (void *session_provider); + +/* + * EventPipeSessionProvider. + */ + +static +void +session_provider_free_func (void *session_provider) +{ + ep_session_provider_free ((EventPipeSessionProvider *)session_provider); +} + +EventPipeSessionProvider * +ep_session_provider_alloc ( + const ep_char8_t *provider_name, + uint64_t keywords, + EventPipeEventLevel logging_level, + const ep_char8_t *filter_data) +{ + EventPipeSessionProvider *instance = ep_rt_object_alloc (EventPipeSessionProvider); + ep_raise_error_if_nok (instance != NULL); + + if (provider_name) { + instance->provider_name = ep_rt_utf8_string_dup (provider_name); + ep_raise_error_if_nok (instance->provider_name != NULL); + } + + if (filter_data) { + instance->filter_data = ep_rt_utf8_string_dup (filter_data); + ep_raise_error_if_nok (instance->filter_data != NULL); + } + + instance->keywords = keywords; + instance->logging_level = logging_level; + +ep_on_exit: + return instance; + +ep_on_error: + ep_session_provider_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_session_provider_free (EventPipeSessionProvider * session_provider) +{ + ep_return_void_if_nok (session_provider != NULL); + + ep_rt_utf8_string_free (session_provider->filter_data); + ep_rt_utf8_string_free (session_provider->provider_name); + ep_rt_object_free (session_provider); +} + +/* + * EventPipeSessionProviderList. + */ + +EventPipeSessionProviderList * +ep_session_provider_list_alloc ( + const EventPipeProviderConfiguration *configs, + uint32_t configs_len) +{ + ep_return_null_if_nok ((configs_len == 0) || (configs_len > 0 && configs != NULL)); + + EventPipeSessionProviderList *instance = ep_rt_object_alloc (EventPipeSessionProviderList); + ep_raise_error_if_nok (instance != NULL); + + instance->catch_all_provider = NULL; + + for (uint32_t i = 0; i < configs_len; ++i) { + const EventPipeProviderConfiguration *config = &configs [i]; + EP_ASSERT (config != NULL); + + // Enable all events if the provider name == '*', all keywords are on and the requested level == verbose. + if ((ep_rt_utf8_string_compare(ep_provider_get_wildcard_name_utf8 (), config->provider_name) == 0) && + (config->keywords == 0xFFFFFFFFFFFFFFFF) && + ((config->logging_level == EP_EVENT_LEVEL_VERBOSE) && (instance->catch_all_provider == NULL))) { + instance->catch_all_provider = ep_session_provider_alloc (NULL, 0xFFFFFFFFFFFFFFFF, EP_EVENT_LEVEL_VERBOSE, NULL ); + ep_raise_error_if_nok (instance->catch_all_provider != NULL); + } + else { + EventPipeSessionProvider * session_provider = ep_session_provider_alloc ( + config->provider_name, + config->keywords, + config->logging_level, + config->filter_data); + ep_rt_session_provider_list_append (&instance->providers, session_provider); + } + } + +ep_on_exit: + return instance; + +ep_on_error: + ep_session_provider_list_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_session_provider_list_free (EventPipeSessionProviderList *session_provider_list) +{ + ep_return_void_if_nok (session_provider_list != NULL); + + ep_rt_session_provider_list_free (&session_provider_list->providers, session_provider_free_func); + ep_session_provider_free (session_provider_list->catch_all_provider); + ep_rt_object_free (session_provider_list); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_session_provider_internals; +const char quiet_linker_empty_file_warning_eventpipe_session_provider_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-session-provider.c b/src/mono/mono/eventpipe/ep-session-provider.c new file mode 100644 index 0000000000000..08b6db7f5a0c3 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-session-provider.c @@ -0,0 +1,60 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +void +session_provider_free_func (void *session_provider); + +/* + * EventPipeSessionProvider. + */ + +static +void +session_provider_free_func (void *session_provider) +{ + ep_session_provider_free ((EventPipeSessionProvider *)session_provider); +} + +/* + * EventPipeSessionProviderList. + */ + +void +ep_session_provider_list_clear (EventPipeSessionProviderList *session_provider_list) +{ + ep_return_void_if_nok (session_provider_list != NULL); + ep_rt_session_provider_list_clear (ep_session_provider_list_get_providers_ref (session_provider_list), session_provider_free_func); +} + +bool +ep_session_provider_list_is_empty (const EventPipeSessionProviderList *session_provider_list) +{ + return (ep_rt_provider_list_is_empty (ep_session_provider_list_get_providers_cref (session_provider_list)) && ep_session_provider_list_get_catch_all_provider (session_provider_list) == NULL); +} + +void +ep_session_provider_list_add_session_provider ( + EventPipeSessionProviderList *session_provider_list, + EventPipeSessionProvider *session_provider) +{ + ep_return_void_if_nok (session_provider != NULL); + ep_rt_session_provider_list_append (ep_session_provider_list_get_providers_ref (session_provider_list), session_provider); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_session_provider; +const char quiet_linker_empty_file_warning_eventpipe_session_provider = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-session-provider.h b/src/mono/mono/eventpipe/ep-session-provider.h new file mode 100644 index 0000000000000..7a2812f09f2e5 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-session-provider.h @@ -0,0 +1,86 @@ +#ifndef __EVENTPIPE_SESSION_PROVIDER_H__ +#define __EVENTPIPE_SESSION_PROVIDER_H__ + +#include "ep-rt-config.h" + +#ifdef ENABLE_PERFTRACING +#include "ep-types.h" + +/* + * EventPipeSessionProvider. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeSessionProvider { +#else +struct _EventPipeSessionProvider_Internal { +#endif + ep_char8_t *provider_name; + uint64_t keywords; + EventPipeEventLevel logging_level; + ep_char8_t *filter_data; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeSessionProvider { + uint8_t _internal [sizeof (struct _EventPipeSessionProvider_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeSessionProvider *, session_provider, const ep_char8_t *, provider_name) +EP_DEFINE_GETTER(EventPipeSessionProvider *, session_provider, uint64_t, keywords) +EP_DEFINE_GETTER(EventPipeSessionProvider *, session_provider, EventPipeEventLevel, logging_level) +EP_DEFINE_GETTER(EventPipeSessionProvider *, session_provider, const ep_char8_t *, filter_data) + +EventPipeSessionProvider * +ep_session_provider_alloc ( + const ep_char8_t *provider_name, + uint64_t keywords, + EventPipeEventLevel logging_level, + const ep_char8_t *filter_data); + +void +ep_session_provider_free (EventPipeSessionProvider * session_provider); + +/* +* EventPipeSessionProviderList. + */ +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeSessionProviderList { +#else +struct _EventPipeSessionProviderList_Internal { +#endif + ep_rt_session_provider_list_t providers; + EventPipeSessionProvider *catch_all_provider; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeSessionProviderList { + uint8_t _internal [sizeof (struct _EventPipeSessionProviderList_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_REF(EventPipeSessionProviderList *, session_provider_list, ep_rt_session_provider_list_t *, providers) +EP_DEFINE_GETTER(EventPipeSessionProviderList *, session_provider_list, EventPipeSessionProvider *, catch_all_provider) + +EventPipeSessionProviderList * +ep_session_provider_list_alloc ( + const EventPipeProviderConfiguration *configs, + uint32_t configs_len); + +void +ep_session_provider_list_free (EventPipeSessionProviderList *session_provider_list); + +void +ep_session_provider_list_clear (EventPipeSessionProviderList *session_provider_list); + +bool +ep_session_provider_list_is_empty (const EventPipeSessionProviderList *session_provider_list); + +void +ep_session_provider_list_add_session_provider ( + EventPipeSessionProviderList *session_provider_list, + EventPipeSessionProvider *session_provider); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_SESSION_PROVIDER_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-session.c b/src/mono/mono/eventpipe/ep-session.c new file mode 100644 index 0000000000000..f4e4caa67e869 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-session.c @@ -0,0 +1,255 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +void +session_disable_ipc_streaming_thread (EventPipeSession *session); + +static +void +session_create_ipc_streaming_thread_lock_held (EventPipeSession *session); + +/* + * EventPipeSession. + */ + +static +void +session_create_ipc_streaming_thread_lock_held (EventPipeSession *session) +{ + //TODO: Implement. +} + +static +void +session_disable_ipc_streaming_thread (EventPipeSession *session) +{ + EP_ASSERT (ep_session_get_session_type (session) == EP_SESSION_TYPE_IPCSTREAM); + EP_ASSERT (ep_session_get_ipc_streaming_enabled (session)); + + EP_ASSERT (!ep_rt_process_detach ()); + + // The IPC streaming thread will watch this value and exit + // when profiling is disabled. + ep_session_set_ipc_streaming_enabled (session, false); + + // 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 (ep_session_get_buffer_manager (session))); + + // Wait for the sampling thread to clean itself up. + ep_rt_wait_event_handle_t *rt_thread_shutdown_event = ep_session_get_rt_thread_shutdown_event_ref (session); + ep_rt_wait_event_wait (rt_thread_shutdown_event, EP_INFINITE_WAIT, false /* bAlertable */); + ep_rt_wait_event_free (rt_thread_shutdown_event); +} + +EventPipeSessionProvider * +ep_session_get_session_provider_lock_held ( + const EventPipeSession *session, + const EventPipeProvider *provider) +{ + ep_rt_config_requires_lock_held (); + + ep_return_null_if_nok (session != NULL && provider != NULL); + + EventPipeSessionProviderList *providers = ep_session_get_providers (session); + ep_return_null_if_nok (providers != NULL); + + EventPipeSessionProvider *catch_all = ep_session_provider_list_get_catch_all_provider (providers); + if (catch_all) + return catch_all; + + EventPipeSessionProvider *session_provider = ep_rt_session_provider_list_find_by_name (ep_session_provider_list_get_providers_ref (providers), ep_provider_get_provider_name (provider)); + + ep_rt_config_requires_lock_held (); + return session_provider; +} + +void +ep_session_enable_rundown_lock_held (EventPipeSession *session) +{ + ep_rt_config_requires_lock_held (); + + ep_return_void_if_nok (session != NULL); + + //TODO: This is CoreCLR specific keywords for native ETW events (ending up in event pipe). + //! The keywords below seems to correspond to: + //! LoaderKeyword (0x00000008) + //! JitKeyword (0x00000010) + //! NgenKeyword (0x00000020) + //! unused_keyword (0x00000100) + //! JittedMethodILToNativeMapKeyword (0x00020000) + //! ThreadTransferKeyword (0x80000000) + const uint64_t keywords = 0x80020138; + const uint32_t verbose_logging_level = (uint32_t)EP_EVENT_LEVEL_VERBOSE; + + EventPipeProviderConfiguration rundown_providers [2]; + uint32_t rundown_providers_len = EP_ARRAY_SIZE (rundown_providers); + + ep_provider_config_init (&rundown_providers [0], ep_config_get_public_provider_name_utf8 (), keywords, verbose_logging_level, NULL); // Public provider. + ep_provider_config_init (&rundown_providers [1], ep_config_get_rundown_provider_name_utf8 (), keywords, verbose_logging_level, NULL); // Rundown provider. + + //TODO: This is CoreCLR specific provider. + // update the provider context here since the callback doesn't happen till we actually try to do rundown. + //MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.EventPipeProvider.Level = VerboseLoggingLevel; + //MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.EventPipeProvider.EnabledKeywordsBitmask = Keywords; + //MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.EventPipeProvider.IsEnabled = true; + + // Update provider list with rundown configuration. + for (uint32_t i = 0; i < rundown_providers_len; ++i) { + const EventPipeProviderConfiguration *config = &rundown_providers [i]; + + EventPipeSessionProvider *session_provider = ep_session_provider_alloc ( + ep_provider_config_get_provider_name (config), + ep_provider_config_get_keywords (config), + ep_provider_config_get_logging_level (config), + ep_provider_config_get_filter_data (config)); + + ep_session_add_session_provider (session, session_provider); + } + + ep_session_set_rundown_enabled (session, true); + + ep_rt_config_requires_lock_held (); + return; +} + +void +ep_session_execute_rundown_lock_held (EventPipeSession *session) +{ + //TODO: Implement. This is mainly runtime specific implementation + //since it will emit native trace events into the pipe (using CoreCLR's ETW support). +} + +void +ep_session_suspend_write_event_lock_held (EventPipeSession *session) +{ + //TODO: Implement. +} + +void +ep_session_write_sequence_point_unbuffered_lock_held (EventPipeSession *session) +{ + //TODO: Implement. +} + +void +ep_session_start_streaming_lock_held (EventPipeSession *session) +{ + ep_rt_config_requires_lock_held (); + + ep_return_void_if_nok (session != NULL); + + if (ep_session_get_file (session) != NULL) + ep_file_initialize_file (ep_session_get_file (session)); + + if (ep_session_get_session_type (session) == EP_SESSION_TYPE_IPCSTREAM) + session_create_ipc_streaming_thread_lock_held (session); + + ep_rt_config_requires_lock_held (); + return; +} + +bool +ep_session_is_valid (const EventPipeSession *session) +{ + return !ep_session_provider_list_is_empty (ep_session_get_providers (session)); +} + +void +ep_session_add_session_provider (EventPipeSession *session, EventPipeSessionProvider *session_provider) +{ + ep_return_void_if_nok (session != NULL); + ep_session_provider_list_add_session_provider (ep_session_get_providers (session), session_provider); +} + +void +ep_session_disable (EventPipeSession *session) +{ + ep_return_void_if_nok (session != NULL); + if (ep_session_get_session_type (session) == EP_SESSION_TYPE_IPCSTREAM && ep_session_get_ipc_streaming_enabled (session)) + session_disable_ipc_streaming_thread (session); + + bool ignored; + ep_session_write_all_buffers_to_file (session, &ignored); + ep_session_provider_list_clear (ep_session_get_providers (session)); +} + +bool +ep_session_write_all_buffers_to_file (EventPipeSession *session, bool *events_written) +{ + //TODO: Implement. + *events_written = false; + return true; +} + +EventPipeEventInstance * +ep_session_get_next_event (EventPipeSession *session) +{ + //TODO: Implement. + return NULL; +} + +EventPipeWaitHandle +ep_session_get_wait_event (EventPipeSession *session) +{ + ep_raise_error_if_nok (session != NULL); + + EventPipeBufferManager *buffer_manager = ep_session_get_buffer_manager (session); + ep_raise_error_if_nok (buffer_manager != NULL); + + return ep_rt_wait_event_get_wait_handle (ep_buffer_manager_get_rt_wait_event_ref (buffer_manager)); + +ep_on_error: + return 0; +} + +uint64_t +ep_session_get_mask (const EventPipeSession *session) +{ + return ((uint64_t)1 << ep_session_get_index (session)); +} + +bool +ep_session_get_rundown_enabled (const EventPipeSession *session) +{ + return (ep_rt_volatile_load_uint32_t(ep_session_get_rundown_enabled_cref (session)) ? true : false); +} + +void +ep_session_set_rundown_enabled ( + EventPipeSession *session, + bool enabled) +{ + ep_rt_volatile_store_uint32_t (ep_session_get_rundown_enabled_ref (session), (enabled) ? 1 : 0); +} + +bool +ep_session_get_ipc_streaming_enabled (const EventPipeSession *session) +{ + return (ep_rt_volatile_load_uint32_t(ep_session_get_ipc_streaming_enabled_cref (session)) ? true : false); +} + +void +ep_session_set_ipc_streaming_enabled ( + EventPipeSession *session, + bool enabled) +{ + ep_rt_volatile_store_uint32_t (ep_session_get_ipc_streaming_enabled_ref (session), (enabled) ? 1 : 0); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_session; +const char quiet_linker_empty_file_warning_eventpipe_session = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-session.h b/src/mono/mono/eventpipe/ep-session.h new file mode 100644 index 0000000000000..cfcf1476938be --- /dev/null +++ b/src/mono/mono/eventpipe/ep-session.h @@ -0,0 +1,130 @@ +#ifndef __EVENTPIPE_SESSION_H__ +#define __EVENTPIPE_SESSION_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeSession. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeSession { +#else +struct _EventPipeSession_Internal { +#endif + uint32_t index; + EventPipeSessionProviderList *providers; + EventPipeBufferManager *buffer_manager; + volatile uint32_t rundown_enabled; + EventPipeSessionType session_type; + EventPipeSerializationFormat format; + bool rundown_requested; + uint64_t session_start_time; + uint64_t session_start_timestamp; + EventPipeFile *file; + volatile uint32_t ipc_streaming_enabled; + EventPipeThread ipc_streaming_thread; + ep_rt_wait_event_handle_t rt_thread_shutdown_event; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeSession { + uint8_t _internal [sizeof (struct _EventPipeSession_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeSession *, session, uint32_t, index) +EP_DEFINE_GETTER(EventPipeSession *, session, EventPipeSessionProviderList *, providers) +EP_DEFINE_GETTER(EventPipeSession *, session, EventPipeBufferManager *, buffer_manager) +EP_DEFINE_GETTER_REF(EventPipeSession *, session, volatile uint32_t *, rundown_enabled) +EP_DEFINE_GETTER(EventPipeSession *, session, EventPipeSessionType, session_type) +EP_DEFINE_GETTER(EventPipeSession *, session, EventPipeSerializationFormat, format) +EP_DEFINE_GETTER(EventPipeSession *, session, bool, rundown_requested) +EP_DEFINE_GETTER(EventPipeSession *, session, uint64_t, session_start_time) +EP_DEFINE_GETTER(EventPipeSession *, session, uint64_t, session_start_timestamp) +EP_DEFINE_GETTER(EventPipeSession *, session, EventPipeFile *, file) +EP_DEFINE_GETTER_REF(EventPipeSession *, session, volatile uint32_t *, ipc_streaming_enabled) +EP_DEFINE_GETTER_REF(EventPipeSession *, session, EventPipeThread *, ipc_streaming_thread) +EP_DEFINE_GETTER_REF(EventPipeSession *, session, ep_rt_wait_event_handle_t *, rt_thread_shutdown_event) + +EventPipeSession * +ep_session_alloc ( + uint32_t index, + const ep_char8_t *output_path, + IpcStream *stream, + EventPipeSessionType session_type, + EventPipeSerializationFormat format, + bool rundown_requested, + uint32_t circular_buffer_size_in_mb, + const EventPipeProviderConfiguration *providers, + uint32_t providers_len, + bool rundown_enabled); + +void +ep_session_free (EventPipeSession *session); + +EventPipeSessionProvider * +ep_session_get_session_provider_lock_held ( + const EventPipeSession *session, + const EventPipeProvider *provider); + +void +ep_session_enable_rundown_lock_held (EventPipeSession *session); + +void +ep_session_execute_rundown_lock_held (EventPipeSession *session); + +void +ep_session_suspend_write_event_lock_held (EventPipeSession *session); + +void +ep_session_write_sequence_point_unbuffered_lock_held (EventPipeSession *session); + +void +ep_session_start_streaming_lock_held (EventPipeSession *session); + +bool +ep_session_is_valid (const EventPipeSession *session); + +void +ep_session_add_session_provider ( + EventPipeSession *session, + EventPipeSessionProvider *session_provider); + +void +ep_session_disable (EventPipeSession *session); + +bool +ep_session_write_all_buffers_to_file (EventPipeSession *session, bool *events_written); + +EventPipeEventInstance * +ep_session_get_next_event (EventPipeSession *session); + +EventPipeWaitHandle +ep_session_get_wait_event (EventPipeSession *session); + +uint64_t +ep_session_get_mask (const EventPipeSession *session); + +bool +ep_session_get_rundown_enabled (const EventPipeSession *session); + +void +ep_session_set_rundown_enabled ( + EventPipeSession *session, + bool enabled); + +bool +ep_session_get_ipc_streaming_enabled (const EventPipeSession *session); + +void +ep_session_set_ipc_streaming_enabled ( + EventPipeSession *session, + bool enabled); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_SESSION_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-stack-contents.h b/src/mono/mono/eventpipe/ep-stack-contents.h new file mode 100644 index 0000000000000..6ffe98d657004 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-stack-contents.h @@ -0,0 +1,172 @@ +#ifndef __EVENTPIPE_STACKCONTENTS_H__ +#define __EVENTPIPE_STACKCONTENTS_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeStackContents. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeStackContents { +#else +struct _EventPipeStackContents_Internal { +#endif + // Array of IP values from a stack crawl. + // Top of stack is at index 0. + uintptr_t stack_frames [EP_MAX_STACK_DEPTH]; +#ifdef EP_CHECKED_BUILD + // Parallel array of MethodDesc pointers. + // Used for debug-only stack printing. + ep_rt_method_desc_t *methods [EP_MAX_STACK_DEPTH]; +#endif + + // The next available slot in stack_frames. + uint32_t next_available_frame; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeStackContents { + uint8_t _internal [sizeof (struct _EventPipeStackContents_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_ARRAY_REF(EventPipeStackContents *, stack_contents, uintptr_t *, const uintptr_t *, stack_frames, stack_frames[0]) +#ifdef EP_CHECKED_BUILD +EP_DEFINE_GETTER_ARRAY_REF(EventPipeStackContents *, stack_contents, ep_rt_method_desc_t **, ep_rt_method_desc_t *const*, methods, methods[0]) +#endif +EP_DEFINE_GETTER(EventPipeStackContents *, stack_contents, uint32_t, next_available_frame) +EP_DEFINE_SETTER(EventPipeStackContents *, stack_contents, uint32_t, next_available_frame) + +EventPipeStackContents * +ep_stack_contents_alloc (void); + +EventPipeStackContents * +ep_stack_contents_init (EventPipeStackContents *stack_contents); + +void +ep_stack_contents_fini (EventPipeStackContents *stack_contents); + +void +ep_stack_contents_free (EventPipeStackContents *stack_contents); + +static +inline +void +ep_stack_contents_copyto ( + EventPipeStackContents *stack_contents, + EventPipeStackContents *dest) +{ + memcpy ( + ep_stack_contents_get_stack_frames_ref (dest), + ep_stack_contents_get_stack_frames_ref (stack_contents), + ep_stack_contents_get_next_available_frame (stack_contents) * sizeof (uintptr_t)); + +#ifdef EP_CHECKED_BUILD + memcpy ( + ep_stack_contents_get_methods_ref (dest), + ep_stack_contents_get_methods_ref (stack_contents), + ep_stack_contents_get_next_available_frame (stack_contents) * sizeof (ep_rt_method_desc_t *)); +#endif + + ep_stack_contents_set_next_available_frame (dest, ep_stack_contents_get_next_available_frame (stack_contents)); +} + +static +inline +void +ep_stack_contents_reset (EventPipeStackContents *stack_contents) +{ + ep_stack_contents_set_next_available_frame (stack_contents, 0); +} + +static +inline +bool +ep_stack_contents_is_empty (EventPipeStackContents *stack_contents) +{ + return (ep_stack_contents_get_next_available_frame (stack_contents) == 0); +} + +static +inline +uint32_t +ep_stack_contents_get_length (EventPipeStackContents *stack_contents) +{ + return ep_stack_contents_get_next_available_frame (stack_contents); +} + +static +inline +uintptr_t +ep_stack_contents_get_ip ( + EventPipeStackContents *stack_contents, + uint32_t frame_index) +{ + EP_ASSERT (frame_index < EP_MAX_STACK_DEPTH); + if (frame_index >= EP_MAX_STACK_DEPTH) + return 0; + + return ep_stack_contents_get_stack_frames_cref (stack_contents)[frame_index]; +} + +#ifdef EP_CHECKED_BUILD +static +inline +ep_rt_method_desc_t * +ep_stack_contents_get_method ( + EventPipeStackContents *stack_contents, + uint32_t frame_index) +{ + EP_ASSERT (frame_index < EP_MAX_STACK_DEPTH); + if (frame_index >= EP_MAX_STACK_DEPTH) + return NULL; + + return ep_stack_contents_get_methods_cref (stack_contents)[frame_index]; +} +#endif + +static +inline +void +ep_stack_contents_append ( + EventPipeStackContents *stack_contents, + uintptr_t control_pc, + ep_rt_method_desc_t *method) +{ + EP_ASSERT (stack_contents != NULL); + uint32_t next_frame = ep_stack_contents_get_next_available_frame (stack_contents); + if (next_frame < EP_MAX_STACK_DEPTH) { + ep_stack_contents_get_stack_frames_ref (stack_contents)[next_frame] = control_pc; +#ifdef EP_CHECKED_BUILD + ep_stack_contents_get_methods_ref (stack_contents)[next_frame] = method; +#endif + next_frame++; + ep_stack_contents_set_next_available_frame (stack_contents, next_frame); + } +} + +static +inline +uint8_t * +ep_stack_contents_get_pointer (const EventPipeStackContents *stack_contents) +{ + EP_ASSERT (stack_contents != NULL); + return (uint8_t *)ep_stack_contents_get_stack_frames_cref (stack_contents); +} + +static +inline +uint32_t +ep_stack_contents_get_size (const EventPipeStackContents *stack_contents) +{ + EP_ASSERT (stack_contents != NULL); + return (ep_stack_contents_get_next_available_frame (stack_contents) * sizeof (uintptr_t)); +} + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_STACKCONTENTS_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-stream-internals.c b/src/mono/mono/eventpipe/ep-stream-internals.c new file mode 100644 index 0000000000000..c62c857552aec --- /dev/null +++ b/src/mono/mono/eventpipe/ep-stream-internals.c @@ -0,0 +1,392 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +void +file_stream_writer_free_func (void *stream); + +static +bool +file_stream_writer_write_func ( + void *stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written); + +static +void +file_write_end (EventPipeFile *file); + +static +void +ipc_stream_writer_free_func (void *stream); + +static +bool +ipc_stream_writer_write_func ( + void *stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written); + +/* + * FastSerializableObject. + */ + +FastSerializableObject * +ep_fast_serializable_object_init ( + FastSerializableObject *fast_serializable_object, + FastSerializableObjectVtable *vtable, + int32_t object_version, + int32_t min_reader_version, + bool is_private) +{ + EP_ASSERT (fast_serializable_object != NULL && vtable != NULL); + + fast_serializable_object->vtable = vtable; + fast_serializable_object->object_version = object_version; + fast_serializable_object->min_reader_version = min_reader_version; + fast_serializable_object->is_private = is_private; + + return fast_serializable_object; +} + +void +ep_fast_serializable_object_fini (FastSerializableObject *fast_serializable_ojbect) +{ + ; +} + +void +ep_fast_serializable_object_free_vcall (FastSerializableObject *fast_serializable_ojbect) +{ + ep_return_void_if_nok (fast_serializable_ojbect != NULL); + + EP_ASSERT (fast_serializable_ojbect->vtable != NULL); + FastSerializableObjectVtable *vtable = fast_serializable_ojbect->vtable; + + EP_ASSERT (vtable->free_func != NULL); + vtable->free_func (fast_serializable_ojbect); +} + +void +ep_fast_serializable_object_fast_serialize_vcall ( + FastSerializableObject *fast_serializable_ojbect, + FastSerializer *fast_serializer) +{ + EP_ASSERT (fast_serializable_ojbect != NULL && fast_serializable_ojbect->vtable != NULL); + FastSerializableObjectVtable *vtable = fast_serializable_ojbect->vtable; + + EP_ASSERT (vtable->fast_serialize_func != NULL); + vtable->fast_serialize_func (fast_serializable_ojbect, fast_serializer); +} + +const ep_char8_t * +ep_fast_serializable_object_get_type_name_vcall (FastSerializableObject *fast_serializable_ojbect) +{ + EP_ASSERT (fast_serializable_ojbect != NULL && fast_serializable_ojbect->vtable != NULL); + FastSerializableObjectVtable *vtable = fast_serializable_ojbect->vtable; + + EP_ASSERT (vtable->get_type_name_func != NULL); + return vtable->get_type_name_func (fast_serializable_ojbect); +} + +/* + * FastSerializer. + */ +FastSerializer * +ep_fast_serializer_alloc (StreamWriter *stream_writer) +{ + ep_return_null_if_nok (stream_writer != NULL); + + FastSerializer *instance = ep_rt_object_alloc (FastSerializer); + ep_raise_error_if_nok (instance != NULL); + + // Ownership transfered. + instance->stream_writer = stream_writer; + instance->required_padding = 0; + instance->write_error_encountered = false; + + const ep_char8_t signature[] = "!FastSerialization.1"; // the consumer lib expects exactly the same string, it must not be changed + uint32_t signature_len = EP_ARRAY_SIZE (signature) - 1; + ep_fast_serializer_write_string (instance, signature, signature_len); + +ep_on_exit: + return instance; + +ep_on_error: + ep_fast_serializer_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_fast_serializer_free (FastSerializer *fast_serializer) +{ + ep_return_void_if_nok (fast_serializer != NULL); + + EP_ASSERT (fast_serializer->stream_writer != NULL); + ep_stream_writer_free_vcall (fast_serializer->stream_writer); + + ep_rt_object_free (fast_serializer); +} + +/* + * FileStream. + */ + +FileStream * +ep_file_stream_alloc (void) +{ + return ep_rt_object_alloc (FileStream); +} + +void +ep_file_stream_free (FileStream *file_stream) +{ + ep_return_void_if_nok (file_stream != NULL); + + ep_file_stream_close (file_stream); + ep_rt_object_free (file_stream); +} + +/* + * FileStreamWriter. + */ + +static +void +file_stream_writer_free_func (void *stream) +{ + ep_file_stream_writer_free ((FileStreamWriter *)stream); +} + +static +bool +file_stream_writer_write_func ( + void *stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written) +{ + EP_ASSERT (stream != NULL); + + return ep_file_stream_writer_write ( + (FileStreamWriter *)stream, + buffer, + bytes_to_write, + bytes_written); +} + +static StreamWriterVtable file_stream_writer_vtable = { + file_stream_writer_free_func, + file_stream_writer_write_func }; + +FileStreamWriter * +ep_file_stream_writer_alloc (const ep_char8_t *output_file_path) +{ + ep_return_null_if_nok (output_file_path != NULL); + + FileStreamWriter *instance = ep_rt_object_alloc (FileStreamWriter); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ep_stream_writer_init ( + &instance->stream_writer, + &file_stream_writer_vtable) != NULL); + + instance->file_stream = ep_file_stream_alloc (); + ep_raise_error_if_nok (instance->file_stream != NULL); + + if (!ep_file_stream_open_write (instance->file_stream, output_file_path)) { + EP_ASSERT (!"Unable to open file for write."); + ep_raise_error (); + } + +ep_on_exit: + return instance; + +ep_on_error: + ep_file_stream_writer_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_file_stream_writer_free (FileStreamWriter *file_stream_writer) +{ + ep_return_void_if_nok (file_stream_writer != NULL); + + ep_file_stream_free (file_stream_writer->file_stream); + ep_stream_writer_fini (&file_stream_writer->stream_writer); + ep_rt_object_free (file_stream_writer); +} + +/* + * IpcStream. + */ + +IpcStream * +ep_ipc_stream_alloc (ep_rt_ipc_handle_t rt_ipc) +{ + IpcStream *instance = ep_rt_object_alloc (IpcStream); + ep_raise_error_if_nok (instance != NULL); + + //Transfer ownership. + instance->rt_ipc = rt_ipc; + +ep_on_exit: + return instance; + +ep_on_error: + ep_ipc_stream_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_ipc_stream_free (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); +} + +/* + * IpcStreamWriter. + */ + +static +void +ipc_stream_writer_free_func (void *stream) +{ + ep_ipc_stream_writer_free ((IpcStreamWriter *)stream); +} + +static +bool +ipc_stream_writer_write_func ( + void *stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written) +{ + EP_ASSERT (stream != NULL); + + return ep_ipc_stream_writer_write ( + (IpcStreamWriter *)stream, + buffer, + bytes_to_write, + bytes_written); +} + +static StreamWriterVtable ipc_stream_writer_vtable = { + ipc_stream_writer_free_func, + ipc_stream_writer_write_func }; + +IpcStreamWriter * +ep_ipc_stream_writer_alloc ( + uint64_t id, + IpcStream *stream) +{ + IpcStreamWriter *instance = ep_rt_object_alloc (IpcStreamWriter); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ep_stream_writer_init ( + &instance->stream_writer, + &ipc_stream_writer_vtable) != NULL); + + //Ownership transfered. + instance->ipc_stream = stream; + +ep_on_exit: + return instance; + +ep_on_error: + ep_ipc_stream_writer_free (instance); + + instance = NULL; + ep_exit_error_handler (); +} + +void +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_stream_writer_fini (&ipc_stream_writer->stream_writer); + ep_rt_object_free (ipc_stream_writer); +} + +/* + * StreamWriter. + */ + +StreamWriter * +ep_stream_writer_init ( + StreamWriter *stream_writer, + StreamWriterVtable *vtable) +{ + EP_ASSERT (stream_writer != NULL && vtable != NULL); + + stream_writer->vtable = vtable; + + return stream_writer; +} + +void +ep_stream_writer_fini (StreamWriter *stream_writer) +{ + ; +} + +void +ep_stream_writer_free_vcall (StreamWriter *stream_writer) +{ + ep_return_void_if_nok (stream_writer != NULL); + + EP_ASSERT (stream_writer->vtable != NULL); + StreamWriterVtable *vtable = stream_writer->vtable; + + EP_ASSERT (vtable->free_func != NULL); + vtable->free_func (stream_writer); +} + +bool +ep_stream_writer_write_vcall ( + StreamWriter *stream_writer, + const uint8_t *buffer, + const uint32_t bytes_to_write, + uint32_t *bytes_written) +{ + EP_ASSERT (stream_writer != NULL && stream_writer->vtable != NULL); + StreamWriterVtable *vtable = stream_writer->vtable; + + EP_ASSERT (vtable->write_func != NULL); + return vtable->write_func (stream_writer, buffer, bytes_to_write, bytes_written); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_stream_internals; +const char quiet_linker_empty_file_warning_eventpipe_stream_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-stream.c b/src/mono/mono/eventpipe/ep-stream.c new file mode 100644 index 0000000000000..5d9ee0060aa38 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-stream.c @@ -0,0 +1,280 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * Forward declares of all static functions. + */ + +static +void +fast_serializer_write_serialization_type ( + FastSerializer *fast_serializer, + FastSerializableObject *fast_serializable_ojbect); + +/* + * FastSerializableObject. + */ + +void +ep_fast_serializable_object_fast_serialize ( + FastSerializableObject *fast_serializable_ojbect, + FastSerializer *fast_serializer) +{ + ep_fast_serializable_object_fast_serialize_vcall (fast_serializable_ojbect, fast_serializer); +} + +const ep_char8_t * +ep_fast_serializable_object_get_type_name (FastSerializableObject *fast_serializable_ojbect) +{ + return ep_fast_serializable_object_get_type_name_vcall (fast_serializable_ojbect); +} + +/* + * FastSerializer. + */ + +static +void +fast_serializer_write_serialization_type ( + FastSerializer *fast_serializer, + FastSerializableObject *fast_serializable_ojbect) +{ + ep_return_void_if_nok (fast_serializable_ojbect != NULL); + + // Write the BeginObject tag. + ep_fast_serializer_write_tag (fast_serializer, ep_fast_serializable_object_get_is_private (fast_serializable_ojbect) ? FAST_SERIALIZER_TAGS_BEGIN_PRIVATE_OBJECT : FAST_SERIALIZER_TAGS_BEGIN_OBJECT, NULL, 0); + + // Write a NullReferenceTag, which implies that the following fields belong to SerializationType. + ep_fast_serializer_write_tag (fast_serializer, FAST_SERIALIZER_TAGS_NULL_REFERENCE, NULL, 0); + + // Write the SerializationType version fields. + int32_t serialization_type [2]; + serialization_type [0] = ep_fast_serializable_object_get_object_version (fast_serializable_ojbect); + serialization_type [1] = ep_fast_serializable_object_get_min_reader_version (fast_serializable_ojbect); + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)serialization_type, sizeof (serialization_type)); + + // Write the SerializationType TypeName field. + const ep_char8_t *type_name = ep_fast_serializable_object_get_type_name_vcall (fast_serializable_ojbect); + if (type_name) + ep_fast_serializer_write_string (fast_serializer, type_name, (uint32_t)ep_rt_utf8_string_len (type_name)); + + // Write the EndObject tag. + ep_fast_serializer_write_tag (fast_serializer, FAST_SERIALIZER_TAGS_END_OBJECT, NULL, 0); +} + +void +ep_fast_serializer_write_buffer ( + FastSerializer *fast_serializer, + const uint8_t *buffer, + uint32_t buffer_len) +{ + ep_return_void_if_nok (fast_serializer != NULL && buffer != NULL && buffer_len > 0); + ep_return_void_if_nok (ep_fast_serializer_get_write_error_encountered (fast_serializer) != true && ep_fast_serializer_get_stream_writer (fast_serializer) != NULL); + + uint32_t bytes_written = 0; + bool result = ep_stream_writer_write (ep_fast_serializer_get_stream_writer (fast_serializer), buffer, buffer_len, &bytes_written); + + uint32_t required_padding = ep_fast_serializer_get_required_padding (fast_serializer); + required_padding = (FAST_SERIALIZER_ALIGNMENT_SIZE + required_padding - (bytes_written & FAST_SERIALIZER_ALIGNMENT_SIZE)) % FAST_SERIALIZER_ALIGNMENT_SIZE; + ep_fast_serializer_set_required_padding (fast_serializer, required_padding); + + // This will cause us to stop writing to the file. + // The file will still remain open until shutdown so that we don't + // have to take a lock at this level when we touch the file stream. + ep_fast_serializer_set_write_error_encountered (fast_serializer, ((buffer_len != bytes_written) || !result)); +} + +void +ep_fast_serializer_write_object ( + FastSerializer *fast_serializer, + FastSerializableObject *fast_serializable_ojbect) +{ + ep_return_void_if_nok (fast_serializer != NULL && fast_serializable_ojbect != NULL); + + ep_fast_serializer_write_tag (fast_serializer, ep_fast_serializable_object_get_is_private (fast_serializable_ojbect) ? FAST_SERIALIZER_TAGS_BEGIN_PRIVATE_OBJECT : FAST_SERIALIZER_TAGS_BEGIN_OBJECT, NULL, 0); + + fast_serializer_write_serialization_type (fast_serializer, fast_serializable_ojbect); + + // Ask the object to serialize itself using the current serializer. + ep_fast_serializable_object_fast_serialize_vcall (fast_serializable_ojbect, fast_serializer); + + ep_fast_serializer_write_tag (fast_serializer, FAST_SERIALIZER_TAGS_END_OBJECT, NULL, 0); +} + +void +ep_fast_serializer_write_string ( + FastSerializer *fast_serializer, + const ep_char8_t *contents, + uint32_t contents_len) +{ + // Write teh string length. + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)&contents_len, sizeof (contents_len)); + + //Wirte the string contents. + ep_fast_serializer_write_buffer (fast_serializer, (const uint8_t *)contents, contents_len); +} + +void +ep_fast_serializer_write_tag ( + FastSerializer *fast_serializer, + FastSerializerTags tag, + const uint8_t *payload, + uint32_t payload_len) +{ + uint8_t tag_as_byte = tag; + ep_fast_serializer_write_buffer (fast_serializer, &tag_as_byte, sizeof (tag_as_byte)); + if (payload != NULL) { + EP_ASSERT (payload_len > 0); + ep_fast_serializer_write_buffer (fast_serializer, payload, payload_len); + } +} + +/* +* FileStream. +*/ + +bool +ep_file_stream_open_write ( + FileStream *file_stream, + const ep_char8_t *path) +{ + ep_return_false_if_nok (file_stream != NULL); + + ep_rt_file_handle_t rt_file = ep_rt_file_open_write (path); + ep_raise_error_if_nok (rt_file != NULL); + + ep_file_stream_set_rt_file (file_stream, rt_file); + return true; + +ep_on_error: + return false; +} + +bool +ep_file_stream_close (FileStream *file_stream) +{ + ep_return_false_if_nok (file_stream != NULL); + return ep_rt_file_close (ep_file_stream_get_rt_file (file_stream)); +} + +bool +ep_file_stream_write ( + FileStream *file_stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written) +{ + ep_return_false_if_nok (file_stream != NULL && buffer != NULL && bytes_to_write > 0 && bytes_written != NULL); + return ep_rt_file_write (ep_file_stream_get_rt_file (file_stream), buffer, bytes_to_write, bytes_written); +} + +/* + * FileStreamWriter. + */ + +bool +ep_file_stream_writer_write ( + FileStreamWriter *file_stream_writer, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written) +{ + ep_return_false_if_nok (file_stream_writer != NULL && buffer != NULL && bytes_to_write > 0 && bytes_written != NULL); + + ep_raise_error_if_nok (ep_file_stream_writer_get_file_stream (file_stream_writer) != NULL); + + return ep_file_stream_write (ep_file_stream_writer_get_file_stream (file_stream_writer), buffer, bytes_to_write, bytes_written); + +ep_on_error: + *bytes_written = 0; + return false; +} + +/* +* IpcStream. +*/ + +bool +ep_ipc_stream_flush (IpcStream *ipc_stream) +{ + //TODO: Implement. + return false; +} + +bool +ep_ipc_stream_disconnect (IpcStream *ipc_stream) +{ + //TODO: Implement. + return false; +} + +bool +ep_ipc_stream_close (IpcStream *ipc_stream) +{ + //TODO: Implement. + return false; +} + +bool +ep_ipc_stream_write ( + IpcStream *ipc_stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written) +{ + //TODO: Implement. + return false; +} + +/* + * IpcStreamWriter. + */ + +bool +ep_ipc_stream_writer_write ( + IpcStreamWriter *ipc_stream_writer, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written) +{ + ep_return_false_if_nok (ipc_stream_writer != NULL && buffer != NULL && bytes_to_write > 0 && bytes_written != NULL); + + ep_raise_error_if_nok (ep_ipc_stream_writer_get_ipc_stream (ipc_stream_writer) != NULL); + + return ep_ipc_stream_write (ep_ipc_stream_writer_get_ipc_stream (ipc_stream_writer), buffer, bytes_to_write, bytes_written); + +ep_on_error: + *bytes_written = 0; + return false; +} + +/* + * StreamWriter. + */ + +bool +ep_stream_writer_write ( + StreamWriter *stream_writer, + const uint8_t *buffer, + const uint32_t bytes_to_write, + uint32_t *bytes_written) +{ + return ep_stream_writer_write_vcall ( + stream_writer, + buffer, + bytes_to_write, + bytes_written); +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_stream; +const char quiet_linker_empty_file_warning_eventpipe_stream = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-stream.h b/src/mono/mono/eventpipe/ep-stream.h new file mode 100644 index 0000000000000..16d9c9e49b8e3 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-stream.h @@ -0,0 +1,364 @@ +#ifndef __EVENTPIPE_STREAM_H__ +#define __EVENTPIPE_STREAM_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +// the enumeration has a specific set of values to keep it compatible with consumer library +// it's sibling is defined in https://github.com/Microsoft/perfview/blob/10d1f92b242c98073b3817ac5ee6d98cd595d39b/src/FastSerialization/FastSerialization.cs#L2295 +typedef enum +{ + FAST_SERIALIZER_TAGS_ERROR = 0, // To improve debugabilty, 0 is an illegal tag. + FAST_SERIALIZER_TAGS_NULL_REFERENCE = 1, // Tag for a null object forwardReference. + FAST_SERIALIZER_TAGS_OBJECT_REFERENCE = 2, // Followed by StreamLabel + // 3 used to belong to ForwardReference, which got removed in V3 + FAST_SERIALIZER_TAGS_BEGIN_OBJECT = 4, // Followed by Type object, object data, tagged EndObject + FAST_SERIALIZER_TAGS_BEGIN_PRIVATE_OBJECT = 5, // Like beginObject, but not placed in interning table on deserialiation + FAST_SERIALIZER_TAGS_END_OBJECT = 6, // Placed after an object to mark its end. + // 7 used to belong to ForwardDefinition, which got removed in V3 + FAST_SERIALIZER_TAGS_BYTE = 8, + FAST_SERIALIZER_TAGS_INT16, + FAST_SERIALIZER_TAGS_INT32, + FAST_SERIALIZER_TAGS_INT64, + FAST_SERIALIZER_TAGS_SKIP_REGION, + FAST_SERIALIZER_TAGS_STRING, + FAST_SERIALIZER_TAGS_BLOB, + FAST_SERIALIZER_TAGS_LIMIT // Just past the last valid tag, used for asserts. +} FastSerializerTags; + +/* + * StreamWriter. + */ + +typedef void (*StreamWriterFreeFunc)(void *stream); +typedef bool (*StreamWriterWriteFunc)(void *stream, const uint8_t *buffer, const uint32_t bytes_to_write, uint32_t *bytes_written); + +struct _StreamWriterVtable { + StreamWriterFreeFunc free_func; + StreamWriterWriteFunc write_func; +}; + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _StreamWriter { +#else +struct _StreamWriter_Internal { +#endif + StreamWriterVtable *vtable; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _StreamWriter { + uint8_t _internal [sizeof (struct _StreamWriter_Internal)]; +}; +#endif + +StreamWriter * +ep_stream_writer_init ( + StreamWriter *stream_writer, + StreamWriterVtable *vtable); + +void +ep_stream_writer_fini (StreamWriter *stream_writer); + +bool +ep_stream_writer_write ( + StreamWriter *stream_writer, + const uint8_t *buffer, + const uint32_t bytes_to_write, + uint32_t *bytes_written); + +void +ep_stream_writer_free_vcall (StreamWriter *stream_writer); + +bool +ep_stream_writer_write_vcall ( + StreamWriter *stream_writer, + const uint8_t *buffer, + const uint32_t bytes_to_write, + uint32_t *bytes_written); + +/* + * FastSerializableObject. + */ + +typedef void (*FastSerializableObjectFreeFunc)(void *object); +typedef void (*FastSerializableObjectFastSerializeFunc)(void *object, FastSerializer *fast_serializer); +typedef const ep_char8_t * (*FastSerializableObjectGetTypeNameFunc)(void *object); + +struct _FastSerializableObjectVtable { + FastSerializableObjectFreeFunc free_func; + FastSerializableObjectFastSerializeFunc fast_serialize_func; + FastSerializableObjectGetTypeNameFunc get_type_name_func; +}; + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _FastSerializableObject { +#else +struct _FastSerializableObject_Internal { +#endif + FastSerializableObjectVtable *vtable; + int32_t object_version; + int32_t min_reader_version; + bool is_private; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _FastSerializableObject { + uint8_t _internal [sizeof (struct _FastSerializableObject_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(FastSerializableObject *, fast_serializable_object, int32_t, object_version) +EP_DEFINE_GETTER(FastSerializableObject *, fast_serializable_object, int32_t, min_reader_version) +EP_DEFINE_GETTER(FastSerializableObject *, fast_serializable_object, bool, is_private) + +FastSerializableObject * +ep_fast_serializable_object_init ( + FastSerializableObject *fast_serializable_object, + FastSerializableObjectVtable *vtable, + int32_t object_version, + int32_t min_reader_version, + bool is_private); + +void +ep_fast_serializable_object_fini (FastSerializableObject *fast_serializable_object); + +void +ep_fast_serializable_object_fast_serialize ( + FastSerializableObject *fast_serializable_ojbect, + FastSerializer *fast_serializer); + +const ep_char8_t * +ep_fast_serializable_object_get_type_name (FastSerializableObject *fast_serializable_ojbect); + +void +ep_fast_serializable_object_free_vcall (FastSerializableObject *fast_serializable_ojbect); + +const ep_char8_t * +ep_fast_serializable_object_get_type_name_vcall (FastSerializableObject *fast_serializable_ojbect); + +void +ep_fast_serializable_object_fast_serialize_vcall ( + FastSerializableObject *fast_serializable_ojbect, + FastSerializer *fast_serializer); + +/* + * FastSerializer. + */ + +#define FAST_SERIALIZER_ALIGNMENT_SIZE 4 + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _FastSerializer { +#else +struct _FastSerializer_Internal { +#endif + StreamWriter *stream_writer; + uint32_t required_padding; + bool write_error_encountered; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _FastSerializer { + uint8_t _internal [sizeof (struct _FastSerializer_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(FastSerializer *, fast_serializer, StreamWriter *, stream_writer) +EP_DEFINE_GETTER(FastSerializer *, fast_serializer, uint32_t, required_padding) +EP_DEFINE_SETTER(FastSerializer *, fast_serializer, uint32_t, required_padding) +EP_DEFINE_GETTER(FastSerializer *, fast_serializer, bool, write_error_encountered) +EP_DEFINE_SETTER(FastSerializer *, fast_serializer, bool, write_error_encountered) + +FastSerializer * +ep_fast_serializer_alloc (StreamWriter *stream_writer); + +void +ep_fast_serializer_free (FastSerializer *fast_serializer); + +void +ep_fast_serializer_write_buffer ( + FastSerializer *fast_serializer, + const uint8_t *buffer, + uint32_t buffer_len); + +void +ep_fast_serializer_write_object ( + FastSerializer *fast_serializer, + FastSerializableObject *fast_serializable_ojbect); + +void +ep_fast_serializer_write_string ( + FastSerializer *fast_serializer, + const ep_char8_t *contents, + uint32_t contents_len); + +void +ep_fast_serializer_write_tag ( + FastSerializer *fast_serializer, + FastSerializerTags tag, + const uint8_t *payload, + uint32_t payload_len); + +/* +* FileStream. +*/ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _FileStream { +#else +struct _FileStream_Internal { +#endif + ep_rt_file_handle_t rt_file; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _FileStream { + uint8_t _internal [sizeof (struct _FileStream_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(FileStream *, file_stream, ep_rt_file_handle_t, rt_file) +EP_DEFINE_SETTER(FileStream *, file_stream, ep_rt_file_handle_t, rt_file) + +FileStream * +ep_file_stream_alloc (void); + +void +ep_file_stream_free (FileStream *file_stream); + +bool +ep_file_stream_open_write ( + FileStream *file_stream, + const ep_char8_t *path); + +bool +ep_file_stream_close (FileStream *file_stream); + +bool +ep_file_stream_write ( + FileStream *file_stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written); + +/* + * FileStreamWriter. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _FileStreamWriter { +#else +struct _FileStreamWriter_Internal { +#endif + StreamWriter stream_writer; + FileStream *file_stream; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _FileStreamWriter { + uint8_t _internal [sizeof (struct _FileStreamWriter_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_REF(FileStreamWriter *, file_stream_writer, StreamWriter *, stream_writer) +EP_DEFINE_GETTER(FileStreamWriter *, file_stream_writer, FileStream *, file_stream) + +FileStreamWriter * +ep_file_stream_writer_alloc (const ep_char8_t *output_file_path); + +void +ep_file_stream_writer_free (FileStreamWriter *file_stream_writer); + +bool +ep_file_stream_writer_write ( + FileStreamWriter *file_stream_writer, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written); + +/* + * IpcStream. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +//TODO: Implement. +struct _IpcStream { +#else +struct _IpcStream_Internal { +#endif + ep_rt_ipc_handle_t rt_ipc; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _IpcStream { + uint8_t _internal [sizeof (struct _IpcStream_Internal)]; +}; +#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); + +void +ep_ipc_stream_free (IpcStream *ipc_stream); + +bool +ep_ipc_stream_flush (IpcStream *stream); + +bool +ep_ipc_stream_disconnect (IpcStream *stream); + +bool +ep_ipc_stream_close (IpcStream *stream); + +bool +ep_ipc_stream_write ( + IpcStream *stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written); + +/* + * IpcStreamWriter. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _IpcStreamWriter { +#else +struct _IpcStreamWriter_Internal { +#endif + StreamWriter stream_writer; + IpcStream *ipc_stream; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _IpcStreamWriter { + uint8_t _internal [sizeof (struct _IpcStreamWriter_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_REF(IpcStreamWriter *, ipc_stream_writer, StreamWriter *, stream_writer) +EP_DEFINE_GETTER(IpcStreamWriter *, ipc_stream_writer, IpcStream *, ipc_stream) + +IpcStreamWriter * +ep_ipc_stream_writer_alloc ( + uint64_t id, + IpcStream *stream); + +void +ep_ipc_stream_writer_free (IpcStreamWriter *ipc_stream_writer); + +bool +ep_ipc_stream_writer_write ( + IpcStreamWriter *ipc_stream_writer, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_STREAM_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-thread-internals.c b/src/mono/mono/eventpipe/ep-thread-internals.c new file mode 100644 index 0000000000000..da2d7ad2b624f --- /dev/null +++ b/src/mono/mono/eventpipe/ep-thread-internals.c @@ -0,0 +1,219 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#define EP_IMPL_GETTER_SETTER +#include "ep.h" + +/* + * EventPipeThread. + */ + +EventPipeThread * +ep_thread_alloc (void) +{ + EventPipeThread *instance = ep_rt_object_alloc (EventPipeThread); + ep_raise_error_if_nok (instance != NULL); + + ep_rt_spin_lock_alloc (&instance->rt_lock); + ep_raise_error_if_nok (instance->rt_lock.lock != NULL); + + instance->os_thread_id = ep_rt_current_thread_get_id (); + memset (instance->session_state, 0, sizeof (instance->session_state)); + +ep_on_exit: + return instance; + +ep_on_error: + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_thread_free (EventPipeThread *thread) +{ + ep_return_void_if_nok (thread != NULL); + + EP_ASSERT (ep_rt_volatile_load_uint32_t ((const volatile uint32_t *)&thread->ref_count) == 0); + +#ifdef EP_CHECKED_BUILD + for (uint32_t i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; ++i) { + EP_ASSERT (thread->session_state [i] == NULL); + } +#endif + + ep_rt_spin_lock_free (&thread->rt_lock); + ep_rt_object_free (thread); +} + +/* + * EventPipeThreadHolder. + */ + +EventPipeThreadHolder * +ep_thread_holder_alloc (EventPipeThread *thread) +{ + ep_return_null_if_nok (thread != NULL); + + EventPipeThreadHolder *instance = ep_rt_object_alloc (EventPipeThreadHolder); + ep_raise_error_if_nok (instance != NULL); + ep_raise_error_if_nok (ep_thread_holder_init (instance, thread) != NULL); + +ep_on_exit: + return instance; + +ep_on_error: + instance = NULL; + ep_exit_error_handler (); +} + +EventPipeThreadHolder * +ep_thread_holder_init ( + EventPipeThreadHolder *thread_holder, + EventPipeThread *thread) +{ + ep_return_null_if_nok (thread_holder != NULL && thread != NULL); + + thread_holder->thread = thread; + ep_thread_addref (thread_holder->thread); + + return thread_holder; +} + +void +ep_thread_holder_fini (EventPipeThreadHolder *thread_holder) +{ + ep_return_void_if_nok (thread_holder != NULL && thread_holder->thread); + ep_thread_release (thread_holder->thread); +} + +void +ep_thread_holder_free (EventPipeThreadHolder *thread_holder) +{ + ep_return_void_if_nok (thread_holder != NULL); + ep_thread_holder_fini (thread_holder); + ep_rt_object_free (thread_holder); +} + +/* + * EventPipeThreadSessionState. + */ + +EventPipeThreadSessionState * +ep_thread_session_state_alloc ( + EventPipeThread *thread, + EventPipeSession *session, + EventPipeBufferManager *buffer_manager) +{ + EventPipeThreadSessionState *instance = ep_rt_object_alloc (EventPipeThreadSessionState); + ep_raise_error_if_nok (instance != NULL); + + ep_raise_error_if_nok (ep_thread_holder_init (&instance->thread_holder, thread) != NULL); + + instance->session = session; + instance->sequence_number = 1; + +#ifdef EP_CHECKED_BUILD + instance->buffer_manager = buffer_manager; +#endif + +ep_on_exit: + return instance; + +ep_on_error: + instance = NULL; + ep_exit_error_handler (); +} + +void +ep_thread_session_state_free (EventPipeThreadSessionState *thread_session_state) +{ + ep_return_void_if_nok (thread_session_state != NULL); + ep_thread_holder_fini (&thread_session_state->thread_holder); + ep_rt_object_free (thread_session_state); +} + +EventPipeThread * +ep_thread_session_state_get_thread (const EventPipeThreadSessionState *thread_session_state) +{ + EP_ASSERT (thread_session_state != NULL); + return thread_session_state->thread_holder.thread; +} + +EventPipeBuffer * +ep_thread_session_state_get_write_buffer (const EventPipeThreadSessionState *thread_session_state) +{ + EP_ASSERT (thread_session_state != NULL); + ep_thread_requires_lock_held (thread_session_state->thread_holder.thread); + + EP_ASSERT ((thread_session_state->write_buffer == NULL) || (ep_rt_volatile_load_uint32_t (&thread_session_state->write_buffer->state) == EP_BUFFER_STATE_WRITABLE)); + return thread_session_state->write_buffer; +} + +void +ep_thread_session_state_set_write_buffer ( + EventPipeThreadSessionState *thread_session_state, + EventPipeBuffer *new_buffer) +{ + EP_ASSERT (thread_session_state != NULL); + ep_thread_requires_lock_held (thread_session_state->thread_holder.thread); + + EP_ASSERT ((new_buffer == NULL) || (ep_rt_volatile_load_uint32_t (&thread_session_state->write_buffer->state) == EP_BUFFER_STATE_WRITABLE)); + EP_ASSERT ((thread_session_state->write_buffer == NULL) || (ep_rt_volatile_load_uint32_t (&thread_session_state->write_buffer->state) == EP_BUFFER_STATE_WRITABLE)); + + if (thread_session_state->write_buffer) + ep_buffer_convert_to_read_only (thread_session_state->write_buffer); + + thread_session_state->write_buffer = new_buffer; +} + +EventPipeBufferList * +ep_thread_session_state_get_buffer_list (const EventPipeThreadSessionState *thread_session_state) +{ + EP_ASSERT (thread_session_state != NULL); + ep_buffer_manager_requires_lock_held (thread_session_state->buffer_manager); + return thread_session_state->buffer_list; +} + +void +ep_thread_session_state_set_buffer_list ( + EventPipeThreadSessionState *thread_session_state, + EventPipeBufferList *new_buffer_list) +{ + EP_ASSERT (thread_session_state != NULL); + ep_buffer_manager_requires_lock_held (thread_session_state->buffer_manager); + thread_session_state->buffer_list = new_buffer_list; +} + +uint32_t +ep_thread_session_state_get_volatile_sequence_number (const EventPipeThreadSessionState *thread_session_state) +{ + EP_ASSERT (thread_session_state != NULL); + return ep_rt_volatile_load_uint32_t_without_barrier (&thread_session_state->sequence_number); +} + +uint32_t +ep_thread_session_state_get_sequence_number (const EventPipeThreadSessionState *thread_session_state) +{ + EP_ASSERT (thread_session_state != NULL); + ep_thread_requires_lock_held (thread_session_state->thread_holder.thread); + return ep_rt_volatile_load_uint32_t_without_barrier (&thread_session_state->sequence_number); +} + +void +ep_thread_session_state_increment_sequence_number (EventPipeThreadSessionState *thread_session_state) +{ + EP_ASSERT (thread_session_state != NULL); + ep_thread_requires_lock_held (thread_session_state->thread_holder.thread); + thread_session_state->sequence_number++; +} + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_thread_internals; +const char quiet_linker_empty_file_warning_eventpipe_thread_internals = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-thread.c b/src/mono/mono/eventpipe/ep-thread.c new file mode 100644 index 0000000000000..68632bea401b0 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-thread.c @@ -0,0 +1,157 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) + +#include "ep.h" + +/* + * EventPipeThread. + */ + +void +ep_thread_addref (EventPipeThread *thread) +{ + EP_ASSERT (thread != NULL); + ep_rt_atomic_inc_int32_t (ep_thread_get_ref_count_ref (thread)); +} + +void +ep_thread_release (EventPipeThread *thread) +{ + EP_ASSERT (thread != NULL); + if (ep_rt_atomic_dec_int32_t (ep_thread_get_ref_count_ref (thread)) == 0) + ep_thread_free (thread); +} + +EventPipeThread * +ep_thread_get (void) +{ + return ep_rt_thread_get (); +} + +EventPipeThread * +ep_thread_get_or_create (void) +{ + return ep_rt_thread_get_or_create (); +} + +void +ep_thread_create_activity_id ( + uint8_t *activity_id, + uint32_t activity_id_len) +{ + ep_return_void_if_nok (activity_id != NULL); + ep_rt_create_activity_id (activity_id, activity_id_len); +} + +void +ep_thread_get_activity_id ( + EventPipeThread *thread, + uint8_t *activity_id, + uint32_t activity_id_len) +{ + ep_return_void_if_nok (thread != NULL && activity_id != NULL); + EP_ASSERT (activity_id_len == EP_ACTIVITY_ID_SIZE); + memcpy (activity_id, ep_thread_get_activity_id_cref (thread), EP_ACTIVITY_ID_SIZE); +} + +void +ep_thread_set_activity_id ( + EventPipeThread *thread, + const uint8_t *activity_id, + uint32_t activity_id_len) +{ + ep_return_void_if_nok (thread != NULL && activity_id != NULL); + EP_ASSERT (activity_id_len == EP_ACTIVITY_ID_SIZE); + memcpy (ep_thread_get_activity_id_ref (thread), activity_id, EP_ACTIVITY_ID_SIZE); +} + +void +ep_thread_set_session_write_in_progress ( + EventPipeThread *thread, + uint32_t session_index) +{ + ep_return_void_if_nok (thread != NULL); + ep_rt_volatile_store_uint32_t (ep_thread_get_writing_event_in_progress_ref (thread), session_index); +} + +uint32_t +ep_thread_get_session_write_in_progress (const EventPipeThread *thread) +{ + ep_return_zero_if_nok (thread != NULL); + return ep_rt_volatile_load_uint32_t (ep_thread_get_writing_event_in_progress_cref (thread)); +} + +EventPipeThreadSessionState * +ep_thread_get_or_create_session_state ( + EventPipeThread *thread, + EventPipeSession *session) +{ + ep_return_null_if_nok (thread != NULL && session != NULL); + EP_ASSERT (ep_session_get_index (session) < EP_MAX_NUMBER_OF_SESSIONS); + ep_thread_requires_lock_held (thread); + + EventPipeThreadSessionState *state = ep_thread_get_session_state_ref (thread)[ep_session_get_index (session)]; + if (!state) { + state = ep_thread_session_state_alloc (thread, session, ep_session_get_buffer_manager (session)); + ep_thread_get_session_state_ref (thread)[ep_session_get_index (session)] = state; + } + + return state; +} + +EventPipeThreadSessionState * +ep_thread_get_session_state ( + const EventPipeThread *thread, + EventPipeSession *session) +{ + ep_return_null_if_nok (thread != NULL && session != NULL); + EP_ASSERT (ep_session_get_index (session) < EP_MAX_NUMBER_OF_SESSIONS); + ep_thread_requires_lock_held (thread); + + EventPipeThreadSessionState *const state = ep_thread_get_session_state_cref (thread)[ep_session_get_index (session)]; + EP_ASSERT (state != NULL); + return state; +} + +void +ep_thread_delete_session_state ( + EventPipeThread *thread, + EventPipeSession *session) +{ + ep_return_void_if_nok (thread != NULL && session != NULL); + ep_thread_requires_lock_held (thread); + + uint32_t index = ep_session_get_index (session); + EP_ASSERT (index < EP_MAX_NUMBER_OF_SESSIONS); + EventPipeThreadSessionState *state = ep_thread_get_session_state_ref (thread)[index]; + EP_ASSERT (state != NULL); + ep_thread_session_state_free (state); + ep_thread_get_session_state_ref (thread)[index] = NULL; +} + +#ifdef EP_CHECKED_BUILD +void +ep_thread_requires_lock_held (const EventPipeThread *thread) +{ + EP_ASSERT (thread != NULL); + ep_rt_spin_lock_requires_lock_held (ep_thread_get_rt_lock_cref (thread)); +} + +void +ep_thread_requires_lock_not_held (const EventPipeThread *thread) +{ + EP_ASSERT (thread != NULL); + ep_rt_spin_lock_requires_lock_not_held (ep_thread_get_rt_lock_cref (thread)); +} +#endif + +#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ +#endif /* ENABLE_PERFTRACING */ + +#ifndef EP_INCLUDE_SOURCE_FILES +extern const char quiet_linker_empty_file_warning_eventpipe_thread; +const char quiet_linker_empty_file_warning_eventpipe_thread = 0; +#endif diff --git a/src/mono/mono/eventpipe/ep-thread.h b/src/mono/mono/eventpipe/ep-thread.h new file mode 100644 index 0000000000000..23d0e5093f237 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-thread.h @@ -0,0 +1,233 @@ +#ifndef __EVENTPIPE_THREAD_H__ +#define __EVENTPIPE_THREAD_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" + +/* + * EventPipeThread. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeThread { +#else +struct _EventPipeThread_Internal { +#endif + EventPipeThreadSessionState *session_state [EP_MAX_NUMBER_OF_SESSIONS]; + uint8_t activity_id [EP_ACTIVITY_ID_SIZE]; + EventPipeSession *rundown_session; + size_t os_thread_id; + ep_rt_thread_handle_t rt_thread; + ep_rt_spin_lock_handle_t rt_lock; + int32_t ref_count; + volatile uint32_t writing_event_in_progress; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeThread { + uint8_t _internal [sizeof (struct _EventPipeThread_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_ARRAY_REF(EventPipeThread *, thread, EventPipeThreadSessionState **, EventPipeThreadSessionState *const*, session_state, session_state[0]); +EP_DEFINE_GETTER_ARRAY_REF(EventPipeThread *, thread, uint8_t *, const uint8_t *, activity_id, activity_id[0]); +EP_DEFINE_GETTER(EventPipeThread *, thread, EventPipeSession *, rundown_session); +EP_DEFINE_SETTER(EventPipeThread *, thread, EventPipeSession *, rundown_session); +EP_DEFINE_GETTER(EventPipeThread *, thread, size_t, os_thread_id); +EP_DEFINE_GETTER_REF(EventPipeThread *, thread, ep_rt_spin_lock_handle_t *, rt_lock); +EP_DEFINE_GETTER_REF(EventPipeThread *, thread, int32_t *, ref_count); +EP_DEFINE_GETTER_REF(EventPipeThread *, thread, volatile uint32_t *, writing_event_in_progress); + +EventPipeThread * +ep_thread_alloc (void); + +void +ep_thread_free (EventPipeThread *thread); + +void +ep_thread_addref (EventPipeThread *thread); + +void +ep_thread_release (EventPipeThread *thread); + +EventPipeThread * +ep_thread_get (void); + +EventPipeThread * +ep_thread_get_or_create (void); + +void +ep_thread_create_activity_id ( + uint8_t *activity_id, + uint32_t activity_id_len); + +void +ep_thread_get_activity_id ( + EventPipeThread *thread, + uint8_t *activity_id, + uint32_t activity_id_len); + +void +ep_thread_set_activity_id ( + EventPipeThread *thread, + const uint8_t *activity_id, + uint32_t activity_id_len); + +static +inline +void +ep_thread_set_as_rundown_thread ( + EventPipeThread *thread, + EventPipeSession *session) +{ + EP_ASSERT (thread != NULL); + ep_thread_set_rundown_session (thread, session); +} + +static +inline +bool +ep_thread_is_rundown_thread (const EventPipeThread *thread) +{ + EP_ASSERT (thread != NULL); + return (ep_thread_get_rundown_session (thread) != NULL); +} + +#ifdef EP_CHECKED_BUILD +void +ep_thread_requires_lock_held (const EventPipeThread *thread); + +void +ep_thread_requires_lock_not_held (const EventPipeThread *thread); +#else +#define ep_thread_requires_lock_held(x) +#define ep_thread_requires_lock_not_held(x) +#endif + +void +ep_thread_set_session_write_in_progress ( + EventPipeThread *thread, + uint32_t session_index); + +uint32_t +ep_thread_get_session_write_in_progress (const EventPipeThread *thread); + +EventPipeThreadSessionState * +ep_thread_get_or_create_session_state ( + EventPipeThread *thread, + EventPipeSession *session); + +EventPipeThreadSessionState * +ep_thread_get_session_state ( + const EventPipeThread *thread, + EventPipeSession *session); + +void +ep_thread_delete_session_state ( + EventPipeThread *thread, + EventPipeSession *session); + +/* + * EventPipeThreadHolder. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeThreadHolder { +#else +struct _EventPipeThreadHolder_Internal { +#endif + EventPipeThread *thread; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeThreadHolder { + uint8_t _internal [sizeof (struct _EventPipeThreadHolder_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeThreadHolder *, thread_holder, EventPipeThread *, thread) + +EventPipeThreadHolder * +ep_thread_holder_alloc (EventPipeThread *thread); + +EventPipeThreadHolder * +ep_thread_holder_init ( + EventPipeThreadHolder *thread_holder, + EventPipeThread *thread); + +void +ep_thread_holder_fini (EventPipeThreadHolder *thread_holder); + +void +ep_thread_holder_free (EventPipeThreadHolder *thread_holder); + +/* + * EventPipeThreadSessionState. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeThreadSessionState { +#else +struct _EventPipeThreadSessionState_Internal { +#endif + EventPipeThreadHolder thread_holder; + EventPipeSession *session; + EventPipeBuffer *write_buffer; + EventPipeBufferList *buffer_list; +#ifdef EP_CHECKED_BUILD + EventPipeBufferManager *buffer_manager; +#endif + volatile uint32_t sequence_number; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeThreadSessionState { + uint8_t _internal [sizeof (struct _EventPipeThreadSessionState_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_REF(EventPipeThreadSessionState *, thread_session_state, EventPipeThreadHolder *, thread_holder) +EP_DEFINE_GETTER(EventPipeThreadSessionState *, thread_session_state, EventPipeSession *, session) + +EventPipeThreadSessionState * +ep_thread_session_state_alloc ( + EventPipeThread *thread, + EventPipeSession *session, + EventPipeBufferManager *buffer_manager); + +void +ep_thread_session_state_free (EventPipeThreadSessionState *thread_session_state); + +EventPipeThread * +ep_thread_session_state_get_thread (const EventPipeThreadSessionState *thread_session_state); + +EventPipeBuffer * +ep_thread_session_state_get_write_buffer (const EventPipeThreadSessionState *thread_session_state); + +void +ep_thread_session_state_set_write_buffer ( + EventPipeThreadSessionState *thread_session_state, + EventPipeBuffer *new_buffer); + +EventPipeBufferList * +ep_thread_session_state_get_buffer_list (const EventPipeThreadSessionState *thread_session_state); + +void +ep_thread_session_state_set_buffer_list ( + EventPipeThreadSessionState *thread_session_state, + EventPipeBufferList *new_buffer_list); + +uint32_t +ep_thread_session_state_get_volatile_sequence_number (const EventPipeThreadSessionState *thread_session_state); + +uint32_t +ep_thread_session_state_get_sequence_number (const EventPipeThreadSessionState *thread_session_state); + +void +ep_thread_session_state_increment_sequence_number (EventPipeThreadSessionState *thread_session_state); + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_THREAD_H__ **/ diff --git a/src/mono/mono/eventpipe/ep-types.h b/src/mono/mono/eventpipe/ep-types.h new file mode 100644 index 0000000000000..7e25f32016c96 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-types.h @@ -0,0 +1,388 @@ +#ifndef __EVENTPIPE_TYPES_H__ +#define __EVENTPIPE_TYPES_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include +#include +#include "ep-rt-types.h" + +/* + * EventPipe Structs. + */ + +typedef struct _EventData EventData; +typedef struct _EventFilterDescriptor EventFilterDescriptor; +typedef struct _EventPipeBuffer EventPipeBuffer; +typedef struct _EventPipeBufferList EventPipeBufferList; +typedef struct _EventPipeBufferManager EventPipeBufferManager; +typedef struct _EventPipeBlock EventPipeBlock; +typedef struct _EventPipeBlockVtable EventPipeBlockVtable; +typedef struct _EventPipeConfiguration EventPipeConfiguration; +typedef struct _EventPipeEvent EventPipeEvent; +typedef struct _EventPipeEventBlockBase EventPipeEventBlockBase; +typedef struct _EventPipeEventBlock EventPipeEventBlock; +typedef struct _EventPipeEventHeader EventPipeEventHeader; +typedef struct _EventPipeEventInstance EventPipeEventInstance; +typedef struct _EventPipeEventMetadataEvent EventPipeEventMetadataEvent; +typedef struct _EventPipeEventSource EventPipeEventSource; +typedef struct _EventPipeFile EventPipeFile; +typedef struct _EventPipeMetadataBlock EventPipeMetadataBlock; +typedef struct _EventPipeParameterDesc EventPipeParameterDesc; +typedef struct _EventPipeProvider EventPipeProvider; +typedef struct _EventPipeProviderCallbackData EventPipeProviderCallbackData; +typedef struct _EventPipeProviderCallbackDataQueue EventPipeProviderCallbackDataQueue; +typedef struct _EventPipeProviderConfiguration EventPipeProviderConfiguration; +typedef struct _EventPipeSession EventPipeSession; +typedef struct _EventPipeSessionProvider EventPipeSessionProvider; +typedef struct _EventPipeSessionProviderList EventPipeSessionProviderList; +typedef struct _EventPipeSequencePoint EventPipeSequencePoint; +typedef struct _EventPipeSequencePointBlock EventPipeSequencePointBlock; +typedef struct _EventPipeStackBlock EventPipeStackBlock; +typedef struct _EventPipeStackContents EventPipeStackContents; +typedef struct _EventPipeThread EventPipeThread; +typedef struct _EventPipeThreadHolder EventPipeThreadHolder; +typedef struct _EventPipeThreadSessionState EventPipeThreadSessionState; +typedef struct _FastSerializableObject FastSerializableObject; +typedef struct _FastSerializableObjectVtable FastSerializableObjectVtable; +typedef struct _FastSerializer FastSerializer; +typedef struct _FileStream FileStream; +typedef struct _FileStreamWriter FileStreamWriter; +typedef struct _IpcStream IpcStream; +typedef struct _IpcStreamWriter IpcStreamWriter; +typedef struct _StackHashEntry StackHashEntry; +typedef struct _StackHashKey StackHashKey; +typedef struct _StreamWriter StreamWriter; +typedef struct _StreamWriterVtable StreamWriterVtable; + +#ifdef EP_INLINE_GETTER_SETTER +#ifndef EP_DEFINE_GETTER +#define EP_DEFINE_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); } +#endif + +#ifndef EP_DEFINE_GETTER_REF +#define EP_DEFINE_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); } +#endif + +#ifndef EP_DEFINE_GETTER_ARRAY_REF +#define EP_DEFINE_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); } +#endif + +#ifndef EP_DEFINE_SETTER +#define EP_DEFINE_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; } +#endif +#else +#ifndef EP_DEFINE_GETTER +#define EP_DEFINE_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); +#endif + +#ifndef EP_DEFINE_GETTER_REF +#define EP_DEFINE_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); +#endif + +#ifndef EP_DEFINE_GETTER_ARRAY_REF +#define EP_DEFINE_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); +#endif + +#ifndef EP_DEFINE_SETTER +#define EP_DEFINE_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); +#endif +#endif + +#define EP_MAX_NUMBER_OF_SESSIONS 64 + +#define EP_ACTIVITY_ID_SIZE 16 + +#define EP_MAX_STACK_DEPTH 100 + +/* + * EventPipe Enums. + */ + +typedef enum { + EP_BUFFER_STATE_WRITABLE = 0, + EP_BUFFER_STATE_READ_ONLY = 1 +} EventPipeBufferState; + +typedef enum { + EP_EVENT_LEVEL_LOG_ALWAYS, + EP_EVENT_LEVEL_CRITICAL, + EP_EVENT_LEVEL_ERROR, + EP_EVENT_LEVEL_WARNING, + EP_EVENT_LEVEL_INFORMATIONAL, + EP_EVENT_LEVEL_VERBOSE +} EventPipeEventLevel; + +typedef enum { + EP_FILE_FLUSH_FLAGS_EVENT_BLOCK = 1, + EP_FILE_FLUSH_FLAGS_METADATA_BLOCK = 2, + EP_FILE_FLUSH_FLAGS_STACK_BLOCK = 4, + EP_FILE_FLUSH_FLAGS_ALL_BLOCKS = EP_FILE_FLUSH_FLAGS_EVENT_BLOCK | EP_FILE_FLUSH_FLAGS_METADATA_BLOCK | EP_FILE_FLUSH_FLAGS_STACK_BLOCK +} EventPipeFileFlushFlags; + +// Represents the type of an event parameter. +// This enum is derived from the managed TypeCode type, though +// not all of these values are available in TypeCode. +// For example, Guid does not exist in TypeCode. +// Keep this in sync with COR_PRF_EVENTPIPE_PARAM_TYPE defined in +// corprof.idl +typedef enum { + EP_PARAMETER_TYPE_EMPTY = 0, // Null reference + EP_PARAMETER_TYPE_OBJECT = 1, // Instance that isn't a value + EP_PARAMETER_TYPE_DB_NULL = 2, // Database null value + EP_PARAMETER_TYPE_BOOLEAN = 3, // Boolean + EP_PARAMETER_TYPE_CHAR = 4, // Unicode character + EP_PARAMETER_TYPE_SBYTE = 5, // Signed 8-bit integer + EP_PARAMETER_TYPE_BYTE = 6, // Unsigned 8-bit integer + EP_PARAMETER_TYPE_INT16 = 7, // Signed 16-bit integer + EP_PARAMETER_TYPE_UINT16 = 8, // Unsigned 16-bit integer + EP_PARAMETER_TYPE_INT32 = 9, // Signed 32-bit integer + EP_PARAMETER_TYPE_UINT32 = 10, // Unsigned 32-bit integer + EP_PARAMETER_TYPE_INT64 = 11, // Signed 64-bit integer + EP_PARAMETER_TYPE_UINT64 = 12, // Unsigned 64-bit integer + EP_PARAMETER_TYPE_SINGLE = 13, // IEEE 32-bit float + EP_PARAMETER_TYPE_DOUBLE = 14, // IEEE 64-bit double + EP_PARAMETER_TYPE_DECIMAL = 15, // Decimal + EP_PARAMETER_TYPE_DATE_TIME = 16, // DateTime + EP_PARAMETER_TYPE_GUID = 17, // Guid + EP_PARAMETER_TYPE_STRING = 18 // Unicode character string +} EventPipeParameterType; + +typedef enum { + EP_SERIALIZATION_FORMAT_NETPERF_V3, + EP_SERIALIZATION_FORMAT_NETTRACE_V4, + EP_SERIALIZATION_FORMAT_COUNT +} EventPipeSerializationFormat; + +typedef enum { + EP_SESSION_TYPE_FILE, + EP_SESSION_TYPE_LISTENER, + EP_SESSION_TYPE_IPCSTREAM +} EventPipeSessionType ; + +typedef enum { + EP_STATE_NOT_INITIALIZED, + EP_STATE_INITIALIZED, + EP_STATE_SHUTTING_DOWN +} EventPipeState; + +/* + * EventPipe Basic Types. + */ + +typedef intptr_t EventPipeWaitHandle; +typedef uint64_t EventPipeSessionID; +typedef char ep_char8_t; +typedef unsigned short ep_char16_t; + +/* + * EventPipe Callbacks. + */ + +// Define the event pipe callback to match the ETW callback signature. +typedef void (*EventPipeCallback)( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_context); + +/* + * EventFilterDescriptor. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventFilterDescriptor { +#else +struct _EventFilterDescriptor_Internal { +#endif + uint64_t ptr; + uint32_t size; + uint32_t type; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventFilterDescriptor { + uint8_t _internal [sizeof (struct _EventFilterDescriptor_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventFilterDescriptor *, event_filter_desc, uint64_t, ptr) +EP_DEFINE_GETTER(EventFilterDescriptor *, event_filter_desc, uint32_t, size) +EP_DEFINE_GETTER(EventFilterDescriptor *, event_filter_desc, uint32_t, type) + +EventFilterDescriptor * +ep_event_filter_desc_alloc ( + uint64_t ptr, + uint32_t size, + uint32_t type); + +EventFilterDescriptor * +ep_event_filter_desc_init ( + EventFilterDescriptor *event_filter_desc, + uint64_t ptr, + uint32_t size, + uint32_t type +); + +void +ep_event_filter_desc_fini (EventFilterDescriptor * filter_desc); + +void +ep_event_filter_desc_free (EventFilterDescriptor * filter_desc); + +/* + * EventPipeProviderCallbackData. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeProviderCallbackData { +#else +struct _EventPipeProviderCallbackData_Internal { +#endif + const ep_char8_t *filter_data; + EventPipeCallback callback_function; + void *callback_data; + int64_t keywords; + EventPipeEventLevel provider_level; + bool enabled; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeProviderCallbackData { + uint8_t _internal [sizeof (struct _EventPipeProviderCallbackData_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeProviderCallbackData *, provider_callback_data, const ep_char8_t *, filter_data) +EP_DEFINE_GETTER(EventPipeProviderCallbackData *, provider_callback_data, EventPipeCallback, callback_function) +EP_DEFINE_GETTER(EventPipeProviderCallbackData *, provider_callback_data, void *, callback_data) +EP_DEFINE_GETTER(EventPipeProviderCallbackData *, provider_callback_data, int64_t, keywords) +EP_DEFINE_GETTER(EventPipeProviderCallbackData *, provider_callback_data, EventPipeEventLevel, provider_level) +EP_DEFINE_GETTER(EventPipeProviderCallbackData *, provider_callback_data, bool, enabled) + +EventPipeProviderCallbackData * +ep_provider_callback_data_alloc ( + const ep_char8_t *filter_data, + EventPipeCallback callback_function, + void *callback_data, + int64_t keywords, + EventPipeEventLevel provider_level, + bool enabled); + +EventPipeProviderCallbackData * +ep_provider_callback_data_alloc_copy (EventPipeProviderCallbackData *provider_callback_data_src); + +EventPipeProviderCallbackData * +ep_provider_callback_data_init ( + EventPipeProviderCallbackData *provider_callback_data, + const ep_char8_t *filter_data, + EventPipeCallback callback_function, + void *callback_data, + int64_t keywords, + EventPipeEventLevel provider_level, + bool enabled); + +EventPipeProviderCallbackData * +ep_provider_callback_data_init_copy ( + EventPipeProviderCallbackData *provider_callback_data_dst, + EventPipeProviderCallbackData *provider_callback_data_src); + +void +ep_provider_callback_data_fini (EventPipeProviderCallbackData *provider_callback_data); + +void +ep_provider_callback_data_free (EventPipeProviderCallbackData *provider_callback_data); + +/* + * EventPipeProviderCallbackDataQueue. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeProviderCallbackDataQueue { +#else +struct _EventPipeProviderCallbackDataQueue_Internal { +#endif + ep_rt_provider_callback_data_queue_t queue; +}; + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeProviderCallbackDataQueue { + uint8_t _internal [sizeof (struct _EventPipeProviderCallbackDataQueue_Internal)]; +}; +#endif + +EP_DEFINE_GETTER_REF(EventPipeProviderCallbackDataQueue *, provider_callback_data_queue, ep_rt_provider_callback_data_queue_t *, queue) + +EventPipeProviderCallbackDataQueue * +ep_provider_callback_data_queue_init (EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + +void +ep_provider_callback_data_queue_fini (EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + +void +ep_provider_callback_data_queue_enqueue ( + EventPipeProviderCallbackDataQueue *provider_callback_data_queue, + EventPipeProviderCallbackData *provider_callback_data); + +bool +ep_provider_callback_data_queue_try_dequeue ( + EventPipeProviderCallbackDataQueue *provider_callback_data_queue, + EventPipeProviderCallbackData *provider_callback_data); + +/* + * EventPipeProviderConfiguration. + */ + +#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeProviderConfiguration { +#else +struct _EventPipeProviderConfiguration_Internal { +#endif + const ep_char8_t *provider_name; + uint64_t keywords; + EventPipeEventLevel logging_level; + const ep_char8_t *filter_data; +}; + + +#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_GETTER_SETTER) +struct _EventPipeProviderConfiguration { + uint8_t _internal [sizeof (struct _EventPipeProviderConfiguration_Internal)]; +}; +#endif + +EP_DEFINE_GETTER(EventPipeProviderConfiguration *, provider_config, const ep_char8_t *, provider_name) +EP_DEFINE_GETTER(EventPipeProviderConfiguration *, provider_config, uint64_t, keywords) +EP_DEFINE_GETTER(EventPipeProviderConfiguration *, provider_config, EventPipeEventLevel, logging_level) +EP_DEFINE_GETTER(EventPipeProviderConfiguration *, provider_config, const ep_char8_t *, filter_data) + +EventPipeProviderConfiguration * +ep_provider_config_init ( + EventPipeProviderConfiguration *provider_config, + const ep_char8_t *provider_name, + uint64_t keywords, + uint32_t logging_level, + const ep_char8_t *filter_data); + +void +ep_provider_config_fini (EventPipeProviderConfiguration *provider_config); + +#endif /* ENABLE_PERFTRACING */ +#endif /* __EVENTPIPE_TYPES_H__ */ diff --git a/src/mono/mono/eventpipe/ep.c b/src/mono/mono/eventpipe/ep.c new file mode 100644 index 0000000000000..3725a8281eb8f --- /dev/null +++ b/src/mono/mono/eventpipe/ep.c @@ -0,0 +1,711 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep.h" + +// Option to include all internal source files into ep.c. +#ifdef EP_INCLUDE_SOURCE_FILES +#define EP_FORCE_INCLUDE_SOURCE_FILES +#include "ep-block.c" +#include "ep-buffer-manager.c" +#include "ep-config.c" +#include "ep-event-instance.c" +#include "ep-event-source.c" +#include "ep-file.c" +#include "ep-metadata-generator.c" +#include "ep-provider.c" +#include "ep-session.c" +#include "ep-session-provider.c" +#include "ep-stream.c" +#include "ep-thread.c" +#endif + +/* + * Forward declares of all static functions. + */ + +static +bool +enabled_lock_held (void); + +static +uint32_t +generate_session_index_lock_held (void); + +static +bool +is_session_id_in_collection_lock_held (EventPipeSessionID id); + +static +EventPipeSessionID +enable_lock_held ( + const ep_char8_t *output_path, + uint32_t circular_buffer_size_in_mb, + const EventPipeProviderConfiguration *providers, + uint32_t providers_len, + EventPipeSessionType session_type, + EventPipeSerializationFormat format, + bool rundown_requested, + IpcStream *stream, + bool enable_sample_profiler, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + +static +void +log_process_info_event (EventPipeEventSource *event_source); + +static +void +disable_lock_held ( + EventPipeSessionID id, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + +static +void +disable ( + EventPipeSessionID id, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue); + +/* + * Global volatile varaibles, only to be accessed through inlined volatile access functions. + */ + +volatile EventPipeState _ep_state = EP_STATE_NOT_INITIALIZED; + +volatile uint32_t _ep_number_of_sessions = 0; + +volatile EventPipeSession *_ep_sessions [EP_MAX_NUMBER_OF_SESSIONS] = { 0 }; + +volatile uint64_t _ep_allow_write = 0; + +/* + * EventPipe. + */ + +static +bool +enabled_lock_held (void) +{ + ep_rt_config_requires_lock_held (); + return (ep_volatile_load_eventpipe_state_without_barrier () >= EP_STATE_INITIALIZED && + ep_volatile_load_number_of_sessions_without_barrier () > 0); +} + +static +uint32_t +generate_session_index_lock_held (void) +{ + ep_rt_config_requires_lock_held (); + + for (uint32_t i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; ++i) + if (ep_volatile_load_session_without_barrier (i) == NULL) + return i; + return EP_MAX_NUMBER_OF_SESSIONS; +} + +static +bool +is_session_id_in_collection_lock_held (EventPipeSessionID session_id) +{ + ep_rt_config_requires_lock_held (); + EP_ASSERT (session_id != 0); + + const EventPipeSession *const session = (EventPipeSession *)session_id; + for (uint32_t i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; ++i) { + if (ep_volatile_load_session (i) == session) { + EP_ASSERT (i == ep_session_get_index (session)); + return true; + } + } + + return false; +} + +static +EventPipeSessionID +enable_lock_held ( + const ep_char8_t *output_path, + uint32_t circular_buffer_size_in_mb, + const EventPipeProviderConfiguration *providers, + uint32_t providers_len, + EventPipeSessionType session_type, + EventPipeSerializationFormat format, + bool rundown_requested, + IpcStream *stream, + bool enable_sample_profiler, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +{ + ep_rt_config_requires_lock_held (); + EP_ASSERT (format < EP_SERIALIZATION_FORMAT_COUNT); + EP_ASSERT (circular_buffer_size_in_mb > 0); + EP_ASSERT (providers_len > 0 && providers != NULL); + EP_ASSERT ((session_type == EP_SESSION_TYPE_FILE && output_path != NULL) ||(session_type == EP_SESSION_TYPE_IPCSTREAM && stream != NULL)); + + EventPipeSession *session = NULL; + EventPipeSessionID session_id = 0; + + ep_raise_error_if_nok (ep_volatile_load_eventpipe_state () == EP_STATE_INITIALIZED); + + const uint32_t session_index = generate_session_index_lock_held (); + ep_raise_error_if_nok (session_index < EP_MAX_NUMBER_OF_SESSIONS); + + session = ep_session_alloc ( + session_index, + output_path, + stream, + session_type, + format, + rundown_requested, + circular_buffer_size_in_mb, + providers, + providers_len, + FALSE); + + ep_raise_error_if_nok (session != NULL && ep_session_is_valid (session) == true); + + session_id = (EventPipeSessionID)session; + + // Return if the index is invalid. + if (ep_session_get_index (session) >= EP_MAX_NUMBER_OF_SESSIONS) { + EP_ASSERT (!"Session index was out of range."); + ep_raise_error (); + } + + if (ep_volatile_load_number_of_sessions () >= EP_MAX_NUMBER_OF_SESSIONS) { + EP_ASSERT (!"max number of sessions reached."); + ep_raise_error (); + } + + // Register the SampleProfiler the very first time (if supported). + ep_rt_sample_profiler_init (provider_callback_data_queue); + + // Enable the EventPipe EventSource. + ep_event_source_enable (ep_event_source_get (), session); + + // Save the session. + if (ep_volatile_load_session_without_barrier (ep_session_get_index (session)) != NULL) { + EP_ASSERT (!"Attempting to override an existing session."); + ep_raise_error (); + } + + ep_volatile_store_session (ep_session_get_index (session), session); + + ep_volatile_store_allow_write (ep_volatile_load_allow_write () | ep_session_get_mask (session)); + ep_volatile_store_number_of_sessions (ep_volatile_load_number_of_sessions () + 1); + + // Enable tracing. + ep_config_enable_disable_lock_held (ep_config_get (), session, provider_callback_data_queue, true); + + session = NULL; + + // Enable the sample profiler (if supported). + if (enable_sample_profiler) + ep_rt_sample_profiler_enable (); + +ep_on_exit: + ep_rt_config_requires_lock_held (); + return session_id; + +ep_on_error: + ep_session_free (session); + + session_id = 0; + ep_exit_error_handler (); +} + +static +void +log_process_info_event (EventPipeEventSource *event_source) +{ + // Get the managed command line. + const ep_char8_t *cmd_line = ep_rt_managed_command_line_get (); + + if (cmd_line == NULL) + cmd_line = ep_rt_command_line_get (); + + // Log the process information event. + ep_char16_t *cmd_line_utf16 = ep_rt_utf8_to_utf16_string (cmd_line, -1); + if (cmd_line_utf16 != NULL) { + ep_event_source_send_process_info (event_source, cmd_line_utf16); + ep_rt_utf16_string_free (cmd_line_utf16); + } +} + +static +void +disable_lock_held ( + EventPipeSessionID id, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +{ + ep_rt_config_requires_lock_held (); + EP_ASSERT (id != 0); + EP_ASSERT (ep_volatile_load_number_of_sessions () > 0); + + if (is_session_id_in_collection_lock_held (id)) { + EventPipeSession *const session = (EventPipeSession *)id; + + // Disable the profiler. + ep_rt_sample_profiler_disable (); + + // Log the process information event. + log_process_info_event (ep_event_source_get ()); + + // Disable session tracing. + ep_config_enable_disable_lock_held (ep_config_get (), session, provider_callback_data_queue, false); + + ep_session_disable (session); // WriteAllBuffersToFile, and remove providers. + + // Do rundown before fully stopping the session unless rundown wasn't requested + if (ep_session_get_rundown_requested (session)) { + ep_session_enable_rundown_lock_held (session); // Set Rundown provider. + EventPipeThread *const thread = ep_thread_get (); + if (thread != NULL) { + ep_thread_set_as_rundown_thread (thread, session); + { + ep_config_enable_disable_lock_held (ep_config_get (), session, provider_callback_data_queue, true); + { + ep_session_execute_rundown_lock_held (session); + } + ep_config_enable_disable_lock_held (ep_config_get (), session, provider_callback_data_queue, false); + } + ep_thread_set_as_rundown_thread (thread, NULL); + } else { + EP_ASSERT (!"Failed to get or create the EventPipeThread for rundown events."); + } + } + + ep_volatile_store_allow_write (ep_volatile_load_allow_write () & ~(ep_session_get_mask (session))); + ep_session_suspend_write_event_lock_held (session); + + bool ignored; + ep_session_write_all_buffers_to_file (session, &ignored); // Flush the buffers to the stream/file + + ep_volatile_store_number_of_sessions (ep_volatile_load_number_of_sessions () - 1); + + // At this point, we should not be writing events to this session anymore + // This is a good time to remove the session from the array. + EP_ASSERT (ep_volatile_load_session (ep_session_get_index (session)) == session); + + // Remove the session from the array, and mask. + ep_volatile_store_session (ep_session_get_index (session), NULL); + + // Write a final sequence point to the file now that all events have + // been emitted. + ep_session_write_sequence_point_unbuffered_lock_held (session); + + ep_session_free (session); + + // Providers can't be deleted during tracing because they may be needed when serializing the file. + ep_config_delete_deferred_providers_lock_held (ep_config_get ()); + } + + ep_rt_config_requires_lock_held (); + return; +} + +static +void +disable ( + EventPipeSessionID id, + EventPipeProviderCallbackDataQueue *provider_callback_data_queue) +{ + ep_rt_config_requires_lock_not_held (); + + EP_CONFIG_LOCK_ENTER + disable_lock_held (id, provider_callback_data_queue); + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +EventPipeSessionID +ep_enable ( + const ep_char8_t *output_path, + uint32_t circular_buffer_size_in_mb, + const EventPipeProviderConfiguration *providers, + uint32_t providers_len, + EventPipeSessionType session_type, + EventPipeSerializationFormat format, + bool rundown_requested, + IpcStream *stream, + bool enable_sample_profiler) +{ + ep_rt_config_requires_lock_not_held (); + EP_ASSERT (format < EP_SERIALIZATION_FORMAT_COUNT); + EP_ASSERT (circular_buffer_size_in_mb > 0); + EP_ASSERT (providers_len > 0 && providers != NULL); + + // If the state or arguments are invalid, bail here. + if (session_type == EP_SESSION_TYPE_FILE && output_path == NULL) + return 0; + if (session_type == EP_SESSION_TYPE_IPCSTREAM && stream == NULL) + return 0; + + EventPipeSessionID session_id = 0; + EventPipeProviderCallbackDataQueue callback_data_queue; + EventPipeProviderCallbackData provider_callback_data; + EventPipeProviderCallbackDataQueue *provider_callback_data_queue = ep_provider_callback_data_queue_init (&callback_data_queue); + + EP_CONFIG_LOCK_ENTER + session_id = enable_lock_held ( + output_path, + circular_buffer_size_in_mb, + providers, + providers_len, + session_type, + format, + rundown_requested, + stream, + enable_sample_profiler, + provider_callback_data_queue); + EP_CONFIG_LOCK_EXIT + + while (ep_provider_callback_data_queue_try_dequeue (provider_callback_data_queue, &provider_callback_data)) + ep_provider_invoke_callback (&provider_callback_data); + +ep_on_exit: + ep_provider_callback_data_queue_fini (provider_callback_data_queue); + ep_rt_config_requires_lock_not_held (); + return session_id; + +ep_on_error: + session_id = 0; + ep_exit_error_handler (); +} + +void +ep_disable (EventPipeSessionID id) +{ + ep_rt_config_requires_lock_not_held (); + + //TODO: Why is this needed? Just to make sure thread is attached to runtime for + //EP_GCX_PREEMP_ENTER/EP_GCX_PREEMP_EXIT to work? + ep_rt_thread_setup (); + + if (id == 0) + return; + + // Don't block GC during clean-up. + EP_GCX_PREEMP_ENTER + + EventPipeProviderCallbackDataQueue callback_data_queue; + EventPipeProviderCallbackData provider_callback_data; + EventPipeProviderCallbackDataQueue *provider_callback_data_queue = ep_provider_callback_data_queue_init (&callback_data_queue); + + disable (id, provider_callback_data_queue); + + while (ep_provider_callback_data_queue_try_dequeue (provider_callback_data_queue, &provider_callback_data)) + ep_provider_invoke_callback (&provider_callback_data); + + ep_provider_callback_data_queue_fini (provider_callback_data_queue); + + EP_GCX_PREEMP_EXIT + + ep_rt_config_requires_lock_not_held (); + return; +} + +EventPipeSession * +ep_get_session (EventPipeSessionID session_id) +{ + ep_rt_config_requires_lock_not_held (); + + EP_CONFIG_LOCK_ENTER + + if (ep_volatile_load_eventpipe_state () == EP_STATE_NOT_INITIALIZED) { + EP_ASSERT (!"EventPipe::GetSession invoked before EventPipe was initialized."); + ep_raise_error_holding_lock (); + } + + ep_raise_error_if_nok_holding_lock (is_session_id_in_collection_lock_held (session_id) == true); + + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return (EventPipeSession *)session_id; + +ep_on_error: + session_id = 0; + ep_exit_error_handler (); +} + +bool +ep_enabled (void) +{ + //TODO: Validate if ever called without holding lock, if so should check be atomic? + return (ep_volatile_load_eventpipe_state () >= EP_STATE_INITIALIZED && + ep_volatile_load_number_of_sessions () > 0); +} + +EventPipeProvider * +ep_create_provider ( + const ep_char8_t *provider_name, + EventPipeCallback callback_func, + void *callback_data) +{ + ep_rt_config_requires_lock_not_held (); + + ep_return_null_if_nok (provider_name != NULL); + + EventPipeProvider *provider = NULL; + EventPipeProviderCallbackDataQueue data_queue; + EventPipeProviderCallbackData provider_callback_data; + EventPipeProviderCallbackDataQueue *provider_callback_data_queue = ep_provider_callback_data_queue_init (&data_queue); + + EP_CONFIG_LOCK_ENTER + provider = ep_config_create_provider_lock_held (ep_config_get (), provider_name, callback_func, callback_data, provider_callback_data_queue); + ep_raise_error_if_nok_holding_lock (provider != NULL); + EP_CONFIG_LOCK_EXIT + + while (ep_provider_callback_data_queue_try_dequeue (provider_callback_data_queue, &provider_callback_data)) + ep_provider_invoke_callback (&provider_callback_data); + +ep_on_exit: + ep_provider_callback_data_queue_fini (provider_callback_data_queue); + ep_rt_config_requires_lock_not_held (); + return provider; + +ep_on_error: + ep_delete_provider (provider); + + provider = NULL; + ep_exit_error_handler (); +} + +void +ep_delete_provider (EventPipeProvider *provider) +{ + ep_rt_config_requires_lock_not_held (); + + ep_return_void_if_nok (provider != NULL); + + // Take the lock to make sure that we don't have a race + // between disabling tracing and deleting a provider + // where we hold a provider after tracing has been disabled. + EP_CONFIG_LOCK_ENTER + if (enabled_lock_held ()) { + // Save the provider until the end of the tracing session. + ep_provider_set_delete_deferred (provider, true); + } else { + ep_config_delete_provider_lock_held (ep_config_get (), provider); + } + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +EventPipeProvider * +ep_get_provider (const ep_char8_t *provider_name) +{ + ep_rt_config_requires_lock_not_held (); + + ep_return_null_if_nok (provider_name != NULL); + + EventPipeProvider *provider = NULL; + + EP_CONFIG_LOCK_ENTER + provider = ep_config_get_provider_lock_held (ep_config_get (), provider_name); + ep_raise_error_if_nok_holding_lock (provider != NULL); + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return provider; + +ep_on_error: + provider = NULL; + ep_exit_error_handler (); +} + +void +ep_init (void) +{ + ep_rt_init (); + ep_rt_config_requires_lock_not_held (); + + //TODO: Implement. Locking pattern between init/shutdown is racy but same as CoreCLR. Needs to be revisited. + if (ep_volatile_load_eventpipe_state () != EP_STATE_NOT_INITIALIZED) { + EP_ASSERT (!"EventPipe already initialized."); + return; + } + + for (uint32_t i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; ++i) + ep_volatile_store_session (i, NULL); + + if (ep_config_init (ep_config_get ())) { + EP_CONFIG_LOCK_ENTER + ep_volatile_store_eventpipe_state_without_barrier (EP_STATE_INITIALIZED); + EP_CONFIG_LOCK_EXIT + } + + //TODO: Implement. + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +void +ep_finish_init (void) +{ + //TODO: Implement. +} + +void +ep_shutdown (void) +{ + ep_rt_config_requires_lock_not_held (); + + //TODO: Locking pattern between init/shutdown is racy but same as CoreCLR. Needs to be revisited. + ep_return_void_if_nok (ep_volatile_load_eventpipe_state () != EP_STATE_SHUTTING_DOWN); + ep_return_void_if_nok (!ep_rt_process_detach ()); + ep_return_void_if_nok (ep_volatile_load_eventpipe_state () == EP_STATE_INITIALIZED); + + EP_CONFIG_LOCK_ENTER + ep_volatile_store_eventpipe_state_without_barrier (EP_STATE_SHUTTING_DOWN); + EP_CONFIG_LOCK_EXIT + + for (uint32_t i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; ++i) { + EventPipeSession *session = ep_volatile_load_session (i); + if (session) + ep_disable ((EventPipeSessionID)session); + } + + // dotnet/coreclr: issue 24850: EventPipe shutdown race conditions + // Deallocating providers/events here might cause AV if a WriteEvent + // was to occur. Thus, we are not doing this cleanup. + + // // Remove EventPipeEventSource first since it tries to use the data structures that we remove below. + // // We need to do this after disabling sessions since those try to write to EventPipeEventSource. + // delete s_pEventSource; + // s_pEventSource = nullptr; + // s_config.Shutdown(); + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + ep_rt_shutdown (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +EventPipeEventMetadataEvent * +ep_build_event_metadata_event ( + EventPipeEventInstance *event_instance, + uint32_t metadata_id) +{ + return ep_config_build_event_metadata_event (ep_config_get (), event_instance, metadata_id); +} + +void +ep_write_event ( + EventPipeEvent *ep_event, + EventData *event_data, + uint32_t event_data_len, + const uint8_t *activity_id, + const uint8_t *related_activity_id) +{ + //TODO: Implement. +} + +EventPipeEventInstance * +ep_get_next_event (EventPipeSessionID session_id) +{ + ep_rt_config_requires_lock_not_held (); + + // Only fetch the next event if a tracing session exists. + // The buffer manager is not disposed until the process is shutdown. + EventPipeSession *const session = ep_get_session (session_id); + return session ? ep_session_get_next_event (session) : NULL; +} + +EventPipeWaitHandle +ep_get_wait_handle (EventPipeSessionID session_id) +{ + EventPipeSession *const session = ep_get_session (session_id); + return session ? ep_session_get_wait_event (session) : 0; +} + +void +ep_start_streaming (EventPipeSessionID session_id) +{ + ep_rt_config_requires_lock_not_held (); + + EP_CONFIG_LOCK_ENTER + ep_raise_error_if_nok_holding_lock (is_session_id_in_collection_lock_held (session_id) == true); + EventPipeSession *const session = (EventPipeSession *)session_id; + ep_session_start_streaming_lock_held (session); + EP_CONFIG_LOCK_EXIT + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +/* + * EventPipePerf. + */ + +uint64_t +ep_perf_counter_query (void) +{ + return ep_rt_perf_counter_query (); +} + +uint64_t +ep_perf_frequency_query (void) +{ + return ep_rt_perf_frequency_query (); +} + +/* + * EventPipeProviderCallbackDataQueue. + */ + +void +ep_provider_callback_data_queue_enqueue ( + EventPipeProviderCallbackDataQueue *provider_callback_data_queue, + EventPipeProviderCallbackData *provider_callback_data) +{ + ep_return_void_if_nok (provider_callback_data_queue != NULL); + ep_rt_provider_callback_data_queue_push_tail (ep_provider_callback_data_queue_get_queue_ref (provider_callback_data_queue), ep_provider_callback_data_alloc_copy (provider_callback_data)); +} + +bool +ep_provider_callback_data_queue_try_dequeue ( + EventPipeProviderCallbackDataQueue *provider_callback_data_queue, + EventPipeProviderCallbackData *provider_callback_data) +{ + ep_return_false_if_nok (provider_callback_data_queue != NULL && ep_rt_provider_callback_data_queue_is_empty (ep_provider_callback_data_queue_get_queue_ref (provider_callback_data_queue)) != true); + + EventPipeProviderCallbackData *value = NULL; + ep_rt_provider_callback_data_queue_pop_head (ep_provider_callback_data_queue_get_queue_ref (provider_callback_data_queue), &value); + ep_provider_callback_data_init_copy (provider_callback_data, value); + ep_provider_callback_data_free (value); + + return true; +} + +#endif /* ENABLE_PERFTRACING */ + +extern const char quiet_linker_empty_file_warning_eventpipe; +const char quiet_linker_empty_file_warning_eventpipe = 0; diff --git a/src/mono/mono/eventpipe/ep.h b/src/mono/mono/eventpipe/ep.h new file mode 100644 index 0000000000000..ca11989aa415b --- /dev/null +++ b/src/mono/mono/eventpipe/ep.h @@ -0,0 +1,272 @@ +#ifndef __EVENTPIPE_H__ +#define __EVENTPIPE_H__ + +#include + +#ifdef ENABLE_PERFTRACING +#include "ep-rt-config.h" +#include "ep-types.h" +#include "ep-stream.h" +#include "ep-thread.h" +#include "ep-block.h" +#include "ep-event-payload.h" +#include "ep-buffer.h" +#include "ep-buffer-manager.h" +#include "ep-stack-contents.h" +#include "ep-event.h" +#include "ep-event-instance.h" +#include "ep-config.h" +#include "ep-event-source.h" +#include "ep-file.h" +#include "ep-metadata-generator.h" +#include "ep-provider.h" +#include "ep-session.h" +#include "ep-session-provider.h" +#include "ep-rt.h" + +/* + * Globals and volatile access functions. + */ + +static +inline +EventPipeState +ep_volatile_load_eventpipe_state (void) +{ + extern volatile EventPipeState _ep_state; + return (EventPipeState)ep_rt_volatile_load_uint32_t ((const volatile uint32_t *)&_ep_state); +} + +static +inline +EventPipeState +ep_volatile_load_eventpipe_state_without_barrier (void) +{ + extern volatile EventPipeState _ep_state; + return (EventPipeState)ep_rt_volatile_load_uint32_t_without_barrier ((const volatile uint32_t *)&_ep_state); +} + +static +inline +void +ep_volatile_store_eventpipe_state (EventPipeState state) +{ + extern volatile EventPipeState _ep_state; + ep_rt_volatile_store_uint32_t ((volatile uint32_t *)&_ep_state, state); +} + +static +inline +void +ep_volatile_store_eventpipe_state_without_barrier (EventPipeState state) +{ + extern volatile EventPipeState _ep_state; + ep_rt_volatile_store_uint32_t_without_barrier ((volatile uint32_t *)&_ep_state, state); +} + +static +inline +EventPipeSession * +ep_volatile_load_session (size_t index) +{ + extern volatile EventPipeSession *_ep_sessions [EP_MAX_NUMBER_OF_SESSIONS]; + return (EventPipeSession *)ep_rt_volatile_load_ptr ((volatile void **)(&_ep_sessions [index])); +} + +static +inline +EventPipeSession * +ep_volatile_load_session_without_barrier (size_t index) +{ + extern volatile EventPipeSession *_ep_sessions [EP_MAX_NUMBER_OF_SESSIONS]; + return (EventPipeSession *)ep_rt_volatile_load_ptr_without_barrier ((volatile void **)(&_ep_sessions [index])); +} + +static +inline +void +ep_volatile_store_session (size_t index, EventPipeSession *session) +{ + extern volatile EventPipeSession *_ep_sessions [EP_MAX_NUMBER_OF_SESSIONS]; + ep_rt_volatile_store_ptr ((volatile void **)(&_ep_sessions [index]), session); +} + +static +inline +void +ep_volatile_store_session_without_barrier (size_t index, EventPipeSession *session) +{ + extern volatile EventPipeSession *_ep_sessions [EP_MAX_NUMBER_OF_SESSIONS]; + ep_rt_volatile_store_ptr_without_barrier ((volatile void **)(&_ep_sessions [index]), session); +} + +static +inline +uint32_t +ep_volatile_load_number_of_sessions (void) +{ + extern volatile uint32_t _ep_number_of_sessions; + return ep_rt_volatile_load_uint32_t (&_ep_number_of_sessions); +} + +static +inline +uint32_t +ep_volatile_load_number_of_sessions_without_barrier (void) +{ + extern volatile uint32_t _ep_number_of_sessions; + return ep_rt_volatile_load_uint32_t_without_barrier (&_ep_number_of_sessions); +} + +static +inline +void +ep_volatile_store_number_of_sessions (uint32_t number_of_sessions) +{ + extern volatile uint32_t _ep_number_of_sessions; + ep_rt_volatile_store_uint32_t (&_ep_number_of_sessions, number_of_sessions); +} + +static +inline +void +ep_volatile_store_number_of_sessions_without_barrier (uint32_t number_of_sessions) +{ + extern volatile uint32_t _ep_number_of_sessions; + ep_rt_volatile_store_uint32_t_without_barrier (&_ep_number_of_sessions, number_of_sessions); +} + +static +inline +uint64_t +ep_volatile_load_allow_write (void) +{ + extern volatile uint64_t _ep_allow_write; + return ep_rt_volatile_load_uint64_t (&_ep_allow_write); +} + +static +inline +uint64_t +ep_volatile_load_allow_write_without_barrier (void) +{ + extern volatile uint64_t _ep_allow_write; + return ep_rt_volatile_load_uint64_t_without_barrier (&_ep_allow_write); +} + +static +inline +void +ep_volatile_store_allow_write (uint64_t allow_write) +{ + extern volatile uint64_t _ep_allow_write; + ep_rt_volatile_store_uint64_t (&_ep_allow_write, allow_write); +} + +static +inline +void +ep_volatile_store_allow_write_without_barrier (uint64_t allow_write) +{ + extern volatile uint64_t _ep_allow_write; + ep_rt_volatile_store_uint64_t_without_barrier (&_ep_allow_write, allow_write); +} + +/* + * EventPipe. + */ + +EventPipeSessionID +ep_enable ( + const ep_char8_t *output_path, + uint32_t circular_buffer_size_in_mb, + const EventPipeProviderConfiguration *providers, + uint32_t providers_len, + EventPipeSessionType session_type, + EventPipeSerializationFormat format, + bool rundown_requested, + IpcStream *stream, + bool enable_sample_profiler); + +void +ep_disable (EventPipeSessionID id); + +EventPipeSession * +ep_get_session (EventPipeSessionID session_id); + +bool +ep_enabled (void); + +EventPipeProvider * +ep_create_provider ( + const ep_char8_t *provider_name, + EventPipeCallback callback_func, + void *callback_data); + +void +ep_delete_provider (EventPipeProvider *provider); + +EventPipeProvider * +ep_get_provider (const ep_char8_t *provider_name); + +void +ep_init (void); + +void +ep_finish_init (void); + +void +ep_shutdown (void); + +EventPipeEventMetadataEvent * +ep_build_event_metadata_event ( + EventPipeEventInstance *event_instance, + uint32_t metadata_id); + +void +ep_write_event ( + EventPipeEvent *ep_event, + EventData *event_data, + uint32_t event_data_len, + const uint8_t *activity_id, + const uint8_t *related_activity_id); + +EventPipeEventInstance * +ep_get_next_event (EventPipeSessionID session_id); + +EventPipeWaitHandle +ep_get_wait_handle (EventPipeSessionID session_id); + +void +ep_start_streaming (EventPipeSessionID session_id); + +/* + * EventPipePerf. + */ + +uint64_t +ep_perf_counter_query (void); + +uint64_t +ep_perf_frequency_query (void); + +#else /* ENABLE_PERFTRACING */ + +static +inline +void +ep_init (void) +{ + ; +} + +static +inline +void +ep_shutdown (void) +{ + ; +} + +#endif /* ENABLE_PERFTRACING */ +#endif /** __EVENTPIPE_H__ **/ diff --git a/src/mono/mono/eventpipe/test/Directory.Build.props b/src/mono/mono/eventpipe/test/Directory.Build.props new file mode 100644 index 0000000000000..4de865b5f1e15 --- /dev/null +++ b/src/mono/mono/eventpipe/test/Directory.Build.props @@ -0,0 +1,3 @@ + + + diff --git a/src/mono/mono/eventpipe/test/Directory.Build.targets b/src/mono/mono/eventpipe/test/Directory.Build.targets new file mode 100644 index 0000000000000..c43cec29bd65d --- /dev/null +++ b/src/mono/mono/eventpipe/test/Directory.Build.targets @@ -0,0 +1,3 @@ + + + diff --git a/src/mono/mono/eventpipe/test/Makefile.am b/src/mono/mono/eventpipe/test/Makefile.am new file mode 100644 index 0000000000000..4513e50da6326 --- /dev/null +++ b/src/mono/mono/eventpipe/test/Makefile.am @@ -0,0 +1,56 @@ +MAKEFLAGS := $(MAKEFLAGS) --no-builtin-rules + +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/mono $(GLIB_CFLAGS) + +if HOST_DARWIN +test_ldflags = -framework CoreFoundation -framework Foundation +endif + +sgen_libs = \ + $(top_builddir)/mono/metadata/libmonoruntimesgen.la \ + $(top_builddir)/mono/sgen/libmonosgen.la \ + $(top_builddir)/mono/utils/libmonoutils.la \ + $(top_builddir)/mono/eglib/libeglib.la + +mini_libs = \ + $(top_builddir)/mono/mini/libmini.la + +if !DISABLE_INTERPRETER +mini_libs += $(top_builddir)/mono/mini/libmono-ee-interp.la +endif + +if !DISABLE_DEBUGGER_AGENT +mini_libs += $(top_builddir)/mono/mini/libmono-dbg.la +endif + +eventpipe_libs = \ + $(top_builddir)/mono/eventpipe/libeventpipe.la + +CFLAGS = $(filter-out @CXX_REMOVE_CFLAGS@, @CFLAGS@) + +if !HOST_WIN32 +if ENABLE_PERFTRACING + +test_eventpipe_SOURCES = \ +ep-test-runner.c \ +ep-test-driver.c \ +ep-fake-tests.c \ +ep-fastserializer-tests.c \ +ep-provider-callback-dataqueue-tests.c \ +ep-setup-tests.c \ +ep-tests.c \ +ep-file-tests.c \ +ep-session-tests.c \ +ep-teardown-tests.c \ +ep-thread-tests.c \ +ep-tests-debug.h \ +ep-tests.h + +test_eventpipe_CFLAGS = $(AM_CFLAGS) $(SGEN_DEFINES) +test_eventpipe_LDADD = $(mini_libs) $(sgen_libs) $(eventpipe_libs) +test_eventpipe_LDFLAGS = $(test_ldflags) + +noinst_PROGRAMS = test-eventpipe + +endif ENABLE_PERFTRACING +endif !HOST_WIN32 diff --git a/src/mono/mono/eventpipe/test/ep-fake-tests.c b/src/mono/mono/eventpipe/test/ep-fake-tests.c new file mode 100644 index 0000000000000..86bc252101893 --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-fake-tests.c @@ -0,0 +1 @@ +#include "mono/eglib/test/fake.c" diff --git a/src/mono/mono/eventpipe/test/ep-fastserializer-tests.c b/src/mono/mono/eventpipe/test/ep-fastserializer-tests.c new file mode 100644 index 0000000000000..0d39f7e6f294a --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-fastserializer-tests.c @@ -0,0 +1,346 @@ +#include "mono/eventpipe/ep.h" +#include "eglib/test/test.h" + +#define TEST_PROVIDER_NAME "MyTestProvider" + +typedef struct _MemoryStreamWriter { + StreamWriter stream_writer; + uint8_t buffer [1024]; + uint8_t *current_ptr; +} MemoryStreamWriter; + +static +void +memory_stream_writer_free_func (void *stream) +{ + g_free ((MemoryStreamWriter *)stream); +} + +static +bool +memory_stream_writer_write_func ( + void *stream, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written) +{ + *bytes_written = 0; + + MemoryStreamWriter *memory_stream = (MemoryStreamWriter *)stream; + if (!stream) + return false; + + if ((memory_stream->current_ptr + bytes_to_write) > (memory_stream->buffer + sizeof (memory_stream->buffer))) + return false; + + memcpy (memory_stream->current_ptr, buffer, bytes_to_write); + memory_stream->current_ptr += bytes_to_write; + *bytes_written = bytes_to_write; + + return true; +} + +static StreamWriterVtable memory_stream_writer_vtable = { + memory_stream_writer_free_func, + memory_stream_writer_write_func }; + +static RESULT +test_fast_serializer_object_fast_serialize (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + MemoryStreamWriter *stream_writer = NULL; + FastSerializer *fast_serializer = NULL; + EventPipeProvider *provider = NULL; + EventPipeEvent *ep_event = NULL; + EventPipeEventInstance *ep_event_instance = NULL; + EventPipeEventBlock *event_block = NULL; + + // Use memory stream for testing. + stream_writer = g_new0 (MemoryStreamWriter, 1); + ep_raise_error_if_nok (stream_writer != NULL); + + test_location = 1; + + stream_writer->current_ptr = stream_writer->buffer; + ep_stream_writer_init (&stream_writer->stream_writer, &memory_stream_writer_vtable); + + fast_serializer = ep_fast_serializer_alloc ((StreamWriter *)stream_writer); + ep_raise_error_if_nok (fast_serializer != NULL); + stream_writer = NULL; + + test_location = 2; + + provider = ep_create_provider (TEST_PROVIDER_NAME, NULL, NULL); + ep_raise_error_if_nok (provider != NULL); + + test_location = 3; + + ep_event = ep_event_alloc (provider, 1, 1, 1, EP_EVENT_LEVEL_VERBOSE, false, NULL, 0); + ep_raise_error_if_nok (ep_event != NULL); + + test_location = 4; + + ep_event_instance = ep_event_instance_alloc (ep_event, 0, 0, NULL, 0, NULL, NULL); + event_block = ep_event_block_alloc (1024, EP_SERIALIZATION_FORMAT_NETTRACE_V4); + ep_raise_error_if_nok (ep_event_instance != NULL && event_block != NULL); + + test_location = 5; + + ep_raise_error_if_nok (ep_event_block_base_write_event ((EventPipeEventBlockBase *)event_block, ep_event_instance, 0, 1, 0, false) == true); + + test_location = 6; + + ep_fast_serializable_object_fast_serialize ((FastSerializableObject *)event_block, fast_serializer); + + //Check memory stream results. + stream_writer = (MemoryStreamWriter *)ep_fast_serializer_get_stream_writer (fast_serializer); + if (stream_writer->buffer == stream_writer->current_ptr) { + result = FAILED ("fast_serialize for EventPipeEventBlock didn't write any data into MemoryStreamWriter"); + ep_raise_error (); + } + +ep_on_exit: + ep_event_instance_free (ep_event_instance); + ep_event_block_free (event_block); + ep_event_free (ep_event); + ep_delete_provider (provider); + ep_fast_serializer_free (fast_serializer); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_fast_serializer_event_block_free_vcall (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeEventBlock *event_block = ep_event_block_alloc (1024, EP_SERIALIZATION_FORMAT_NETTRACE_V4); + ep_raise_error_if_nok (event_block != NULL); + + test_location = 1; + + ep_fast_serializable_object_free_vcall ((FastSerializableObject *)event_block); + +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_fast_serializer_metadata_block_free_vcall (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeMetadataBlock *metadata_block = ep_metadata_block_alloc (1024); + ep_raise_error_if_nok (metadata_block != NULL); + + test_location = 1; + + ep_fast_serializable_object_free_vcall ((FastSerializableObject *)metadata_block); + +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_fast_serializer_sequence_point_block_free_vcall (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeSequencePoint sequence_point; + EventPipeSequencePointBlock *sequence_point_block = NULL; + + ep_raise_error_if_nok (ep_sequence_point_init (&sequence_point) != NULL); + + test_location = 1; + + sequence_point_block = ep_sequence_point_block_alloc (&sequence_point); + ep_raise_error_if_nok (sequence_point_block != NULL); + + test_location = 2; + + ep_fast_serializable_object_free_vcall ((FastSerializableObject *)sequence_point_block); + +ep_on_exit: + ep_sequence_point_fini (&sequence_point); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_fast_serializer_stack_block_free_vcall (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeStackBlock *stack_block = ep_stack_block_alloc (1024); + ep_raise_error_if_nok (stack_block != NULL); + + test_location = 1; + + ep_fast_serializable_object_free_vcall ((FastSerializableObject *)stack_block); + +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_fast_serializer_event_block_get_type_name (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeEventBlock *event_block = ep_event_block_alloc (1024, EP_SERIALIZATION_FORMAT_NETTRACE_V4); + ep_raise_error_if_nok (event_block != NULL); + + test_location = 1; + + const char *type_name = (char *)ep_fast_serializable_object_get_type_name ((FastSerializableObject *)event_block); + if (strcmp (type_name, "EventBlock")) { + result = FAILED ("get_type_name for EventPipeEventBlock returned unexpected value, retrieved: %s, expected: %s", type_name, "EventBlock"); + ep_raise_error (); + } + +ep_on_exit: + ep_event_block_free (event_block); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_fast_serializer_metadata_block_get_type_name (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeMetadataBlock *metadata_block = ep_metadata_block_alloc (1024); + ep_raise_error_if_nok (metadata_block != NULL); + + test_location = 1; + + const char *type_name = (char *)ep_fast_serializable_object_get_type_name ((FastSerializableObject *)metadata_block); + if (strcmp (type_name, "MetadataBlock")) { + result = FAILED ("get_type_name for EventPipeMetadataBlock returned unexpected value, retrieved: %s, expected: %s", type_name, "MetadataBlock"); + ep_raise_error (); + } + +ep_on_exit: + ep_metadata_block_free (metadata_block); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_fast_serializer_sequence_point_block_get_type_name (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeSequencePoint sequence_point; + EventPipeSequencePointBlock *sequence_point_block = NULL; + + ep_raise_error_if_nok (ep_sequence_point_init (&sequence_point) != NULL); + + test_location = 1; + + sequence_point_block = ep_sequence_point_block_alloc (&sequence_point); + ep_raise_error_if_nok (sequence_point_block != NULL); + + test_location = 2; + + const char *type_name = (char *)ep_fast_serializable_object_get_type_name ((FastSerializableObject *)sequence_point_block); + if (strcmp (type_name, "SPBlock")) { + result = FAILED ("get_type_name for EventPipeSequencePointBlock returned unexpected value, retrieved: %s, expected: %s", type_name, "SPBlock"); + ep_raise_error (); + } + +ep_on_exit: + ep_sequence_point_block_free (sequence_point_block); + ep_sequence_point_fini (&sequence_point); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_fast_serializer_stack_block_get_type_name (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeStackBlock *stack_block = ep_stack_block_alloc (1024); + ep_raise_error_if_nok (stack_block != NULL); + + test_location = 1; + + const char *type_name = (char *)ep_fast_serializable_object_get_type_name ((FastSerializableObject *)stack_block); + if (strcmp (type_name, "StackBlock")) { + result = FAILED ("get_type_name for EventPipeStackBlock returned unexpected value, retrieved: %s, expected: %s", type_name, "StackBlock"); + ep_raise_error (); + } + +ep_on_exit: + ep_stack_block_free (stack_block); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + 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. + +static Test ep_fastserializer_tests [] = { + {"fast_serializer_object_fast_serialize", test_fast_serializer_object_fast_serialize}, + {"test_fast_serializer_event_block_free_vcall", test_fast_serializer_event_block_free_vcall}, + {"test_fast_serializer_metadata_block_free_vcall", test_fast_serializer_metadata_block_free_vcall}, + {"test_fast_serializer_sequence_point_block_free_vcall", test_fast_serializer_sequence_point_block_free_vcall}, + {"test_fast_serializer_stack_block_free_vcall", test_fast_serializer_stack_block_free_vcall}, + {"test_fast_serializer_event_block_get_type_name", test_fast_serializer_event_block_get_type_name}, + {"test_fast_serializer_metadata_block_get_type_name", test_fast_serializer_metadata_block_get_type_name}, + {"test_fast_serializer_sequence_point_block_get_type_name", test_fast_serializer_sequence_point_block_get_type_name}, + {"test_fast_serializer_stack_block_get_type_name", test_fast_serializer_stack_block_get_type_name}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(ep_fastserializer_tests_init, ep_fastserializer_tests) diff --git a/src/mono/mono/eventpipe/test/ep-file-tests.c b/src/mono/mono/eventpipe/test/ep-file-tests.c new file mode 100644 index 0000000000000..696d2442c9baf --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-file-tests.c @@ -0,0 +1,172 @@ +#include "mono/eventpipe/ep.h" +#include "eglib/test/test.h" + +#define TEST_PROVIDER_NAME "MyTestProvider" +#define TEST_FILE "./ep_test_create_file.txt" + +static RESULT +test_create_file (EventPipeSerializationFormat format) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeFile *file = NULL; + FileStreamWriter *file_stream_writer = NULL; + + file_stream_writer = ep_file_stream_writer_alloc (TEST_FILE); + ep_raise_error_if_nok (file_stream_writer != NULL); + + test_location = 1; + + file = ep_file_alloc ((StreamWriter *)file_stream_writer, format); + ep_raise_error_if_nok (file != NULL); + + file_stream_writer = NULL; + if (!ep_file_initialize_file (file)) { + result = FAILED ("ep_file_initialize_file failed"); + ep_raise_error (); + } + + test_location = 2; + + ep_file_flush (file, EP_FILE_FLUSH_FLAGS_ALL_BLOCKS); + + test_location = 3; + +ep_on_exit: + ep_file_free (file); + ep_file_stream_writer_free (file_stream_writer); + + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_file_write_event (EventPipeSerializationFormat format, bool write_event, bool write_sequence_point) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeFile *file = NULL; + FileStreamWriter *file_stream_writer = NULL; + EventPipeProvider *provider = NULL; + EventPipeEvent *ep_event = NULL; + EventPipeEventInstance *ep_event_instance = NULL; + EventPipeEventMetadataEvent *metadata_event = NULL; + + file_stream_writer = ep_file_stream_writer_alloc (TEST_FILE); + ep_raise_error_if_nok (file_stream_writer != NULL); + + test_location = 1; + + file = ep_file_alloc ((StreamWriter *)file_stream_writer, format); + ep_raise_error_if_nok (file != NULL); + + file_stream_writer = NULL; + if (!ep_file_initialize_file (file)) { + result = FAILED ("ep_file_initialize_file failed"); + ep_raise_error (); + } + + test_location = 2; + + if (write_event) { + provider = ep_create_provider (TEST_PROVIDER_NAME, NULL, NULL); + ep_raise_error_if_nok (provider != NULL); + + test_location = 3; + + ep_event = ep_event_alloc (provider, 1, 1, 1, EP_EVENT_LEVEL_VERBOSE, false, NULL, 0); + ep_raise_error_if_nok (ep_event != NULL); + + test_location = 4; + + ep_event_instance = ep_event_instance_alloc (ep_event, 0, 0, NULL, 0, NULL, NULL); + ep_raise_error_if_nok (ep_event_instance != NULL); + + test_location = 5; + + metadata_event = ep_build_event_metadata_event (ep_event_instance, 1); + ep_raise_error_if_nok (metadata_event != NULL); + + ep_file_write_event (file, (EventPipeEventInstance *)metadata_event, 1, 1 , true); + } + + if (write_sequence_point) { + EventPipeSequencePoint sequence_point; + ep_sequence_point_init (&sequence_point); + ep_file_write_sequence_point (file, &sequence_point); + ep_sequence_point_fini (&sequence_point); + } + + ep_file_flush (file, EP_FILE_FLUSH_FLAGS_ALL_BLOCKS); + + test_location = 6; + +ep_on_exit: + ep_delete_provider (provider); + ep_event_free (ep_event); + ep_event_instance_free (ep_event_instance); + ep_event_metdata_event_free (metadata_event); + ep_file_free (file); + ep_file_stream_writer_free (file_stream_writer); + + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_create_file_netperf_v3 (void) +{ + return test_create_file (EP_SERIALIZATION_FORMAT_NETPERF_V3); +} + +static RESULT +test_create_file_nettrace_v4 (void) +{ + return test_create_file (EP_SERIALIZATION_FORMAT_NETTRACE_V4); +} + +static RESULT +test_file_write_event_netperf_v3 (void) +{ + return test_file_write_event (EP_SERIALIZATION_FORMAT_NETPERF_V3, true, false); +} + +static RESULT +test_file_write_event_nettrace_v4 (void) +{ + return test_file_write_event (EP_SERIALIZATION_FORMAT_NETTRACE_V4, true, false); +} + +static RESULT +test_file_write_sequence_point_netperf_v3 (void) +{ + return test_file_write_event (EP_SERIALIZATION_FORMAT_NETPERF_V3, false, true); +} + +static RESULT +test_file_write_sequence_point_nettrace_v4 (void) +{ + return test_file_write_event (EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, true); +} + +static Test ep_file_tests [] = { + {"test_create_file_netperf_v3", test_create_file_netperf_v3}, + {"test_create_file_nettrace_v4", test_create_file_nettrace_v4}, + {"test_file_write_event_netperf_v3", test_file_write_event_netperf_v3}, + {"test_file_write_event_nettrace_v4", test_file_write_event_nettrace_v4}, + {"test_file_write_sequence_point_netperf_v3", test_file_write_sequence_point_netperf_v3}, + {"test_file_write_sequence_point_nettrace_v4", test_file_write_sequence_point_nettrace_v4}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(ep_file_tests_init, ep_file_tests) diff --git a/src/mono/mono/eventpipe/test/ep-provider-callback-dataqueue-tests.c b/src/mono/mono/eventpipe/test/ep-provider-callback-dataqueue-tests.c new file mode 100644 index 0000000000000..06a45c4c6a5e7 --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-provider-callback-dataqueue-tests.c @@ -0,0 +1,98 @@ +#include "mono/eventpipe/ep.h" +#include "eglib/test/test.h" + +#define TEST_PROVIDER_NAME "MyTestProvider" +#define TEST_FILE "./ep_test_create_file.txt" + +#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_provider_callback_data_queue_setup (void) +{ +#ifdef _CRTDBG_MAP_ALLOC + _CrtMemCheckpoint (&eventpipe_memory_start_snapshot); +#endif + return NULL; +} + +static +void +provider_callback ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_context) +{ + ; +} + +static RESULT +test_provider_callback_data_queue (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeProviderCallbackDataQueue callback_data_queue; + EventPipeProviderCallbackDataQueue *provider_callback_data_queue = ep_provider_callback_data_queue_init (&callback_data_queue); + + EventPipeProviderCallbackData enqueue_callback_data; + EventPipeProviderCallbackData *provider_enqueue_callback_data = ep_provider_callback_data_init ( + &enqueue_callback_data, + "", + provider_callback, + NULL, + 1, + EP_EVENT_LEVEL_LOG_ALWAYS, + true); + + for (uint32_t i = 0; i < 1000; ++i) + ep_provider_callback_data_queue_enqueue (provider_callback_data_queue, provider_enqueue_callback_data); + + EventPipeProviderCallbackData dequeue_callback_data; + uint32_t deque_counter = 0; + while (ep_provider_callback_data_queue_try_dequeue (provider_callback_data_queue, &dequeue_callback_data)) + deque_counter++; + + if (deque_counter != 1000) { + result = FAILED ("Unexpected number of provider callback invokes"); + ep_raise_error (); + } + +ep_on_exit: + ep_provider_callback_data_queue_fini (provider_callback_data_queue); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_provider_callback_data_queue_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_provider_callback_data_queue_tests [] = { + {"test_provider_callback_data_queue_setup", test_provider_callback_data_queue_setup}, + {"test_provider_callback_data_queue", test_provider_callback_data_queue}, + {"test_provider_callback_data_queue_teardown", test_provider_callback_data_queue_teardown}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(ep_provider_callback_data_queue_tests_init, ep_provider_callback_data_queue_tests) diff --git a/src/mono/mono/eventpipe/test/ep-session-tests.c b/src/mono/mono/eventpipe/test/ep-session-tests.c new file mode 100644 index 0000000000000..c9ef8fa79acfe --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-session-tests.c @@ -0,0 +1,244 @@ +#include "mono/eventpipe/ep.h" +#include "eglib/test/test.h" + +#define TEST_PROVIDER_NAME "MyTestProvider" +#define TEST_FILE "./ep_test_create_file.txt" + +#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_session_setup (void) +{ +#ifdef _CRTDBG_MAP_ALLOC + _CrtMemCheckpoint (&eventpipe_memory_start_snapshot); +#endif + return NULL; +} + +static RESULT +test_create_delete_session (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + EventPipeSession *test_session = NULL; + + EventPipeProviderConfiguration provider_config; + ep_raise_error_if_nok (ep_provider_config_init (&provider_config, TEST_PROVIDER_NAME, 1, EP_EVENT_LEVEL_LOG_ALWAYS, "") != NULL); + + test_location = 1; + + EP_CONFIG_LOCK_ENTER + test_session = ep_session_alloc ( + 1, + TEST_FILE, + NULL, + EP_SESSION_TYPE_FILE, + EP_SERIALIZATION_FORMAT_NETTRACE_V4, + false, + 1, + &provider_config, + 1, + false); + EP_CONFIG_LOCK_EXIT + + ep_raise_error_if_nok (test_session != NULL); + +ep_on_exit: + ep_session_free (test_session); + if (test_location != 0) + ep_provider_config_fini (&provider_config); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_add_session_providers (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + EventPipeSession *test_session = NULL; + EventPipeSessionProvider *test_session_provider = NULL; + + EventPipeProviderConfiguration provider_config; + ep_raise_error_if_nok (ep_provider_config_init (&provider_config, TEST_PROVIDER_NAME, 1, EP_EVENT_LEVEL_LOG_ALWAYS, "") != NULL); + + test_location = 1; + + EP_CONFIG_LOCK_ENTER + test_session = ep_session_alloc ( + 1, + TEST_FILE, + NULL, + EP_SESSION_TYPE_FILE, + EP_SERIALIZATION_FORMAT_NETTRACE_V4, + false, + 1, + &provider_config, + 1, + false); + EP_CONFIG_LOCK_EXIT + + ep_raise_error_if_nok (test_session != NULL); + + test_location = 2; + + if (!ep_session_is_valid (test_session)) { + result = FAILED ("ep_session_is_valid returned false with session providers"); + ep_raise_error (); + } + + test_location = 3; + + test_session_provider = ep_session_provider_alloc (TEST_PROVIDER_NAME, 1, EP_EVENT_LEVEL_LOG_ALWAYS, ""); + ep_raise_error_if_nok (test_session_provider != NULL); + + test_location = 4; + + ep_session_add_session_provider (test_session, test_session_provider); + test_session_provider = NULL; + + if (!ep_session_is_valid (test_session)) { + result = FAILED ("ep_session_is_valid returned false with session providers"); + ep_raise_error (); + } + + test_location = 5; + + ep_session_disable (test_session); + + if (ep_session_is_valid (test_session)) { + result = FAILED ("ep_session_is_valid returned true without session providers"); + ep_raise_error (); + } + +ep_on_exit: + ep_session_free (test_session); + if (test_location != 0) + ep_provider_config_fini (&provider_config); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_session_special_get_set (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + EventPipeSession *test_session = NULL; + + EventPipeProviderConfiguration provider_config; + 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; + + EP_CONFIG_LOCK_ENTER + test_session = ep_session_alloc ( + 1, + TEST_FILE, + NULL, + EP_SESSION_TYPE_FILE, + EP_SERIALIZATION_FORMAT_NETTRACE_V4, + false, + 1, + current_provider_config, + 1, + false); + EP_CONFIG_LOCK_EXIT + + ep_raise_error_if_nok (test_session != NULL); + + test_location = 2; + + if (ep_session_get_rundown_enabled (test_session)) { + result = FAILED ("ep_session_get_rundown_enabled returned true, should be false"); + ep_raise_error (); + } + + test_location = 3; + + ep_session_set_rundown_enabled (test_session, true); + + if (!ep_session_get_rundown_enabled (test_session)) { + result = FAILED ("ep_session_get_rundown_enabled returned false, should be true"); + ep_raise_error (); + } + + test_location = 4; + + if (ep_session_get_ipc_streaming_enabled (test_session)) { + result = FAILED ("ep_session_get_ipc_streaming_enabled returned true, should be false"); + ep_raise_error (); + } + + test_location = 5; + + ep_session_set_ipc_streaming_enabled (test_session, true); + + if (!ep_session_get_ipc_streaming_enabled (test_session)) { + result = FAILED ("ep_session_set_ipc_streaming_enabled returned false, should be true"); + ep_raise_error (); + } + + ep_session_set_ipc_streaming_enabled (test_session, false); + + test_location = 6; + + if (!ep_session_get_wait_event (test_session)) { + result = FAILED ("ep_session_get_wait_event failed"); + ep_raise_error (); + } + + test_location = 7; + + if (!ep_session_get_mask (test_session)) { + result = FAILED ("Unexpected session mask"); + ep_raise_error (); + } + +ep_on_exit: + ep_session_free (test_session); + ep_provider_config_fini (current_provider_config); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_session_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_session_tests [] = { + {"test_session_setup", test_session_setup}, + {"test_create_delete_session", test_create_delete_session}, + {"test_add_session_providers", test_add_session_providers}, + {"test_session_special_get_set", test_session_special_get_set}, + {"test_session_teardown", test_session_teardown}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(ep_session_tests_init, ep_session_tests) diff --git a/src/mono/mono/eventpipe/test/ep-setup-tests.c b/src/mono/mono/eventpipe/test/ep-setup-tests.c new file mode 100644 index 0000000000000..30e51463bc169 --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-setup-tests.c @@ -0,0 +1,27 @@ +#include "mono/eventpipe/ep.h" +#include "eglib/test/test.h" +#include +#include + +MonoDomain *eventpipe_test_domain; + +static RESULT +test_setup (void) +{ + char *core_root = g_getenv ("CORE_ROOT"); + if (core_root) { + mono_set_assemblies_path (core_root); + g_free (core_root); + } + + eventpipe_test_domain = mono_jit_init_version_for_test_only ("eventpipe-tests", "v4.0.30319"); + + return NULL; +} + +static Test ep_setup_tests [] = { + {"test_setup", test_setup}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(ep_setup_tests_init, ep_setup_tests) diff --git a/src/mono/mono/eventpipe/test/ep-teardown-tests.c b/src/mono/mono/eventpipe/test/ep-teardown-tests.c new file mode 100644 index 0000000000000..3800f9360e725 --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-teardown-tests.c @@ -0,0 +1,25 @@ +#include "mono/eventpipe/ep.h" +#include "eglib/test/test.h" +#include + +#define TEST_FILE "./ep_test_create_file.txt" + +extern MonoDomain *eventpipe_test_domain; + +static RESULT +test_teardown (void) +{ + if (eventpipe_test_domain) + mono_jit_cleanup (eventpipe_test_domain); + + unlink (TEST_FILE); + + return NULL; +} + +static Test ep_teardown_tests [] = { + {"test_teardown", test_teardown}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(ep_teardown_tests_init, ep_teardown_tests) diff --git a/src/mono/mono/eventpipe/test/ep-test-driver.c b/src/mono/mono/eventpipe/test/ep-test-driver.c new file mode 100644 index 0000000000000..a1aa730b814c7 --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-test-driver.c @@ -0,0 +1,5 @@ +#define DRIVER_EXTERNAL_TESTS +#define DRIVER_NAME "eventpipe-tests" + +#include "ep-tests.h" +#include "mono/eglib/test/driver.c" diff --git a/src/mono/mono/eventpipe/test/ep-test-runner.c b/src/mono/mono/eventpipe/test/ep-test-runner.c new file mode 100644 index 0000000000000..7e9072000f921 --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-test-runner.c @@ -0,0 +1 @@ +#include "mono/eglib/test/test.c" diff --git a/src/mono/mono/eventpipe/test/ep-test.sln b/src/mono/mono/eventpipe/test/ep-test.sln new file mode 100644 index 0000000000000..4431637a8dbb8 --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-test.sln @@ -0,0 +1,127 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ep-test", "ep-test.vcxproj", "{AF48E883-A558-4027-8934-32CFD2750D04}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "eglib", "..\..\..\msvc\eglib.vcxproj", "{158073ED-99AE-4196-9EDC-DDB2344F8466}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmonoutils", "..\..\..\msvc\libmonoutils.vcxproj", "{8FC2B0C8-51AD-49DF-851F-5D01A77A75E4}" + ProjectSection(ProjectDependencies) = postProject + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740} = {C36612BD-22D3-4B95-85E2-7FDC4FC5D740} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmonoruntime", "..\..\..\msvc\libmonoruntime.vcxproj", "{C36612BD-22D3-4B95-85E2-7FDC4FC5D739}" + ProjectSection(ProjectDependencies) = postProject + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740} = {C36612BD-22D3-4B95-85E2-7FDC4FC5D740} + {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4} = {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgcmonosgen", "..\..\..\msvc\libgcmonosgen.vcxproj", "{C36612BD-22D3-4B95-85E2-7FDC4FC5D740}" + ProjectSection(ProjectDependencies) = postProject + {158073ED-99AE-4196-9EDC-DDB2344F8466} = {158073ED-99AE-4196-9EDC-DDB2344F8466} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmini", "..\..\..\msvc\libmini.vcxproj", "{88D2EB79-592D-45F8-B849-AE021C1D983A}" + ProjectSection(ProjectDependencies) = postProject + {C36612BD-22D3-4B95-85E2-7FDC4FC5D739} = {C36612BD-22D3-4B95-85E2-7FDC4FC5D739} + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740} = {C36612BD-22D3-4B95-85E2-7FDC4FC5D740} + {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4} = {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "static-mono-runtime", "static-mono-runtime", "{B46B84C3-14C4-4C81-B4A3-CE5292663515}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmono-static", "..\..\..\msvc\libmono-static.vcxproj", "{CB0D9E92-293C-439C-9AC7-C5F59B6E0772}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "build-init", "..\..\..\msvc\build-init.vcxproj", "{92AE7622-5F58-4234-9A26-9EC71876B3F4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AF48E883-A558-4027-8934-32CFD2750D04}.Debug|Win32.ActiveCfg = Debug|Win32 + {AF48E883-A558-4027-8934-32CFD2750D04}.Debug|Win32.Build.0 = Debug|Win32 + {AF48E883-A558-4027-8934-32CFD2750D04}.Debug|x64.ActiveCfg = Debug|x64 + {AF48E883-A558-4027-8934-32CFD2750D04}.Debug|x64.Build.0 = Debug|x64 + {AF48E883-A558-4027-8934-32CFD2750D04}.Release|Win32.ActiveCfg = Release|Win32 + {AF48E883-A558-4027-8934-32CFD2750D04}.Release|Win32.Build.0 = Release|Win32 + {AF48E883-A558-4027-8934-32CFD2750D04}.Release|x64.ActiveCfg = Release|x64 + {AF48E883-A558-4027-8934-32CFD2750D04}.Release|x64.Build.0 = Release|x64 + {158073ED-99AE-4196-9EDC-DDB2344F8466}.Debug|Win32.ActiveCfg = Debug|Win32 + {158073ED-99AE-4196-9EDC-DDB2344F8466}.Debug|Win32.Build.0 = Debug|Win32 + {158073ED-99AE-4196-9EDC-DDB2344F8466}.Debug|x64.ActiveCfg = Debug|x64 + {158073ED-99AE-4196-9EDC-DDB2344F8466}.Debug|x64.Build.0 = Debug|x64 + {158073ED-99AE-4196-9EDC-DDB2344F8466}.Release|Win32.ActiveCfg = Release|Win32 + {158073ED-99AE-4196-9EDC-DDB2344F8466}.Release|Win32.Build.0 = Release|Win32 + {158073ED-99AE-4196-9EDC-DDB2344F8466}.Release|x64.ActiveCfg = Release|x64 + {158073ED-99AE-4196-9EDC-DDB2344F8466}.Release|x64.Build.0 = Release|x64 + {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4}.Debug|Win32.ActiveCfg = Debug|Win32 + {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4}.Debug|Win32.Build.0 = Debug|Win32 + {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4}.Debug|x64.ActiveCfg = Debug|x64 + {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4}.Debug|x64.Build.0 = Debug|x64 + {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4}.Release|Win32.ActiveCfg = Release|Win32 + {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4}.Release|Win32.Build.0 = Release|Win32 + {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4}.Release|x64.ActiveCfg = Release|x64 + {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4}.Release|x64.Build.0 = Release|x64 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D739}.Debug|Win32.ActiveCfg = Debug|Win32 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D739}.Debug|Win32.Build.0 = Debug|Win32 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D739}.Debug|x64.ActiveCfg = Debug|x64 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D739}.Debug|x64.Build.0 = Debug|x64 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D739}.Release|Win32.ActiveCfg = Release|Win32 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D739}.Release|Win32.Build.0 = Release|Win32 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D739}.Release|x64.ActiveCfg = Release|x64 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D739}.Release|x64.Build.0 = Release|x64 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740}.Debug|Win32.ActiveCfg = Debug|Win32 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740}.Debug|Win32.Build.0 = Debug|Win32 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740}.Debug|x64.ActiveCfg = Debug|x64 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740}.Debug|x64.Build.0 = Debug|x64 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740}.Release|Win32.ActiveCfg = Release|Win32 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740}.Release|Win32.Build.0 = Release|Win32 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740}.Release|x64.ActiveCfg = Release|x64 + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740}.Release|x64.Build.0 = Release|x64 + {88D2EB79-592D-45F8-B849-AE021C1D983A}.Debug|Win32.ActiveCfg = Debug|Win32 + {88D2EB79-592D-45F8-B849-AE021C1D983A}.Debug|Win32.Build.0 = Debug|Win32 + {88D2EB79-592D-45F8-B849-AE021C1D983A}.Debug|x64.ActiveCfg = Debug|x64 + {88D2EB79-592D-45F8-B849-AE021C1D983A}.Debug|x64.Build.0 = Debug|x64 + {88D2EB79-592D-45F8-B849-AE021C1D983A}.Release|Win32.ActiveCfg = Release|Win32 + {88D2EB79-592D-45F8-B849-AE021C1D983A}.Release|Win32.Build.0 = Release|Win32 + {88D2EB79-592D-45F8-B849-AE021C1D983A}.Release|x64.ActiveCfg = Release|x64 + {88D2EB79-592D-45F8-B849-AE021C1D983A}.Release|x64.Build.0 = Release|x64 + {CB0D9E92-293C-439C-9AC7-C5F59B6E0772}.Debug|Win32.ActiveCfg = Debug|Win32 + {CB0D9E92-293C-439C-9AC7-C5F59B6E0772}.Debug|Win32.Build.0 = Debug|Win32 + {CB0D9E92-293C-439C-9AC7-C5F59B6E0772}.Debug|x64.ActiveCfg = Debug|x64 + {CB0D9E92-293C-439C-9AC7-C5F59B6E0772}.Debug|x64.Build.0 = Debug|x64 + {CB0D9E92-293C-439C-9AC7-C5F59B6E0772}.Release|Win32.ActiveCfg = Release|Win32 + {CB0D9E92-293C-439C-9AC7-C5F59B6E0772}.Release|Win32.Build.0 = Release|Win32 + {CB0D9E92-293C-439C-9AC7-C5F59B6E0772}.Release|x64.ActiveCfg = Release|x64 + {CB0D9E92-293C-439C-9AC7-C5F59B6E0772}.Release|x64.Build.0 = Release|x64 + {92AE7622-5F58-4234-9A26-9EC71876B3F4}.Debug|Win32.ActiveCfg = Debug|Win32 + {92AE7622-5F58-4234-9A26-9EC71876B3F4}.Debug|Win32.Build.0 = Debug|Win32 + {92AE7622-5F58-4234-9A26-9EC71876B3F4}.Debug|x64.ActiveCfg = Debug|x64 + {92AE7622-5F58-4234-9A26-9EC71876B3F4}.Debug|x64.Build.0 = Debug|x64 + {92AE7622-5F58-4234-9A26-9EC71876B3F4}.Release|Win32.ActiveCfg = Release|Win32 + {92AE7622-5F58-4234-9A26-9EC71876B3F4}.Release|Win32.Build.0 = Release|Win32 + {92AE7622-5F58-4234-9A26-9EC71876B3F4}.Release|x64.ActiveCfg = Release|x64 + {92AE7622-5F58-4234-9A26-9EC71876B3F4}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {158073ED-99AE-4196-9EDC-DDB2344F8466} = {B46B84C3-14C4-4C81-B4A3-CE5292663515} + {8FC2B0C8-51AD-49DF-851F-5D01A77A75E4} = {B46B84C3-14C4-4C81-B4A3-CE5292663515} + {C36612BD-22D3-4B95-85E2-7FDC4FC5D739} = {B46B84C3-14C4-4C81-B4A3-CE5292663515} + {C36612BD-22D3-4B95-85E2-7FDC4FC5D740} = {B46B84C3-14C4-4C81-B4A3-CE5292663515} + {88D2EB79-592D-45F8-B849-AE021C1D983A} = {B46B84C3-14C4-4C81-B4A3-CE5292663515} + {CB0D9E92-293C-439C-9AC7-C5F59B6E0772} = {B46B84C3-14C4-4C81-B4A3-CE5292663515} + {92AE7622-5F58-4234-9A26-9EC71876B3F4} = {B46B84C3-14C4-4C81-B4A3-CE5292663515} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CE83F76A-FA5A-4DF0-B015-5D802539DEFC} + EndGlobalSection +EndGlobal diff --git a/src/mono/mono/eventpipe/test/ep-test.vcxproj b/src/mono/mono/eventpipe/test/ep-test.vcxproj new file mode 100644 index 0000000000000..6687c5d70819c --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-test.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + ..\..\.. + + + + + + + + + + + + + + + + + + + + + {cb0d9e92-293c-439c-9ac7-c5f59b6e0772} + + + + {AF48E883-A558-4027-8934-32CFD2750D04} + eventpipe + Win32Proj + 10.0 + + + + Application + false + $(DefaultPlatformToolset) + Unicode + + + Application + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + + + + + + + + + + + + + + + + + + + + + false + $(ProjectName)$(MONO_TARGET_SUFFIX) + $(MONO_BUILD_DIR_PREFIX)$(Platform)\bin\$(Configuration)\ + $(MONO_BUILD_DIR_PREFIX)$(Platform)\obj\$(ProjectName)$(MONO_TARGET_SUFFIX)\$(Configuration)\ + + + false + $(ProjectName)$(MONO_TARGET_SUFFIX) + $(MONO_BUILD_DIR_PREFIX)$(Platform)\bin\$(Configuration)\ + $(MONO_BUILD_DIR_PREFIX)$(Platform)\obj\$(ProjectName)$(MONO_TARGET_SUFFIX)\$(Configuration)\ + + + true + $(ProjectName)$(MONO_TARGET_SUFFIX) + $(MONO_BUILD_DIR_PREFIX)$(Platform)\bin\$(Configuration)\ + $(MONO_BUILD_DIR_PREFIX)$(Platform)\obj\$(ProjectName)$(MONO_TARGET_SUFFIX)\$(Configuration)\ + + + true + $(ProjectName)$(MONO_TARGET_SUFFIX) + $(MONO_BUILD_DIR_PREFIX)$(Platform)\bin\$(Configuration)\ + $(MONO_BUILD_DIR_PREFIX)$(Platform)\obj\$(ProjectName)$(MONO_TARGET_SUFFIX)\$(Configuration)\ + + + + $(MONO_DIR);$(MONO_INCLUDE_DIR);$(MONO_EGLIB_SOURCE_DIR);%(AdditionalIncludeDirectories) + Level4 + Disabled + $(ProjectDir)ep-tests-debug.h + + + %(AdditionalDependencies) + %(AdditionalLibraryDirectories) + Console + + + + + Level4 + $(MONO_DIR);$(MONO_INCLUDE_DIR);$(MONO_EGLIB_SOURCE_DIR);%(AdditionalIncludeDirectories) + true + true + true + + + %(AdditionalLibraryDirectories) + Console + %(AdditionalDependencies) + UseLinkTimeCodeGeneration + + + + + X64 + + + $(MONO_DIR);$(MONO_INCLUDE_DIR);$(MONO_EGLIB_SOURCE_DIR);%(AdditionalIncludeDirectories) + Level4 + Disabled + $(ProjectDir)ep-tests-debug.h + + + %(AdditionalDependencies) + %(AdditionalLibraryDirectories) + Console + + + + + X64 + + + Level4 + $(MONO_DIR);$(MONO_INCLUDE_DIR);$(MONO_EGLIB_SOURCE_DIR);%(AdditionalIncludeDirectories) + true + true + true + + + %(AdditionalLibraryDirectories) + Console + %(AdditionalDependencies) + UseLinkTimeCodeGeneration + + + + + + \ No newline at end of file diff --git a/src/mono/mono/eventpipe/test/ep-test.vcxproj.filters b/src/mono/mono/eventpipe/test/ep-test.vcxproj.filters new file mode 100644 index 0000000000000..e9d41287b865a --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-test.vcxproj.filters @@ -0,0 +1,63 @@ + + + + + Source Files\tests + + + Source Files\tests + + + Source Files\tests + + + Source Files\tests + + + Source Files\tests + + + Source Files\tests + + + Source Files\tests + + + Source Files\tests + + + Source Files\testframework + + + Source Files\testframework + + + Source Files\testframework + + + + + {d0fb7679-6e47-42a8-bd98-9c31c4bb065a} + + + {ee850408-9c60-43ab-8845-133cdd03c67a} + + + {0bf8953c-e42a-464e-b422-a451238f319f} + + + {29232fda-0808-499c-9d29-f079a62967e7} + + + {e0fbc587-715b-4470-be21-7645cfe77e5a} + + + + + Header Files\tests + + + Header Files\tests + + + \ No newline at end of file diff --git a/src/mono/mono/eventpipe/test/ep-tests-debug.h b/src/mono/mono/eventpipe/test/ep-tests-debug.h new file mode 100644 index 0000000000000..e86795ea3ff5f --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-tests-debug.h @@ -0,0 +1,8 @@ +#ifndef __EVENTPIPE_TESTS_DEBUG_H__ +#define __EVENTPIPE_TESTS_DEBUG_H__ + +#define _CRTDBG_MAP_ALLOC +#include +#include + +#endif /* __EVENTPIPE_TESTS_DEBUG_H__ */ diff --git a/src/mono/mono/eventpipe/test/ep-tests.c b/src/mono/mono/eventpipe/test/ep-tests.c new file mode 100644 index 0000000000000..2cc0ddb1bad87 --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-tests.c @@ -0,0 +1,399 @@ +#include "mono/eventpipe/ep.h" +#include "eglib/test/test.h" + +#define TEST_PROVIDER_NAME "MyTestProvider" +#define TEST_FILE "./ep_test_create_file.txt" + +#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_eventpipe_setup (void) +{ +#ifdef _CRTDBG_MAP_ALLOC + _CrtMemCheckpoint (&eventpipe_memory_start_snapshot); +#endif + return NULL; +} + +static RESULT +test_create_delete_provider (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeProvider *test_provider = ep_create_provider (TEST_PROVIDER_NAME, NULL, NULL); + if (!test_provider) { + result = FAILED ("Failed to create provider %s, ep_create_provider returned NULL", TEST_PROVIDER_NAME); + ep_raise_error (); + } + +ep_on_exit: + ep_delete_provider (test_provider); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_stress_create_delete_provider (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeProvider *test_providers [1000] = {0}; + + for (uint32_t i = 0; i < 1000; ++i) { + char *provider_name = g_strdup_printf (TEST_PROVIDER_NAME "_%i", i); + test_providers [i] = ep_create_provider (provider_name, NULL, NULL); + g_free (provider_name); + + if (!test_providers [i]) { + result = FAILED ("Failed to create provider %s_%i, ep_create_provider returned NULL", TEST_PROVIDER_NAME, i); + ep_raise_error (); + } + } + +ep_on_exit: + for (uint32_t i = 0; i < 1000; ++i) { + if (test_providers [i]) + ep_delete_provider (test_providers [i]); + } + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_get_provider (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeProvider *test_provider = ep_create_provider (TEST_PROVIDER_NAME, NULL, NULL); + if (!test_provider) { + result = FAILED ("Failed to create provider %s, ep_create_provider returned NULL", TEST_PROVIDER_NAME); + ep_raise_error (); + } + + test_location = 1; + + EventPipeProvider *returned_test_provider = ep_get_provider (TEST_PROVIDER_NAME); + if (!returned_test_provider) { + result = FAILED ("Failed to get provider %s, ep_get_provider returned NULL", TEST_PROVIDER_NAME); + ep_raise_error (); + } + + test_location = 2; + + ep_delete_provider (test_provider); + test_provider = NULL; + + returned_test_provider = ep_get_provider (TEST_PROVIDER_NAME); + if (returned_test_provider) { + result = FAILED ("Provider %s, still returned from ep_get_provider after deleted", TEST_PROVIDER_NAME); + ep_raise_error (); + } + +ep_on_exit: + ep_delete_provider (test_provider); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_create_same_provider_twice (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeProvider *test_provider = ep_create_provider (TEST_PROVIDER_NAME, NULL, NULL); + if (!test_provider) { + result = FAILED ("Failed to create provider %s, ep_create_provider returned NULL", TEST_PROVIDER_NAME); + ep_raise_error (); + } + + test_location = 1; + + EventPipeProvider *returned_test_provider = ep_get_provider (TEST_PROVIDER_NAME); + if (!returned_test_provider) { + result = FAILED ("Failed to get provider %s, ep_get_provider returned NULL", TEST_PROVIDER_NAME); + ep_raise_error (); + } + + test_location = 2; + + EventPipeProvider *test_provider2 = ep_create_provider (TEST_PROVIDER_NAME, NULL, NULL); + if (test_provider2) { + result = FAILED ("Creating an already existing provider %s, succeeded", TEST_PROVIDER_NAME); + ep_raise_error (); + } + + test_location = 3; + + returned_test_provider = ep_get_provider (TEST_PROVIDER_NAME); + if (!returned_test_provider) { + result = FAILED ("Failed to get provider %s, ep_get_provider returned NULL", TEST_PROVIDER_NAME); + ep_raise_error (); + } + +ep_on_exit: + ep_delete_provider (test_provider); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + + +static RESULT +test_enable_disable (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + 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, ""); + ep_raise_error_if_nok (current_provider_config != NULL); + + test_location = 1; + + session_id = ep_enable ( + TEST_FILE, + 1, + current_provider_config, + 1, + EP_SESSION_TYPE_FILE, + EP_SERIALIZATION_FORMAT_NETTRACE_V4, + false, + NULL, + false); + + if (!session_id) { + result = FAILED ("Failed to enable session"); + ep_raise_error (); + } + + test_location = 2; + + if (!ep_enabled ()) { + result = FAILED ("event pipe disabled"); + ep_raise_error (); + } + +ep_on_exit: + ep_disable (session_id); + ep_provider_config_fini (current_provider_config); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static bool provider_callback_data; + +static +void +provider_callback ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_context) +{ + *(bool *)callback_context = true; +} + +static RESULT +test_create_delete_provider_with_callback (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeSessionID session_id = 0; + EventPipeProvider *test_provider = NULL; + EventPipeProviderConfiguration provider_config; + + 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; + + session_id = ep_enable ( + TEST_FILE, + 1, + current_provider_config, + 1, + EP_SESSION_TYPE_FILE, + EP_SERIALIZATION_FORMAT_NETTRACE_V4, + false, + NULL, + false); + + if (!session_id) { + result = FAILED ("Failed to enable session"); + ep_raise_error (); + } + + test_location = 2; + + test_provider = ep_create_provider (TEST_PROVIDER_NAME, provider_callback, &provider_callback_data); + ep_raise_error_if_nok (test_provider != NULL); + + test_location = 3; + + if (!provider_callback_data) { + result = FAILED ("Provider callback not called"); + ep_raise_error (); + } + +ep_on_exit: + ep_delete_provider (test_provider); + ep_disable (session_id); + ep_provider_config_fini (current_provider_config); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_build_event_metadata (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeProvider *provider = NULL; + EventPipeEvent *ep_event = NULL; + EventPipeEventInstance *ep_event_instance = NULL; + EventPipeEventMetadataEvent *metadata_event = NULL; + + provider = ep_create_provider (TEST_PROVIDER_NAME, NULL, NULL); + ep_raise_error_if_nok (provider != NULL); + + test_location = 1; + + ep_event = ep_event_alloc (provider, 1, 1, 1, EP_EVENT_LEVEL_VERBOSE, false, NULL, 0); + ep_raise_error_if_nok (ep_event != NULL); + + test_location = 2; + + ep_event_instance = ep_event_instance_alloc (ep_event, 0, 0, NULL, 0, NULL, NULL); + ep_raise_error_if_nok (ep_event_instance != NULL); + + test_location = 3; + + metadata_event = ep_build_event_metadata_event (ep_event_instance, 1); + ep_raise_error_if_nok (metadata_event != NULL); + + test_location = 4; + +ep_on_exit: + ep_delete_provider (provider); + ep_event_free (ep_event); + ep_event_instance_free (ep_event_instance); + ep_event_metdata_event_free (metadata_event); + + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_start_session_streaming (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + 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, ""); + ep_raise_error_if_nok (current_provider_config != NULL); + + test_location = 1; + + session_id = ep_enable ( + TEST_FILE, + 1, + current_provider_config, + 1, + EP_SESSION_TYPE_FILE, + EP_SERIALIZATION_FORMAT_NETTRACE_V4, + false, + NULL, + false); + + if (!session_id) { + result = FAILED ("Failed to enable session"); + ep_raise_error (); + } + + test_location = 2; + + ep_start_streaming (session_id); + +ep_on_exit: + ep_disable (session_id); + ep_provider_config_fini (current_provider_config); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_eventpipe_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_tests [] = { + {"test_eventpipe_setup", test_eventpipe_setup}, + {"test_create_delete_provider", test_create_delete_provider}, + {"test_stress_create_delete_provider", test_stress_create_delete_provider}, + {"test_get_provider", test_get_provider}, + {"test_create_same_provider_twice", test_create_same_provider_twice}, + {"test_enable_disable", test_enable_disable}, + {"test_create_delete_provider_with_callback", test_create_delete_provider_with_callback}, + {"test_build_event_metadata", test_build_event_metadata}, + {"test_start_session_streaming", test_start_session_streaming}, + {"test_eventpipe_teardown", test_eventpipe_teardown}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(ep_tests_init, ep_tests) diff --git a/src/mono/mono/eventpipe/test/ep-tests.h b/src/mono/mono/eventpipe/test/ep-tests.h new file mode 100644 index 0000000000000..c8e56be313a6e --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-tests.h @@ -0,0 +1,30 @@ +#ifndef _EVENTPIPE_TESTS_H +#define _EVENTPIPE_TESTS_H + +#include "eglib/test/test.h" + +DEFINE_TEST_GROUP_INIT_H(ep_setup_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); +DEFINE_TEST_GROUP_INIT_H(ep_session_tests_init); +DEFINE_TEST_GROUP_INIT_H(ep_thread_tests_init); +DEFINE_TEST_GROUP_INIT_H(ep_tests_init); +DEFINE_TEST_GROUP_INIT_H(fake_tests_init); +DEFINE_TEST_GROUP_INIT_H(ep_teardown_tests_init); + +const +static Group test_groups [] = { + {"setup", ep_setup_tests_init}, + {"fastserialzier", ep_fastserializer_tests_init}, + {"provider-callback-dataqueue", ep_provider_callback_data_queue_tests_init}, + {"file", ep_file_tests_init}, + {"session", ep_session_tests_init}, + {"thread", ep_thread_tests_init}, + {"eventpipe", ep_tests_init}, + {"fake", fake_tests_init}, + {"teardown", ep_teardown_tests_init}, + {NULL, NULL} +}; + +#endif /* _EVENTPIPE_TESTS_H */ diff --git a/src/mono/mono/eventpipe/test/ep-thread-tests.c b/src/mono/mono/eventpipe/test/ep-thread-tests.c new file mode 100644 index 0000000000000..9f68befe532a0 --- /dev/null +++ b/src/mono/mono/eventpipe/test/ep-thread-tests.c @@ -0,0 +1,478 @@ +#include "mono/eventpipe/ep.h" +#include "eglib/test/test.h" + +#define TEST_FILE "./ep_test_create_file.txt" + +#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_thread_setup (void) +{ +#ifdef _CRTDBG_MAP_ALLOC + _CrtMemCheckpoint (&eventpipe_memory_start_snapshot); +#endif + return NULL; +} + +static RESULT +test_create_free_thread (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeThread *thread = ep_thread_alloc (); + if (!thread) { + result = FAILED ("Failed to create thread"); + ep_raise_error (); + } + +ep_on_exit: + ep_thread_free (thread); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_addref_release_thread (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeThread *thread = ep_thread_alloc (); + if (!thread) { + result = FAILED ("Failed to create thread"); + ep_raise_error (); + } + + test_location = 1; + + if (ep_rt_volatile_load_uint32_t ((const volatile uint32_t *)ep_thread_get_ref_count_ref (thread)) != 0) { + result = FAILED ("Ref count should start at 0"); + ep_raise_error (); + } + + test_location = 2; + + ep_thread_addref (thread); + + if (ep_rt_volatile_load_uint32_t ((const volatile uint32_t *)ep_thread_get_ref_count_ref (thread)) != 1) { + result = FAILED ("addref should increment 1"); + ep_raise_error (); + } + + test_location = 3; + + ep_thread_release (thread); + thread = NULL; + +ep_on_exit: + ep_thread_free (thread); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_get_or_create_thread (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeThread *thread = ep_thread_get (); + if (thread) { + result = FAILED ("ep_thread_get should return NULL"); + ep_raise_error (); + } + + test_location = 1; + + thread = ep_thread_get_or_create (); + if (!thread) { + result = FAILED ("ep_thread_get_or_create should not return NULL"); + ep_raise_error (); + } + + test_location = 2; + + thread = ep_thread_get (); + if (!thread) { + result = FAILED ("ep_thread_get should not return NULL"); + ep_raise_error (); + } + + test_location = 3; + + if (ep_rt_volatile_load_uint32_t ((const volatile uint32_t *)ep_thread_get_ref_count_ref (thread)) != 1) { + result = FAILED ("thread ref count should be 1"); + ep_raise_error (); + } + + test_location = 4; + + // Need to emulate a thread exit to make sure TLS gets cleaned up for current thread + // or we will get memory leaks reported. + ep_rt_mono_thread_exited (); + + thread = ep_thread_get (); + if (thread) { + result = FAILED ("ep_thread_get should return NULL"); + ep_raise_error (); + } + +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_thread_activity_id (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + uint8_t empty_id [EP_ACTIVITY_ID_SIZE] = {0}; + uint8_t current_activity_id [EP_ACTIVITY_ID_SIZE]; + uint8_t new_activity_id [EP_ACTIVITY_ID_SIZE]; + + ep_thread_create_activity_id (new_activity_id, sizeof (new_activity_id)); + if (!memcmp (empty_id, new_activity_id, sizeof (new_activity_id))) { + result = FAILED ("Created activity id is empty"); + ep_raise_error (); + } + + test_location = 1; + + EventPipeThread *thread = ep_thread_get (); + if (thread) { + result = FAILED ("ep_thread_get should return NULL"); + ep_raise_error (); + } + + test_location = 2; + + thread = ep_thread_get_or_create (); + if (!thread) { + result = FAILED ("ep_thread_get_or_create should not return NULL"); + ep_raise_error (); + } + + test_location = 3; + + ep_thread_get_activity_id (thread, current_activity_id, sizeof (current_activity_id)); + if (memcmp (empty_id, current_activity_id, sizeof (current_activity_id))) { + result = FAILED ("Current activity id is not empty"); + ep_raise_error (); + } + + test_location = 4; + + ep_thread_set_activity_id (thread, new_activity_id, sizeof (new_activity_id)); + + ep_thread_get_activity_id (thread, current_activity_id, sizeof (current_activity_id)); + if (memcmp (new_activity_id, current_activity_id, sizeof (current_activity_id))) { + result = FAILED ("Current activity id doesn't match previously set activity id"); + ep_raise_error (); + } + + test_location = 5; + + // Need to emulate a thread exit to make sure TLS gets cleaned up for current thread + // or we will get memory leaks reported. + ep_rt_mono_thread_exited (); + + thread = ep_thread_get (); + if (thread) { + result = FAILED ("ep_thread_get should return NULL"); + ep_raise_error (); + } + +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_thread_is_rundown_thread (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeThread *thread = ep_thread_alloc (); + if (!thread) { + result = FAILED ("Failed to create thread"); + ep_raise_error (); + } + + test_location = 1; + + if (ep_thread_is_rundown_thread (thread)) { + result = FAILED ("Thread is a rundown thread"); + ep_raise_error (); + } + + test_location = 2; + + EventPipeSession dummy_session; + ep_thread_set_as_rundown_thread (thread, &dummy_session); + if (!ep_thread_is_rundown_thread (thread)) { + result = FAILED ("Thread is not a rundown thread"); + ep_raise_error (); + } + + test_location = 3; + + if (ep_thread_get_rundown_session (thread) != &dummy_session) { + result = FAILED ("Unexpected rundown session"); + ep_raise_error (); + } + + test_location = 4; + + ep_thread_set_as_rundown_thread (thread, NULL); + + if (ep_thread_is_rundown_thread (thread)) { + result = FAILED ("Thread is a rundown thread"); + ep_raise_error (); + } + +ep_on_exit: + ep_thread_free (thread); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_thread_lock (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeThread *thread = ep_thread_alloc (); + if (!thread) { + result = FAILED ("Failed to create thread"); + ep_raise_error (); + } + + test_location = 1; + + ep_thread_requires_lock_not_held (thread); + + ep_rt_spin_lock_aquire (ep_thread_get_rt_lock_ref (thread)); + + ep_thread_requires_lock_held (thread); + + ep_rt_spin_lock_release (ep_thread_get_rt_lock_ref (thread)); + + ep_thread_requires_lock_not_held (thread); + +ep_on_exit: + ep_thread_free (thread); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_thread_session_write (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + + EventPipeThread *thread = ep_thread_alloc (); + if (!thread) { + result = FAILED ("Failed to create thread"); + ep_raise_error (); + } + + test_location = 1; + + uint32_t session_write = ep_thread_get_session_write_in_progress (thread); + if (session_write) { + result = FAILED ("Session write is in progress"); + ep_raise_error (); + } + + test_location = 2; + + ep_thread_set_session_write_in_progress (thread, 1); + + session_write = ep_thread_get_session_write_in_progress (thread); + if (session_write != 1) { + result = FAILED ("Wrong session id in write progress"); + ep_raise_error (); + } + + test_location = 3; + + ep_thread_set_session_write_in_progress (thread, 0); + + session_write = ep_thread_get_session_write_in_progress (thread); + if (session_write) { + result = FAILED ("Session write is in progress"); + ep_raise_error (); + } + +ep_on_exit: + ep_thread_free (thread); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_thread_session_state (void) +{ + RESULT result = NULL; + uint32_t test_location = 0; + EventPipeThread *thread = NULL; + EventPipeProviderConfiguration *provider_config = NULL; + EventPipeSession *session = NULL; + EventPipeThreadSessionState *session_state = NULL; + + thread = ep_thread_alloc (); + if (!thread) { + result = FAILED ("Failed to create thread"); + ep_raise_error (); + } + + ep_thread_addref (thread); + + test_location = 1; + + { + EventPipeProviderConfiguration dummy_config; + if (!ep_provider_config_init (&dummy_config, "DummyProvider", 0, 0, "")) { + result = FAILED ("Failed to init provider config"); + ep_raise_error (); + } + provider_config = &dummy_config; + } + + test_location = 2; + + EP_CONFIG_LOCK_ENTER + session = ep_session_alloc ( + 1, + TEST_FILE, + NULL, + EP_SESSION_TYPE_FILE, + EP_SERIALIZATION_FORMAT_NETTRACE_V4, + false, + 1, + provider_config, + 1, + false); + EP_CONFIG_LOCK_EXIT + + if (!session) { + result = FAILED ("Failed to alloc session"); + ep_raise_error (); + } + + test_location = 3; + + ep_rt_spin_lock_aquire (ep_thread_get_rt_lock_ref (thread)); + session_state = ep_thread_get_or_create_session_state (thread, session); + ep_rt_spin_lock_release (ep_thread_get_rt_lock_ref (thread)); + + if (!session_state) { + result = FAILED ("Failed to alloc session state"); + ep_raise_error (); + } + + test_location = 4; + + ep_rt_spin_lock_aquire (ep_thread_get_rt_lock_ref (thread)); + EventPipeThreadSessionState *current_session_state = ep_thread_get_or_create_session_state (thread, session); + ep_rt_spin_lock_release (ep_thread_get_rt_lock_ref (thread)); + + if (current_session_state != session_state) { + result = FAILED ("Second call to get_or_create_session_state allocated new session_state"); + ep_raise_error (); + } + + test_location = 5; + + ep_rt_spin_lock_aquire (ep_thread_get_rt_lock_ref (thread)); + current_session_state = ep_thread_get_session_state (thread, session); + ep_rt_spin_lock_release (ep_thread_get_rt_lock_ref (thread)); + + if (current_session_state != session_state) { + result = FAILED ("Call to get_session_state allocated returned unexpected session"); + ep_raise_error (); + } + +ep_on_exit: + if (thread && session_state) { + ep_rt_spin_lock_aquire (ep_thread_get_rt_lock_ref (thread)); + ep_thread_delete_session_state (thread, session); + ep_rt_spin_lock_release (ep_thread_get_rt_lock_ref (thread)); + } + ep_session_free (session); + ep_provider_config_fini (provider_config); + ep_thread_release (thread); + return result; + +ep_on_error: + if (!result) + result = FAILED ("Failed at test location=%i", test_location); + ep_exit_error_handler (); +} + +static RESULT +test_thread_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_thread_tests [] = { + {"test_thread_setup", test_thread_setup}, + {"test_create_free_thread", test_create_free_thread}, + {"test_addref_release_thread", test_addref_release_thread}, + {"test_get_or_create_thread", test_get_or_create_thread}, + {"test_thread_activity_id", test_thread_activity_id}, + {"test_thread_is_rundown_thread", test_thread_is_rundown_thread}, + {"test_thread_lock", test_thread_lock}, + {"test_thread_session_write", test_thread_session_write}, + {"test_thread_session_state", test_thread_session_state}, + {"test_thread_teardown", test_thread_teardown}, + {NULL, NULL} +}; + +DEFINE_TEST_GROUP_INIT(ep_thread_tests_init, ep_thread_tests) diff --git a/src/mono/mono/metadata/Makefile.am b/src/mono/mono/metadata/Makefile.am index 2ba687b890e2b..d9ad09f5cca39 100644 --- a/src/mono/mono/metadata/Makefile.am +++ b/src/mono/mono/metadata/Makefile.am @@ -91,6 +91,10 @@ if !ENABLE_ILGEN ilgen_libraries = libmono-ilgen.la endif +if ENABLE_PERFTRACING +eventpipe_libs = $(top_builddir)/mono/eventpipe/libeventpipe.la +endif + BUNDLE_ZLIB_PATH=$(top_builddir)/mono/zlib/libz.la if HAVE_STATIC_ZLIB @@ -169,7 +173,7 @@ if BITCODE if WASM libmono_icall_table_la_LIBADD = # empty to avoid duplicate symbols when enabling dynamic linking else -libmono_icall_table_la_LIBADD = $(glib_libs) ../utils/libmonoutils.la ../sgen/libmonosgen.la libmonoruntimesgen.la +libmono_icall_table_la_LIBADD = $(glib_libs) ../utils/libmonoutils.la ../sgen/libmonosgen.la $(eventpipe_libs) libmonoruntimesgen.la endif endif endif @@ -193,7 +197,7 @@ if BITCODE if WASM libmono_ilgen_la_LIBADD = # empty to avoid duplicate symbols when enabling dynamic linking else -libmono_ilgen_la_LIBADD = $(glib_libs) ../utils/libmonoutils.la ../sgen/libmonosgen.la libmonoruntimesgen.la +libmono_ilgen_la_LIBADD = $(glib_libs) ../utils/libmonoutils.la ../sgen/libmonosgen.la $(eventpipe_libs) libmonoruntimesgen.la endif endif endif @@ -260,6 +264,7 @@ common_sources = \ domain-internals.h \ environment.c \ environment.h \ + icall-eventpipe.c \ exception.c \ exception.h \ exception-internals.h \ diff --git a/src/mono/mono/metadata/icall-eventpipe.c b/src/mono/mono/metadata/icall-eventpipe.c new file mode 100644 index 0000000000000..9f64e90cc44db --- /dev/null +++ b/src/mono/mono/metadata/icall-eventpipe.c @@ -0,0 +1,489 @@ +#include +#include +#include + +#ifdef ENABLE_NETCORE +#include + +#ifdef ENABLE_PERFTRACING +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef enum _EventPipeActivityControlCode { + EP_ACTIVITY_CONTROL_GET_ID = 1, + EP_ACTIVITY_CONTROL_SET_ID = 2, + EP_ACTIVITY_CONTROL_CREATE_ID = 3, + EP_ACTIVITY_CONTROL_GET_SET_ID = 4, + EP_ACTIVITY_CONTROL_CREATE_SET_ID = 5 +} EventPipeActivityControlCode; + +typedef struct _EventPipeProviderConfigurationNative { + gunichar2 *provider_name; + uint64_t keywords; + uint32_t logging_level; + 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; + int64_t timestamp_frequency; +} EventPipeSessionInfo; + +typedef struct _EventPipeEventInstanceData { + intptr_t provider_id; + uint32_t event_id; + uint32_t thread_id; + int64_t timestamp; + uint8_t activity_id [EP_ACTIVITY_ID_SIZE]; + uint8_t related_activity_id [EP_ACTIVITY_ID_SIZE]; + const uint8_t *payload; + uint32_t payload_len; +} EventPipeEventInstanceData; + +gboolean ep_rt_mono_initialized; +MonoNativeTlsKey ep_rt_mono_thread_holder_tls_id; +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) +{ + g_assert (ep_rt_mono_rand_provider != NULL); + return mono_rand_try_get_bytes (&ep_rt_mono_rand_provider, buffer, buffer_size, error); +} + +static +EventPipeThread * +eventpipe_thread_get (void) +{ + EventPipeThreadHolder *thread_holder = mono_native_tls_get_value (ep_rt_mono_thread_holder_tls_id); + return thread_holder ? ep_thread_holder_get_thread (thread_holder) : NULL; +} + +static +EventPipeThread * +eventpipe_thread_get_or_create (void) +{ + EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (ep_rt_mono_thread_holder_tls_id); + if (!thread_holder && thread_holder_alloc_callback_func) { + thread_holder = thread_holder_alloc_callback_func (); + mono_native_tls_set_value (ep_rt_mono_thread_holder_tls_id, thread_holder); + } + return ep_thread_holder_get_thread (thread_holder); +} + +static +void +eventpipe_thread_exited (void) +{ + if (ep_rt_mono_initialized) { + EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (ep_rt_mono_thread_holder_tls_id); + if (thread_holder && thread_holder_free_callback_func) + thread_holder_free_callback_func (thread_holder); + mono_native_tls_set_value (ep_rt_mono_thread_holder_tls_id, NULL); + } +} + +static +void +profiler_eventpipe_thread_exited (MonoProfiler *prof, uintptr_t tid) +{ + eventpipe_thread_exited (); +} + +void +mono_eventpipe_init ( + EventPipeMonoFuncTable *table, + ep_rt_thread_holder_alloc_func thread_holder_alloc_func, + ep_rt_thread_holder_free_func thread_holder_free_func) +{ + g_assert (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; + table->ep_rt_mono_native_thread_id_equals = mono_native_thread_id_equals; + table->ep_rt_mono_runtime_is_shutting_down = mono_runtime_is_shutting_down; + table->ep_rt_mono_rand_try_get_bytes = rand_try_get_bytes_func; + table->ep_rt_mono_thread_get = eventpipe_thread_get; + table->ep_rt_mono_thread_get_or_create = eventpipe_thread_get_or_create; + table->ep_rt_mono_thread_exited = eventpipe_thread_exited; + table->ep_rt_mono_w32file_close = mono_w32file_close; + table->ep_rt_mono_w32file_create = mono_w32file_create; + table->ep_rt_mono_w32file_write = mono_w32file_write; + + thread_holder_alloc_callback_func = thread_holder_alloc_func; + thread_holder_free_callback_func = thread_holder_free_func; + mono_native_tls_alloc (&ep_rt_mono_thread_holder_tls_id, NULL); + + mono_100ns_ticks (); + mono_rand_open (); + ep_rt_mono_rand_provider = mono_rand_init (NULL, 0); + + ep_rt_mono_initialized = TRUE; + + MonoProfilerHandle profiler = mono_profiler_create (NULL); + mono_profiler_set_thread_stopped_callback (profiler, profiler_eventpipe_thread_exited); +} + +void +mono_eventpipe_fini (void) +{ + if (ep_rt_mono_initialized) + mono_rand_close (ep_rt_mono_rand_provider); + + ep_rt_mono_rand_provider = NULL; + thread_holder_alloc_callback_func = NULL; + thread_holder_free_callback_func = NULL; + ep_rt_mono_initialized = FALSE; +} + +gconstpointer +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_CreateProvider ( + MonoStringHandle provider_name, + MonoDelegateHandle callback_func, + MonoError *error) +{ + EventPipeProvider *provider = NULL; + + if (MONO_HANDLE_IS_NULL (provider_name)) { + mono_error_set_argument_null (error, "providerName", ""); + return NULL; + } + + char *provider_name_utf8 = mono_string_handle_to_utf8 (provider_name, error); + + //TODO: Need to pin delegate if we switch to safe mode or maybe we should get funcptr in icall? + provider = ep_create_provider (provider_name_utf8, MONO_HANDLE_GETVAL (callback_func, delegate_trampoline), NULL); + + g_free (provider_name_utf8); + return provider; +} + +intptr_t +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_DefineEvent ( + intptr_t provider_handle, + uint32_t event_id, + int64_t keywords, + uint32_t event_version, + uint32_t level, + const uint8_t *metadata, + uint32_t metadata_len) +{ + g_assert (provider_handle != 0); + + EventPipeProvider *provider = (EventPipeProvider *)provider_handle; + EventPipeEvent *ep_event = ep_provider_add_event (provider, event_id, (uint64_t)keywords, event_version, (EventPipeEventLevel)level, /* needStack = */ true, metadata, metadata_len); + + g_assert (ep_event != NULL); + return (intptr_t)ep_event; +} + +void +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_DeleteProvider (intptr_t provider_handle) +{ + if (provider_handle) { + ep_delete_provider ((EventPipeProvider *)provider_handle); + } +} + +void +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_Disable (uint64_t session_id) +{ + ep_disable (session_id); +} + +uint64_t +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_Enable ( + const gunichar2 *output_file, + /* EventPipeSerializationFormat */int32_t format, + uint32_t circular_buffer_size_mb, + /* EventPipeProviderConfigurationNative[] */const void *providers, + uint32_t providers_len) +{ + ERROR_DECL (error); + EventPipeSessionID session_id = 0; + + if (circular_buffer_size_mb == 0 || format > EP_SERIALIZATION_FORMAT_COUNT || providers_len == 0 || providers == NULL) + return 0; + + char *output_file_utf8 = mono_utf16_to_utf8 (output_file, g_utf16_len (output_file), error); + + session_id = ep_enable ( + output_file_utf8, + circular_buffer_size_mb, + providers, + providers_len, + output_file != NULL ? EP_SESSION_TYPE_FILE : EP_SESSION_TYPE_LISTENER, + (EventPipeSerializationFormat)format, + true, + NULL, + true); + ep_start_streaming (session_id); + + g_free (output_file_utf8); + return session_id; +} + +int32_t +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_EventActivityIdControl ( + uint32_t control_code, + /* GUID * */uint8_t *activity_id) +{ + int32_t result = 0; + EventPipeThread *thread = ep_thread_get (); + + if (thread == NULL) + return 1; + + uint8_t current_activity_id [EP_ACTIVITY_ID_SIZE]; + EventPipeActivityControlCode activity_control_code = (EventPipeActivityControlCode)control_code; + switch (activity_control_code) { + case EP_ACTIVITY_CONTROL_GET_ID: + ep_thread_get_activity_id (thread, activity_id, EP_ACTIVITY_ID_SIZE); + break; + case EP_ACTIVITY_CONTROL_SET_ID: + ep_thread_set_activity_id (thread, activity_id, EP_ACTIVITY_ID_SIZE); + break; + case EP_ACTIVITY_CONTROL_CREATE_ID: + ep_thread_create_activity_id (activity_id, EP_ACTIVITY_ID_SIZE); + break; + case EP_ACTIVITY_CONTROL_GET_SET_ID: + ep_thread_get_activity_id (thread, activity_id, EP_ACTIVITY_ID_SIZE); + ep_thread_create_activity_id (current_activity_id, G_N_ELEMENTS (current_activity_id)); + ep_thread_set_activity_id (thread, current_activity_id, G_N_ELEMENTS (current_activity_id)); + break; + default: + result = 1; + break; + } + + return result; +} + +MonoBoolean +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetNextEvent ( + uint64_t session_id, + /* EventPipeEventInstanceData * */void *instance) +{ + g_assert (instance != NULL); + + EventPipeEventInstance *const next_instance = ep_get_next_event (session_id); + EventPipeEventInstanceData *const data = (EventPipeEventInstanceData *)instance; + if (next_instance && data) { + const EventPipeEvent *const ep_event = ep_event_instance_get_ep_event (next_instance); + if (ep_event) { + data->provider_id = (intptr_t)ep_event_get_provider (ep_event); + data->event_id = ep_event_get_event_id (ep_event); + } + data->thread_id = ep_event_instance_get_thread_id (next_instance); + data->timestamp = ep_event_instance_get_timestamp (next_instance); + memcpy (&data->activity_id, ep_event_instance_get_activity_id_cref (next_instance), EP_ACTIVITY_ID_SIZE); + memcpy (&data->related_activity_id, ep_event_instance_get_related_activity_id_cref (next_instance), EP_ACTIVITY_ID_SIZE); + data->payload = ep_event_instance_get_data (next_instance); + data->payload_len = ep_event_instance_get_data_len (next_instance); + } + + return next_instance != NULL; +} + +intptr_t +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetProvider (const gunichar2 *provider_name) +{ + ERROR_DECL (error); + char * provider_name_utf8 = NULL; + EventPipeProvider *provider = NULL; + + if (provider_name) { + provider_name_utf8 = mono_utf16_to_utf8 (provider_name, g_utf16_len (provider_name), error); + provider = ep_get_provider (provider_name_utf8); + } + + g_free (provider_name_utf8); + return (intptr_t)provider; +} + +MonoBoolean +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetSessionInfo ( + uint64_t session_id, + /* EventPipeSessionInfo * */void *session_info) +{ + bool result = false; + if (session_info) { + EventPipeSession *session = ep_get_session ((EventPipeSessionID)session_id); + if (session) { + EventPipeSessionInfo *instance = (EventPipeSessionInfo *)session_info; + instance->starttime_as_utc_filetime = ep_session_get_session_start_time (session); + instance->start_timestamp = ep_session_get_session_start_timestamp (session); + instance->timestamp_frequency = ep_perf_frequency_query (); + result = true; + } + } + + return result; +} + +intptr_t +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetWaitHandle (uint64_t session_id) +{ + return (intptr_t)ep_get_wait_handle (session_id); +} + +void +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WriteEventData ( + intptr_t event_handle, + /* EventProviderEventData[] */const void *event_data, + uint32_t data_len, + /* GUID * */const uint8_t *activity_id, + /* GUID * */const uint8_t *related_activity_id) +{ + ; +} + +#else /* ENABLE_PERFTRACING */ + +gconstpointer +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_CreateProvider ( + MonoStringHandle provider_name, + MonoDelegateHandle callback_func, + MonoError *error) +{ + mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.CreateProvider"); + return NULL; +} + +intptr_t +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_DefineEvent ( + intptr_t provider_handle, + uint32_t event_id, + int64_t keywords, + uint32_t event_version, + uint32_t level, + const uint8_t *metadata, + uint32_t metadata_len) +{ + ERROR_DECL (error); + mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.DefineEvent"); + mono_error_set_pending_exception (error); + return 0; +} + +void +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_DeleteProvider (intptr_t provider_handle) +{ + ERROR_DECL (error); + mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.DeleteProvider"); + mono_error_set_pending_exception (error); +} + +void +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_Disable (uint64_t session_id) +{ + ERROR_DECL (error); + mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.Disable"); + mono_error_set_pending_exception (error); +} + +uint64_t +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_Enable ( + const gunichar2 *output_file, + /* EventPipeSerializationFormat */int32_t format, + uint32_t circular_buffer_size_mb, + /* EventPipeProviderConfigurationNative[] */const void *providers, + uint32_t providers_len) +{ + ERROR_DECL (error); + mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.Enable"); + mono_error_set_pending_exception (error); + return 0; +} + +int32_t +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_EventActivityIdControl ( + uint32_t control_code, + /* GUID * */uint8_t *activity_id) +{ + ERROR_DECL (error); + mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.EventActivityIdControl"); + mono_error_set_pending_exception (error); + return 0; +} + +MonoBoolean +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetNextEvent ( + uint64_t session_id, + /* EventPipeEventInstanceData * */void *instance) +{ + ERROR_DECL (error); + mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.GetNextEvent"); + mono_error_set_pending_exception (error); + return FALSE; +} + +intptr_t +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetProvider (const gunichar2 *provider_name) +{ + ERROR_DECL (error); + mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.GetProvider"); + mono_error_set_pending_exception (error); + return 0; +} + +MonoBoolean +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetSessionInfo ( + uint64_t session_id, + /* EventPipeSessionInfo * */void *session_info) +{ + ERROR_DECL (error); + mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.GetSessionInfo"); + mono_error_set_pending_exception (error); + return FALSE; +} + +intptr_t +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetWaitHandle (uint64_t session_id) +{ + ERROR_DECL (error); + mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.GetWaitHandle"); + mono_error_set_pending_exception (error); + return 0; +} + +void +ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WriteEventData ( + intptr_t event_handle, + /* EventProviderEventData[] */const void *event_data, + uint32_t data_len, + /* GUID * */const uint8_t *activity_id, + /* GUID * */const uint8_t *related_activity_id) +{ + ERROR_DECL (error); + mono_error_set_not_implemented (error, "System.Diagnostics.Tracing.EventPipeInternal.WriteEventData"); + mono_error_set_pending_exception (error); +} + +#endif /* ENABLE_PERFTRACING */ +#endif /* ENABLE_NETCORE */ + +MONO_EMPTY_SOURCE_FILE (eventpipe_rt_mono); diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 0fa8444117d9a..c51006c362ed1 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -8427,115 +8427,6 @@ ves_icall_System_Diagnostics_Debugger_Log (int level, MonoString *volatile* cate mono_get_runtime_callbacks ()->debug_log (level, *category, *message); } -#ifdef ENABLE_NETCORE -#define EVENT_PIPE_DUMMY_PROVIDER_ID 1 -#define EVENT_PIPE_DUMMY_SESSION_ID 1 -#define EVENT_PIPE_DUMMY_EVENT_ID 1 -#define EVENT_PIPE_ERROR_SUCCESS 0 -#define EVENT_PIPE_INVALID_WAIT_HANDLE 0 - -typedef enum _EventPipeSerializationFormat{ - NetPerf, - NetTrace -} EventPipeSerializationFormat; - -typedef struct _EventPipeProviderConfigurationNative { - gunichar2 *provider_name; - uint64_t keywords; - uint32_t logging_level; - 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; - int64_t timestamp_frequency; -} EventPipeSessionInfo; - -typedef struct _EventPipeEventInstanceData { - intptr_t provider_id; - uint32_t event_id; - uint32_t thread_id; - int64_t timestamp; - uint8_t activity_id[16]; - uint8_t related_activity_id[16]; - const uint8_t *payload; - uint32_t payload_length; -} EventPipeEventInstanceData; - -gconstpointer -ves_icall_System_Diagnostics_Tracing_EventPipeInternal_CreateProvider (MonoStringHandle provider_name, MonoDelegateHandle callback_func, MonoError *error) -{ - return GUINT_TO_POINTER (EVENT_PIPE_DUMMY_PROVIDER_ID); -} - -intptr_t -ves_icall_System_Diagnostics_Tracing_EventPipeInternal_DefineEvent (intptr_t prov_handle, uint32_t event_id, int64_t keywords, uint32_t event_version, uint32_t level, const uint8_t *metadata, uint32_t metadata_len) -{ - return EVENT_PIPE_DUMMY_EVENT_ID; -} - -void -ves_icall_System_Diagnostics_Tracing_EventPipeInternal_DeleteProvider (intptr_t prov_handle) -{ - ; -} - -void -ves_icall_System_Diagnostics_Tracing_EventPipeInternal_Disable (uint64_t session_id) -{ - ; -} - -uint64_t -ves_icall_System_Diagnostics_Tracing_EventPipeInternal_Enable (const_gunichar2_ptr output_file, /* EventPipeSerializationFormat */int32_t format, uint32_t circular_buffer_size_mb, /* EventPipeProviderConfigurationNative[] */const void *providers, uint32_t num_providers) -{ - return EVENT_PIPE_DUMMY_SESSION_ID; -} - -int32_t -ves_icall_System_Diagnostics_Tracing_EventPipeInternal_EventActivityIdControl (uint32_t control_code, /* GUID * */uint8_t *activity_id) -{ - return EVENT_PIPE_ERROR_SUCCESS; -} - -MonoBoolean -ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetNextEvent (uint64_t session_id, /* EventPipeEventInstanceData * */void *instance) -{ - return FALSE; -} - -intptr_t -ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetProvider (const_gunichar2_ptr provider_name) -{ - return EVENT_PIPE_DUMMY_PROVIDER_ID; -} - -MonoBoolean -ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetSessionInfo (uint64_t session_id, /* EventPipeSessionInfo * */void *session_info) -{ - return FALSE; -} - -intptr_t -ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetWaitHandle (uint64_t session_id) -{ - return EVENT_PIPE_INVALID_WAIT_HANDLE; -} - -void -ves_icall_System_Diagnostics_Tracing_EventPipeInternal_WriteEventData (intptr_t event_handle, /* EventProviderEventData[] */const void *event_data, uint32_t data_count, /* GUID * */const uint8_t *activity_id, /* GUID * */const uint8_t *related_activity_id) -{ - ; -} -#endif /* ENABLE_NETCORE */ - #ifndef HOST_WIN32 static inline void mono_icall_write_windows_debug_string (const gunichar2 *message) diff --git a/src/mono/mono/mini/Makefile.am.in b/src/mono/mono/mini/Makefile.am.in index 4fa894984cc16..a8e9891def5b7 100755 --- a/src/mono/mono/mini/Makefile.am.in +++ b/src/mono/mono/mini/Makefile.am.in @@ -29,6 +29,10 @@ endif glib_libs = $(monodir)/mono/eglib/libeglib.la +if ENABLE_PERFTRACING +eventpipe_libs = $(monodir)/mono/eventpipe/libeventpipe.la +endif + boehm_libs= \ $(monodir)/mono/metadata/libmonoruntime.la \ $(monodir)/mono/utils/libmonoutils.la \ @@ -39,6 +43,7 @@ sgen_libs = \ $(monodir)/mono/metadata/libmonoruntimesgen.la \ $(monodir)/mono/sgen/libmonosgen.la \ $(monodir)/mono/utils/libmonoutils.la \ + $(eventpipe_libs) \ $(glib_libs) if ENABLE_LLVM diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index e76f06f4186a0..ca2a59cf11a20 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -74,6 +74,10 @@ #include #include +#ifdef ENABLE_PERFTRACING +#include +#endif + #include "mini.h" #include "seq-points.h" #include "tasklets.h" @@ -4531,6 +4535,10 @@ mini_init (const char *filename, const char *runtime_version) mono_install_get_class_from_name (mono_aot_get_class_from_name); mono_install_jit_info_find_in_aot (mono_aot_find_jit_info); +#ifdef ENABLE_PERFTRACING + ep_init (); +#endif + mono_profiler_state.context_enable = mini_profiler_context_enable; mono_profiler_state.context_get_this = mini_profiler_context_get_this; mono_profiler_state.context_get_argument = mini_profiler_context_get_argument; @@ -4992,6 +5000,9 @@ mini_cleanup (MonoDomain *domain) mono_runtime_print_stats (); jit_stats_cleanup (); mono_jit_dump_cleanup (); +#ifdef ENABLE_PERFTRACING + ep_shutdown (); +#endif } #else void diff --git a/src/mono/msvc/build-init.vcxproj b/src/mono/msvc/build-init.vcxproj index 8d15e191ac40d..8b36028ea476c 100644 --- a/src/mono/msvc/build-init.vcxproj +++ b/src/mono/msvc/build-init.vcxproj @@ -142,6 +142,7 @@ <_EnableDefines Condition="'$(MONO_ENABLE_LLVM)' == 'true'">$(_EnableDefines);ENABLE_LLVM;ENABLE_LLVM_RUNTIME <_EnableDefines Condition="'$(MONO_ENABLE_NETCORE)' == 'true'">$(_EnableDefines);ENABLE_NETCORE + <_EnableDefines Condition="'$(MONO_ENABLE_PERFTRACING)' == 'true'">$(_EnableDefines);ENABLE_PERFTRACING <_HaveDefines Condition="'$(MONO_ENABLE_BTLS)' == 'true'">$(_HaveDefines);HAVE_BTLS <_EnableDefines>$(_EnableDefines.Trim(';')) <_HaveDefines>$(_HaveDefines.Trim(';')) diff --git a/src/mono/msvc/libeventpipe.targets b/src/mono/msvc/libeventpipe.targets new file mode 100644 index 0000000000000..9758f292b376c --- /dev/null +++ b/src/mono/msvc/libeventpipe.targets @@ -0,0 +1,149 @@ + + + + false + true + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + $(ExcludeEventPipeFromBuild) + CompileAsC + + + + diff --git a/src/mono/msvc/libeventpipe.targets.filters b/src/mono/msvc/libeventpipe.targets.filters new file mode 100644 index 0000000000000..1bb9dcffc7bc3 --- /dev/null +++ b/src/mono/msvc/libeventpipe.targets.filters @@ -0,0 +1,172 @@ + + + + + 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 + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source 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 + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Source Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + Header Files$(MonoRuntimeFilterSubFolder)\eventpipe + + + + + {D6D64FF2-7951-44D8-B965-4593893CEF35} + + + {B372B1CF-F13A-43B8-97E0-DF7A9563D4AE} + + + diff --git a/src/mono/msvc/libmono-dynamic.vcxproj b/src/mono/msvc/libmono-dynamic.vcxproj index c63be2c85ad72..cd120b7cfb16f 100644 --- a/src/mono/msvc/libmono-dynamic.vcxproj +++ b/src/mono/msvc/libmono-dynamic.vcxproj @@ -94,7 +94,6 @@ - /D /NODEFAULTLIB:LIBCD" " %(AdditionalOptions) Disabled $(MONO_DIR);$(MONO_INCLUDE_DIR);$(LIBGC_CPPFLAGS_INCLUDE);$(GLIB_CFLAGS_INCLUDE);$(MONO_LLVM_DEFAULT_INCLUDE_DIR);$(SHIM_GLOBALIZATION_INCLUDE_DIR);%(AdditionalIncludeDirectories) WIN32;WIN32_LEAN_AND_MEAN;$(GC_DEFINES);MONO_DLL_EXPORT;LLVM_API_VERSION=$(MONO_LLVM_DEFAULT_API_VERSION);$(SHIM_GLOBALIZATION_DEFINES);_DEBUG;%(PreprocessorDefinitions) @@ -121,7 +120,6 @@ X64 - /D /NODEFAULTLIB:LIBCD" " %(AdditionalOptions) Disabled $(MONO_DIR);$(MONO_INCLUDE_DIR);$(LIBGC_CPPFLAGS_INCLUDE);$(GLIB_CFLAGS_INCLUDE);$(MONO_LLVM_DEFAULT_INCLUDE_DIR);$(SHIM_GLOBALIZATION_INCLUDE_DIR);%(AdditionalIncludeDirectories) WIN32;WIN32_LEAN_AND_MEAN;$(GC_DEFINES);MONO_DLL_EXPORT;LLVM_API_VERSION=$(MONO_LLVM_DEFAULT_API_VERSION);$(SHIM_GLOBALIZATION_DEFINES);WIN64;_DEBUG;%(PreprocessorDefinitions) @@ -145,7 +143,6 @@ - /D /NODEFAULTLIB:LIBCD" " %(AdditionalOptions) true $(MONO_DIR);$(MONO_INCLUDE_DIR);$(LIBGC_CPPFLAGS_INCLUDE);$(GLIB_CFLAGS_INCLUDE);$(MONO_LLVM_DEFAULT_INCLUDE_DIR);$(SHIM_GLOBALIZATION_INCLUDE_DIR);%(AdditionalIncludeDirectories) WIN32;WIN32_LEAN_AND_MEAN;$(GC_DEFINES);MONO_DLL_EXPORT;LLVM_API_VERSION=$(MONO_LLVM_DEFAULT_API_VERSION);$(SHIM_GLOBALIZATION_DEFINES);NDEBUG;%(PreprocessorDefinitions) @@ -174,7 +171,6 @@ X64 - /D /NODEFAULTLIB:LIBCD" " %(AdditionalOptions) true $(MONO_DIR);$(MONO_INCLUDE_DIR);$(LIBGC_CPPFLAGS_INCLUDE);$(GLIB_CFLAGS_INCLUDE);$(MONO_LLVM_DEFAULT_INCLUDE_DIR);$(SHIM_GLOBALIZATION_INCLUDE_DIR);%(AdditionalIncludeDirectories) WIN32;WIN32_LEAN_AND_MEAN;$(GC_DEFINES);MONO_DLL_EXPORT;LLVM_API_VERSION=$(MONO_LLVM_DEFAULT_API_VERSION);$(SHIM_GLOBALIZATION_DEFINES);WIN64;NDEBUG;%(PreprocessorDefinitions) diff --git a/src/mono/msvc/libmonoruntime-common.targets b/src/mono/msvc/libmonoruntime-common.targets index 3e6f62e64d155..316efc8a4fc23 100644 --- a/src/mono/msvc/libmonoruntime-common.targets +++ b/src/mono/msvc/libmonoruntime-common.targets @@ -36,6 +36,7 @@ + diff --git a/src/mono/msvc/libmonoruntime-common.targets.filters b/src/mono/msvc/libmonoruntime-common.targets.filters index d614b6bcc49f7..fb4a4e3c84060 100644 --- a/src/mono/msvc/libmonoruntime-common.targets.filters +++ b/src/mono/msvc/libmonoruntime-common.targets.filters @@ -97,6 +97,9 @@ Source Files$(MonoRuntimeFilterSubFolder)\common + + Source Files$(MonoRuntimeFilterSubFolder)\common + Source Files$(MonoRuntimeFilterSubFolder)\common diff --git a/src/mono/msvc/libmonoruntime.targets b/src/mono/msvc/libmonoruntime.targets index 7b797ee766ddc..ef506c56f4555 100644 --- a/src/mono/msvc/libmonoruntime.targets +++ b/src/mono/msvc/libmonoruntime.targets @@ -2,6 +2,7 @@ + diff --git a/src/mono/msvc/libmonoruntime.targets.filters b/src/mono/msvc/libmonoruntime.targets.filters index d59ba5b295cee..6add73fc2df6d 100644 --- a/src/mono/msvc/libmonoruntime.targets.filters +++ b/src/mono/msvc/libmonoruntime.targets.filters @@ -2,6 +2,7 @@ + diff --git a/src/mono/msvc/mono.props b/src/mono/msvc/mono.props index 502c5398527d2..dbfed0b19f4a2 100644 --- a/src/mono/msvc/mono.props +++ b/src/mono/msvc/mono.props @@ -28,8 +28,11 @@ false + false true - false + + false + true .. @@ -124,6 +127,9 @@ $(MONO_ENABLE_NETCORE) + + $(MONO_ENABLE_PERFTRACING) + HAVE_CONFIG_H diff --git a/src/mono/msvc/mono.winconfig.targets b/src/mono/msvc/mono.winconfig.targets index d8e39ca7ad2dc..62e92339ee266 100644 --- a/src/mono/msvc/mono.winconfig.targets +++ b/src/mono/msvc/mono.winconfig.targets @@ -124,7 +124,8 @@ "ENABLE_LLVM_RUNTIME", "ENABLE_HYBRID_SUSPEND", "ENABLE_COOP_SUSPEND", - "ENABLE_NETCORE" }; + "ENABLE_NETCORE", + "ENABLE_PERFTRACING" }; var enableFeatures = GetConfigFeatures(path, ".*#define.*ENABLE_.*1"); if (enableDefines != null)