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