1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.mavibot.btree;
21
22
23 import java.io.BufferedReader;
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.InputStreamReader;
27 import java.io.RandomAccessFile;
28 import java.nio.BufferUnderflowException;
29 import java.nio.ByteBuffer;
30 import java.nio.channels.FileChannel;
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37
38 import org.apache.directory.mavibot.btree.exception.InvalidBTreeException;
39 import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
40 import org.apache.directory.mavibot.btree.serializer.LongSerializer;
41 import org.apache.directory.mavibot.btree.serializer.StringSerializer;
42 import org.apache.directory.mavibot.btree.util.Strings;
43
44
45
46
47
48
49
50 public class MavibotInspector
51 {
52
53 private File dbFile;
54
55
56 private static RecordManager rm;
57
58 private BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
59
60
61 private static final String GLOBAL_PAGES_NAME = "__global__";
62 private static final String FREE_PAGES_NAME = "__free-pages__";
63
64
65 private static Set<String> knownPagesArrays = new HashSet<String>();
66
67
68
69
70
71 private static Map<String, int[]> checkedPages = new HashMap<String, int[]>();
72
73 static
74 {
75 knownPagesArrays.add( GLOBAL_PAGES_NAME );
76 knownPagesArrays.add( FREE_PAGES_NAME );
77 knownPagesArrays.add( RecordManager.BTREE_OF_BTREES_NAME );
78 knownPagesArrays.add( RecordManager.COPIED_PAGE_BTREE_NAME );
79 }
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 public MavibotInspector( File dbFile )
98 {
99 this.dbFile = dbFile;
100 }
101
102
103
104
105
106 private boolean checkFilePresence()
107 {
108 if ( dbFile == null )
109 {
110 System.out.println( "No mavibot database file was given" );
111 return false;
112 }
113
114 if ( !dbFile.exists() )
115 {
116 System.out.println( "Given mavibot database file " + dbFile + " doesn't exist" );
117 return false;
118 }
119
120 return true;
121 }
122
123
124
125
126
127 public void printFileSize() throws IOException
128 {
129 FileChannel fileChannel = new RandomAccessFile( dbFile, "r" ).getChannel();
130
131 long l = fileChannel.size();
132
133 fileChannel.close();
134
135 String msg;
136
137 if ( l < 1024 )
138 {
139 msg = l + " bytes";
140 }
141 else
142 {
143 msg = ( l / 1024 ) + " KB";
144 }
145
146 System.out.println( msg );
147
148 fileChannel.close();
149 }
150
151
152
153
154
155 public void printNumberOfBTrees()
156 {
157 int nbBtrees = 0;
158
159 if ( rm != null )
160 {
161 nbBtrees = rm.getNbManagedTrees();
162 }
163
164
165 System.out.println( "Total Number of BTrees: " + nbBtrees );
166 }
167
168
169
170
171
172 public void printBTreeNames()
173 {
174 if ( rm == null )
175 {
176 System.out.println( "Couldn't find the number of managed btrees" );
177 return;
178 }
179
180 Set<String> trees = rm.getManagedTrees();
181 System.out.println( "\nManaged BTrees:" );
182
183 for ( String tree : trees )
184 {
185 System.out.println( tree );
186 }
187
188 System.out.println();
189 }
190
191
192
193
194
195 public void inspectBTree()
196 {
197 if ( rm == null )
198 {
199 System.out.println( "Cannot check BTree(s)" );
200 return;
201 }
202
203 System.out.print( "BTree Name: " );
204 String name = readLine();
205
206 PersistedBTree<?, ?> pb = ( PersistedBTree<?, ?> ) rm.getManagedTree( name );
207
208 if ( pb == null )
209 {
210 System.out.println( "No BTree exists with the name '" + name + "'" );
211 return;
212 }
213
214 System.out.println( "\nBTree offset: " + String.format( "0x%1$08x", pb.getBtreeOffset() ) );
215 System.out.println( "BTree _info_ offset: " + String.format( "0x%1$08x", pb.getBtreeInfoOffset() ) );
216 System.out.println( "BTree root page offset: " + String.format( "0x%1$08x", pb.getRootPageOffset() ) );
217 System.out.println( "Number of elements present: " + pb.getNbElems() );
218 System.out.println( "BTree Page size: " + pb.getPageSize() );
219 System.out.println( "BTree revision: " + pb.getRevision() );
220 System.out.println( "Key serializer: " + pb.getKeySerializerFQCN() );
221 System.out.println( "Value serializer: " + pb.getValueSerializerFQCN() );
222 System.out.println();
223 }
224
225
226
227
228
229 private boolean loadRm()
230 {
231 try
232 {
233 if ( rm != null )
234 {
235 System.out.println( "Closing record manager" );
236 rm.close();
237 }
238
239 rm = new RecordManager( dbFile.getAbsolutePath() );
240 System.out.println( "Loaded record manager" );
241 }
242 catch ( Exception e )
243 {
244 System.out.println( "Given database file seems to be corrupted. " + e.getMessage() );
245 return false;
246 }
247
248 return true;
249 }
250
251
252
253
254
255 static void check( RecordManager recordManager )
256 {
257 try
258 {
259 rm = recordManager;
260
261
262 ByteBuffer recordManagerHeader = ByteBuffer.allocate( RecordManager.RECORD_MANAGER_HEADER_SIZE );
263 long fileSize = recordManager.fileChannel.size();
264
265 if ( fileSize < RecordManager.RECORD_MANAGER_HEADER_SIZE )
266 {
267 throw new InvalidBTreeException( "File size too small : " + fileSize );
268 }
269
270
271 recordManager.fileChannel.read( recordManagerHeader, 0L );
272 recordManagerHeader.flip();
273
274
275 int pageSize = recordManagerHeader.getInt();
276
277 if ( ( pageSize != recordManager.pageSize ) || ( pageSize < 32 )
278 || ( ( pageSize & ( ~pageSize + 1 ) ) != pageSize ) )
279 {
280 throw new InvalidBTreeException( "Wrong page size : " + pageSize );
281 }
282
283
284 long nbPages = ( fileSize - RecordManager.RECORD_MANAGER_HEADER_SIZE ) / pageSize;
285
286
287 int nbBtrees = recordManagerHeader.getInt();
288
289 if ( ( nbBtrees < 0 ) || ( nbBtrees != recordManager.nbBtree ) )
290 {
291 throw new InvalidBTreeException( "Wrong nb trees : " + nbBtrees );
292 }
293
294
295
296 long firstFreePage = recordManagerHeader.getLong();
297
298 if ( firstFreePage != RecordManager.NO_PAGE )
299 {
300 checkOffset( recordManager, firstFreePage );
301 }
302
303 int nbPageBits = ( int ) ( nbPages / 32 );
304
305
306 checkedPages.put( GLOBAL_PAGES_NAME, new int[nbPageBits + 1] );
307
308
309 checkedPages.put( FREE_PAGES_NAME, new int[nbPageBits + 1] );
310
311
312 checkedPages.put( RecordManager.BTREE_OF_BTREES_NAME, new int[nbPageBits + 1] );
313
314
315 checkedPages.put( RecordManager.COPIED_PAGE_BTREE_NAME, new int[nbPageBits + 1] );
316
317
318 checkFreePages( recordManager, checkedPages );
319
320
321 long currentBtreeOfBtreesOffset = recordManagerHeader.getLong();
322 long previousBtreeOfBtreesOffset = recordManagerHeader.getLong();
323 long currentCopiedPagesBtreeOffset = recordManagerHeader.getLong();
324 long previousCopiedPagesBtreeOffset = recordManagerHeader.getLong();
325
326
327 if ( previousBtreeOfBtreesOffset != RecordManager.NO_PAGE )
328 {
329 System.out.println( "The previous Btree of Btrees offset is not valid : "
330 + previousBtreeOfBtreesOffset );
331 return;
332 }
333
334
335 if ( previousCopiedPagesBtreeOffset != RecordManager.NO_PAGE )
336 {
337 System.out.println( "The previous Copied Pages Btree offset is not valid : "
338 + previousCopiedPagesBtreeOffset );
339 return;
340 }
341
342
343 checkOffset( recordManager, currentBtreeOfBtreesOffset );
344
345
346 checkOffset( recordManager, currentCopiedPagesBtreeOffset );
347
348
349 checkBtreeOfBtrees( recordManager, checkedPages );
350
351
352 checkBtree( recordManager, currentCopiedPagesBtreeOffset, checkedPages );
353
354
355 dumpCheckedPages( recordManager, checkedPages );
356 }
357 catch ( Exception e )
358 {
359
360
361 e.printStackTrace();
362 throw new InvalidBTreeException( "Error : " + e.getMessage() );
363 }
364 }
365
366
367
368
369
370 private static <K, V> void checkBtreeOfBtrees( RecordManager recordManager, Map<String, int[]> checkedPages )
371 throws Exception
372 {
373
374 PageIO[] bobHeaderPageIos = recordManager
375 .readPageIOs( recordManager.currentBtreeOfBtreesOffset, Long.MAX_VALUE );
376
377
378 updateCheckedPages( checkedPages.get( RecordManager.BTREE_OF_BTREES_NAME ), recordManager.pageSize,
379 bobHeaderPageIos );
380 updateCheckedPages( checkedPages.get( GLOBAL_PAGES_NAME ), recordManager.pageSize, bobHeaderPageIos );
381
382 long dataPos = 0L;
383
384
385 recordManager.readLong( bobHeaderPageIos, dataPos );
386 dataPos += RecordManager.LONG_SIZE;
387
388
389 recordManager.readLong( bobHeaderPageIos, dataPos );
390 dataPos += RecordManager.LONG_SIZE;
391
392
393 long rootPageOffset = recordManager.readLong( bobHeaderPageIos, dataPos );
394
395 checkOffset( recordManager, rootPageOffset );
396
397 dataPos += RecordManager.LONG_SIZE;
398
399
400 long btreeInfoOffset = recordManager.readLong( bobHeaderPageIos, dataPos );
401
402 checkOffset( recordManager, btreeInfoOffset );
403
404 checkBtreeInfo( recordManager, checkedPages, btreeInfoOffset, -1L );
405
406
407
408 checkBtreeOfBtreesPage( recordManager, checkedPages, rootPageOffset );
409 }
410
411
412
413
414
415 private static <K, V> void checkBtree( RecordManager recordManager, long btreeOffset,
416 Map<String, int[]> checkedPages ) throws Exception
417 {
418
419 PageIO[] btreeHeaderPageIos = recordManager.readPageIOs( btreeOffset, Long.MAX_VALUE );
420
421 long dataPos = 0L;
422
423
424 long btreeRevision = recordManager.readLong( btreeHeaderPageIos, dataPos );
425 dataPos += RecordManager.LONG_SIZE;
426
427
428 recordManager.readLong( btreeHeaderPageIos, dataPos );
429 dataPos += RecordManager.LONG_SIZE;
430
431
432 long rootPageOffset = recordManager.readLong( btreeHeaderPageIos, dataPos );
433
434 checkOffset( recordManager, rootPageOffset );
435
436 dataPos += RecordManager.LONG_SIZE;
437
438
439 long btreeInfoOffset = recordManager.readLong( btreeHeaderPageIos, dataPos );
440
441 checkOffset( recordManager, btreeInfoOffset );
442
443 BtreeInfo<K, V> btreeInfo = checkBtreeInfo( recordManager, checkedPages, btreeInfoOffset, btreeRevision );
444
445
446 updateCheckedPages( checkedPages.get( btreeInfo.btreeName ), recordManager.pageSize, btreeHeaderPageIos );
447 updateCheckedPages( checkedPages.get( GLOBAL_PAGES_NAME ), recordManager.pageSize, btreeHeaderPageIos );
448
449
450 checkBtreePage( recordManager, btreeInfo, checkedPages, rootPageOffset );
451 }
452
453
454
455
456
457 private static <K, V> void checkBtreePage( RecordManager recordManager, BtreeInfo<K, V> btreeInfo,
458 Map<String, int[]> checkedPages, long pageOffset ) throws Exception
459 {
460 PageIO[] pageIos = recordManager.readPageIOs( pageOffset, Long.MAX_VALUE );
461
462
463 updateCheckedPages( checkedPages.get( btreeInfo.btreeName ), recordManager.pageSize, pageIos );
464 updateCheckedPages( checkedPages.get( GLOBAL_PAGES_NAME ), recordManager.pageSize, pageIos );
465
466
467 long position = 0L;
468
469
470 long revision = recordManager.readLong( pageIos, position );
471 position += RecordManager.LONG_SIZE;
472
473
474 int nbElems = recordManager.readInt( pageIos, position );
475 position += RecordManager.INT_SIZE;
476
477
478
479
480
481 ByteBuffer byteBuffer = recordManager.readBytes( pageIos, position );
482
483
484
485
486 if ( nbElems >= 0 )
487 {
488
489 checkBtreeLeaf( recordManager, btreeInfo, checkedPages, nbElems, revision, byteBuffer, pageIos );
490 }
491 else
492 {
493
494 long[] children = checkBtreeNode( recordManager, btreeInfo, checkedPages, -nbElems, revision, byteBuffer,
495 pageIos );
496
497 for ( int pos = 0; pos <= -nbElems; pos++ )
498 {
499
500 checkBtreePage( recordManager, btreeInfo, checkedPages, children[pos] );
501 }
502 }
503 }
504
505
506
507
508
509
510 private static <K, V> BtreeInfo<K, V> checkBtreeInfo( RecordManager recordManager, Map<String, int[]> checkedPages,
511 long btreeInfoOffset, long btreeRevision ) throws IOException
512 {
513 BtreeInfo<K, V> btreeInfo = new BtreeInfo<K, V>();
514
515 PageIO[] btreeInfoPagesIos = recordManager.readPageIOs( btreeInfoOffset, Long.MAX_VALUE );
516
517 long dataPos = 0L;
518
519
520 recordManager.readInt( btreeInfoPagesIos, dataPos );
521 dataPos += RecordManager.INT_SIZE;
522
523
524 ByteBuffer btreeNameBytes = recordManager.readBytes( btreeInfoPagesIos, dataPos );
525 dataPos += RecordManager.INT_SIZE + btreeNameBytes.limit();
526 String btreeName = Strings.utf8ToString( btreeNameBytes );
527
528
529 ByteBuffer keySerializerBytes = recordManager.readBytes( btreeInfoPagesIos, dataPos );
530
531 if ( keySerializerBytes != null )
532 {
533 String keySerializerFqcn = Strings.utf8ToString( keySerializerBytes );
534
535 btreeInfo.keySerializer = getSerializer( keySerializerFqcn );
536 }
537
538 dataPos += RecordManager.INT_SIZE + keySerializerBytes.limit();
539
540
541 ByteBuffer valueSerializerBytes = recordManager.readBytes( btreeInfoPagesIos, dataPos );
542
543 if ( valueSerializerBytes != null )
544 {
545 String valueSerializerFqcn = Strings.utf8ToString( valueSerializerBytes );
546
547 btreeInfo.valueSerializer = getSerializer( valueSerializerFqcn );
548 }
549
550 dataPos += RecordManager.INT_SIZE + valueSerializerBytes.limit();
551
552
553 recordManager.readInt( btreeInfoPagesIos, dataPos );
554 dataPos += RecordManager.INT_SIZE;
555
556
557 if ( !RecordManager.COPIED_PAGE_BTREE_NAME.equals( btreeName )
558 && !RecordManager.BTREE_OF_BTREES_NAME.equals( btreeName ) )
559 {
560
561 }
562
563 btreeInfo.btreeName = btreeName;
564
565
566 int[] checkedPagesArray = checkedPages.get( btreeName );
567
568 if ( checkedPagesArray == null )
569 {
570
571 checkedPagesArray = createPageArray( recordManager );
572 checkedPages.put( btreeName, checkedPagesArray );
573 }
574
575 updateCheckedPages( checkedPagesArray, recordManager.pageSize, btreeInfoPagesIos );
576 updateCheckedPages( checkedPages.get( GLOBAL_PAGES_NAME ), recordManager.pageSize, btreeInfoPagesIos );
577
578 return btreeInfo;
579 }
580
581
582
583
584
585 @SuppressWarnings("unchecked")
586 private static <T> ElementSerializer<T> getSerializer( String serializerFqcn )
587 {
588 try
589 {
590 Class<?> serializerClass = Class.forName( serializerFqcn );
591 ElementSerializer<T> serializer = null;
592
593 try
594 {
595 serializer = ( ElementSerializer<T> ) serializerClass.getDeclaredField( "INSTANCE" ).get( null );
596 }
597 catch ( NoSuchFieldException e )
598 {
599
600 }
601
602 if ( serializer == null )
603 {
604 serializer = ( ElementSerializer<T> ) serializerClass.newInstance();
605 }
606
607 return serializer;
608 }
609 catch ( Exception e )
610 {
611 throw new InvalidBTreeException( "Error : " + e.getMessage() );
612 }
613 }
614
615
616
617
618
619 private static <K, V> void checkBtreeOfBtreesPage( RecordManager recordManager, Map<String, int[]> checkedPages,
620 long pageOffset ) throws Exception
621 {
622 PageIO[] pageIos = recordManager.readPageIOs( pageOffset, Long.MAX_VALUE );
623
624
625 updateCheckedPages( checkedPages.get( RecordManager.BTREE_OF_BTREES_NAME ), recordManager.pageSize, pageIos );
626 updateCheckedPages( checkedPages.get( GLOBAL_PAGES_NAME ), recordManager.pageSize, pageIos );
627
628
629 long position = 0L;
630
631
632 long revision = recordManager.readLong( pageIos, position );
633 position += RecordManager.LONG_SIZE;
634
635
636 int nbElems = recordManager.readInt( pageIos, position );
637 position += RecordManager.INT_SIZE;
638
639
640
641
642
643 ByteBuffer byteBuffer = recordManager.readBytes( pageIos, position );
644
645
646
647
648 if ( nbElems >= 0 )
649 {
650
651 checkBtreeOfBtreesLeaf( recordManager, checkedPages, nbElems, revision, byteBuffer, pageIos );
652 }
653 else
654 {
655
656 long[] children = checkBtreeOfBtreesNode( recordManager, checkedPages, -nbElems, revision, byteBuffer,
657 pageIos );
658
659 for ( int pos = 0; pos <= -nbElems; pos++ )
660 {
661
662 checkBtreeOfBtreesPage( recordManager, checkedPages, children[pos] );
663 }
664 }
665 }
666
667
668
669
670
671 private static <K, V> void checkBtreeOfBtreesLeaf( RecordManager recordManager, Map<String, int[]> checkedPages,
672 int nbElems, long revision, ByteBuffer byteBuffer, PageIO[] pageIos ) throws Exception
673 {
674
675 for ( int i = 0; i < nbElems; i++ )
676 {
677 try
678 {
679
680 int nbValues = byteBuffer.getInt();
681
682 if ( nbValues != 1 )
683 {
684 throw new InvalidBTreeException( "We should have only one value for a BOB " + nbValues );
685 }
686
687
688
689 int valueLength = byteBuffer.getInt();
690
691 if ( valueLength != RecordManager.LONG_SIZE + RecordManager.INT_SIZE )
692 {
693 throw new InvalidBTreeException( "The BOB value length is invalid " + valueLength );
694 }
695
696
697 int offsetLength = byteBuffer.getInt();
698
699 if ( offsetLength != RecordManager.LONG_SIZE )
700 {
701 throw new InvalidBTreeException( "The BOB value offset length is invalid " + offsetLength );
702 }
703
704
705 long btreeOffset = byteBuffer.getLong();
706
707 checkOffset( recordManager, btreeOffset );
708
709
710
711 int keyLength = byteBuffer.getInt();
712
713
714 if ( keyLength < RecordManager.LONG_SIZE + RecordManager.INT_SIZE )
715 {
716 throw new InvalidBTreeException( "The BOB key length is invalid " + keyLength );
717 }
718
719
720 long btreeRevision = byteBuffer.getLong();
721
722
723 int btreeNameLength = byteBuffer.getInt();
724
725
726 if ( keyLength != RecordManager.LONG_SIZE + RecordManager.INT_SIZE + btreeNameLength )
727 {
728 throw new InvalidBTreeException( "The BOB key length is not the expected value " +
729 ( RecordManager.LONG_SIZE + RecordManager.INT_SIZE + btreeNameLength ) + ", expected "
730 + keyLength );
731 }
732
733 byte[] bytes = new byte[btreeNameLength];
734 byteBuffer.get( bytes );
735 String btreeName = Strings.utf8ToString( bytes );
736
737
738 int[] btreePagesArray = createPageArray( recordManager );
739 checkedPages.put( btreeName, btreePagesArray );
740
741
742 checkBtree( recordManager, btreeOffset, checkedPages );
743
744
745 }
746 catch ( BufferUnderflowException bue )
747 {
748 throw new InvalidBTreeException( "The BOB leaf byte buffer is too short : " + bue.getMessage() );
749 }
750 }
751 }
752
753
754
755
756
757 private static <K, V> void checkBtreeLeaf( RecordManager recordManager, BtreeInfo<K, V> btreeInfo,
758 Map<String, int[]> checkedPages, int nbElems, long revision, ByteBuffer byteBuffer, PageIO[] pageIos )
759 throws Exception
760 {
761
762 for ( int i = 0; i < nbElems; i++ )
763 {
764 try
765 {
766
767 int nbValues = byteBuffer.getInt();
768
769 if ( nbValues < 0 )
770 {
771
772 long subBtreeOffset = byteBuffer.getLong();
773
774
775 checkBtree( recordManager, subBtreeOffset, checkedPages );
776
777
778
779 byteBuffer.getInt();
780
781
782 btreeInfo.keySerializer.deserialize( byteBuffer );
783 }
784 else
785 {
786
787
788 byteBuffer.getInt();
789 btreeInfo.valueSerializer.deserialize( byteBuffer );
790
791
792 byteBuffer.getInt();
793
794 btreeInfo.keySerializer.deserialize( byteBuffer );
795 }
796 }
797 catch ( BufferUnderflowException bue )
798 {
799 throw new InvalidBTreeException( "The BOB leaf byte buffer is too short : " + bue.getMessage() );
800 }
801 }
802 }
803
804
805
806
807
808 private static <K, V> long[] checkBtreeOfBtreesNode( RecordManager recordManager, Map<String, int[]> checkedPages,
809 int nbElems, long revision,
810 ByteBuffer byteBuffer, PageIO[] pageIos ) throws IOException
811 {
812 long[] children = new long[nbElems + 1];
813
814
815 for ( int i = 0; i < nbElems; i++ )
816 {
817
818 long firstOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
819
820 checkOffset( recordManager, firstOffset );
821
822 long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
823
824 checkOffset( recordManager, lastOffset );
825
826 children[i] = firstOffset;
827
828
829 int keyLength = byteBuffer.getInt();
830
831
832 if ( keyLength < RecordManager.LONG_SIZE + RecordManager.INT_SIZE )
833 {
834 throw new InvalidBTreeException( "The BOB key length is invalid " + keyLength );
835 }
836
837
838 byteBuffer.getLong();
839
840
841 int btreeNameLength = byteBuffer.getInt();
842
843
844 if ( keyLength != RecordManager.LONG_SIZE + RecordManager.INT_SIZE + btreeNameLength )
845 {
846 throw new InvalidBTreeException( "The BOB key length is not the expected value " +
847 ( RecordManager.LONG_SIZE + RecordManager.INT_SIZE + btreeNameLength ) + ", expected " + keyLength );
848 }
849
850
851 byte[] bytes = new byte[btreeNameLength];
852 byteBuffer.get( bytes );
853 }
854
855
856
857 long firstOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
858
859 checkOffset( recordManager, firstOffset );
860
861 long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
862
863 checkOffset( recordManager, lastOffset );
864
865 children[nbElems] = firstOffset;
866
867
868 return children;
869 }
870
871
872
873
874
875 private static <K, V> long[] checkBtreeNode( RecordManager recordManager, BtreeInfo<K, V> btreeInfo,
876 Map<String, int[]> checkedPages, int nbElems, long revision, ByteBuffer byteBuffer, PageIO[] pageIos )
877 throws Exception
878 {
879 long[] children = new long[nbElems + 1];
880
881
882 for ( int i = 0; i < nbElems; i++ )
883 {
884 try
885 {
886
887 long firstOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
888
889 checkOffset( recordManager, firstOffset );
890
891 long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
892
893 checkOffset( recordManager, lastOffset );
894
895 children[i] = firstOffset;
896
897
898
899 byteBuffer.getInt();
900
901
902 btreeInfo.keySerializer.deserialize( byteBuffer );
903 }
904 catch ( BufferUnderflowException bue )
905 {
906 throw new InvalidBTreeException( "The BOB leaf byte buffer is too short : " + bue.getMessage() );
907 }
908 }
909
910
911
912 long firstOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
913
914 checkOffset( recordManager, firstOffset );
915
916 long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
917
918 checkOffset( recordManager, lastOffset );
919
920 children[nbElems] = firstOffset;
921
922 return children;
923 }
924
925
926
927
928
929 private static int[] createPageArray( RecordManager recordManager ) throws IOException
930 {
931 long fileSize = recordManager.fileChannel.size();
932 int pageSize = recordManager.pageSize;
933 long nbPages = ( fileSize - RecordManager.RECORD_MANAGER_HEADER_SIZE ) / pageSize;
934 int nbPageBits = ( int ) ( nbPages / 32 );
935
936 return new int[nbPageBits + 1];
937 }
938
939
940
941
942
943 private static void updateCheckedPages( int[] checkedPages, int pageSize, PageIO... pageIos )
944 {
945 for ( PageIO pageIO : pageIos )
946 {
947 long offset = pageIO.getOffset();
948
949 setCheckedPage( rm, checkedPages, offset );
950 }
951 }
952
953
954
955
956
957
958
959
960
961
962 private static void checkOffset( RecordManager recordManager, long offset ) throws IOException
963 {
964 if ( ( offset == RecordManager.NO_PAGE ) ||
965 ( ( ( offset - RecordManager.RECORD_MANAGER_HEADER_SIZE ) % recordManager.pageSize ) != 0 ) ||
966 ( offset > recordManager.fileChannel.size() ) )
967 {
968 throw new InvalidBTreeException( "Invalid Offset : " + offset );
969 }
970 }
971
972
973
974
975
976 private static void checkFreePages( RecordManager recordManager, Map<String, int[]> checkedPages )
977 throws IOException
978 {
979 if ( recordManager.firstFreePage == RecordManager.NO_PAGE )
980 {
981 return;
982 }
983
984
985 long currentOffset = recordManager.firstFreePage;
986 long fileSize = recordManager.fileChannel.size();
987
988 while ( currentOffset != RecordManager.NO_PAGE )
989 {
990 if ( currentOffset > fileSize )
991 {
992 System.out.println( "Wrong free page offset, above file size : " + currentOffset );
993 return;
994 }
995
996 try
997 {
998 PageIO pageIo = recordManager.fetchPage( currentOffset );
999
1000 if ( currentOffset != pageIo.getOffset() )
1001 {
1002 System.out.println( "PageIO offset is incorrect : " + currentOffset + "-"
1003 + pageIo.getOffset() );
1004 return;
1005 }
1006
1007 setCheckedPage( recordManager, checkedPages.get( GLOBAL_PAGES_NAME ), currentOffset );
1008 setCheckedPage( recordManager, checkedPages.get( FREE_PAGES_NAME ), currentOffset );
1009
1010 long newOffset = pageIo.getNextPage();
1011 currentOffset = newOffset;
1012 }
1013 catch ( IOException ioe )
1014 {
1015 throw new InvalidBTreeException( "Cannot fetch page at : " + currentOffset );
1016 }
1017 }
1018 }
1019
1020
1021
1022
1023
1024 private static void setCheckedPage( RecordManager recordManager, int[] checkedPages, long offset )
1025 {
1026 int pageNumber = ( int ) offset / recordManager.pageSize;
1027 int nbBitsPage = ( RecordManager.INT_SIZE << 3 );
1028 long pageMask = checkedPages[pageNumber / nbBitsPage];
1029 long mask = 1L << pageNumber % nbBitsPage;
1030
1031 if ( ( pageMask & mask ) != 0 )
1032 {
1033
1034 }
1035
1036 pageMask |= mask;
1037
1038 checkedPages[pageNumber / nbBitsPage] |= pageMask;
1039 }
1040
1041
1042
1043
1044
1045
1046 private static void dumpCheckedPages( RecordManager recordManager, Map<String, int[]> checkedPages )
1047 throws IOException
1048 {
1049
1050 int[] globalArray = checkedPages.get( GLOBAL_PAGES_NAME );
1051 String result = dumpPageArray( recordManager, globalArray );
1052
1053 String dump = String.format( "%1$-40s : %2$s", GLOBAL_PAGES_NAME, result );
1054 System.out.println( dump );
1055
1056
1057 int[] freePagesArray = checkedPages.get( FREE_PAGES_NAME );
1058 result = dumpPageArray( recordManager, freePagesArray );
1059
1060 dump = String.format( "%1$-40s : %2$s", FREE_PAGES_NAME, result );
1061 System.out.println( dump );
1062
1063
1064 int[] btreeOfBtreesArray = checkedPages.get( RecordManager.BTREE_OF_BTREES_NAME );
1065 result = dumpPageArray( recordManager, btreeOfBtreesArray );
1066
1067 dump = String.format( "%1$-40s : %2$s", RecordManager.BTREE_OF_BTREES_NAME, result );
1068 System.out.println( dump );
1069
1070
1071 int[] copiedPagesArray = checkedPages.get( RecordManager.COPIED_PAGE_BTREE_NAME );
1072 result = dumpPageArray( recordManager, copiedPagesArray );
1073
1074 dump = String.format( "%1$-40s : %2$s", RecordManager.COPIED_PAGE_BTREE_NAME, result );
1075 System.out.println( dump );
1076
1077
1078 for ( String btreeName : checkedPages.keySet() )
1079 {
1080
1081 if ( knownPagesArrays.contains( btreeName ) )
1082 {
1083 continue;
1084 }
1085
1086 int[] btreePagesArray = checkedPages.get( btreeName );
1087 result = dumpPageArray( recordManager, btreePagesArray );
1088
1089 dump = String.format( "%1$-40s : %2$s", btreeName, result );
1090 System.out.println( dump );
1091 }
1092 }
1093
1094
1095
1096
1097
1098 public static List<Long> getFreePages() throws IOException
1099 {
1100 return getPageOffsets( FREE_PAGES_NAME );
1101 }
1102
1103
1104
1105
1106
1107 public static List<Long> getGlobalPages() throws IOException
1108 {
1109 return getPageOffsets( GLOBAL_PAGES_NAME );
1110 }
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121 public static List<Long> getPageOffsets( String pageArrayName ) throws IOException
1122 {
1123 List<Long> lst = new ArrayList<Long>();
1124
1125 int[] fparry = checkedPages.get( pageArrayName );
1126
1127 long nbPagesChecked = 0;
1128 long fileSize = rm.fileChannel.size();
1129 long nbPages = ( fileSize - RecordManager.RECORD_MANAGER_HEADER_SIZE ) / rm.pageSize;
1130
1131 for ( int checkedPage : fparry )
1132 {
1133 for ( int j = 0; j < 32; j++ )
1134 {
1135
1136 if ( nbPagesChecked > nbPages + 1 )
1137 {
1138 break;
1139 }
1140 else
1141 {
1142 int mask = ( checkedPage & ( 1 << j ) );
1143 if ( mask != 0 )
1144 {
1145 lst.add( nbPagesChecked * rm.pageSize);
1146 }
1147 }
1148
1149 nbPagesChecked++;
1150 }
1151 }
1152
1153 return lst;
1154 }
1155
1156
1157
1158
1159
1160 private static String dumpPageArray( RecordManager recordManager, int[] checkedPages ) throws IOException
1161 {
1162 StringBuilder sb = new StringBuilder();
1163 int i = -1;
1164 int nbPagesChecked = 0;
1165 long fileSize = recordManager.fileChannel.size();
1166 long nbPages = ( fileSize - RecordManager.RECORD_MANAGER_HEADER_SIZE ) / recordManager.pageSize;
1167
1168 for ( int checkedPage : checkedPages )
1169 {
1170 if ( i == 0 )
1171 {
1172 sb.append( " " );
1173 i++;
1174 }
1175 else
1176 {
1177 i = 0;
1178 }
1179
1180 sb.append( "[" ).append( i ).append( "] " );
1181
1182 for ( int j = 0; j < 32; j++ )
1183 {
1184 if ( nbPagesChecked >= nbPages + 1 )
1185 {
1186 sb.append( "." );
1187 }
1188 else
1189 {
1190 if ( ( checkedPage & ( 1 << j ) ) == 0 )
1191 {
1192 sb.append( "0" );
1193 }
1194 else
1195 {
1196 sb.append( "1" );
1197 }
1198 }
1199
1200 nbPagesChecked++;
1201 }
1202 }
1203
1204 return sb.toString();
1205 }
1206
1207
1208
1209
1210
1211 public void start() throws Exception
1212 {
1213 if ( !checkFilePresence() )
1214 {
1215 return;
1216 }
1217
1218 if ( !loadRm() )
1219 {
1220 return;
1221 }
1222
1223 boolean stop = false;
1224
1225 while ( !stop )
1226 {
1227 System.out.println( "Choose an option:" );
1228 System.out.println( "n - Print Number of BTrees" );
1229 System.out.println( "b - Print BTree Names" );
1230 System.out.println( "i - Inspect BTree" );
1231 System.out.println( "c - Check Free Pages" );
1232 System.out.println( "s - Get database file size" );
1233 System.out.println( "d - Dump RecordManager" );
1234 System.out.println( "r - Reload RecordManager" );
1235 System.out.println( "o - Read page at offset" );
1236 System.out.println( "q - Quit" );
1237
1238 char c = readOption();
1239
1240 switch ( c )
1241 {
1242 case 'n':
1243 printNumberOfBTrees();
1244 break;
1245
1246 case 'b':
1247 printBTreeNames();
1248 break;
1249
1250 case 'i':
1251 inspectBTree();
1252 break;
1253
1254 case 'c':
1255 long fileSize = rm.fileChannel.size();
1256 long nbPages = fileSize / rm.pageSize;
1257 int nbPageBits = ( int ) ( nbPages / RecordManager.INT_SIZE );
1258
1259 Map<String, int[]> checkedPages = new HashMap<String, int[]>( 2 );
1260
1261
1262 checkedPages.put( GLOBAL_PAGES_NAME, new int[nbPageBits + 1] );
1263
1264
1265 checkedPages.put( FREE_PAGES_NAME, new int[nbPageBits + 1] );
1266
1267 checkFreePages( rm, checkedPages );
1268 break;
1269
1270 case 's':
1271 printFileSize();
1272 break;
1273
1274 case 'd':
1275 check( rm );
1276 break;
1277
1278 case 'r':
1279 loadRm();
1280 break;
1281
1282 case 'o':
1283 readPageAt();
1284 break;
1285 case 'q':
1286 stop = true;
1287 break;
1288
1289 default:
1290 System.out.println( "Invalid option" );
1291
1292 break;
1293 }
1294 }
1295
1296 try
1297 {
1298 rm.close();
1299 br.close();
1300 }
1301 catch ( Exception e )
1302 {
1303
1304 }
1305 }
1306
1307
1308
1309
1310
1311 private String readLine()
1312 {
1313 try
1314 {
1315 String line = br.readLine();
1316
1317 if ( line != null )
1318 {
1319 return line.trim();
1320 }
1321 else
1322 {
1323 return "";
1324 }
1325 }
1326 catch ( Exception e )
1327 {
1328 throw new RuntimeException( e );
1329 }
1330 }
1331
1332
1333
1334
1335
1336 private char readOption()
1337 {
1338 try
1339 {
1340 String s = br.readLine();
1341
1342 if ( ( s == null ) || ( s.length() == 0 ) )
1343 {
1344 return ' ';
1345 }
1346
1347 return s.charAt( 0 );
1348 }
1349 catch ( Exception e )
1350 {
1351 throw new RuntimeException( e );
1352 }
1353 }
1354
1355
1356 private void readPageAt() throws IOException
1357 {
1358 System.out.println();
1359 System.out.print( "Offset: " );
1360
1361 String s = readLine();
1362
1363 long offset = -1;
1364
1365 try
1366 {
1367 offset = Long.parseLong( s.trim() );
1368 }
1369 catch( Exception e )
1370 {
1371 offset = -1;
1372 }
1373
1374 if( offset < 0 || offset > (rm.fileChannel.size() - rm.DEFAULT_PAGE_SIZE) )
1375 {
1376 System.out.println( "Invalid offset " + s );
1377 return;
1378 }
1379
1380 PageIO io = rm.fetchPage( offset );
1381
1382 List<Long> ll = new ArrayList<Long>();
1383 ll.add( offset );
1384
1385 do
1386 {
1387
1388
1389
1390
1391 long next = io.getNextPage();
1392 ll.add( next );
1393 if ( next == -1 )
1394 {
1395 break;
1396 }
1397
1398 io = rm.fetchPage( next );
1399 }
1400 while( true );
1401
1402 int i = 0;
1403 for ( ; i < ll.size() - 2; i++ )
1404 {
1405 System.out.print( ll.get( i ) + " --> ");
1406 }
1407
1408 System.out.println( ll.get( i ) );
1409 }
1410
1411
1412
1413
1414
1415 public static void main( String[] args ) throws Exception
1416 {
1417
1418 if ( args.length == 0 )
1419 {
1420 System.out.println( "Usage java MavibotInspector <db-file-path>" );
1421 System.exit( 0 );
1422 }
1423
1424 File f = new File( args[0] );
1425
1426 MavibotInspector mi = new MavibotInspector( f );
1427 mi.start();
1428 }
1429 }
1430
1431
1432
1433
1434 final class BtreeInfo<K, V>
1435 {
1436
1437 String btreeName;
1438
1439
1440 ElementSerializer<K> keySerializer;
1441
1442
1443 ElementSerializer<V> valueSerializer;
1444
1445
1446 public String toString()
1447 {
1448 StringBuilder sb = new StringBuilder();
1449
1450 sb.append( "B-tree Info :" );
1451 sb.append( "\n name : " ).append( btreeName );
1452 sb.append( "\n key serializer : " ).append( keySerializer.getClass().getName() );
1453 sb.append( "\n value serializer : " ).append( valueSerializer.getClass().getName() );
1454
1455 return sb.toString();
1456 }
1457 }