Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Diagnostic Dumping Functionality #1443

Draft
wants to merge 39 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
7ffe56c
Add Enhanced Diagnostic Dump Feature with Configurable Output Format…
mdaneri Nov 2, 2024
8cc7d02
add pipe example
mdaneri Nov 2, 2024
6121cb5
various improvements
mdaneri Nov 3, 2024
d362833
Merge branch 'develop' into invoke-podedump
mdaneri Nov 3, 2024
dbf54d9
Fixes and improvements
mdaneri Nov 4, 2024
14afc55
Update Helpers.ps1
mdaneri Nov 4, 2024
34d7b6b
added variables from runspace
mdaneri Nov 5, 2024
f05adda
Update Dump.ps1
mdaneri Nov 5, 2024
b64da21
Added suspend and resume server
mdaneri Nov 5, 2024
7db258a
internazioanalization+ example
mdaneri Nov 5, 2024
88642e8
Update PetData.json
mdaneri Nov 5, 2024
65836b5
Documentation
mdaneri Nov 6, 2024
52574f9
upgrade powershell to '7.2.24'
mdaneri Nov 6, 2024
0bd3f8a
Update Server.Tests.ps1
mdaneri Nov 6, 2024
7dd5f96
Merge branch 'develop' into invoke-podedump
mdaneri Nov 6, 2024
6135e17
fixes
mdaneri Nov 8, 2024
82a6020
Fix CTRL B issue
mdaneri Nov 11, 2024
49f962b
fixes
mdaneri Nov 13, 2024
70a8696
Merge branch 'develop' into invoke-podedump
Badgerati Nov 22, 2024
82c2a28
add net9
mdaneri Nov 22, 2024
6ec63d9
Merge remote-tracking branch 'upstream/develop' into invoke-podedump
mdaneri Nov 23, 2024
e41c207
Merge remote-tracking branch 'upstream/develop' into invoke-podedump
mdaneri Nov 25, 2024
f0c8b45
doc fixes
mdaneri Nov 25, 2024
dc76df3
first review
mdaneri Nov 30, 2024
c8ab57c
second update
mdaneri Dec 1, 2024
79fdf46
suspend http and tcp
mdaneri Dec 1, 2024
900001a
fix tests
mdaneri Dec 2, 2024
526325c
WSS suspension
mdaneri Dec 2, 2024
ce5cc01
Fix suspend
mdaneri Dec 2, 2024
9cdaf4e
Update DebuggerHandler.cs
mdaneri Dec 2, 2024
414d785
minor changes
mdaneri Dec 4, 2024
68f14b0
tentative to use a new terminate token
mdaneri Dec 4, 2024
edc604e
looking good
mdaneri Dec 5, 2024
0081477
Fix the suspend and dump
mdaneri Dec 6, 2024
1791f76
Add progress bar
mdaneri Dec 6, 2024
525638f
debugging
mdaneri Dec 7, 2024
5493c91
suspend resume fixed
mdaneri Dec 7, 2024
3ea0267
fixes
mdaneri Dec 8, 2024
a9dfecf
Changed the runspace enumeration logic
mdaneri Dec 8, 2024
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,6 @@ examples/PetStore/data/PetData.json
packers/choco/pode.nuspec
packers/choco/tools/ChocolateyInstall.ps1
docs/Getting-Started/Samples.md

# Dump Folder
Dump
87 changes: 87 additions & 0 deletions docs/Getting-Started/Debug.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,3 +246,90 @@ Add-PodeSchedule -Name 'TestSchedule' -Cron '@hourly' -ScriptBlock {
```

In this example, the schedule outputs the name of the runspace executing the script block every hour. This can be useful for logging and monitoring purposes when dealing with multiple schedules or tasks.


## Memory Dump for Diagnostics

Pode provides a powerful memory dump feature to capture detailed information during critical failures or fatal exceptions. This feature, triggered by the `Invoke-PodeDump` function, captures the state of your application, including memory usage, runspace details, variables, and stack traces. You can configure the dump format, enable or disable it, and specify the save location. By default, Pode saves the dump in JSON format, but you can also choose from other supported formats.

### Configuring Dump Defaults

To set up default options for the memory dump feature in Pode, you can configure them in the `server.psd1` configuration file. Under the `Server.Debug.Dump` section, you can specify whether to enable the dump, the default format, and the save path. Below is an example configuration for setting up defaults:

```powershell
@{
Server = @{
Debug = @{
Breakpoints = @{
Enable = $true
}
mdaneri marked this conversation as resolved.
Show resolved Hide resolved
Dump = @{
Enable = $true
Format = 'Yaml' # Options: 'json', 'clixml', 'txt', 'bin', 'yaml'
Path = './Dump' # Path to save the dump files
}
}
}
}
```

- **Enable**: Boolean value to enable or disable the memory dump feature.
- **Format**: Specifies the default format for the dump file. Supported formats are `json`, `clixml`, `txt`, `bin`, and `yaml`.
- **Path**: Specifies the directory where the dump file will be saved. If the directory does not exist, it will be created.

### Overriding Default Settings at Runtime

The `Invoke-PodeDump` function allows you to override these defaults at runtime by passing parameters to specify the format and path. This can be useful for debugging in specific cases without altering the default configuration.

```powershell
try {
# Simulate a critical error
throw [System.OutOfMemoryException] "Simulated out of memory error"
}
catch {
# Capture the dump with custom options
Invoke-PodeDump -ErrorRecord $_ -Format 'clixml' -Path 'C:\CustomDump' -Halt
}
```

In this example:
- The memory dump is saved in CLIXML format instead of the default.
- The dump file is saved in the specified directory (`C:\CustomDump`) instead of the default path.
- The `-Halt` switch will terminate the application after the dump is saved.

### Using the Dump Feature in Pode

To use the dump feature effectively in your Pode server, you may want to include it in specific places within your code to capture state information when critical errors occur.

Example usage in a Pode server:

```powershell
Start-PodeServer -EnableBreakpoints {
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http

Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
try {
# Simulate an operation that could fail
$processes = Get-Process -ErrorAction Stop
Write-PodeJsonResponse -Value @{ Process = $processes[0] }
}
catch {
# Invoke a memory dump when a critical error occurs
$_ | Invoke-PodeDump -Halt
}
}
}
```

In this setup, if an error occurs in the route, `Invoke-PodeDump` is called, capturing the current state and halting the application if the `-Halt` switch is set.

### Dumping Options and Best Practices
mdaneri marked this conversation as resolved.
Show resolved Hide resolved

- **Enabling Dump in Production**: Enabling the dump in production can be valuable for diagnosing unexpected failures. However, use it selectively, especially if you are using binary or CLIXML formats, as they can produce large files.
- **Specifying Formats**: Choose a format based on your needs:
- **JSON** and **YAML** are human-readable and suitable for quick inspection.
- **CLIXML** is ideal for preserving object structures, especially when analyzing PowerShell-specific data.
- **Binary** is compact and suitable for raw state captures but requires deserialization for inspection.
- **Setting the Path**: Use a dedicated folder for dump files (e.g., `./Dump`) to keep diagnostic files organized. The default path in the configuration can be overridden at runtime if needed.

With these configurations and usage practices, the memory dump feature in Pode can provide a powerful tool for diagnostics and debugging, capturing critical state information at the time of failure.
12 changes: 12 additions & 0 deletions examples/PetStore/Petstore-OpenApi.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -895,5 +895,17 @@ Some useful links:
Set-PodeOARequest -Parameters ( New-PodeOAStringProperty -Name 'username' -Description 'The name that needs to be deleted.' -Required | ConvertTo-PodeOAParameter -In Path ) -PassThru |
Add-PodeOAResponse -StatusCode 400 -Description 'Invalid username supplied' -PassThru |
Add-PodeOAResponse -StatusCode 404 -Description 'User not found'

Add-PodeRoute -PassThru -Method Get -Path '/dump' -ScriptBlock {
$format = $WebEvent.Query['format']
try {
# Simulate a critical error
throw [System.DivideByZeroException] 'Simulated divide by zero error'
}
catch {
$_ | Invoke-PodeDump -Format $format
}
} | Set-PodeOARouteInfo -Summary 'Dump state' -Description 'Dump the memory state of the server.' -Tags 'dump' -OperationId 'dump'-PassThru |
Set-PodeOARequest -Parameters (New-PodeOAStringProperty -Name 'format' -Description 'Dump export format.' -Enum 'json', 'clixml', 'txt', 'bin', 'yaml' -Default 'json' | ConvertTo-PodeOAParameter -In Query )
}
}
8 changes: 8 additions & 0 deletions examples/PetStore/data/PetData.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@
"petId": 1,
"id": 2,
"quantity": 50
},
"10": {
"id": 10,
"petId": 198772,
"quantity": 7,
"shipDate": "2024-11-04T02:08:54.351Z",
"status": "approved",
"complete": true
}
},
"Scope": [
Expand Down
12 changes: 12 additions & 0 deletions examples/PetStore/server.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,22 @@
Server = @{
Timeout = 60
BodySize = 100MB
Debug = @{
Breakpoints = @{
Enable = $true
}
Dump = @{
Enabled = $true
Format = 'json'
Path = './Dump'
MaxDepth = 6
}
}
}
Web = @{
OpenApi = @{
DefaultDefinitionTag = 'v3.0.3'
}
}

}
6 changes: 6 additions & 0 deletions examples/server.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@
Breakpoints = @{
Enable = $true
}
Dump = @{
Enabled = $true
Format = 'json'
Path = './Dump'
MaxDepth = 6
}
}
}
}
1 change: 1 addition & 0 deletions src/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
'Get-PodeCurrentRunspaceName',
'Set-PodeCurrentRunspaceName',
'Invoke-PodeGC',
'Invoke-PodeDump',

# routes
'Add-PodeRoute',
Expand Down
4 changes: 4 additions & 0 deletions src/Pode.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,8 @@ try {
catch {
throw ("Failed to load the Pode module. $_")
}
finally {
# Cleanup temporary variables
Remove-Variable -Name 'tmpPodeLocale', 'localesPath', 'moduleManifest', 'root', 'version', 'libsPath', 'netFolder', 'podeDll', 'sysfuncs', 'sysaliases', 'funcs', 'aliases', 'moduleManifestPath', 'moduleVersion' -ErrorAction SilentlyContinue
}

31 changes: 24 additions & 7 deletions src/Private/Context.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,19 @@ function New-PodeContext {
'Errors' = 'errors'
}

$ctx.Server.Debug = @{
Breakpoints = @{
Debug = $false
mdaneri marked this conversation as resolved.
Show resolved Hide resolved
}
Dump = @{
Enabled = $true
Format = 'Json'
Path = './Dump'
MaxDepth = 5
Param = @{}
}
}

# check if there is any global configuration
$ctx.Server.Configuration = Open-PodeConfiguration -ServerRoot $ServerRoot -Context $ctx

Expand All @@ -214,10 +227,6 @@ function New-PodeContext {

# debugging
if ($EnableBreakpoints) {
if ($null -eq $ctx.Server.Debug) {
$ctx.Server.Debug = @{ Breakpoints = @{} }
}

$ctx.Server.Debug.Breakpoints.Enabled = $EnableBreakpoints.IsPresent
}

Expand Down Expand Up @@ -320,13 +329,13 @@ function New-PodeContext {

# routes for pages and api
$ctx.Server.Routes = [ordered]@{
# common methods
# common methods
'get' = [ordered]@{}
'post' = [ordered]@{}
'put' = [ordered]@{}
'patch' = [ordered]@{}
'delete' = [ordered]@{}
# other methods
# other methods
'connect' = [ordered]@{}
'head' = [ordered]@{}
'merge' = [ordered]@{}
Expand Down Expand Up @@ -408,6 +417,7 @@ function New-PodeContext {
$ctx.Tokens = @{
Cancellation = [System.Threading.CancellationTokenSource]::new()
Restart = [System.Threading.CancellationTokenSource]::new()
Dump = [System.Threading.CancellationTokenSource]::new()
}

# requests that should be logged
Expand Down Expand Up @@ -900,7 +910,14 @@ function Set-PodeServerConfiguration {
# debug
$Context.Server.Debug = @{
Breakpoints = @{
Enabled = [bool]$Configuration.Debug.Breakpoints.Enable
Enabled = [bool](Protect-PodeValue -Value $Configuration.Debug.Breakpoints.Enable -Default $Context.Server.Debug.Breakpoints.Enable)
}
Dump = @{
Enabled = [bool](Protect-PodeValue -Value $Configuration.Debug.Dump.Enabled -Default $Context.Server.Debug.Dump.Enabled)
Format = [string] (Protect-PodeValue -Value $Configuration.Debug.Dump.Format -Default $Context.Server.Debug.Dump.Format)
Path = [string] (Protect-PodeValue -Value $Configuration.Debug.Dump.Path -Default $Context.Server.Debug.Dump.Path)
MaxDepth = [int] (Protect-PodeValue -Value $Configuration.Debug.Dump.MaxDepth -Default $Context.Server.Debug.Dump.MaxDepth)
Param = @{}
}
}
}
Expand Down
Loading
Loading