summaryrefslogtreecommitdiff
path: root/src/gc
diff options
context:
space:
mode:
authorSean Gillespie <segilles@microsoft.com>2018-01-25 18:17:57 -0800
committerGitHub <noreply@github.com>2018-01-25 18:17:57 -0800
commit145cfe19f9e4f3e8a84b75a3048fe98fffb59a68 (patch)
tree3c993b736f5e07f0b4886453e06591a563d08cc6 /src/gc
parentb7398b0d827248e1eacce251a764feb8bd11d198 (diff)
downloadcoreclr-145cfe19f9e4f3e8a84b75a3048fe98fffb59a68.tar.gz
coreclr-145cfe19f9e4f3e8a84b75a3048fe98fffb59a68.tar.bz2
coreclr-145cfe19f9e4f3e8a84b75a3048fe98fffb59a68.zip
[Local GC] FEATURE_EVENT_TRACE 3/n: Defining and Firing Dynamic Events (#16000)
Diffstat (limited to 'src/gc')
-rw-r--r--src/gc/gcevent_serializers.h141
-rw-r--r--src/gc/gcevents.h2
-rw-r--r--src/gc/gceventstatus.h68
-rw-r--r--src/gc/gcinterface.ee.h10
-rw-r--r--src/gc/gcsvr.cpp1
-rw-r--r--src/gc/gcwks.cpp1
6 files changed, 211 insertions, 12 deletions
diff --git a/src/gc/gcevent_serializers.h b/src/gc/gcevent_serializers.h
new file mode 100644
index 0000000000..643ab82a15
--- /dev/null
+++ b/src/gc/gcevent_serializers.h
@@ -0,0 +1,141 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __GCEVENT_SERIALIZERS_H__
+#define __GCEVENT_SERIALIZERS_H__
+
+/*
+ * gcevent_serializers.h - Serialization traits and plumbing for
+ * serializing dynamic events.
+ *
+ * Dynamic events are events that can be fired by the GC without prior
+ * knowledge of the EE. In order to accomplish this, the GC sends raw
+ * bytes to the EE using the `IGCToCLR::FireDynamicEvent` callback, which
+ * the EE will then fire as its own event.
+ *
+ * In order to keep the friction of adding new dynamic events low, this
+ * file defines a simple ETW-style binary serialization format that
+ * is efficient and easy to both serialize and deserialize.
+ *
+ * ## Serializing Types
+ *
+ * This file makes use of `EventSerializationTraits` to serialize
+ * types. A type can opt-in to serialization using the mechanisms
+ * in this file by specializing the `EventSerializationTraits` template,
+ * providing implementations of `Serialize` and `SerializedSize`.
+ *
+ * If you attempt to serialize a type that does not implement this trait,
+ * you will receive an error message like this:
+ *
+ * bool gc_event::EventSerializationTraits<Head>::Serialize(const T&,uint8_t **)': attempting to reference a deleted function
+ * with
+ * [
+ * Head=<your type you tried to serialize>,
+ * T=<your type you tried to serialize>
+ * ]
+ *
+ * If you get this message, you will need to specialize `EventSerializationTraits`
+ * for the type you want to serialize.
+ */
+
+#ifdef _MSC_VER
+#define ByteSwap32 _byteswap_ulong
+#define ByteSwap64 _byteswap_uint64
+#else
+#define ByteSwap32 __bulitin_bswap32
+#define ByteSwap64 __builtin_bswap64
+#endif // MSC_VER
+
+namespace gc_event
+{
+
+/*
+ * `EventSerializatonTraits` is a trait implemented by types that
+ * can be serialized to the payload of a dynamic event.
+ */
+template<class T>
+struct EventSerializationTraits
+{
+ /*
+ * Serializes the value `value` to the buffer `buffer`, incrementing
+ * the buffer double-pointer to point to the next byte to be written.
+ *
+ * It is the responsibility of the caller to ensure that the buffer is
+ * large enough to accomodate the serialized form of T.
+ */
+ static void Serialize(const T& value, uint8_t** buffer) = delete;
+
+ /*
+ * Returns the size of the value `value` if it were to be serialized.
+ */
+ static size_t SerializedSize(const T& value) = delete;
+};
+
+/*
+ * EventSerializationTraits implementation for uint32_t. Other integral types
+ * can follow this pattern.
+ *
+ * The convention here is that integral types are always serialized as
+ * little-endian.
+ */
+template<>
+struct EventSerializationTraits<uint32_t>
+{
+ static void Serialize(const uint32_t& value, uint8_t** buffer)
+ {
+#if defined(BIGENDIAN)
+ **((uint32_t**)buffer) = ByteSwap32(value);
+#else
+ **((uint32_t**)buffer) = value;
+#endif // BIGENDIAN
+ *buffer += sizeof(uint32_t);
+ }
+
+ static size_t SerializedSize(const uint32_t& value)
+ {
+ return sizeof(uint32_t);
+ }
+};
+
+/*
+ * Helper routines for serializing lists of arguments.
+ */
+
+/*
+ * Given a list of arguments , returns the total size of
+ * the buffer required to fully serialize the list of arguments.
+ */
+template<class Head, class... Tail>
+size_t SerializedSize(Head head, Tail... tail)
+{
+ return EventSerializationTraits<Head>::SerializedSize(head) + SerializedSize(tail...);
+}
+
+template<class Head>
+size_t SerializedSize(Head head)
+{
+ return EventSerializationTraits<Head>::SerializedSize(head);
+}
+
+/*
+ * Given a list of arguments and a list of actual parameters, serialize
+ * the arguments into the buffer that's given to us.
+ */
+template<class Head, class... Tail>
+void Serialize(uint8_t** buf, Head head, Tail... tail)
+{
+ EventSerializationTraits<Head>::Serialize(head, buf);
+ Serialize(buf, tail...);
+}
+
+template<class Head>
+void Serialize(uint8_t** buf, Head head)
+{
+ EventSerializationTraits<Head>::Serialize(head, buf);
+}
+
+} // namespace gc_event
+
+#endif // __GCEVENT_SERIALIZERS_H__
+
diff --git a/src/gc/gcevents.h b/src/gc/gcevents.h
index cfc1571418..94087d7171 100644
--- a/src/gc/gcevents.h
+++ b/src/gc/gcevents.h
@@ -6,7 +6,7 @@
#endif // KNOWN_EVENT
#ifndef DYNAMIC_EVENT
- #define DYNAMIC_EVENT(name, provider, level, keyword, ...)
+ #define DYNAMIC_EVENT(name, level, keyword, ...)
#endif // DYNAMIC_EVENT
#undef KNOWN_EVENT
diff --git a/src/gc/gceventstatus.h b/src/gc/gceventstatus.h
index 1d992fcda3..95ed337475 100644
--- a/src/gc/gceventstatus.h
+++ b/src/gc/gceventstatus.h
@@ -27,6 +27,7 @@
#include "common.h"
#include "gcenv.h"
#include "gc.h"
+#include "gcevent_serializers.h"
// Uncomment this define to print out event state changes to standard error.
// #define TRACE_GC_EVENT_STATE 1
@@ -187,23 +188,70 @@ private:
GCEventStatus() = delete;
};
-class GCDynamicEvent
+/*
+ * FireDynamicEvent is a variadic function that fires a dynamic event with the
+ * given name and event payload. This function serializes the arguments into
+ * a binary payload that is then passed to IGCToCLREventSink::FireDynamicEvent.
+ */
+template<typename... EventArgument>
+void FireDynamicEvent(const char* name, EventArgument... arguments)
{
- /* TODO(segilles) - Not Yet Implemented */
+ size_t size = gc_event::SerializedSize(arguments...);
+ if (size > UINT32_MAX)
+ {
+ // ETW can't handle anything this big.
+ // we shouldn't be firing events that big anyway.
+ return;
+ }
+
+ uint8_t* buf = new (nothrow) uint8_t[size];
+ if (!buf)
+ {
+ // best effort - if we're OOM, don't bother with the event.
+ return;
+ }
+
+ memset(buf, 0, size);
+ uint8_t* cursor = buf;
+ gc_event::Serialize(&cursor, arguments...);
+ IGCToCLREventSink* sink = GCToEEInterface::EventSink();
+ assert(sink != nullptr);
+ sink->FireDynamicEvent(name, buf, static_cast<uint32_t>(size));
+ delete[] buf;
};
+/*
+ * In order to provide a consistent interface between known and dynamic events,
+ * two wrapper functions are generated for each known and dynamic event:
+ * GCEventEnabled##name() - Returns true if the event is enabled, false otherwise.
+ * GCEventFire##name(...) - Fires the event, with the event payload consisting of
+ * the arguments to the function.
+ *
+ * Because the schema of dynamic events comes from the DYNAMIC_EVENT xmacro, we use
+ * the arguments vector as the argument list to `FireDynamicEvent`, which will traverse
+ * the list of arguments and call `IGCToCLREventSink::FireDynamicEvent` with a serialized
+ * payload. Known events will delegate to IGCToCLREventSink::Fire##name.
+ */
#if FEATURE_EVENT_TRACE
-#define KNOWN_EVENT(name, _provider, _level, _keyword) \
- inline bool GCEventEnabled##name() { return GCEventStatus::IsEnabled(_provider, _level, _keyword); }
+#define KNOWN_EVENT(name, provider, level, keyword) \
+ inline bool GCEventEnabled##name() { return GCEventStatus::IsEnabled(provider, keyword, level); } \
+ template<typename... EventActualArgument> \
+ inline void GCEventFire##name(EventActualArgument... arguments) \
+ { \
+ IGCToCLREventSink* sink = GCToEEInterface::EventSink(); \
+ assert(sink != nullptr); \
+ sink->Fire##name(arguments...); \
+ }
+
+#define DYNAMIC_EVENT(name, level, keyword, ...) \
+ inline bool GCEventEnabled##name() { return GCEventStatus::IsEnabled(GCEventProvider_Default, keyword, level); } \
+ template<typename... EventActualArgument> \
+ inline void GCEventFire##name(EventActualArgument... arguments) { FireDynamicEvent<__VA_ARGS__>(#name, arguments...); }
+
#include "gcevents.h"
#define EVENT_ENABLED(name) GCEventEnabled##name()
-#define FIRE_EVENT(name, ...) \
- do { \
- IGCToCLREventSink* sink = GCToEEInterface::EventSink(); \
- assert(sink != nullptr); \
- sink->Fire##name(__VA_ARGS__); \
- } while(0)
+#define FIRE_EVENT(name, ...) GCEventFire##name(__VA_ARGS__)
#else
#define EVENT_ENABLED(name) false
#define FIRE_EVENT(name, ...) 0
diff --git a/src/gc/gcinterface.ee.h b/src/gc/gcinterface.ee.h
index f765362676..657e16ebb1 100644
--- a/src/gc/gcinterface.ee.h
+++ b/src/gc/gcinterface.ee.h
@@ -16,7 +16,15 @@
// to the EE. ([LOCALGC TODO dynamic event implementation])
class IGCToCLREventSink
{
- /* [LOCALGC TODO] This will be filled with events as they get ported */
+public:
+ // Fires a dynamic event with the given event name and payload. Dynamic
+ // events are not known to the EE and are fired as an unschematized event
+ // to the underlying eventing implementation.
+ virtual
+ void FireDynamicEvent(
+ const char* eventName,
+ void* payload,
+ uint32_t payloadSize) = 0;
};
// This interface provides the interface that the GC will use to speak to the rest
diff --git a/src/gc/gcsvr.cpp b/src/gc/gcsvr.cpp
index db67aa3eef..e85555c77f 100644
--- a/src/gc/gcsvr.cpp
+++ b/src/gc/gcsvr.cpp
@@ -17,6 +17,7 @@
#include "handletable.h"
#include "handletable.inl"
#include "gcenv.inl"
+#include "gceventstatus.h"
#define SERVER_GC 1
diff --git a/src/gc/gcwks.cpp b/src/gc/gcwks.cpp
index 335755608d..ad66dd6b34 100644
--- a/src/gc/gcwks.cpp
+++ b/src/gc/gcwks.cpp
@@ -15,6 +15,7 @@
#include "handletable.h"
#include "handletable.inl"
#include "gcenv.inl"
+#include "gceventstatus.h"
#ifdef SERVER_GC
#undef SERVER_GC