Skip to content

Commit

Permalink
Initial DynamicEvent support
Browse files Browse the repository at this point in the history
  • Loading branch information
cshung committed Jun 19, 2024
1 parent 6cf8b2a commit 4c3e7d6
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// TODO, AndrewAu, remove this condition when new TraceEvent is available through Nuget.
#if CUSTOM_TRACE_EVENT

using Microsoft.Diagnostics.Tracing.Analysis.GC;
using Microsoft.Diagnostics.Tracing.Parsers.GCDynamic;
using System.Diagnostics;
using System.Dynamic;

namespace GC.Analysis.API.DynamicEvents
{

public static class TraceGCExtensions
{
public static dynamic DynamicEvents(this TraceGC traceGC)
{
return new DynamicIndex(traceGC.DynamicEvents);
}
}

// TODO, AndrewAu, this could be compiled for better performance, right now we are doing linear lookups
// We can do validation so that the input make sense (i.e. no unexpected types, no duplicate event/field), etc
//
public class DynamicEventSchema
{
internal static List<DynamicEventSchema> DynamicEventSchemas = new List<DynamicEventSchema>();

public string DynamicEventName { get; set; }

public List<KeyValuePair<string, Type>> Fields { get; set; }

public static void Set(List<DynamicEventSchema> dynamicEventSchemas)
{
DynamicEventSchemas = dynamicEventSchemas;
}
}

internal class DynamicIndex : DynamicObject
{
private List<DynamicEvent> index;

public DynamicIndex(List<DynamicEvent> index)
{
this.index = index;
}

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (DynamicEventSchema.DynamicEventSchemas.Any(s => string.Equals(s.DynamicEventName, binder.Name)))
{
string name = binder.Name;
// TODO, andrewau, handle the case where we have multiple dynamic event of the same name in a single GC.
//
// That should be an array of these dynamic events
// Somewhere the schema should tell us if this is expected or not so not to generate wrong stuff just because the data is corrupted.
//
result = new DynamicEventObject(index.Single(r => string.Equals(r.Name, binder.Name)), DynamicEventSchema.DynamicEventSchemas.Single(s => string.Equals(s.DynamicEventName, binder.Name)).Fields);
return true;
}
else
{
result = null;
return false;
}
}
}

internal class DynamicEventObject : DynamicObject
{
private DynamicEvent dynamicEvent;
private Dictionary<string, object> fieldValues;

public DynamicEventObject(DynamicEvent dynamicEvent, List<KeyValuePair<string, Type>> fields)
{
this.dynamicEvent = dynamicEvent;
this.fieldValues = new Dictionary<string, object>();
int offset = 0;
foreach (KeyValuePair<string, Type> field in fields)
{
object value = null;
// TODO, AndrewAu, all types that we envision this will be needed
if (field.Value == typeof(ushort))
{
value = BitConverter.ToUInt16(dynamicEvent.Payload, offset);
offset += 2;
}
else if (field.Value == typeof(ulong))
{
value = BitConverter.ToUInt64(dynamicEvent.Payload, offset);
offset += 8;
}
else
{
Debug.Fail("Unknown field type");
}
this.fieldValues.Add(field.Key, value);
}
}

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string name = binder.Name;
if (string.Equals(name, "TimeStamp"))
{
result = this.dynamicEvent.TimeStamp;
return true;
}
else
if (this.fieldValues.TryGetValue(name, out var fieldValue))
{
result = fieldValue;
return true;
}
else
{
result = null;
return false;
}
}

public override string ToString()
{
return "I am " + this.dynamicEvent.Name + " with these fields: \n" + string.Join("\n", this.fieldValues.Select(kvp => kvp.Key + "->" + kvp.Value));
}
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
},
"outputs": [],
"source": [
"dotnet build -c Release \"..\\..\\GC.Analysis.API\""
"dotnet build -c Release \"..\\..\\GC.Analysis.API\" /p:CustomTraceEvent=true /p:PerfViewPath=C:\\Dev\\PerfView"
]
},
{
Expand Down Expand Up @@ -213,6 +213,64 @@
"GCProcessData corerunToInvestigate = corerunData[0];\n",
"List<TraceGC> traceGCs = corerunToInvestigate.GCs;"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Dynamic Event"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"using GC.Analysis.API.DynamicEvents;\n",
"\n",
"DynamicEventSchema.Set(new List<DynamicEventSchema>\n",
"{\n",
" new DynamicEventSchema\n",
" {\n",
" DynamicEventName = \"ABC\",\n",
" Fields = new List<KeyValuePair<string, Type>>\n",
" {\n",
" new KeyValuePair<string, Type>(\"version\", typeof(ushort)),\n",
" new KeyValuePair<string, Type>(\"Number\", typeof(ulong)),\n",
" }\n",
" }\n",
"});"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"traceGCs[0].DynamicEvents().ABC.Number"
]
}
],
"metadata": {
Expand Down

0 comments on commit 4c3e7d6

Please sign in to comment.