using Microsoft.Diagnostics.Tracing.Session; using ProfilerHelpers; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace Tracing { static class Program { static public async Task CreateTracingSession( bool noSampling, bool sortBySize, int topTypesLimit ) { ShowHeader(); try { TraceEventSession session = new TraceEventSession( "Tracing", TraceEventSessionOptions.Create ); log.info( $"Create PerProcessProfilingState" ); using( var processes = new PerProcessProfilingState() ) { log.info( $"Create Memory profiler for session" ); var profiler = new Memory( session, processes ); log.info( $"Start task" ); var task = profiler.StartAsync( noSampling ); /* log.info( $"await the Continue" ); await task.ContinueWith( ( t ) => { log.info( $"Task is done, Dispose" ); session.Dispose(); } ); */ //log.info("Press ENTER to stop memory profiling"); //Console.ReadLine(); /* try { await task; ShowResults( processes, sortBySize, topTypesLimit ); return 0; } catch( Exception x ) { log.info( x.Message ); ShowHelp(); } */ } return -1; } catch( Exception x ) { log.info( x.Message ); ShowHelp(); } return -2; } private static void ShowResults( PerProcessProfilingState processes, bool sortBySize, int topTypesLimit ) { foreach( var pid in processes.Allocations.Keys ) { // skip processes without symbol resolution if( !processes.Methods.ContainsKey( pid ) ) continue; // skip processes without allocations if( !processes.Allocations[pid].GetAllAllocations().Any() ) continue; ShowResults( GetProcessName( pid, processes.Names ), processes.Methods[pid], processes.Allocations[pid], sortBySize, topTypesLimit ); } } private static string GetProcessName( int pid, Dictionary names ) { if( names.TryGetValue( pid, out var name ) ) return name; return pid.ToString(); } private static void ShowResults( string name, MethodStore methods, ProcessAllocations allocations, bool sortBySize, int topTypesLimit ) { log.info( $"Memory allocations for {name}" ); log.info( $"" ); log.info( "---------------------------------------------------------" ); log.info( " Count Size Type" ); log.info( "---------------------------------------------------------" ); IEnumerable types = ( sortBySize ) ? allocations.GetAllAllocations().OrderByDescending( a => a.Size ) : allocations.GetAllAllocations().OrderByDescending( a => a.Count ) ; if( topTypesLimit != -1 ) types = types.Take( topTypesLimit ); foreach( var allocation in types ) { log.info( $"{allocation.Count,9} {allocation.Size,11} {allocation.TypeName}" ); log.info( $"" ); DumpStacks( allocation, methods ); log.info( $"" ); } log.info( $"" ); log.info( $"" ); } private static void DumpStacks( AllocationInfo allocation, MethodStore methods ) { var stacks = allocation.Stacks.OrderByDescending( s => s.Count ).Take( 10 ); foreach( var stack in stacks ) { log.info( $"{stack.Count,6} allocations" ); log.info( "----------------------------------" ); DumpStack( stack.Stack, methods ); log.info( $"" ); } } private static void DumpStack( AddressStack stack, MethodStore methods ) { var callstack = stack.Stack; for( int i = 0; i < Math.Min( 10, callstack.Count ); i++ ) { log.info( $" {methods.GetFullName( callstack[i] )}" ); } } private static void ShowHeader() { log.info( "Tracing v1.0.0 - Sampled memory profiler for .NET applications" ); log.info( "by Christophe Nasarre" ); log.info( $"" ); } private static void ShowHelp() { log.info( $"" ); log.info( "Tracing shows sampled allocations of a given .NET application." ); log.info( "Usage: Tracing [-a (all allocations)] [-c (sort by count instead of default by size)] [-t ]" ); log.info( " Ex: Tracing -t -1 (all types sampled allocations sorted by size)" ); log.info( " Ex: Tracing -c -t 10 (allocations for top 10 types sorted by count)" ); log.info( $"" ); } } }