package org.deidentifier.arx.gui; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import org.deidentifier.arx.gui.view.SWTUtil; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.DeviceData; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.List; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; /** * Code based on: https://www.eclipse.org/articles/swt-design-2/sleak.htm * */ public class DebugResourceLeaks { class Resource { Object resource; Error error; int occurrences; @Override protected Resource clone() { Resource resource = new Resource(); resource.error = error; resource.resource = this.resource; resource.occurrences = occurrences; return resource; } } public static void main(String[] args) { DebugResourceLeaks sleak = new DebugResourceLeaks(); Display display = sleak.open(); Main.main(display, new String[0]); } private Display display; private Shell shell; private Label resourceStatistics; private Label resourceStackTrace; private List listResources; private List listResourcesSameStackTrace; private Resource[] resources; private Resource[] resourcesSameStackTrace; private void collectAll() { DeviceData info = display.getDeviceData(); if (!info.tracking) { MessageBox dialog = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK); dialog.setText(shell.getText()); dialog.setMessage("Warning: Device is not tracking resource allocation"); dialog.open(); } Object[] objects = info.objects; Error[] errors = info.errors; resources = new Resource[objects.length]; for (int i = 0; i < resources.length; i++) { Resource resource = new Resource(); resource.error = errors[i]; resource.resource = objects[i]; resource.occurrences = 1; resources[i] = resource; } Map<String, Integer> objectTypesTimes = new TreeMap<String, Integer>(); Map<String, Resource> objectSameStackTrace = new HashMap<String, Resource>(); for (int i = 0; i < resources.length; i++) { String className = resources[i].resource.getClass().getSimpleName(); Integer count = objectTypesTimes.get(className); if (count == null) { objectTypesTimes.put(className, 1); } else { objectTypesTimes.put(className, count + 1); } String stackTrace = getStackTrace(resources[i].error); if (!objectSameStackTrace.containsKey(stackTrace)) { Resource resource = resources[i].clone(); resource.occurrences = 1; objectSameStackTrace.put(stackTrace, resource); } else { Resource resource = objectSameStackTrace.get(stackTrace); resource.occurrences++; } } resourcesSameStackTrace = new Resource[objectSameStackTrace.size()]; int idx = 0; for (Entry<String, Resource> entry : objectSameStackTrace.entrySet()) { resourcesSameStackTrace[idx] = entry.getValue(); idx++; } Arrays.sort(resourcesSameStackTrace, new Comparator<Resource>() { @Override public int compare(Resource o1, Resource o2) { return o2.occurrences - o1.occurrences; } }); StringBuilder statistics = new StringBuilder(); for (Entry<String, Integer> entry : objectTypesTimes.entrySet()) { statistics.append(entry.getKey()); statistics.append(": "); statistics.append(entry.getValue()); statistics.append("\n"); } statistics.append("Total: "); statistics.append(resources.length); statistics.append("\n"); // Display listResources.removeAll(); for (int i = 0; i < resources.length; i++) { listResources.add(resources[i].resource.getClass().getSimpleName() + "(" + resources[i].resource.hashCode() + ")"); } listResourcesSameStackTrace.removeAll(); for (int i = 0; i < resourcesSameStackTrace.length; i++) { listResourcesSameStackTrace.add(resourcesSameStackTrace[i].resource.getClass().getSimpleName() + "(" + resourcesSameStackTrace[i].resource.hashCode() + ")" + "[" + resourcesSameStackTrace[i].occurrences + "x]"); } resourceStatistics.setText(statistics.toString()); } private String getStackTrace(Error error) { ByteArrayOutputStream stream = new ByteArrayOutputStream(); PrintStream s = new PrintStream(stream); error.printStackTrace(s); return stream.toString(); } private Display open() { DeviceData data = new DeviceData(); data.tracking = true; Display display = new Display(data); this.display = display; shell = new Shell(display); shell.setText("Resources"); shell.setLayout(SWTUtil.createGridLayout(2)); Button collect = new Button(shell, SWT.PUSH); collect.setText("Collect data"); collect.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { collectAll(); } }); final GridData d = new GridData(); d.grabExcessHorizontalSpace = true; d.horizontalSpan = 2; collect.setLayoutData(d); listResources = new List(shell, SWT.BORDER | SWT.V_SCROLL); listResources.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { selectObject(); } }); listResources.setLayoutData(SWTUtil.createFillGridData()); listResourcesSameStackTrace = new List(shell, SWT.BORDER | SWT.V_SCROLL); listResourcesSameStackTrace.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { selectEqualObject(); } }); listResourcesSameStackTrace.setLayoutData(SWTUtil.createFillGridData()); resourceStackTrace = new Label(shell, SWT.BORDER); resourceStackTrace.setText(""); resourceStackTrace.setLayoutData(SWTUtil.createFillGridData()); resourceStatistics = new Label(shell, SWT.BORDER); resourceStatistics.setText("0 object(s)"); resourceStatistics.setLayoutData(SWTUtil.createFillGridData()); shell.open(); return display; } private void selectEqualObject() { int index = listResourcesSameStackTrace.getSelectionIndex(); if (index == -1) { return; } resourceStackTrace.setText(getStackTrace(resourcesSameStackTrace[index].error)); resourceStackTrace.setVisible(true); } private void selectObject() { int index = listResources.getSelectionIndex(); if (index == -1) { return; } resourceStackTrace.setText(getStackTrace(resources[index].error)); resourceStackTrace.setVisible(true); } }