Event types
editEach event type is a class that derives from ChromiumTracingEvent. Its Type property returns the phase of the event in the format, the ph field of the JSON, which tells the viewer how to show it.
The specification of the format describes each phase in detail. The examples below show the most common ones.
Durations
A complete event describes an operation in one event, once it is done: see Getting started. Begin and end events describe it in two events, so the start can be written before the operation ends:
using var stream = new MemoryStream();
await using (var writer = new ChromiumTracingWriter(stream))
{
var start = DateTimeOffset.UtcNow;
// The events of a thread must be ordered: an End event closes the last Begin event of the thread
await writer.WriteEventAsync(new ChromiumTracingDurationBeginEvent { Name = "Build", Timestamp = start, ProcessId = 1, ThreadId = 1 });
await writer.WriteEventAsync(new ChromiumTracingDurationBeginEvent { Name = "Restore", Timestamp = start.AddMilliseconds(10), ProcessId = 1, ThreadId = 1 });
await writer.WriteEventAsync(new ChromiumTracingDurationEndEvent { Timestamp = start.AddMilliseconds(40), ProcessId = 1, ThreadId = 1 });
await writer.WriteEventAsync(new ChromiumTracingDurationEndEvent { Timestamp = start.AddMilliseconds(100), ProcessId = 1, ThreadId = 1 });
}
using var trace = JsonDocument.Parse(stream.ToArray());
Assert.Equal(["B", "B", "E", "E"], trace.RootElement.EnumerateArray().Select(item => item.GetProperty("ph").GetString()).ToArray());The durations of a thread nest: the viewer draws Restore under Build. An end event closes the last open begin event of its process and thread, so the example does not give it a name. Complete events nest the same way when one lies within another.
A complete event has a Duration, and optionally a ThreadDuration (the time the thread actually ran) and an EndStackTrace.
Instants and counters
using var stream = new MemoryStream();
await using (var writer = new ChromiumTracingWriter(stream))
{
var start = DateTimeOffset.UtcNow;
await writer.WriteEventAsync(new ChromiumTracingInstantEvent
{
Name = "Cache cleared",
Timestamp = start,
Scope = ChromiumTracingInstantEventScope.Global, // a line across the whole timeline
ProcessId = 1,
ThreadId = 1,
});
// Each argument of a counter event is a series of the same chart
await writer.WriteEventAsync(new ChromiumTracingCounterEvent
{
Name = "Cache",
Timestamp = start,
ProcessId = 1,
Arguments = new Dictionary<string, object?>(StringComparer.Ordinal) { ["hits"] = 0, ["misses"] = 0 },
});
await writer.WriteEventAsync(new ChromiumTracingCounterEvent
{
Name = "Cache",
Timestamp = start.AddSeconds(1),
ProcessId = 1,
Arguments = new Dictionary<string, object?>(StringComparer.Ordinal) { ["hits"] = 120, ["misses"] = 8 },
});
}
using var trace = JsonDocument.Parse(stream.ToArray());
Assert.Equal(["i", "C", "C"], trace.RootElement.EnumerateArray().Select(item => item.GetProperty("ph").GetString()).ToArray());- The Scope of an instant event is Thread, Process or Global: the mark is drawn on the row of the thread, of the process, or across the whole timeline.
- A counter event gives the values of a counter at its timestamp. Each argument is a series of the chart; the values are numbers. The
Idproperty of a counter event, when set, makes the counter distinct from the counters with the same name.
Asynchronous operations
Operations that overlap on the same thread, such as concurrent downloads, cannot be nested durations. Asynchronous events describe them with an Id: the events with the same category and id belong to the same operation.
using var stream = new MemoryStream();
await using (var writer = new ChromiumTracingWriter(stream))
{
var start = DateTimeOffset.UtcNow;
// Two downloads run at the same time: the Id tells them apart
await writer.WriteEventAsync(new ChromiumTracingAsyncBeginEvent { Name = "Download", Category = "http", Id = 1, Timestamp = start, ProcessId = 1 });
await writer.WriteEventAsync(new ChromiumTracingAsyncBeginEvent { Name = "Download", Category = "http", Id = 2, Timestamp = start.AddMilliseconds(5), ProcessId = 1 });
await writer.WriteEventAsync(new ChromiumTracingAsyncInstantEvent { Name = "Headers received", Category = "http", Id = 1, Timestamp = start.AddMilliseconds(20), ProcessId = 1 });
await writer.WriteEventAsync(new ChromiumTracingAsyncEndEvent { Name = "Download", Category = "http", Id = 2, Timestamp = start.AddMilliseconds(50), ProcessId = 1 });
await writer.WriteEventAsync(new ChromiumTracingAsyncEndEvent { Name = "Download", Category = "http", Id = 1, Timestamp = start.AddMilliseconds(80), ProcessId = 1 });
}
using var trace = JsonDocument.Parse(stream.ToArray());
Assert.Equal(["b", "b", "n", "e", "e"], trace.RootElement.EnumerateArray().Select(item => item.GetProperty("ph").GetString()).ToArray());ChromiumTracingAsyncInstantEvent marks a point in time within the operation.
Flows
Flow events draw arrows from one event to another, for instance from the code that queues a work item to the thread that runs it. ChromiumTracingFlowBeginEvent starts the flow, ChromiumTracingFlowStepEvent adds a step, and ChromiumTracingFlowEndEvent ends it. The begin and step events are attached to the duration that encloses them on their thread. The BindingPoint of the end event chooses the duration the arrow points to: NextSlice, the default, is the next duration that starts on its thread, and EnclosingSlice the one that encloses it.
The flow event types do not have the id field that the format uses to tell the flows apart, so they cannot describe several flows that overlap.
Objects
Object events show the lifetime of an object, identified by its Id, and snapshots of its state in their arguments:
using var stream = new MemoryStream();
await using (var writer = new ChromiumTracingWriter(stream))
{
var start = DateTimeOffset.UtcNow;
await writer.WriteEventAsync(new ChromiumTracingObjectCreatedEvent { Name = "Connection", Id = "connection-1", Timestamp = start, ProcessId = 1 });
await writer.WriteEventAsync(new ChromiumTracingObjectSnapshotEvent
{
Name = "Connection",
Id = "connection-1",
Timestamp = start.AddMilliseconds(10),
ProcessId = 1,
Arguments = new Dictionary<string, object?>(StringComparer.Ordinal) { ["state"] = "open" },
});
await writer.WriteEventAsync(new ChromiumTracingObjectDestroyedEvent { Name = "Connection", Id = "connection-1", Timestamp = start.AddMilliseconds(30), ProcessId = 1 });
}
using var trace = JsonDocument.Parse(stream.ToArray());
Assert.Equal(["N", "O", "D"], trace.RootElement.EnumerateArray().Select(item => item.GetProperty("ph").GetString()).ToArray());Names of the processes and threads
By default, the viewer shows the numbers of the processes and threads. Metadata events give them names and choose their order:
using var stream = new MemoryStream();
await using (var writer = new ChromiumTracingWriter(stream))
{
await writer.WriteEventAsync(ChromiumTracingMetadataEvent.ProcessName(pid: 1, "Build server"));
await writer.WriteEventAsync(ChromiumTracingMetadataEvent.ProcessSortIndex(pid: 1, index: 0));
await writer.WriteEventAsync(ChromiumTracingMetadataEvent.ThreadName(pid: 1, tid: 1, "Main thread"));
await writer.WriteEventAsync(ChromiumTracingMetadataEvent.ThreadName(pid: 1, tid: 2, "Worker"));
}
using var trace = JsonDocument.Parse(stream.ToArray());
Assert.Equal(
["process_name", "process_sort_index", "thread_name", "thread_name"],
trace.RootElement.EnumerateArray().Select(item => item.GetProperty("name").GetString()).ToArray());| Method | Sets |
|---|---|
ProcessName(pid, name) | The name of a process |
ProcessLabels(pid, labels) | The labels of a process |
ProcessSortIndex(pid, index) | The position of a process; lower indexes come first |
ThreadName(pid, tid, name) | The name of a thread |
ThreadSortIndex(pid, tid, index) | The position of a thread within its process |