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