FileDocCategorySizeDatePackage
SysinfoPanel.javaAPI DocAndroid 1.5 API20217Wed May 06 22:41:08 BST 2009com.android.ddmuilib

SysinfoPanel

public class SysinfoPanel extends TablePanel implements com.android.ddmlib.IShellOutputReceiver
Displays system information graphs obtained from a bugreport file or device.

Fields Summary
private org.eclipse.swt.widgets.Label
mLabel
private org.eclipse.swt.widgets.Button
mFetchButton
private org.eclipse.swt.widgets.Combo
mDisplayMode
private org.jfree.data.general.DefaultPieDataset
mDataset
private File
mDataFile
private FileOutputStream
mTempStream
private int
mMode
private static final int
MODE_CPU
private static final int
MODE_ALARM
private static final int
MODE_WAKELOCK
private static final int
MODE_MEMINFO
private static final int
MODE_SYNC
private static final String[]
BUGREPORT_SECTION
private static final String[]
DUMP_COMMAND
private static final String[]
CAPTIONS
Constructors Summary
Methods Summary
public voidaddOutput(byte[] data, int offset, int length)
Adds output to the temp file. IShellOutputReceiver method. Called by executeShellCommand().

        try {
            mTempStream.write(data, offset, length);
        }
        catch (IOException e) {
            Log.e("DDMS", e);
        }
    
public voidclientChanged(com.android.ddmlib.Client client, int changeMask)

        // Don't care
    
public voidclientSelected()
Sent when a new client is selected. The new client can be accessed with {@link #getCurrentClient()}.

    
protected org.eclipse.swt.widgets.ControlcreateControl(org.eclipse.swt.widgets.Composite parent)
Create our controls for the UI panel.

        Composite top = new Composite(parent, SWT.NONE);
        top.setLayout(new GridLayout(1, false));
        top.setLayoutData(new GridData(GridData.FILL_BOTH));

        Composite buttons = new Composite(top, SWT.NONE);
        buttons.setLayout(new RowLayout());

        mDisplayMode = new Combo(buttons, SWT.PUSH);
        for (String mode : CAPTIONS) {
            mDisplayMode.add(mode);
        }
        mDisplayMode.select(mMode);
        mDisplayMode.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                mMode = mDisplayMode.getSelectionIndex();
                if (mDataFile != null) {
                    generateDataset(mDataFile);
                } else if (getCurrentDevice() != null) {
                    loadFromDevice();
                }
            }
        });

        final Button loadButton = new Button(buttons, SWT.PUSH);
        loadButton.setText("Load from File");
        loadButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                FileDialog fileDialog = new FileDialog(loadButton.getShell(),
                        SWT.OPEN);
                fileDialog.setText("Load bugreport");
                String filename = fileDialog.open();
                if (filename != null) {
                    mDataFile = new File(filename);
                    generateDataset(mDataFile);
                }
            }
        });

        mFetchButton = new Button(buttons, SWT.PUSH);
        mFetchButton.setText("Update from Device");
        mFetchButton.setEnabled(false);
        mFetchButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                loadFromDevice();
            }
        });

        mLabel = new Label(top, SWT.NONE);
        mLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        mDataset = new DefaultPieDataset();
        JFreeChart chart = ChartFactory.createPieChart("", mDataset, false
                /* legend */, true/* tooltips */, false /* urls */);

        ChartComposite chartComposite = new ChartComposite(top,
                SWT.BORDER, chart,
                ChartComposite.DEFAULT_HEIGHT,
                ChartComposite.DEFAULT_HEIGHT,
                ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH,
                ChartComposite.DEFAULT_MINIMUM_DRAW_HEIGHT,
                3000,
                // max draw width. We don't want it to zoom, so we put a big number
                3000,
                // max draw height. We don't want it to zoom, so we put a big number
                true,  // off-screen buffer
                true,  // properties
                true,  // save
                true,  // print
                false,  // zoom
                true);
        chartComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
        return top;
    
public voiddeviceSelected()
Sent when a new device is selected. The new device can be accessed with {@link #getCurrentDevice()}

        if (getCurrentDevice() != null) {
            mFetchButton.setEnabled(true);
            loadFromDevice();
        } else {
            mFetchButton.setEnabled(false);
        }
    
public voidflush()
Processes output from shell command. IShellOutputReceiver method. The output is passed to generateDataset(). Called by executeShellCommand() on completion.

        if (mTempStream != null) {
            try {
                mTempStream.close();
                generateDataset(mDataFile);
                mTempStream = null;
                mDataFile = null;
            } catch (IOException e) {
                Log.e("DDMS", e);
            }
        }
    
public voidgenerateDataset(java.io.File file)
Generates the dataset to display.

param
file The bugreport file to process.


                     
        
        mDataset.clear();
        mLabel.setText("");
        if (file == null) {
            return;
        }
        try {
            BufferedReader br = getBugreportReader(file);
            if (mMode == MODE_CPU) {
                readCpuDataset(br);
            } else if (mMode == MODE_ALARM) {
                readAlarmDataset(br);
            } else if (mMode == MODE_WAKELOCK) {
                readWakelockDataset(br);
            } else if (mMode == MODE_MEMINFO) {
                readMeminfoDataset(br);
            } else if (mMode == MODE_SYNC) {
                readSyncDataset(br);
            }
        } catch (IOException e) {
            Log.e("DDMS", e);
        }
    
private java.io.BufferedReadergetBugreportReader(java.io.File file)
Helper to open a bugreport and skip to the specified section.

param
file File to open
return
Reader to bugreport file
throws
java.io.IOException on file error

        BufferedReader br = new BufferedReader(new FileReader(file));
        // Skip over the unwanted bugreport sections
        while (true) {
            String line = br.readLine();
            if (line == null) {
                Log.d("DDMS", "Service not found " + line);
                break;
            }
            if ((line.startsWith("DUMP OF SERVICE ") || line.startsWith("-----")) &&
                    line.indexOf(BUGREPORT_SECTION[mMode]) > 0) {
                break;
            }
        }
        return br;
    
voidinitShellOutputBuffer()
Initializes temporary output file for executeShellCommand().

throws
IOException on file error

        mDataFile = File.createTempFile("ddmsfile", ".txt");
        mDataFile.deleteOnExit();
        mTempStream = new FileOutputStream(mDataFile);
    
public booleanisCancelled()
IShellOutputReceiver method.

return
false - don't cancel

        return false;
    
private voidloadFromDevice()
Fetches a new bugreport from the device and updates the display. Fetching is asynchronous. See also addOutput, flush, and isCancelled.

        try {
            initShellOutputBuffer();
            if (mMode == MODE_MEMINFO) {
                // Hack to add bugreport-style section header for meminfo
                mTempStream.write("------ MEMORY INFO ------\n".getBytes());
            }
            getCurrentDevice().executeShellCommand(
                    DUMP_COMMAND[mMode], this);
        } catch (IOException e) {
            Log.e("DDMS", e);
        }
    
private static longparseTimeMs(java.lang.String s)
Parse the time string generated by BatteryStats. A typical new-format string is "11d 13h 45m 39s 999ms". A typical old-format string is "12.3 sec".

return
time in ms

        long total = 0;
        // Matches a single component e.g. "12.3 sec" or "45ms"
        Pattern p = Pattern.compile("([\\d\\.]+)\\s*([a-z]+)");
        Matcher m = p.matcher(s);
        while (m.find()) {
            String label = m.group(2);
            if ("sec".equals(label)) {
                // Backwards compatibility with old time format
                total += (long) (Double.parseDouble(m.group(1)) * 1000);
                continue;
            }
            long value = Integer.parseInt(m.group(1));
            if ("d".equals(label)) {
                total += value * 24 * 60 * 60 * 1000;
            } else if ("h".equals(label)) {
                total += value * 60 * 60 * 1000;
            } else if ("m".equals(label)) {
                total += value * 60 * 1000;
            } else if ("s".equals(label)) {
                total += value * 1000;
            } else if ("ms".equals(label)) {
                total += value;
            }
        }
        return total;
    
voidreadAlarmDataset(java.io.BufferedReader br)
Processes alarm information from bugreport. Updates mDataset with the new data.

param
br Reader providing the content
throws
IOException if error reading file

        Pattern pattern = Pattern
                .compile("(\\d+) alarms: Intent .*\\.([^. ]+) flags");

        while (true) {
            String line = br.readLine();
            if (line == null || line.startsWith("DUMP OF SERVICE")) {
                // Done, or moved on to the next service
                break;
            }
            Matcher m = pattern.matcher(line);
            if (m.find()) {
                long count = Long.parseLong(m.group(1));
                String name = m.group(2);
                mDataset.setValue(name, count);
            }
        }
    
voidreadCpuDataset(java.io.BufferedReader br)
Processes cpu load information from bugreport. Updates mDataset with the new data.

param
br Reader providing the content
throws
IOException if error reading file

        Pattern pattern = Pattern
                .compile("(\\S+): (\\S+)% = (.+)% user . (.+)% kernel");

        while (true) {
            String line = br.readLine();
            if (line == null || line.startsWith("DUMP OF SERVICE")) {
                // Done, or moved on to the next service
                break;
            }
            if (line.startsWith("Load:")) {
                mLabel.setText(line);
                continue;
            }
            Matcher m = pattern.matcher(line);
            if (m.find()) {
                String name = m.group(1);
                long both = Long.parseLong(m.group(2));
                long user = Long.parseLong(m.group(3));
                long kernel = Long.parseLong(m.group(4));
                if ("TOTAL".equals(name)) {
                    if (both < 100) {
                        mDataset.setValue("Idle", (100 - both));
                    }
                } else {
                    // Try to make graphs more useful even with rounding;
                    // log often has 0% user + 0% kernel = 1% total
                    // We arbitrarily give extra to kernel
                    if (user > 0) {
                        mDataset.setValue(name + " (user)", user);
                    }
                    if (kernel > 0) {
                        mDataset.setValue(name + " (kernel)" , both - user);
                    }
                    if (user == 0 && kernel == 0 && both > 0) {
                        mDataset.setValue(name, both);
                    }
                }
            }
        }
    
voidreadMeminfoDataset(java.io.BufferedReader br)
Processes meminfo information from bugreport. Updates mDataset with the new data.

param
br Reader providing the content
throws
IOException if error reading file

        Pattern valuePattern = Pattern.compile("(\\d+) kB");
        long total = 0;
        long other = 0;
        mLabel.setText("PSS in kB");        

        // Scan meminfo
        while (true) {
            String line = br.readLine();
            if (line == null) {
                // End of file
                break;
            }
            Matcher m = valuePattern.matcher(line);
            if (m.find()) {
                long kb = Long.parseLong(m.group(1));
                if (line.startsWith("MemTotal")) {
                    total = kb;
                } else if (line.startsWith("MemFree")) {
                    mDataset.setValue("Free", kb);
                    total -= kb;
                } else if (line.startsWith("Slab")) {
                    mDataset.setValue("Slab", kb);
                    total -= kb;
                } else if (line.startsWith("PageTables")) {
                    mDataset.setValue("PageTables", kb);
                    total -= kb;
                } else if (line.startsWith("Buffers") && kb > 0) {
                    mDataset.setValue("Buffers", kb);
                    total -= kb;
                } else if (line.startsWith("Inactive")) {
                    mDataset.setValue("Inactive", kb);
                    total -= kb;
                } else if (line.startsWith("MemFree")) {
                    mDataset.setValue("Free", kb);
                    total -= kb;
                }
            } else {
                break;
            }
        }
        // Scan procrank
        while (true) {
            String line = br.readLine();
            if (line == null) {
                break;
            }
            if (line.indexOf("PROCRANK") >= 0 || line.indexOf("PID") >= 0) {
                // procrank header
                continue;
            }
            if  (line.indexOf("----") >= 0) {
                //end of procrank section
                break;
            }
            // Extract pss field from procrank output
            long pss = Long.parseLong(line.substring(23, 31).trim());
            String cmdline = line.substring(43).trim().replace("/system/bin/", "");
            // Arbitrary minimum size to display
            if (pss > 2000) {
                mDataset.setValue(cmdline, pss);
            } else {
                other += pss;
            }
            total -= pss;
        }
        mDataset.setValue("Other", other);
        mDataset.setValue("Unknown", total);
    
voidreadSyncDataset(java.io.BufferedReader br)
Processes sync information from bugreport. Updates mDataset with the new data.

param
br Reader providing the content
throws
IOException if error reading file

        while (true) {
            String line = br.readLine();
            if (line == null || line.startsWith("DUMP OF SERVICE")) {
                // Done, or moved on to the next service
                break;
            }
            if (line.startsWith(" |") && line.length() > 70) {
                String authority = line.substring(3, 18).trim();
                String duration = line.substring(61, 70).trim();
                // Duration is MM:SS or HH:MM:SS (DateUtils.formatElapsedTime)
                String durParts[] = duration.split(":");
                if (durParts.length == 2) {
                    long dur = Long.parseLong(durParts[0]) * 60 + Long
                            .parseLong(durParts[1]);
                    mDataset.setValue(authority, dur);
                } else if (duration.length() == 3) {
                    long dur = Long.parseLong(durParts[0]) * 3600
                            + Long.parseLong(durParts[1]) * 60 + Long
                            .parseLong(durParts[2]);
                    mDataset.setValue(authority, dur);
                }
            }
        }
    
voidreadWakelockDataset(java.io.BufferedReader br)
Processes wakelock information from bugreport. Updates mDataset with the new data.

param
br Reader providing the content
throws
IOException if error reading file

        Pattern lockPattern = Pattern.compile("Wake lock (\\S+): (.+) partial");
        Pattern totalPattern = Pattern.compile("Total: (.+) uptime");
        double total = 0;
        boolean inCurrent = false;

        while (true) {
            String line = br.readLine();
            if (line == null || line.startsWith("DUMP OF SERVICE")) {
                // Done, or moved on to the next service
                break;
            }
            if (line.startsWith("Current Battery Usage Statistics")) {
                inCurrent = true;
            } else if (inCurrent) {
                Matcher m = lockPattern.matcher(line);
                if (m.find()) {
                    double value = parseTimeMs(m.group(2)) / 1000.;
                    mDataset.setValue(m.group(1), value);
                    total -= value;
                } else {
                    m = totalPattern.matcher(line);
                    if (m.find()) {
                        total += parseTimeMs(m.group(1)) / 1000.;
                    }
                }
            }
        }
        if (total > 0) {
            mDataset.setValue("Unlocked", total);
        }
    
public voidsetFocus()
Sets the focus to the proper control inside the panel.

        mDisplayMode.setFocus();