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