Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<SentryVersion>5.9.0</SentryVersion>
<SentryVersion>5.11.0-alpha.1</SentryVersion>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Sentry" Version="$(SentryVersion)" />
Expand Down
8 changes: 6 additions & 2 deletions src/SymbolCollector.Android.Library/AndroidUploader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,29 @@ public AndroidUploader(Client client, ILogger<AndroidUploader> logger)
_logger = logger;
}

public Task StartUpload(string friendlyName, CancellationToken token) =>
public Task StartUpload(string friendlyName, ISpan uploadSpan, CancellationToken token) =>
Task.Run(async () =>
{
var paths = new[] { "/system/lib", "/system/lib64", "/system/", "/vendor/lib" };

_logger.LogInformation("Using friendly name: {friendlyName} and paths: {paths}",
friendlyName, paths);

var androidUploaderSpan = uploadSpan.StartChild("androidUpload", "uploading all paths async");
try
{
await _client.UploadAllPathsAsync(friendlyName, BatchType.Android, paths, token);
await _client.UploadAllPathsAsync(friendlyName, BatchType.Android, paths, androidUploaderSpan, token);
androidUploaderSpan.Finish();
}
catch (OperationCanceledException)
{
androidUploaderSpan.Finish(SpanStatus.Cancelled);
}
catch (Exception e)
{
_logger.LogError(e, "Failed uploading {friendlyName} paths: {paths}",
friendlyName, paths);
androidUploaderSpan.Finish(e);
// Make sure event is flushed and rethrow
await SentrySdk.FlushAsync(TimeSpan.FromSeconds(3));
throw;
Expand Down
2 changes: 1 addition & 1 deletion src/SymbolCollector.Android.Library/AutoUploader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void Run(Context context)
s.Contexts.OperatingSystem.KernelVersion = uname.Release;
}
});
var uploadTask = uploader.StartUpload(friendlyName, source.Token);
var uploadTask = uploader.StartUpload(friendlyName, tran, source.Token);
uploadTask.ContinueWith(t =>
{
tran.Finish(t.IsCompletedSuccessfully ? SpanStatus.Ok : SpanStatus.UnknownError);
Expand Down
58 changes: 31 additions & 27 deletions src/SymbolCollector.Android/MainActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class MainActivity : Activity
private string _friendlyName = null!; // set on OnCreate
private IHost _host = null!; // set on OnCreate
private IServiceProvider _serviceProvider = null!; // set on OnCreate
private ITransactionTracer _startupTransaction = null!; // set on OnCreate
private SentryTraceHeader _parentTraceHeader = null!;

protected override void OnCreate(Bundle? savedInstanceState)
{
Expand All @@ -35,10 +35,12 @@ protected override void OnCreate(Bundle? savedInstanceState)
_serviceProvider = _host.Services;

// It's set in Host.Init above
SentrySdk.ConfigureScope(s => _startupTransaction = s.Transaction!);
ITransactionTracer startupTransaction = null!;
SentrySdk.ConfigureScope(s => startupTransaction = s.Transaction!);
_parentTraceHeader = startupTransaction.GetTraceHeader();
AddSentryContext();

var span = _startupTransaction.StartChild("OnCreate");
var onCreateSpan = startupTransaction.StartChild("OnCreate");
try
{
base.OnCreate(savedInstanceState);
Expand Down Expand Up @@ -71,10 +73,23 @@ protected override void OnCreate(Bundle? savedInstanceState)
uploadButton.Click += OnUploadButtonOnClick;
cancelButton.Click += OnCancelButtonOnClick;

onCreateSpan.Finish(SpanStatus.Ok);
startupTransaction.Finish(SpanStatus.Ok);
return;

void OnCancelButtonOnClick(object? sender, EventArgs args)
{
SentrySdk.AddBreadcrumb("OnCancelButtonOnClick", category: "ui.event");
Unfocus();
source.Cancel();
}

async void OnUploadButtonOnClick(object? sender, EventArgs args)
{
// The scope tracks the overall app transaction while this is only batch uploading
var uploadTransaction = SentrySdk.StartTransaction("BatchUpload", "batch.upload", _startupTransaction.GetTraceHeader());
var uploadTransaction = SentrySdk.StartTransaction("BatchUpload", "batch.upload", _parentTraceHeader);
// startup transaction has completed at this point, this starts when the user pressed Upload so make this transaction global:
SentrySdk.ConfigureScope(s => s.Transaction = uploadTransaction);

try
{
Expand All @@ -89,32 +104,23 @@ async void OnUploadButtonOnClick(object? sender, EventArgs args)
uploadButton.Enabled = false;
source = new CancellationTokenSource();

var uploadTask = uploader.StartUpload(_friendlyName, source.Token);
var uploadTask = uploader.StartUpload(_friendlyName, uploadTransaction, source.Token);
var updateUiTask = StartUiUpdater(source.Token, metrics);

await UploadAsync(uploadTask, updateUiTask, metrics, cancelButton, uploadButton, uploadTransaction, source);
// Successful completion (or timeout) of the transaction is handled internally.
await AwaitOnUploadAndCancellationTasks(uploadTask, updateUiTask, metrics, cancelButton, uploadButton, uploadTransaction, source);
}
catch (Exception e)
{
uploadTransaction.Finish(e);
throw;
}
}

void OnCancelButtonOnClick(object? sender, EventArgs args)
{
SentrySdk.AddBreadcrumb("OnCancelButtonOnClick", category: "ui.event");
Unfocus();
source.Cancel();
}

span.Finish(SpanStatus.Ok);
_startupTransaction.Finish(SpanStatus.Ok);
}
catch (Exception e)
{
span.Finish(e);
_startupTransaction.Finish(e);
onCreateSpan.Finish(e);
startupTransaction.Finish(e);
throw;
}
}
Expand All @@ -128,13 +134,13 @@ private void Unfocus()
}
}

private async Task UploadAsync(
private async Task AwaitOnUploadAndCancellationTasks(
Task uploadTask,
Task updateUiTask,
ClientMetrics metrics,
View cancelButton,
View uploadButton,
ISpan span,
ISpan uploadTransaction,
CancellationTokenSource source)
{
var container = base.FindViewById(Resource.Id.metrics_container)!;
Expand All @@ -159,28 +165,28 @@ private async Task UploadAsync(
ranForContainer.Visibility = ViewStates.Visible;

ranForLabel.Text = metrics.RanFor.ToString();
span.Finish(SpanStatus.Ok);
uploadTransaction.Finish(SpanStatus.Ok);
}
else if (uploadTask.IsFaulted)
{
await ShowError(uploadTask.Exception);
span.Finish(SpanStatus.InternalError);
uploadTransaction.Finish(SpanStatus.InternalError);
}
else
{
cancelButton.Enabled = false;
uploadButton.Enabled = true;
span.Finish(SpanStatus.Cancelled);
uploadTransaction.Finish(SpanStatus.Cancelled);
}
}
catch (Exception e)
{
await ShowError(e);
span.Finish(e);
uploadTransaction.Finish(e);
}
finally
{
source.Cancel();
await source.CancelAsync();
}
}

Expand Down Expand Up @@ -278,8 +284,6 @@ private void AddSentryContext()

SentrySdk.ConfigureScope(s =>
{
s.Transaction = _startupTransaction;

if (uname is { })
{
s.Contexts["uname"] = new
Expand Down
4 changes: 2 additions & 2 deletions src/SymbolCollector.Console/ConsoleUploader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public async Task StartUploadSymbols(IEnumerable<string> paths, string bundleId,
var type = batchType ?? DeviceBatchType();
_logger.LogInformation("Uploading bundle {bundleId} of type {type} and paths: {paths}",
bundleId, type, paths);
await _client.UploadAllPathsAsync(bundleId, type, paths, token);
await _client.UploadAllPathsAsync(bundleId, type, paths, transaction, token);
transaction.Finish(SpanStatus.Ok);
}
catch (Exception e)
Expand Down Expand Up @@ -87,4 +87,4 @@ static BatchType DeviceBatchType()

throw new InvalidOperationException("No BatchType available for the current device.");
}
}
}
90 changes: 54 additions & 36 deletions src/SymbolCollector.Core/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,59 +30,75 @@ public Client(
SentrySdk.ConfigureScope(s => s.SetExtra(nameof(Metrics), Metrics));
}

public async Task UploadAllPathsAsync(
string friendlyName,
public async Task UploadAllPathsAsync(string friendlyName,
BatchType type,
IEnumerable<string> topLevelPaths,
ISpan span,
CancellationToken cancellationToken)
{
var groupsSpan = SentrySdk.GetSpan()?.StartChild("group.get", "Get the group of directories to search in parallel");
List<List<string>> groups;
var counter = 0;
var groups =
(from topPath in topLevelPaths
from lookupDirectory in SafeGetDirectories(topPath)
where _blockListedPaths?.Contains(lookupDirectory) != true
let c = counter++
group lookupDirectory by c / ParallelTasks
into grp
select grp.ToList()).ToList();
groupsSpan?.Finish();

var startSpan = SentrySdk.GetSpan()?.StartChild("batch.start");
Guid batchId;
try
{
batchId = await _symbolClient.Start(friendlyName, type, cancellationToken);
startSpan?.Finish(SpanStatus.Ok);
var groupsSpan = span.StartChild("group.get", "Get the group of directories to search in parallel");
groups =
(from topPath in topLevelPaths
from lookupDirectory in SafeGetDirectories(topPath)
where _blockListedPaths?.Contains(lookupDirectory) != true
let c = counter++
group lookupDirectory by c / ParallelTasks
into grp
select grp.ToList()).ToList();
groupsSpan.Finish();
}
catch (Exception e)

Guid batchId;
{
startSpan?.Finish(e);
throw;
var startSpan = span.StartChild("batch.start");
try
{
batchId = await _symbolClient.Start(friendlyName, type, cancellationToken);
startSpan.Finish(SpanStatus.Ok);
}
catch (Exception e)
{
startSpan.Finish(e);
throw;
}
}

var uploadSpan = SentrySdk.GetSpan()?.StartChild("batch.upload");
uploadSpan?.SetTag("groups", groups.Count.ToString());
uploadSpan?.SetTag("total_items", counter.ToString());
try
{
foreach (var group in groups)
var uploadSpan = span.StartChild("batch.upload", "concurrent batch upload");
uploadSpan.SetData("groups", groups.Count.ToString());
uploadSpan.SetData("total_items", counter.ToString());

// use this as parent to all outgoing HTTP requests now:
SentrySdk.ConfigureScope(s => s.Span = uploadSpan);
int i = 0;
try
{
await UploadParallel(batchId, group, cancellationToken);
foreach (var group in groups)
{
if (i++ == 2) break;
await UploadParallel(batchId, group, cancellationToken);
}
uploadSpan.Finish(SpanStatus.Ok);
}
catch (Exception e)
{
_logger.LogError(e, "Failed processing files for {batchId}. Rethrowing and leaving the batch open.",
batchId);
uploadSpan.Finish(e);
throw;
}
uploadSpan?.Finish(SpanStatus.Ok);
}
catch (Exception e)

{
uploadSpan?.Finish(e);
_logger.LogError(e, "Failed processing files for {batchId}. Rethrowing and leaving the batch open.",
batchId);
throw;
var stopSpan = span.StartChild("batch.close");
await _symbolClient.Close(batchId, cancellationToken);
stopSpan.Finish(SpanStatus.Ok);
}

var stopSpan = SentrySdk.GetSpan()?.StartChild("batch.close");
await _symbolClient.Close(batchId, cancellationToken);
stopSpan?.Finish(SpanStatus.Ok);
return;

IEnumerable<string> SafeGetDirectories(string path)
{
Expand Down Expand Up @@ -122,6 +138,8 @@ private async Task UploadParallel(Guid batchId, IEnumerable<string> paths, Cance
{
tasks.Add(UploadFilesAsync(batchId, path, cancellationToken));
Metrics.JobsInFlightAdd(1);
// TODO: Remove me. Keeping it short to test e2e
// return;
}
else
{
Expand Down
4 changes: 2 additions & 2 deletions test/SymbolCollector.Core.Tests/ClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public async Task UploadAllPathsAsync_TestFilesDirectory_FilesDetected()
new HttpClient(_fixture.HttpMessageHandler));

var sut = _fixture.GetSut();
await sut.UploadAllPathsAsync("friendly name", BatchType.IOS, new[] {"TestFiles"}, CancellationToken.None);
await sut.UploadAllPathsAsync("friendly name", BatchType.IOS, new[] {"TestFiles"}, SentrySdk.StartTransaction("test", "test-op"), CancellationToken.None);

// number of valid test files in TestFiles
Assert.Equal(12, counter);
Expand All @@ -84,7 +84,7 @@ public async Task UploadAllPathsAsync_TestFilesDirectory_FileCorrectlySent()
Substitute.For<ILogger<ObjectFileParser>>(), new FatBinaryReader());

var sut = _fixture.GetSut();
await sut.UploadAllPathsAsync("friendly name", BatchType.IOS, new[] {"TestFiles"}, CancellationToken.None);
await sut.UploadAllPathsAsync("friendly name", BatchType.IOS, new[] {"TestFiles"}, SentrySdk.StartTransaction("test", "test-op"), CancellationToken.None);

// Make sure all valid test files were picked up
var testFiles = new ObjectFileResultTestCases()
Expand Down