/*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ /* depots. Master copy resides in Bartok Depot. Changes should be */ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ // // Copyright (c) Microsoft Corporation. All rights reserved. // #define ENABLED namespace System.GCs { using System; using System.Threading; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; #if SINGULARITY using Microsoft.Singularity; #if SINGULARITY_PROCESS using Microsoft.Singularity.V1.Services; #endif #endif /// /// Enumeration of Event Types. /// public enum GCEvent: int { CreateHeap, DestroyHeap, StopTheWorld, StopThread, ResumeTheWorld, ComputeRootSet, TraceStart, TraceSpecial, SweepStart, SweepSpecial, SweepPreCommit, CollectionComplete, RootSetComputed, StackScanStart, StackScanComplete } /// /// Manages statistics from the collector. Intended for communication /// with the scheduler. /// internal class CollectorStatistics { [Inline] private static string EventToString(GCEvent ev) { switch (ev) { case GCEvent.CreateHeap: return "CreateHeap "; case GCEvent.DestroyHeap: return "DestroyHeap "; case GCEvent.StopTheWorld: return "StopTheWorld "; case GCEvent.StopThread: return "StopThread "; case GCEvent.ResumeTheWorld: return "ResumeTheWorld "; case GCEvent.ComputeRootSet: return "ComputeRootSet "; case GCEvent.TraceStart: return "TraceStart "; case GCEvent.TraceSpecial: return "TraceSpecial "; case GCEvent.SweepStart: return "SweepStart "; case GCEvent.SweepSpecial: return "SweepSpecial "; case GCEvent.SweepPreCommit: return "SweepPreCommit "; case GCEvent.CollectionComplete: return "CollectionComplete"; case GCEvent.RootSetComputed: return "RootSetComputed "; case GCEvent.StackScanStart: return "StackScanStart "; case GCEvent.StackScanComplete: return "StackScanComplete "; default: return "Unknown! "; } } [Inline] private static string EventToFormatString(GCEvent ev) { switch (ev) { case GCEvent.CreateHeap: return "CreateHeap"; case GCEvent.DestroyHeap: return "DestroyHeap"; case GCEvent.StopTheWorld: return "StopTheWorld"; case GCEvent.StopThread: return "StopThread, thread {0}"; case GCEvent.ResumeTheWorld: return "ResumeTheWorld"; case GCEvent.ComputeRootSet: return "ComputeRootSet"; case GCEvent.TraceStart: return "TraceStart"; case GCEvent.TraceSpecial: return "TraceSpecial"; case GCEvent.SweepStart: return "SweepStart"; case GCEvent.SweepSpecial: return "SweepSpecial"; case GCEvent.SweepPreCommit: return "SweepPreCommit"; case GCEvent.CollectionComplete: return "CollectionComplete"; case GCEvent.RootSetComputed: return "RootSetComputed"; case GCEvent.StackScanStart: return "StackScanStart, thread {0}"; case GCEvent.StackScanComplete: return "StackScanComplete, thread {0}"; default: return "Unknown!"; } } /// /// The current value of the counter. /// public static long PerformanceCounter { [Inline] get { long val; bool ok = QueryPerformanceCounter(out val); VTable.Assert(ok); return val; } } public static long ThreadTime { [Inline] get { #if SINGULARITY return 0; #else long creationTime, exitTime, kernelTime, userTime; UIntPtr handle = Thread.CurrentThread.asmWin32ThreadHandle; bool ok = GetThreadTimes(handle, out creationTime, out exitTime, out kernelTime, out userTime); VTable.Assert(ok); return kernelTime + userTime; #endif } } #if SINGULARITY_KERNEL public static bool QueryPerformanceCounter(out long lpPerformanceCount) { lpPerformanceCount = unchecked((long)Processor.CycleCount); return true; } public static bool QueryPerformanceFrequency(out long lpFrequency) { lpFrequency = unchecked((long)Processor.CyclesPerSecond); return true; } #elif SINGULARITY_PROCESS public static bool QueryPerformanceCounter(out long lpPerformanceCount) { lpPerformanceCount = unchecked((long)ProcessService.GetCycleCount()); return true; } public static bool QueryPerformanceFrequency(out long lpFrequency) { lpFrequency = unchecked((long)ProcessService.GetCyclesPerSecond()); return true; } #else // not SINGULARITY [DllImport("BRT")] [GCAnnotation(GCOption.NOGC)] [StackBound(1024)] public static extern bool QueryPerformanceCounter( out long lpPerformanceCount); [DllImport("BRT")] [GCAnnotation(GCOption.NOGC)] [StackBound(1024)] public static extern bool QueryPerformanceFrequency( out long lpFrequency); [DllImport("BRT")] [GCAnnotation(GCOption.NOGC)] [StackBound(1024)] public static extern bool GetThreadTimes( UIntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime ); #endif /// /// The number of counter values per microsecond. /// public static long CounterFrequency; /// /// The counter value at the initialization of statistics. /// public static long StartCount; /// /// Initialize the statistics information /// public static void Initialize() { #if ENABLED bool ok = QueryPerformanceFrequency(out CounterFrequency); VTable.Assert(CounterFrequency > 0); VTable.Assert(ok); StartCount = PerformanceCounter; #endif } /// /// Print out a summary /// public static void Summary() { } /// /// A GC event. /// [Inline] public static void Event(GCEvent ev) { Event(ev, -1); } /// /// A GC event. /// [Inline] public static void Event(GCEvent ev, long val) { #if ENABLED long time = ((PerformanceCounter - StartCount) * 1000000 / CounterFrequency); LogEntry le; le.TimeStamp = time; le.ThreadTime = 0; le.Event = ev; le.Data = val; Log(le); #endif } private static SpinLock hack; private static void Log(LogEntry le) { #if SINGULARITY Tracing.Log(Tracing.Audit, EventToFormatString(le.Event), unchecked((UIntPtr)(uint)le.Data)); #endif #if DONT Thread t = Thread.CurrentThread; VTable.DebugPrint("#GCEVENT Thread {0}: {1}, {2}, {3}\n", __arglist(t.threadIndex, EventToString(le.Event), le.TimeStamp, le.Data)); #endif } private struct LogEntry { public GCEvent Event; public long TimeStamp; public long ThreadTime; public long Data; } } }