OSDN Git Service

android-2.1_r1 snapshot
[android-x86/sdk.git] / ddms / libs / ddmuilib / src / com / android / ddmuilib / log / event / DisplayGraph.java
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.EventValueDescription;
22 import com.android.ddmlib.log.InvalidTypeException;
23 import org.eclipse.swt.widgets.Composite;
24 import org.eclipse.swt.widgets.Control;
25 import org.jfree.chart.axis.AxisLocation;
26 import org.jfree.chart.axis.NumberAxis;
27 import org.jfree.chart.plot.XYPlot;
28 import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
29 import org.jfree.chart.renderer.xy.XYAreaRenderer;
30 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
31 import org.jfree.data.time.Millisecond;
32 import org.jfree.data.time.TimeSeries;
33 import org.jfree.data.time.TimeSeriesCollection;
34
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Date;
38 import java.util.HashMap;
39 import java.util.Map;
40
41 public class DisplayGraph extends EventDisplay {
42
43     public DisplayGraph(String name) {
44         super(name);
45     }
46
47     /**
48      * Resets the display.
49      */
50     @Override
51     void resetUI() {
52         Collection<TimeSeriesCollection> datasets = mValueTypeDataSetMap.values();
53         for (TimeSeriesCollection dataset : datasets) {
54             dataset.removeAllSeries();
55         }
56         if (mOccurrenceDataSet != null) {
57             mOccurrenceDataSet.removeAllSeries();
58         }
59         mValueDescriptorSeriesMap.clear();
60         mOcurrenceDescriptorSeriesMap.clear();
61     }
62
63     /**
64      * Creates the UI for the event display.
65      * @param parent the parent composite.
66      * @param logParser the current log parser.
67      * @return the created control (which may have children).
68      */
69     @Override
70     public Control createComposite(final Composite parent, EventLogParser logParser,
71             final ILogColumnListener listener) {
72         String title = getChartTitle(logParser);
73         return createCompositeChart(parent, logParser, title);
74     }
75
76     /**
77      * Adds event to the display.
78      */
79     @Override
80     void newEvent(EventContainer event, EventLogParser logParser) {
81         ArrayList<ValueDisplayDescriptor> valueDescriptors =
82                 new ArrayList<ValueDisplayDescriptor>();
83
84         ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors =
85                 new ArrayList<OccurrenceDisplayDescriptor>();
86
87         if (filterEvent(event, valueDescriptors, occurrenceDescriptors)) {
88             updateChart(event, logParser, valueDescriptors, occurrenceDescriptors);
89         }
90     }
91
92      /**
93      * Updates the chart with the {@link EventContainer} by adding the values/occurrences defined
94      * by the {@link ValueDisplayDescriptor} and {@link OccurrenceDisplayDescriptor} objects from
95      * the two lists.
96      * <p/>This method is only called when at least one of the descriptor list is non empty.
97      * @param event
98      * @param logParser
99      * @param valueDescriptors
100      * @param occurrenceDescriptors
101      */
102     private void updateChart(EventContainer event, EventLogParser logParser,
103             ArrayList<ValueDisplayDescriptor> valueDescriptors,
104             ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
105         Map<Integer, String> tagMap = logParser.getTagMap();
106
107         Millisecond millisecondTime = null;
108         long msec = -1;
109
110         // If the event container is a cpu container (tag == 2721), and there is no descriptor
111         // for the total CPU load, then we do accumulate all the values.
112         boolean accumulateValues = false;
113         double accumulatedValue = 0;
114
115         if (event.mTag == 2721) {
116             accumulateValues = true;
117             for (ValueDisplayDescriptor descriptor : valueDescriptors) {
118                 accumulateValues &= (descriptor.valueIndex != 0);
119             }
120         }
121
122         for (ValueDisplayDescriptor descriptor : valueDescriptors) {
123             try {
124                 // get the hashmap for this descriptor
125                 HashMap<Integer, TimeSeries> map = mValueDescriptorSeriesMap.get(descriptor);
126
127                 // if it's not there yet, we create it.
128                 if (map == null) {
129                     map = new HashMap<Integer, TimeSeries>();
130                     mValueDescriptorSeriesMap.put(descriptor, map);
131                 }
132
133                 // get the TimeSeries for this pid
134                 TimeSeries timeSeries = map.get(event.pid);
135
136                 // if it doesn't exist yet, we create it
137                 if (timeSeries == null) {
138                     // get the series name
139                     String seriesFullName = null;
140                     String seriesLabel = getSeriesLabel(event, descriptor);
141
142                     switch (mValueDescriptorCheck) {
143                         case EVENT_CHECK_SAME_TAG:
144                             seriesFullName = String.format("%1$s / %2$s", seriesLabel,
145                                     descriptor.valueName);
146                             break;
147                         case EVENT_CHECK_SAME_VALUE:
148                             seriesFullName = String.format("%1$s", seriesLabel);
149                             break;
150                         default:
151                             seriesFullName = String.format("%1$s / %2$s: %3$s", seriesLabel,
152                                     tagMap.get(descriptor.eventTag),
153                                     descriptor.valueName);
154                             break;
155                     }
156
157                     // get the data set for this ValueType
158                     TimeSeriesCollection dataset = getValueDataset(
159                             logParser.getEventInfoMap().get(event.mTag)[descriptor.valueIndex]
160                                                                         .getValueType(),
161                             accumulateValues);
162
163                     // create the series
164                     timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
165                     if (mMaximumChartItemAge != -1) {
166                         timeSeries.setMaximumItemAge(mMaximumChartItemAge * 1000);
167                     }
168
169                     dataset.addSeries(timeSeries);
170
171                     // add it to the map.
172                     map.put(event.pid, timeSeries);
173                 }
174
175                 // update the timeSeries.
176
177                 // get the value from the event
178                 double value = event.getValueAsDouble(descriptor.valueIndex);
179
180                 // accumulate the values if needed.
181                 if (accumulateValues) {
182                     accumulatedValue += value;
183                     value = accumulatedValue;
184                 }
185
186                 // get the time
187                 if (millisecondTime == null) {
188                     msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
189                     millisecondTime = new Millisecond(new Date(msec));
190                 }
191
192                 // add the value to the time series
193                 timeSeries.addOrUpdate(millisecondTime, value);
194             } catch (InvalidTypeException e) {
195                 // just ignore this descriptor if there's a type mismatch
196             }
197         }
198
199         for (OccurrenceDisplayDescriptor descriptor : occurrenceDescriptors) {
200             try {
201                 // get the hashmap for this descriptor
202                 HashMap<Integer, TimeSeries> map = mOcurrenceDescriptorSeriesMap.get(descriptor);
203
204                 // if it's not there yet, we create it.
205                 if (map == null) {
206                     map = new HashMap<Integer, TimeSeries>();
207                     mOcurrenceDescriptorSeriesMap.put(descriptor, map);
208                 }
209
210                 // get the TimeSeries for this pid
211                 TimeSeries timeSeries = map.get(event.pid);
212
213                 // if it doesn't exist yet, we create it.
214                 if (timeSeries == null) {
215                     String seriesLabel = getSeriesLabel(event, descriptor);
216
217                     String seriesFullName = String.format("[%1$s:%2$s]",
218                             tagMap.get(descriptor.eventTag), seriesLabel);
219
220                     timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
221                     if (mMaximumChartItemAge != -1) {
222                         timeSeries.setMaximumItemAge(mMaximumChartItemAge);
223                     }
224
225                     getOccurrenceDataSet().addSeries(timeSeries);
226
227                     map.put(event.pid, timeSeries);
228                 }
229
230                 // update the series
231
232                 // get the time
233                 if (millisecondTime == null) {
234                     msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
235                     millisecondTime = new Millisecond(new Date(msec));
236                 }
237
238                 // add the value to the time series
239                 timeSeries.addOrUpdate(millisecondTime, 0); // the value is unused
240             } catch (InvalidTypeException e) {
241                 // just ignore this descriptor if there's a type mismatch
242             }
243         }
244
245         // go through all the series and remove old values.
246         if (msec != -1 && mMaximumChartItemAge != -1) {
247             Collection<HashMap<Integer, TimeSeries>> pidMapValues =
248                 mValueDescriptorSeriesMap.values();
249
250             for (HashMap<Integer, TimeSeries> pidMapValue : pidMapValues) {
251                 Collection<TimeSeries> seriesCollection = pidMapValue.values();
252
253                 for (TimeSeries timeSeries : seriesCollection) {
254                     timeSeries.removeAgedItems(msec, true);
255                 }
256             }
257
258             pidMapValues = mOcurrenceDescriptorSeriesMap.values();
259             for (HashMap<Integer, TimeSeries> pidMapValue : pidMapValues) {
260                 Collection<TimeSeries> seriesCollection = pidMapValue.values();
261
262                 for (TimeSeries timeSeries : seriesCollection) {
263                     timeSeries.removeAgedItems(msec, true);
264                 }
265             }
266         }
267     }
268
269        /**
270      * Returns a {@link TimeSeriesCollection} for a specific {@link com.android.ddmlib.log.EventValueDescription.ValueType}.
271      * If the data set is not yet created, it is first allocated and set up into the
272      * {@link org.jfree.chart.JFreeChart} object.
273      * @param type the {@link com.android.ddmlib.log.EventValueDescription.ValueType} of the data set.
274      * @param accumulateValues
275      */
276     private TimeSeriesCollection getValueDataset(EventValueDescription.ValueType type, boolean accumulateValues) {
277         TimeSeriesCollection dataset = mValueTypeDataSetMap.get(type);
278         if (dataset == null) {
279             // create the data set and store it in the map
280             dataset = new TimeSeriesCollection();
281             mValueTypeDataSetMap.put(type, dataset);
282
283             // create the renderer and configure it depending on the ValueType
284             AbstractXYItemRenderer renderer;
285             if (type == EventValueDescription.ValueType.PERCENT && accumulateValues) {
286                 renderer = new XYAreaRenderer();
287             } else {
288                 XYLineAndShapeRenderer r = new XYLineAndShapeRenderer();
289                 r.setBaseShapesVisible(type != EventValueDescription.ValueType.PERCENT);
290
291                 renderer = r;
292             }
293
294             // set both the dataset and the renderer in the plot object.
295             XYPlot xyPlot = mChart.getXYPlot();
296             xyPlot.setDataset(mDataSetCount, dataset);
297             xyPlot.setRenderer(mDataSetCount, renderer);
298
299             // put a new axis label, and configure it.
300             NumberAxis axis = new NumberAxis(type.toString());
301
302             if (type == EventValueDescription.ValueType.PERCENT) {
303                 // force percent range to be (0,100) fixed.
304                 axis.setAutoRange(false);
305                 axis.setRange(0., 100.);
306             }
307
308             // for the index, we ignore the occurrence dataset
309             int count = mDataSetCount;
310             if (mOccurrenceDataSet != null) {
311                 count--;
312             }
313
314             xyPlot.setRangeAxis(count, axis);
315             if ((count % 2) == 0) {
316                 xyPlot.setRangeAxisLocation(count, AxisLocation.BOTTOM_OR_LEFT);
317             } else {
318                 xyPlot.setRangeAxisLocation(count, AxisLocation.TOP_OR_RIGHT);
319             }
320
321             // now we link the dataset and the axis
322             xyPlot.mapDatasetToRangeAxis(mDataSetCount, count);
323
324             mDataSetCount++;
325         }
326
327         return dataset;
328     }
329
330     /**
331      * Return the series label for this event. This only contains the pid information.
332      * @param event the {@link EventContainer}
333      * @param descriptor the {@link OccurrenceDisplayDescriptor}
334      * @return the series label.
335      * @throws InvalidTypeException
336      */
337     private String getSeriesLabel(EventContainer event, OccurrenceDisplayDescriptor descriptor)
338             throws InvalidTypeException {
339         if (descriptor.seriesValueIndex != -1) {
340             if (descriptor.includePid == false) {
341                 return event.getValueAsString(descriptor.seriesValueIndex);
342             } else {
343                 return String.format("%1$s (%2$d)",
344                         event.getValueAsString(descriptor.seriesValueIndex), event.pid);
345             }
346         }
347
348         return Integer.toString(event.pid);
349     }
350
351     /**
352      * Returns the {@link TimeSeriesCollection} for the occurrence display. If the data set is not
353      * yet created, it is first allocated and set up into the {@link org.jfree.chart.JFreeChart} object.
354      */
355     private TimeSeriesCollection getOccurrenceDataSet() {
356         if (mOccurrenceDataSet == null) {
357             mOccurrenceDataSet = new TimeSeriesCollection();
358
359             XYPlot xyPlot = mChart.getXYPlot();
360             xyPlot.setDataset(mDataSetCount, mOccurrenceDataSet);
361
362             OccurrenceRenderer renderer = new OccurrenceRenderer();
363             renderer.setBaseShapesVisible(false);
364             xyPlot.setRenderer(mDataSetCount, renderer);
365
366             mDataSetCount++;
367         }
368
369         return mOccurrenceDataSet;
370     }
371
372     /**
373      * Gets display type
374      *
375      * @return display type as an integer
376      */
377     @Override
378     int getDisplayType() {
379         return DISPLAY_TYPE_GRAPH;
380     }
381
382     /**
383      * Sets the current {@link EventLogParser} object.
384      */
385     @Override
386     protected void setNewLogParser(EventLogParser logParser) {
387         if (mChart != null) {
388             mChart.setTitle(getChartTitle(logParser));
389         }
390     }
391     /**
392      * Returns a meaningful chart title based on the value of {@link #mValueDescriptorCheck}.
393      *
394      * @param logParser the logParser.
395      * @return the chart title.
396      */
397     private String getChartTitle(EventLogParser logParser) {
398         if (mValueDescriptors.size() > 0) {
399             String chartDesc = null;
400             switch (mValueDescriptorCheck) {
401                 case EVENT_CHECK_SAME_TAG:
402                     if (logParser != null) {
403                         chartDesc = logParser.getTagMap().get(mValueDescriptors.get(0).eventTag);
404                     }
405                     break;
406                 case EVENT_CHECK_SAME_VALUE:
407                     if (logParser != null) {
408                         chartDesc = String.format("%1$s / %2$s",
409                                 logParser.getTagMap().get(mValueDescriptors.get(0).eventTag),
410                                 mValueDescriptors.get(0).valueName);
411                     }
412                     break;
413             }
414
415             if (chartDesc != null) {
416                 return String.format("%1$s - %2$s", mName, chartDesc);
417             }
418         }
419
420         return mName;
421     }
422 }