tmf: Add command to manually refresh traces
[deliverable/tracecompass.git] / btf / org.eclipse.tracecompass.btf.core / src / org / eclipse / tracecompass / btf / core / trace / BtfTrace.java
1 /*******************************************************************************
2 * Copyright (c) 2014, 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 * Matthew Khouzam - Initial API and implementation
11 * Patrick Tasse - Fix parsing of instance numbers
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.btf.core.trace;
15
16 import java.io.File;
17 import java.io.FileNotFoundException;
18 import java.io.IOException;
19 import java.io.RandomAccessFile;
20 import java.nio.ByteBuffer;
21 import java.text.ParseException;
22 import java.text.SimpleDateFormat;
23 import java.util.Date;
24 import java.util.HashMap;
25 import java.util.Map;
26 import java.util.TreeMap;
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.tracecompass.btf.core.Activator;
33 import org.eclipse.tracecompass.btf.core.event.BtfEvent;
34 import org.eclipse.tracecompass.btf.core.event.BtfEventType;
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.CustomTxtTraceContext;
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.ITmfTraceProperties;
46 import org.eclipse.tracecompass.tmf.core.trace.TmfContext;
47 import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
48 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
49 import org.eclipse.tracecompass.tmf.core.trace.TraceValidationStatus;
50 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
51 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
52 import org.eclipse.tracecompass.tmf.core.trace.indexer.TmfBTreeTraceIndexer;
53 import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.ITmfCheckpoint;
54 import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.TmfCheckpoint;
55 import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
56 import org.eclipse.tracecompass.tmf.core.trace.location.TmfLongLocation;
57
58 import com.google.common.collect.ImmutableMap;
59
60 /**
61 * BTF reader. Reads Best Trace Format traces.
62 *
63 * @author Matthew Khouzam
64 */
65 public class BtfTrace extends TmfTrace implements ITmfPersistentlyIndexable, ITmfTraceProperties {
66
67 private static final int MAX_FIELDS = 7;
68
69 private static final long MICROSECONDS_IN_A_SECOND = 1000000L;
70
71 private static final String VERSION = "#version"; //$NON-NLS-1$
72 private static final String CREATOR = "#creator"; //$NON-NLS-1$
73 private static final String CREATIONDATE = "#creationDate"; //$NON-NLS-1$
74 private static final String INPUTFILE = "#inputFile"; //$NON-NLS-1$
75 private static final String TIMESCALE = "#timeScale"; //$NON-NLS-1$
76 private static final String ENTITYTYPE = "#entityType"; //$NON-NLS-1$
77 private static final String ENTITYTABLE = "#entityTable"; //$NON-NLS-1$
78 private static final String ENTITYTYPETABLE = "#entityTypeTable"; //$NON-NLS-1$
79
80 // lower-case helpers
81 private static final String lCREATIONDATE = "#creationdate"; //$NON-NLS-1$
82 private static final String lINPUTFILE = "#inputfile"; //$NON-NLS-1$
83 private static final String lTIMESCALE = "#timescale"; //$NON-NLS-1$
84 private static final String lENTITYTYPE = "#entitytype"; //$NON-NLS-1$
85 private static final String lENTITYTABLE = "#entitytable"; //$NON-NLS-1$
86 private static final String lENTITYTYPETABLE = "#entitytypetable"; //$NON-NLS-1$
87
88 private static final TmfLongLocation NULL_LOCATION = new TmfLongLocation(-1L);
89
90 private static final int CACHE_SIZE = 256;
91 private static final int MAX_CONFIDENCE = 100;
92 private static final int MAX_LINES = 100;
93
94 private static int fCheckpointSize = -1;
95
96 private final Map<String, String> fProperties = new HashMap<>();
97
98 private final Map<Integer, String> fEntityTable = new TreeMap<>();
99 private final Map<BtfEventType, String> fEntityTypeTable = new HashMap<>();
100 private final Map<Integer, BtfEventType> fEntityTypes = new TreeMap<>();
101
102 private String fVersion;
103 private String fCreator;
104 private String fCreationDate;
105 private String fInputFile;
106 // default unit is ns
107 private BtfTimestampFormat fTsFormat = BtfTimestampFormat.NS;
108
109 private File fFile;
110 private RandomAccessFile fFileInput;
111 private long fDataOffset;
112 private long fTsOffset = 0;
113
114 /**
115 * Default constructor
116 */
117 public BtfTrace() {
118 super();
119 setCacheSize(CACHE_SIZE);
120 fProperties.put(TIMESCALE, fTsFormat.toString());
121 }
122
123 private void parseHeader(RandomAccessFile input) throws IOException {
124 String line = input.readLine();
125 long pos = 0;
126 while (line != null && line.startsWith("#")) { //$NON-NLS-1$
127 String[] tokens = line.split(" ", 2); //$NON-NLS-1$
128 /*
129 * please note that the examples we were given and the spec are NOT
130 * consistent, so we are ignoring the case to avoid issues
131 */
132 switch (tokens[0].toLowerCase()) {
133 case VERSION:
134 fVersion = tokens[1];
135 fProperties.put(VERSION, fVersion);
136 break;
137 case CREATOR:
138 fCreator = tokens[1];
139 fProperties.put(CREATOR, fCreator);
140 break;
141 case lCREATIONDATE:
142 fCreationDate = tokens[1];
143 fProperties.put(CREATIONDATE, fCreationDate);
144
145 try {
146 // DateFormats are inherently unsafe for multithreaded use so we can't make this a field. Just in case.
147 final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"); //$NON-NLS-1$
148 Date dateTime = ISO8601DATEFORMAT.parse(fCreationDate);
149 fTsOffset = dateTime.getTime() * MICROSECONDS_IN_A_SECOND;
150 } catch (ParseException e) {
151 Activator.logWarning("Creation date error: " + e.getMessage()); //$NON-NLS-1$
152 }
153 break;
154 case lINPUTFILE:
155 fInputFile = tokens[1];
156 fProperties.put(INPUTFILE, fInputFile);
157 break;
158 case lTIMESCALE:
159 fTsFormat = BtfTimestampFormat.parse(tokens[1]);
160 fProperties.put(TIMESCALE, fTsFormat.toString());
161 break;
162 case lENTITYTYPE:
163 pos = fFileInput.getFilePointer();
164 line = fFileInput.readLine();
165 while (line.startsWith("#-")) { //$NON-NLS-1$
166 String tempLine = line.substring(1);
167 String[] elements = tempLine.split(" ", 2); //$NON-NLS-1$
168 fEntityTypes.put(Integer.parseInt(elements[0]), BtfEventTypeFactory.parse(elements[1]));
169 pos = fFileInput.getFilePointer();
170 line = fFileInput.readLine();
171 }
172 fFileInput.seek(pos);
173 fProperties.put(ENTITYTYPE, fEntityTypes.toString());
174 break;
175 case lENTITYTABLE:
176 pos = fFileInput.getFilePointer();
177 line = fFileInput.readLine();
178 while (line.startsWith("#-")) { //$NON-NLS-1$
179 String tempLine = line.substring(1);
180 String[] elements = tempLine.split(" ", 2); //$NON-NLS-1$
181 fEntityTable.put(Integer.parseInt(elements[0]), elements[1]);
182 pos = fFileInput.getFilePointer();
183 line = fFileInput.readLine();
184 }
185 fProperties.put(ENTITYTABLE, fEntityTable.toString());
186 fFileInput.seek(pos);
187 break;
188 case lENTITYTYPETABLE:
189 pos = fFileInput.getFilePointer();
190 line = fFileInput.readLine();
191 while (line.startsWith("#-")) { //$NON-NLS-1$
192 String tempLine = line.substring(1);
193 String[] elements = tempLine.split(" ", 2); //$NON-NLS-1$
194 fEntityTypeTable.put(BtfEventTypeFactory.parse(elements[0]), elements[1]);
195 pos = fFileInput.getFilePointer();
196 line = fFileInput.readLine();
197 }
198 fFileInput.seek(pos);
199 fProperties.put(ENTITYTYPETABLE, fEntityTypeTable.toString());
200 break;
201 default:
202 break;
203 }
204 fDataOffset = input.getFilePointer();
205 line = input.readLine();
206 }
207 fTsOffset = (long) (fTsOffset * fTsFormat.getScaleFactor());
208 }
209
210 @Override
211 public void initTrace(IResource resource, String path, Class<? extends ITmfEvent> type) throws TmfTraceException {
212 super.initTrace(resource, path, type);
213 fFile = new File(path);
214 try {
215 fFileInput = new RandomAccessFile(fFile, "r"); //$NON-NLS-1$
216 parseHeader(fFileInput);
217 } catch (IOException e) {
218 throw new TmfTraceException(e.getMessage(), e);
219 }
220
221 }
222
223 private void initFile() throws TmfTraceException {
224 closeFile();
225 try {
226 fFileInput = new BufferedRandomAccessFile(getPath(), "r"); //$NON-NLS-1$
227 } catch (IOException e) {
228 throw new TmfTraceException(e.getMessage(), e);
229 }
230 }
231
232 private void closeFile() {
233 if (fFileInput != null) {
234 try {
235 fFileInput.close();
236 } catch (IOException e) {
237 } finally {
238 fFileInput = null;
239 }
240 }
241 }
242
243 @Override
244 public IStatus validate(IProject project, String path) {
245 File file = new File(path);
246 if (!file.exists()) {
247 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "File not found: " + path); //$NON-NLS-1$
248 }
249 if (!file.isFile()) {
250 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Not a file. It's a directory: " + path); //$NON-NLS-1$
251 }
252 int confidence = 0;
253 try {
254 if (!TmfTraceUtils.isText(file)) {
255 return new TraceValidationStatus(confidence, Activator.PLUGIN_ID);
256 }
257 } catch (IOException e) {
258 Activator.logError("Error validating file: " + path, e); //$NON-NLS-1$
259 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "IOException validating file: " + path, e); //$NON-NLS-1$
260 }
261 try (BufferedRandomAccessFile rafile = new BufferedRandomAccessFile(path, "r")) { //$NON-NLS-1$
262 int lineCount = 0;
263 int matches = 0;
264 String line = rafile.getNextLine();
265 while ((line != null) && line.startsWith("#")) { //$NON-NLS-1$
266 line = rafile.getNextLine();
267 }
268 while ((line != null) && (lineCount++ < MAX_LINES)) {
269 try {
270 ITmfEvent event = parseLine(0, line);
271 if (event != null) {
272 matches++;
273 }
274 } catch (RuntimeException e) {
275 confidence = Integer.MIN_VALUE;
276 }
277
278 confidence = MAX_CONFIDENCE * matches / lineCount;
279 line = rafile.getNextLine();
280 }
281 } catch (IOException e) {
282 Activator.logError("Error validating file: " + path, e); //$NON-NLS-1$
283 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "IOException validating file: " + path, e); //$NON-NLS-1$
284 }
285
286 return new TraceValidationStatus(confidence, Activator.PLUGIN_ID);
287 }
288
289 @Override
290 public ITmfLocation getCurrentLocation() {
291 long temp = -1;
292 try {
293 temp = fFileInput.getFilePointer();
294 } catch (IOException e) {
295 }
296 return new TmfLongLocation(temp);
297 }
298
299 @Override
300 public double getLocationRatio(ITmfLocation location) {
301 long size = fFile.length() - fDataOffset;
302 long pos;
303 try {
304 pos = fFileInput.getFilePointer() - fDataOffset;
305 } catch (IOException e) {
306 pos = 0;
307 }
308 return 1.0 / size * pos;
309 }
310
311 @Override
312 public ITmfContext seekEvent(ITmfLocation location) {
313 final TmfContext context = new TmfContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
314 if (NULL_LOCATION.equals(location) || fFile == null) {
315 return context;
316 }
317 try {
318 if (location == null) {
319 fFileInput.seek(fDataOffset);
320 } else if (location.getLocationInfo() instanceof Long) {
321 fFileInput.seek((Long) location.getLocationInfo());
322 }
323 context.setLocation(new TmfLongLocation(fFileInput.getFilePointer()));
324 return context;
325 } catch (final FileNotFoundException e) {
326 Activator.logError("Error seeking event. File not found: " + getPath(), e); //$NON-NLS-1$
327 return context;
328 } catch (final IOException e) {
329 Activator.logError("Error seeking event. File: " + getPath(), e); //$NON-NLS-1$
330 return context;
331 }
332 }
333
334 @Override
335 public ITmfContext seekEvent(double ratio) {
336 if (fFile == null) {
337 return new TmfContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
338 }
339 try {
340 long pos = Math.round(ratio * fFile.length()) - fDataOffset;
341 while (pos > 0) {
342 fFileInput.seek(pos - 1);
343 if (fFileInput.read() == '\n') {
344 break;
345 }
346 pos--;
347 }
348 final ITmfLocation location = new TmfLongLocation(pos);
349 final ITmfContext context = seekEvent(location);
350 context.setRank(ITmfContext.UNKNOWN_RANK);
351 return context;
352 } catch (final IOException e) {
353 Activator.logError("Error seeking event. File: " + getPath(), e); //$NON-NLS-1$
354 return new CustomTxtTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
355 }
356 }
357
358 @Override
359 public ITmfEvent parseEvent(ITmfContext tmfContext) {
360 if (fFile == null || (!(tmfContext instanceof TmfContext))) {
361 return null;
362 }
363
364 final TmfContext context = (TmfContext) tmfContext;
365 if (context.getLocation() == null
366 || !(context.getLocation().getLocationInfo() instanceof Long)
367 || NULL_LOCATION.equals(context.getLocation())) {
368 return null;
369 }
370
371 return parseLine(context);
372
373 }
374
375 /**
376 * Parse a line with a context
377 *
378 * @param context
379 * the context, has a location
380 * @return the event from a given line
381 */
382 private ITmfEvent parseLine(TmfContext context) {
383 try {
384 if (!context.getLocation().getLocationInfo().equals(fFileInput.getFilePointer())) {
385 seekEvent(context.getLocation());
386 }
387 } catch (IOException e1) {
388 seekEvent(context.getLocation());
389 }
390 String line;
391 try {
392 line = fFileInput.readLine();
393 return parseLine(context.getRank(), line);
394
395 } catch (IOException e) {
396 }
397
398 return null;
399 }
400
401 /**
402 * Parse a line of text and make an event using it.
403 *
404 * @param rank
405 * the rank of the event
406 * @param line
407 * the raw string of the event
408 * @return the event
409 */
410 private ITmfEvent parseLine(long rank, String line) {
411 if (line == null) {
412 return null;
413 }
414 String[] tokens = line.split(",", MAX_FIELDS); //$NON-NLS-1$
415 if (tokens.length < MAX_FIELDS) {
416 return null;
417 }
418 int i = 0;
419 long timestamp = Long.parseLong(tokens[i++]);
420 String source = tokens[i++];
421 long sourceInstance = -1;
422 try {
423 sourceInstance = Long.parseLong(tokens[i++]);
424 } catch (NumberFormatException e) {
425 // this field can be empty
426 }
427 BtfEventType type = BtfEventTypeFactory.parse(tokens[i++]);
428 String target = tokens[i++];
429 long targetInstance = -1;
430 try {
431 targetInstance = Long.parseLong(tokens[i++]);
432 } catch (NumberFormatException e) {
433 // this field can be empty
434 }
435 String event = tokens[i++];
436
437 ITmfEventField content = type.generateContent(event, sourceInstance, targetInstance);
438
439 return new BtfEvent(this, rank,
440 getTimestampTransform().transform(fTsFormat.createTimestamp(timestamp + fTsOffset)),
441 source,
442 type,
443 type.getDescription(),
444 content,
445 target);
446 }
447
448 @Override
449 public int getCheckpointSize() {
450 synchronized (BtfTrace.class) {
451 if (fCheckpointSize == -1) {
452 TmfCheckpoint c = new TmfCheckpoint(TmfTimestamp.ZERO, new TmfLongLocation(0L), 0);
453 ByteBuffer b = ByteBuffer.allocate(ITmfCheckpoint.MAX_SERIALIZE_SIZE);
454 b.clear();
455 c.serialize(b);
456 fCheckpointSize = b.position();
457 }
458 }
459
460 return fCheckpointSize;
461 }
462
463 @Override
464 public ITmfLocation restoreLocation(ByteBuffer bufferIn) {
465 return new TmfLongLocation(bufferIn);
466 }
467
468 @Override
469 protected ITmfTraceIndexer createIndexer(int interval) {
470 return new TmfBTreeTraceIndexer(this, interval);
471 }
472
473 @Override
474 public Map<String, String> getTraceProperties() {
475 return ImmutableMap.copyOf(fProperties);
476 }
477
478 @Override
479 public Iterable<ITmfEventAspect> getEventAspects() {
480 return BtfEventAspects.getAspects();
481 }
482
483 @Override
484 public synchronized void dispose() {
485 RandomAccessFile raf = fFileInput;
486 if (raf != null) {
487 try {
488 raf.close();
489 } catch (IOException e) {
490 }
491 }
492 super.dispose();
493 }
494
495 @TmfSignalHandler
496 @Override
497 public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal) {
498 if (signal.getTrace() == this) {
499 try {
500 synchronized (this) {
501 // Reset the file handle in case it has reached the end of the
502 // file already. Otherwise, it will not be able to read new data
503 // pass the previous end.
504 initFile();
505 }
506 } catch (TmfTraceException e) {
507 Activator.logError(e.getLocalizedMessage(), e);
508 }
509 }
510 super.traceRangeUpdated(signal);
511 }
512 }
This page took 0.040779 seconds and 5 git commands to generate.