blob: 1c006bcdab02ed58cd7fd659aa1f3f6a755a44f4 [file] [log] [blame]
/*
* 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 SyncCommon {
// 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;
public DisplaySync(String name) {
super(name);
}
/**
* 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");
resetUI();
return composite;
}
/**
* Resets the display.
*/
@Override
void resetUI() {
super.resetUI();
XYPlot xyPlot = mChart.getXYPlot();
XYBarRenderer br = new XYBarRenderer();
mDatasetsSync = new TimePeriodValues[NUM_AUTHS];
mTooltipsSync = new List[NUM_AUTHS];
mTooltipGenerators = new CustomXYToolTipGenerator[NUM_AUTHS];
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.
*
* @param event The event
* @param logParser The parser providing the event.
*/
@Override
void newEvent(EventContainer event, EventLogParser logParser) {
super.newEvent(event, logParser); // Handle sync operation
try {
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);
}
}
} 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 && "g".equals(key)) {
sb.append("aggregation query: ").append(val).append("\n");
} else if (auth == CONTACTS && "G".equals(key)) {
sb.append("aggregation merge: ").append(val).append("\n");
} else if (auth == CONTACTS && "n".equals(key)) {
sb.append("num entries: ").append(val).append("\n");
} else if (auth == CONTACTS && "p".equals(key)) {
sb.append("photos uploaded from server: ").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();
}
/**
* Callback to process a sync event.
*/
@Override
void processSyncEvent(EventContainer event, int auth, long startTime, long stopTime,
String details, boolean newEvent, int syncSource) {
if (!newEvent) {
// Details arrived for a previous sync event
// Remove event before reinserting.
int lastItem = mDatasetsSync[auth].getItemCount();
mDatasetsSync[auth].delete(lastItem-1, lastItem-1);
mTooltipsSync[auth].remove(lastItem-1);
}
double height = getHeightFromDetails(details);
height = height / (stopTime - startTime + 1) * 10000;
if (height > 30) {
height = 30;
}
mDatasetsSync[auth].add(new SimpleTimePeriod(startTime, stopTime), height);
mTooltipsSync[auth].add(getTextFromDetails(auth, details, syncSource));
mTooltipGenerators[auth].addToolTipSeries(mTooltipsSync[auth]);
if (details.indexOf('x') >= 0 || details.indexOf('X') >= 0) {
long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
mDatasetError.addOrUpdate(new FixedMillisecond(msec), -1);
}
}
/**
* Gets display type
*
* @return display type as an integer
*/
@Override
int getDisplayType() {
return DISPLAY_TYPE_SYNC;
}
}