Files
sbox-public/engine/Launcher/SboxProfiler/FirefoxExport/FirefoxProfiler.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

1317 lines
31 KiB
C#

// Copyright (c) Alexandre Mutel. All rights reserved.
// Licensed under the BSD-Clause 2 license.
// See license.txt file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#nullable enable
// ReSharper disable InconsistentNaming
namespace Sandbox;
/// <summary>
/// The Firefox profiler JSON format.
/// </summary>
/// <remarks>
/// This file was manually converted from https://github.com/xoofx/firefox-profiler/blob/main/src/types/profile.js
/// </remarks>
public static partial class FirefoxProfiler
{
[JsonSourceGenerationOptions(
WriteIndented = false, // Set to true when debugging
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
//Converters = [
// //typeof(KebabCaseEnumConverter<MarkerDisplayLocation>),
// //typeof(KebabCaseEnumConverter<MarkerGraphType>),
// //typeof(MarkerPayloadConverter),
// //typeof(ProfileColorEnumConverter)
// ]
)
]
[JsonSerializable( typeof( Profile ) )]
[JsonSerializable( typeof( MarkerTableFormatType ) )]
//[JsonSerializable(typeof(MarkerDisplayLocation))]
//[JsonSerializable(typeof(MarkerGraphType))]
//[JsonSerializable(typeof(ProfileColor))]
public partial class JsonProfilerContext : JsonSerializerContext
{
}
public class StackTable
{
public StackTable()
{
Frame = new List<int>();
Category = new List<int?>();
Subcategory = new List<int?>();
Prefix = new List<int?>();
}
public List<int> Frame { get; set; }
public List<int?> Category { get; set; }
public List<int?> Subcategory { get; set; }
public List<int?> Prefix { get; set; }
public int Length { get; set; }
}
public abstract class SamplesLikeTable
{
public SamplesLikeTable()
{
Stack = new List<int?>();
}
public List<int?> Stack { get; set; }
public List<double>? Time { get; set; }
public List<double>? TimeDeltas { get; set; }
public List<int>? Weight { get; set; }
public string WeightType { get; set; } = string.Empty;
public int Length { get; set; }
}
public class SamplesTable : SamplesLikeTable
{
public SamplesTable()
{
}
public List<double?>? Responsiveness { get; set; }
public List<double?>? EventDelay { get; set; }
public List<int?>? ThreadCPUDelta { get; set; }
public List<int>? ThreadId { get; set; }
}
public class JsAllocationsTable : SamplesLikeTable
{
public JsAllocationsTable()
{
ClassName = new List<string>();
TypeName = new List<string>();
CoarseType = new List<string>();
InNursery = new List<bool>();
}
public List<string> ClassName { get; set; }
public List<string> TypeName { get; set; }
public List<string> CoarseType { get; set; }
public List<bool> InNursery { get; set; }
}
public class RawMarkerTable
{
public RawMarkerTable()
{
Data = new List<MarkerPayload?>();
Name = new List<int>();
StartTime = new List<double?>();
EndTime = new List<double?>();
Phase = new List<MarkerPhase>();
Category = new List<int>();
ThreadId = new List<int>();
}
public List<MarkerPayload?> Data { get; set; }
public List<int> Name { get; set; }
public List<double?> StartTime { get; set; }
public List<double?> EndTime { get; set; }
public List<MarkerPhase> Phase { get; set; }
public List<int> Category { get; set; }
public List<int> ThreadId { get; set; }
public int Length { get; set; }
}
public enum MarkerPhase
{
Instance = 0,
Interval = 1,
IntervalStart = 2,
IntervalEnd = 3,
}
public class FrameTable
{
public FrameTable()
{
Address = new List<int>();
InlineDepth = new List<int>();
Category = new List<int?>();
Subcategory = new List<int?>();
Func = new List<int>();
NativeSymbol = new List<int?>();
InnerWindowID = new List<int?>();
Implementation = new List<int?>();
Line = new List<int?>();
Column = new List<int?>();
}
public List<int> Address { get; set; }
public List<int> InlineDepth { get; set; }
public List<int?> Category { get; set; }
public List<int?> Subcategory { get; set; }
public List<int> Func { get; set; }
public List<int?> NativeSymbol { get; set; }
public List<int?> InnerWindowID { get; set; }
public List<int?> Implementation { get; set; }
public List<int?> Line { get; set; }
public List<int?> Column { get; set; }
public int Length { get; set; }
}
public class FuncTable
{
public FuncTable()
{
Name = new List<int>();
IsJS = new List<bool>();
RelevantForJS = new List<bool>();
Resource = new List<int>();
FileName = new List<int?>();
LineNumber = new List<int?>();
ColumnNumber = new List<int?>();
}
public List<int> Name { get; set; }
public List<bool> IsJS { get; set; }
public List<bool> RelevantForJS { get; set; }
public List<int> Resource { get; set; }
public List<int?> FileName { get; set; }
public List<int?> LineNumber { get; set; }
public List<int?> ColumnNumber { get; set; }
public int Length { get; set; }
}
public class NativeSymbolTable
{
public NativeSymbolTable()
{
LibIndex = new List<int>();
Address = new List<int>();
Name = new List<int>();
FunctionSize = new List<int?>();
}
public List<int> LibIndex { get; set; }
public List<int> Address { get; set; }
public List<int> Name { get; set; }
public List<int?> FunctionSize { get; set; }
public int Length { get; set; }
}
public class ResourceTable
{
public ResourceTable()
{
Lib = new List<int?>();
Name = new List<int>();
Host = new List<int?>();
Type = new List<int>();
}
public List<int?> Lib { get; set; }
public List<int> Name { get; set; }
public List<int?> Host { get; set; }
public List<int> Type { get; set; }
public int Length { get; set; }
}
public class Lib
{
public Lib()
{
Arch = string.Empty;
Name = string.Empty;
Path = string.Empty;
DebugName = string.Empty;
DebugPath = string.Empty;
BreakpadId = string.Empty;
}
public ulong? AddressStart { get; set; }
public ulong? AddressEnd { get; set; }
public ulong? AddressOffset { get; set; }
public string Arch { get; set; }
public string Name { get; set; }
public string Path { get; set; }
public string DebugName { get; set; }
public string DebugPath { get; set; }
public string BreakpadId { get; set; }
public string? CodeId { get; set; }
}
public class Category
{
public Category()
{
Name = string.Empty;
Color = ProfileColor.Grey;
Subcategories = new List<string>();
}
public string Name { get; set; }
public ProfileColor Color { get; set; }
public List<string> Subcategories { get; set; }
}
public class Page
{
public Page()
{
Url = string.Empty;
}
public int TabID { get; set; }
public int InnerWindowID { get; set; }
public string Url { get; set; }
public int EmbedderInnerWindowID { get; set; }
public bool? IsPrivateBrowsing { get; set; }
public string? Favicon { get; set; }
}
public class PausedRange
{
public double? StartTime { get; set; }
public double? EndTime { get; set; }
public string Reason { get; set; } = string.Empty;
}
public class ProfilerConfiguration
{
public ProfilerConfiguration()
{
Threads = new List<string>();
Features = new List<string>();
}
public List<string> Threads { get; set; }
public List<string> Features { get; set; }
public int Capacity { get; set; }
public int? Duration { get; set; }
public int? ActiveTabID { get; set; }
}
public class VisualMetrics
{
public VisualMetrics()
{
VisualProgress = new List<ProgressGraphData>();
ContentfulSpeedIndexProgress = new List<ProgressGraphData>();
PerceptualSpeedIndexProgress = new List<ProgressGraphData>();
}
[JsonPropertyName( "FirstVisualChange" )]
public int FirstVisualChange { get; set; }
[JsonPropertyName( "LastVisualChange" )]
public int LastVisualChange { get; set; }
[JsonPropertyName( "SpeedIndex" )]
public int SpeedIndex { get; set; }
[JsonPropertyName( "VisualProgress" )]
public List<ProgressGraphData> VisualProgress { get; set; }
[JsonPropertyName( "ContentfulSpeedIndex" )]
public int? ContentfulSpeedIndex { get; set; }
[JsonPropertyName( "ContentfulSpeedIndexProgress" )]
public List<ProgressGraphData> ContentfulSpeedIndexProgress { get; set; }
[JsonPropertyName( "PerceptualSpeedIndex" )]
public int? PerceptualSpeedIndex { get; set; }
[JsonPropertyName( "PerceptualSpeedIndexProgress" )]
public List<ProgressGraphData> PerceptualSpeedIndexProgress { get; set; }
[JsonPropertyName( "VisualReadiness" )]
public int VisualReadiness { get; set; }
[JsonPropertyName( "VisualComplete85" )]
public int VisualComplete85 { get; set; }
[JsonPropertyName( "VisualComplete95" )]
public int VisualComplete95 { get; set; }
[JsonPropertyName( "VisualComplete99" )]
public int VisualComplete99 { get; set; }
}
public class ProgressGraphData
{
public int Percent { get; set; }
public double? Timestamp { get; set; }
}
public class ProfilerOverheadStats
{
public int MaxCleaning { get; set; }
public int MaxCounter { get; set; }
public int MaxInterval { get; set; }
public int MaxLockings { get; set; }
public int MaxOverhead { get; set; }
public int MaxThread { get; set; }
public int MeanCleaning { get; set; }
public int MeanCounter { get; set; }
public int MeanInterval { get; set; }
public int MeanLockings { get; set; }
public int MeanOverhead { get; set; }
public int MeanThread { get; set; }
public int MinCleaning { get; set; }
public int MinCounter { get; set; }
public int MinInterval { get; set; }
public int MinLockings { get; set; }
public int MinOverhead { get; set; }
public int MinThread { get; set; }
public int OverheadDurations { get; set; }
public int OverheadPercentage { get; set; }
public int ProfiledDuration { get; set; }
public int SamplingCount { get; set; }
}
public class ProfilerOverheadSamplesTable
{
public ProfilerOverheadSamplesTable()
{
Counters = new List<int>();
ExpiredMarkerCleaning = new List<int>();
Locking = new List<int>();
Threads = new List<int>();
Time = new List<int>();
}
public List<int> Counters { get; set; }
public List<int> ExpiredMarkerCleaning { get; set; }
public List<int> Locking { get; set; }
public List<int> Threads { get; set; }
public List<int> Time { get; set; }
public int Length { get; set; }
}
public class ProfilerOverhead
{
public ProfilerOverhead()
{
Samples = new ProfilerOverheadSamplesTable();
Pid = string.Empty;
}
public ProfilerOverheadSamplesTable Samples { get; set; }
public ProfilerOverheadStats? Statistics { get; set; }
public string Pid { get; set; }
public int MainThreadIndex { get; set; }
}
public class Thread
{
public Thread()
{
PausedRanges = new List<PausedRange>();
Name = string.Empty;
Samples = new SamplesTable();
Markers = new RawMarkerTable();
StackTable = new StackTable();
FrameTable = new FrameTable();
StringArray = new List<string>();
FuncTable = new FuncTable();
ResourceTable = new ResourceTable();
NativeSymbols = new NativeSymbolTable();
Pid = string.Empty;
Tid = string.Empty;
}
public string ProcessType { get; set; } = string.Empty;
public double ProcessStartupTime { get; set; }
public double? ProcessShutdownTime { get; set; }
public double RegisterTime { get; set; }
public double? UnregisterTime { get; set; }
public List<PausedRange> PausedRanges { get; set; }
public bool? ShowMarkersInTimeline { get; set; }
public string Name { get; set; }
public bool IsMainThread { get; set; }
[JsonPropertyName( "eTLD+1" )]
public string? ETLDPlus1 { get; set; }
public string? ProcessName { get; set; }
public bool? IsJsTracer { get; set; }
public string Pid { get; set; }
public string Tid { get; set; }
public SamplesTable Samples { get; set; }
//[JsonPropertyName("jsAllocations")]
//public JsAllocationsTable? JsAllocations { get; set; }
//[JsonPropertyName("nativeAllocations")]
//public NativeAllocationsTable? NativeAllocations { get; set; }
public RawMarkerTable Markers { get; set; }
public StackTable StackTable { get; set; }
public FrameTable FrameTable { get; set; }
public List<string> StringArray { get; set; }
public FuncTable FuncTable { get; set; }
public ResourceTable ResourceTable { get; set; }
public NativeSymbolTable NativeSymbols { get; set; }
public JsTracerTable? JsTracer { get; set; }
public bool? IsPrivateBrowsing { get; set; }
public int? UserContextId { get; set; }
}
public abstract class NativeAllocationsTable : SamplesLikeTable
{
}
public class BalancedNativeAllocationsTable : NativeAllocationsTable
{
public BalancedNativeAllocationsTable()
{
MemoryAddress = new();
ThreadId = new();
}
public List<int> MemoryAddress { get; set; }
public List<int> ThreadId { get; set; }
}
public class UnbalancedNativeAllocationsTable : NativeAllocationsTable
{
}
public class ProfileMeta
{
public ProfileMeta()
{
MarkerSchema = new List<MarkerSchema>();
Interval = 0;
StartTime = 0;
Product = string.Empty;
}
public double Interval { get; set; }
public double StartTime { get; set; }
public double? EndTime { get; set; }
public double? ProfilingStartTime { get; set; }
public double? ProfilingEndTime { get; set; }
public int ProcessType { get; set; }
public ExtensionTable? Extensions { get; set; }
public List<Category>? Categories { get; set; }
public string Product { get; set; }
public int Stackwalk { get; set; }
public bool? Debug { get; set; }
public int Version { get; set; }
public int PreprocessedProfileVersion { get; set; }
public string? Abi { get; set; }
public string? Misc { get; set; }
public string? Oscpu { get; set; }
public int? MainMemory { get; set; }
public string? Platform { get; set; }
public string? Toolkit { get; set; }
public string? AppBuildID { get; set; }
public string? Arguments { get; set; }
public string? SourceURL { get; set; }
public int? PhysicalCPUs { get; set; }
public int? LogicalCPUs { get; set; }
public string? CPUName { get; set; }
public bool? Symbolicated { get; set; }
public bool? SymbolicationNotSupported { get; set; }
public string? UpdateChannel { get; set; }
public VisualMetrics? VisualMetrics { get; set; }
public ProfilerConfiguration? Configuration { get; set; }
public List<MarkerSchema> MarkerSchema { get; set; }
public SampleUnits? SampleUnits { get; set; }
public string? Device { get; set; }
public string? ImportedFrom { get; set; }
public bool? UsesOnlyOneStackType { get; set; }
public bool? DoesNotUseFrameImplementation { get; set; }
public bool? SourceCodeIsNotOnSearchfox { get; set; }
public List<ExtraProfileInfoSection>? Extra { get; set; }
public List<int>? InitialVisibleThreads { get; set; }
public List<int>? InitialSelectedThreads { get; set; }
public bool? KeepProfileThreadOrder { get; set; }
public double? GramsOfCO2ePerKWh { get; set; }
}
public class Profile
{
public Profile()
{
Meta = new ProfileMeta();
Libs = new List<Lib>();
Threads = new List<Thread>();
}
public ProfileMeta Meta { get; set; }
public List<Lib> Libs { get; set; }
public List<Page>? Pages { get; set; }
public List<Counter>? Counters { get; set; }
public List<ProfilerOverhead>? ProfilerOverhead { get; set; }
public List<Thread> Threads { get; set; }
}
public class ExtensionTable
{
public ExtensionTable()
{
BaseURL = new List<string>();
Id = new List<string>();
Name = new List<string>();
}
public List<string> BaseURL { get; set; }
public List<string> Id { get; set; }
public List<string> Name { get; set; }
public int Length { get; set; }
}
public class CounterSamplesTable
{
public CounterSamplesTable()
{
Count = new List<long>();
}
public List<double>? Time { get; set; }
public List<double>? TimeDeltas { get; set; }
public List<int>? Number { get; set; }
public List<long> Count { get; set; }
public int Length { get; set; }
}
public class Counter
{
public Counter()
{
Name = string.Empty;
Category = string.Empty;
Description = string.Empty;
Pid = string.Empty;
Samples = new CounterSamplesTable();
}
public string Name { get; set; }
public string Category { get; set; } // 'Memory', 'power', 'Bandwidth'
public string Description { get; set; }
public ProfileColor? Color { get; set; }
public string Pid { get; set; }
public int MainThreadIndex { get; set; }
public CounterSamplesTable Samples { get; set; }
}
public class JsTracerTable
{
public JsTracerTable()
{
Events = new List<int>();
Timestamps = new List<int>();
Durations = new List<int?>();
Line = new List<int?>();
Column = new List<int?>();
}
public List<int> Events { get; set; }
public List<int> Timestamps { get; set; }
public List<int?> Durations { get; set; }
public List<int?> Line { get; set; }
public List<int?> Column { get; set; }
public int Length { get; set; }
}
public class SampleUnits
{
public SampleUnits()
{
Time = string.Empty;
EventDelay = string.Empty;
ThreadCPUDelta = string.Empty;
}
public string Time { get; set; }
public string EventDelay { get; set; }
public string ThreadCPUDelta { get; set; }
}
public class ExtraProfileInfoSection
{
public ExtraProfileInfoSection()
{
Label = string.Empty;
Entries = new List<ProfileInfoEntry>();
}
public string Label { get; set; }
public List<ProfileInfoEntry> Entries { get; set; }
}
public class ProfileInfoEntry
{
public ProfileInfoEntry()
{
Label = string.Empty;
Format = string.Empty;
Value = string.Empty;
}
public string Label { get; set; }
public string Format { get; set; }
public string Value { get; set; }
}
public abstract class MarkerFormatType
{
protected MarkerFormatType( string type )
{
Type = type;
}
[JsonIgnore]
public string Type { get; set; }
// ----------------------------------------------------
// String types.
/// <summary>
/// Show the URL, and handle PII sanitization
/// </summary>
public static readonly MarkerSimpleFormatType Url = new( "url" );
/// <summary>
/// Show the file path, and handle PII sanitization.
/// </summary>
public static readonly MarkerSimpleFormatType FilePath = new( "file-path" );
/// <summary>
/// Show regular string, and handle PII sanitization.
/// </summary>
public static readonly MarkerSimpleFormatType SanitizedString = new( "sanitized-string" );
/// <summary>
/// Important: do not put URL or file path information here, as it will not be
/// sanitized. Please be careful with including other types of PII here as well.
/// e.g. "Label: Some String"
/// </summary>
public static readonly MarkerSimpleFormatType String = new( "string" );
/// <summary>
/// An index into a (currently) thread-local string table, aka UniqueStringArray.
/// This is effectively an integer, so wherever we need to display this value, we
/// must first perform a lookup into the appropriate string table.
/// </summary>
public static readonly MarkerSimpleFormatType UniqueString = new( "unique-string" );
// ----------------------------------------------------
// Flow types.
/// <summary>
/// A flow ID is a u64 identifier that's unique across processes. In the current
/// implementation, we represent them as hex strings, as string table indexes.
/// </summary>
public static readonly MarkerSimpleFormatType FlowId = new( "flow-id" );
/// <summary>
/// A terminating flow ID is a flow ID that, when used in a marker with timestamp T,
/// makes it so that if the same flow ID is used in a marker whose timestamp is
/// after T, that flow ID is considered to refer to a different flow.
/// </summary>
public static readonly MarkerSimpleFormatType TerminatingFlowId = new( "terminating-flow-id" );
// ----------------------------------------------------
// Numeric types
// Note: All time and durations are stored as milliseconds.
/// <summary>
/// For time data that represents a duration of time.
/// e.g. "Label: 5s, 5ms, 5μs"
/// </summary>
public static readonly MarkerSimpleFormatType Duration = new( "duration" );
/// <summary>
/// Data that happened at a specific time, relative to the start of
/// the profile. e.g. "Label: 15.5s, 20.5ms, 30.5μs"
/// </summary>
public static readonly MarkerSimpleFormatType Time = new( "time" );
// The following are alternatives to display a time only in a specific unit of time.
/// <summary> "Label: 5s" </summary>
public static readonly MarkerSimpleFormatType Seconds = new( "seconds" );
/// <summary> "Label: 5ms" </summary>
public static readonly MarkerSimpleFormatType Milliseconds = new( "milliseconds" );
/// <summary> "Label: 5μs" </summary>
public static readonly MarkerSimpleFormatType Microseconds = new( "microseconds" );
/// <summary> "Label: 5ns" </summary>
public static readonly MarkerSimpleFormatType Nanoseconds = new( "nanoseconds" );
/// <summary>
/// e.g. "Label: 5.55mb, 5 bytes, 312.5kb"
/// </summary>
public static readonly MarkerSimpleFormatType Bytes = new( "bytes" );
/// <summary>
/// This should be a value between 0 and 1.
/// "Label: 50%"
/// </summary>
public static readonly MarkerSimpleFormatType Percentage = new( "percentage" );
/// <summary>
/// The integer should be used for generic representations of numbers. Do not
/// use it for time information.
/// "Label: 52, 5,323, 1,234,567"
/// </summary>
public static readonly MarkerSimpleFormatType Integer = new( "integer" );
/// <summary>
/// The decimal should be used for generic representations of numbers. Do not
/// use it for time information.
/// "Label: 52.23, 0.0054, 123,456.78"
/// </summary>
public static readonly MarkerSimpleFormatType Decimal = new( "decimal" );
public static readonly MarkerSimpleFormatType Pid = new( "pid" );
public static readonly MarkerSimpleFormatType Tid = new( "tid" );
public static readonly MarkerSimpleFormatType List = new( "list" );
/// <summary>
/// Represents a table format, with columns of type `TableColumnFormat[]`.
/// </summary>
public static MarkerTableFormatType Table( IEnumerable<TableColumnFormat> columns )
{
var table = new MarkerTableFormatType();
table.Columns.AddRange( columns );
return table;
}
}
public class MarkerSimpleFormatType( string type ) : MarkerFormatType( type );
private sealed class MarkerFormatTypeConverter : JsonConverter<MarkerFormatType>
{
public override MarkerFormatType Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options )
{
if ( reader.TokenType == JsonTokenType.String )
{
string propertyName = reader.GetString() ?? string.Empty;
MarkerFormatType? markerFormatType = propertyName switch
{
"url" => MarkerFormatType.Url,
"file-path" => MarkerFormatType.FilePath,
"sanitized-string" => MarkerFormatType.SanitizedString,
"string" => MarkerFormatType.String,
"unique-string" => MarkerFormatType.UniqueString,
"flow-id" => MarkerFormatType.FlowId,
"terminating-flow-id" => MarkerFormatType.TerminatingFlowId,
"duration" => MarkerFormatType.Duration,
"time" => MarkerFormatType.Time,
"seconds" => MarkerFormatType.Seconds,
"milliseconds" => MarkerFormatType.Milliseconds,
"microseconds" => MarkerFormatType.Microseconds,
"nanoseconds" => MarkerFormatType.Nanoseconds,
"bytes" => MarkerFormatType.Bytes,
"percentage" => MarkerFormatType.Percentage,
"integer" => MarkerFormatType.Integer,
"decimal" => MarkerFormatType.Decimal,
"pid" => MarkerFormatType.Pid,
"tid" => MarkerFormatType.Tid,
"list" => MarkerFormatType.List,
_ => new MarkerSimpleFormatType( propertyName )
};
return markerFormatType;
}
return JsonSerializer.Deserialize( ref reader, JsonProfilerContext.Default.MarkerTableFormatType )!;
}
public override void Write( Utf8JsonWriter writer, MarkerFormatType value, JsonSerializerOptions options )
{
if ( value is MarkerSimpleFormatType simpleFormatType )
{
writer.WriteStringValue( simpleFormatType.Type );
}
else
{
JsonSerializer.Serialize( writer, (object?)value, (JsonTypeInfo)JsonProfilerContext.Default.MarkerTableFormatType );
}
}
}
public class MarkerTableFormatType : MarkerFormatType
{
public MarkerTableFormatType() : base( "table" )
{
Columns = new();
}
public List<TableColumnFormat> Columns { get; set; }
}
public class TableColumnFormat
{
public TableColumnFormat()
{
Label = string.Empty;
}
[JsonConverter( typeof( MarkerFormatTypeConverter ) )]
public MarkerFormatType? Type { get; set; }
public string Label { get; set; }
}
[JsonConverter( typeof( KebabCaseEnumConverter<MarkerDisplayLocation> ) )]
public enum MarkerDisplayLocation
{
MarkerChart,
MarkerTable,
TimelineOverview,
TimelineMemory,
TimelineIpc,
TimelineFileio,
StackChart
}
private class KebabCaseEnumConverter<TEnum>() :
JsonStringEnumConverter<TEnum>( JsonNamingPolicy.KebabCaseLower ) where TEnum : struct, Enum;
[JsonConverter( typeof( KebabCaseEnumConverter<MarkerGraphType> ) )]
public enum MarkerGraphType
{
Bar,
Line,
LineFilled
}
public class MarkerGraph
{
public MarkerGraph()
{
Key = string.Empty;
}
public string Key { get; set; }
public MarkerGraphType Type { get; set; }
public ProfileColor? Color { get; set; }
}
[JsonConverter( typeof( ProfileColorEnumConverter ) )]
public enum ProfileColor
{
Blue,
Green,
Grey,
Ink,
Magenta,
Orange,
Purple,
Red,
Teal,
Yellow,
}
private sealed class ProfileColorEnumConverter() : JsonStringEnumConverter<ProfileColor>( JsonNamingPolicy.CamelCase, false );
public class MarkerSchema
{
public MarkerSchema()
{
Name = string.Empty;
Display = new List<MarkerDisplayLocation>();
Data = new List<MarkerDataItem>();
}
public string Name { get; set; }
public string? TooltipLabel { get; set; }
public string? TableLabel { get; set; }
public string? ChartLabel { get; set; }
public List<MarkerDisplayLocation> Display { get; set; }
public List<MarkerDataItem> Data { get; set; }
public List<MarkerGraph>? Graphs { get; set; }
public bool? IsStackBased { get; set; }
}
public class MarkerDataItem
{
public string? Key { get; set; }
public string? Label { get; set; }
[JsonConverter( typeof( MarkerFormatTypeConverter ) )]
public MarkerFormatType? Format { get; set; }
public bool? Searchable { get; set; }
public string? Value { get; set; }
}
[JsonConverter( typeof( MarkerPayloadConverter ) )]
public class MarkerPayload
{
public string? Type { get; set; }
public Dictionary<string, object?>? ExtensionData { get; set; }
/// <summary>
/// Writes the JSON representation of the current object.
/// </summary>
/// <param name="writer">The JSON writer.</param>
/// <param name="payload">The marker payload.</param>
/// <param name="options">The JSON serializer options.</param>
protected internal virtual void WriteJson( Utf8JsonWriter writer, MarkerPayload payload, JsonSerializerOptions options )
{
}
}
private class MarkerPayloadConverter : JsonConverter<MarkerPayload>
{
public override MarkerPayload? Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options )
{
if ( reader.TokenType != JsonTokenType.StartObject )
{
throw new JsonException( "Expected object start { for MarkerPayload" );
}
var payload = new MarkerPayload();
while ( reader.Read() )
{
if ( reader.TokenType == JsonTokenType.EndObject )
{
return payload;
}
if ( reader.TokenType != JsonTokenType.PropertyName )
{
throw new JsonException( "Expected property name for MarkerPayload" );
}
string propertyName = reader.GetString() ?? string.Empty;
if ( propertyName == "type" )
{
reader.Read();
payload.Type = reader.GetString();
}
else
{
if ( payload.ExtensionData == null )
{
payload.ExtensionData = new Dictionary<string, object?>();
}
reader.Read();
switch ( reader.TokenType )
{
case JsonTokenType.String:
payload.ExtensionData[propertyName] = reader.GetString();
break;
case JsonTokenType.Number:
payload.ExtensionData[propertyName] = reader.GetDouble();
break;
case JsonTokenType.True:
payload.ExtensionData[propertyName] = true;
break;
case JsonTokenType.False:
payload.ExtensionData[propertyName] = false;
break;
case JsonTokenType.Null:
payload.ExtensionData[propertyName] = null;
break;
default:
#pragma warning disable IL3050
#pragma warning disable IL2026
payload.ExtensionData[propertyName] = JsonSerializer.Deserialize<object>( ref reader );
#pragma warning restore IL2026
#pragma warning restore IL3050
break;
}
}
}
return payload;
}
public override void Write( Utf8JsonWriter writer, MarkerPayload payload, JsonSerializerOptions options )
{
writer.WriteStartObject();
if ( payload.Type != null )
{
writer.WriteString( "type", payload.Type );
}
if ( payload.ExtensionData != null )
{
foreach ( var (key, value) in payload.ExtensionData )
{
writer.WritePropertyName( key );
switch ( value )
{
case string stringValue:
writer.WriteStringValue( stringValue );
break;
case long longValue:
writer.WriteNumberValue( longValue );
break;
case int intValue:
writer.WriteNumberValue( intValue );
break;
case short intValue:
writer.WriteNumberValue( intValue );
break;
case sbyte intValue:
writer.WriteNumberValue( intValue );
break;
case ulong longValue:
writer.WriteNumberValue( longValue );
break;
case uint intValue:
writer.WriteNumberValue( intValue );
break;
case ushort intValue:
writer.WriteNumberValue( intValue );
break;
case byte intValue:
writer.WriteNumberValue( intValue );
break;
case float floatValue:
writer.WriteNumberValue( floatValue );
break;
case double doubleValue:
writer.WriteNumberValue( doubleValue );
break;
case bool boolValue:
writer.WriteBooleanValue( boolValue );
break;
case null:
writer.WriteNullValue();
break;
default:
#pragma warning disable IL3050
#pragma warning disable IL2026
JsonSerializer.Serialize( writer, value );
#pragma warning restore IL2026
#pragma warning restore IL3050
break;
}
}
}
payload.WriteJson( writer, payload, options );
writer.WriteEndObject();
}
}
}