318a56660b1d3676040b694c22ad0b45b37130b2
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / parsers / custom / CustomTxtTrace.java
1 /*******************************************************************************
2 * Copyright (c) 2010, 2016 Ericsson
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 * Patrick Tasse - Initial API and implementation
11 * Bernd Hufmann - Add trace type id handling
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.tmf.core.parsers.custom;
15
16 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
17
18 import java.io.File;
19 import java.io.FileNotFoundException;
20 import java.io.IOException;
21 import java.nio.ByteBuffer;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map.Entry;
26 import java.util.regex.Matcher;
27
28 import org.eclipse.core.resources.IProject;
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.runtime.IStatus;
31 import org.eclipse.core.runtime.Status;
32 import org.eclipse.jdt.annotation.NonNull;
33 import org.eclipse.tracecompass.internal.tmf.core.Activator;
34 import org.eclipse.tracecompass.internal.tmf.core.parsers.custom.CustomEventAspects;
35 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
36 import org.eclipse.tracecompass.tmf.core.event.ITmfEventField;
37 import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
38 import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
39 import org.eclipse.tracecompass.tmf.core.io.BufferedRandomAccessFile;
40 import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomTxtTraceDefinition.InputLine;
41 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
42 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal;
43 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
44 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
45 import org.eclipse.tracecompass.tmf.core.trace.TmfContext;
46 import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
47 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
48 import org.eclipse.tracecompass.tmf.core.trace.TraceValidationStatus;
49 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
50 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
51 import org.eclipse.tracecompass.tmf.core.trace.indexer.TmfBTreeTraceIndexer;
52 import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.ITmfCheckpoint;
53 import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.TmfCheckpoint;
54 import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
55 import org.eclipse.tracecompass.tmf.core.trace.location.TmfLongLocation;
56
57 /**
58 * Base class for custom plain text traces.
59 *
60 * @author Patrick Tassé
61 */
62 public class CustomTxtTrace extends TmfTrace implements ITmfPersistentlyIndexable {
63
64 private static final TmfLongLocation NULL_LOCATION = new TmfLongLocation(-1L);
65 private static final int DEFAULT_CACHE_SIZE = 100;
66 private static final int MAX_LINES = 100;
67 private static final int MAX_CONFIDENCE = 100;
68
69 private final CustomTxtTraceDefinition fDefinition;
70 private final ITmfEventField fRootField;
71 private BufferedRandomAccessFile fFile;
72 private final @NonNull String fTraceTypeId;
73
74 private static final char SEPARATOR = ':';
75 private static final String CUSTOM_TXT_TRACE_TYPE_PREFIX = "custom.txt.trace" + SEPARATOR; //$NON-NLS-1$
76 private static final String LINUX_TOOLS_CUSTOM_TXT_TRACE_TYPE_PREFIX = "org.eclipse.linuxtools.tmf.core.parsers.custom.CustomTxtTrace" + SEPARATOR; //$NON-NLS-1$
77 private static final String EARLY_TRACE_COMPASS_CUSTOM_TXT_TRACE_TYPE_PREFIX = "org.eclipse.tracecompass.tmf.core.parsers.custom.CustomTxtTrace" + SEPARATOR; //$NON-NLS-1$
78
79 /**
80 * Basic constructor.
81 *
82 * @param definition
83 * Text trace definition
84 */
85 public CustomTxtTrace(final CustomTxtTraceDefinition definition) {
86 fDefinition = definition;
87 fRootField = CustomEventType.getRootField(definition);
88 fTraceTypeId = buildTraceTypeId(definition.categoryName, definition.definitionName);
89 setCacheSize(DEFAULT_CACHE_SIZE);
90 }
91
92 /**
93 * Full constructor.
94 *
95 * @param resource
96 * Trace's resource.
97 * @param definition
98 * Text trace definition
99 * @param path
100 * Path to the trace file
101 * @param cacheSize
102 * Cache size to use
103 * @throws TmfTraceException
104 * If we couldn't open the trace at 'path'
105 */
106 public CustomTxtTrace(final IResource resource,
107 final CustomTxtTraceDefinition definition, final String path,
108 final int cacheSize) throws TmfTraceException {
109 this(definition);
110 setCacheSize((cacheSize > 0) ? cacheSize : DEFAULT_CACHE_SIZE);
111 initTrace(resource, path, CustomTxtEvent.class);
112 }
113
114 @Override
115 public void initTrace(final IResource resource, final String path, final Class<? extends ITmfEvent> eventType) throws TmfTraceException {
116 super.initTrace(resource, path, eventType);
117 initFile();
118 }
119
120 private void initFile() throws TmfTraceException {
121 closeFile();
122 try {
123 fFile = new BufferedRandomAccessFile(getPath(), "r"); //$NON-NLS-1$
124 } catch (IOException e) {
125 throw new TmfTraceException(e.getMessage(), e);
126 }
127 }
128
129 @Override
130 public synchronized void dispose() {
131 super.dispose();
132 closeFile();
133 }
134
135 private void closeFile() {
136 if (fFile != null) {
137 try {
138 fFile.close();
139 } catch (IOException e) {
140 } finally {
141 fFile = null;
142 }
143 }
144 }
145
146 @Override
147 public ITmfTraceIndexer getIndexer() {
148 return super.getIndexer();
149 }
150
151 @Override
152 public Iterable<ITmfEventAspect<?>> getEventAspects() {
153 return CustomEventAspects.generateAspects(fDefinition);
154 }
155
156 @Override
157 public synchronized TmfContext seekEvent(final ITmfLocation location) {
158 final CustomTxtTraceContext context = new CustomTxtTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
159 if (NULL_LOCATION.equals(location) || fFile == null) {
160 return context;
161 }
162 try {
163 if (location == null) {
164 fFile.seek(0);
165 } else if (location.getLocationInfo() instanceof Long) {
166 fFile.seek((Long) location.getLocationInfo());
167 }
168 long rawPos = fFile.getFilePointer();
169 String line = fFile.getNextLine();
170 while (line != null) {
171 for (final InputLine input : getFirstLines()) {
172 final Matcher matcher = input.getPattern().matcher(line);
173 if (matcher.matches()) {
174 context.setLocation(new TmfLongLocation(rawPos));
175 context.firstLineMatcher = matcher;
176 context.firstLine = line;
177 context.nextLineLocation = fFile.getFilePointer();
178 context.inputLine = input;
179 return context;
180 }
181 }
182 rawPos = fFile.getFilePointer();
183 line = fFile.getNextLine();
184 }
185 return context;
186 } catch (final FileNotFoundException e) {
187 Activator.logError("Error seeking event. File not found: " + getPath(), e); //$NON-NLS-1$
188 return context;
189 } catch (final IOException e) {
190 Activator.logError("Error seeking event. File: " + getPath(), e); //$NON-NLS-1$
191 return context;
192 }
193
194 }
195
196 @Override
197 public synchronized TmfContext seekEvent(final double ratio) {
198 if (fFile == null) {
199 return new CustomTxtTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
200 }
201 try {
202 long pos = Math.round(ratio * fFile.length());
203 while (pos > 0) {
204 fFile.seek(pos - 1);
205 if (fFile.read() == '\n') {
206 break;
207 }
208 pos--;
209 }
210 final ITmfLocation location = new TmfLongLocation(pos);
211 final TmfContext context = seekEvent(location);
212 context.setRank(ITmfContext.UNKNOWN_RANK);
213 return context;
214 } catch (final IOException e) {
215 Activator.logError("Error seeking event. File: " + getPath(), e); //$NON-NLS-1$
216 return new CustomTxtTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
217 }
218 }
219
220 @Override
221 public synchronized double getLocationRatio(final ITmfLocation location) {
222 if (fFile == null) {
223 return 0;
224 }
225 try {
226 if (location.getLocationInfo() instanceof Long) {
227 return ((Long) location.getLocationInfo()).doubleValue() / fFile.length();
228 }
229 } catch (final IOException e) {
230 Activator.logError("Error seeking event. File: " + getPath(), e); //$NON-NLS-1$
231 }
232 return 0;
233 }
234
235 @Override
236 public ITmfLocation getCurrentLocation() {
237 // TODO Auto-generated method stub
238 return null;
239 }
240
241 @Override
242 public synchronized CustomTxtEvent parseEvent(final ITmfContext tmfContext) {
243 ITmfContext context = seekEvent(tmfContext.getLocation());
244 return parse(context);
245 }
246
247 @Override
248 public synchronized CustomTxtEvent getNext(final ITmfContext context) {
249 final ITmfContext savedContext = new TmfContext(context.getLocation(), context.getRank());
250 final CustomTxtEvent event = parse(context);
251 if (event != null) {
252 updateAttributes(savedContext, event);
253 context.increaseRank();
254 }
255 return event;
256 }
257
258 private synchronized CustomTxtEvent parse(final ITmfContext tmfContext) {
259 if (fFile == null) {
260 return null;
261 }
262 if (!(tmfContext instanceof CustomTxtTraceContext)) {
263 return null;
264 }
265
266 final CustomTxtTraceContext context = (CustomTxtTraceContext) tmfContext;
267 if (context.getLocation() == null || !(context.getLocation().getLocationInfo() instanceof Long) || NULL_LOCATION.equals(context.getLocation())) {
268 return null;
269 }
270
271 CustomTxtEvent event = parseFirstLine(context);
272
273 final HashMap<InputLine, Integer> countMap = new HashMap<>();
274 InputLine currentInput = null;
275 if (context.inputLine.childrenInputs != null && context.inputLine.childrenInputs.size() > 0) {
276 currentInput = context.inputLine.childrenInputs.get(0);
277 countMap.put(currentInput, 0);
278 }
279
280 try {
281 if (fFile.getFilePointer() != context.nextLineLocation) {
282 fFile.seek(context.nextLineLocation);
283 }
284 long rawPos = fFile.getFilePointer();
285 String line = fFile.getNextLine();
286 while (line != null) {
287 boolean processed = false;
288 if (currentInput == null) {
289 for (final InputLine input : getFirstLines()) {
290 final Matcher matcher = input.getPattern().matcher(line);
291 if (matcher.matches()) {
292 context.setLocation(new TmfLongLocation(rawPos));
293 context.firstLineMatcher = matcher;
294 context.firstLine = line;
295 context.nextLineLocation = fFile.getFilePointer();
296 context.inputLine = input;
297 return event;
298 }
299 }
300 } else {
301 if (checkNotNull(countMap.get(currentInput)) >= currentInput.getMinCount()) {
302 final List<InputLine> nextInputs = currentInput.getNextInputs(countMap);
303 if (nextInputs.size() == 0 || nextInputs.get(nextInputs.size() - 1).getMinCount() == 0) {
304 for (final InputLine input : getFirstLines()) {
305 final Matcher matcher = input.getPattern().matcher(line);
306 if (matcher.matches()) {
307 context.setLocation(new TmfLongLocation(rawPos));
308 context.firstLineMatcher = matcher;
309 context.firstLine = line;
310 context.nextLineLocation = fFile.getFilePointer();
311 context.inputLine = input;
312 return event;
313 }
314 }
315 }
316 for (final InputLine input : nextInputs) {
317 final Matcher matcher = input.getPattern().matcher(line);
318 if (matcher.matches()) {
319 event.processGroups(input, matcher);
320 currentInput = input;
321 if (countMap.get(currentInput) == null) {
322 countMap.put(currentInput, 1);
323 } else {
324 countMap.put(currentInput, checkNotNull(countMap.get(currentInput)) + 1);
325 }
326 Iterator<InputLine> iter = countMap.keySet().iterator();
327 while (iter.hasNext()) {
328 final InputLine inputLine = iter.next();
329 if (inputLine.level > currentInput.level) {
330 iter.remove();
331 }
332 }
333 if (currentInput.childrenInputs != null && currentInput.childrenInputs.size() > 0) {
334 currentInput = currentInput.childrenInputs.get(0);
335 countMap.put(currentInput, 0);
336 } else if (checkNotNull(countMap.get(currentInput)) >= currentInput.getMaxCount()) {
337 if (currentInput.getNextInputs(countMap).size() > 0) {
338 currentInput = currentInput.getNextInputs(countMap).get(0);
339 if (countMap.get(currentInput) == null) {
340 countMap.put(currentInput, 0);
341 }
342 iter = countMap.keySet().iterator();
343 while (iter.hasNext()) {
344 final InputLine inputLine = iter.next();
345 if (inputLine.level > currentInput.level) {
346 iter.remove();
347 }
348 }
349 } else {
350 currentInput = null;
351 }
352 }
353 processed = true;
354 break;
355 }
356 }
357 }
358 if (!processed && currentInput != null) {
359 final Matcher matcher = currentInput.getPattern().matcher(line);
360 if (matcher.matches()) {
361 event.processGroups(currentInput, matcher);
362 countMap.put(currentInput, checkNotNull(countMap.get(currentInput)) + 1);
363 if (currentInput.childrenInputs != null && currentInput.childrenInputs.size() > 0) {
364 currentInput = currentInput.childrenInputs.get(0);
365 countMap.put(currentInput, 0);
366 } else if (checkNotNull(countMap.get(currentInput)) >= currentInput.getMaxCount()) {
367 if (currentInput.getNextInputs(countMap).size() > 0) {
368 currentInput = currentInput.getNextInputs(countMap).get(0);
369 if (countMap.get(currentInput) == null) {
370 countMap.put(currentInput, 0);
371 }
372 final Iterator<InputLine> iter = countMap.keySet().iterator();
373 while (iter.hasNext()) {
374 final InputLine inputLine = iter.next();
375 if (inputLine.level > currentInput.level) {
376 iter.remove();
377 }
378 }
379 } else {
380 currentInput = null;
381 }
382 }
383 }
384 ((StringBuffer) event.getContentValue()).append("\n").append(line); //$NON-NLS-1$
385 }
386 }
387 rawPos = fFile.getFilePointer();
388 line = fFile.getNextLine();
389 }
390 } catch (final IOException e) {
391 Activator.logError("Error seeking event. File: " + getPath(), e); //$NON-NLS-1$
392 }
393 for (final Entry<InputLine, Integer> entry : countMap.entrySet()) {
394 if (entry.getValue() < entry.getKey().getMinCount()) {
395 event = null;
396 }
397 }
398 context.setLocation(NULL_LOCATION);
399 return event;
400 }
401
402 /**
403 * @return The first few lines of the text file
404 */
405 public List<InputLine> getFirstLines() {
406 return fDefinition.inputs;
407 }
408
409 /**
410 * Parse the first line of the trace (to recognize the type).
411 *
412 * @param context
413 * Trace context
414 * @return The first event
415 */
416 public CustomTxtEvent parseFirstLine(final CustomTxtTraceContext context) {
417 CustomTxtEventType eventType = new CustomTxtEventType(checkNotNull(fDefinition.definitionName), fRootField);
418 final CustomTxtEvent event = new CustomTxtEvent(fDefinition, this, TmfTimestamp.ZERO, eventType);
419 event.processGroups(context.inputLine, context.firstLineMatcher);
420 event.setContent(new CustomEventContent(event, new StringBuffer(context.firstLine)));
421 return event;
422 }
423
424 /**
425 * Get the trace definition.
426 *
427 * @return The trace definition
428 */
429 public CustomTraceDefinition getDefinition() {
430 return fDefinition;
431 }
432
433 /**
434 * {@inheritDoc}
435 * <p>
436 * The default implementation computes the confidence as the percentage of
437 * lines in the first 100 lines of the file which match any of the root
438 * input line patterns.
439 */
440 @Override
441 public IStatus validate(IProject project, String path) {
442 File file = new File(path);
443 if (!file.exists() || !file.isFile() || !file.canRead()) {
444 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.CustomTrace_FileNotFound + ": " + path); //$NON-NLS-1$
445 }
446 int confidence = 0;
447 try {
448 if (!TmfTraceUtils.isText(file)) {
449 return new TraceValidationStatus(confidence, Activator.PLUGIN_ID);
450 }
451 } catch (IOException e) {
452 Activator.logError("Error validating file: " + path, e); //$NON-NLS-1$
453 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "IOException validating file: " + path, e); //$NON-NLS-1$
454 }
455 try (BufferedRandomAccessFile rafile = new BufferedRandomAccessFile(path, "r")) { //$NON-NLS-1$
456 int lineCount = 0;
457 double matches = 0.0;
458 String line = rafile.getNextLine();
459
460 while ((line != null) && (lineCount++ < MAX_LINES)) {
461 for (InputLine inputLine : fDefinition.inputs) {
462 Matcher matcher = inputLine.getPattern().matcher(line);
463 if (matcher.matches()) {
464 int groupCount = matcher.groupCount();
465 matches += (1.0 + groupCount / ((double) groupCount + 1));
466 break;
467 }
468 }
469 confidence = (int) (MAX_CONFIDENCE * matches / lineCount);
470 line = rafile.getNextLine();
471 }
472 } catch (IOException e) {
473 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "IOException validating file: " + path, e); //$NON-NLS-1$
474 }
475 return new TraceValidationStatus(confidence, Activator.PLUGIN_ID);
476 }
477
478 private static int fCheckpointSize = -1;
479
480 @Override
481 public synchronized int getCheckpointSize() {
482 if (fCheckpointSize == -1) {
483 TmfCheckpoint c = new TmfCheckpoint(TmfTimestamp.ZERO, new TmfLongLocation(0L), 0);
484 ByteBuffer b = ByteBuffer.allocate(ITmfCheckpoint.MAX_SERIALIZE_SIZE);
485 b.clear();
486 c.serialize(b);
487 fCheckpointSize = b.position();
488 }
489
490 return fCheckpointSize;
491 }
492
493 @Override
494 public ITmfLocation restoreLocation(ByteBuffer bufferIn) {
495 return new TmfLongLocation(bufferIn);
496 }
497
498 @Override
499 protected ITmfTraceIndexer createIndexer(int interval) {
500 return new TmfBTreeTraceIndexer(this, interval);
501 }
502
503 @Override
504 public String getTraceTypeId() {
505 return fTraceTypeId;
506 }
507
508 /**
509 * Build the trace type id for a custom text trace
510 *
511 * @param category
512 * the category
513 * @param definitionName
514 * the definition name
515 * @return the trace type id
516 */
517 public static @NonNull String buildTraceTypeId(String category, String definitionName) {
518 return CUSTOM_TXT_TRACE_TYPE_PREFIX + category + SEPARATOR + definitionName;
519 }
520
521 /**
522 * Checks whether the given trace type ID is a custom text trace type ID
523 *
524 * @param traceTypeId
525 * the trace type ID to check
526 * @return <code>true</code> if it's a custom text trace type ID else <code>false</code>
527 */
528 public static boolean isCustomTraceTypeId(@NonNull String traceTypeId) {
529 return traceTypeId.startsWith(CUSTOM_TXT_TRACE_TYPE_PREFIX);
530 }
531
532 /**
533 * This methods builds a trace type ID from a given ID taking into
534 * consideration any format changes that were done for the IDs of custom
535 * text traces. For example, such format change took place when moving to
536 * Trace Compass. Trace type IDs that are part of the plug-in extension for
537 * trace types won't be changed.
538 *
539 * This method is useful for IDs that were persisted in the workspace before
540 * the format changes (e.g. in the persistent properties of a trace
541 * resource).
542 *
543 * It ensures backwards compatibility of the workspace for custom text
544 * traces.
545 *
546 * @param traceTypeId
547 * the legacy trace type ID
548 * @return the trace type id in Trace Compass format
549 */
550 public static @NonNull String buildCompatibilityTraceTypeId(@NonNull String traceTypeId) {
551 // Handle early Trace Compass custom text trace type IDs
552 if (traceTypeId.startsWith(EARLY_TRACE_COMPASS_CUSTOM_TXT_TRACE_TYPE_PREFIX)) {
553 return CUSTOM_TXT_TRACE_TYPE_PREFIX + traceTypeId.substring(EARLY_TRACE_COMPASS_CUSTOM_TXT_TRACE_TYPE_PREFIX.length());
554 }
555
556 // Handle Linux Tools custom text trace type IDs (with and without category)
557 int index = traceTypeId.lastIndexOf(SEPARATOR);
558 if ((index != -1) && (traceTypeId.startsWith(LINUX_TOOLS_CUSTOM_TXT_TRACE_TYPE_PREFIX))) {
559 String definitionName = index < traceTypeId.length() ? traceTypeId.substring(index + 1) : ""; //$NON-NLS-1$
560 if (traceTypeId.contains(CustomTxtTrace.class.getSimpleName() + SEPARATOR) && traceTypeId.indexOf(SEPARATOR) == index) {
561 return buildTraceTypeId(CustomTxtTraceDefinition.CUSTOM_TXT_CATEGORY, definitionName);
562 }
563 return CUSTOM_TXT_TRACE_TYPE_PREFIX + traceTypeId.substring(LINUX_TOOLS_CUSTOM_TXT_TRACE_TYPE_PREFIX.length());
564 }
565 return traceTypeId;
566 }
567
568 @TmfSignalHandler
569 @Override
570 public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal) {
571 if (signal.getTrace() == this) {
572 try {
573 synchronized (this) {
574 // Reset the file handle in case it has reached the end of the
575 // file already. Otherwise, it will not be able to read new data
576 // pass the previous end.
577 initFile();
578 }
579 } catch (TmfTraceException e) {
580 Activator.logError(e.getLocalizedMessage(), e);
581 }
582 }
583 super.traceRangeUpdated(signal);
584 }
585 }
This page took 0.043129 seconds and 4 git commands to generate.