Skip to content

Commit

Permalink
fix: Stacktrace parsing for WindowsPowershell 5.1 (#50)
Browse files Browse the repository at this point in the history
* prepare tests before the fix

* fixup

* fixup CI

* fix windows powershell

* chore: update changelog

* more path validation
  • Loading branch information
vaind authored Jul 4, 2024
1 parent e8a6619 commit 638d83a
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 11 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Fixes

- StackTrace parsing on Windows Powershell 5.1 ([#50](https://github.com/getsentry/sentry-powershell/pull/50))

## 0.1.0

### Features
Expand Down
23 changes: 14 additions & 9 deletions modules/Sentry/private/StackTraceProcessor.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class StackTraceProcessor : SentryEventProcessor
{
$sentryFrames.Capacity = $this.StackTraceFrames.Count + 1
}
else
elseif ($null -ne $this.StackTraceString)
{
$sentryFrames.Capacity = $this.StackTraceString.Count + 1
}
Expand All @@ -139,7 +139,7 @@ class StackTraceProcessor : SentryEventProcessor
$sentryFrames.Add($this.CreateFrame($frame))
}
}
else
elseif ($null -ne $this.StackTraceString)
{
# Note: if InvocationInfo is present, use it to update:
# - the first frame (in case of `$_ | Out-Sentry` in a catch clause).
Expand Down Expand Up @@ -177,7 +177,7 @@ class StackTraceProcessor : SentryEventProcessor
{
# Update module info
$this.SetModule($sentryFrame)
$sentryFrame.InApp = $null -eq $sentryFrame.Module
$sentryFrame.InApp = [string]::IsNullOrEmpty($sentryFrame.Module)
$this.SetContextLines($sentryFrame)
}

Expand Down Expand Up @@ -210,7 +210,7 @@ class StackTraceProcessor : SentryEventProcessor
{
$sentryFrame = [Sentry.SentryStackFrame]::new()
# at funcB, C:\dev\sentry-powershell\tests\capture.tests.ps1: line 363
$regex = 'at (?<Function>[^,]+), (?<AbsolutePath>.+): line (?<LineNumber>\d+)'
$regex = 'at (?<Function>[^,]*), (?<AbsolutePath>.*): line (?<LineNumber>\d*)'
if ($frame -match $regex)
{
$sentryFrame.AbsolutePath = $Matches.AbsolutePath
Expand All @@ -226,12 +226,12 @@ class StackTraceProcessor : SentryEventProcessor

hidden SetScriptInfo([Sentry.SentryStackFrame] $sentryFrame, [System.Management.Automation.CallStackFrame] $frame)
{
if ($null -ne $frame.ScriptName)
if (![string]::IsNullOrEmpty($frame.ScriptName))
{
$sentryFrame.AbsolutePath = $frame.ScriptName
$sentryFrame.LineNumber = $frame.ScriptLineNumber
}
elseif ($null -ne $frame.Position -and $null -ne $frame.Position.File)
elseif (![string]::IsNullOrEmpty($frame.Position) -and ![string]::IsNullOrEmpty($frame.Position.File))
{
$sentryFrame.AbsolutePath = $frame.Position.File
$sentryFrame.LineNumber = $frame.Position.StartLineNumber
Expand All @@ -241,7 +241,7 @@ class StackTraceProcessor : SentryEventProcessor

hidden SetModule([Sentry.SentryStackFrame] $sentryFrame)
{
if ($null -ne $sentryFrame.AbsolutePath)
if (![string]::IsNullOrEmpty($sentryFrame.AbsolutePath))
{
if ($prefix = $this.modulePaths | Where-Object { $sentryFrame.AbsolutePath.StartsWith($_) })
{
Expand All @@ -265,7 +265,7 @@ class StackTraceProcessor : SentryEventProcessor

hidden SetFunction([Sentry.SentryStackFrame] $sentryFrame, [System.Management.Automation.CallStackFrame] $frame)
{
if ($null -eq $sentryFrame.AbsolutePath -and $frame.FunctionName -eq '<ScriptBlock>' -and $null -ne $frame.Position)
if ([string]::IsNullOrEmpty($sentryFrame.AbsolutePath) -and $frame.FunctionName -eq '<ScriptBlock>' -and ![string]::IsNullOrEmpty($frame.Position))
{
$sentryFrame.Function = $frame.Position.Text
}
Expand All @@ -277,7 +277,12 @@ class StackTraceProcessor : SentryEventProcessor

hidden SetContextLines([Sentry.SentryStackFrame] $sentryFrame)
{
if ($null -ne $sentryFrame.AbsolutePath -and $sentryFrame.LineNumber -ge 1 -and (Test-Path $sentryFrame.AbsolutePath -PathType Leaf))
if ([string]::IsNullOrEmpty($sentryFrame.AbsolutePath) -or $sentryFrame.LineNumber -lt 1)
{
return
}

if ((Test-Path $sentryFrame.AbsolutePath -IsValid) -and (Test-Path $sentryFrame.AbsolutePath -PathType Leaf))
{
try
{
Expand Down
2 changes: 1 addition & 1 deletion modules/Sentry/public/Out-Sentry.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function Out-Sentry
{
# Note: we use ScriptStackTrace even though we need to parse it, becaause it contains actual stack trace
# to the throw, not just the trace to the call to this function.
$processor.StackTraceString = $ErrorRecord.ScriptStackTrace -split "[`r`n]+" | Where-Object { $_ -ne 'at <ScriptBlock>, <No file>: line 1' }
$processor.StackTraceString = @($ErrorRecord.ScriptStackTrace -split "[`r`n]+")
$processor.InvocationInfo = $ErrorRecord.InvocationInfo
}

Expand Down
2 changes: 1 addition & 1 deletion scripts/bump-version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
set -euxo pipefail

# Requires powershell: `brew install powershell`
# craft executes this file by convension, passing the new version as the second argument:
# craft executes this file by convention, passing the new version as the second argument:
pwsh ./scripts/bump-version.ps1 "$2"
28 changes: 28 additions & 0 deletions tests/stacktrace-processor.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
BeforeAll {
. "$PSScriptRoot/../modules/Sentry/private/StackTraceProcessor.ps1"
}

Describe 'StackTraceProcessor' {
It 'Parses stack trace properly' {
$event_ = [Sentry.SentryEvent]::new()
$event_.Message = 'Test'
$event_.Level = [Sentry.SentryLevel]::Info

$sut = [StackTraceProcessor]::new()
$sut.StackTraceString = 'at funcB, C:\dev\sentry-powershell\tests\throwing.ps1: line 17
at <ScriptBlock>, <No file>: line 1
at <ScriptBlock>, : line 3' -split "[`r`n]+"
$sut.process($event_)

$frames = $event_.SentryThreads[0].Stacktrace.Frames
$frames[0].Function | Should -Be '<ScriptBlock>'
$frames[0].AbsolutePath | Should -Be ''
$frames[0].LineNumber | Should -Be 3
$frames[1].Function | Should -Be '<ScriptBlock>'
$frames[1].AbsolutePath | Should -Be '<No file>'
$frames[1].LineNumber | Should -Be 1
$frames[2].Function | Should -Be 'funcB'
$frames[2].AbsolutePath | Should -Be 'C:\dev\sentry-powershell\tests\throwing.ps1'
$frames[2].LineNumber | Should -Be 17
}
}

0 comments on commit 638d83a

Please sign in to comment.