Skip to content

Commit

Permalink
Initial work of porting native diagnostic eventpipe library to C. (#3…
Browse files Browse the repository at this point in the history
…4600)

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.
  • Loading branch information
lateralusX authored May 26, 2020
1 parent 2d88a3d commit c5b55f0
Show file tree
Hide file tree
Showing 93 changed files with 13,904 additions and 121 deletions.
3 changes: 3 additions & 0 deletions src/mono/cmake/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
10 changes: 10 additions & 0 deletions src/mono/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/mono/m4/mono-output.m4
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono.proj
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,7 @@
<ItemGroup>
<_MonoBuildParams Include="/p:MONO_BUILD_DIR_PREFIX=&quot;&quot;$(MonoObjDir)&quot;&quot;" />
<_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" />
Expand Down
17 changes: 13 additions & 4 deletions src/mono/mono/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
71 changes: 71 additions & 0 deletions src/mono/mono/eventpipe/Makefile.am
Original file line number Diff line number Diff line change
@@ -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@
Loading

0 comments on commit c5b55f0

Please sign in to comment.