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