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
.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
;
58 import com
.google
.common
.collect
.ImmutableMap
;
61 * BTF reader. Reads Best Trace Format traces.
63 * @author Matthew Khouzam
65 public class BtfTrace
extends TmfTrace
implements ITmfPersistentlyIndexable
, ITmfTraceProperties
{
67 private static final int MAX_FIELDS
= 7;
69 private static final long MICROSECONDS_IN_A_SECOND
= 1000000L;
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$
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$
88 private static final TmfLongLocation NULL_LOCATION
= new TmfLongLocation(-1L);
90 private static final int CACHE_SIZE
= 256;
91 private static final int MAX_CONFIDENCE
= 100;
92 private static final int MAX_LINES
= 100;
94 private static int fCheckpointSize
= -1;
96 private final Map
<String
, String
> fProperties
= new HashMap
<>();
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
<>();
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
;
110 private RandomAccessFile fFileInput
;
111 private long fDataOffset
;
112 private long fTsOffset
= 0;
115 * Default constructor
119 setCacheSize(CACHE_SIZE
);
120 fProperties
.put(TIMESCALE
, fTsFormat
.toString());
123 private void parseHeader(RandomAccessFile input
) throws IOException
{
124 String line
= input
.readLine();
126 while (line
!= null && line
.startsWith("#")) { //$NON-NLS-1$
127 String
[] tokens
= line
.split(" ", 2); //$NON-NLS-1$
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
132 switch (tokens
[0].toLowerCase()) {
134 fVersion
= tokens
[1];
135 fProperties
.put(VERSION
, fVersion
);
138 fCreator
= tokens
[1];
139 fProperties
.put(CREATOR
, fCreator
);
142 fCreationDate
= tokens
[1];
143 fProperties
.put(CREATIONDATE
, fCreationDate
);
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$
155 fInputFile
= tokens
[1];
156 fProperties
.put(INPUTFILE
, fInputFile
);
159 fTsFormat
= BtfTimestampFormat
.parse(tokens
[1]);
160 fProperties
.put(TIMESCALE
, fTsFormat
.toString());
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();
172 fFileInput
.seek(pos
);
173 fProperties
.put(ENTITYTYPE
, fEntityTypes
.toString());
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();
185 fProperties
.put(ENTITYTABLE
, fEntityTable
.toString());
186 fFileInput
.seek(pos
);
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();
198 fFileInput
.seek(pos
);
199 fProperties
.put(ENTITYTYPETABLE
, fEntityTypeTable
.toString());
204 fDataOffset
= input
.getFilePointer();
205 line
= input
.readLine();
207 fTsOffset
= (long) (fTsOffset
* fTsFormat
.getScaleFactor());
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
);
215 fFileInput
= new RandomAccessFile(fFile
, "r"); //$NON-NLS-1$
216 parseHeader(fFileInput
);
217 } catch (IOException e
) {
218 throw new TmfTraceException(e
.getMessage(), e
);
223 private void initFile() throws TmfTraceException
{
226 fFileInput
= new BufferedRandomAccessFile(getPath(), "r"); //$NON-NLS-1$
227 } catch (IOException e
) {
228 throw new TmfTraceException(e
.getMessage(), e
);
232 private void closeFile() {
233 if (fFileInput
!= null) {
236 } catch (IOException e
) {
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$
249 if (!file
.isFile()) {
250 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, "Not a file. It's a directory: " + path
); //$NON-NLS-1$
254 if (!TmfTraceUtils
.isText(file
)) {
255 return new TraceValidationStatus(confidence
, Activator
.PLUGIN_ID
);
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$
261 try (BufferedRandomAccessFile rafile
= new BufferedRandomAccessFile(path
, "r")) { //$NON-NLS-1$
264 String line
= rafile
.getNextLine();
265 while ((line
!= null) && line
.startsWith("#")) { //$NON-NLS-1$
266 line
= rafile
.getNextLine();
268 while ((line
!= null) && (lineCount
++ < MAX_LINES
)) {
270 ITmfEvent event
= parseLine(0, line
);
274 } catch (RuntimeException e
) {
275 confidence
= Integer
.MIN_VALUE
;
278 confidence
= MAX_CONFIDENCE
* matches
/ lineCount
;
279 line
= rafile
.getNextLine();
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$
286 return new TraceValidationStatus(confidence
, Activator
.PLUGIN_ID
);
290 public ITmfLocation
getCurrentLocation() {
293 temp
= fFileInput
.getFilePointer();
294 } catch (IOException e
) {
296 return new TmfLongLocation(temp
);
300 public double getLocationRatio(ITmfLocation location
) {
301 long size
= fFile
.length() - fDataOffset
;
304 pos
= fFileInput
.getFilePointer() - fDataOffset
;
305 } catch (IOException e
) {
308 return 1.0 / size
* pos
;
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) {
318 if (location
== null) {
319 fFileInput
.seek(fDataOffset
);
320 } else if (location
.getLocationInfo() instanceof Long
) {
321 fFileInput
.seek((Long
) location
.getLocationInfo());
323 context
.setLocation(new TmfLongLocation(fFileInput
.getFilePointer()));
325 } catch (final FileNotFoundException e
) {
326 Activator
.logError("Error seeking event. File not found: " + getPath(), e
); //$NON-NLS-1$
328 } catch (final IOException e
) {
329 Activator
.logError("Error seeking event. File: " + getPath(), e
); //$NON-NLS-1$
335 public ITmfContext
seekEvent(double ratio
) {
337 return new TmfContext(NULL_LOCATION
, ITmfContext
.UNKNOWN_RANK
);
340 long pos
= Math
.round(ratio
* fFile
.length()) - fDataOffset
;
342 fFileInput
.seek(pos
- 1);
343 if (fFileInput
.read() == '\n') {
348 final ITmfLocation location
= new TmfLongLocation(pos
);
349 final ITmfContext context
= seekEvent(location
);
350 context
.setRank(ITmfContext
.UNKNOWN_RANK
);
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
);
359 public ITmfEvent
parseEvent(ITmfContext tmfContext
) {
360 if (fFile
== null || (!(tmfContext
instanceof TmfContext
))) {
364 final TmfContext context
= (TmfContext
) tmfContext
;
365 if (context
.getLocation() == null
366 || !(context
.getLocation().getLocationInfo() instanceof Long
)
367 || NULL_LOCATION
.equals(context
.getLocation())) {
371 return parseLine(context
);
376 * Parse a line with a context
379 * the context, has a location
380 * @return the event from a given line
382 private ITmfEvent
parseLine(TmfContext context
) {
384 if (!context
.getLocation().getLocationInfo().equals(fFileInput
.getFilePointer())) {
385 seekEvent(context
.getLocation());
387 } catch (IOException e1
) {
388 seekEvent(context
.getLocation());
392 line
= fFileInput
.readLine();
393 return parseLine(context
.getRank(), line
);
395 } catch (IOException e
) {
402 * Parse a line of text and make an event using it.
405 * the rank of the event
407 * the raw string of the event
410 private ITmfEvent
parseLine(long rank
, String line
) {
414 String
[] tokens
= line
.split(",", MAX_FIELDS
); //$NON-NLS-1$
415 if (tokens
.length
< MAX_FIELDS
) {
419 long timestamp
= Long
.parseLong(tokens
[i
++]);
420 String source
= tokens
[i
++];
421 long sourceInstance
= -1;
423 sourceInstance
= Long
.parseLong(tokens
[i
++]);
424 } catch (NumberFormatException e
) {
425 // this field can be empty
427 BtfEventType type
= BtfEventTypeFactory
.parse(tokens
[i
++]);
428 String target
= tokens
[i
++];
429 long targetInstance
= -1;
431 targetInstance
= Long
.parseLong(tokens
[i
++]);
432 } catch (NumberFormatException e
) {
433 // this field can be empty
435 String event
= tokens
[i
++];
437 ITmfEventField content
= type
.generateContent(event
, sourceInstance
, targetInstance
);
439 return new BtfEvent(this, rank
,
440 getTimestampTransform().transform(fTsFormat
.createTimestamp(timestamp
+ fTsOffset
)),
443 type
.getDescription(),
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
);
456 fCheckpointSize
= b
.position();
460 return fCheckpointSize
;
464 public ITmfLocation
restoreLocation(ByteBuffer bufferIn
) {
465 return new TmfLongLocation(bufferIn
);
469 protected ITmfTraceIndexer
createIndexer(int interval
) {
470 return new TmfBTreeTraceIndexer(this, interval
);
474 public Map
<String
, String
> getTraceProperties() {
475 return ImmutableMap
.copyOf(fProperties
);
479 public Iterable
<ITmfEventAspect
> getEventAspects() {
480 return BtfEventAspects
.getAspects();
484 public synchronized void dispose() {
485 RandomAccessFile raf
= fFileInput
;
489 } catch (IOException e
) {
497 public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal
) {
498 if (signal
.getTrace() == this) {
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.
506 } catch (TmfTraceException e
) {
507 Activator
.logError(e
.getLocalizedMessage(), e
);
510 super.traceRangeUpdated(signal
);