Skip to content

Commit a0599d0

Browse files
committed
trace2: add region infrastructure
Add supporting infrastructure for regions. This includes the normal `Write` methods in Trace2 and corresponding Trace2 messages with a few notable distinctions: 1. There is an abstract parent RegionMessage that inherits from Trace2Message. The RegionEnterMessage and RegionLeave messages inherit from this parent (rather than Trace2Message) to maximize reuse of shared fields. 2. We create a new Region class that implements DisposableObject and call it using a CreateRegion method in Trace2. This ensures that we call WriteRegionLeave before a Region object is disposed - a key requirement of Git's Trace2 API for regions. Additionally, it allows us to manage getting the executing thread name and region timings within the class, thus removing this burden from consumers. An additional note is that the region_enter and region_leave events will always have the same line number with this model. This was deemed to be acceptable, however, given that knowing where a region begins executing provides enough information for inspection of the region if it is needed.
1 parent 7abcfef commit a0599d0

File tree

3 files changed

+266
-1
lines changed

3 files changed

+266
-1
lines changed

src/shared/Core/Trace2.cs

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public enum Trace2Event
1919
ChildStart = 3,
2020
ChildExit = 4,
2121
Error = 5,
22+
RegionEnter = 6,
23+
RegionLeave = 7,
2224
}
2325

2426
/// <summary>
@@ -54,6 +56,40 @@ public class PerformanceFormatSpan
5456
public int EndPadding { get; set; }
5557
}
5658

59+
/// <summary>
60+
/// Class that manages regions.
61+
/// </summary>
62+
public class Region : DisposableObject
63+
{
64+
private readonly ITrace2 _trace2;
65+
private readonly string _category;
66+
private readonly string _label;
67+
private readonly string _filePath;
68+
private readonly int _lineNumber;
69+
private readonly string _message;
70+
private readonly DateTimeOffset _startTime;
71+
72+
public Region(ITrace2 trace2, string category, string label, string filePath, int lineNumber, string message = "")
73+
{
74+
_trace2 = trace2;
75+
_category = category;
76+
_label = label;
77+
_filePath = filePath;
78+
_lineNumber = lineNumber;
79+
_message = message;
80+
81+
_startTime = DateTimeOffset.UtcNow;
82+
83+
_trace2.WriteRegionEnter(_category, _label, _message, _filePath, _lineNumber);
84+
}
85+
86+
protected override void ReleaseManagedResources()
87+
{
88+
double relativeTime = (DateTimeOffset.UtcNow - _startTime).TotalSeconds;
89+
_trace2.WriteRegionLeave(relativeTime, _category, _label, _message, _filePath, _lineNumber);
90+
}
91+
}
92+
5793
/// <summary>
5894
/// Represents the application's TRACE2 tracing system.
5995
/// </summary>
@@ -139,6 +175,59 @@ void WriteError(
139175
string filePath = "",
140176
[System.Runtime.CompilerServices.CallerLineNumber]
141177
int lineNumber = 0);
178+
179+
/// <summary>
180+
/// Creates a region and manages entry/leaving.
181+
/// </summary>
182+
/// <param name="category">Category of region.</param>
183+
/// <param name="label">Description of region.</param>
184+
/// <param name="message">Message associated with entering region.</param>
185+
/// <param name="filePath">Path of the file this method is called from.</param>
186+
/// <param name="lineNumber">Line number of file this method is called from.</param>
187+
Region CreateRegion(
188+
string category,
189+
string label,
190+
string message = "",
191+
[System.Runtime.CompilerServices.CallerFilePath]
192+
string filePath = "",
193+
[System.Runtime.CompilerServices.CallerLineNumber]
194+
int lineNumber = 0);
195+
196+
/// <summary>
197+
/// Writes a region enter message to the trace writer.
198+
/// </summary>
199+
/// <param name="category">Category of region.</param>
200+
/// <param name="label">Description of region.</param>
201+
/// <param name="message">Message associated with entering region.</param>
202+
/// <param name="filePath">Path of the file this method is called from.</param>
203+
/// <param name="lineNumber">Line number of file this method is called from.</param>
204+
void WriteRegionEnter(
205+
string category,
206+
string label,
207+
string message = "",
208+
[System.Runtime.CompilerServices.CallerFilePath]
209+
string filePath = "",
210+
[System.Runtime.CompilerServices.CallerLineNumber]
211+
int lineNumber = 0);
212+
213+
/// <summary>
214+
/// Writes a region leave message to the trace writer.
215+
/// </summary>
216+
/// <param name="relativeTime">Time of region execution.</param>
217+
/// <param name="category">Category of region.</param>
218+
/// <param name="label">Description of region.</param>
219+
/// <param name="message">Message associated with entering region.</param>
220+
/// <param name="filePath">Path of the file this method is called from.</param>
221+
/// <param name="lineNumber">Line number of file this method is called from.</param>
222+
void WriteRegionLeave(
223+
double relativeTime,
224+
string category,
225+
string label,
226+
string message = "",
227+
[System.Runtime.CompilerServices.CallerFilePath]
228+
string filePath = "",
229+
[System.Runtime.CompilerServices.CallerLineNumber]
230+
int lineNumber = 0);
142231
}
143232

144233
public class Trace2 : DisposableObject, ITrace2
@@ -307,6 +396,64 @@ public void WriteError(
307396
});
308397
}
309398

399+
public Region CreateRegion(
400+
string category,
401+
string label,
402+
string message,
403+
string filePath,
404+
int lineNumber)
405+
{
406+
return new Region(this, category, label, filePath, lineNumber, message);
407+
}
408+
409+
public void WriteRegionEnter(
410+
string category,
411+
string label,
412+
string message = "",
413+
string filePath = "",
414+
int lineNumber = 0)
415+
{
416+
WriteMessage(new RegionEnterMessage()
417+
{
418+
Event = Trace2Event.RegionEnter,
419+
Sid = _sid,
420+
Time = DateTimeOffset.UtcNow,
421+
Category = category,
422+
Label = label,
423+
Message = message == "" ? label : message,
424+
Thread = BuildThreadName(),
425+
File = Path.GetFileName(filePath),
426+
Line = lineNumber,
427+
ElapsedTime = (DateTimeOffset.UtcNow - _applicationStartTime).TotalSeconds,
428+
Depth = ProcessManager.Depth
429+
});
430+
}
431+
432+
public void WriteRegionLeave(
433+
double relativeTime,
434+
string category,
435+
string label,
436+
string message = "",
437+
string filePath = "",
438+
int lineNumber = 0)
439+
{
440+
WriteMessage(new RegionLeaveMessage()
441+
{
442+
Event = Trace2Event.RegionLeave,
443+
Sid = _sid,
444+
Time = DateTimeOffset.UtcNow,
445+
Category = category,
446+
Label = label,
447+
Message = message == "" ? label : message,
448+
Thread = BuildThreadName(),
449+
File = Path.GetFileName(filePath),
450+
Line = lineNumber,
451+
ElapsedTime = (DateTimeOffset.UtcNow - _applicationStartTime).TotalSeconds,
452+
RelativeTime = relativeTime,
453+
Depth = ProcessManager.Depth
454+
});
455+
}
456+
310457
protected override void ReleaseManagedResources()
311458
{
312459
lock (_writersLock)

src/shared/Core/Trace2Message.cs

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ private static string BuildSpan(PerformanceFormatSpan component, string data)
162162

163163
if (data.Length < dataLimit)
164164
{
165-
component.EndPadding += Math.Abs(sizeDifference);
165+
// Increase end padding for short values
166+
component.EndPadding += sizeDifference;
166167
}
167168

168169
var beginPadding = new string(' ', component.BeginPadding);
@@ -436,3 +437,95 @@ protected override string GetEventMessage(Trace2FormatTarget formatTarget)
436437
return Message;
437438
}
438439
}
440+
441+
public abstract class RegionMessage : Trace2Message
442+
{
443+
[JsonProperty("repo")]
444+
445+
// Defaults to 1, as does Git.
446+
// See https://git-scm.com/docs/api-trace2#Documentation/technical/api-trace2.txt-codeltrepo-idgtcode for details.
447+
public int Repo { get; set; } = 1;
448+
449+
// TODO: Remove default value if support for nested regions is implemented.
450+
[JsonProperty("nesting")]
451+
public int Nesting { get; set; } = 1;
452+
453+
[JsonProperty("category")]
454+
public string Category { get; set; }
455+
456+
[JsonProperty("label")]
457+
public string Label { get; set; }
458+
459+
[JsonProperty("msg")]
460+
public string Message { get; set; }
461+
462+
public double ElapsedTime { get; set; }
463+
}
464+
465+
public class RegionEnterMessage : RegionMessage
466+
{
467+
public override string ToJson()
468+
{
469+
return JsonConvert.SerializeObject(this,
470+
new StringEnumConverter(typeof(SnakeCaseNamingStrategy)),
471+
new IsoDateTimeConverter()
472+
{
473+
DateTimeFormat = TimeFormat
474+
});
475+
}
476+
477+
public override string ToNormalString()
478+
{
479+
return BuildNormalString();
480+
}
481+
482+
public override string ToPerformanceString()
483+
{
484+
return BuildPerformanceString();
485+
}
486+
487+
protected override string BuildPerformanceSpan()
488+
{
489+
return $"|{BuildRepoSpan(Repo)}|{BuildTimeSpan(ElapsedTime)}| |{BuildCategorySpan(Category)}";
490+
}
491+
492+
protected override string GetEventMessage(Trace2FormatTarget formatTarget)
493+
{
494+
return Message;
495+
}
496+
}
497+
498+
public class RegionLeaveMessage : RegionMessage
499+
{
500+
public double RelativeTime { get; set; }
501+
502+
public override string ToJson()
503+
{
504+
return JsonConvert.SerializeObject(this,
505+
new StringEnumConverter(typeof(SnakeCaseNamingStrategy)),
506+
new IsoDateTimeConverter()
507+
{
508+
DateTimeFormat = TimeFormat
509+
});
510+
}
511+
512+
public override string ToNormalString()
513+
{
514+
return BuildNormalString();
515+
}
516+
517+
public override string ToPerformanceString()
518+
{
519+
return BuildPerformanceString();
520+
}
521+
522+
protected override string BuildPerformanceSpan()
523+
{
524+
return $"|{BuildRepoSpan(Repo)}|{BuildTimeSpan(ElapsedTime)}|{BuildTimeSpan(RelativeTime)}|{BuildCategorySpan(Category)}";
525+
}
526+
527+
protected override string GetEventMessage(Trace2FormatTarget formatTarget)
528+
{
529+
return Message;
530+
}
531+
}

src/shared/TestInfrastructure/Objects/NullTrace.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,31 @@ public void WriteError(
8484
string filePath = "",
8585
int lineNumber = 0) { }
8686

87+
public Region CreateRegion(
88+
string category,
89+
string label,
90+
string message = "",
91+
string filePath = "",
92+
int lineNumber = 0)
93+
{
94+
return new Region(this, category, label, filePath, lineNumber, message);
95+
}
96+
97+
public void WriteRegionEnter(
98+
string category,
99+
string label,
100+
string message = "",
101+
string filePath = "",
102+
int lineNumber = 0) { }
103+
104+
public void WriteRegionLeave(
105+
double relativeTime,
106+
string category,
107+
string label,
108+
string message = "",
109+
string filePath = "",
110+
int lineNumber = 0) { }
111+
87112
#endregion
88113

89114
#region IDisposable

0 commit comments

Comments
 (0)