-
Notifications
You must be signed in to change notification settings - Fork 0
Profiler: Analysis
DebugTools provides a number of cmdlets to analyze the data collected in a profiler trace. These cmdlets break down into the following categories:
- Methods
- Frames
- Exceptions
By default, all analysis cmdlets will operate against the implicit profiler session if no session is specified. Sessions can be retrieved using the Get-DbgProfiler
cmdlet
Get-DbgProfiler -Id 1001 | Get-DbgProfilerMethod
For more information on the implicit profiler session, please see Session Management.
Unfortunately, as some cmdlet names are long to describe what they do, it can be a bit confusing trying to remember and type out the exact name of a cmdlet. It is recommended to use PowerShell auto-completion when typing out some of the longer cmdlet names in DebugTools. i.e. you can simply do
Show-Db <tab>
to auto-complete Show-DbgProfilerStackTrace
Once a target process has been launched containing the DebugTools Profiler, regardless of whether or not you are actively recording frame transitions, all methods JITted will be reported back to the profiler controller process. All frames currently known to the profiler controller can be listed using the Get-DbgProfilerMethod
cmdlet
C:\> Get-DbgProfilerMethod
FunctionID ModuleName TypeName MethodName
---------- ---------- -------- ----------
0x7FFCBEFADF60 System.Management.Automation.dll <GetDefaultAvailableModuleFiles>d__2 <>m__Finally2
0x7FFCBEE10530 System.Management.Automation.dll System.Management.Automation.PowerShell CoreInvoke
...
The Get-DbgProfilerMethod
provides a number of parameters that can be used to search for target methods. The simplest way to search using Get-DbgProfilerMethod
is to specify a wildcard expression specifying part of the fully qualified method name (including namespace, type and method name).
C:\> Get-DbgProfilerMethod *childitem*
Get-DbgProfilerMethod
provides a number of parameters for performing more specific types of filtering: you can filter based on the -ModuleName
, -TypeName
, -MethodName
and even -Exclude
certain name patterns that you're not interested in.
By default, Get-DbgProfiler
will search all methods that have been invoked since the target process was started. To search only for methods that were invoked in the last trace, specify the -LastTrace
parameter.
C:\> Get-DbgProfilerMethod -TypeName *child* -LastTrace
Searching for methods from the last trace can sometimes be a better idea than trying to search for frames, or even drawing a visual stack trace. Once you've identified some potential methods you may be interested, you can jump to cmdlets like Show-DbgProfilerStackTrace
which can sometimes take some time to run depending on the number of captured frames that need to be filtered.
To view the parameters available on Get-DbgProfilerMethod
do Get-Help Get-DbgProfilerMethod
When a trace is stopped, the data collected in that trace is cached on the profiler session. The root frames of this trace can be retrieved using the Get-DbgProfilerLastStackTrace
cmdlet
C:\> Get-DbgProfilerLastStackTrace
ThreadId ThreadName Parent Children
-------- ---------- ------ --------
19516 Pipeline Execution Thread {System.Management.Automation.Runspaces.Pipeline...
...
The data contained returned from this cmdlet can then potentially be cached and then passed to other cmdlets that analyze frames. If no frame is specified to a frame analysis cmdlet, an analysis will automatically be performed starting from the root frame of each thread in the last recorded trace. Due to the amount of memory a trace can potentially consume, if you wish to hold onto the output of a previous trace for potentially comparing two traces, you should keep an eye on your process/system memory, as this data will not be garbage collected as long as you are holding a reference to it.
Frames can be searched for using the Get-DbgProfilerStackFrame
cmdlet. The Get-DbgProfilerStackFrame
cmdlet supports specifying a wide array of parameters for narrowing down its search results.
# List all frames whose fully qualified name includes "childitem"
C:\> Get-DbgProfilerStackFrame *childitem*
Display MethodInfo
------- ----------
GetChildItemCommand.get_Force Microsoft.PowerShell.Commands.GetChildItemCommand.get_Force
GetChildItemCommand.get_Include Microsoft.PowerShell.Commands.GetChildItemCommand.get_Include
GetChildItemCommand.get_Exclude Microsoft.PowerShell.Commands.GetChildItemCommand.get_Exclude
...
To get a more visual feel for a stack trace, the Show-DbgProfilerStackTrace
can be used. Show-DbgProfilerStackTrace
supports all of the same filtering parameters that Get-DbgProfilerStackFrame
supports.
Filter cmdlets provide many parameters which can be used to drill down to just the stacks containing frames you are interested in. Except where noted, all parameters listed allow specifying an array of potential values to match against. All frames supported by a given cmdlet can be shown within PowerShell itself by doing Get-Help <cmdlet>
Get-Help Show-DbgProfilerStackTrace
There are three types of parameters that can be specified
- Inclusion parameters indicate whether a given frame should be included/excluded based on its full method name (namespace + type + method name)
- Value parameters filter for parameters/return values that match a specific value/expression
- Special parameters perform special functions for certain cmdlets
Inclusion parameters are treated as a logical AND
. As such, the following invocation
C:\> Show-DbgProfilerStackTrace *process* -Unmanaged
shows the stack traces of all unmanaged frames whose full method name contains "process"
Parameter | Description |
---|---|
-Include |
Include frames whose combined namespace, type and method name match the specified wildcard. Can be positionally specified at index 0 |
-Exclude |
Exclude frames whose combined namespace, type and method name match the specified wildcard. |
-CalledFrom |
Include frames whose ancestor's combined namespace, type and method name match the specified wildcard. |
-Unmanaged |
Include frames that were reported to be transitions to or from unmanaged code. This parameter is a switch (-Unmanaged ) |
-MethodModuleName |
Include frames whose full module path (i.e. DLL/EXE filename, excluding path) matches the specified wildcard |
-MethodTypeName |
Include frames whose type name (including namespace) matches the specified wildcard. This parameter can be used in lieu of -Include to specifically target an expression against the type name |
-MethodName |
Include frames whose method name matches the specified wildcard. This parameter can be used in lieu of -Include to specifically target an expression against the method name without looking at the type/namespace |
-ParentMethodModuleName |
Similar to -MethodModuleName but includes frames whose parent frame's method's module matches the specified wildcard expression |
-ParentMethodTypeName |
Similar to -MethodTypeName but includes frames whose parent frame's method's type (including namespace) matches the specified wildcard expression |
-ParentMethodName |
Similar to -MethodName but includes frames whose parent frame's method's name matches the specified wildcard expression without looking at the type/namespace |
Value parameters are treated as a logical OR
. As soon as DebugTools matches any specified value, the frame will be included. It is not currently possible to filter value parameters using logical AND
. If you would like to see this feature, please open an issue!
Parameter | Description |
---|---|
-VoidValue |
Includes frames containing a parameter/return value (or object member of said value) of type void . This parameter is a switch (-VoidValue ) |
-BoolValue |
Includes frames containing a parameter/return value (or object member of said value) with the specified bool value |
-CharValue |
Includes frames containing a parameter/return value (or object member of said value) with the specified char value |
-SByteValue |
Includes frames containing a parameter/return value (or object member of said value) with the specified sbyte value |
-ByteValue |
Includes frames containing a parameter/return value (or object member of said value) with the specified byte value |
-Int16Value |
Includes frames containing a parameter/return value (or object member of said value) with the specified short value |
-UInt16Value |
Includes frames containing a parameter/return value (or object member of said value) with the specified ushort value |
-Int32Value |
Includes frames containing a parameter/return value (or object member of said value) with the specified int value |
-UInt32Value |
Includes frames containing a parameter/return value (or object member of said value) with the specified uint value |
-Int64Value |
Includes frames containing a parameter/return value (or object member of said value) with the specified long value |
-UInt64Value |
Includes frames containing a parameter/return value (or object member of said value) with the specified ulong value |
-FloatValue |
Includes frames containing a parameter/return value (or object member of said value) with the specified float value |
-DoubleValue |
Includes frames containing a parameter/return value (or object member of said value) with the specified double value |
-IntPtrValue |
Includes frames containing a parameter/return value (or object member of said value) with the specified IntPtr value |
-UIntPtrValue |
Includes frames containing a parameter/return value (or object member of said value) with the specified UIntPtr value |
-StringValue |
Includes frames containing a parameter/return value (or object member of said value) that matches the specified wildcard expression |
-ClassTypeName |
Includes frames containing a parameter/return value (or object member of said value) that is a class or struct whose namespace + type name matches the specified wildcard expression. This parameter matches both classes and structs, and is also aliased as StructTypeName . |
Parameter | Description |
---|---|
-Unique |
Limits the output to only include the first frame targeting a given method, as identified by its internal FunctionID . This parameter is evaluated after all other filters have executed. This parameter is a switch (-Unique ) |
Parameter | Description |
---|---|
-Sequence |
Returns the frame with the specified sequence |
Parameter | Description |
---|---|
-ExcludeNamespace |
Omit the namespace on all methods/types emitted. This parameter is a switch (-ExcludeNamespace ) |
-IncludeSequence |
Include the unique method sequence on all methods emitted. This parameter is a switch. |
-IncludeModule |
Include the module name on all methods emitted. This parameter is a switch. |
-Simple |
Shorthand for specifying -Unique and -ExcludeNamespace
|
-Depth |
Specifies how many frames deep the cmdlet should display frames when invoked with no filters. When filters are specified, the entirety of the stack trace will always be shown |
-Unlimited |
When no filters have been specified, specifies that -Depth should be ignored and all frames should displayed. Note that when displaying very very large stack traces, emitting extreme amounts of fancily formatted text to the PowerShell console can cause it to glitch out. This parameter is a switch |
Parameter | Description |
---|---|
-DisableProgress |
Disables the progress bar that informs you how to disable tracing (via Ctrl+C ) to prevent it from overlapping the output that is being emitted from the cmdlet. This parameter is a switch. |
Cmdlets capable of filtering frames (including Get-DbgProfilerStackFrame
, Watch-DbgProfilerStack
Show-DbgProfilerStackTrace
) allow specifying that only -Unique
frames should be displayed. However, "uniqueness" can mean different things depending on your perspective.
To illustrate this, consider the following stack trace, with sequence numbers listed in brackets after each frame
first (1)
├─second (2)
│ └─third (3)
│ ├─second (4)
│ │ └─fourth (5)
│ └─second (6)
│ └─fourth (7)
└─second (8)
In this trace, the method second
occurs in four different frames
- Twice, being called from
first
- Twice, being called from
third
which itself called from a separate invocation ofsecond
There are several different strategies that can potentially be used for determining "uniqueness" of a frame
Strategy | Pro | Con | Example |
---|---|---|---|
Include the first occurrence of a method executed globally | Exactly one occurrence of second will occur in the trace |
Methods such as fourth which only occur on a subsequent, or inner invocation, will be lost |
first(1) └─second(2) |
Analyze the entire hierarchy a frame and all its children for determining uniqueness | |||
Only compare frames at the same level under the same parent | |||
Make child frames that would have been lost children of the first unique parent we are including | All frames are included in the output | Frames will be listed under a parent frame that isn't actually their parent |
Currently, DebugTools uses Strategy 1, taking the first occurrence of a method executed globally. Depending on how DebugTools' filter cmdlets are invoked, different behavior can be exhibited.
There are two phases of analysis that occur when DebugTools attempts to filter a stack trace
- Frame Analysis
- Tree Reconstruction
When only the -Unique
parameter is specified,
Show-DbgProfilerStackTrace -Unique
DebugTools skips the entire Frame Analysis stage entirely, and proceeds straight to the Tree Reconstruction Phase, where the first frame encountered that uses each method will be included in the new stack trace that is built.
If any additional parameters are specified, DebugTools will search for all frames that match the specified predicate, and then construct a new tree with all matched frames as leaf nodes.
Show-DbgProfilerStackTrace -Unique *childitem*
When determining uniqueness, the uniqueness engine will prefer the parents of any matches frames over standalone frames to ensure a given frame is not listed twice. For example, consider the following scenario filtering for unique methods that are like *movenext*
30600
└─System.Diagnostics.Tracing.EventListener.DisposeOnShutdown (1)
├─Enumerator.MoveNext (4)
└─Enumerator.MoveNext (36)
└─Enumerator.MoveNextRare (37)
Methods Enumerator.MoveNext
and Enumerator.MoveNextRare
are found to match the predicate. The first invocation of each is selected. But since a separate invocation of Enumerator.MoveNext
calls Enumerator.MoveNextRare
, we could inadvertently list Enumerator.MoveNext
twice, which is not what we want! As such, DebugTools will analyze the parents of MoveNext (4)
and MoveNextRare (37)
, see that MoveNext (36)
will be included, and use that instead of MoveNext (4)
giving us the final output of
30600
└─System.Diagnostics.Tracing.EventListener.DisposeOnShutdown (1)
└─Enumerator.MoveNext (36)
└─Enumerator.MoveNextRare (37)
If two unique methods B
and C
are each executed from a different invocation of function A
however, function A
will be listed twice in the output
# Even though -Unique methods were listed, A is listed twice due to B and C being called from
# separate invocation of A
1000
├─A
│ └─B
└─A
└─C
A list of all exceptions captured in the last trace can be displayed via the Get-DbgProfilerException
cmdlet
C:\> Get-DbgProfilerException
ThreadId Type Sequence Status ThrownFrame HandledFrame
-------- ---- -------- ------ ----------- ------------
3640 System.IO.FileNotFoundException 1 Caught ExecutionContext.LoadAssembly ExecutionContext.LoadAssembly
...
Exceptions output can be limited to those that occurred on a specific -ThreadId
or those that match a specific -Type
C:\> Get-DbgProfilerException *invalidoperation*
Traces can be saved to a file for future viewing using the Export-DbgProfilerStackTrace
cmdlet
Note: DebugTools does not currently support exporting exception details, only frame/method details
C:\> Export-DbgProfilerStackTrace C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 31/03/2023 1:49 PM 658302 StackTrace_42004_devenv_2023-03-31_1349h.gz
Export-DbgProfilerStackTrace
will automatically attempt to guess whether the path you've specified refers to a file or a directory. As stack traces can very quickly get quite large, by default Export-DbgProfilerStackTrace
will GZip the file up as it is written. If you wish to inspect or modify the output file, specify -Raw
to emit the trace as an XML file instead.
C:\> Export-DbgProfilerStackTrace C:\export -Raw
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 31/03/2023 1:58 PM 10523613 export.xml
Note that files emitted as raw XML can be over 15x the size of an equivalent GZip file.
To export a specific stack trace within a larger trace that has been collected, the Get-DbgProfilerStackFrame
cmdlet can be used. By default, Get-DbgProfilerStackFrame
will emit all frames that match the specified filters. This is in contrast to Show-DbgProfilerStackTrace
which performs the same filtering but then constructs a virtual tree containing just the frames that will need to be displayed. Get-DbgProfilerStackFrame
can be used to gain access to these virtual trees via the use of the -Roots
parameter
# Get all individual frames that contained the string value "1+1"
C:\> Get-DbgProfilerStackFrame -StringValue "1+1"
# Get the root frame(s) of a virtual tree that descends down to all nodes containing
# the string value "1+1". Multiple trees may be emitted when matches occurred across
# multiple threads
C:\> Get-DbgProfilerStackFrame -StringValue "1+1" -Roots
# Export the virtual tree(s) of all nodes containing the string value "1+1"
# to C:\plus.gz
C:\> Get-DbgProfilerStackFrame -StringValue "1+1" -Roots | Export-DbgProfilerStackTrace C:\plus
To import a previously exported stack trace, use the Import-DbgProfilerStackTrace
cmdlet
C:\> Import-DbgProfilerStackTrace C:\export.xml
Import-DbgProfilerStackTrace
will automatically detect whether the specified file is an XML or GZip file. Files must end in either *.xml
or *.gz
or Import-DbgProfilerStackTrace
will not be able to read them.
Imported stack traces will generate a pseudo ProfilerSession
that is stored in the current session state, similar to profiler sessions normally created via Start-DbgProfiler
. You can then perform analytics by executing all normal cmdlets that operate on sessions/frames as described in this article.