Home | History | Annotate | Download | only in ddmuilib
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ddmuilib;
     18 
     19 import com.android.ddmlib.AllocationInfo;
     20 import com.android.ddmlib.Client;
     21 import com.android.ddmlib.AllocationInfo.AllocationSorter;
     22 import com.android.ddmlib.AllocationInfo.SortMode;
     23 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
     24 import com.android.ddmlib.ClientData.AllocationTrackingStatus;
     25 
     26 import org.eclipse.jface.preference.IPreferenceStore;
     27 import org.eclipse.jface.viewers.ILabelProviderListener;
     28 import org.eclipse.jface.viewers.ISelection;
     29 import org.eclipse.jface.viewers.ISelectionChangedListener;
     30 import org.eclipse.jface.viewers.IStructuredContentProvider;
     31 import org.eclipse.jface.viewers.IStructuredSelection;
     32 import org.eclipse.jface.viewers.ITableLabelProvider;
     33 import org.eclipse.jface.viewers.SelectionChangedEvent;
     34 import org.eclipse.jface.viewers.TableViewer;
     35 import org.eclipse.jface.viewers.Viewer;
     36 import org.eclipse.swt.SWT;
     37 import org.eclipse.swt.SWTException;
     38 import org.eclipse.swt.events.ModifyEvent;
     39 import org.eclipse.swt.events.ModifyListener;
     40 import org.eclipse.swt.events.SelectionAdapter;
     41 import org.eclipse.swt.events.SelectionEvent;
     42 import org.eclipse.swt.graphics.Color;
     43 import org.eclipse.swt.graphics.Image;
     44 import org.eclipse.swt.graphics.Rectangle;
     45 import org.eclipse.swt.layout.FormAttachment;
     46 import org.eclipse.swt.layout.FormData;
     47 import org.eclipse.swt.layout.FormLayout;
     48 import org.eclipse.swt.layout.GridData;
     49 import org.eclipse.swt.layout.GridLayout;
     50 import org.eclipse.swt.widgets.Button;
     51 import org.eclipse.swt.widgets.Label;
     52 import org.eclipse.swt.widgets.Composite;
     53 import org.eclipse.swt.widgets.Control;
     54 import org.eclipse.swt.widgets.Display;
     55 import org.eclipse.swt.widgets.Event;
     56 import org.eclipse.swt.widgets.Listener;
     57 import org.eclipse.swt.widgets.Sash;
     58 import org.eclipse.swt.widgets.Table;
     59 import org.eclipse.swt.widgets.TableColumn;
     60 import org.eclipse.swt.widgets.Text;
     61 
     62 import java.util.ArrayList;
     63 import java.util.Arrays;
     64 
     65 /**
     66  * Base class for our information panels.
     67  */
     68 public class AllocationPanel extends TablePanel {
     69 
     70     private final static String PREFS_ALLOC_COL_NUMBER = "allocPanel.Col00"; //$NON-NLS-1$
     71     private final static String PREFS_ALLOC_COL_SIZE = "allocPanel.Col0"; //$NON-NLS-1$
     72     private final static String PREFS_ALLOC_COL_CLASS = "allocPanel.Col1"; //$NON-NLS-1$
     73     private final static String PREFS_ALLOC_COL_THREAD = "allocPanel.Col2"; //$NON-NLS-1$
     74     private final static String PREFS_ALLOC_COL_TRACE_CLASS = "allocPanel.Col3"; //$NON-NLS-1$
     75     private final static String PREFS_ALLOC_COL_TRACE_METHOD = "allocPanel.Col4"; //$NON-NLS-1$
     76 
     77     private final static String PREFS_ALLOC_SASH = "allocPanel.sash"; //$NON-NLS-1$
     78 
     79     private static final String PREFS_STACK_COL_CLASS = "allocPanel.stack.col0"; //$NON-NLS-1$
     80     private static final String PREFS_STACK_COL_METHOD = "allocPanel.stack.col1"; //$NON-NLS-1$
     81     private static final String PREFS_STACK_COL_FILE = "allocPanel.stack.col2"; //$NON-NLS-1$
     82     private static final String PREFS_STACK_COL_LINE = "allocPanel.stack.col3"; //$NON-NLS-1$
     83     private static final String PREFS_STACK_COL_NATIVE = "allocPanel.stack.col4"; //$NON-NLS-1$
     84 
     85     private Composite mAllocationBase;
     86     private Table mAllocationTable;
     87     private TableViewer mAllocationViewer;
     88 
     89     private StackTracePanel mStackTracePanel;
     90     private Table mStackTraceTable;
     91     private Button mEnableButton;
     92     private Button mRequestButton;
     93     private Button mTraceFilterCheck;
     94 
     95     private final AllocationSorter mSorter = new AllocationSorter();
     96     private TableColumn mSortColumn;
     97     private Image mSortUpImg;
     98     private Image mSortDownImg;
     99     private String mFilterText = null;
    100 
    101     /**
    102      * Content Provider to display the allocations of a client.
    103      * Expected input is a {@link Client} object, elements used in the table are of type
    104      * {@link AllocationInfo}.
    105      */
    106     private class AllocationContentProvider implements IStructuredContentProvider {
    107         public Object[] getElements(Object inputElement) {
    108             if (inputElement instanceof Client) {
    109                 AllocationInfo[] allocs = ((Client)inputElement).getClientData().getAllocations();
    110                 if (allocs != null) {
    111                     if (mFilterText != null && mFilterText.length() > 0) {
    112                         allocs = getFilteredAllocations(allocs, mFilterText);
    113                     }
    114                     Arrays.sort(allocs, mSorter);
    115                     return allocs;
    116                 }
    117             }
    118 
    119             return new Object[0];
    120         }
    121 
    122         public void dispose() {
    123             // pass
    124         }
    125 
    126         public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
    127             // pass
    128         }
    129     }
    130 
    131     /**
    132      * A Label Provider to use with {@link AllocationContentProvider}. It expects the elements to be
    133      * of type {@link AllocationInfo}.
    134      */
    135     private static class AllocationLabelProvider implements ITableLabelProvider {
    136 
    137         public Image getColumnImage(Object element, int columnIndex) {
    138             return null;
    139         }
    140 
    141         public String getColumnText(Object element, int columnIndex) {
    142             if (element instanceof AllocationInfo) {
    143                 AllocationInfo alloc = (AllocationInfo)element;
    144                 switch (columnIndex) {
    145                     case 0:
    146                         return Integer.toString(alloc.getAllocNumber());
    147                     case 1:
    148                         return Integer.toString(alloc.getSize());
    149                     case 2:
    150                         return alloc.getAllocatedClass();
    151                     case 3:
    152                         return Short.toString(alloc.getThreadId());
    153                     case 4:
    154                         return alloc.getFirstTraceClassName();
    155                     case 5:
    156                         return alloc.getFirstTraceMethodName();
    157                 }
    158             }
    159 
    160             return null;
    161         }
    162 
    163         public void addListener(ILabelProviderListener listener) {
    164             // pass
    165         }
    166 
    167         public void dispose() {
    168             // pass
    169         }
    170 
    171         public boolean isLabelProperty(Object element, String property) {
    172             // pass
    173             return false;
    174         }
    175 
    176         public void removeListener(ILabelProviderListener listener) {
    177             // pass
    178         }
    179     }
    180 
    181     /**
    182      * Create our control(s).
    183      */
    184     @Override
    185     protected Control createControl(Composite parent) {
    186         final IPreferenceStore store = DdmUiPreferences.getStore();
    187 
    188         Display display = parent.getDisplay();
    189 
    190         // get some images
    191         mSortUpImg = ImageLoader.getDdmUiLibLoader().loadImage("sort_up.png", display);
    192         mSortDownImg = ImageLoader.getDdmUiLibLoader().loadImage("sort_down.png", display);
    193 
    194         // base composite for selected client with enabled thread update.
    195         mAllocationBase = new Composite(parent, SWT.NONE);
    196         mAllocationBase.setLayout(new FormLayout());
    197 
    198         // table above the sash
    199         Composite topParent = new Composite(mAllocationBase, SWT.NONE);
    200         topParent.setLayout(new GridLayout(6, false));
    201 
    202         mEnableButton = new Button(topParent, SWT.PUSH);
    203         mEnableButton.addSelectionListener(new SelectionAdapter() {
    204             @Override
    205             public void widgetSelected(SelectionEvent e) {
    206                 Client current = getCurrentClient();
    207                 AllocationTrackingStatus status = current.getClientData().getAllocationStatus();
    208                 if (status == AllocationTrackingStatus.ON) {
    209                     current.enableAllocationTracker(false);
    210                 } else {
    211                     current.enableAllocationTracker(true);
    212                 }
    213                 current.requestAllocationStatus();
    214             }
    215         });
    216 
    217         mRequestButton = new Button(topParent, SWT.PUSH);
    218         mRequestButton.setText("Get Allocations");
    219         mRequestButton.addSelectionListener(new SelectionAdapter() {
    220             @Override
    221             public void widgetSelected(SelectionEvent e) {
    222                 getCurrentClient().requestAllocationDetails();
    223             }
    224         });
    225 
    226         setUpButtons(false /* enabled */, AllocationTrackingStatus.OFF);
    227 
    228         GridData gridData;
    229 
    230         Composite spacer = new Composite(topParent, SWT.NONE);
    231         spacer.setLayoutData(gridData = new GridData(GridData.FILL_HORIZONTAL));
    232 
    233         new Label(topParent, SWT.NONE).setText("Filter:");
    234 
    235         final Text filterText = new Text(topParent, SWT.BORDER);
    236         filterText.setLayoutData(gridData = new GridData(GridData.FILL_HORIZONTAL));
    237         gridData.widthHint = 200;
    238 
    239         filterText.addModifyListener(new ModifyListener() {
    240             public void modifyText(ModifyEvent arg0) {
    241                 mFilterText  = filterText.getText().trim();
    242                 mAllocationViewer.refresh();
    243             }
    244         });
    245 
    246         mTraceFilterCheck = new Button(topParent, SWT.CHECK);
    247         mTraceFilterCheck.setText("Inc. trace");
    248         mTraceFilterCheck.addSelectionListener(new SelectionAdapter() {
    249             @Override
    250             public void widgetSelected(SelectionEvent arg0) {
    251                 mAllocationViewer.refresh();
    252             }
    253         });
    254 
    255         mAllocationTable = new Table(topParent, SWT.MULTI | SWT.FULL_SELECTION);
    256         mAllocationTable.setLayoutData(gridData = new GridData(GridData.FILL_BOTH));
    257         gridData.horizontalSpan = 6;
    258         mAllocationTable.setHeaderVisible(true);
    259         mAllocationTable.setLinesVisible(true);
    260 
    261         final TableColumn numberCol = TableHelper.createTableColumn(
    262                 mAllocationTable,
    263                 "Alloc Order",
    264                 SWT.RIGHT,
    265                 "Alloc Order", //$NON-NLS-1$
    266                 PREFS_ALLOC_COL_NUMBER, store);
    267         numberCol.addSelectionListener(new SelectionAdapter() {
    268             @Override
    269             public void widgetSelected(SelectionEvent arg0) {
    270                 setSortColumn(numberCol, SortMode.NUMBER);
    271             }
    272         });
    273 
    274         final TableColumn sizeCol = TableHelper.createTableColumn(
    275                 mAllocationTable,
    276                 "Allocation Size",
    277                 SWT.RIGHT,
    278                 "888", //$NON-NLS-1$
    279                 PREFS_ALLOC_COL_SIZE, store);
    280         sizeCol.addSelectionListener(new SelectionAdapter() {
    281             @Override
    282             public void widgetSelected(SelectionEvent arg0) {
    283                 setSortColumn(sizeCol, SortMode.SIZE);
    284             }
    285         });
    286 
    287         final TableColumn classCol = TableHelper.createTableColumn(
    288                 mAllocationTable,
    289                 "Allocated Class",
    290                 SWT.LEFT,
    291                 "Allocated Class", //$NON-NLS-1$
    292                 PREFS_ALLOC_COL_CLASS, store);
    293         classCol.addSelectionListener(new SelectionAdapter() {
    294             @Override
    295             public void widgetSelected(SelectionEvent arg0) {
    296                 setSortColumn(classCol, SortMode.CLASS);
    297             }
    298         });
    299 
    300         final TableColumn threadCol = TableHelper.createTableColumn(
    301                 mAllocationTable,
    302                 "Thread Id",
    303                 SWT.LEFT,
    304                 "999", //$NON-NLS-1$
    305                 PREFS_ALLOC_COL_THREAD, store);
    306         threadCol.addSelectionListener(new SelectionAdapter() {
    307             @Override
    308             public void widgetSelected(SelectionEvent arg0) {
    309                 setSortColumn(threadCol, SortMode.THREAD);
    310             }
    311         });
    312 
    313         final TableColumn inClassCol = TableHelper.createTableColumn(
    314                 mAllocationTable,
    315                 "Allocated in",
    316                 SWT.LEFT,
    317                 "utime", //$NON-NLS-1$
    318                 PREFS_ALLOC_COL_TRACE_CLASS, store);
    319         inClassCol.addSelectionListener(new SelectionAdapter() {
    320             @Override
    321             public void widgetSelected(SelectionEvent arg0) {
    322                 setSortColumn(inClassCol, SortMode.IN_CLASS);
    323             }
    324         });
    325 
    326         final TableColumn inMethodCol = TableHelper.createTableColumn(
    327                 mAllocationTable,
    328                 "Allocated in",
    329                 SWT.LEFT,
    330                 "utime", //$NON-NLS-1$
    331                 PREFS_ALLOC_COL_TRACE_METHOD, store);
    332         inMethodCol.addSelectionListener(new SelectionAdapter() {
    333             @Override
    334             public void widgetSelected(SelectionEvent arg0) {
    335                 setSortColumn(inMethodCol, SortMode.IN_METHOD);
    336             }
    337         });
    338 
    339         // init the default sort colum
    340         switch (mSorter.getSortMode()) {
    341             case SIZE:
    342                 mSortColumn = sizeCol;
    343                 break;
    344             case CLASS:
    345                 mSortColumn = classCol;
    346                 break;
    347             case THREAD:
    348                 mSortColumn = threadCol;
    349                 break;
    350             case IN_CLASS:
    351                 mSortColumn = inClassCol;
    352                 break;
    353             case IN_METHOD:
    354                 mSortColumn = inMethodCol;
    355                 break;
    356         }
    357 
    358         mSortColumn.setImage(mSorter.isDescending() ? mSortDownImg : mSortUpImg);
    359 
    360         mAllocationViewer = new TableViewer(mAllocationTable);
    361         mAllocationViewer.setContentProvider(new AllocationContentProvider());
    362         mAllocationViewer.setLabelProvider(new AllocationLabelProvider());
    363 
    364         mAllocationViewer.addSelectionChangedListener(new ISelectionChangedListener() {
    365             public void selectionChanged(SelectionChangedEvent event) {
    366                 AllocationInfo selectedAlloc = getAllocationSelection(event.getSelection());
    367                 updateAllocationStackTrace(selectedAlloc);
    368             }
    369         });
    370 
    371         // the separating sash
    372         final Sash sash = new Sash(mAllocationBase, SWT.HORIZONTAL);
    373         Color darkGray = parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
    374         sash.setBackground(darkGray);
    375 
    376         // the UI below the sash
    377         mStackTracePanel = new StackTracePanel();
    378         mStackTraceTable = mStackTracePanel.createPanel(mAllocationBase,
    379                 PREFS_STACK_COL_CLASS,
    380                 PREFS_STACK_COL_METHOD,
    381                 PREFS_STACK_COL_FILE,
    382                 PREFS_STACK_COL_LINE,
    383                 PREFS_STACK_COL_NATIVE,
    384                 store);
    385 
    386         // now setup the sash.
    387         // form layout data
    388         FormData data = new FormData();
    389         data.top = new FormAttachment(0, 0);
    390         data.bottom = new FormAttachment(sash, 0);
    391         data.left = new FormAttachment(0, 0);
    392         data.right = new FormAttachment(100, 0);
    393         topParent.setLayoutData(data);
    394 
    395         final FormData sashData = new FormData();
    396         if (store != null && store.contains(PREFS_ALLOC_SASH)) {
    397             sashData.top = new FormAttachment(0, store.getInt(PREFS_ALLOC_SASH));
    398         } else {
    399             sashData.top = new FormAttachment(50,0); // 50% across
    400         }
    401         sashData.left = new FormAttachment(0, 0);
    402         sashData.right = new FormAttachment(100, 0);
    403         sash.setLayoutData(sashData);
    404 
    405         data = new FormData();
    406         data.top = new FormAttachment(sash, 0);
    407         data.bottom = new FormAttachment(100, 0);
    408         data.left = new FormAttachment(0, 0);
    409         data.right = new FormAttachment(100, 0);
    410         mStackTraceTable.setLayoutData(data);
    411 
    412         // allow resizes, but cap at minPanelWidth
    413         sash.addListener(SWT.Selection, new Listener() {
    414             public void handleEvent(Event e) {
    415                 Rectangle sashRect = sash.getBounds();
    416                 Rectangle panelRect = mAllocationBase.getClientArea();
    417                 int bottom = panelRect.height - sashRect.height - 100;
    418                 e.y = Math.max(Math.min(e.y, bottom), 100);
    419                 if (e.y != sashRect.y) {
    420                     sashData.top = new FormAttachment(0, e.y);
    421                     store.setValue(PREFS_ALLOC_SASH, e.y);
    422                     mAllocationBase.layout();
    423                 }
    424             }
    425         });
    426 
    427         return mAllocationBase;
    428     }
    429 
    430     @Override
    431     public void dispose() {
    432         mSortUpImg.dispose();
    433         mSortDownImg.dispose();
    434         super.dispose();
    435     }
    436 
    437     /**
    438      * Sets the focus to the proper control inside the panel.
    439      */
    440     @Override
    441     public void setFocus() {
    442         mAllocationTable.setFocus();
    443     }
    444 
    445     /**
    446      * Sent when an existing client information changed.
    447      * <p/>
    448      * This is sent from a non UI thread.
    449      * @param client the updated client.
    450      * @param changeMask the bit mask describing the changed properties. It can contain
    451      * any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME}
    452      * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE},
    453      * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
    454      * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
    455      *
    456      * @see IClientChangeListener#clientChanged(Client, int)
    457      */
    458     public void clientChanged(final Client client, int changeMask) {
    459         if (client == getCurrentClient()) {
    460             if ((changeMask & Client.CHANGE_HEAP_ALLOCATIONS) != 0) {
    461                 try {
    462                     mAllocationTable.getDisplay().asyncExec(new Runnable() {
    463                         public void run() {
    464                             mAllocationViewer.refresh();
    465                             updateAllocationStackCall();
    466                         }
    467                     });
    468                 } catch (SWTException e) {
    469                     // widget is disposed, we do nothing
    470                 }
    471             } else if ((changeMask & Client.CHANGE_HEAP_ALLOCATION_STATUS) != 0) {
    472                 try {
    473                     mAllocationTable.getDisplay().asyncExec(new Runnable() {
    474                         public void run() {
    475                             setUpButtons(true, client.getClientData().getAllocationStatus());
    476                         }
    477                     });
    478                 } catch (SWTException e) {
    479                     // widget is disposed, we do nothing
    480                 }
    481             }
    482         }
    483     }
    484 
    485     /**
    486      * Sent when a new device is selected. The new device can be accessed
    487      * with {@link #getCurrentDevice()}.
    488      */
    489     @Override
    490     public void deviceSelected() {
    491         // pass
    492     }
    493 
    494     /**
    495      * Sent when a new client is selected. The new client can be accessed
    496      * with {@link #getCurrentClient()}.
    497      */
    498     @Override
    499     public void clientSelected() {
    500         if (mAllocationTable.isDisposed()) {
    501             return;
    502         }
    503 
    504         Client client = getCurrentClient();
    505 
    506         mStackTracePanel.setCurrentClient(client);
    507         mStackTracePanel.setViewerInput(null); // always empty on client selection change.
    508 
    509         if (client != null) {
    510             setUpButtons(true /* enabled */, client.getClientData().getAllocationStatus());
    511         } else {
    512             setUpButtons(false /* enabled */, AllocationTrackingStatus.OFF);
    513         }
    514 
    515         mAllocationViewer.setInput(client);
    516     }
    517 
    518     /**
    519      * Updates the stack call of the currently selected thread.
    520      * <p/>
    521      * This <b>must</b> be called from the UI thread.
    522      */
    523     private void updateAllocationStackCall() {
    524         Client client = getCurrentClient();
    525         if (client != null) {
    526             // get the current selection in the ThreadTable
    527             AllocationInfo selectedAlloc = getAllocationSelection(null);
    528 
    529             if (selectedAlloc != null) {
    530                 updateAllocationStackTrace(selectedAlloc);
    531             } else {
    532                 updateAllocationStackTrace(null);
    533             }
    534         }
    535     }
    536 
    537     /**
    538      * updates the stackcall of the specified allocation. If <code>null</code> the UI is emptied
    539      * of current data.
    540      * @param thread
    541      */
    542     private void updateAllocationStackTrace(AllocationInfo alloc) {
    543         mStackTracePanel.setViewerInput(alloc);
    544     }
    545 
    546     @Override
    547     protected void setTableFocusListener() {
    548         addTableToFocusListener(mAllocationTable);
    549         addTableToFocusListener(mStackTraceTable);
    550     }
    551 
    552     /**
    553      * Returns the current allocation selection or <code>null</code> if none is found.
    554      * If a {@link ISelection} object is specified, the first {@link AllocationInfo} from this
    555      * selection is returned, otherwise, the <code>ISelection</code> returned by
    556      * {@link TableViewer#getSelection()} is used.
    557      * @param selection the {@link ISelection} to use, or <code>null</code>
    558      */
    559     private AllocationInfo getAllocationSelection(ISelection selection) {
    560         if (selection == null) {
    561             selection = mAllocationViewer.getSelection();
    562         }
    563 
    564         if (selection instanceof IStructuredSelection) {
    565             IStructuredSelection structuredSelection = (IStructuredSelection)selection;
    566             Object object = structuredSelection.getFirstElement();
    567             if (object instanceof AllocationInfo) {
    568                 return (AllocationInfo)object;
    569             }
    570         }
    571 
    572         return null;
    573     }
    574 
    575     /**
    576      *
    577      * @param enabled
    578      * @param trackingStatus
    579      */
    580     private void setUpButtons(boolean enabled, AllocationTrackingStatus trackingStatus) {
    581         if (enabled) {
    582             switch (trackingStatus) {
    583                 case UNKNOWN:
    584                     mEnableButton.setText("?");
    585                     mEnableButton.setEnabled(false);
    586                     mRequestButton.setEnabled(false);
    587                     break;
    588                 case OFF:
    589                     mEnableButton.setText("Start Tracking");
    590                     mEnableButton.setEnabled(true);
    591                     mRequestButton.setEnabled(false);
    592                     break;
    593                 case ON:
    594                     mEnableButton.setText("Stop Tracking");
    595                     mEnableButton.setEnabled(true);
    596                     mRequestButton.setEnabled(true);
    597                     break;
    598             }
    599         } else {
    600             mEnableButton.setEnabled(false);
    601             mRequestButton.setEnabled(false);
    602             mEnableButton.setText("Start Tracking");
    603         }
    604     }
    605 
    606     private void setSortColumn(final TableColumn column, SortMode sortMode) {
    607         // set the new sort mode
    608         mSorter.setSortMode(sortMode);
    609 
    610         mAllocationTable.setRedraw(false);
    611 
    612         // remove image from previous sort colum
    613         if (mSortColumn != column) {
    614             mSortColumn.setImage(null);
    615         }
    616 
    617         mSortColumn = column;
    618         if (mSorter.isDescending()) {
    619             mSortColumn.setImage(mSortDownImg);
    620         } else {
    621             mSortColumn.setImage(mSortUpImg);
    622         }
    623 
    624         mAllocationTable.setRedraw(true);
    625         mAllocationViewer.refresh();
    626     }
    627 
    628     private AllocationInfo[] getFilteredAllocations(AllocationInfo[] allocations,
    629             String filterText) {
    630         ArrayList<AllocationInfo> results = new ArrayList<AllocationInfo>();
    631 
    632         filterText = filterText.toLowerCase();
    633         boolean fullTrace = mTraceFilterCheck.getSelection();
    634 
    635         for (AllocationInfo info : allocations) {
    636             if (info.filter(filterText, fullTrace)) {
    637                 results.add(info);
    638             }
    639         }
    640 
    641         return results.toArray(new AllocationInfo[results.size()]);
    642     }
    643 
    644 }
    645 
    646