auto import from //branches/cupcake/...@130745

This commit is contained in:
The Android Open Source Project
2009-02-10 15:43:58 -08:00
parent 5a4d0fa291
commit e3c5766074
95 changed files with 6116 additions and 2460 deletions

View File

@@ -268,56 +268,61 @@ final class AdbHelper {
};
byte[] reply;
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to talk
// to a specific device
setDevice(adbChan, device);
if (write(adbChan, request) == false)
throw new IOException("failed asking for frame buffer");
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
Log.w("ddms", "Got timeout or unhappy response from ADB fb req: "
+ resp.message);
adbChan.close();
return null;
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to talk
// to a specific device
setDevice(adbChan, device);
if (write(adbChan, request) == false)
throw new IOException("failed asking for frame buffer");
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
Log.w("ddms", "Got timeout or unhappy response from ADB fb req: "
+ resp.message);
adbChan.close();
return null;
}
reply = new byte[16];
if (read(adbChan, reply) == false) {
Log.w("ddms", "got partial reply from ADB fb:");
Log.hexDump("ddms", LogLevel.WARN, reply, 0, reply.length);
adbChan.close();
return null;
}
ByteBuffer buf = ByteBuffer.wrap(reply);
buf.order(ByteOrder.LITTLE_ENDIAN);
imageParams.bpp = buf.getInt();
imageParams.size = buf.getInt();
imageParams.width = buf.getInt();
imageParams.height = buf.getInt();
Log.d("ddms", "image params: bpp=" + imageParams.bpp + ", size="
+ imageParams.size + ", width=" + imageParams.width
+ ", height=" + imageParams.height);
if (write(adbChan, nudge) == false)
throw new IOException("failed nudging");
reply = new byte[imageParams.size];
if (read(adbChan, reply) == false) {
Log.w("ddms", "got truncated reply from ADB fb data");
adbChan.close();
return null;
}
imageParams.data = reply;
} finally {
if (adbChan != null) {
adbChan.close();
}
}
reply = new byte[16];
if (read(adbChan, reply) == false) {
Log.w("ddms", "got partial reply from ADB fb:");
Log.hexDump("ddms", LogLevel.WARN, reply, 0, reply.length);
adbChan.close();
return null;
}
ByteBuffer buf = ByteBuffer.wrap(reply);
buf.order(ByteOrder.LITTLE_ENDIAN);
imageParams.bpp = buf.getInt();
imageParams.size = buf.getInt();
imageParams.width = buf.getInt();
imageParams.height = buf.getInt();
Log.d("ddms", "image params: bpp=" + imageParams.bpp + ", size="
+ imageParams.size + ", width=" + imageParams.width
+ ", height=" + imageParams.height);
if (write(adbChan, nudge) == false)
throw new IOException("failed nudging");
reply = new byte[imageParams.size];
if (read(adbChan, reply) == false) {
Log.w("ddms", "got truncated reply from ADB fb data");
adbChan.close();
return null;
}
imageParams.data = reply;
adbChan.close();
return imageParams;
}
@@ -330,58 +335,61 @@ final class AdbHelper {
throws IOException {
Log.v("ddms", "execute: running " + command);
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to talk
// to a specific device
setDevice(adbChan, device);
// if the device is not -1, then we first tell adb we're looking to
// talk
// to a specific device
setDevice(adbChan, device);
byte[] request = formAdbRequest("shell:" + command); //$NON-NLS-1$
if (write(adbChan, request) == false)
throw new IOException("failed submitting shell command");
byte[] request = formAdbRequest("shell:" + command); //$NON-NLS-1$
if (write(adbChan, request) == false)
throw new IOException("failed submitting shell command");
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
Log.e("ddms", "ADB rejected shell command (" + command + "): "
+ resp.message);
throw new IOException("sad result from adb: " + resp.message);
}
byte[] data = new byte[16384];
ByteBuffer buf = ByteBuffer.wrap(data);
while (true) {
int count;
if (rcvr != null && rcvr.isCancelled()) {
Log.v("ddms", "execute: cancelled");
break;
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
Log.e("ddms", "ADB rejected shell command (" + command + "): " + resp.message);
throw new IOException("sad result from adb: " + resp.message);
}
count = adbChan.read(buf);
if (count < 0) {
// we're at the end, we flush the output
rcvr.flush();
Log.v("ddms",
"execute '" + command + "' on '" + device + "' : EOF hit. Read: " + count);
break;
} else if (count == 0) {
try {
Thread.sleep(WAIT_TIME * 5);
} catch (InterruptedException ie) {
byte[] data = new byte[16384];
ByteBuffer buf = ByteBuffer.wrap(data);
while (true) {
int count;
if (rcvr != null && rcvr.isCancelled()) {
Log.v("ddms", "execute: cancelled");
break;
}
} else {
if (rcvr != null) {
rcvr.addOutput(buf.array(), buf.arrayOffset(), buf
.position());
count = adbChan.read(buf);
if (count < 0) {
// we're at the end, we flush the output
rcvr.flush();
Log.v("ddms", "execute '" + command + "' on '" + device + "' : EOF hit. Read: "
+ count);
break;
} else if (count == 0) {
try {
Thread.sleep(WAIT_TIME * 5);
} catch (InterruptedException ie) {
}
} else {
if (rcvr != null) {
rcvr.addOutput(buf.array(), buf.arrayOffset(), buf.position());
}
buf.rewind();
}
buf.rewind();
}
} finally {
if (adbChan != null) {
adbChan.close();
}
Log.v("ddms", "execute: returning");
}
adbChan.close();
Log.v("ddms", "execute: returning");
}
/**
@@ -407,49 +415,55 @@ final class AdbHelper {
*/
public static void runLogService(InetSocketAddress adbSockAddr, Device device, String logName,
LogReceiver rcvr) throws IOException {
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to talk
// to a specific device
setDevice(adbChan, device);
byte[] request = formAdbRequest("log:" + logName);
if (write(adbChan, request) == false) {
throw new IOException("failed to submit the log command");
}
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
throw new IOException("Device rejected log command: " + resp.message);
}
byte[] data = new byte[16384];
ByteBuffer buf = ByteBuffer.wrap(data);
while (true) {
int count;
if (rcvr != null && rcvr.isCancelled()) {
break;
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to talk
// to a specific device
setDevice(adbChan, device);
byte[] request = formAdbRequest("log:" + logName);
if (write(adbChan, request) == false) {
throw new IOException("failed to submit the log command");
}
count = adbChan.read(buf);
if (count < 0) {
break;
} else if (count == 0) {
try {
Thread.sleep(WAIT_TIME * 5);
} catch (InterruptedException ie) {
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
throw new IOException("Device rejected log command: " + resp.message);
}
byte[] data = new byte[16384];
ByteBuffer buf = ByteBuffer.wrap(data);
while (true) {
int count;
if (rcvr != null && rcvr.isCancelled()) {
break;
}
} else {
if (rcvr != null) {
rcvr.parseNewData(buf.array(), buf.arrayOffset(), buf.position());
count = adbChan.read(buf);
if (count < 0) {
break;
} else if (count == 0) {
try {
Thread.sleep(WAIT_TIME * 5);
} catch (InterruptedException ie) {
}
} else {
if (rcvr != null) {
rcvr.parseNewData(buf.array(), buf.arrayOffset(), buf.position());
}
buf.rewind();
}
buf.rewind();
}
} finally {
if (adbChan != null) {
adbChan.close();
}
}
adbChan.close();
}
/**
@@ -464,24 +478,29 @@ final class AdbHelper {
public static boolean createForward(InetSocketAddress adbSockAddr, Device device, int localPort,
int remotePort) throws IOException {
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
byte[] request = formAdbRequest(String.format(
"host-serial:%1$s:forward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
device.serialNumber, localPort, remotePort));
if (write(adbChan, request) == false) {
throw new IOException("failed to submit the forward command.");
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
byte[] request = formAdbRequest(String.format(
"host-serial:%1$s:forward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
device.serialNumber, localPort, remotePort));
if (write(adbChan, request) == false) {
throw new IOException("failed to submit the forward command.");
}
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
throw new IOException("Device rejected command: " + resp.message);
}
} finally {
if (adbChan != null) {
adbChan.close();
}
}
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
throw new IOException("Device rejected command: " + resp.message);
}
adbChan.close();
return true;
}
@@ -497,24 +516,29 @@ final class AdbHelper {
public static boolean removeForward(InetSocketAddress adbSockAddr, Device device, int localPort,
int remotePort) throws IOException {
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
byte[] request = formAdbRequest(String.format(
"host-serial:%1$s:killforward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
device.serialNumber, localPort, remotePort));
if (!write(adbChan, request)) {
throw new IOException("failed to submit the remove forward command.");
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
byte[] request = formAdbRequest(String.format(
"host-serial:%1$s:killforward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
device.serialNumber, localPort, remotePort));
if (!write(adbChan, request)) {
throw new IOException("failed to submit the remove forward command.");
}
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
throw new IOException("Device rejected command: " + resp.message);
}
} finally {
if (adbChan != null) {
adbChan.close();
}
}
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
throw new IOException("Device rejected command: " + resp.message);
}
adbChan.close();
return true;
}

View File

@@ -69,8 +69,8 @@ public final class Device implements IDevice {
/** Serial number of the device */
String serialNumber = null;
/** Name of the vm */
String mVmName = null;
/** Name of the AVD */
String mAvdName = null;
/** State of the device. */
DeviceState state = null;
@@ -94,8 +94,8 @@ public final class Device implements IDevice {
return serialNumber;
}
public String getVmName() {
return mVmName;
public String getAvdName() {
return mAvdName;
}

View File

@@ -420,11 +420,11 @@ final class DeviceMonitor {
device.executeShellCommand(GetPropReceiver.GETPROP_COMMAND,
new GetPropReceiver(device));
// now get the emulator VM name (if applicable).
// now get the emulator Virtual Device name (if applicable).
if (device.isEmulator()) {
EmulatorConsole console = EmulatorConsole.getConsole(device);
if (console != null) {
device.mVmName = console.getVmName();
device.mAvdName = console.getAvdName();
}
}
} catch (IOException e) {
@@ -470,7 +470,7 @@ final class DeviceMonitor {
} catch (IOException e1) {
// we can ignore that one. It may already have been closed.
}
Log.e("DeviceMonitor",
Log.d("DeviceMonitor",
"Connection Failure when starting to monitor device '"
+ device + "' : " + e.getMessage());
}
@@ -558,7 +558,7 @@ final class DeviceMonitor {
processIncomingJdwpData(device, socket, length);
} catch (IOException ioe) {
Log.e("DeviceMonitor",
Log.d("DeviceMonitor",
"Error reading jdwp list: " + ioe.getMessage());
socket.close();

View File

@@ -54,7 +54,7 @@ public final class EmulatorConsole {
private final static String HOST = "127.0.0.1"; //$NON-NLS-1$
private final static String COMMAND_PING = "help\r\n"; //$NON-NLS-1$
private final static String COMMAND_VM_NAME = "vm name\r\n"; //$NON-NLS-1$
private final static String COMMAND_AVD_NAME = "vm name\r\n"; //$NON-NLS-1$ // TODO change with emulator
private final static String COMMAND_KILL = "kill\r\n"; //$NON-NLS-1$
private final static String COMMAND_GSM_STATUS = "gsm status\r\n"; //$NON-NLS-1$
private final static String COMMAND_GSM_CALL = "gsm call %1$s\r\n"; //$NON-NLS-1$
@@ -309,8 +309,8 @@ public final class EmulatorConsole {
}
}
public synchronized String getVmName() {
if (sendCommand(COMMAND_VM_NAME)) {
public synchronized String getAvdName() {
if (sendCommand(COMMAND_AVD_NAME)) {
String[] result = readLines();
if (result != null && result.length == 2) { // this should be the name on first line,
// and ok on 2nd line

View File

@@ -46,13 +46,13 @@ public interface IDevice {
public String getSerialNumber();
/**
* Returns the name of the VM the emulator is running.
* Returns the name of the AVD the emulator is running.
* <p/>This is only valid if {@link #isEmulator()} returns true.
* <p/>If the emulator is not running any VM (for instance it's running from an Android source
* <p/>If the emulator is not running any AVD (for instance it's running from an Android source
* tree build), this method will return "<code>&lt;build&gt;</code>".
* @return the name of the VM or <code>null</code> if there isn't any.
* @return the name of the AVD or <code>null</code> if there isn't any.
*/
public String getVmName();
public String getAvdName();
/**
* Returns the state of the device.

View File

@@ -201,7 +201,7 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
throw new UnsupportedOperationException();
}
public String getVmName() {
public String getAvdName() {
return "";
}

View File

@@ -200,15 +200,15 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
case DEVICE_COL_STATE:
return getStateString(device);
case DEVICE_COL_BUILD: {
String vmName = device.getVmName();
String avdName = device.getAvdName();
String debuggable = device.getProperty(Device.PROP_DEBUGGABLE);
String version = device.getProperty(Device.PROP_BUILD_VERSION);
if (device.isEmulator()) {
if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
return String.format("%1$s [%2$s, debug]", vmName, //$NON-NLS-1$
return String.format("%1$s [%2$s, debug]", avdName, //$NON-NLS-1$
version);
} else {
return String.format("%1$s [%2$s]", vmName, version); //$NON-NLS-1$
return String.format("%1$s [%2$s]", avdName, version); //$NON-NLS-1$
}
} else {
if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ddmuilib.log.event;
import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventLogParser;
import java.util.ArrayList;
public class DisplayFilteredLog extends DisplayLog {
public DisplayFilteredLog(String name) {
super(name);
}
/**
* Adds event to the display.
*/
@Override
void newEvent(EventContainer event, EventLogParser logParser) {
ArrayList<ValueDisplayDescriptor> valueDescriptors =
new ArrayList<ValueDisplayDescriptor>();
ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors =
new ArrayList<OccurrenceDisplayDescriptor>();
if (filterEvent(event, valueDescriptors, occurrenceDescriptors)) {
addToLog(event, logParser, valueDescriptors, occurrenceDescriptors);
}
}
/**
* Gets display type
*
* @return display type as an integer
*/
@Override
int getDisplayType() {
return DISPLAY_TYPE_FILTERED_LOG;
}
}

View File

@@ -0,0 +1,422 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ddmuilib.log.event;
import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventLogParser;
import com.android.ddmlib.log.EventValueDescription;
import com.android.ddmlib.log.InvalidTypeException;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
import org.jfree.chart.renderer.xy.XYAreaRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class DisplayGraph extends EventDisplay {
public DisplayGraph(String name) {
super(name);
}
/**
* Resets the display.
*/
@Override
void resetUI() {
Collection<TimeSeriesCollection> datasets = mValueTypeDataSetMap.values();
for (TimeSeriesCollection dataset : datasets) {
dataset.removeAllSeries();
}
if (mOccurrenceDataSet != null) {
mOccurrenceDataSet.removeAllSeries();
}
mValueDescriptorSeriesMap.clear();
mOcurrenceDescriptorSeriesMap.clear();
}
/**
* Creates the UI for the event display.
* @param parent the parent composite.
* @param logParser the current log parser.
* @return the created control (which may have children).
*/
@Override
public Control createComposite(final Composite parent, EventLogParser logParser,
final ILogColumnListener listener) {
String title = getChartTitle(logParser);
return createCompositeChart(parent, logParser, title);
}
/**
* Adds event to the display.
*/
@Override
void newEvent(EventContainer event, EventLogParser logParser) {
ArrayList<ValueDisplayDescriptor> valueDescriptors =
new ArrayList<ValueDisplayDescriptor>();
ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors =
new ArrayList<OccurrenceDisplayDescriptor>();
if (filterEvent(event, valueDescriptors, occurrenceDescriptors)) {
updateChart(event, logParser, valueDescriptors, occurrenceDescriptors);
}
}
/**
* Updates the chart with the {@link EventContainer} by adding the values/occurrences defined
* by the {@link ValueDisplayDescriptor} and {@link OccurrenceDisplayDescriptor} objects from
* the two lists.
* <p/>This method is only called when at least one of the descriptor list is non empty.
* @param event
* @param logParser
* @param valueDescriptors
* @param occurrenceDescriptors
*/
private void updateChart(EventContainer event, EventLogParser logParser,
ArrayList<ValueDisplayDescriptor> valueDescriptors,
ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
Map<Integer, String> tagMap = logParser.getTagMap();
Millisecond millisecondTime = null;
long msec = -1;
// If the event container is a cpu container (tag == 2721), and there is no descriptor
// for the total CPU load, then we do accumulate all the values.
boolean accumulateValues = false;
double accumulatedValue = 0;
if (event.mTag == 2721) {
accumulateValues = true;
for (ValueDisplayDescriptor descriptor : valueDescriptors) {
accumulateValues &= (descriptor.valueIndex != 0);
}
}
for (ValueDisplayDescriptor descriptor : valueDescriptors) {
try {
// get the hashmap for this descriptor
HashMap<Integer, TimeSeries> map = mValueDescriptorSeriesMap.get(descriptor);
// if it's not there yet, we create it.
if (map == null) {
map = new HashMap<Integer, TimeSeries>();
mValueDescriptorSeriesMap.put(descriptor, map);
}
// get the TimeSeries for this pid
TimeSeries timeSeries = map.get(event.pid);
// if it doesn't exist yet, we create it
if (timeSeries == null) {
// get the series name
String seriesFullName = null;
String seriesLabel = getSeriesLabel(event, descriptor);
switch (mValueDescriptorCheck) {
case EVENT_CHECK_SAME_TAG:
seriesFullName = String.format("%1$s / %2$s", seriesLabel,
descriptor.valueName);
break;
case EVENT_CHECK_SAME_VALUE:
seriesFullName = String.format("%1$s", seriesLabel);
break;
default:
seriesFullName = String.format("%1$s / %2$s: %3$s", seriesLabel,
tagMap.get(descriptor.eventTag),
descriptor.valueName);
break;
}
// get the data set for this ValueType
TimeSeriesCollection dataset = getValueDataset(
logParser.getEventInfoMap().get(event.mTag)[descriptor.valueIndex]
.getValueType(),
accumulateValues);
// create the series
timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
if (mMaximumChartItemAge != -1) {
timeSeries.setMaximumItemAge(mMaximumChartItemAge * 1000);
}
dataset.addSeries(timeSeries);
// add it to the map.
map.put(event.pid, timeSeries);
}
// update the timeSeries.
// get the value from the event
double value = event.getValueAsDouble(descriptor.valueIndex);
// accumulate the values if needed.
if (accumulateValues) {
accumulatedValue += value;
value = accumulatedValue;
}
// get the time
if (millisecondTime == null) {
msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
millisecondTime = new Millisecond(new Date(msec));
}
// add the value to the time series
timeSeries.addOrUpdate(millisecondTime, value);
} catch (InvalidTypeException e) {
// just ignore this descriptor if there's a type mismatch
}
}
for (OccurrenceDisplayDescriptor descriptor : occurrenceDescriptors) {
try {
// get the hashmap for this descriptor
HashMap<Integer, TimeSeries> map = mOcurrenceDescriptorSeriesMap.get(descriptor);
// if it's not there yet, we create it.
if (map == null) {
map = new HashMap<Integer, TimeSeries>();
mOcurrenceDescriptorSeriesMap.put(descriptor, map);
}
// get the TimeSeries for this pid
TimeSeries timeSeries = map.get(event.pid);
// if it doesn't exist yet, we create it.
if (timeSeries == null) {
String seriesLabel = getSeriesLabel(event, descriptor);
String seriesFullName = String.format("[%1$s:%2$s]",
tagMap.get(descriptor.eventTag), seriesLabel);
timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
if (mMaximumChartItemAge != -1) {
timeSeries.setMaximumItemAge(mMaximumChartItemAge);
}
getOccurrenceDataSet().addSeries(timeSeries);
map.put(event.pid, timeSeries);
}
// update the series
// get the time
if (millisecondTime == null) {
msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
millisecondTime = new Millisecond(new Date(msec));
}
// add the value to the time series
timeSeries.addOrUpdate(millisecondTime, 0); // the value is unused
} catch (InvalidTypeException e) {
// just ignore this descriptor if there's a type mismatch
}
}
// go through all the series and remove old values.
if (msec != -1 && mMaximumChartItemAge != -1) {
Collection<HashMap<Integer, TimeSeries>> pidMapValues =
mValueDescriptorSeriesMap.values();
for (HashMap<Integer, TimeSeries> pidMapValue : pidMapValues) {
Collection<TimeSeries> seriesCollection = pidMapValue.values();
for (TimeSeries timeSeries : seriesCollection) {
timeSeries.removeAgedItems(msec, true);
}
}
pidMapValues = mOcurrenceDescriptorSeriesMap.values();
for (HashMap<Integer, TimeSeries> pidMapValue : pidMapValues) {
Collection<TimeSeries> seriesCollection = pidMapValue.values();
for (TimeSeries timeSeries : seriesCollection) {
timeSeries.removeAgedItems(msec, true);
}
}
}
}
/**
* Returns a {@link TimeSeriesCollection} for a specific {@link com.android.ddmlib.log.EventValueDescription.ValueType}.
* If the data set is not yet created, it is first allocated and set up into the
* {@link org.jfree.chart.JFreeChart} object.
* @param type the {@link com.android.ddmlib.log.EventValueDescription.ValueType} of the data set.
* @param accumulateValues
*/
private TimeSeriesCollection getValueDataset(EventValueDescription.ValueType type, boolean accumulateValues) {
TimeSeriesCollection dataset = mValueTypeDataSetMap.get(type);
if (dataset == null) {
// create the data set and store it in the map
dataset = new TimeSeriesCollection();
mValueTypeDataSetMap.put(type, dataset);
// create the renderer and configure it depending on the ValueType
AbstractXYItemRenderer renderer;
if (type == EventValueDescription.ValueType.PERCENT && accumulateValues) {
renderer = new XYAreaRenderer();
} else {
XYLineAndShapeRenderer r = new XYLineAndShapeRenderer();
r.setBaseShapesVisible(type != EventValueDescription.ValueType.PERCENT);
renderer = r;
}
// set both the dataset and the renderer in the plot object.
XYPlot xyPlot = mChart.getXYPlot();
xyPlot.setDataset(mDataSetCount, dataset);
xyPlot.setRenderer(mDataSetCount, renderer);
// put a new axis label, and configure it.
NumberAxis axis = new NumberAxis(type.toString());
if (type == EventValueDescription.ValueType.PERCENT) {
// force percent range to be (0,100) fixed.
axis.setAutoRange(false);
axis.setRange(0., 100.);
}
// for the index, we ignore the occurrence dataset
int count = mDataSetCount;
if (mOccurrenceDataSet != null) {
count--;
}
xyPlot.setRangeAxis(count, axis);
if ((count % 2) == 0) {
xyPlot.setRangeAxisLocation(count, AxisLocation.BOTTOM_OR_LEFT);
} else {
xyPlot.setRangeAxisLocation(count, AxisLocation.TOP_OR_RIGHT);
}
// now we link the dataset and the axis
xyPlot.mapDatasetToRangeAxis(mDataSetCount, count);
mDataSetCount++;
}
return dataset;
}
/**
* Return the series label for this event. This only contains the pid information.
* @param event the {@link EventContainer}
* @param descriptor the {@link OccurrenceDisplayDescriptor}
* @return the series label.
* @throws InvalidTypeException
*/
private String getSeriesLabel(EventContainer event, OccurrenceDisplayDescriptor descriptor)
throws InvalidTypeException {
if (descriptor.seriesValueIndex != -1) {
if (descriptor.includePid == false) {
return event.getValueAsString(descriptor.seriesValueIndex);
} else {
return String.format("%1$s (%2$d)",
event.getValueAsString(descriptor.seriesValueIndex), event.pid);
}
}
return Integer.toString(event.pid);
}
/**
* Returns the {@link TimeSeriesCollection} for the occurrence display. If the data set is not
* yet created, it is first allocated and set up into the {@link org.jfree.chart.JFreeChart} object.
*/
private TimeSeriesCollection getOccurrenceDataSet() {
if (mOccurrenceDataSet == null) {
mOccurrenceDataSet = new TimeSeriesCollection();
XYPlot xyPlot = mChart.getXYPlot();
xyPlot.setDataset(mDataSetCount, mOccurrenceDataSet);
OccurrenceRenderer renderer = new OccurrenceRenderer();
renderer.setBaseShapesVisible(false);
xyPlot.setRenderer(mDataSetCount, renderer);
mDataSetCount++;
}
return mOccurrenceDataSet;
}
/**
* Gets display type
*
* @return display type as an integer
*/
@Override
int getDisplayType() {
return DISPLAY_TYPE_GRAPH;
}
/**
* Sets the current {@link EventLogParser} object.
*/
@Override
protected void setNewLogParser(EventLogParser logParser) {
if (mChart != null) {
mChart.setTitle(getChartTitle(logParser));
}
}
/**
* Returns a meaningful chart title based on the value of {@link #mValueDescriptorCheck}.
*
* @param logParser the logParser.
* @return the chart title.
*/
private String getChartTitle(EventLogParser logParser) {
if (mValueDescriptors.size() > 0) {
String chartDesc = null;
switch (mValueDescriptorCheck) {
case EVENT_CHECK_SAME_TAG:
if (logParser != null) {
chartDesc = logParser.getTagMap().get(mValueDescriptors.get(0).eventTag);
}
break;
case EVENT_CHECK_SAME_VALUE:
if (logParser != null) {
chartDesc = String.format("%1$s / %2$s",
logParser.getTagMap().get(mValueDescriptors.get(0).eventTag),
mValueDescriptors.get(0).valueName);
}
break;
}
if (chartDesc != null) {
return String.format("%1$s - %2$s", mName, chartDesc);
}
}
return mName;
}
}

View File

@@ -0,0 +1,379 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ddmuilib.log.event;
import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventLogParser;
import com.android.ddmlib.log.EventValueDescription;
import com.android.ddmlib.log.InvalidTypeException;
import com.android.ddmuilib.DdmUiPreferences;
import com.android.ddmuilib.TableHelper;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import java.util.ArrayList;
import java.util.Calendar;
public class DisplayLog extends EventDisplay {
public DisplayLog(String name) {
super(name);
}
private final static String PREFS_COL_DATE = "EventLogPanel.log.Col1"; //$NON-NLS-1$
private final static String PREFS_COL_PID = "EventLogPanel.log.Col2"; //$NON-NLS-1$
private final static String PREFS_COL_EVENTTAG = "EventLogPanel.log.Col3"; //$NON-NLS-1$
private final static String PREFS_COL_VALUENAME = "EventLogPanel.log.Col4"; //$NON-NLS-1$
private final static String PREFS_COL_VALUE = "EventLogPanel.log.Col5"; //$NON-NLS-1$
private final static String PREFS_COL_TYPE = "EventLogPanel.log.Col6"; //$NON-NLS-1$
/**
* Resets the display.
*/
@Override
void resetUI() {
mLogTable.removeAll();
}
/**
* Adds event to the display.
*/
@Override
void newEvent(EventContainer event, EventLogParser logParser) {
addToLog(event, logParser);
}
/**
* Creates the UI for the event display.
*
* @param parent the parent composite.
* @param logParser the current log parser.
* @return the created control (which may have children).
*/
@Override
Control createComposite(Composite parent, EventLogParser logParser, ILogColumnListener listener) {
return createLogUI(parent, listener);
}
/**
* Adds an {@link EventContainer} to the log.
*
* @param event the event.
* @param logParser the log parser.
*/
private void addToLog(EventContainer event, EventLogParser logParser) {
ScrollBar bar = mLogTable.getVerticalBar();
boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
// get the date.
Calendar c = Calendar.getInstance();
long msec = (long) event.sec * 1000L;
c.setTimeInMillis(msec);
// convert the time into a string
String date = String.format("%1$tF %1$tT", c);
String eventName = logParser.getTagMap().get(event.mTag);
String pidName = Integer.toString(event.pid);
// get the value description
EventValueDescription[] valueDescription = logParser.getEventInfoMap().get(event.mTag);
if (valueDescription != null) {
for (int i = 0; i < valueDescription.length; i++) {
EventValueDescription description = valueDescription[i];
try {
String value = event.getValueAsString(i);
logValue(date, pidName, eventName, description.getName(), value,
description.getEventValueType(), description.getValueType());
} catch (InvalidTypeException e) {
logValue(date, pidName, eventName, description.getName(), e.getMessage(),
description.getEventValueType(), description.getValueType());
}
}
// scroll if needed, by showing the last item
if (scroll) {
int itemCount = mLogTable.getItemCount();
if (itemCount > 0) {
mLogTable.showItem(mLogTable.getItem(itemCount - 1));
}
}
}
}
/**
* Adds an {@link EventContainer} to the log. Only add the values/occurrences defined by
* the list of descriptors. If an event is configured to be displayed by value and occurrence,
* only the values are displayed (as they mark an event occurrence anyway).
* <p/>This method is only called when at least one of the descriptor list is non empty.
*
* @param event
* @param logParser
* @param valueDescriptors
* @param occurrenceDescriptors
*/
protected void addToLog(EventContainer event, EventLogParser logParser,
ArrayList<ValueDisplayDescriptor> valueDescriptors,
ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
ScrollBar bar = mLogTable.getVerticalBar();
boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
// get the date.
Calendar c = Calendar.getInstance();
long msec = (long) event.sec * 1000L;
c.setTimeInMillis(msec);
// convert the time into a string
String date = String.format("%1$tF %1$tT", c);
String eventName = logParser.getTagMap().get(event.mTag);
String pidName = Integer.toString(event.pid);
if (valueDescriptors.size() > 0) {
for (ValueDisplayDescriptor descriptor : valueDescriptors) {
logDescriptor(event, descriptor, date, pidName, eventName, logParser);
}
} else {
// we display the event. Since the StringBuilder contains the header (date, event name,
// pid) at this point, there isn't anything else to display.
}
// scroll if needed, by showing the last item
if (scroll) {
int itemCount = mLogTable.getItemCount();
if (itemCount > 0) {
mLogTable.showItem(mLogTable.getItem(itemCount - 1));
}
}
}
/**
* Logs a value in the ui.
*
* @param date
* @param pid
* @param event
* @param valueName
* @param value
* @param eventValueType
* @param valueType
*/
private void logValue(String date, String pid, String event, String valueName,
String value, EventContainer.EventValueType eventValueType, EventValueDescription.ValueType valueType) {
TableItem item = new TableItem(mLogTable, SWT.NONE);
item.setText(0, date);
item.setText(1, pid);
item.setText(2, event);
item.setText(3, valueName);
item.setText(4, value);
String type;
if (valueType != EventValueDescription.ValueType.NOT_APPLICABLE) {
type = String.format("%1$s, %2$s", eventValueType.toString(), valueType.toString());
} else {
type = eventValueType.toString();
}
item.setText(5, type);
}
/**
* Logs a value from an {@link EventContainer} as defined by the {@link ValueDisplayDescriptor}.
*
* @param event the EventContainer
* @param descriptor the ValueDisplayDescriptor defining which value to display.
* @param date the date of the event in a string.
* @param pidName
* @param eventName
* @param logParser
*/
private void logDescriptor(EventContainer event, ValueDisplayDescriptor descriptor,
String date, String pidName, String eventName, EventLogParser logParser) {
String value;
try {
value = event.getValueAsString(descriptor.valueIndex);
} catch (InvalidTypeException e) {
value = e.getMessage();
}
EventValueDescription[] values = logParser.getEventInfoMap().get(event.mTag);
EventValueDescription valueDescription = values[descriptor.valueIndex];
logValue(date, pidName, eventName, descriptor.valueName, value,
valueDescription.getEventValueType(), valueDescription.getValueType());
}
/**
* Creates the UI for a log display.
*
* @param parent the parent {@link Composite}
* @param listener the {@link ILogColumnListener} to notify on column resize events.
* @return the top Composite of the UI.
*/
private Control createLogUI(Composite parent, final ILogColumnListener listener) {
Composite mainComp = new Composite(parent, SWT.NONE);
GridLayout gl;
mainComp.setLayout(gl = new GridLayout(1, false));
gl.marginHeight = gl.marginWidth = 0;
mainComp.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
mLogTable = null;
}
});
Label l = new Label(mainComp, SWT.CENTER);
l.setText(mName);
l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mLogTable = new Table(mainComp, SWT.MULTI | SWT.FULL_SELECTION | SWT.V_SCROLL |
SWT.BORDER);
mLogTable.setLayoutData(new GridData(GridData.FILL_BOTH));
IPreferenceStore store = DdmUiPreferences.getStore();
TableColumn col = TableHelper.createTableColumn(
mLogTable, "Time",
SWT.LEFT, "0000-00-00 00:00:00", PREFS_COL_DATE, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(0, (TableColumn) source);
}
}
});
col = TableHelper.createTableColumn(
mLogTable, "pid",
SWT.LEFT, "0000", PREFS_COL_PID, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(1, (TableColumn) source);
}
}
});
col = TableHelper.createTableColumn(
mLogTable, "Event",
SWT.LEFT, "abcdejghijklmno", PREFS_COL_EVENTTAG, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(2, (TableColumn) source);
}
}
});
col = TableHelper.createTableColumn(
mLogTable, "Name",
SWT.LEFT, "Process Name", PREFS_COL_VALUENAME, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(3, (TableColumn) source);
}
}
});
col = TableHelper.createTableColumn(
mLogTable, "Value",
SWT.LEFT, "0000000", PREFS_COL_VALUE, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(4, (TableColumn) source);
}
}
});
col = TableHelper.createTableColumn(
mLogTable, "Type",
SWT.LEFT, "long, seconds", PREFS_COL_TYPE, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(5, (TableColumn) source);
}
}
});
mLogTable.setHeaderVisible(true);
mLogTable.setLinesVisible(true);
return mainComp;
}
/**
* Resizes the <code>index</code>-th column of the log {@link Table} (if applicable).
* <p/>
* This does nothing if the <code>Table</code> object is <code>null</code> (because the display
* type does not use a column) or if the <code>index</code>-th column is in fact the originating
* column passed as argument.
*
* @param index the index of the column to resize
* @param sourceColumn the original column that was resize, and on which we need to sync the
* index-th column width.
*/
@Override
void resizeColumn(int index, TableColumn sourceColumn) {
if (mLogTable != null) {
TableColumn col = mLogTable.getColumn(index);
if (col != sourceColumn) {
col.setWidth(sourceColumn.getWidth());
}
}
}
/**
* Gets display type
*
* @return display type as an integer
*/
@Override
int getDisplayType() {
return DISPLAY_TYPE_LOG_ALL;
}
}

View File

@@ -0,0 +1,338 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ddmuilib.log.event;
import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventLogParser;
import com.android.ddmlib.log.InvalidTypeException;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.jfree.chart.labels.CustomXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.SimpleTimePeriod;
import org.jfree.data.time.TimePeriodValues;
import org.jfree.data.time.TimePeriodValuesCollection;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.util.ShapeUtilities;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;
public class DisplaySync extends EventDisplay {
// Information to graph for each authority
private TimePeriodValues mDatasetsSync[];
private List<String> mTooltipsSync[];
private CustomXYToolTipGenerator mTooltipGenerators[];
private TimeSeries mDatasetsSyncTickle[];
// Dataset of error events to graph
private TimeSeries mDatasetError;
// State information while processing the event stream
private int mLastState; // 0 if event started, 1 if event stopped
private long mLastStartTime; // ms
private long mLastStopTime; //ms
private String mLastDetails;
private int mLastEvent; // server, poll, etc
public DisplaySync(String name) {
super(name);
}
/**
* Resets the display.
*/
@Override
void resetUI() {
initSyncDisplay();
}
/**
* Creates the UI for the event display.
* @param parent the parent composite.
* @param logParser the current log parser.
* @return the created control (which may have children).
*/
@Override
public Control createComposite(final Composite parent, EventLogParser logParser,
final ILogColumnListener listener) {
Control composite = createCompositeChart(parent, logParser, "Sync Status");
initSyncDisplay();
return composite;
}
/**
* Initialize the Plot and series data for the sync display.
*/
void initSyncDisplay() {
XYPlot xyPlot = mChart.getXYPlot();
XYBarRenderer br = new XYBarRenderer();
mDatasetsSync = new TimePeriodValues[NUM_AUTHS];
mTooltipsSync = new List[NUM_AUTHS];
mTooltipGenerators = new CustomXYToolTipGenerator[NUM_AUTHS];
mLastDetails = "";
TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
xyPlot.setDataset(tpvc);
xyPlot.setRenderer(0, br);
XYLineAndShapeRenderer ls = new XYLineAndShapeRenderer();
ls.setBaseLinesVisible(false);
mDatasetsSyncTickle = new TimeSeries[NUM_AUTHS];
TimeSeriesCollection tsc = new TimeSeriesCollection();
xyPlot.setDataset(1, tsc);
xyPlot.setRenderer(1, ls);
mDatasetError = new TimeSeries("Errors", FixedMillisecond.class);
xyPlot.setDataset(2, new TimeSeriesCollection(mDatasetError));
XYLineAndShapeRenderer errls = new XYLineAndShapeRenderer();
errls.setBaseLinesVisible(false);
errls.setSeriesPaint(0, Color.RED);
xyPlot.setRenderer(2, errls);
for (int i = 0; i < NUM_AUTHS; i++) {
br.setSeriesPaint(i, AUTH_COLORS[i]);
ls.setSeriesPaint(i, AUTH_COLORS[i]);
mDatasetsSync[i] = new TimePeriodValues(AUTH_NAMES[i]);
tpvc.addSeries(mDatasetsSync[i]);
mTooltipsSync[i] = new ArrayList<String>();
mTooltipGenerators[i] = new CustomXYToolTipGenerator();
br.setSeriesToolTipGenerator(i, mTooltipGenerators[i]);
mTooltipGenerators[i].addToolTipSeries(mTooltipsSync[i]);
mDatasetsSyncTickle[i] = new TimeSeries(AUTH_NAMES[i] + " tickle", FixedMillisecond.class);
tsc.addSeries(mDatasetsSyncTickle[i]);
ls.setSeriesShape(i, ShapeUtilities.createUpTriangle(2.5f));
}
}
/**
* Updates the display with a new event. This is the main entry point for
* each event. This method has the logic to tie together the start event,
* stop event, and details event into one graph item. Note that the details
* can happen before or after the stop event.
* @param event The event
* @param logParser the log parser (unused)
*/
@Override
void newEvent(EventContainer event, EventLogParser logParser) {
try {
if (event.mTag == EVENT_SYNC) {
int state = Integer.parseInt(event.getValueAsString(1));
if (state == 0) { // start
mLastStartTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
mLastState = 0;
mLastEvent = Integer.parseInt(event.getValueAsString(2));
mLastDetails = "";
} else if (state == 1) { // stop
if (mLastState == 0) {
mLastStopTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
if (mLastStartTime == 0) {
// Log starts with a stop event
mLastStartTime = mLastStopTime;
}
addEvent(event);
mLastState = 1;
}
}
} else if (event.mTag == EVENT_TICKLE) {
int auth = getAuth(event.getValueAsString(0));
if (auth >= 0) {
long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
mDatasetsSyncTickle[auth].addOrUpdate(new FixedMillisecond(msec), -1);
}
} else if (event.mTag == EVENT_SYNC_DETAILS) {
int auth = getAuth(event.getValueAsString(0));
mLastDetails = event.getValueAsString(3);
if (mLastState != 0) { // Not inside event
long updateTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
if (updateTime - mLastStopTime <= 250) {
// Got details within 250ms after event, so delete and re-insert
// Details later than 250ms (arbitrary) are discarded as probably
// unrelated.
int lastItem = mDatasetsSync[auth].getItemCount();
mDatasetsSync[auth].delete(lastItem-1, lastItem-1);
mTooltipsSync[auth].remove(lastItem-1);
addEvent(event);
}
}
}
} catch (InvalidTypeException e) {
}
}
/**
* Generate the height for an event.
* Height is somewhat arbitrarily the count of "things" that happened
* during the sync.
* When network traffic measurements are available, code should be modified
* to use that instead.
* @param details The details string associated with the event
* @return The height in arbirary units (0-100)
*/
private int getHeightFromDetails(String details) {
if (details == null) {
return 1; // Arbitrary
}
int total = 0;
String parts[] = details.split("[a-zA-Z]");
for (String part : parts) {
if ("".equals(part)) continue;
total += Integer.parseInt(part);
}
if (total == 0) {
total = 1;
}
return total;
}
/**
* Generates the tooltips text for an event.
* This method decodes the cryptic details string.
* @param auth The authority associated with the event
* @param details The details string
* @param eventSource server, poll, etc.
* @return The text to display in the tooltips
*/
private String getTextFromDetails(int auth, String details, int eventSource) {
StringBuffer sb = new StringBuffer();
sb.append(AUTH_NAMES[auth]).append(": \n");
Scanner scanner = new Scanner(details);
Pattern charPat = Pattern.compile("[a-zA-Z]");
Pattern numPat = Pattern.compile("[0-9]+");
while (scanner.hasNext()) {
String key = scanner.findInLine(charPat);
int val = Integer.parseInt(scanner.findInLine(numPat));
if (auth == GMAIL && "M".equals(key)) {
sb.append("messages from server: ").append(val).append("\n");
} else if (auth == GMAIL && "L".equals(key)) {
sb.append("labels from server: ").append(val).append("\n");
} else if (auth == GMAIL && "C".equals(key)) {
sb.append("check conversation requests from server: ").append(val).append("\n");
} else if (auth == GMAIL && "A".equals(key)) {
sb.append("attachments from server: ").append(val).append("\n");
} else if (auth == GMAIL && "U".equals(key)) {
sb.append("op updates from server: ").append(val).append("\n");
} else if (auth == GMAIL && "u".equals(key)) {
sb.append("op updates to server: ").append(val).append("\n");
} else if (auth == GMAIL && "S".equals(key)) {
sb.append("send/receive cycles: ").append(val).append("\n");
} else if ("Q".equals(key)) {
sb.append("queries to server: ").append(val).append("\n");
} else if ("E".equals(key)) {
sb.append("entries from server: ").append(val).append("\n");
} else if ("u".equals(key)) {
sb.append("updates from client: ").append(val).append("\n");
} else if ("i".equals(key)) {
sb.append("inserts from client: ").append(val).append("\n");
} else if ("d".equals(key)) {
sb.append("deletes from client: ").append(val).append("\n");
} else if ("f".equals(key)) {
sb.append("full sync requested\n");
} else if ("r".equals(key)) {
sb.append("partial sync unavailable\n");
} else if ("X".equals(key)) {
sb.append("hard error\n");
} else if ("e".equals(key)) {
sb.append("number of parse exceptions: ").append(val).append("\n");
} else if ("c".equals(key)) {
sb.append("number of conflicts: ").append(val).append("\n");
} else if ("a".equals(key)) {
sb.append("number of auth exceptions: ").append(val).append("\n");
} else if ("D".equals(key)) {
sb.append("too many deletions\n");
} else if ("R".equals(key)) {
sb.append("too many retries: ").append(val).append("\n");
} else if ("b".equals(key)) {
sb.append("database error\n");
} else if ("x".equals(key)) {
sb.append("soft error\n");
} else if ("l".equals(key)) {
sb.append("sync already in progress\n");
} else if ("I".equals(key)) {
sb.append("io exception\n");
} else if (auth == CONTACTS && "p".equals(key)) {
sb.append("photos uploaded from client: ").append(val).append("\n");
} else if (auth == CONTACTS && "P".equals(key)) {
sb.append("photos downloaded from server: ").append(val).append("\n");
} else if (auth == CALENDAR && "F".equals(key)) {
sb.append("server refresh\n");
} else if (auth == CALENDAR && "s".equals(key)) {
sb.append("server diffs fetched\n");
} else {
sb.append(key).append("=").append(val);
}
}
if (eventSource == 0) {
sb.append("(server)");
} else if (eventSource == 1) {
sb.append("(local)");
} else if (eventSource == 2) {
sb.append("(poll)");
} else if (eventSource == 3) {
sb.append("(user)");
}
return sb.toString();
}
/**
* Helper to add an event to the data series.
* Also updates error series if appropriate (x or X in details).
* @param event The event
*/
private void addEvent(EventContainer event) {
try {
int auth = getAuth(event.getValueAsString(0));
double height = getHeightFromDetails(mLastDetails);
height = height / (mLastStopTime - mLastStartTime + 1) * 10000;
if (height > 30) {
height = 30;
}
mDatasetsSync[auth].add(new SimpleTimePeriod(mLastStartTime, mLastStopTime), height);
mTooltipsSync[auth].add(getTextFromDetails(auth, mLastDetails,
mLastEvent));
mTooltipGenerators[auth].addToolTipSeries(mTooltipsSync[auth]);
if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
mDatasetError.addOrUpdate(new FixedMillisecond(msec), -1);
}
} catch (InvalidTypeException e) {
e.printStackTrace();
}
}
/**
* Gets display type
*
* @return display type as an integer
*/
@Override
int getDisplayType() {
return DISPLAY_TYPE_SYNC;
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ddmuilib.log.event;
import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventLogParser;
import com.android.ddmlib.log.InvalidTypeException;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.RegularTimePeriod;
import org.jfree.data.time.SimpleTimePeriod;
import org.jfree.data.time.TimePeriodValues;
import org.jfree.data.time.TimePeriodValuesCollection;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
public class DisplaySyncHistogram extends EventDisplay {
// State information while processing the event stream
protected int mLastState; // 0 if event started, 1 if event stopped
protected long mLastStartTime; // ms
protected long mLastStopTime; //ms
protected String mLastDetails;
protected int mLastEvent; // server, poll, etc
public DisplaySyncHistogram(String name) {
super(name);
}
/**
* Resets the display.
*/
@Override
void resetUI() {
initSyncHistogramDisplay();
}
/**
* Creates the UI for the event display.
* @param parent the parent composite.
* @param logParser the current log parser.
* @return the created control (which may have children).
*/
@Override
public Control createComposite(final Composite parent, EventLogParser logParser,
final ILogColumnListener listener) {
Control composite = createCompositeChart(parent, logParser, "Sync Histogram");
initSyncHistogramDisplay();
return composite;
}
// Information to graph for each authority
private TimePeriodValues mDatasetsSyncHist[];
/**
* Initializes the display.
*/
private void initSyncHistogramDisplay() {
XYPlot xyPlot = mChart.getXYPlot();
AbstractXYItemRenderer br = new XYBarRenderer();
mDatasetsSyncHist = new TimePeriodValues[NUM_AUTHS+1];
mLastDetails = "";
mTimePeriodMap = new HashMap[NUM_AUTHS + 1];
TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
xyPlot.setDataset(tpvc);
xyPlot.setRenderer(br);
for (int i = 0; i < NUM_AUTHS + 1; i++) {
br.setSeriesPaint(i, AUTH_COLORS[i]);
mDatasetsSyncHist[i] = new TimePeriodValues(AUTH_NAMES[i]);
tpvc.addSeries(mDatasetsSyncHist[i]);
mTimePeriodMap[i] = new HashMap<SimpleTimePeriod, Integer>();
}
}
/**
* Updates the display with a new event. This is the main entry point for
* each event. This method has the logic to tie together the start event,
* stop event, and details event into one graph item. Note that the details
* can happen before or after the stop event.
* @param event The event
*/
@Override
void newEvent(EventContainer event, EventLogParser logParser) {
try {
if (event.mTag == EVENT_SYNC) {
int state = Integer.parseInt(event.getValueAsString(1));
if (state == 0) { // start
mLastStartTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
mLastState = 0;
mLastEvent = Integer.parseInt(event.getValueAsString(2));
mLastDetails = "";
} else if (state == 1) { // stop
if (mLastState == 0) {
mLastStopTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
if (mLastStartTime == 0) {
// Log starts with a stop event
mLastStartTime = mLastStopTime;
}
int auth = getAuth(event.getValueAsString(0));
if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
auth = ERRORS;
}
double delta = (mLastStopTime - mLastStartTime) * 100. / 1000 / 3600; // Percent of hour
addHistEvent(event, auth, delta);
mLastState = 1;
}
}
} else if (event.mTag == EVENT_SYNC_DETAILS) {
int auth = getAuth(event.getValueAsString(0));
mLastDetails = event.getValueAsString(3);
if (mLastState != 0) { // Not inside event
long updateTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
if (updateTime - mLastStopTime <= 250) {
// Got details within 250ms after event, so delete and re-insert
// Details later than 250ms (arbitrary) are discarded as probably
// unrelated.
//int lastItem = mDatasetsSync[auth].getItemCount();
//addHistEvent(event);
if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
// Item turns out to be in error, so transfer time from old auth to error.
double delta = (mLastStopTime - mLastStartTime) * 100. / 1000 / 3600; // Percent of hour
addHistEvent(event, auth, -delta);
addHistEvent(event, ERRORS, delta);
}
}
}
}
} catch (InvalidTypeException e) {
}
}
/**
* Helper to add an event to the data series.
* Also updates error series if appropriate (x or X in details).
* @param event The event
* @param auth
* @param value
*/
private void addHistEvent(EventContainer event, int auth, double value) {
SimpleTimePeriod hour = getTimePeriod(mLastStopTime, mHistWidth);
// Loop over all datasets to do the stacking.
for (int i = auth; i <= ERRORS; i++) {
addToPeriod(mDatasetsSyncHist, i, hour, value);
}
}
Map<SimpleTimePeriod, Integer> mTimePeriodMap[];
private void addToPeriod(TimePeriodValues tpv[], int auth, SimpleTimePeriod period, double value) {
int index;
if (mTimePeriodMap[auth].containsKey(period)) {
index = mTimePeriodMap[auth].get(period);
double oldValue = tpv[auth].getValue(index).doubleValue();
tpv[auth].update(index, oldValue + value);
} else {
index = tpv[auth].getItemCount();
mTimePeriodMap[auth].put(period, index);
tpv[auth].add(period, value);
}
}
/**
* Creates a multiple-hour time period for the histogram.
* @param time Time in milliseconds.
* @param numHoursWide: should divide into a day.
* @return SimpleTimePeriod covering the number of hours and containing time.
*/
private SimpleTimePeriod getTimePeriod(long time, long numHoursWide) {
Date date = new Date(time);
TimeZone zone = RegularTimePeriod.DEFAULT_TIME_ZONE;
Calendar calendar = Calendar.getInstance(zone);
calendar.setTime(date);
long hoursOfYear = calendar.get(Calendar.HOUR_OF_DAY) + calendar.get(Calendar.DAY_OF_YEAR) * 24;
int year = calendar.get(Calendar.YEAR);
hoursOfYear = (hoursOfYear / numHoursWide) * numHoursWide;
calendar.clear();
calendar.set(year, 0, 1, 0, 0); // Jan 1
long start = calendar.getTimeInMillis() + hoursOfYear * 3600 * 1000;
return new SimpleTimePeriod(start, start + numHoursWide * 3600 * 1000);
}
/**
* Gets display type
*
* @return display type as an integer
*/
@Override
int getDisplayType() {
return DISPLAY_TYPE_SYNC_HIST;
}
}

View File

@@ -23,7 +23,6 @@ import com.android.ddmuilib.DdmUiPreferences;
import com.android.ddmuilib.IImageLoader;
import com.android.ddmuilib.log.event.EventDisplay.OccurrenceDisplayDescriptor;
import com.android.ddmuilib.log.event.EventDisplay.ValueDisplayDescriptor;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
@@ -444,10 +443,13 @@ class EventDisplayOptions extends Dialog {
@Override
public void widgetSelected(SelectionEvent e) {
EventDisplay eventDisplay = getCurrentEventDisplay();
if (eventDisplay != null) {
if (eventDisplay != null && eventDisplay.getDisplayType() != mDisplayTypeCombo.getSelectionIndex()) {
/* Replace the EventDisplay object with a different subclass */
setModified();
eventDisplay.setDisplayType(mDisplayTypeCombo.getSelectionIndex());
fillUiWith(eventDisplay);
String name = eventDisplay.getName();
EventDisplay newEventDisplay = EventDisplay.eventDisplayFactory(mDisplayTypeCombo.getSelectionIndex(), name);
setCurrentEventDisplay(newEventDisplay);
fillUiWith(newEventDisplay);
}
}
});
@@ -693,7 +695,7 @@ class EventDisplayOptions extends Dialog {
private void duplicateEventDisplay(ArrayList<EventDisplay> displayList) {
for (EventDisplay eventDisplay : displayList) {
mDisplayList.add(new EventDisplay(eventDisplay));
mDisplayList.add(EventDisplay.clone(eventDisplay));
}
}
@@ -744,7 +746,7 @@ class EventDisplayOptions extends Dialog {
String name = String.format("display %1$d", count + 1);
EventDisplay eventDisplay = new EventDisplay(name);
EventDisplay eventDisplay = EventDisplay.eventDisplayFactory(0 /* type*/, name);
mDisplayList.add(eventDisplay);
mEventDisplayList.add(name);
@@ -779,6 +781,13 @@ class EventDisplayOptions extends Dialog {
return null;
}
private void setCurrentEventDisplay(EventDisplay eventDisplay) {
int selection = mEventDisplayList.getSelectionIndex();
if (selection != -1) {
mDisplayList.set(selection, eventDisplay);
}
}
private void handleEventDisplaySelection() {
EventDisplay eventDisplay = getCurrentEventDisplay();

View File

@@ -38,7 +38,7 @@ public class EventLogImporter {
if (top == null) {
throw new FileNotFoundException();
}
final String tagFile = top + "/data/etc/event-log-tags";
final String tagFile = top + "/system/core/logcat/event-log-tags";
BufferedReader tagReader = new BufferedReader(
new InputStreamReader(new FileInputStream(tagFile)));
BufferedReader eventReader = new BufferedReader(