| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | |
| 5 | #ifndef __GCEVENT_SERIALIZERS_H__ |
| 6 | #define __GCEVENT_SERIALIZERS_H__ |
| 7 | |
| 8 | /* |
| 9 | * gcevent_serializers.h - Serialization traits and plumbing for |
| 10 | * serializing dynamic events. |
| 11 | * |
| 12 | * Dynamic events are events that can be fired by the GC without prior |
| 13 | * knowledge of the EE. In order to accomplish this, the GC sends raw |
| 14 | * bytes to the EE using the `IGCToCLR::FireDynamicEvent` callback, which |
| 15 | * the EE will then fire as its own event. |
| 16 | * |
| 17 | * In order to keep the friction of adding new dynamic events low, this |
| 18 | * file defines a simple ETW-style binary serialization format that |
| 19 | * is efficient and easy to both serialize and deserialize. |
| 20 | * |
| 21 | * ## Serializing Types |
| 22 | * |
| 23 | * This file makes use of `EventSerializationTraits` to serialize |
| 24 | * types. A type can opt-in to serialization using the mechanisms |
| 25 | * in this file by specializing the `EventSerializationTraits` template, |
| 26 | * providing implementations of `Serialize` and `SerializedSize`. |
| 27 | * |
| 28 | * If you attempt to serialize a type that does not implement this trait, |
| 29 | * you will receive an error message like this: |
| 30 | * |
| 31 | * bool gc_event::EventSerializationTraits<Head>::Serialize(const T&,uint8_t **)': attempting to reference a deleted function |
| 32 | * with |
| 33 | * [ |
| 34 | * Head=<your type you tried to serialize>, |
| 35 | * T=<your type you tried to serialize> |
| 36 | * ] |
| 37 | * |
| 38 | * If you get this message, you will need to specialize `EventSerializationTraits` |
| 39 | * for the type you want to serialize. |
| 40 | */ |
| 41 | |
| 42 | #ifdef _MSC_VER |
| 43 | #define ByteSwap32 _byteswap_ulong |
| 44 | #define ByteSwap64 _byteswap_uint64 |
| 45 | #else |
| 46 | #define ByteSwap32 __bulitin_bswap32 |
| 47 | #define ByteSwap64 __builtin_bswap64 |
| 48 | #endif // MSC_VER |
| 49 | |
| 50 | namespace gc_event |
| 51 | { |
| 52 | |
| 53 | /* |
| 54 | * `EventSerializatonTraits` is a trait implemented by types that |
| 55 | * can be serialized to the payload of a dynamic event. |
| 56 | */ |
| 57 | template<class T> |
| 58 | struct EventSerializationTraits |
| 59 | { |
| 60 | /* |
| 61 | * Serializes the value `value` to the buffer `buffer`, incrementing |
| 62 | * the buffer double-pointer to point to the next byte to be written. |
| 63 | * |
| 64 | * It is the responsibility of the caller to ensure that the buffer is |
| 65 | * large enough to accomodate the serialized form of T. |
| 66 | */ |
| 67 | static void Serialize(const T& value, uint8_t** buffer) = delete; |
| 68 | |
| 69 | /* |
| 70 | * Returns the size of the value `value` if it were to be serialized. |
| 71 | */ |
| 72 | static size_t SerializedSize(const T& value) = delete; |
| 73 | }; |
| 74 | |
| 75 | /* |
| 76 | * EventSerializationTraits implementation for uint32_t. Other integral types |
| 77 | * can follow this pattern. |
| 78 | * |
| 79 | * The convention here is that integral types are always serialized as |
| 80 | * little-endian. |
| 81 | */ |
| 82 | template<> |
| 83 | struct EventSerializationTraits<uint32_t> |
| 84 | { |
| 85 | static void Serialize(const uint32_t& value, uint8_t** buffer) |
| 86 | { |
| 87 | #if defined(BIGENDIAN) |
| 88 | **((uint32_t**)buffer) = ByteSwap32(value); |
| 89 | #else |
| 90 | **((uint32_t**)buffer) = value; |
| 91 | #endif // BIGENDIAN |
| 92 | *buffer += sizeof(uint32_t); |
| 93 | } |
| 94 | |
| 95 | static size_t SerializedSize(const uint32_t& value) |
| 96 | { |
| 97 | return sizeof(uint32_t); |
| 98 | } |
| 99 | }; |
| 100 | |
| 101 | /* |
| 102 | * Helper routines for serializing lists of arguments. |
| 103 | */ |
| 104 | |
| 105 | /* |
| 106 | * Given a list of arguments , returns the total size of |
| 107 | * the buffer required to fully serialize the list of arguments. |
| 108 | */ |
| 109 | template<class Head> |
| 110 | size_t SerializedSize(Head head) |
| 111 | { |
| 112 | return EventSerializationTraits<Head>::SerializedSize(head); |
| 113 | } |
| 114 | |
| 115 | template<class Head, class... Tail> |
| 116 | size_t SerializedSize(Head head, Tail... tail) |
| 117 | { |
| 118 | return EventSerializationTraits<Head>::SerializedSize(head) + SerializedSize(tail...); |
| 119 | } |
| 120 | |
| 121 | /* |
| 122 | * Given a list of arguments and a list of actual parameters, serialize |
| 123 | * the arguments into the buffer that's given to us. |
| 124 | */ |
| 125 | template<class Head> |
| 126 | void Serialize(uint8_t** buf, Head head) |
| 127 | { |
| 128 | EventSerializationTraits<Head>::Serialize(head, buf); |
| 129 | } |
| 130 | |
| 131 | template<class Head, class... Tail> |
| 132 | void Serialize(uint8_t** buf, Head head, Tail... tail) |
| 133 | { |
| 134 | EventSerializationTraits<Head>::Serialize(head, buf); |
| 135 | Serialize(buf, tail...); |
| 136 | } |
| 137 | |
| 138 | } // namespace gc_event |
| 139 | |
| 140 | #endif // __GCEVENT_SERIALIZERS_H__ |
| 141 | |
| 142 | |