public async Task Execute(Guid taskId, CancellationToken cancellationToken) { if (!stackwalk.Configured) { throw new Exception("Stackwalk is not configured"); } var task = await database.StackwalkTasks.FindAsync(new object[] { taskId }, cancellationToken); if (task == null) { logger.LogError("Can't stackwalk on non-existent task: {TaskId}", taskId); return; } var symbolPrepareTask = symbolPreparer.PrepareSymbolsInFolder(symbolFolder, cancellationToken); logger.LogInformation("Starting stackwalk on task {TaskId}", taskId); var semaphore = localTempFileLocks.GetTempFilePath(task.DumpTempCategory, out string baseFolder); var filePath = Path.Combine(baseFolder, task.DumpFileName); FileStream?dump = null; // On Linux an open file should not impact deleting etc. so I'm pretty sure this is pretty safe await semaphore.WaitAsync(cancellationToken); try { if (File.Exists(filePath)) { dump = File.OpenRead(filePath); } } finally { semaphore.Release(); } await symbolPrepareTask; if (string.IsNullOrEmpty(task.DumpFileName) || dump == null) { logger.LogError("Can't stackwalk for task with missing dump file: {FilePath}", filePath); return; } var startTime = DateTime.UtcNow; // TODO: implement an async API in the stackwalk service and swap to using that here // TODO: also then combine this with StartStackwalkOnReportJob class string result; try { result = await stackwalk.PerformBlockingStackwalk(dump, task.StackwalkPlatform, cancellationToken); task.Succeeded = true; } catch (Exception e) { // TODO: probably wants to retry at least once or twice here instead of immediately failing logger.LogError(e, "Failed to run stackwalk task"); result = "Failed to run stackwalk"; task.Succeeded = false; } var duration = DateTime.UtcNow - startTime; logger.LogInformation("Stackwalking (task) took: {Duration}", duration); if (task.DeleteDumpAfterRunning) { await semaphore.WaitAsync(cancellationToken); try { File.Delete(filePath); logger.LogInformation("Deleted processed file for stackwalk task: {FilePath}", filePath); } finally { semaphore.Release(); } } if (string.IsNullOrWhiteSpace(result)) { result = "Resulting decoded crash dump is empty"; } task.Result = result; task.FinishedAt = DateTime.UtcNow; // Don't want to cancel here as we can no longer undelete the file // ReSharper disable once MethodSupportsCancellation await database.SaveChangesAsync(); }
public async Task Execute(long reportId, CancellationToken cancellationToken) { if (!stackwalk.Configured) { throw new Exception("Stackwalk is not configured"); } var report = await database.CrashReports.FindAsync(new object[] { reportId }, cancellationToken); if (report == null) { logger.LogError("Can't stackwalk on non-existent report: {ReportId}", reportId); return; } if (string.IsNullOrEmpty(report.DumpLocalFileName)) { logger.LogError("Can't stackwalk on report that no longer has local dump: {ReportId}", reportId); return; } var symbolPrepareTask = symbolPreparer.PrepareSymbolsInFolder(symbolFolder, cancellationToken); logger.LogInformation("Starting stackwalk on report {ReportId}", reportId); var semaphore = localTempFileLocks.GetTempFilePath(CrashReport.CrashReportTempStorageFolderName, out string baseFolder); var filePath = Path.Combine(baseFolder, report.DumpLocalFileName); FileStream?dump = null; // On Linux an open file should not impact deleting etc. so I'm pretty sure this is pretty safe await semaphore.WaitAsync(cancellationToken); try { if (File.Exists(filePath)) { dump = File.OpenRead(filePath); } } finally { semaphore.Release(); } await symbolPrepareTask; if (report.DumpLocalFileName == null || dump == null) { logger.LogError("Can't stackwalk on report with missing dump file: {ReportId}", reportId); return; } var startTime = DateTime.UtcNow; // TODO: implement an async API in the stackwalk service and swap to using that here var result = await stackwalk.PerformBlockingStackwalk(dump, report.Platform, cancellationToken); var primaryCallstack = stackwalk.FindPrimaryCallstack(result); var condensedCallstack = stackwalk.CondenseCallstack(primaryCallstack); cancellationToken.ThrowIfCancellationRequested(); var duration = DateTime.UtcNow - startTime; logger.LogInformation("Stackwalking took: {Duration}", duration); await database.LogEntries.AddAsync(new LogEntry() { Message = $"Stackwalking performed on report {report.Id}, result length: {result.Length}, " + $"duration: {duration}", }, cancellationToken); if (string.IsNullOrWhiteSpace(result)) { result = "Resulting decoded crash dump is empty"; } report.UpdateProcessedDumpIfChanged(result, primaryCallstack, condensedCallstack); await database.SaveChangesWithConflictResolvingAsync( conflictEntries => { DatabaseConcurrencyHelpers.ResolveSingleEntityConcurrencyConflict(conflictEntries, report); report.UpdateProcessedDumpIfChanged(result, primaryCallstack, condensedCallstack); }, cancellationToken); jobClient.Schedule <CheckCrashReportDuplicatesJob>(x => x.Execute(report.Id, CancellationToken.None), TimeSpan.FromSeconds(10)); }