Skip to content

Commit f844238

Browse files
authored
trace2: add regions (#1162)
Add `TRACE2` region tracing capability. The first six commits contain minor refactors, bug fixes, and cleanups. The first moves Trace2 messages into their own file. The second removes the unnecessary `AddDots()` method. The third fixes certain conflated timing information in `child_start` and `child_exit` events. The fourth ensures we're writing exit message times in UTC. The fifth adds descriptive comments to Trace2-related classes that did not yet have them. The sixth adds best-effort logic to capture thread names. The next three commits are dedicated to preparing for and adding region-related logic. The seventh commit updates custom performance format span handling to account for the repo and category fields, which are used in region tracing. The eighth adds region-related events, encapsulated in a class that inherits from `DisposableObject` (which in turn inherits from `IDisposable`). The ninth adds region tracing to methods of interest pertaining to OAuth authentication. The final commit updates GCM's documentation to inform users of how to enable and utilize Trace2 tracing. A final note: This implementation does not support nested regions, as it is not required for the current regions of interest.
2 parents eefca38 + ecf94e8 commit f844238

File tree

8 files changed

+870
-416
lines changed

8 files changed

+870
-416
lines changed

docs/development.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ Git to be the one launching us.
9898

9999
### Collect trace output
100100

101+
GCM has two tracing systems - one that is distinctly GCM's and one that
102+
implements certain features of [Git's Trace2 API][trace2]. Below are
103+
instructions for how to use each.
104+
105+
#### `GCM_TRACE`
106+
101107
If you want to debug a release build or installation of GCM, you can set the
102108
`GCM_TRACE` environment variable to `1` to print trace information to standard
103109
error, or to an absolute file path to write trace information to a file.
@@ -110,6 +116,80 @@ $ GCM_TRACE=1 git-credential-manager version
110116
> Git Credential Manager version 2.0.124-beta+e1ebbe1517 (macOS, .NET 5.0)
111117
```
112118

119+
#### Git's Trace2 API
120+
121+
This API can also be used to print debug, performance, and telemetry information
122+
to stderr or a file in various formats.
123+
124+
##### Supported format targets
125+
126+
1. The Normal Format Target: Similar to `GCM_TRACE`, this target writes
127+
human-readable output and is best suited for debugging. It can be enabled via
128+
environment variable or config, for example:
129+
130+
```shell
131+
export GIT_TRACE2=1
132+
```
133+
134+
or
135+
136+
```shell
137+
git config --global trace2.normalTarget ~/log.normal
138+
```
139+
140+
0. The Performance Format Target: This format is column-based and geared toward
141+
analyzing performance during development and testing. It can be enabled via
142+
environment variable or config, for example:
143+
144+
```shell
145+
export GIT_TRACE2_PERF=1
146+
```
147+
148+
or
149+
150+
```shell
151+
git config --global trace2.perfTarget ~/log.perf
152+
```
153+
154+
0. The Event Format Target: This format is json-based and is geared toward
155+
collection of large quantities of data for advanced analysis. It can be enabled
156+
via environment variable or config, for example:
157+
158+
```shell
159+
export GIT_TRACE2_EVENT=1
160+
```
161+
162+
or
163+
164+
```shell
165+
git config --global trace2.eventTarget ~/log.event
166+
```
167+
168+
You can read more about each of these format targets in the [corresponding
169+
section][trace2-targets] of Git's Trace2 API documentation.
170+
171+
##### Supported events
172+
173+
The below describes, at a high level, the Trace2 API events that are currently
174+
supported in GCM and the information they provide:
175+
176+
1. `version`: contains the version of the current executable (e.g. GCM or a
177+
helper exe)
178+
0. `start`: contains the complete argv received by current executable's `Main()`
179+
method
180+
0. `exit`: contains current executable's exit code
181+
0. `child_start`: describes a child process that is about to be spawned
182+
0. `child_exit`: describes a child process at exit
183+
0. `region_enter`: describes a region (e.g. a timer for a section of code that
184+
is interesting) on entry
185+
0. `region_leave`: describes a region on leaving
186+
187+
You can read more about each of these format targets in the [corresponding
188+
section][trace2-events] of Git's Trace2 API documentation.
189+
190+
Want to see more events? Consider contributing! We'd :love: to see your
191+
awesome work in support of building out this API.
192+
113193
### Code coverage metrics
114194

115195
If you want code coverage metrics these can be generated either from the command
@@ -169,4 +249,7 @@ Some URLs are ignored by lychee, per the [lycheeignore][lycheeignore].
169249
[lycheeignore]: ../.lycheeignore
170250
[markdownlint]: https://github.com/DavidAnson/markdownlint-cli2
171251
[markdownlint-config]: ../.markdownlint.jsonc
252+
[trace2]: https://git-scm.com/docs/api-trace2
253+
[trace2-events]: https://git-scm.com/docs/api-trace2#_event_specific_keyvalue_pairs
254+
[trace2-targets]: https://git-scm.com/docs/api-trace2#_trace2_targets
172255
[vscode-markdownlint]: https://github.com/DavidAnson/vscode-markdownlint
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using GitCredentialManager;
2+
using Xunit;
3+
4+
namespace Core.Tests;
5+
6+
public class Trace2MessageTests
7+
{
8+
[Theory]
9+
[InlineData(0.013772, " 0.013772 ")]
10+
[InlineData(26.316083, " 26.316083 ")]
11+
[InlineData(100.316083, "100.316083 ")]
12+
[InlineData(1000.316083, "1000.316083")]
13+
public void BuildTimeSpan_Match_Returns_Expected_String(double input, string expected)
14+
{
15+
var actual = Trace2Message.BuildTimeSpan(input);
16+
Assert.Equal(expected, actual);
17+
}
18+
19+
[Fact]
20+
public void BuildRepoSpan_Match_Returns_Expected_String()
21+
{
22+
var input = 1;
23+
var expected = " r1 ";
24+
var actual = Trace2Message.BuildRepoSpan(input);
25+
Assert.Equal(expected, actual);
26+
}
27+
28+
[Theory]
29+
[InlineData("foo", " foo ")]
30+
[InlineData("foobar", " foobar ")]
31+
[InlineData("foo_bar_baz", " foo_bar_baz ")]
32+
[InlineData("foobarbazfoo", " foobarbazfo ")]
33+
public void BuildCategorySpan_Match_Returns_Expected_String(string input, string expected)
34+
{
35+
var actual = Trace2Message.BuildCategorySpan(input);
36+
Assert.Equal(expected, actual);
37+
}
38+
}

src/shared/Core.Tests/Trace2Tests.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,4 @@ public void TryGetPipeName_Windows_Returns_Expected_Value(string input, string e
2727
Assert.True(isSuccessful);
2828
Assert.Matches(actual, expected);
2929
}
30-
31-
[Theory]
32-
[InlineData(0.013772, " 0.013772 ")]
33-
[InlineData(26.316083, " 26.316083 ")]
34-
[InlineData(100.316083, "100.316083 ")]
35-
public void BuildTimeSpan_Match_Returns_Expected_String(double input, string expected)
36-
{
37-
var actual = Trace2Message.BuildTimeSpan(input);
38-
Assert.Equal(expected, actual);
39-
}
4030
}

src/shared/Core/Authentication/OAuth/OAuth2Client.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ public async Task<OAuth2AuthorizationCodeResult> GetAuthorizationCodeAsync(IEnum
183183

184184
public async Task<OAuth2DeviceCodeResult> GetDeviceCodeAsync(IEnumerable<string> scopes, CancellationToken ct)
185185
{
186+
var label = "get device code";
187+
using IDisposable region = _trace2.CreateRegion(OAuth2Constants.Trace2Category, label);
188+
186189
if (_endpoints.DeviceAuthorizationEndpoint is null)
187190
{
188191
throw new Trace2InvalidOperationException(_trace2,
@@ -218,6 +221,9 @@ public async Task<OAuth2DeviceCodeResult> GetDeviceCodeAsync(IEnumerable<string>
218221

219222
public async Task<OAuth2TokenResult> GetTokenByAuthorizationCodeAsync(OAuth2AuthorizationCodeResult authorizationCodeResult, CancellationToken ct)
220223
{
224+
var label = "get token by auth code";
225+
using IDisposable region = _trace2.CreateRegion(OAuth2Constants.Trace2Category, label);
226+
221227
var formData = new Dictionary<string, string>
222228
{
223229
[OAuth2Constants.TokenEndpoint.GrantTypeParameter] = OAuth2Constants.TokenEndpoint.AuthorizationCodeGrantType,
@@ -254,6 +260,9 @@ public async Task<OAuth2TokenResult> GetTokenByAuthorizationCodeAsync(OAuth2Auth
254260

255261
public async Task<OAuth2TokenResult> GetTokenByRefreshTokenAsync(string refreshToken, CancellationToken ct)
256262
{
263+
var label = "get token by refresh token";
264+
using IDisposable region = _trace2.CreateRegion(OAuth2Constants.Trace2Category, label);
265+
257266
var formData = new Dictionary<string, string>
258267
{
259268
[OAuth2Constants.TokenEndpoint.GrantTypeParameter] = OAuth2Constants.TokenEndpoint.RefreshTokenGrantType,

src/shared/Core/Authentication/OAuth/OAuth2Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public static class OAuth2Constants
77
public const string ClientSecretParameter = "client_secret";
88
public const string RedirectUriParameter = "redirect_uri";
99
public const string ScopeParameter = "scope";
10+
public const string Trace2Category = "oauth2";
1011

1112
public static class AuthorizationEndpoint
1213
{

0 commit comments

Comments
 (0)