auto import from //branches/cupcake/...@130745
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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><build></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.
|
||||
|
||||
@@ -201,7 +201,7 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public String getVmName() {
|
||||
public String getAvdName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@@ -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$
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user