diff --git a/example.ps1 b/example.ps1 new file mode 100644 index 0000000..fab56a3 --- /dev/null +++ b/example.ps1 @@ -0,0 +1,16 @@ +$test = 1+1 +$test = 1+1 +sleep -Milliseconds 34 + +$test = 1+1 +$test = 1+1 +# comments +foreach($n in 1..10) +{ + "test string" + sleep -Milliseconds 3 +} +sleep -Milliseconds 12 +sleep -Milliseconds 5 +$test = 1+1 +$test = 1+1 \ No newline at end of file diff --git a/profiler.ps1 b/profiler.ps1 new file mode 100644 index 0000000..6ac9493 --- /dev/null +++ b/profiler.ps1 @@ -0,0 +1,85 @@ + +function global:Invoke-PSProfile +{ + [cmdletbinding()] + param([System.Management.Automation.LineBreakpoint]$InputObject) + $Global:PSProfile['queue'].Enqueue(@{ + Breakpoint = $_ + ElapsedMilliseconds = $Global:PSProfile['time'].ElapsedMilliseconds + }) +} + +function New-PSProfile +{ + [CmdletBinding()] + param( + [string[]] + $Path, + + [scriptblock] + $CommandScript + + ) + + $breakPoint = @() + $fileMap = @{} + + foreach($file in (Resolve-Path $Path)) + { + $fileMap[$file.Path] = @( Get-Content -Path $file | %{@{line=$_}}) + + $lines = $fileMap[$file.Path].count + $breakPoint += Set-PSBreakpoint -Script $file -Line (1..$lines) -Action {global:Invoke-PSProfile -InputObject $_ } + } + + $Global:PSProfile = @{} + $Global:PSProfile['queue'] = New-Object System.Collections.Queue + $Global:PSProfile['time'] = [System.Diagnostics.Stopwatch]::StartNew() + + [void] $CommandScript.Invoke() + + Remove-PSBreakpoint $breakpoint + + foreach($node in $Global:PSProfile['queue'].GetEnumerator()) + { + $record = $fileMap[$node.Breakpoint.Script][$node.Breakpoint.Line-1] + if($lastNode) + { + $duration = $node.ElapsedMilliseconds - $lastNode.ElapsedMilliseconds + } + else + { + $duration = $node.ElapsedMilliseconds + } + + + if($lastRecord) + { + $lastRecord.TotalRuntime += $duration + $lastRecord.HitCount += 1 + $lastRecord.AVG = $lastRecord.TotalRuntime / $lastRecord.HitCount + + if($lastRecord.Min -eq $null -or $lastRecord.Min -gt $duration) + { + $lastRecord.Min = $duration + } + if($lastRecord.Max -eq $null -or $lastRecord.Max -lt $duration) + { + $lastRecord.Max = $duration + } + } + + $lastRecord = $record + $lastNode = $node + } + + foreach($script in $fileMap.Keys) + { + foreach($line in $fileMap[$script]) + { + "[{0:0000},{1:00},{2:000}]`t{3}" -f $line.TotalRuntime,$line.HitCount,$line.AVG,$line.line + } + } +} + +New-PSProfile -Path .\example.ps1 -CommandScript {.\example.ps1} -OutVariable results diff --git a/profiler2.ps1 b/profiler2.ps1 new file mode 100644 index 0000000..73d6320 --- /dev/null +++ b/profiler2.ps1 @@ -0,0 +1,150 @@ + + +class ScriptLine +{ + [float] $Milliseconds = 0 + [float] $HitCount = 0 + [float] $Min = [float]::MaxValue + [float] $Max = [float]::MinValue + [float] $Average = 0 + [int] $LineNumber + [string] $Path + [string] $Text + + [void]AddExecutionTime([float]$Milliseconds) + { + $this.Milliseconds += $Milliseconds + $this.HitCount += 1 + $this.Average = $this.Milliseconds / $this.HitCount + + if($Milliseconds -lt $this.Min) + { + $this.Min = $Milliseconds + } + + if($Milliseconds -gt $this.Max) + { + $this.Max = $Milliseconds + } + } + + [string] ToString() + { + $values = @( + $this.Milliseconds + $this.HitCount + $this.Average + $this.Text + ) + return ("[{0:0000}ms,{1:0000},{2:0000}ms] {3}" -f $values) + } +} + +function global:Invoke-PSProfile +{ + [cmdletbinding()] + param([System.Management.Automation.LineBreakpoint]$InputObject) + $Global:PSProfile['queue'].Enqueue(@{ + Breakpoint = $_ + ElapsedMilliseconds = $Global:PSProfile['time'].ElapsedMilliseconds + }) +} + +function New-PSProfile +{ + [CmdletBinding()] + param( + [string[]] + $Path, + + [scriptblock] + $CommandScript + + ) + + $breakPoint = @() + $fileMap = @{} + + foreach($file in (Resolve-Path $Path)) + { + $fileMap[$file.Path] = @( Get-Content -Path $file | %{[ScriptLine]@{text=$_;path=$file.path}}) + + $lines = $fileMap[$file.Path].count + $breakPoint += Set-PSBreakpoint -Script $file -Line (1..$lines) -Action {global:Invoke-PSProfile -InputObject $_ } + } + + + [void] $CommandScript.Invoke() + + Remove-PSBreakpoint $breakpoint + + #$fileMap | ConvertTo-Json + + + foreach($node in $Global:PSProfile['queue'].GetEnumerator()) + { + $record = $fileMap[$node.Breakpoint.Script][$node.Breakpoint.Line-1] + $record.LineNumber = $node.Breakpoint.Line - 1 + + if($lastNode) + { + $duration = $node.ElapsedMilliseconds - $lastNode.ElapsedMilliseconds + } + else + { + $duration = $node.ElapsedMilliseconds + } + + + if($lastRecord) + { + $lastRecord.AddExecutionTime($duration) + } + + $lastRecord = $record + $lastNode = $node + } + + foreach($script in $fileMap.Keys) + { + foreach($line in $fileMap[$script]) + { + Write-Output $line + } + } +} + +New-PSProfile -Path .\example.ps1 -CommandScript {.\example.ps1} -OutVariable results + +class ScriptProfiler { + + static [System.Collections.Queue] $Queue + static [System.Diagnostics.Stopwatch] $Timer + + static [void] Start() + { + [ScriptProfiler]::Queue = New-Object System.Collections.Queue + [ScriptProfiler]::Timer = [System.Diagnostics.Stopwatch]::StartNew() + } + + static [void] RecordExecution ([System.Management.Automation.LineBreakpoint]$InputObject) + { + [ScriptProfiler]::Queue.Enqueue(@{ + Breakpoint = $InputObject + ElapsedMilliseconds = [ScriptProfiler]::Timer.ElapsedMilliseconds + }) + } +} + +$Global:PSProfile = @{} + $Global:PSProfile['queue'] = New-Object System.Collections.Queue + $Global:PSProfile['time'] = [System.Diagnostics.Stopwatch]::StartNew() + function global:Invoke-PSProfile + { + [cmdletbinding()] + param([System.Management.Automation.LineBreakpoint]$InputObject) + $Global:PSProfile['queue'].Enqueue(@{ + Breakpoint = $_ + ElapsedMilliseconds = $Global:PSProfile['time'].ElapsedMilliseconds + }) + } \ No newline at end of file diff --git a/profiler3.ps1 b/profiler3.ps1 new file mode 100644 index 0000000..bb10fb7 --- /dev/null +++ b/profiler3.ps1 @@ -0,0 +1,130 @@ + +class ScriptProfiler { + + static [System.Collections.Queue] $Queue + static [System.Diagnostics.Stopwatch] $Timer + + static [void] Start() + { + [ScriptProfiler]::Queue = New-Object System.Collections.Queue + [ScriptProfiler]::Timer = [System.Diagnostics.Stopwatch]::StartNew() + } + + static [void] RecordExecution ([System.Management.Automation.LineBreakpoint]$InputObject) + { + [ScriptProfiler]::Queue.Enqueue(@{ + Breakpoint = $InputObject + ElapsedMilliseconds = [ScriptProfiler]::Timer.ElapsedMilliseconds + }) + } +} + +class ScriptLine +{ + [float] $Milliseconds = 0 + [float] $HitCount = 0 + [float] $Min = [float]::MaxValue + [float] $Max = [float]::MinValue + [float] $Average = 0 + [int] $LineNumber + [string] $Path + [string] $Text + + [void]AddExecutionTime([float]$Milliseconds) + { + $this.Milliseconds += $Milliseconds + $this.HitCount += 1 + $this.Average = $this.Milliseconds / $this.HitCount + + if($Milliseconds -lt $this.Min) + { + $this.Min = $Milliseconds + } + + if($Milliseconds -gt $this.Max) + { + $this.Max = $Milliseconds + } + } + + [string] ToString() + { + $values = @( + $this.Milliseconds + $this.HitCount + $this.Average + $this.Text + ) + return ("[{0:0000}ms,{1:0000},{2:0000}ms] {3}" -f $values) + } +} + + +function Get-Chronometer +{ + [CmdletBinding()] + param( + [string[]] + $Path, + + [scriptblock] + $CommandScript + + ) + + $breakPoint = @() + $fileMap = @{} + + foreach($file in (Resolve-Path $Path)) + { + $fileMap[$file.Path] = @( Get-Content -Path $file | %{[ScriptLine]@{text=$_;path=$file.path}}) + + $lines = $fileMap[$file.Path].count + $breakPoint += Set-PSBreakpoint -Script $file -Line (1..$lines) -Action {[ScriptProfiler]::RecordExecution( $_) } + } + + [ScriptProfiler]::Start() + [void] $CommandScript.Invoke() + + Remove-PSBreakpoint $breakpoint + + #$fileMap | ConvertTo-Json + + + foreach($node in [ScriptProfiler]::Queue.GetEnumerator()) + { + $record = $fileMap[$node.Breakpoint.Script][$node.Breakpoint.Line-1] + $record.LineNumber = $node.Breakpoint.Line - 1 + + if($lastNode) + { + $duration = $node.ElapsedMilliseconds - $lastNode.ElapsedMilliseconds + } + else + { + $duration = $node.ElapsedMilliseconds + } + + + if($lastRecord) + { + $lastRecord.AddExecutionTime($duration) + } + + $lastRecord = $record + $lastNode = $node + } + + foreach($script in $fileMap.Keys) + { + foreach($line in $fileMap[$script]) + { + Write-Output $line + } + } +} + +Get-Chronometer -Path .\example.ps1 -CommandScript {.\example.ps1} -OutVariable results + + +$results | % ToString \ No newline at end of file diff --git a/tests.ps1 b/tests.ps1 new file mode 100644 index 0000000..2b04689 --- /dev/null +++ b/tests.ps1 @@ -0,0 +1,30 @@ +$script:test2 = 20 + +Set-PSBreakpoint -Script .\example.ps1 -Line (1..9) -OutVariable breakpoint -Action {global:Set-BPInfo $_} +.\example.ps1 + +global:Get-BPInfo | fl * + +global:Set-BPInfo + +$script:test2 + +$breakpoint | Get-Member + +Remove-PSBreakpoint $breakpoint + +function global:Set-BPInfo { + param($value) + $global:BPinfo += 1 + $global:BPvalue = $value +} + +function global:Get-BPInfo { + $global:BPvalue +} + +# per file +# per line +# hit count +# execution +# the command before \ No newline at end of file