• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ddmuilib.log.event;
18 
19 import com.android.ddmlib.log.EventContainer;
20 import com.android.ddmlib.log.EventLogParser;
21 import com.android.ddmlib.log.InvalidTypeException;
22 import org.eclipse.swt.widgets.Composite;
23 import org.eclipse.swt.widgets.Control;
24 import org.jfree.chart.labels.CustomXYToolTipGenerator;
25 import org.jfree.chart.plot.XYPlot;
26 import org.jfree.chart.renderer.xy.XYBarRenderer;
27 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
28 import org.jfree.data.time.FixedMillisecond;
29 import org.jfree.data.time.SimpleTimePeriod;
30 import org.jfree.data.time.TimePeriodValues;
31 import org.jfree.data.time.TimePeriodValuesCollection;
32 import org.jfree.data.time.TimeSeries;
33 import org.jfree.data.time.TimeSeriesCollection;
34 import org.jfree.util.ShapeUtilities;
35 
36 import java.awt.Color;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Scanner;
40 import java.util.regex.Pattern;
41 
42 public class DisplaySync extends SyncCommon {
43 
44     // Information to graph for each authority
45     private TimePeriodValues mDatasetsSync[];
46     private List<String> mTooltipsSync[];
47     private CustomXYToolTipGenerator mTooltipGenerators[];
48     private TimeSeries mDatasetsSyncTickle[];
49 
50     // Dataset of error events to graph
51     private TimeSeries mDatasetError;
52 
DisplaySync(String name)53     public DisplaySync(String name) {
54         super(name);
55     }
56 
57     /**
58      * Creates the UI for the event display.
59      * @param parent the parent composite.
60      * @param logParser the current log parser.
61      * @return the created control (which may have children).
62      */
63     @Override
createComposite(final Composite parent, EventLogParser logParser, final ILogColumnListener listener)64     public Control createComposite(final Composite parent, EventLogParser logParser,
65             final ILogColumnListener listener) {
66         Control composite = createCompositeChart(parent, logParser, "Sync Status");
67         resetUI();
68         return composite;
69     }
70 
71     /**
72      * Resets the display.
73      */
74     @Override
resetUI()75     void resetUI() {
76         super.resetUI();
77         XYPlot xyPlot = mChart.getXYPlot();
78 
79         XYBarRenderer br = new XYBarRenderer();
80         mDatasetsSync = new TimePeriodValues[NUM_AUTHS];
81         mTooltipsSync = new List[NUM_AUTHS];
82         mTooltipGenerators = new CustomXYToolTipGenerator[NUM_AUTHS];
83 
84         TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
85         xyPlot.setDataset(tpvc);
86         xyPlot.setRenderer(0, br);
87 
88         XYLineAndShapeRenderer ls = new XYLineAndShapeRenderer();
89         ls.setBaseLinesVisible(false);
90         mDatasetsSyncTickle = new TimeSeries[NUM_AUTHS];
91         TimeSeriesCollection tsc = new TimeSeriesCollection();
92         xyPlot.setDataset(1, tsc);
93         xyPlot.setRenderer(1, ls);
94 
95         mDatasetError = new TimeSeries("Errors", FixedMillisecond.class);
96         xyPlot.setDataset(2, new TimeSeriesCollection(mDatasetError));
97         XYLineAndShapeRenderer errls = new XYLineAndShapeRenderer();
98         errls.setBaseLinesVisible(false);
99         errls.setSeriesPaint(0, Color.RED);
100         xyPlot.setRenderer(2, errls);
101 
102         for (int i = 0; i < NUM_AUTHS; i++) {
103             br.setSeriesPaint(i, AUTH_COLORS[i]);
104             ls.setSeriesPaint(i, AUTH_COLORS[i]);
105             mDatasetsSync[i] = new TimePeriodValues(AUTH_NAMES[i]);
106             tpvc.addSeries(mDatasetsSync[i]);
107             mTooltipsSync[i] = new ArrayList<String>();
108             mTooltipGenerators[i] = new CustomXYToolTipGenerator();
109             br.setSeriesToolTipGenerator(i, mTooltipGenerators[i]);
110             mTooltipGenerators[i].addToolTipSeries(mTooltipsSync[i]);
111 
112             mDatasetsSyncTickle[i] = new TimeSeries(AUTH_NAMES[i] + " tickle",
113                     FixedMillisecond.class);
114             tsc.addSeries(mDatasetsSyncTickle[i]);
115             ls.setSeriesShape(i, ShapeUtilities.createUpTriangle(2.5f));
116         }
117     }
118 
119     /**
120      * Updates the display with a new event.
121      *
122      * @param event     The event
123      * @param logParser The parser providing the event.
124      */
125     @Override
newEvent(EventContainer event, EventLogParser logParser)126     void newEvent(EventContainer event, EventLogParser logParser) {
127         super.newEvent(event, logParser); // Handle sync operation
128         try {
129             if (event.mTag == EVENT_TICKLE) {
130                 int auth = getAuth(event.getValueAsString(0));
131                 if (auth >= 0) {
132                     long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
133                     mDatasetsSyncTickle[auth].addOrUpdate(new FixedMillisecond(msec), -1);
134                 }
135             }
136         } catch (InvalidTypeException e) {
137         }
138     }
139 
140     /**
141      * Generate the height for an event.
142      * Height is somewhat arbitrarily the count of "things" that happened
143      * during the sync.
144      * When network traffic measurements are available, code should be modified
145      * to use that instead.
146      * @param details The details string associated with the event
147      * @return The height in arbirary units (0-100)
148      */
getHeightFromDetails(String details)149     private int getHeightFromDetails(String details) {
150         if (details == null) {
151             return 1; // Arbitrary
152         }
153         int total = 0;
154         String parts[] = details.split("[a-zA-Z]");
155         for (String part : parts) {
156             if ("".equals(part)) continue;
157             total += Integer.parseInt(part);
158         }
159         if (total == 0) {
160             total = 1;
161         }
162         return total;
163     }
164 
165     /**
166      * Generates the tooltips text for an event.
167      * This method decodes the cryptic details string.
168      * @param auth The authority associated with the event
169      * @param details The details string
170      * @param eventSource server, poll, etc.
171      * @return The text to display in the tooltips
172      */
getTextFromDetails(int auth, String details, int eventSource)173     private String getTextFromDetails(int auth, String details, int eventSource) {
174 
175         StringBuffer sb = new StringBuffer();
176         sb.append(AUTH_NAMES[auth]).append(": \n");
177 
178         Scanner scanner = new Scanner(details);
179         Pattern charPat = Pattern.compile("[a-zA-Z]");
180         Pattern numPat = Pattern.compile("[0-9]+");
181         while (scanner.hasNext()) {
182             String key = scanner.findInLine(charPat);
183             int val = Integer.parseInt(scanner.findInLine(numPat));
184             if (auth == GMAIL && "M".equals(key)) {
185                 sb.append("messages from server: ").append(val).append("\n");
186             } else if (auth == GMAIL && "L".equals(key)) {
187                 sb.append("labels from server: ").append(val).append("\n");
188             } else if (auth == GMAIL && "C".equals(key)) {
189                 sb.append("check conversation requests from server: ").append(val).append("\n");
190             } else if (auth == GMAIL && "A".equals(key)) {
191                 sb.append("attachments from server: ").append(val).append("\n");
192             } else if (auth == GMAIL && "U".equals(key)) {
193                 sb.append("op updates from server: ").append(val).append("\n");
194             } else if (auth == GMAIL && "u".equals(key)) {
195                 sb.append("op updates to server: ").append(val).append("\n");
196             } else if (auth == GMAIL && "S".equals(key)) {
197                 sb.append("send/receive cycles: ").append(val).append("\n");
198             } else if ("Q".equals(key)) {
199                 sb.append("queries to server: ").append(val).append("\n");
200             } else if ("E".equals(key)) {
201                 sb.append("entries from server: ").append(val).append("\n");
202             } else if ("u".equals(key)) {
203                 sb.append("updates from client: ").append(val).append("\n");
204             } else if ("i".equals(key)) {
205                 sb.append("inserts from client: ").append(val).append("\n");
206             } else if ("d".equals(key)) {
207                 sb.append("deletes from client: ").append(val).append("\n");
208             } else if ("f".equals(key)) {
209                 sb.append("full sync requested\n");
210             } else if ("r".equals(key)) {
211                 sb.append("partial sync unavailable\n");
212             } else if ("X".equals(key)) {
213                 sb.append("hard error\n");
214             } else if ("e".equals(key)) {
215                 sb.append("number of parse exceptions: ").append(val).append("\n");
216             } else if ("c".equals(key)) {
217                 sb.append("number of conflicts: ").append(val).append("\n");
218             } else if ("a".equals(key)) {
219                 sb.append("number of auth exceptions: ").append(val).append("\n");
220             } else if ("D".equals(key)) {
221                 sb.append("too many deletions\n");
222             } else if ("R".equals(key)) {
223                 sb.append("too many retries: ").append(val).append("\n");
224             } else if ("b".equals(key)) {
225                 sb.append("database error\n");
226             } else if ("x".equals(key)) {
227                 sb.append("soft error\n");
228             } else if ("l".equals(key)) {
229                 sb.append("sync already in progress\n");
230             } else if ("I".equals(key)) {
231                 sb.append("io exception\n");
232             } else if (auth == CONTACTS && "g".equals(key)) {
233                 sb.append("aggregation query: ").append(val).append("\n");
234             } else if (auth == CONTACTS && "G".equals(key)) {
235                 sb.append("aggregation merge: ").append(val).append("\n");
236             } else if (auth == CONTACTS && "n".equals(key)) {
237                 sb.append("num entries: ").append(val).append("\n");
238             } else if (auth == CONTACTS && "p".equals(key)) {
239                 sb.append("photos uploaded from server: ").append(val).append("\n");
240             } else if (auth == CONTACTS && "P".equals(key)) {
241                 sb.append("photos downloaded from server: ").append(val).append("\n");
242             } else if (auth == CALENDAR && "F".equals(key)) {
243                 sb.append("server refresh\n");
244             } else if (auth == CALENDAR && "s".equals(key)) {
245                 sb.append("server diffs fetched\n");
246             } else {
247                 sb.append(key).append("=").append(val);
248             }
249         }
250         if (eventSource == 0) {
251             sb.append("(server)");
252         } else if (eventSource == 1) {
253             sb.append("(local)");
254         } else if (eventSource == 2) {
255             sb.append("(poll)");
256         } else if (eventSource == 3) {
257             sb.append("(user)");
258         }
259         return sb.toString();
260     }
261 
262 
263     /**
264      * Callback to process a sync event.
265      */
266     @Override
processSyncEvent(EventContainer event, int auth, long startTime, long stopTime, String details, boolean newEvent, int syncSource)267     void processSyncEvent(EventContainer event, int auth, long startTime, long stopTime,
268             String details, boolean newEvent, int syncSource) {
269         if (!newEvent) {
270             // Details arrived for a previous sync event
271             // Remove event before reinserting.
272             int lastItem = mDatasetsSync[auth].getItemCount();
273             mDatasetsSync[auth].delete(lastItem-1, lastItem-1);
274             mTooltipsSync[auth].remove(lastItem-1);
275         }
276         double height = getHeightFromDetails(details);
277         height = height / (stopTime - startTime + 1) * 10000;
278         if (height > 30) {
279             height = 30;
280         }
281         mDatasetsSync[auth].add(new SimpleTimePeriod(startTime, stopTime), height);
282         mTooltipsSync[auth].add(getTextFromDetails(auth, details, syncSource));
283         mTooltipGenerators[auth].addToolTipSeries(mTooltipsSync[auth]);
284         if (details.indexOf('x') >= 0 || details.indexOf('X') >= 0) {
285             long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
286             mDatasetError.addOrUpdate(new FixedMillisecond(msec), -1);
287         }
288     }
289 
290     /**
291      * Gets display type
292      *
293      * @return display type as an integer
294      */
295     @Override
getDisplayType()296     int getDisplayType() {
297         return DISPLAY_TYPE_SYNC;
298     }
299 }
300