tmf: Bug 477508: Fix wrong event count and ranks in TmfXmlTraceStub
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core.tests / stubs / org / eclipse / tracecompass / tmf / tests / stubs / trace / xml / TmfXmlTraceStub.java
1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 École Polytechnique de Montréal
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Geneviève Bastien - Initial implementation
11 * Patrick Tasse - Dispose wrapped trace
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.tmf.tests.stubs.trace.xml;
15
16 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.URL;
22 import java.util.Collection;
23
24 import javax.xml.XMLConstants;
25 import javax.xml.transform.Source;
26 import javax.xml.transform.stream.StreamSource;
27 import javax.xml.validation.Schema;
28 import javax.xml.validation.SchemaFactory;
29 import javax.xml.validation.Validator;
30
31 import org.eclipse.core.resources.IProject;
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.runtime.IStatus;
34 import org.eclipse.core.runtime.Status;
35 import org.eclipse.jdt.annotation.NonNull;
36 import org.eclipse.jdt.annotation.Nullable;
37 import org.eclipse.osgi.util.NLS;
38 import org.eclipse.tracecompass.internal.tmf.core.Activator;
39 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
40 import org.eclipse.tracecompass.tmf.core.event.ITmfEventField;
41 import org.eclipse.tracecompass.tmf.core.event.ITmfEventType;
42 import org.eclipse.tracecompass.tmf.core.event.TmfEvent;
43 import org.eclipse.tracecompass.tmf.core.event.TmfEventField;
44 import org.eclipse.tracecompass.tmf.core.event.TmfEventType;
45 import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
46 import org.eclipse.tracecompass.tmf.core.event.aspect.TmfContentFieldAspect;
47 import org.eclipse.tracecompass.tmf.core.event.aspect.TmfCpuAspect;
48 import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
49 import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomEventContent;
50 import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlEvent;
51 import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlTrace;
52 import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlTraceDefinition;
53 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
54 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
55 import org.eclipse.tracecompass.tmf.core.timestamp.TmfNanoTimestamp;
56 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
57 import org.eclipse.tracecompass.tmf.core.trace.TmfContext;
58 import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
59 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
60 import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.TmfCheckpointIndexer;
61 import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
62 import org.xml.sax.SAXException;
63
64 import com.google.common.collect.ImmutableList;
65
66 /**
67 * An XML development trace using a custom XML trace definition and schema.
68 *
69 * This class will typically be used to build custom traces to unit test more
70 * complex functionalities like analyzes or to develop and test data-driven
71 * analyzes.
72 *
73 * This class wraps a custom XML trace and rewrites the returned events in the
74 * getNext() method so that event's fields are the ones defined in <field ... />
75 * elements instead of those defined in the custom XML parser. This way, each
76 * event can have a different set of fields. This class can, for example, mimic
77 * a CTF trace.
78 *
79 * @author Geneviève Bastien
80 */
81 public class TmfXmlTraceStub extends TmfTrace {
82
83 private static final String DEVELOPMENT_TRACE_PARSER_PATH = "TmfXmlDevelopmentTrace.xml"; //$NON-NLS-1$
84 private static final String DEVELOPMENT_TRACE_XSD = "TmfXmlDevelopmentTrace.xsd"; //$NON-NLS-1$
85 private static final String EMPTY = ""; //$NON-NLS-1$
86
87 /* XML elements and attributes names */
88 private static final String EVENT_NAME_FIELD = "Message"; //$NON-NLS-1$
89 private static final String FIELD_NAMES_FIELD = "fields"; //$NON-NLS-1$
90 private static final String VALUES_FIELD = "values"; //$NON-NLS-1$
91 private static final String TYPES_FIELD = "type"; //$NON-NLS-1$
92 private static final String VALUES_SEPARATOR = " \\| "; //$NON-NLS-1$
93 private static final String TYPE_INTEGER = "int"; //$NON-NLS-1$
94 private static final String TYPE_LONG = "long"; //$NON-NLS-1$
95 private static final String ASPECT_CPU = "cpu";
96
97 private static final Long SECONDS_TO_NS = 1000000000L;
98
99 private final CustomXmlTraceDefinition fDefinition;
100 private CustomXmlTrace fTrace;
101
102 private Collection<ITmfEventAspect> fAspects = TmfTrace.BASE_ASPECTS;
103
104 /**
105 * Constructor. Constructs the custom XML trace with the appropriate
106 * definition.
107 */
108 public TmfXmlTraceStub() {
109
110 /* Load custom XML definition */
111 try (InputStream in = TmfXmlTraceStub.class.getResourceAsStream(DEVELOPMENT_TRACE_PARSER_PATH);) {
112 CustomXmlTraceDefinition[] definitions = CustomXmlTraceDefinition.loadAll(in);
113 if (definitions.length < 2) {
114 throw new IllegalStateException("The custom trace definition does not exist"); //$NON-NLS-1$
115 }
116 /* The first definition parses the 'set_aspects' event */
117 fTrace = new CustomXmlTrace(definitions[0]) {
118 @Override
119 protected ITmfTraceIndexer createIndexer(int interval) {
120 /* Use the in-memory checkpoint indexer */
121 return new TmfCheckpointIndexer(this, interval);
122 }
123 };
124 /* The second definition parses 'event' trace events */
125 fDefinition = checkNotNull(definitions[1]);
126 } catch (IOException e) {
127 throw new IllegalStateException("Cannot open the trace parser for development traces"); //$NON-NLS-1$
128 }
129
130 }
131
132 @Override
133 public void initTrace(@Nullable IResource resource, @Nullable String path, @Nullable Class<? extends ITmfEvent> type) throws TmfTraceException {
134 super.initTrace(resource, path, type);
135 ITmfContext ctx;
136
137 /* Initialize and read the trace with the 'set_aspects' definition */
138 TmfSignalManager.deregister(fTrace);
139 fTrace.initTrace(resource, path, type);
140 ctx = seekEvent(0L);
141 /* If a set_aspects event exists, getNext() will process it */
142 getNext(ctx);
143 ctx.dispose();
144 fTrace.dispose();
145
146 /* Initialize a new trace with the trace events definition */
147 fTrace = new CustomXmlTrace(fDefinition);
148 TmfSignalManager.deregister(fTrace);
149 fTrace.initTrace(resource, path, type);
150 /* Set the start and (current) end times for this trace */
151 ctx = seekEvent(0L);
152 if (ctx == null) {
153 return;
154 }
155 ITmfEvent event = getNext(ctx);
156 if (event != null) {
157 final ITmfTimestamp curTime = event.getTimestamp();
158 this.setStartTime(curTime);
159 this.setEndTime(curTime);
160 }
161 ctx.dispose();
162 }
163
164 @Override
165 public synchronized void dispose() {
166 super.dispose();
167 fTrace.dispose();
168 }
169
170 @Override
171 public @Nullable ITmfEvent parseEvent(@Nullable ITmfContext context) {
172 return fTrace.parseEvent(context);
173 }
174
175 @Override
176 public @Nullable ITmfLocation getCurrentLocation() {
177 return fTrace.getCurrentLocation();
178 }
179
180 @Override
181 public double getLocationRatio(@Nullable ITmfLocation location) {
182 return fTrace.getLocationRatio(location);
183 }
184
185 @Override
186 public @Nullable ITmfContext seekEvent(@Nullable ITmfLocation location) {
187 return fTrace.seekEvent(location);
188 }
189
190 @Override
191 public @Nullable ITmfContext seekEvent(double ratio) {
192 return fTrace.seekEvent(ratio);
193 }
194
195 @Override
196 public IStatus validate(@Nullable IProject project, @Nullable String path) {
197 File xmlFile = new File(path);
198 if (!xmlFile.exists() || !xmlFile.isFile() || !xmlFile.canRead()) {
199 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.Messages.TmfDevelopmentTrace_FileNotFound, path));
200 }
201 /* Does the XML file validate with the XSD */
202 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
203 Source xmlSource = new StreamSource(xmlFile);
204
205 try {
206 URL url = TmfXmlTraceStub.class.getResource(DEVELOPMENT_TRACE_XSD);
207 Schema schema = schemaFactory.newSchema(url);
208
209 Validator validator = schema.newValidator();
210 validator.validate(xmlSource);
211 } catch (SAXException e) {
212 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.Messages.TmfDevelopmentTrace_ValidationError, path), e);
213 } catch (IOException e) {
214 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.Messages.TmfDevelopmentTrace_IoError, path), e);
215 }
216 @SuppressWarnings("null")
217 @NonNull
218 IStatus status = Status.OK_STATUS;
219 return status;
220 }
221
222 private static String getStringValue(ITmfEventField content, String fieldName) {
223 ITmfEventField field = content.getField(fieldName);
224 if (field == null) {
225 return EMPTY;
226 }
227 Object val = field.getValue();
228 if (!(val instanceof String)) {
229 return EMPTY;
230 }
231 return (String) val;
232 }
233
234 @Override
235 public synchronized @Nullable ITmfEvent getNext(@Nullable ITmfContext context) {
236 if (context == null) {
237 return null;
238 }
239 final ITmfContext savedContext = new TmfContext(context.getLocation(), context.getRank());
240 CustomXmlEvent event = fTrace.getNext(context);
241 if (event == null) {
242 return null;
243 }
244
245 /* Translate the content of the event */
246 /* The "fields" field contains a | separated list of field names */
247 /* The "values" field contains a | separated list of field values */
248 /* the "type" field contains a | separated list of field types */
249 ITmfEventField content = event.getContent();
250 if (content == null) {
251 return null;
252 }
253
254 String fieldString = getStringValue(content, FIELD_NAMES_FIELD);
255 String valueString = getStringValue(content, VALUES_FIELD);
256 String typeString = getStringValue(content, TYPES_FIELD);
257
258 String[] fields = fieldString.split(VALUES_SEPARATOR);
259 String[] values = valueString.split(VALUES_SEPARATOR);
260 String[] types = typeString.split(VALUES_SEPARATOR);
261 ITmfEventField[] fieldsArray = new TmfEventField[fields.length];
262
263 for (int i = 0; i < fields.length; i++) {
264 String value = EMPTY;
265 if (values.length > i) {
266 value = values[i];
267 }
268 String type = null;
269 if (types.length > i) {
270 type = types[i];
271 }
272 Object val = value;
273 if (type != null) {
274 switch (type) {
275 case TYPE_INTEGER: {
276 try {
277 val = Integer.valueOf(value);
278 } catch (NumberFormatException e) {
279 Activator.logError(String.format("Get next XML event: cannot cast value %s to integer", value), e); //$NON-NLS-1$
280 val = 0;
281 }
282 break;
283 }
284 case TYPE_LONG: {
285 try {
286 val = Long.valueOf(value);
287 } catch (NumberFormatException e) {
288 Activator.logError(String.format("Get next XML event: cannot cast value %s to long", value), e); //$NON-NLS-1$
289 val = 0L;
290 }
291 break;
292 }
293 default:
294 break;
295 }
296 }
297 fieldsArray[i] = new TmfEventField(checkNotNull(fields[i]), val, null);
298 }
299
300 /* Generate the aspects for this trace if it is the 'set_aspects' definition */
301 if (fTrace.getDefinition() != fDefinition) {
302 generateAspects(fieldsArray);
303 return null;
304 }
305
306 /* Create a new event with new fields and name */
307 ITmfEventType customEventType = event.getType();
308 String eventName = getStringValue(content, EVENT_NAME_FIELD);
309 TmfEventType eventType = new TmfEventType(eventName, customEventType.getRootField());
310 ITmfEventField eventFields = new CustomEventContent(content.getName(), content.getValue(), fieldsArray);
311 /*
312 * TODO: Timestamps for these traces are in nanos, but since the
313 * CustomXmlTrace does not support this format, the timestamp of the
314 * original is in second and we need to convert it. We should do that at
315 * the source when it is supported
316 */
317 ITmfTimestamp timestamp = new TmfNanoTimestamp(event.getTimestamp().getValue() / SECONDS_TO_NS);
318 TmfEvent newEvent = new TmfEvent(this, ITmfContext.UNKNOWN_RANK, timestamp, eventType, eventFields);
319 updateAttributes(savedContext, event);
320 return newEvent;
321 }
322
323 private void generateAspects(ITmfEventField[] fieldsArray) {
324 ImmutableList.Builder<ITmfEventAspect> builder = new ImmutableList.Builder<>();
325
326 /* Initialize the first default trace aspects */
327 builder.add(ITmfEventAspect.BaseAspects.TIMESTAMP);
328 builder.add(ITmfEventAspect.BaseAspects.EVENT_TYPE);
329
330 /* Add custom aspects in between */
331 for (ITmfEventField field : fieldsArray) {
332 String name = field.getName();
333 final ITmfEventAspect aspect = new TmfContentFieldAspect(name, name);
334 if (name.equals(ASPECT_CPU)) {
335 builder.add(new TmfCpuAspect() {
336 @Override
337 public @Nullable Integer resolve(ITmfEvent event) {
338 Object result = aspect.resolve(event);
339 if (result instanceof Number) {
340 return ((Number) result).intValue();
341 }
342 return null;
343 }
344 });
345 } else {
346 builder.add(aspect);
347 }
348 }
349
350 /* Add the big content aspect */
351 builder.add(ITmfEventAspect.BaseAspects.CONTENTS);
352 fAspects = builder.build();
353 }
354
355 @Override
356 public Iterable<ITmfEventAspect> getEventAspects() {
357 return fAspects;
358 }
359
360 }
This page took 0.040596 seconds and 6 git commands to generate.