tmf: Rework unneeded throw in the GSS backend
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / tmf / core / statesystem / backend / historytree / HistoryTree.java
CommitLineData
a52fde77
AM
1/*******************************************************************************
2 * Copyright (c) 2012 Ericsson
3 * Copyright (c) 2010, 2011 École Polytechnique de Montréal
4 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
5 *
6 * All rights reserved. This program and the accompanying materials are
7 * made available under the terms of the Eclipse Public License v1.0 which
8 * accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 *******************************************************************************/
12
13package org.eclipse.linuxtools.tmf.core.statesystem.backend.historytree;
14
15import java.io.File;
16import java.io.FileInputStream;
17import java.io.IOException;
18import java.io.PrintWriter;
19import java.nio.ByteBuffer;
20import java.nio.ByteOrder;
21import java.nio.channels.FileChannel;
22import java.util.Vector;
23
24import org.eclipse.linuxtools.tmf.core.statesystem.TimeRangeException;
25
26/**
27 * Meta-container for the History Tree. This structure contains all the
28 * high-level data relevant to the tree.
29 *
30 * @author alexmont
31 *
32 */
33class HistoryTree {
34
35 private static final int HISTORY_FILE_MAGIC_NUMBER = 0x05FFA900;
36
37 /**
38 * File format version. Increment minor on backwards-compatible changes.
39 * Increment major + set minor back to 0 when breaking compatibility.
40 */
41 private static final int MAJOR_VERSION = 3;
42 private static final byte MINOR_VERSION = 0;
43
44 /**
45 * Tree-specific configuration
46 */
47 /* Container for all the configuration constants */
48 protected final HTConfig config;
49
50 /* Reader/writer object */
51 private final HT_IO treeIO;
52
53 /**
54 * Variable Fields (will change throughout the existance of the SHT)
55 */
56 /* Latest timestamp found in the tree (at any given moment) */
57 private long treeEnd;
58
59 /* How many nodes exist in this tree, total */
60 private int nodeCount;
61
62 /* "Cache" to keep the active nodes in memory */
63 protected Vector<CoreNode> latestBranch;
64
65 /**
66 * Create a new State History from scratch, using a SHTConfig object for
67 * configuration
68 *
69 * @param conf
70 * @throws IOException
71 */
72 private HistoryTree(HTConfig conf) throws IOException {
73 /*
74 * Simple assertion to make sure we have enough place in the 0th block
75 * for the tree configuration
76 */
77 assert (conf.blockSize >= getTreeHeaderSize());
78
79 config = conf;
80 treeEnd = conf.treeStart;
81 nodeCount = 0;
82 latestBranch = new Vector<CoreNode>();
83
84 /* Prepare the IO object */
85 treeIO = new HT_IO(this, true);
86
87 /* Add the first node to the tree */
88 CoreNode firstNode = initNewCoreNode(-1, conf.treeStart);
89 latestBranch.add(firstNode);
90 }
91
92 /**
93 * "New State History" constructor, which doesn't use SHTConfig but the
94 * individual values separately. Kept for now for backwards compatibility,
95 * but you should definitely consider using SHTConfig instead (since its
96 * contents can then change without directly affecting SHT's API).
97 */
98 HistoryTree(File newStateFile, int blockSize, int maxChildren,
99 long startTime) throws IOException {
100 this(new HTConfig(newStateFile, blockSize, maxChildren, startTime));
101 }
102
103 /**
104 * "Reader" constructor : instantiate a SHTree from an existing tree file on
105 * disk
106 *
107 * @param existingFileName
108 * Path/filename of the history-file we are to open
109 * @throws IOException
110 */
111 HistoryTree(File existingStateFile) throws IOException {
112 /*
113 * Open the file ourselves, get the tree header information we need,
114 * then pass on the descriptor to the TreeIO object.
115 */
116 int rootNodeSeqNb, res;
117 int bs, maxc;
fb12b0c2 118 long startTime;
a52fde77
AM
119
120 /* Java I/O mumbo jumbo... */
fee997a5
AM
121 if (!existingStateFile.exists()) {
122 throw new IOException("Selected state file does not exist"); //$NON-NLS-1$
123 }
fb12b0c2
AM
124 if (existingStateFile.length() <= 0) {
125 throw new IOException("Invalid state file selected, " + //$NON-NLS-1$
126 "target file is empty"); //$NON-NLS-1$
a52fde77
AM
127 }
128
129 FileInputStream fis = new FileInputStream(existingStateFile);
130 ByteBuffer buffer = ByteBuffer.allocate(getTreeHeaderSize());
131 FileChannel fc = fis.getChannel();
132 buffer.order(ByteOrder.LITTLE_ENDIAN);
133 buffer.clear();
134 fc.read(buffer);
135 buffer.flip();
136
137 /*
138 * Check the magic number,to make sure we're opening the right type of
139 * file
140 */
141 res = buffer.getInt();
142 if (res != HISTORY_FILE_MAGIC_NUMBER) {
6f04204e
AM
143 fc.close();
144 fis.close();
fb12b0c2
AM
145 throw new IOException("Selected file does not" + //$NON-NLS-1$
146 "look like a History Tree file"); //$NON-NLS-1$
a52fde77
AM
147 }
148
149 res = buffer.getInt(); /* Major version number */
150 if (res != MAJOR_VERSION) {
6f04204e
AM
151 fc.close();
152 fis.close();
a52fde77
AM
153 throw new IOException("Select History Tree file is of an older " //$NON-NLS-1$
154 + "format. Please use a previous version of " //$NON-NLS-1$
155 + "the parser to open it."); //$NON-NLS-1$
156 }
157
158 res = buffer.getInt(); /* Minor version number */
159
160 bs = buffer.getInt(); /* Block Size */
161 maxc = buffer.getInt(); /* Max nb of children per node */
162
163 this.nodeCount = buffer.getInt();
164 rootNodeSeqNb = buffer.getInt();
fb12b0c2 165 startTime = buffer.getLong();
a52fde77 166
fb12b0c2 167 this.config = new HTConfig(existingStateFile, bs, maxc, startTime);
6f04204e 168 fc.close();
a52fde77
AM
169 fis.close();
170 /*
171 * FIXME We close fis here and the TreeIO will then reopen the same
172 * file, not extremely elegant. But how to pass the information here to
173 * the SHT otherwise?
174 */
175 this.treeIO = new HT_IO(this, false);
176
177 rebuildLatestBranch(rootNodeSeqNb);
a52fde77 178 this.treeEnd = latestBranch.firstElement().getNodeEnd();
fb12b0c2
AM
179
180 /*
181 * Make sure the history start time we read previously is consistent
182 * with was is actually in the root node.
183 */
184 if (startTime != latestBranch.firstElement().getNodeStart()) {
185 fc.close();
186 fis.close();
187 throw new IOException("Inconsistent start times in the" + //$NON-NLS-1$
188 "history file, it might be corrupted."); //$NON-NLS-1$
189 }
a52fde77
AM
190 }
191
192 /**
193 * "Save" the tree to disk. This method will cause the treeIO object to
194 * commit all nodes to disk and then return the RandomAccessFile descriptor
195 * so the Tree object can save its configuration into the header of the
196 * file.
197 *
198 * @param requestedEndTime
199 */
200 void closeTree() {
201 FileChannel fc;
202 ByteBuffer buffer;
203 int i, res;
204
205 /* Close off the latest branch of the tree */
206 for (i = 0; i < latestBranch.size(); i++) {
207 latestBranch.get(i).closeThisNode(treeEnd);
208 treeIO.writeNode(latestBranch.get(i));
209 }
210
211 /* Only use this for debugging purposes, it's VERY slow! */
212 // this.checkIntegrity();
213
214 fc = treeIO.getFcOut();
215 buffer = ByteBuffer.allocate(getTreeHeaderSize());
216 buffer.order(ByteOrder.LITTLE_ENDIAN);
217 buffer.clear();
218
219 /* Save the config of the tree to the header of the file */
220 try {
221 fc.position(0);
222
223 buffer.putInt(HISTORY_FILE_MAGIC_NUMBER);
224
225 buffer.putInt(MAJOR_VERSION);
226 buffer.putInt(MINOR_VERSION);
227
228 buffer.putInt(config.blockSize);
229 buffer.putInt(config.maxChildren);
230
231 buffer.putInt(nodeCount);
232
233 /* root node seq. nb */
234 buffer.putInt(latestBranch.firstElement().getSequenceNumber());
235
fb12b0c2
AM
236 /* start time of this history */
237 buffer.putLong(latestBranch.firstElement().getNodeStart());
238
a52fde77
AM
239 buffer.flip();
240 res = fc.write(buffer);
241 assert (res <= getTreeHeaderSize());
242 /* done writing the file header */
243
244 } catch (IOException e) {
6f04204e 245 /* We should not have any problems at this point... */
a52fde77 246 e.printStackTrace();
6f04204e
AM
247 } finally {
248 try {
249 fc.close();
250 } catch (IOException e) {
251 e.printStackTrace();
252 }
a52fde77
AM
253 }
254 return;
255 }
256
257 /**
258 * @name Accessors
259 */
ab604305 260
a52fde77
AM
261 long getTreeStart() {
262 return config.treeStart;
263 }
264
265 long getTreeEnd() {
266 return treeEnd;
267 }
268
269 int getNodeCount() {
270 return nodeCount;
271 }
272
273 HT_IO getTreeIO() {
274 return treeIO;
275 }
276
277 /**
278 * Rebuild the latestBranch "cache" object by reading the nodes from disk
279 * (When we are opening an existing file on disk and want to append to it,
280 * for example).
281 *
282 * @param rootNodeSeqNb
283 * The sequence number of the root node, so we know where to
284 * start
285 */
286 private void rebuildLatestBranch(int rootNodeSeqNb) {
287 HTNode nextChildNode;
288
289 this.latestBranch = new Vector<CoreNode>();
290
291 nextChildNode = treeIO.readNodeFromDisk(rootNodeSeqNb);
292 latestBranch.add((CoreNode) nextChildNode);
293 while (latestBranch.lastElement().getNbChildren() > 0) {
294 nextChildNode = treeIO.readNodeFromDisk(latestBranch.lastElement().getLatestChild());
295 latestBranch.add((CoreNode) nextChildNode);
296 }
297 }
298
299 /**
300 * Insert an interval in the tree
301 *
302 * @param interval
303 */
304 void insertInterval(HTInterval interval) throws TimeRangeException {
305 if (interval.getStartTime() < config.treeStart) {
306 throw new TimeRangeException();
307 }
308 tryInsertAtNode(interval, latestBranch.size() - 1);
309 }
310
311 /**
312 * Inner method to find in which node we should add the interval.
313 *
314 * @param interval
315 * The interval to add to the tree
316 * @param indexOfNode
317 * The index *in the latestBranch* where we are trying the
318 * insertion
319 */
320 private void tryInsertAtNode(HTInterval interval, int indexOfNode) {
321 HTNode targetNode = latestBranch.get(indexOfNode);
322
323 /* Verify if there is enough room in this node to store this interval */
324 if (interval.getIntervalSize() > targetNode.getNodeFreeSpace()) {
325 /* Nope, not enough room. Insert in a new sibling instead. */
326 addSiblingNode(indexOfNode);
327 tryInsertAtNode(interval, latestBranch.size() - 1);
328 return;
329 }
330
331 /* Make sure the interval time range fits this node */
332 if (interval.getStartTime() < targetNode.getNodeStart()) {
333 /*
334 * No, this interval starts before the startTime of this node. We
335 * need to check recursively in parents if it can fit.
336 */
337 assert (indexOfNode >= 1);
338 tryInsertAtNode(interval, indexOfNode - 1);
339 return;
340 }
341
342 /*
343 * Ok, there is room, and the interval fits in this time slot. Let's add
344 * it.
345 */
346 targetNode.addInterval(interval);
347
348 /* Update treeEnd if needed */
349 if (interval.getEndTime() > this.treeEnd) {
350 this.treeEnd = interval.getEndTime();
351 }
352 return;
353 }
354
355 /**
356 * Method to add a sibling to any node in the latest branch. This will add
357 * children back down to the leaf level, if needed.
358 *
359 * @param indexOfNode
360 * The index in latestBranch where we start adding
361 */
362 private void addSiblingNode(int indexOfNode) {
363 int i;
364 CoreNode newNode, prevNode;
365 long splitTime = treeEnd;
366
367 assert (indexOfNode < latestBranch.size());
368
369 /* Check if we need to add a new root node */
370 if (indexOfNode == 0) {
371 addNewRootNode();
372 return;
373 }
374
375 /* Check if we can indeed add a child to the target parent */
376 if (latestBranch.get(indexOfNode - 1).getNbChildren() == config.maxChildren) {
377 /* If not, add a branch starting one level higher instead */
378 addSiblingNode(indexOfNode - 1);
379 return;
380 }
381
382 /* Split off the new branch from the old one */
383 for (i = indexOfNode; i < latestBranch.size(); i++) {
384 latestBranch.get(i).closeThisNode(splitTime);
385 treeIO.writeNode(latestBranch.get(i));
386
387 prevNode = latestBranch.get(i - 1);
388 newNode = initNewCoreNode(prevNode.getSequenceNumber(),
389 splitTime + 1);
390 prevNode.linkNewChild(newNode);
391
392 latestBranch.set(i, newNode);
393 }
394 return;
395 }
396
397 /**
398 * Similar to the previous method, except here we rebuild a completely new
399 * latestBranch
400 */
401 private void addNewRootNode() {
402 int i, depth;
403 CoreNode oldRootNode, newRootNode, newNode, prevNode;
404 long splitTime = this.treeEnd;
405
406 oldRootNode = latestBranch.firstElement();
407 newRootNode = initNewCoreNode(-1, config.treeStart);
408
409 /* Tell the old root node that it isn't root anymore */
410 oldRootNode.setParentSequenceNumber(newRootNode.getSequenceNumber());
411
412 /* Close off the whole current latestBranch */
413 for (i = 0; i < latestBranch.size(); i++) {
414 latestBranch.get(i).closeThisNode(splitTime);
415 treeIO.writeNode(latestBranch.get(i));
416 }
417
418 /* Link the new root to its first child (the previous root node) */
419 newRootNode.linkNewChild(oldRootNode);
420
421 /* Rebuild a new latestBranch */
422 depth = latestBranch.size();
423 latestBranch = new Vector<CoreNode>();
424 latestBranch.add(newRootNode);
425 for (i = 1; i < depth + 1; i++) {
426 prevNode = latestBranch.get(i - 1);
427 newNode = initNewCoreNode(prevNode.getParentSequenceNumber(),
428 splitTime + 1);
429 prevNode.linkNewChild(newNode);
430 latestBranch.add(newNode);
431 }
432 }
433
434 /**
435 * Add a new empty node to the tree.
436 *
437 * @param parentSeqNumber
438 * Sequence number of this node's parent
439 * @param startTime
440 * Start time of the new node
441 * @return The newly created node
442 */
443 private CoreNode initNewCoreNode(int parentSeqNumber, long startTime) {
444 CoreNode newNode = new CoreNode(this, this.nodeCount, parentSeqNumber,
445 startTime);
446 this.nodeCount++;
447
448 /* Update the treeEnd if needed */
449 if (startTime >= this.treeEnd) {
450 this.treeEnd = startTime + 1;
451 }
452 return newNode;
453 }
454
455 /**
456 * Inner method to select the next child of the current node intersecting
457 * the given timestamp. Useful for moving down the tree following one
458 * branch.
459 *
460 * @param currentNode
461 * @param t
462 * @return The child node intersecting t
463 */
464 HTNode selectNextChild(CoreNode currentNode, long t) {
465 assert (currentNode.getNbChildren() > 0);
466 int potentialNextSeqNb = currentNode.getSequenceNumber();
467
468 for (int i = 0; i < currentNode.getNbChildren(); i++) {
469 if (t >= currentNode.getChildStart(i)) {
470 potentialNextSeqNb = currentNode.getChild(i);
471 } else {
472 break;
473 }
474 }
475 /*
476 * Once we exit this loop, we should have found a children to follow. If
477 * we didn't, there's a problem.
478 */
479 assert (potentialNextSeqNb != currentNode.getSequenceNumber());
480
481 /*
482 * Since this code path is quite performance-critical, avoid iterating
483 * through the whole latestBranch array if we know for sure the next
484 * node has to be on disk
485 */
486 if (currentNode.isDone()) {
487 return treeIO.readNodeFromDisk(potentialNextSeqNb);
488 }
489 return treeIO.readNode(potentialNextSeqNb);
490 }
491
492 /**
493 * Helper function to get the size of the "tree header" in the tree-file The
494 * nodes will use this offset to know where they should be in the file. This
495 * should always be a multiple of 4K.
496 */
497 static int getTreeHeaderSize() {
498 return 4096;
499 }
500
501 long getFileSize() {
502 return config.stateFile.length();
503 }
504
505 /**
506 * @name Test/debugging functions
507 */
508
509 /* Only used for debugging, shouldn't be externalized */
510 @SuppressWarnings("nls")
511 boolean checkNodeIntegrity(HTNode zenode) {
ab604305 512
a52fde77
AM
513 HTNode otherNode;
514 CoreNode node;
ab604305 515 StringBuffer buf = new StringBuffer();
a52fde77
AM
516 boolean ret = true;
517
518 // FIXME /* Only testing Core Nodes for now */
519 if (!(zenode instanceof CoreNode)) {
520 return true;
521 }
522
523 node = (CoreNode) zenode;
524
525 /*
526 * Test that this node's start and end times match the start of the
527 * first child and the end of the last child, respectively
528 */
529 if (node.getNbChildren() > 0) {
530 otherNode = treeIO.readNode(node.getChild(0));
531 if (node.getNodeStart() != otherNode.getNodeStart()) {
ab604305 532 buf.append("Start time of node (" + node.getNodeStart() + ") "
a52fde77
AM
533 + "does not match start time of first child " + "("
534 + otherNode.getNodeStart() + "), " + "node #"
ab604305 535 + otherNode.getSequenceNumber() + ")\n");
a52fde77
AM
536 ret = false;
537 }
538 if (node.isDone()) {
539 otherNode = treeIO.readNode(node.getLatestChild());
540 if (node.getNodeEnd() != otherNode.getNodeEnd()) {
ab604305 541 buf.append("End time of node (" + node.getNodeEnd()
a52fde77
AM
542 + ") does not match end time of last child ("
543 + otherNode.getNodeEnd() + ", node #"
ab604305 544 + otherNode.getSequenceNumber() + ")\n");
a52fde77
AM
545 ret = false;
546 }
547 }
548 }
549
550 /*
551 * Test that the childStartTimes[] array matches the real nodes' start
552 * times
553 */
554 for (int i = 0; i < node.getNbChildren(); i++) {
555 otherNode = treeIO.readNode(node.getChild(i));
556 if (otherNode.getNodeStart() != node.getChildStart(i)) {
ab604305 557 buf.append(" Expected start time of child node #"
a52fde77
AM
558 + node.getChild(i) + ": " + node.getChildStart(i)
559 + "\n" + " Actual start time of node #"
560 + otherNode.getSequenceNumber() + ": "
ab604305 561 + otherNode.getNodeStart() + "\n");
a52fde77
AM
562 ret = false;
563 }
564 }
565
566 if (!ret) {
567 System.out.println("");
568 System.out.println("SHT: Integrity check failed for node #"
569 + node.getSequenceNumber() + ":");
ab604305 570 System.out.println(buf.toString());
a52fde77
AM
571 }
572 return ret;
573 }
574
575 void checkIntegrity() {
576 for (int i = 0; i < nodeCount; i++) {
577 checkNodeIntegrity(treeIO.readNode(i));
578 }
579 }
580
581 /* Only used for debugging, shouldn't be externalized */
582 @SuppressWarnings("nls")
583 @Override
584 public String toString() {
585 return "Information on the current tree:\n\n" + "Blocksize: "
586 + config.blockSize + "\n" + "Max nb. of children per node: "
587 + config.maxChildren + "\n" + "Number of nodes: " + nodeCount
588 + "\n" + "Depth of the tree: " + latestBranch.size() + "\n"
589 + "Size of the treefile: " + this.getFileSize() + "\n"
590 + "Root node has sequence number: "
591 + latestBranch.firstElement().getSequenceNumber() + "\n"
592 + "'Latest leaf' has sequence number: "
593 + latestBranch.lastElement().getSequenceNumber();
594 }
595
596 private int curDepth;
597
598 /**
599 * Start at currentNode and print the contents of all its children, in
600 * pre-order. Give the root node in parameter to visit the whole tree, and
601 * have a nice overview.
602 */
603 @SuppressWarnings("nls")
604 private void preOrderPrint(PrintWriter writer, boolean printIntervals,
605 CoreNode currentNode) {
606 /* Only used for debugging, shouldn't be externalized */
607 int i, j;
608 HTNode nextNode;
609
610 writer.println(currentNode.toString());
611 if (printIntervals) {
612 currentNode.debugPrintIntervals(writer);
613 }
614 curDepth++;
615
616 for (i = 0; i < currentNode.getNbChildren(); i++) {
617 nextNode = treeIO.readNode(currentNode.getChild(i));
618 assert (nextNode instanceof CoreNode); // TODO temporary
619 for (j = 0; j < curDepth - 1; j++) {
620 writer.print(" ");
621 }
622 writer.print("+-");
623 preOrderPrint(writer, printIntervals, (CoreNode) nextNode);
624 }
625 curDepth--;
626 return;
627 }
628
629 /**
630 * Print out the full tree for debugging purposes
631 *
632 * @param writer
633 * PrintWriter in which to write the output
634 * @param printIntervals
635 * Says if you want to output the full interval information
636 */
637 void debugPrintFullTree(PrintWriter writer, boolean printIntervals) {
638 /* Only used for debugging, shouldn't be externalized */
639 curDepth = 0;
640 this.preOrderPrint(writer, false, latestBranch.firstElement());
641
642 if (printIntervals) {
643 writer.println("\nDetails of intervals:"); //$NON-NLS-1$
644 curDepth = 0;
645 this.preOrderPrint(writer, true, latestBranch.firstElement());
646 }
647 writer.println('\n');
648 }
649
650}
This page took 0.052301 seconds and 5 git commands to generate.