1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 Ericsson
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
10 * Matthew Khouzam - Initial API and implementation
11 * Patrick Tasse - Fix parsing of instance numbers
12 *******************************************************************************/
14 package org
.eclipse
.tracecompass
.btf
.core
.trace
;
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
;
26 import java
.util
.TreeMap
;
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
.btf
.core
.Activator
;
34 import org
.eclipse
.tracecompass
.btf
.core
.event
.BtfEvent
;
35 import org
.eclipse
.tracecompass
.btf
.core
.event
.BtfEventType
;
36 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
37 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEventField
;
38 import org
.eclipse
.tracecompass
.tmf
.core
.event
.aspect
.ITmfEventAspect
;
39 import org
.eclipse
.tracecompass
.tmf
.core
.exceptions
.TmfTraceException
;
40 import org
.eclipse
.tracecompass
.tmf
.core
.io
.BufferedRandomAccessFile
;
41 import org
.eclipse
.tracecompass
.tmf
.core
.parsers
.custom
.CustomTxtTraceContext
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.ITmfPropertiesProvider
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceRangeUpdatedSignal
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimestamp
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfContext
;
47 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfContext
;
48 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTrace
;
49 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
50 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TraceValidationStatus
;
51 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.indexer
.ITmfPersistentlyIndexable
;
52 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.indexer
.ITmfTraceIndexer
;
53 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.indexer
.TmfBTreeTraceIndexer
;
54 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.indexer
.checkpoint
.ITmfCheckpoint
;
55 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.indexer
.checkpoint
.TmfCheckpoint
;
56 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.location
.ITmfLocation
;
57 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.location
.TmfLongLocation
;
59 import com
.google
.common
.collect
.ImmutableMap
;
62 * BTF reader. Reads Best Trace Format traces.
64 * @author Matthew Khouzam
66 public class BtfTrace
extends TmfTrace
implements ITmfPersistentlyIndexable
, ITmfPropertiesProvider
{
68 private static final int MAX_FIELDS
= 7;
70 private static final long MICROSECONDS_IN_A_SECOND
= 1000000L;
72 private static final String VERSION
= "#version"; //$NON-NLS-1$
73 private static final String CREATOR
= "#creator"; //$NON-NLS-1$
74 private static final String CREATIONDATE
= "#creationDate"; //$NON-NLS-1$
75 private static final String INPUTFILE
= "#inputFile"; //$NON-NLS-1$
76 private static final String TIMESCALE
= "#timeScale"; //$NON-NLS-1$
77 private static final String ENTITYTYPE
= "#entityType"; //$NON-NLS-1$
78 private static final String ENTITYTABLE
= "#entityTable"; //$NON-NLS-1$
79 private static final String ENTITYTYPETABLE
= "#entityTypeTable"; //$NON-NLS-1$
82 private static final String lCREATIONDATE
= "#creationdate"; //$NON-NLS-1$
83 private static final String lINPUTFILE
= "#inputfile"; //$NON-NLS-1$
84 private static final String lTIMESCALE
= "#timescale"; //$NON-NLS-1$
85 private static final String lENTITYTYPE
= "#entitytype"; //$NON-NLS-1$
86 private static final String lENTITYTABLE
= "#entitytable"; //$NON-NLS-1$
87 private static final String lENTITYTYPETABLE
= "#entitytypetable"; //$NON-NLS-1$
89 private static final TmfLongLocation NULL_LOCATION
= new TmfLongLocation(-1L);
91 private static final int CACHE_SIZE
= 256;
92 private static final int MAX_CONFIDENCE
= 100;
93 private static final int MAX_LINES
= 100;
95 private static int fCheckpointSize
= -1;
97 private final @NonNull Map
<String
, String
> fProperties
= new HashMap
<>();
99 private final @NonNull Map
<Integer
, String
> fEntityTable
= new TreeMap
<>();
100 private final @NonNull Map
<BtfEventType
, String
> fEntityTypeTable
= new HashMap
<>();
101 private final @NonNull Map
<Integer
, BtfEventType
> fEntityTypes
= new TreeMap
<>();
103 private String fVersion
;
104 private String fCreator
;
105 private String fCreationDate
;
106 private String fInputFile
;
107 // default unit is ns
108 private BtfTimestampFormat fTsFormat
= BtfTimestampFormat
.NS
;
111 private RandomAccessFile fFileInput
;
112 private long fDataOffset
;
113 private long fTsOffset
= 0;
116 * Default constructor
120 setCacheSize(CACHE_SIZE
);
121 fProperties
.put(TIMESCALE
, fTsFormat
.toString());
124 private void parseHeader(RandomAccessFile input
) throws IOException
{
125 String line
= input
.readLine();
127 while (line
!= null && line
.startsWith("#")) { //$NON-NLS-1$
128 String
[] tokens
= line
.split(" ", 2); //$NON-NLS-1$
130 * please note that the examples we were given and the spec are NOT
131 * consistent, so we are ignoring the case to avoid issues
133 switch (tokens
[0].toLowerCase()) {
135 fVersion
= tokens
[1];
136 fProperties
.put(VERSION
, fVersion
);
139 fCreator
= tokens
[1];
140 fProperties
.put(CREATOR
, fCreator
);
143 fCreationDate
= tokens
[1];
144 fProperties
.put(CREATIONDATE
, fCreationDate
);
147 // DateFormats are inherently unsafe for multithreaded use
148 // so we can't make this a field. Just in case.
149 final SimpleDateFormat ISO8601DATEFORMAT
= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"); //$NON-NLS-1$
150 Date dateTime
= ISO8601DATEFORMAT
.parse(fCreationDate
);
151 fTsOffset
= dateTime
.getTime() * MICROSECONDS_IN_A_SECOND
;
152 } catch (ParseException e
) {
153 Activator
.logWarning("Creation date error: " + e
.getMessage()); //$NON-NLS-1$
157 fInputFile
= tokens
[1];
158 fProperties
.put(INPUTFILE
, fInputFile
);
161 fTsFormat
= BtfTimestampFormat
.parse(tokens
[1]);
162 fProperties
.put(TIMESCALE
, fTsFormat
.toString());
165 pos
= fFileInput
.getFilePointer();
166 line
= fFileInput
.readLine();
167 while (line
.startsWith("#-")) { //$NON-NLS-1$
168 String tempLine
= line
.substring(1);
169 String
[] elements
= tempLine
.split(" ", 2); //$NON-NLS-1$
170 fEntityTypes
.put(Integer
.parseInt(elements
[0]), BtfEventTypeFactory
.parse(elements
[1]));
171 pos
= fFileInput
.getFilePointer();
172 line
= fFileInput
.readLine();
174 fFileInput
.seek(pos
);
175 fProperties
.put(ENTITYTYPE
, fEntityTypes
.toString());
178 pos
= fFileInput
.getFilePointer();
179 line
= fFileInput
.readLine();
180 while (line
.startsWith("#-")) { //$NON-NLS-1$
181 String tempLine
= line
.substring(1);
182 String
[] elements
= tempLine
.split(" ", 2); //$NON-NLS-1$
183 fEntityTable
.put(Integer
.parseInt(elements
[0]), elements
[1]);
184 pos
= fFileInput
.getFilePointer();
185 line
= fFileInput
.readLine();
187 fProperties
.put(ENTITYTABLE
, fEntityTable
.toString());
188 fFileInput
.seek(pos
);
190 case lENTITYTYPETABLE
:
191 pos
= fFileInput
.getFilePointer();
192 line
= fFileInput
.readLine();
193 while (line
.startsWith("#-")) { //$NON-NLS-1$
194 String tempLine
= line
.substring(1);
195 String
[] elements
= tempLine
.split(" ", 2); //$NON-NLS-1$
196 fEntityTypeTable
.put(BtfEventTypeFactory
.parse(elements
[0]), elements
[1]);
197 pos
= fFileInput
.getFilePointer();
198 line
= fFileInput
.readLine();
200 fFileInput
.seek(pos
);
201 fProperties
.put(ENTITYTYPETABLE
, fEntityTypeTable
.toString());
206 fDataOffset
= input
.getFilePointer();
207 line
= input
.readLine();
209 fTsOffset
= (long) (fTsOffset
* fTsFormat
.getScaleFactor());
213 public void initTrace(IResource resource
, String path
, Class
<?
extends ITmfEvent
> type
) throws TmfTraceException
{
214 super.initTrace(resource
, path
, type
);
215 fFile
= new File(path
);
217 fFileInput
= new RandomAccessFile(fFile
, "r"); //$NON-NLS-1$
218 parseHeader(fFileInput
);
219 } catch (IOException e
) {
220 throw new TmfTraceException(e
.getMessage(), e
);
225 private void initFile() throws TmfTraceException
{
228 fFileInput
= new BufferedRandomAccessFile(getPath(), "r"); //$NON-NLS-1$
229 } catch (IOException e
) {
230 throw new TmfTraceException(e
.getMessage(), e
);
234 private void closeFile() {
235 if (fFileInput
!= null) {
238 } catch (IOException e
) {
246 public IStatus
validate(IProject project
, String path
) {
247 File file
= new File(path
);
248 if (!file
.exists()) {
249 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, "File not found: " + path
); //$NON-NLS-1$
251 if (!file
.isFile()) {
252 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, "Not a file. It's a directory: " + path
); //$NON-NLS-1$
256 if (!TmfTraceUtils
.isText(file
)) {
257 return new TraceValidationStatus(confidence
, Activator
.PLUGIN_ID
);
259 } catch (IOException e
) {
260 Activator
.logError("Error validating file: " + path
, e
); //$NON-NLS-1$
261 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, "IOException validating file: " + path
, e
); //$NON-NLS-1$
263 try (BufferedRandomAccessFile rafile
= new BufferedRandomAccessFile(path
, "r")) { //$NON-NLS-1$
266 String line
= rafile
.getNextLine();
267 while ((line
!= null) && line
.startsWith("#")) { //$NON-NLS-1$
268 line
= rafile
.getNextLine();
270 while ((line
!= null) && (lineCount
++ < MAX_LINES
)) {
272 ITmfEvent event
= parseLine(0, line
);
276 } catch (RuntimeException e
) {
277 confidence
= Integer
.MIN_VALUE
;
280 confidence
= MAX_CONFIDENCE
* matches
/ lineCount
;
281 line
= rafile
.getNextLine();
283 } catch (IOException e
) {
284 Activator
.logError("Error validating file: " + path
, e
); //$NON-NLS-1$
285 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, "IOException validating file: " + path
, e
); //$NON-NLS-1$
288 return new TraceValidationStatus(confidence
, Activator
.PLUGIN_ID
);
292 public ITmfLocation
getCurrentLocation() {
295 temp
= fFileInput
.getFilePointer();
296 } catch (IOException e
) {
298 return new TmfLongLocation(temp
);
302 public double getLocationRatio(ITmfLocation location
) {
303 long size
= fFile
.length() - fDataOffset
;
306 pos
= fFileInput
.getFilePointer() - fDataOffset
;
307 } catch (IOException e
) {
310 return 1.0 / size
* pos
;
314 public ITmfContext
seekEvent(ITmfLocation location
) {
315 final TmfContext context
= new TmfContext(NULL_LOCATION
, ITmfContext
.UNKNOWN_RANK
);
316 if (NULL_LOCATION
.equals(location
) || fFile
== null) {
320 if (location
== null) {
321 fFileInput
.seek(fDataOffset
);
322 } else if (location
.getLocationInfo() instanceof Long
) {
323 fFileInput
.seek((Long
) location
.getLocationInfo());
325 context
.setLocation(new TmfLongLocation(fFileInput
.getFilePointer()));
327 } catch (final FileNotFoundException e
) {
328 Activator
.logError("Error seeking event. File not found: " + getPath(), e
); //$NON-NLS-1$
330 } catch (final IOException e
) {
331 Activator
.logError("Error seeking event. File: " + getPath(), e
); //$NON-NLS-1$
337 public ITmfContext
seekEvent(double ratio
) {
339 return new TmfContext(NULL_LOCATION
, ITmfContext
.UNKNOWN_RANK
);
342 long pos
= Math
.round(ratio
* fFile
.length()) - fDataOffset
;
344 fFileInput
.seek(pos
- 1);
345 if (fFileInput
.read() == '\n') {
350 final ITmfLocation location
= new TmfLongLocation(pos
);
351 final ITmfContext context
= seekEvent(location
);
352 context
.setRank(ITmfContext
.UNKNOWN_RANK
);
354 } catch (final IOException e
) {
355 Activator
.logError("Error seeking event. File: " + getPath(), e
); //$NON-NLS-1$
356 return new CustomTxtTraceContext(NULL_LOCATION
, ITmfContext
.UNKNOWN_RANK
);
361 public ITmfEvent
parseEvent(ITmfContext tmfContext
) {
362 if (fFile
== null || (!(tmfContext
instanceof TmfContext
))) {
366 final TmfContext context
= (TmfContext
) tmfContext
;
367 ITmfLocation location
= context
.getLocation();
369 || !(location
.getLocationInfo() instanceof Long
)
370 || NULL_LOCATION
.equals(location
)) {
374 return parseLine(context
);
379 * Parse a line with a context
382 * the context, has a location
383 * @return the event from a given line
385 private ITmfEvent
parseLine(TmfContext context
) {
386 ITmfLocation location
= context
.getLocation();
387 if (location
!= null) {
389 if (!location
.getLocationInfo().equals(fFileInput
.getFilePointer())) {
392 } catch (IOException e1
) {
397 line
= fFileInput
.readLine();
398 return parseLine(context
.getRank(), line
);
400 } catch (IOException e
) {
401 Activator
.logError(e
.getMessage(), e
);
408 * Parse a line of text and make an event using it.
411 * the rank of the event
413 * the raw string of the event
416 private ITmfEvent
parseLine(long rank
, String line
) {
420 String
[] tokens
= line
.split(",", MAX_FIELDS
); //$NON-NLS-1$
421 if (tokens
.length
< MAX_FIELDS
) {
425 long timestamp
= Long
.parseLong(tokens
[i
++]);
426 String source
= tokens
[i
++];
427 long sourceInstance
= -1;
429 sourceInstance
= Long
.parseLong(tokens
[i
++]);
430 } catch (NumberFormatException e
) {
431 // this field can be empty
433 BtfEventType type
= BtfEventTypeFactory
.parse(tokens
[i
++]);
434 String target
= tokens
[i
++];
435 long targetInstance
= -1;
437 targetInstance
= Long
.parseLong(tokens
[i
++]);
438 } catch (NumberFormatException e
) {
439 // this field can be empty
441 String event
= tokens
[i
++];
443 ITmfEventField content
= type
.generateContent(event
, sourceInstance
, targetInstance
);
445 return new BtfEvent(this, rank
,
446 getTimestampTransform().transform(fTsFormat
.createTimestamp(timestamp
+ fTsOffset
)),
449 type
.getDescription(),
455 public int getCheckpointSize() {
456 synchronized (BtfTrace
.class) {
457 if (fCheckpointSize
== -1) {
458 TmfCheckpoint c
= new TmfCheckpoint(TmfTimestamp
.ZERO
, new TmfLongLocation(0L), 0);
459 ByteBuffer b
= ByteBuffer
.allocate(ITmfCheckpoint
.MAX_SERIALIZE_SIZE
);
462 fCheckpointSize
= b
.position();
466 return fCheckpointSize
;
470 public ITmfLocation
restoreLocation(ByteBuffer bufferIn
) {
471 return new TmfLongLocation(bufferIn
);
475 protected ITmfTraceIndexer
createIndexer(int interval
) {
476 return new TmfBTreeTraceIndexer(this, interval
);
483 public Map
<String
, String
> getProperties() {
484 return ImmutableMap
.copyOf(fProperties
);
488 public Iterable
<ITmfEventAspect
<?
>> getEventAspects() {
489 return BtfEventAspects
.getAspects();
493 public synchronized void dispose() {
494 RandomAccessFile raf
= fFileInput
;
498 } catch (IOException e
) {
506 public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal
) {
507 if (signal
.getTrace() == this) {
509 synchronized (this) {
511 * Reset the file handle in case it has reached the end of
512 * the file already. Otherwise, it will not be able to read
513 * new data pass the previous end.
517 } catch (TmfTraceException e
) {
518 Activator
.logError(e
.getLocalizedMessage(), e
);
521 super.traceRangeUpdated(signal
);