public static int EntryPoint(string pwzArgument) { Debug.WriteLine("[KeeFarceDLL] Starting"); //string processName = Process.GetCurrentProcess().ProcessName; //MessageBox.Show("The current process is " + processName + " and I am running C# code! Yuss!"); if (is64Bit) { Debug.WriteLine("[KeeFarceDLL] Target is 64 bit"); } // Retrieve the DocumentManagerEx object off the heap // TODO: KeePass can support multiple password files , so should probably modify this to load // ALL of the DocumentManagerEx's into a list and process 'em, as opposed to just breaking // after finding the first one. IntPtr docManagerPtr = IntPtr.Zero; using (DataTarget dataTarget = DataTarget.AttachToProcess(Process.GetCurrentProcess().Id, 5000, AttachFlag.Passive)) { string dacLocation = dataTarget.ClrVersions[0].TryGetDacLocation(); ClrRuntime runtime = CreateRuntimeHack(dataTarget, dacLocation, 4, 5); Debug.WriteLine("[KeeFarceDLL] Attached to process."); ClrHeap heap = runtime.GetHeap(); foreach (ulong obj in heap.EnumerateObjects()) { ClrType type = heap.GetObjectType(obj); ulong size = type.GetSize(obj); if (type.Name == "KeePass.UI.DocumentManagerEx") { Debug.WriteLine("[KeeFarceDLL] Found DocumentManagerEx at: " + obj.ToString("X") + " " + type.Name); docManagerPtr = (IntPtr)obj; break; } } if (docManagerPtr == IntPtr.Zero) { // Didn't find a document manager, time to return. Debug.WriteLine("[KeeFarceDLL] No DocumentManagerEx found"); return(1); } } // Get the DocumentManagerEx object Converter <object> ptrconv = new Converter <object>(); object documentManagerEx = ptrconv.ConvertFromIntPtr(docManagerPtr); var info = new DocumentManagerExInfo(documentManagerEx); int r = doExport(info.ActiveDatabase, info.RootGroup, exportFile); return(r); }
/// <summary> /// Taken from https://github.com/Microsoft/dotnetsamples/blob/master/Microsoft.Diagnostics.Runtime/CLRMD/docs/WalkingTheHeap.md /// </summary> /// <param name="obj"></param> /// <param name="count"></param> /// <param name="size"></param> static protected void GetObjSize(ClrHeap heap, ulong obj, ObjectSet farReachingParents, out uint count, out ulong size) { // Evaluation stack Stack <ulong> eval = new Stack <ulong>(); // To make sure we don't count the same object twice, we'll keep a set of all objects // we've seen before. Note the ObjectSet here is basically just "HashSet<ulong>". // However, HashSet<ulong> is *extremely* memory inefficient. So we use our own to // avoid OOMs. ObjectSet considered = new ObjectSet(heap); count = 0; size = 0; eval.Push(obj); while (eval.Count > 0) { // Pop an object, ignore it if we've seen it before. obj = eval.Pop(); if (farReachingParents.Contains(obj)) { continue; } if (considered.Contains(obj)) { continue; } considered.Add(obj); // Grab the type. We will only get null here in the case of heap corruption. ClrType type = heap.GetObjectType(obj); if (type == null) { continue; } count++; size += type.GetSize(obj); // Now enumerate all objects that this object points to, add them to the // evaluation stack if we haven't seen them before. type.EnumerateRefsOfObject(obj, delegate(ulong child, int offset) { if (child != 0 && !considered.Contains(child) && !farReachingParents.Contains(child)) { eval.Push(child); } }); } }
private bool FilterObject(ulong obj, ulong mt) { bool match = true; ClrType type = null; if (!String.IsNullOrEmpty(TypeRegex)) { type = _heap.GetObjectType(obj); if (type == null || String.IsNullOrEmpty(type.Name)) { match = false; } if (match) { // TODO Consider compiling the regex match = Regex.IsMatch(type.Name, TypeRegex); } } if (match && MinSize != -1) { type = type ?? _heap.GetObjectType(obj); match = (long)type.GetSize(obj) >= MinSize; } if (match && MaxSize != -1) { type = type ?? _heap.GetObjectType(obj); match = (long)type.GetSize(obj) <= MaxSize; } if (match && MethodTable != 0) { match = MethodTable == mt; } return(match); }
private void StoreData(CancellationToken token) { BeginUpdate(); Dictionary <ClrType, ClrTypeStats> stats = new Dictionary <ClrType, ClrTypeStats>(); foreach (var address in ClrDump.Heap.EnumerateObjectAddresses()) { ClrType type = ClrDump.Heap.GetObjectType(address); if (type == null) { continue; } ulong size = type.GetSize(address); ClrTypeStats stat; if (!stats.TryGetValue(type, out stat)) { stat = new ClrTypeStats(stats.Count, type); stats[type] = stat; } stat.Inc(size); InsertInstances(stat.Id, address); type.EnumerateRefsOfObject(address, delegate(ulong child, int offset) { InsertReferences(child, address); }); n++; if (n % 1024 * 64 == 0) { if (token.IsCancellationRequested) { return; } ClrDump.MessageBus.Status(GetStats()); } } ClrDump.MessageBus.Log(this, "Inserting type stats..."); foreach (var stat in stats.Values) { InsertTypeStat(stat); } EndUpdate(); ClrDump.MessageBus.Status("Cache Initialized.", StatusType.EndTask); }
private void GetObjSize(ClrHeap heap, ulong obj, out uint count, out ulong size) { // Evaluation stack var eval = new Stack <ulong>(); // To make sure we don't count the same object twice, we'll keep a set of all objects // we've seen before. var considered = new HashSet <ulong>(); count = 0; size = 0; eval.Push(obj); while (eval.Count > 0) { // Pop an object, ignore it if we've seen it before. obj = eval.Pop(); if (!considered.Add(obj)) { continue; } // Grab the type. We will only get null here in the case of heap corruption. ClrType type = heap.GetObjectType(obj); if (type == null) { continue; } count++; size += type.GetSize(obj); // Now enumerate all objects that this object points to, add them to the // evaluation stack if we haven't seen them before. type.EnumerateRefsOfObject(obj, (child, offset) => { if (child != 0 && !considered.Contains(child)) { eval.Push(child); } }); } }
private static SortedDictionary <string, ManagedObject> ListObjects(ClrRuntime runtime) { SortedDictionary <String, ManagedObject> managedObjects = new SortedDictionary <string, ManagedObject>(); if (runtime.Heap.CanWalkHeap) { foreach (ulong ptr in runtime.Heap.EnumerateObjectAddresses()) { ClrType type = runtime.Heap.GetObjectType(ptr); //Free objects are not real objects in the strictest sense. They are actually markers placed by the GC to //denote free space on the heap. Free objects have no fields (though they do have a size). In general, if //you are trying to find heap fragmentation, you will need to take a look at how many Free objects there are, //how big they are, and what lies between them. Otherwise, you should ignore them. //https://github.com/Microsoft/clrmd/blob/master/Documentation/TypesAndFields.md if (type == null || type.IsFree) { continue; } ManagedObject mo = null; if (!managedObjects.ContainsKey(type.Name)) { mo = new ManagedObject() { ObjectName = type.Name }; managedObjects.Add(type.Name, mo); } if (mo == null) { mo = managedObjects[type.Name]; } mo.ObjectCount++; mo.ObjectSize += type.GetSize(ptr); } } return(managedObjects); }
public void Add(ulong candidateAddress, [NotNull] ClrType type) { if (candidateAddress == 0) { return; } Assert.ArgumentNotNull(type, "type"); HeapTypeStatisticsEntry existing = Get(type.MetadataToken); var size = (long)type.GetSize(candidateAddress); if (existing == null) { var added = new HeapTypeStatisticsEntry(size, type); typeStats.Add(type.MetadataToken, added); return; } existing.AddTypeUsage(size); }
private void treeViewObjects_BeforeExpand(object sender, TreeViewCancelEventArgs e) { if (e.Node.Nodes.Count == 1 && e.Node.Nodes[0].Tag == null) { e.Node.Nodes.Clear(); ClrTypeHelper c = (ClrTypeHelper)e.Node.Tag; ClrType type = runtime.Heap.GetObjectType(c.Ptr); foreach (ClrObject o in type.EnumerateObjectReferences(c.Ptr)) { if (o.Type.Name == "System.String") { continue; } ClrType refType = runtime.Heap.GetObjectType(o.Address); TreeNode refNode = new TreeNode(refType.Name); refNode.Nodes.Add(new TreeNode()); ClrTypeHelper refHelper = new ClrTypeHelper() { Ptr = o.Address, Name = refType.Name, Size = type.GetSize(o.Address), TreeNode = refNode }; System.Diagnostics.Debug.WriteLine("{0:X} {1}", o.Address, refType.Name); refNode.Tag = refHelper; e.Node.Nodes.Add(refNode); globalSearchList.Add(refHelper); } foreach (ClrInstanceField f in type.Fields) { System.Diagnostics.Debug.WriteLine("{0:X} {1}", f.GetAddress(c.Ptr), f.Type); } } }
private static void PrintManagedObjects(ClrRuntime runtime) { if (!runtime.Heap.CanWalkHeap) { Console.WriteLine("Cannot walk the heap!"); } else { foreach (ulong obj in runtime.Heap.EnumerateObjectAddresses()) { ClrType type = runtime.Heap.GetObjectType(obj); // If heap corruption, continue past this object. if (type == null) { continue; } ulong size = type.GetSize(obj); LogHelper.Logger.InfoFormat("{0,12:X} {1,8:n0} {2,1:n0} {3}", obj, size, runtime.Heap.GetGeneration(obj), type.Name); } } }
//TODO serialize this properly public void InitMemoryStat() { if (this.context.Runtime != null && this.context.Heap != null) { foreach (var obj in this.context.Heap.EnumerateObjectAddresses()) { ulong value; // get value at pointer this.context.Runtime.ReadPointer(obj, out value); ClrType type = this.context.Heap.GetObjectType(obj); if (type == null || type.IsFree) { continue; } // needs obj reference, because types can be variable sized in CLR! ulong size = type.GetSize(obj); if (memDict.ContainsKey(value)) { memDict[value].Count++; memDict[value].Size += size; } else { memDict.Add(value, new SDMemoryObject { Count = 1, Size = size, Type = string.IsNullOrEmpty(type.Name) ? "Unknown" : type.Name } ); } } } }
private static HeapSnapshot Compute(ClrHeap heap) { var snapshot = new HeapSnapshot(); foreach (ulong objAddress in heap.EnumerateObjectAddresses()) { ClrType type = heap.GetObjectType(objAddress); if (type == null) { continue; } var mt = type.MethodTable.ToString(); var name = type.Name; var key = HeapSnapshot.GetKey(name, mt); if (!snapshot.TypeStats.TryGetValue(key, out var entry)) { entry = new TypeEntry() { Name = name, MethodTable = mt }; snapshot.TypeStats[key] = entry; } entry.Count = entry.Count + 1; snapshot.ObjectCount = snapshot.ObjectCount + 1; var size = type.GetSize(objAddress); entry.TotalSize = entry.TotalSize + (long)size; snapshot.Size = snapshot.Size + (long)size; } return(snapshot); }
static Dictionary <ClrType, Entry> DumpHeap(ClrRuntime runtime) { Dictionary <ClrType, Entry> stats = new Dictionary <ClrType, Entry>(); try { // Create a ClrRuntime instance from the dump and dac location. The ClrRuntime // object represents a version of CLR loaded in the process. It contains data // such as the managed threads in the process, the AppDomains in the process, // the managed heap, and so on. //ClrRuntime runtime = CreateRuntime(dump, dac); // Walk the entire heap and build heap statistics in "stats". // This is the way to walk every object on the heap: Get the ClrHeap instance // from the runtime. Walk every segment in heap.Segments, and use // ClrSegment.FirstObject and ClrSegment.NextObject to iterate through // objects on that segment. ClrHeap heap = runtime.GetHeap(); foreach (ClrSegment seg in heap.Segments) { for (ulong obj = seg.FirstObject; obj != 0; obj = seg.NextObject(obj)) { // This gets the type of the object. ClrType type = heap.GetObjectType(obj); if (type == null) { continue; } ulong size = type.GetSize(obj); //objs[obj] = type; // If the user didn't request "-stat", print out the object. // Console.WriteLine("{0,16:X} {1,12:n0} {2}", obj, size, type.Name); // Add an entry to the dictionary, if one doesn't already exist. Entry entry = null; if (!stats.TryGetValue(type, out entry)) { entry = new Entry(); entry.Name = type.Name; entry.refs.Add(obj); //entry.obj = obj; stats[type] = entry; } else { //entry.obj = obj; entry.refs.Add(obj); } // Update the statistics for this object. entry.Count++; entry.Size += type.GetSize(obj); //type.EnumerateRefsOfObject(obj, delegate (ulong child, int offset) //{ // objs[obj] = child; //}); } } // Now print out statistics. // Console.WriteLine(); // We'll actually let linq do the heavy lifting. var sortedStats = from entry in stats.Values orderby entry.Size select entry; //Console.WriteLine("{0,12} {1,12} {2}", "Size", "Count", "Type"); //foreach (var entry in sortedStats) // Console.WriteLine("{0,12:n0} {1,12:n0} {2}", entry.Size, entry.Count, entry.Name); } catch (Exception ex) { Console.WriteLine("Unhandled exception:"); Console.WriteLine(ex); } finally { } return(stats); }
private static (uint, ulong) PopulateReferencedObjects(ClrHeap heap, ClrObjectModel clrObjModel) { // Evaluation stack var eval = new Stack <ulong>(); var addressToClrObjectModel = new Dictionary <ulong, ClrObjectModel> { { clrObjModel.Address, clrObjModel } }; // To make sure we don't count the same object twice, we'll keep a set of all objects // we've seen before. Note the ObjectSet here is basically just "HashSet<ulong>". // However, HashSet<ulong> is *extremely* memory inefficient. So we use our own to // avoid OOMs. var currentObj = clrObjModel.Address; var considered = new ObjectSet(heap); uint count = 0; ulong size = 0; eval.Push(currentObj); while (eval.Count > 0) { // Pop an object, ignore it if we've seen it before. currentObj = eval.Pop(); if (considered.Contains(currentObj)) { continue; } considered.Add(currentObj); // Grab the type. We will only get null here in the case of heap corruption. ClrType type = heap.GetObjectType(currentObj); if (type == null) { continue; } count++; size += type.GetSize(currentObj); // Now enumerate all objects that this object points to, add them to the // evaluation stack if we haven't seen them before. type.EnumerateRefsOfObject(currentObj, delegate(ulong child, int offset) { if (child != 0 && !considered.Contains(child)) { var childObj = heap.GetObject(child); ClrObjectModel childClrModel = null; if (addressToClrObjectModel.ContainsKey(childObj)) { childClrModel = addressToClrObjectModel[childObj]; } else { childClrModel = new ClrObjectModel { TypeName = childObj.Type.Name, Address = childObj, ReferencedObjects = new List <ClrObjectModel>(), }; addressToClrObjectModel[currentObj] = childClrModel; } addressToClrObjectModel[currentObj].ReferencedObjects.Add(childClrModel); eval.Push(child); } }); } return(count, size); }
public void OptimizedClassHistogram() { using (DataTarget dt = TestTargets.Types.LoadFullDump()) { ClrRuntime runtime = dt.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; // ensure that the optimized version is faster than the default one Stopwatch timing = new Stopwatch(); var notOptimizedStatistics = new Dictionary <ClrType, TypeEntry>(32768); int notOptimizedObjectCount = 0; // with an optimized version timing.Start(); foreach (var objDetails in heap.EnumerateObjectDetails()) { ClrType type = objDetails.Item3; ulong size = objDetails.Item4; TypeEntry entry; if (!notOptimizedStatistics.TryGetValue(type, out entry)) { entry = new TypeEntry() { TypeName = type.Name, Size = 0 }; notOptimizedStatistics[type] = entry; } entry.Count++; entry.Size += size; notOptimizedObjectCount++; } timing.Stop(); var notOptimizedTime = TimeSpan.FromMilliseconds(timing.ElapsedMilliseconds); // with an non-optimized version var statistics = new Dictionary <ClrType, TypeEntry>(32768); int objectCount = 0; timing.Restart(); foreach (ulong objAddress in heap.EnumerateObjectAddresses()) { ClrType type = heap.GetObjectType(objAddress); ulong size = type.GetSize(objAddress); TypeEntry entry; if (!statistics.TryGetValue(type, out entry)) { entry = new TypeEntry() { TypeName = type.Name, Size = 0 }; statistics[type] = entry; } entry.Count++; entry.Size += size; objectCount++; } timing.Stop(); // check object count Assert.AreEqual(notOptimizedObjectCount, objectCount); // check heap content var types = notOptimizedStatistics.Keys; foreach (var type in types) { var notOptimizedDetails = notOptimizedStatistics[type]; var details = statistics[type]; Assert.AreEqual(notOptimizedDetails.TypeName, details.TypeName); Assert.AreEqual(notOptimizedDetails.Count, details.Count); Assert.AreEqual(notOptimizedDetails.Size, details.Size); } Assert.AreEqual(types.Count, statistics.Count); // checking that optimized is faster could be flaky // Assert.IsTrue(notOptimizedTime > TimeSpan.FromMilliseconds(timing.ElapsedMilliseconds)); } }
public override bool TryGetMember(GetMemberBinder binder, out object result) { if (IsNull()) { result = new ClrNullValue(m_heap); return(true); } if (binder.Name == "__Size") { result = m_type.GetSize(m_addr); return(true); } if (binder.Name == "__Type") { result = m_type.Name; return(true); } if (binder.Name == "__Address") { result = String.Format("{0:x16}", m_addr); return(true); } if (binder.Name == "__Fields") { result = (from f in m_type.Fields where f.Type != null select new { Name = f.Name, Type = f.Type.Name }).ToArray(); return(true); } ClrInstanceField field = null; foreach (var instanceField in m_type.Fields) { // Auto-generated properties have this ugly <> thing around their fields that you can't put // in a C# expression. Clean them up to make them accessible by their property name. var cleanFieldName = instanceField.Name; if (cleanFieldName.StartsWith("<") && cleanFieldName.EndsWith(">k__BackingField")) { cleanFieldName = cleanFieldName.Substring( 1, cleanFieldName.Length - (1 + ">k__BackingField".Length)); } if (cleanFieldName == binder.Name) { field = instanceField; break; } } if (field == null) { if (ClrDynamicClass.GetStaticField(m_heap, m_type, binder, out result)) { return(true); } throw new InvalidOperationException(string.Format("Type '{0}' does not contain a '{1}' field.", m_type.Name, binder.Name)); } if (field.IsPrimitive()) { object value = field.GetValue(m_addr, m_inner); if (value == null) { result = new ClrNullValue(m_heap); } else { result = new ClrPrimitiveValue(value, field.ElementType); } return(true); } else if (field.IsValueClass()) { ulong addr = field.GetAddress(m_addr, m_inner); result = new ClrObject(m_heap, field.Type, addr, true); return(true); } else if (field.ElementType == ClrElementType.String) { ulong addr = field.GetAddress(m_addr, m_inner); if (!m_heap.GetRuntime().ReadPointer(addr, out addr)) { result = new ClrNullValue(m_heap); return(true); } result = new ClrObject(m_heap, field.Type, addr); return(true); } else { object value = field.GetValue(m_addr, m_inner); if (value == null) { result = new ClrNullValue(m_heap); } else { result = new ClrObject(m_heap, field.Type, (ulong)value); } return(true); } }
public override ulong GetSize(ulong objRef) { ClrType realType = DesktopHeap.GetObjectType(objRef); return(realType.GetSize(objRef)); }
/// <summary> /// GetSize returns the size in bytes for the total overhead of the object 'objRef'. /// </summary> /// <param name="objRef">The object reference.</param> /// <returns>System.UInt64.</returns> public ulong GetSize(ulong objRef) => ClrType.GetSize(objRef);
static void Main(string[] args) { bool stat, live; string dump, dac; if (!TryParseArgs(args, out dump, out dac, out stat, out live)) { Usage(); Environment.Exit(1); } try { ClrRuntime runtime = CreateRuntime(dump, dac); ClrHeap heap = runtime.GetHeap(); ObjectSet liveObjs = null; if (live) { liveObjs = GetLiveObjects(heap); } Dictionary <ClrType, Entry> stats = new Dictionary <ClrType, Entry>(); if (!stat) { Console.WriteLine("{0,16} {1,12} {2}", "Object", "Size", "Type"); } foreach (ClrSegment seg in heap.Segments) { for (ulong obj = seg.FirstObject; obj != 0; obj = seg.NextObject(obj)) { if (live && !liveObjs.Contains(obj)) { continue; } // This gets the type of the object. ClrType type = heap.GetObjectType(obj); ulong size = type.GetSize(obj); // If the user didn't request "-stat", print out the object. if (!stat) { Console.WriteLine("{0,16:X} {1,12:n0} {2}", obj, size, type.Name); } // Add an entry to the dictionary, if one doesn't already exist. Entry entry = null; if (!stats.TryGetValue(type, out entry)) { entry = new Entry(); entry.Name = type.Name; stats[type] = entry; } // Update the statistics for this object. entry.Count++; entry.Size += type.GetSize(obj); } } // Now print out statistics. if (!stat) { Console.WriteLine(); } // We'll actually let linq do the heavy lifting. var sortedStats = from entry in stats.Values orderby entry.Size select entry; ulong totalSize = 0, totalCount = 0; Console.WriteLine("{0,12} {1,12} {2}", "Size", "Count", "Type"); foreach (var entry in sortedStats) { Console.WriteLine("{0,12:n0} {1,12:n0} {2}", entry.Size, entry.Count, entry.Name); totalSize += entry.Size; totalCount += (uint)entry.Count; } Console.WriteLine(); Console.WriteLine("Total: {0:n0} bytes in {1:n0} objects", totalSize, totalCount); } catch (Exception ex) { Console.WriteLine("Unhandled exception:"); Console.WriteLine(ex); } }
private void InspectProcess(int pId) { using (DataTarget target = DataTarget.AttachToProcess(pId, 10000)) { ShowCLRRuntimeInformation(target); ClrRuntime runtime = target.ClrVersions.First().CreateRuntime(); LoadThreads(runtime); Dictionary <string, TypeEntry> types = new Dictionary <string, TypeEntry>(); Dictionary <string, StringInfor> stringCount = new Dictionary <string, StringInfor>(); ClrHeap heap = runtime.Heap; var finalizerQueueObjects = runtime.EnumerateFinalizerQueueObjectAddresses().ToList(); var pinnedObjects = runtime.EnumerateHandles().Where(h => h.IsPinned).Select(h => h.Object).ToList(); var blockingObjects = heap.EnumerateBlockingObjects().Select(x => x.Object).ToList(); //var blockingObjects = runtime.Threads.SelectMany(x => x.BlockingObjects).Select(x => x.Object).ToList(); foreach (ulong obj in heap.EnumerateObjects()) { ClrType type = heap.GetObjectType(obj); var size = type.GetSize(obj); var gen = heap.GetGeneration(obj); var isInFinalizerQueue = finalizerQueueObjects.Contains(obj); var isPinned = pinnedObjects.Contains(obj); var isBlocking = blockingObjects.Contains(obj); if (types.ContainsKey(type.Name)) { types[type.Name].Count++; types[type.Name].MinSize = Math.Min(types[type.Name].MinSize, size); types[type.Name].MaxSize = Math.Max(types[type.Name].MaxSize, size); types[type.Name].TotalSize += size; types[type.Name].Generation0 += gen == 0 ? 1 : 0; types[type.Name].Generation1 += gen == 1 ? 1 : 0; types[type.Name].Generation2 += gen == 2 ? 1 : 0; types[type.Name].FinalizerQueueCount += isInFinalizerQueue ? 1 : 0; types[type.Name].PinnedCount += isPinned ? 1 : 0; types[type.Name].BlockingCount += isBlocking ? 1 : 0; } else { types[type.Name] = new TypeEntry { Name = type.Name, Count = 1, MinSize = size, MaxSize = size, TotalSize = size, Generation0 = gen == 0 ? 1 : 0, Generation1 = gen == 1 ? 1 : 0, Generation2 = gen == 2 ? 1 : 0, FinalizerQueueCount = isInFinalizerQueue ? 1 : 0, PinnedCount = isPinned ? 1 : 0, BlockingCount = isBlocking ? 1 : 0, }; } if (type.IsString) { var text = (string)type.GetValue(obj); if (stringCount.ContainsKey(text)) { stringCount[text].Count++; stringCount[text].Generation0 += gen == 0 ? 1 : 0; stringCount[text].Generation1 += gen == 1 ? 1 : 0; stringCount[text].Generation2 += gen == 2 ? 1 : 0; } else { stringCount[text] = new StringInfor { Text = text, Count = 1, Generation0 = gen == 0 ? 1 : 0, Generation1 = gen == 1 ? 1 : 0, Generation2 = gen == 2 ? 1 : 0, }; } } } var sortOrder = heapObjectsGrid.SortOrder == SortOrder.Ascending ? ListSortDirection.Ascending : ListSortDirection.Descending; var sortCol = heapObjectsGrid.SortedColumn; int rowcount = 0; heapObjectsGrid.Rows.Clear(); foreach (var val in types.Where(x => x.Value.Count > 1)) { var infor = val.Value; DataGridViewRow gridrow = new DataGridViewRow(); gridrow.DefaultCellStyle.SelectionBackColor = Color.Black; if (rowcount % 2 > 0) { gridrow.DefaultCellStyle.BackColor = Color.AliceBlue; } rowcount++; DataGridViewTextBoxCell name = new DataGridViewTextBoxCell(); name.Value = infor.Name; gridrow.Cells.Add(name); DataGridViewTextBoxCell count = new DataGridViewTextBoxCell(); count.Style.Alignment = DataGridViewContentAlignment.BottomRight; count.Style.Format = "n0"; count.Value = infor.Count; gridrow.Cells.Add(count); DataGridViewTextBoxCell minSize = new DataGridViewTextBoxCell(); minSize.Style.Alignment = DataGridViewContentAlignment.BottomRight; minSize.Style.Format = "n0"; if (infor.MinSize >= 85000) { minSize.Style.ForeColor = Color.Orange; minSize.ToolTipText = "Large Object Heap"; } minSize.Value = infor.MinSize; gridrow.Cells.Add(minSize); DataGridViewTextBoxCell maxSize = new DataGridViewTextBoxCell(); maxSize.Style.Alignment = DataGridViewContentAlignment.BottomRight; maxSize.Style.Format = "n0"; if (infor.MaxSize >= 85000) { maxSize.Style.ForeColor = Color.Orange; maxSize.ToolTipText = "Large Object Heap"; } maxSize.Value = infor.MaxSize; gridrow.Cells.Add(maxSize); DataGridViewTextBoxCell totalSize = new DataGridViewTextBoxCell(); totalSize.Style.Alignment = DataGridViewContentAlignment.BottomRight; totalSize.Style.Format = "n0"; totalSize.Value = infor.TotalSize; gridrow.Cells.Add(totalSize); DataGridViewTextBoxCell generation0 = new DataGridViewTextBoxCell(); generation0.Value = infor.Generation0; generation0.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation0.Style.Format = "n0"; gridrow.Cells.Add(generation0); DataGridViewTextBoxCell generation1 = new DataGridViewTextBoxCell(); generation1.Value = infor.Generation1; generation1.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation1.Style.Format = "n0"; gridrow.Cells.Add(generation1); DataGridViewTextBoxCell generation2 = new DataGridViewTextBoxCell(); generation2.Value = infor.Generation2; generation2.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation2.Style.Format = "n0"; gridrow.Cells.Add(generation2); DataGridViewTextBoxCell finalizerQueue = new DataGridViewTextBoxCell(); finalizerQueue.Value = infor.FinalizerQueueCount; finalizerQueue.Style.Alignment = DataGridViewContentAlignment.BottomRight; finalizerQueue.Style.Format = "n0"; gridrow.Cells.Add(finalizerQueue); DataGridViewTextBoxCell pinned = new DataGridViewTextBoxCell(); pinned.Value = infor.PinnedCount; pinned.Style.Alignment = DataGridViewContentAlignment.BottomRight; pinned.Style.Format = "n0"; gridrow.Cells.Add(pinned); DataGridViewTextBoxCell blocking = new DataGridViewTextBoxCell(); blocking.Value = infor.BlockingCount; blocking.Style.Alignment = DataGridViewContentAlignment.BottomRight; blocking.Style.Format = "n0"; gridrow.Cells.Add(blocking); heapObjectsGrid.Rows.Add(gridrow); } heapObjectsGrid.Columns[1].HeaderCell.ToolTipText = $"Total: { types.Values.Sum(x => x.Count).ToString("N0")} objects"; heapObjectsGrid.Columns[4].HeaderCell.ToolTipText = $"Total: {types.Values.Sum(x => (long)x.TotalSize).ToString("N0")} bytes"; if (sortCol != null) { heapObjectsGrid.Sort(sortCol, sortOrder); } // Strings Grid View: rowcount = 0; stringsGrid.Rows.Clear(); foreach (var str in stringCount) { DataGridViewRow gridrow = new DataGridViewRow(); gridrow.DefaultCellStyle.SelectionBackColor = Color.Black; if (rowcount % 2 > 0) { gridrow.DefaultCellStyle.BackColor = Color.AliceBlue; } rowcount++; DataGridViewTextBoxCell name = new DataGridViewTextBoxCell(); name.Value = str.Key; gridrow.Cells.Add(name); DataGridViewTextBoxCell length = new DataGridViewTextBoxCell(); length.Style.Alignment = DataGridViewContentAlignment.BottomRight; length.Style.Format = "n0"; length.Value = str.Key.Length; gridrow.Cells.Add(length); DataGridViewTextBoxCell count = new DataGridViewTextBoxCell(); count.Style.Alignment = DataGridViewContentAlignment.BottomRight; count.Style.Format = "n0"; count.Value = str.Value.Count; gridrow.Cells.Add(count); DataGridViewTextBoxCell generation0 = new DataGridViewTextBoxCell(); generation0.Value = str.Value.Generation0; generation0.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation0.Style.Format = "n0"; gridrow.Cells.Add(generation0); DataGridViewTextBoxCell generation1 = new DataGridViewTextBoxCell(); generation1.Value = str.Value.Generation1; generation1.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation1.Style.Format = "n0"; gridrow.Cells.Add(generation1); DataGridViewTextBoxCell generation2 = new DataGridViewTextBoxCell(); generation2.Value = str.Value.Generation2; generation2.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation2.Style.Format = "n0"; gridrow.Cells.Add(generation2); stringsGrid.Rows.Add(gridrow); } } }
public void GetSize(ulong objRef, out ulong pSize) { pSize = m_type.GetSize(objRef); }
/// <summary> /// Find all Strings in Heap an generate a dublicate string report /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonStrings_Click(object sender, EventArgs e) { if (Process == null) { return; } saveFileDialogStrings.FileName = String.Format("clrmd_{0:yyyyMMdd_HHmmss}", DateTime.Now); if (saveFileDialogStrings.ShowDialog() != DialogResult.OK) { return; } if (File.Exists(saveFileDialogStrings.FileName)) { File.Delete(saveFileDialogStrings.FileName); } Cursor.Current = Cursors.WaitCursor; HashSet <StringObjectHelper> stringObjectList = new HashSet <StringObjectHelper>(); ulong fullSize = 0; int objectCount = 0; using (StreamWriter writer = new StreamWriter(saveFileDialogStrings.FileName, true, Encoding.UTF8)) { using (DataTarget dataTarget = DataTarget.AttachToProcess(Process.Id, dataTargetTimeOut, dataTargetAttachFlag)) { ClrInfo clrVersion = dataTarget.ClrVersions.First(); ClrRuntime runtime = clrVersion.CreateRuntime(); if (runtime.Heap.CanWalkHeap) { foreach (ulong ptr in runtime.Heap.EnumerateObjectAddresses()) { ClrType type = runtime.Heap.GetObjectType(ptr); if (type == null || type.IsString == false) { continue; } StringObjectHelper stringObject = new StringObjectHelper(); stringObject.String = (string)type.GetValue(ptr); stringObject.Size = type.GetSize(ptr); StringObjectHelper stringObjectFound; if (stringObjectList.TryGetValue(stringObject, out stringObjectFound)) { stringObjectFound.PtrList.Add(ptr); } else { stringObject.PtrList.Add(ptr); stringObjectList.Add(stringObject); } objectCount++; fullSize += stringObject.Size; //write all strings writer.WriteLine("**{0}#{1:X}#G{2}#{3:n0}", objectCount, ptr, type.Heap.GetGeneration(ptr), stringObject.Size); writer.WriteLine(stringObject.String); } } writer.WriteLine(); writer.WriteLine("**Position#HeapPtr#Generation#Size"); writer.WriteLine(); writer.WriteLine("{0:n0} String Objects", objectCount); writer.WriteLine("{0:n0} String unique Strings", stringObjectList.Count); writer.WriteLine("{0:n0} Bytes", fullSize); } } //write dublicate info var orderedList = stringObjectList.OrderByDescending(x => x.PtrList.Count); using (StreamWriter writer = new StreamWriter(saveFileDialogStrings.FileName + ".unique.csv", true, Encoding.UTF8)) { writer.WriteLine("Count;Size;FullSize;Content;Ptrs"); foreach (StringObjectHelper so in orderedList) { string pointerList = String.Join(",", so.PtrList.Select(x => String.Format("x{0:X}", x))); writer.WriteLine(String.Format("{0};{1};{2};{3};{4}", so.PtrList.Count, so.Size, so.Size * (ulong)so.PtrList.Count, (so.String.Length > 50 ? so.String.Substring(0, 50) + " ..." : so.String).Replace("'", "").Replace(";", "").Replace("\r", " ").Replace("\n", ""), pointerList.Length > 50 ? pointerList.Substring(0, 50) + " ..." : pointerList)); } } Cursor.Current = Cursors.Default; }
static void Main(string[] args) { bool stat; string dump, dac; if (!TryParseArgs(args, out dump, out dac, out stat)) { Usage(); Environment.Exit(1); } try { // Create a ClrRuntime instance from the dump and dac location. The ClrRuntime // object represents a version of CLR loaded in the process. It contains data // such as the managed threads in the process, the AppDomains in the process, // the managed heap, and so on. ClrRuntime runtime = CreateRuntime(dump, dac); // Walk the entire heap and build heap statistics in "stats". Dictionary <ClrType, Entry> stats = new Dictionary <ClrType, Entry>(); if (!stat) { Console.WriteLine("{0,16} {1,12} {2}", "Object", "Size", "Type"); } // This is the way to walk every object on the heap: Get the ClrHeap instance // from the runtime. Walk every segment in heap.Segments, and use // ClrSegment.FirstObject and ClrSegment.NextObject to iterate through // objects on that segment. ClrHeap heap = runtime.GetHeap(); foreach (ClrSegment seg in heap.Segments) { for (ulong obj = seg.FirstObject; obj != 0; obj = seg.NextObject(obj)) { // This gets the type of the object. ClrType type = heap.GetObjectType(obj); ulong size = type.GetSize(obj); // If the user didn't request "-stat", print out the object. if (!stat) { Console.WriteLine("{0,16:X} {1,12:n0} {2}", obj, size, type.Name); } // Add an entry to the dictionary, if one doesn't already exist. Entry entry = null; if (!stats.TryGetValue(type, out entry)) { entry = new Entry(); entry.Name = type.Name; stats[type] = entry; } // Update the statistics for this object. entry.Count++; entry.Size += type.GetSize(obj); } } // Now print out statistics. if (!stat) { Console.WriteLine(); } // We'll actually let linq do the heavy lifting. var sortedStats = from entry in stats.Values orderby entry.Size select entry; Console.WriteLine("{0,12} {1,12} {2}", "Size", "Count", "Type"); foreach (var entry in sortedStats) { Console.WriteLine("{0,12:n0} {1,12:n0} {2}", entry.Size, entry.Count, entry.Name); } } catch (Exception ex) { Console.WriteLine("Unhandled exception:"); Console.WriteLine(ex); } }
private static void ExamineProcessHeap(ClrRuntime runtime, ClrHeap heap, bool showGcHeapInfo) { if (!heap.CanWalkHeap) { Console.WriteLine("Cannot walk the heap!"); return; } ulong totalStringObjectSize = 0, stringObjectCounter = 0, byteArraySize = 0; ulong asciiStringSize = 0, unicodeStringSize = 0, isoStringSize = 0; //, utf8StringSize = 0; ulong asciiStringCount = 0, unicodeStringCount = 0, isoStringCount = 0; //, utf8StringCount = 0; ulong compressedStringSize = 0, uncompressedStringSize = 0; foreach (var obj in heap.EnumerateObjectAddresses()) { ClrType type = heap.GetObjectType(obj); // If heap corruption, continue past this object. Or if it's NOT a String we also ignore it if (type == null || type.IsString == false) { continue; } stringObjectCounter++; var text = (string)type.GetValue(obj); var rawBytes = Encoding.Unicode.GetBytes(text); totalStringObjectSize += type.GetSize(obj); byteArraySize += (ulong)rawBytes.Length; VerifyStringObjectSize(runtime, type, obj, text); // Try each encoding in order, so we find the most-compact encoding that the text would fit in byte[] textAsBytes = null; if (IsASCII(text, out textAsBytes)) { asciiStringSize += (ulong)rawBytes.Length; asciiStringCount++; // ASCII is compressed as ISO-8859-1 (Latin-1) NOT ASCII if (IsIsoLatin1(text, out textAsBytes)) { compressedStringSize += (ulong)textAsBytes.Length; } else { Console.WriteLine("ERROR: \"{0}\" is ASCII but can't be encoded as ISO-8859-1 (Latin-1)", text); } } // From http://stackoverflow.com/questions/7048745/what-is-the-difference-between-utf-8-and-iso-8859-1 // "ISO 8859-1 is a single-byte encoding that can represent the first 256 Unicode characters" else if (IsIsoLatin1(text, out textAsBytes)) { isoStringSize += (ulong)rawBytes.Length; isoStringCount++; compressedStringSize += (ulong)textAsBytes.Length; } // UTF-8 and UTF-16 can both support the same range of text/character values ("Code Points"), they just store it in different ways // From http://stackoverflow.com/questions/4655250/difference-between-utf-8-and-utf-16/4655335#4655335 // "Both UTF-8 and UTF-16 are variable length (multi-byte) encodings. // However, in UTF-8 a character may occupy a minimum of 8 bits, while in UTF-16 character length starts with 16 bits." //else if (IsUTF8(text, out textAsBytes)) //{ // utf8StringSize += (ulong)rawBytes.Length; // utf8StringCount++; // compressedStringSize += (ulong)textAsBytes.Length; //} else { unicodeStringSize += (ulong)rawBytes.Length; unicodeStringCount++; uncompressedStringSize += (ulong)rawBytes.Length; } } Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("\n\"System.String\" memory usage info"); Console.ResetColor(); Console.WriteLine("Overall {0:N0} \"System.String\" objects take up {1:N0} bytes ({2:N2} MB)", stringObjectCounter, totalStringObjectSize, totalStringObjectSize / 1024.0 / 1024.0); Console.WriteLine("Of this underlying byte arrays (as Unicode) take up {0:N0} bytes ({1:N2} MB)", byteArraySize, byteArraySize / 1024.0 / 1024.0); Console.WriteLine("Remaining data (object headers, other fields, etc) are {0:N0} bytes ({1:N2} MB), at {2:0.##} bytes per object\n", totalStringObjectSize - byteArraySize, (totalStringObjectSize - byteArraySize) / 1024.0 / 1024.0, (totalStringObjectSize - byteArraySize) / (double)stringObjectCounter); Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("Actual Encoding that the \"System.String\" could be stored as (with corresponding data size)"); Console.ResetColor(); Console.WriteLine(" {0,15:N0} bytes ({1,8:N0} strings) as ASCII", asciiStringSize, asciiStringCount); Console.WriteLine(" {0,15:N0} bytes ({1,8:N0} strings) as ISO-8859-1 (Latin-1)", isoStringSize, isoStringCount); //Console.WriteLine(" {0,15:N0} bytes ({1,8:N0} strings) are UTF-8", utf8StringSize, utf8StringCount); Console.WriteLine(" {0,15:N0} bytes ({1,8:N0} strings) as Unicode", unicodeStringSize, unicodeStringCount); Console.WriteLine("Total: {0:N0} bytes (expected: {1:N0}{2})\n", asciiStringSize + isoStringSize + unicodeStringSize, byteArraySize, (asciiStringSize + isoStringSize + unicodeStringSize != byteArraySize) ? " - ERROR" : ""); Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("Compression Summary:"); Console.ResetColor(); Console.WriteLine(" {0,15:N0} bytes Compressed (to ISO-8859-1 (Latin-1))", compressedStringSize); Console.WriteLine(" {0,15:N0} bytes Uncompressed (as Unicode)", uncompressedStringSize); Console.WriteLine(" {0,15:N0} bytes EXTRA to enable compression (1-byte field, per \"System.String\" object)", stringObjectCounter); var totalBytesUsed = compressedStringSize + uncompressedStringSize + stringObjectCounter; var totalBytesSaved = byteArraySize - totalBytesUsed; Console.WriteLine("\nTotal Usage: {0:N0} bytes ({1:N2} MB), compared to {2:N0} ({3:N2} MB) before compression", totalBytesUsed, totalBytesUsed / 1024.0 / 1024.0, byteArraySize, byteArraySize / 1024.0 / 1024.0); Console.WriteLine("Total Saving: {0:N0} bytes ({1:N2} MB)\n", totalBytesSaved, totalBytesSaved / 1024.0 / 1024.0); }
private static void RunApplication(CommandLineOptions opts) { if (opts.PID == 0 && String.IsNullOrEmpty(opts.ProcessName)) { ShowHelp(null); return; } ; List <int> pids = new List <int>(); if (opts.PID != 0) { pids.Add(opts.PID); } if (!string.IsNullOrEmpty(opts.ProcessName)) { var o = Process.GetProcessesByName(opts.ProcessName); pids.AddRange(o.Select(s => s.Id)); } if (pids.Any()) { Console.WriteLine($"Dumping {pids.Count} process ids."); foreach (int thisPid in pids) { try { bool currProc64 = CLRUtility.Is64BitProcess(); bool remoteProc64 = CLRUtility.Is64BitProcess(thisPid); Console.WriteLine(""); Console.WriteLine($"Process ID: {thisPid}"); if (currProc64 != remoteProc64) { Console.WriteLine("Process Bitness must match. This process is {0}, {1} is {2}", currProc64 ? "64-bit" : "32-bit", thisPid, remoteProc64 ? "64-bit" : "32-bit"); return; } using (DataTarget d = DataTarget.AttachToProcess(thisPid, 5000, AttachFlag.NonInvasive)) { Console.WriteLine($"Dumping all CLRversions for process {thisPid}"); foreach (ClrInfo currInfo in d.ClrVersions) { ClrRuntime runtime = currInfo.CreateRuntime(); Console.WriteLine($"CLR: {currInfo.Version} GC: {(runtime.ServerGC ? "Server " : "Workstation")} HeapSize: 0:{runtime.Heap.GetSizeByGen(0),12} 1:{runtime.Heap.GetSizeByGen(1),12} L:{runtime.Heap.GetSizeByGen(2),12}", ""); foreach (ClrAppDomain domain in runtime.AppDomains) { IEnumerable <ClrThread> threadList = runtime.Threads.Where(x => (x.AppDomain == domain.Address)); string domainInfo = $"App Domain: {domain.Name.PadRight(40)} ID: {domain.Id} Threads: {threadList.Count()} Address: {domain.Address,12:X8}"; Console.WriteLine(domainInfo); Console.WriteLine($"Modules: {domain.Modules.Count.ToString("D3")} (showing GAC? {opts.GAC})"); foreach (ClrModule currMod in domain.Modules) { bool printMe = (!string.IsNullOrEmpty(currMod.AssemblyName)) && (!currMod.AssemblyName.Contains("\\GAC_") | opts.GAC); if (printMe) { Console.WriteLine($"{currMod.AssemblyName}"); } } if (!opts.HideThreads) { Console.WriteLine(""); Console.WriteLine("Threads:"); foreach (ClrThread clrt in threadList) { string threadInfo = $" Thread: {clrt.ManagedThreadId,3:G} NT: {clrt.OSThreadId,12:X8} GC: {clrt.IsGC} {(clrt.IsAlive ? " " : "DEAD")}"; Console.WriteLine(threadInfo); int frameNum = 0; if (!opts.ShowFrames) { continue; } foreach (ClrStackFrame frame in clrt.StackTrace) { string frameInfo = $" Frame: {frameNum,2:G} IP: {frame.InstructionPointer,12:X} {frame.DisplayString}"; Console.WriteLine(frameInfo); frameNum++; } } } if (opts.Heap) { Console.WriteLine(""); Console.WriteLine("Heap Segments:"); Console.WriteLine("{0,12} {1,12} {2,12} {3,12} {4,4} {5}", "Start", "End", "Committed", "Reserved", "Proc", "Type"); foreach (ClrSegment thisSeg in runtime.Heap.Segments) { string type; if (thisSeg.IsEphemeral) { type = "Ephemeral"; } else if (thisSeg.IsLarge) { type = "Large"; } else { type = "Gen2"; } Console.WriteLine("{0,12:X} {1,12:X} {2,12:X} {3,12:X} {4,4} {5}", thisSeg.Start, thisSeg.End, thisSeg.CommittedEnd, thisSeg.ReservedEnd, thisSeg.ProcessorAffinity, type); } if (opts.Recurse) { if (!runtime.Heap.CanWalkHeap) { Console.WriteLine("Can't walk heap!"); } else { Console.WriteLine("Dumping LOH"); foreach (ClrSegment thisSeg in runtime.Heap.Segments.Where(x => x.IsLarge)) { for (ulong objId = thisSeg.FirstObject; objId != 0; objId = thisSeg.NextObject(objId)) { ClrType thisType = runtime.Heap.GetObjectType(objId); ulong thisSize = thisType.GetSize(objId); Console.WriteLine("{0,12:X} {1,8:n0} {2,1:n0} {3}", objId, thisSize, thisSeg.GetGeneration(objId), thisType.Name); } } } } } Console.WriteLine(""); } } } } catch (ArgumentException ae) { Console.WriteLine(ae.Message); } catch (ApplicationException aex) { Console.WriteLine(aex.Message); } catch (Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine(ex.GetType()); Console.WriteLine(ex.StackTrace); Console.WriteLine("Cannot get the process. " + " Make sure the process exists and it's a managed process and if it's running as admin, you need to be too."); } } //foreach (int thisPid in pids) { } else { Console.WriteLine($"No PIDS to process. Input Name: {opts.ProcessName} pid: {opts.PID}"); }; }