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 | |