Git Product home page Git Product logo

realmon's Introduction

GCRealTimeMon

This is a monitoring tool that tells you when GCs happen in a .NET process and characteristics about these GCs.

Right now it's super simple - given a PID or process name it will show you a few things about GCs as they happen in that process.

Command Line Arguments

Command Line Argument Name Description
n Name of the Process Grabs the first process with the name matching that with the one specified with this argument
p Process Id Process Id to monitor GC for
c Path of the Configuration File Path to the configuration file used by the monitor. This file is a YAML file. By default, it's the Default.yaml file is loaded. If no argument is specified, the command line prompt to create the config will be displayed and the default config will be overwritten with the inputted values; this new config will be used for this session.
g Path of the New Configuration File Path to the configuration file that'll be created using a command line prompt, persisted to the given path and used by the monitor for this particular session. This file will be of the YAML format.
s Flag to enable Call Stacks Passing the 's' command line option will enable displaying call stacks for induced GCs and large object allocations. This mode is currently only available for Windows and only if realmon is run with admin privileges.

Runtime Keys

Key Action
s Prints detailed stats of the last collection and the state of each generation

Example Usage

GCRealTimeMon -p 14028

or

GCRealTimeMon -n devenv

Example output:

------- press s for current stats or any other key to exit -------

Monitoring process with name: Samples.AspNet5 and pid: 45044
GC#     index |            type |   gen | pause (ms) |                reason |
------------------------------------------------------------------------------
GC#         1 | NonConcurrentGC |     0 |       7.45 |            AllocSmall |
GC#         2 | NonConcurrentGC |     1 |      17.88 |            AllocSmall |
GC#         3 | NonConcurrentGC |     0 |       3.20 |            AllocSmall |
------------------------------------------------------------------------------
Heap Stats as of 2021-11-08 03:15:30Z (Run 1 for gen 0):
  Heaps: 16
  Handles: 2,015
  Pinned Obj Count: 8
  Last Run Stats:
    Total Heap: 15,846,992 Bytes
      Gen 0:               384 Bytes
      Gen 1:        10,718,432 Bytes
      Gen 2:               384 Bytes
      Gen 3:         4,358,056 Bytes
      Gen 4:           769,736 Bytes
------------------------------------------------------------------------------

Configuration

The configuration file is a YAML based file currently with the following sections:

  • columns: The columns to display.
  • available_columns: All columns that are available to display.
  • stats_mode: Configurations related to the heap stats.
    • timer: Specifying this with a period magnitude and type that dictates the candence of the timer that prints the heap stats.
      • the period type can either be in minutes 'm' or seconds 's'.
      • the period magnitude has to be as an int.
      • Examples:
        • "timer" : "5m" # 5 minutes
        • "timer" : "20s" # 20 seconds
    • A full example with the Heap printing every 30 seconds can be found here
  • display_conditions: Conditions via which info about each GC is displayed.
    • min gc duration (msec): Specifying this value will filter GCs with pause durations less than the said value.
    • Examples:
      • min gc duration (msec) : 200
      • min gc duration (msec) : 10.0
      • min gc duration (msec) : 200.254

Currently, the available columns are:

Column Name Full Name / Description Trace Event Property
index The GC Index. TraceGC.Number
gen The Generation. TraceGC.Generation
type The Type of GC. TraceGC.Type
reason Reason for GC. TraceGC.Reason
suspension time (ms) The time in milliseconds that it took to suspend all threads to start this GC TraceGC.SuspendDurationMSec
pause time (ms) The time managed threads were paused during this GC, in milliseconds TraceGC.PauseDurationMSec
pause time (%) The amount of time that execution in managed code is blocked because the GC needs exclusive use to the heap. For background GCs this is small. TraceGC.PauseTimePercentageSinceLastGC
gen0 alloc (mb) Amount allocated in Gen0 since the last GC occurred in MB. TraceGC.UserAllocated[(int)Gens.Gen0]
gen0 alloc rate The average allocation rate since the last GC. (TraceGC.UserAllocated[(int)Gens.Gen0] * 1000.0) / TraceGC.DurationSinceLastRestartMSec
peak size (mb) The size on entry of this GC (includes fragmentation) in MB. TraceGC.HeapSizePeakMB
after size (mb) The size on exit of this GC (includes fragmentation) in MB. TraceGC.HeapSizeAfterMB
peak/after Peak / After TraceGC.HeapSizePeakMB / TraceGC.HeapSizeAfterMB
promoted (mb) Memory this GC promoted in MB. TraceGC.PromotedMB
gen0 size (mb) Size of gen0 at the end of this GC in MB. TraceGC.GenSizeAfterMB(Gens.Gen0)
gen0 survival rate The % of objects in Gen0 that survived this GC. TraceGC.SurvivalPercent(Gens.Gen0)
gen0 frag ratio The % of fragmentation on Gen0 at the end of this GC. TraceGC.GenFragmentationPercent(Gens.Gen0)
gen1 size (mb) Size of gen1 at the end of this GC in MB. TraceGC.GenSizeAfterMB(Gens.Gen1)
gen1 survival rate The % of objects in Gen1 that survived this GC. Only available if we are doing a gen1 GC. TraceGC.SurvivalPercent(Gens.Gen1)
gen1 frag ratio The % of fragmentation on Gen1 at the end of this GC. TraceGC.GenFragmentationPercent(Gens.Gen1)
gen2 size (mb) Size of Gen2 in MB at the end of this GC. TraceGC.GenSizeAfterMB(Gens.Gen2)
gen2 survival rate The % of objects in Gen2 that survived this GC. Only available if we are doing a gen2 GC. TraceGC.SurvivalPercent(Gens.Gen2)
gen2 frag ratio The % of fragmentation on Gen2 at the end of this GC. TraceGC.GenFragmentationPercent(Gens.Gen2)
LOH size (mb) Size of Large object heap (LOH) at the end of this GC in MB. TraceGC.GenSizeAfterMB(Gens.LargeObj)
LOH survival rate The % of objects in the large object heap (LOH) that survived the GC. Only available if we are doing a gen2 GC. TraceGC.SurvivalPercent(Gens.LargeObj)
LOH frag ratio The % of fragmentation on the large object heap (LOH) at the end of this GC. TraceGC.GenFragmentationPercent(Gens.LargeObj)
finalize promoted (mb) The size of finalizable objects that were discovered to be dead and so promoted during this GC, in MB. TraceGC.HeapStats.FinalizationPromotedSize / 1000000.0
pinned objects Number of pinned objects observed in this GC. TraceGC.HeapStats.PinnedObjectCount

Call Stacks

In an effort to aid with perf diagnosis, call stacks are currently available for induced GCs and large object allocations on Windows, only. Enabling call stacks requires the process to run with administrator privileges.

NOTE: There might be a significant overhead when requesting stacks as more providers are enabled to retrieve call stacks and resolve symbols for the requested call stacks.

Troubleshooting

Symbols aren't properly resolved

The symbol resolution logic relies on the fact that _NT_SYMBOL_PATH is populated with the appropriate symbol path(s). More details on how this can be troubleshooted here. If the _NT_SYMBOL_PATH environment variable isn't set, the symbol path will default to the following to ensure symbols are resolved: ;SRV*C:\Symbols*https://msdl.microsoft.com/download/symbols;SRV*C:\Symbols*https://nuget.smbsrc.net;SRV*C:\Symbols*https://referencesource.microsoft.com/symbols

Getting the following exception: Unhandled exception. System.Runtime.InteropServices.COMException (0x800705AA): Insufficient system resources exist to complete the requested service. (0x800705AA)

This exception is thrown as a result of session exhaustion i.e. enough sessions are being created that you're unable to create another. You can troubleshoot this issue in 2 ways:

  1. Rebooting your machine will clear up any zombie sessions.
  2. Manually stopping sessions
    1. Open up a command prompt / powershell instance in admin mode.
    2. Enter the following command to check to view the list of all the sessions: logman query -ets.
    3. Stop dangling session(s) using: logman -ets stop <NameOfSession>.

More details on this issue can be found here.

Adding New Columns

The process to add a new column from the TraceGC event is the following:

  1. Include the column name in the available_columns in the Default.yaml config.
  2. Define a ColumnInfo object in the ColumnInfoMap with the following properties:
    1. The name
    2. Alignment
    3. A Func<TraceGC, object> that looks up an object in via a TraceGC event.
    4. Format (optional)
  3. Optionally add corresponding unit tests.
  4. Update the documentation here with the new column.

Theming

By default, output is colored and formatted using Spectre.Console. If stdout is being redirected, then plain text formatting is used instead. A dark theme is used by default unless the console background color is detected as white, then a light theme is used. You can configure all colors using the following fields in the theme yaml node:

(All color values can be defined using Spectre Color strings or hex values.)

Name Description
use_plain_text Set to true to force the output to be plain text. Default is false. Note that the NO_COLOR initiative is also supported.
gc_table_header_color The color for the GC table headers.
gen0_row_color The color for row entries of Gen 0 events in the GC table.
gen1_row_color The color for row entries of Gen 1 events in the GC table.
gen2_row_color The color for row entries of Gen 2 events in the GC table.
gen0_heap_color The color for gen0 stats in the heap stats table.
gen1_heap_color The color for gen1 stats in the heap stats table.
gen2_heap_color The color for gen2 stats in the heap stats table.
gen3_heap_color The color for gen3 (LOH) stats in the heap stats table.
gen4_heap_color The color for gen4 (pinned object heap) stats in the heap stats table.
total_heap_color The color used to for information about total heap stats in the heap stats table.
highlight_color The color used to call out special information, like the process being monitored.
message_color The color used for standard messages written to output.
warning_color The color used for warning messages written to output.

Unit Tests

The unit tests are in the test directory and can be run by:

dotnet test

Building

Build with VS

Open GCRealTimeMon.sln and build it with Visual Studio.

Build with command line

cd src/GCRealTimeMon
dotnet publish -c Release -r win-x64 # build on Windows
dotnet publish -c Release -r linux-x64 # build on Linux
dotnet publish -c Release -r osx-x64 # build on macOS

Additionaly, you can pass /p:AotCompilation=true to build GCRealTimeMon with NativeAOT. This requires native C++ toolchain (MSVC or clang) to be installed on the machine.

cd src/GCRealTimeMon
dotnet publish -c Release -r win-x64 /p:AotCompilation=true # build on Windows
dotnet publish -c Release -r linux-x64 /p:AotCompilation=true # build on Linux
dotnet publish -c Release -r osx-x64 /p:AotCompilation=true # build on macOS

Build artifacts can be found in bin/Release/netcoreapp3.1/[rid]/publish.

Build dotnet-gcmon tool with command line

cd src/dotnet-gcmon
dotnet build -c Release

How to generate the global .NET CLI tool dotnet-gcmon

Such a tool is simply a console application stored in a nuget package with some specific properties. The dotnet-gcmon.csproj file contains the corresponding settings and link to the implementation of the GCRealTimeMon console application.

When building this dotnet-gcmon C# project, a nuget package (dotnet-gcmon.(version x.y.z).nupkg) is generated under the nupkg folder. Ensure that the configuration is "Release" to publish a new version.

It is also possible to manually generate the package in Release, by using the following command in the .csproj folder:

dotnet pack -c Release

If you make changes to the global tool you should test it locally by first uninstalling an existing version

dotnet tool uninstall -g dotnet-gcmon

and then installing the local .nupkg with this command line:

dotnet tool install -g dotnet-gcmon --version 0.5.0 --add-source C:\realmon\src\dotnet-gcmon\nupkg\

(replace 0.5.0 with the version you specified in the .csproj and C:\realmon with the name of your dir for the tool)

To publish a new version, upload the new dotnet-gcmon.(version x.y.z).nupkg file to https://www.nuget.org/packages/manage/upload.

After a while, it should appear under https://www.nuget.org/packages/dotnet-gcmon.

At that point, use the following command to install it on a machine:

   dotnet tool install -g dotnet-gcmon

NOTE: to prepare a new version, update the following property x.y.z in the .csproj file before building/generating the nuget package.

NOTE: when new files are added to GCRealTimeMon project, don't forget to add them as links in the dotnet-gcmon.csproj file.

Read https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools-how-to-create for more details about creating a global .NET CLI tool.

Contribution

Contributions are very welcome! Tasks I'm currently thinking about -

  • More info about GCs (the TraceGC class in TraceEvent provides a ton of info about each GC).
  • Take more command line args that allow uses to specify things like only show GCs that are blocking or show the allocated bytes in gen0/LOH inbetween GCs.

If you are interested in working on any of these, your contributions are very much appreciated. Or if you have suggestions on other features, feel free to open an issue or a PR.

realmon's People

Contributors

chrisnas avatar hez2010 avatar maoni0 avatar mokosan avatar nickcraver avatar onyxmaster avatar ryandle avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

realmon's Issues

investigate memory usage

I looked at the tool briefly today and I could see sometimes the memory usage go up pretty quickly (like when I was using one gcmon process to monitor anther gcmon process ๐Ÿ˜€). a hasty look at the callstacks for VirtualAlloc* in windbg gives me a few categories for committed bytes - some are understandable; others aren't. for example, this is a pretty common callstack -

00 00007ffb`1433b36d     : 00000000`00000001 00007ffb`147db558 00000000`00000000 00007ffb`143ba699 : KERNELBASE!VirtualAlloc [minkernel\kernelbase\mmsup.c @ 1022] 
01 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!ClrVirtualAlloc+0x12 [D:\a\_work\1\s\src\coreclr\vm\hosting.cpp @ 75] 
[some gc frames omitted]
0b 00007ffb`14348ee1     : 00000000`00000002 00000000`00000050 00000116`e659d6c8 00000000`00000000 : coreclr!WKS::GCHeap::Alloc+0x84 [D:\a\_work\1\s\src\coreclr\gc\gc.cpp @ 43664] 
0c (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!Alloc+0xb3 [D:\a\_work\1\s\src\coreclr\vm\gchelpers.cpp @ 228] 
0d (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!AllocateObject+0x11b [D:\a\_work\1\s\src\coreclr\vm\gchelpers.cpp @ 981] 
0e 00007ffa`b4ef8022     : 00007ffa`b4fa4ea8 00000000`00000003 00000116`c609a488 00007ffa`b4efba7d : coreclr!JIT_New+0x201 [D:\a\_work\1\s\src\coreclr\vm\jithelpers.cpp @ 2324] 
0f 00007ffa`b4ef105a     : 00000116`c603c3a8 00000116`c603bee8 00000116`c6066100 00000116`c63b1fe8 : Microsoft_Diagnostics_Tracing_TraceEvent!Microsoft.Diagnostics.Tracing.Parsers.Clr.GCPerHeapHistoryTraceData.GenData(Microsoft.Diagnostics.Tracing.Parsers.Clr.Gens)+0x6212
10 00007ffa`b4ee9194     : 00000116`c609a188 00007ffb`14453876 00000000`00000000 00000116`c5f368e0 : Microsoft_Diagnostics_Tracing_TraceEvent!Microsoft.Diagnostics.Tracing.Analysis.GC.GCStats.ProcessPerHeapHistory(Microsoft.Diagnostics.Tracing.Analysis.TraceLoadedDotNetRuntime, Microsoft.Diagnostics.Tracing.Parsers.Clr.GCPerHeapHistoryTraceData)+0x27a
11 00007ffa`b4ef73d9     : 00000116`e81d1740 00000015`1ed7d860 00000116`e81d1f58 00000000`00000238 : Microsoft_Diagnostics_Tracing_TraceEvent!Microsoft.Diagnostics.Tracing.TraceEventDispatcher.DoDispatch(Microsoft.Diagnostics.Tracing.TraceEvent)+0x74
12 00007ffa`b4ee8ce2     : 00000116`e81d1f20 00000015`1ed7d969 00000116`e81b80e0 00000000`00000010 : Microsoft_Diagnostics_Tracing_TraceEvent!Microsoft.Diagnostics.Tracing.ETWTraceEventSource.RawDispatch(EVENT_RECORD*)+0xe6c9
13 00007ffb`4ac337fb     : 00000116`e81d1f20 00000015`1ed7d969 00000116`00000238 00000116`e828c6d0 : System_Console!ILStubClass.IL_STUB_ReversePInvoke(Int64)+0x42
14 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : sechost!EtwpDoEventRecordCallbacks+0x24 [minkernel\etw\control\tracedc.cpp @ 1945] 
15 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : sechost!EtwpDoEventCallbacks+0x31 [minkernel\etw\control\tracedc.cpp @ 1956] 
16 00007ffb`4ac33633     : 00000116`e828c6d0 00000000`00000000 00000000`00000000 7fffffff`ffffffff : sechost!EtwpLoadEventTrigger+0x15b [minkernel\etw\control\tracert.cpp @ 1435] 
17 00007ffb`4ac3b86a     : 00000000`00000000 00000015`1ed7db40 00000116`e828c6d0 00000000`00000000 : sechost!EtwpProcessRealTimeTraces+0xc7 [minkernel\etw\control\tracert.cpp @ 468] 
18 00007ffa`b4ee5be4     : 00000000`00000000 00007ffb`00000001 00000116`c5f377c8 00000000`00000000 : sechost!ProcessTrace+0x18a [minkernel\etw\control\tracedc.cpp @ 2469] 

we are still processing the GC events even though we are not displaying the results because those are not event happening in the process we asked for. these should be dead though since I'm not storing them anywhere. should verify this is the case.

but things like this seem odd -

 # RetAddr               : Args to Child                                                           : Call Site
00 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : KERNELBASE!VirtualAllocEx [minkernel\kernelbase\mmsup.c @ 1041] 
01 00007ffb`1440261d     : 00001d59`e1a7ba00 00000000`00000001 00007ffb`2ba50000 00000000`00000202 : KERNELBASE!VirtualAlloc+0x4 [minkernel\kernelbase\mmsup.c @ 1023] 
02 00007ffb`1436ab25     : 00000000`00000030 00000015`1dffdf29 00000000`00000030 00000000`00000000 : coreclr!UnlockedLoaderHeap::GetMoreCommittedPages+0x79 [D:\a\_work\1\s\src\coreclr\utilcode\loaderheap.cpp @ 1234] 
03 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!UnlockedLoaderHeap::UnlockedAllocMem_NoThrow+0xe6 [D:\a\_work\1\s\src\coreclr\utilcode\loaderheap.cpp @ 1400] 
04 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!UnlockedLoaderHeap::UnlockedAllocMem+0xe6 [D:\a\_work\1\s\src\coreclr\utilcode\loaderheap.cpp @ 1266] 
05 00007ffb`14344940     : 00000116`c4155178 00000015`1dffe379 00007ffb`147e29d0 00000116`c4155178 : coreclr!LoaderHeap::RealAllocMemUnsafe+0x141 [D:\a\_work\1\s\src\coreclr\inc\loaderheap.h @ 561] 
06 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!LoaderHeap::RealAllocMem+0x15 [D:\a\_work\1\s\src\coreclr\inc\loaderheap.h @ 517] 
07 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!EEJitManager::allocCode+0x379 [D:\a\_work\1\s\src\coreclr\vm\codeman.cpp @ 2707] 
08 00007ffb`2ba91b54     : 00000116`e660c800 00000116`00000000 00000116`00000001 00007ffb`0000001f : coreclr!CEEJitInfo::allocMem+0x570 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 12451] 
09 00007ffb`2bb18433     : 00000015`1dffeb2c 00000116`e660bf18 00000116`e660c830 00007ffb`2ba50000 : clrjit!emitter::emitEndCodeGen+0x124 [D:\a\_work\1\s\src\coreclr\jit\emit.cpp @ 5731] 
0a 00007ffb`2bb1d482     : 00000116`e660c830 00000015`1dffe4b0 00000000`00000000 00007ffb`2bb880b0 : clrjit!CodeGen::genEmitMachineCode+0x183 [D:\a\_work\1\s\src\coreclr\jit\codegencommon.cpp @ 2446] 
0b 00007ffb`2bb18567     : 00000116`e660c830 00000015`1dffeaa8 00000000`00000000 00007ffb`2ba50000 : clrjit!CodeGenPhase::DoPhase+0x12 [D:\a\_work\1\s\src\coreclr\jit\codegen.h @ 1610] 
0c (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : clrjit!Phase::Run+0x44 [D:\a\_work\1\s\src\coreclr\jit\phase.cpp @ 61] 
0d (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : clrjit!DoPhase+0x4b [D:\a\_work\1\s\src\coreclr\jit\codegen.h @ 1623] 
0e 00007ffb`2bac4aed     : 00000116`e660bf18 00000116`e6613758 00000000`00000000 00000116`e6613758 : clrjit!CodeGen::genGenerateCode+0xd7 [D:\a\_work\1\s\src\coreclr\jit\codegencommon.cpp @ 2099] 
0f 00007ffb`2ba7deb1     : 00000116`e660bf18 00000116`e660bf18 00000000`00000000 00000116`e660bf18 : clrjit!Compiler::compCompile+0xf1d [D:\a\_work\1\s\src\coreclr\jit\compiler.cpp @ 5216] 
10 00007ffb`2ba8209a     : 00007ffa`b48f56d0 00000116`e660bf18 00000015`1dffeba0 00000015`1dfff900 : clrjit!Compiler::compCompileHelper+0x291 [D:\a\_work\1\s\src\coreclr\jit\compiler.cpp @ 6422] 
11 00007ffb`2ba83492     : 00000000`00000000 00000000`00000000 00007ffa`b49b4950 00000015`1dffe9d0 : clrjit!Compiler::compCompile+0x24a [D:\a\_work\1\s\src\coreclr\jit\compiler.cpp @ 5685] 
12 00007ffb`2bb1b633     : 00007ffa`b49b4950 00007ffa`b47e4000 00000015`1dffeb80 00000015`1dffee30 : clrjit!jitNativeCode+0x262 [D:\a\_work\1\s\src\coreclr\jit\compiler.cpp @ 7055] 
13 00007ffb`14367571     : 00000116`e6587110 00000000`00000000 00000015`1dfff0d8 00000015`1dffecd0 : clrjit!CILJit::compileMethod+0x83 [D:\a\_work\1\s\src\coreclr\jit\ee_il_dll.cpp @ 279] 
14 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!invokeCompileMethodHelper+0x86 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 12774] 
15 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!invokeCompileMethod+0xc5 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 12839] 
16 00007ffb`14366c31     : 00000015`00000096 00000015`1dfff0d8 00000015`1dfff070 00000015`1dffef10 : coreclr!UnsafeJitFunction+0x7f1 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 13355] 
17 00007ffb`143668fc     : 00000000`00000001 00007ffa`b49b4950 00007ffa`b49b4950 00007ffb`4c73b86b : coreclr!MethodDesc::JitCompileCodeLocked+0x1f1 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 1051] 
18 00007ffb`1435f7f9     : 00000116`e8243a70 00000015`1dfff520 00000116`e8243a70 00000116`e8243a78 : coreclr!MethodDesc::JitCompileCodeLockedEventWrapper+0x148 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 920] 
19 00007ffb`14399dc2     : 00007ffa`b49b4950 00000116`00000000 00000000`00000000 00000000`00000000 : coreclr!MethodDesc::JitCompileCode+0x2a9 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 860] 
1a 00007ffb`14399cee     : 00000000`00000000 00000000`00000000 00000116`e6521b90 00007ffa`00000000 : coreclr!MethodDesc::PrepareILBasedCode+0x66 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 439] 
1b (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!MethodDesc::PrepareCode+0x10 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 332] 
1c 00007ffb`1439a445     : 00000000`00000000 00000000`00000064 00000116`c4183920 00000000`00000002 : coreclr!TieredCompilationManager::CompileCodeVersion+0xce [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 906] 
1d (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!TieredCompilationManager::OptimizeMethod+0x22 [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 883] 
1e 00007ffb`1445bcb8     : 00000116`c4183920 00000015`1dfff7b8 00000000`0004e200 00000000`0007a120 : coreclr!TieredCompilationManager::DoBackgroundWork+0x125 [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 768] 
1f 00007ffb`1445bbac     : 00000000`00989680 00007ffb`147d5f98 00000116`c414f860 00000000`0004e200 : coreclr!TieredCompilationManager::BackgroundWorkerStart+0xc8 [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 482] 
20 00007ffb`143cf921     : 00000000`00000130 00000000`00000000 00000000`00000000 00007ffb`143cb8ee : coreclr!TieredCompilationManager::BackgroundWorkerBootstrapper1+0x5c [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 431] 
21 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!ManagedThreadBase_DispatchInner+0xd [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 7312] 
22 00007ffb`143cf9fa     : 00000000`00000000 00000000`00000130 00000000`00000000 00000000`00000000 : coreclr!ManagedThreadBase_DispatchMiddle+0x85 [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 7356] 
23 00007ffb`14473efa     : 00000116`00000001 ffffffff`ffffffff 00000116`e828b8f0 00000116`e6587110 : coreclr!ManagedThreadBase_DispatchOuter+0xae [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 7515] 
24 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!ManagedThreadBase_FullTransition+0x24 [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 7560] 
25 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!ManagedThreadBase::KickOff+0x24 [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 7595] 
26 00007ffb`4c627034     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : coreclr!TieredCompilationManager::BackgroundWorkerBootstrapper0+0x3a [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 414] 
27 00007ffb`4c762651     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14 [clientcore\base\win32\client\thread.c @ 64] 
28 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21 [minkernel\ntdll\rtlstrt.c @ 1153] 
 # RetAddr               : Args to Child                                                           : Call Site
00 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : KERNELBASE!VirtualAllocExNuma [minkernel\kernelbase\mmsup.c @ 1066] 
01 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : KERNELBASE!VirtualAllocEx+0xd [minkernel\kernelbase\mmsup.c @ 1041] 
02 00007ffb`1440261d     : 00007ffa`b4fb9000 00000000`00001000 00007ffb`2ba50000 00000000`00000202 : KERNELBASE!VirtualAlloc+0x11 [minkernel\kernelbase\mmsup.c @ 1023] 
03 00007ffb`1436ab25     : 00000000`00000030 00000015`1dffdf29 00000000`00000030 00000000`00000000 : coreclr!UnlockedLoaderHeap::GetMoreCommittedPages+0x79 [D:\a\_work\1\s\src\coreclr\utilcode\loaderheap.cpp @ 1234] 
04 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!UnlockedLoaderHeap::UnlockedAllocMem_NoThrow+0xe6 [D:\a\_work\1\s\src\coreclr\utilcode\loaderheap.cpp @ 1400] 
05 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!UnlockedLoaderHeap::UnlockedAllocMem+0xe6 [D:\a\_work\1\s\src\coreclr\utilcode\loaderheap.cpp @ 1266] 
06 00007ffb`14344940     : 00000116`c4155178 00000015`1dffe379 00007ffb`147e29d0 00000116`c4155178 : coreclr!LoaderHeap::RealAllocMemUnsafe+0x141 [D:\a\_work\1\s\src\coreclr\inc\loaderheap.h @ 561] 
07 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!LoaderHeap::RealAllocMem+0x15 [D:\a\_work\1\s\src\coreclr\inc\loaderheap.h @ 517] 
08 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!EEJitManager::allocCode+0x379 [D:\a\_work\1\s\src\coreclr\vm\codeman.cpp @ 2707] 
09 00007ffb`2ba91b54     : 00000116`e660c800 00000116`00000000 00000116`00000001 00007ffb`0000001f : coreclr!CEEJitInfo::allocMem+0x570 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 12451] 
0a 00007ffb`2bb18433     : 00000015`1dffeb2c 00000116`e660bf18 00000116`e660c830 00007ffb`2ba50000 : clrjit!emitter::emitEndCodeGen+0x124 [D:\a\_work\1\s\src\coreclr\jit\emit.cpp @ 5731] 
0b 00007ffb`2bb1d482     : 00000116`e660c830 00000015`1dffe4b0 00000000`00000000 00007ffb`2bb880b0 : clrjit!CodeGen::genEmitMachineCode+0x183 [D:\a\_work\1\s\src\coreclr\jit\codegencommon.cpp @ 2446] 
0c 00007ffb`2bb18567     : 00000116`e660c830 00000015`1dffeaa8 00000000`00000000 00007ffb`2ba50000 : clrjit!CodeGenPhase::DoPhase+0x12 [D:\a\_work\1\s\src\coreclr\jit\codegen.h @ 1610] 
0d (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : clrjit!Phase::Run+0x44 [D:\a\_work\1\s\src\coreclr\jit\phase.cpp @ 61] 
0e (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : clrjit!DoPhase+0x4b [D:\a\_work\1\s\src\coreclr\jit\codegen.h @ 1623] 
0f 00007ffb`2bac4aed     : 00000116`e660bf18 00000116`e6613758 00000000`00000000 00000116`e6613758 : clrjit!CodeGen::genGenerateCode+0xd7 [D:\a\_work\1\s\src\coreclr\jit\codegencommon.cpp @ 2099] 
10 00007ffb`2ba7deb1     : 00000116`e660bf18 00000116`e660bf18 00000000`00000000 00000116`e660bf18 : clrjit!Compiler::compCompile+0xf1d [D:\a\_work\1\s\src\coreclr\jit\compiler.cpp @ 5216] 
11 00007ffb`2ba8209a     : 00007ffa`b48f56d0 00000116`e660bf18 00000015`1dffeba0 00000015`1dfff900 : clrjit!Compiler::compCompileHelper+0x291 [D:\a\_work\1\s\src\coreclr\jit\compiler.cpp @ 6422] 
12 00007ffb`2ba83492     : 00000000`00000000 00000000`00000000 00007ffa`b49b4950 00000015`1dffe9d0 : clrjit!Compiler::compCompile+0x24a [D:\a\_work\1\s\src\coreclr\jit\compiler.cpp @ 5685] 
13 00007ffb`2bb1b633     : 00007ffa`b49b4950 00007ffa`b47e4000 00000015`1dffeb80 00000015`1dffee30 : clrjit!jitNativeCode+0x262 [D:\a\_work\1\s\src\coreclr\jit\compiler.cpp @ 7055] 
14 00007ffb`14367571     : 00000116`e6587110 00000000`00000000 00000015`1dfff0d8 00000015`1dffecd0 : clrjit!CILJit::compileMethod+0x83 [D:\a\_work\1\s\src\coreclr\jit\ee_il_dll.cpp @ 279] 
15 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!invokeCompileMethodHelper+0x86 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 12774] 
16 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!invokeCompileMethod+0xc5 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 12839] 
17 00007ffb`14366c31     : 00000015`00000096 00000015`1dfff0d8 00000015`1dfff070 00000015`1dffef10 : coreclr!UnsafeJitFunction+0x7f1 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 13355] 
18 00007ffb`143668fc     : 00000000`00000001 00007ffa`b49b4950 00007ffa`b49b4950 00007ffb`4c73b86b : coreclr!MethodDesc::JitCompileCodeLocked+0x1f1 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 1051] 
19 00007ffb`1435f7f9     : 00000116`e8243a70 00000015`1dfff520 00000116`e8243a70 00000116`e8243a78 : coreclr!MethodDesc::JitCompileCodeLockedEventWrapper+0x148 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 920] 
1a 00007ffb`14399dc2     : 00007ffa`b49b4950 00000116`00000000 00000000`00000000 00000000`00000000 : coreclr!MethodDesc::JitCompileCode+0x2a9 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 860] 
1b 00007ffb`14399cee     : 00000000`00000000 00000000`00000000 00000116`e6521b90 00007ffa`00000000 : coreclr!MethodDesc::PrepareILBasedCode+0x66 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 439] 
1c (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!MethodDesc::PrepareCode+0x10 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 332] 
1d 00007ffb`1439a445     : 00000000`00000000 00000000`00000064 00000116`c4183920 00000000`00000002 : coreclr!TieredCompilationManager::CompileCodeVersion+0xce [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 906] 
1e (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!TieredCompilationManager::OptimizeMethod+0x22 [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 883] 
1f 00007ffb`1445bcb8     : 00000116`c4183920 00000015`1dfff7b8 00000000`0004e200 00000000`0007a120 : coreclr!TieredCompilationManager::DoBackgroundWork+0x125 [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 768] 
20 00007ffb`1445bbac     : 00000000`00989680 00007ffb`147d5f98 00000116`c414f860 00000000`0004e200 : coreclr!TieredCompilationManager::BackgroundWorkerStart+0xc8 [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 482] 
21 00007ffb`143cf921     : 00000000`00000130 00000000`00000000 00000000`00000000 00007ffb`143cb8ee : coreclr!TieredCompilationManager::BackgroundWorkerBootstrapper1+0x5c [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 431] 
22 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!ManagedThreadBase_DispatchInner+0xd [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 7312] 
23 00007ffb`143cf9fa     : 00000000`00000000 00000000`00000130 00000000`00000000 00000000`00000000 : coreclr!ManagedThreadBase_DispatchMiddle+0x85 [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 7356] 
24 00007ffb`14473efa     : 00000116`00000001 ffffffff`ffffffff 00000116`e828b8f0 00000116`e6587110 : coreclr!ManagedThreadBase_DispatchOuter+0xae [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 7515] 
25 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!ManagedThreadBase_FullTransition+0x24 [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 7560] 
26 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!ManagedThreadBase::KickOff+0x24 [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 7595] 
27 00007ffb`4c627034     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : coreclr!TieredCompilationManager::BackgroundWorkerBootstrapper0+0x3a [D:\a\_work\1\s\src\coreclr\vm\tieredcompilation.cpp @ 414] 
28 00007ffb`4c762651     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14 [clientcore\base\win32\client\thread.c @ 64] 
29 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21 [minkernel\ntdll\rtlstrt.c @ 1153] 

and I'm seeing many of these (which is odd 'cause we should only be disposing the session once).

would be worthwhile to do a deeper investigation.

Add Functional Tests

Now that we are adding an increasing number of command line conditions, automated functional tests will be essential to efficiently test out conditions.

The conditions to test could include:

  1. Configuration creation.
  2. Testing basic monitoring functionality.
  3. Running both dotnet cli + GCRealTimeMon exes and comparing the equality of results.
  4. Testing different build configs such as AoT Compilation.

Once these tests are added, the .github/workflows script must be edited to include these new tests.

make realmon work on Linux

this involves the following -

  • using the Diagnostics Client library to start an EventPipe session instead of an ETW session which is what TraceEventSession.EnableProvider does. conveniently there's already sample code here that shows how to start such a session in real time!

  • the necessary build files to build this on Linux.

gcmon stops emitting GC events if a 2nd gcmon is used to monitor the first

  1. Open admin console
  2. Run gcmon on a process dotnet gcmon -n <processName>
  3. Observe that it is emitting GC events / works as expected.
  4. Open a 2nd admin console
  5. Run a 2nd gcmon instance to monitor the first dotnet gcmon -p <pidOfFirstGcMonProcess>

Expected: The first gcmon process continues to work as expected and the second one reports on the 1st's GC behavior.

Actual: The first gcmon appears to stop writing gc events to the output once the 2nd starts monitoring it.

I've been able to repro this on Windows 10 20H2. Haven't tried any other platforms.

Requires .NET 3.1.0 when newer .NET installed

When I installed the tool and tried to run it, I get an information: required .NET 3.1.0 (runtime or SDK) while I have installed .NET 5 and 6.

When I installed 3.1.0 SDK, it works fine.

Attaching screenshot.

obraz

Expected behavior: it should run on newer version of .NET.

show callstacks for induced GCs and large object allocations

these are things that are very practical for perf diagnostics - you'd want to know if there are GCs induced who induced it and often folks would like to know who's allocating large objects.

this is the high level idea (the reason I didn't write down details is I only know the top level idea at this point :)) -

for ETW the IPs in the callstacks are decoded based on rundown events so the idea we run down at the beginning instead of at the end, and then when there are new JIT method events we will add those. and TraceLog can already be used with a real time session.

with EventPipe this may not be straightforward as right now it may not be able to run down at the beginning.

add a separate dir to build the NativeAoT version

some folks have expressed the desire to use this tool without having to install .NET 6. the .net 6 requirement is just for the NativeAoT which you can always build separately. so we should not require folks to have to have 6.0.

my suggestion is we should have a separate dir that builds AoT that does require 6.0 (instead of having everyone who wants to use this and doesn't want to install 6.0 to change src/GCRealTimeMon/GCRealTimeMon.csproj themselves). the rest can use 5.0 as a min version (actually we could even make it 3.1...).

Proposal: Prettify console output using Spectre.Console

I hacked together a prototype of what the console might look like if we used a library like Spectre.Console to markup the output:

image

The above styling is just to get the idea across.

Is there any interest in using Spectre.Console to beautify the output? Leaving out extra details to first see if there is even interest in this or if there are reasons to keep the output simpler.

Remove the Reference to Sharprompt and Replace Usage with the Spectre.Console API (old Spectre.CLI API)

Related to #39, now that we are planning to incorporate the Spectre.Console API, makes sense to replace all usage of the Sharprompt API for the prompts and remove the reference altogether.

These include:

  1. Creating a new Config which involves:
  2. Filling in the Default Values if a configuration is meant to be overwritten:
    • For the MultiSelectionPrompt This done by using the Select<T> functionality.

Session Leaks on Ctrl+C

There are instances where if the user clicks Ctrl+C to close realmon, the session isn't getting disposed.

Repro Steps

  1. Run realmon normally and note the session name by putting a break point here.
  2. Ctrl+C to close the session.
  3. Open up a command line prompt / powershell in admin mode.
  4. logman query -ets to list all the sessions on the machine. Here is where I found the session still exists even after exiting the process.

Eventually, this will result in the following exception:
Unhandled exception. System.Runtime.InteropServices.COMException (0x800705AA): Insufficient system resources exist to complete the requested service. (0x800705AA).

Solution

Subscribe to the Console.CancelKeyPress event and explicitly dispose the session.

get rid of the CPU Time (%) column from all places

I should have thought about this when we added this column.

without actually collecting CPU samples this would always be NaN. and I don't see much usage for folks to actually need to know the CPU time %.

if it's proven to be something that folks do need we can certainly add it back and actually adjust the keywords for the events we collect to support showing a valid value for it. another reason is this doesn't currently work well on linux (you could use the built in cpu events which would suspend the EE everytime it needs to collect cpu samples so it's not practical to use). for windows the perfview command is this:

PerfView /nogui /KernelEvents=Process+Thread+ImageLoad+Profile /ClrEvents=GC+Stack /ClrEventLevel=Informational /BufferSize:3000 /CircularMB:3000 collect

we just need to make the corresponding change when we start the event session.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.