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