Index: src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java (revision 1711291) +++ src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java (working copy) @@ -239,6 +239,15 @@ } }); + JMenuItem menuPCM = new JMenuItem("Persisted Compaction Maps"); + menuPCM.setMnemonic(KeyEvent.VK_P); + menuPCM.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ev) { + treePanel.printPCMInfo(); + } + }); + menuBar.add(menuReopen); menuBar.add(new JSeparator(JSeparator.VERTICAL)); menuBar.add(menuCompaction); @@ -249,6 +258,8 @@ menuBar.add(new JSeparator(JSeparator.VERTICAL)); menuBar.add(menuDiff); menuBar.add(new JSeparator(JSeparator.VERTICAL)); + menuBar.add(menuPCM); + menuBar.add(new JSeparator(JSeparator.VERTICAL)); frame.setJMenuBar(menuBar); frame.pack(); Index: src/main/java/org/apache/jackrabbit/oak/explorer/NodeStoreTree.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/explorer/NodeStoreTree.java (revision 1711291) +++ src/main/java/org/apache/jackrabbit/oak/explorer/NodeStoreTree.java (working copy) @@ -39,6 +39,7 @@ import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.newline; import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.printGcRoots; import static org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStateHelper.getTemplateId; +import static org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStateHelper.readPCMHistory; import java.awt.GridLayout; import java.io.Closeable; @@ -64,6 +65,7 @@ import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.plugins.segment.RecordId; +import org.apache.jackrabbit.oak.plugins.segment.RecordUsageAnalyser; import org.apache.jackrabbit.oak.plugins.segment.SegmentBlob; import org.apache.jackrabbit.oak.plugins.segment.SegmentId; import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeState; @@ -647,6 +649,37 @@ return true; } + public void printPCMInfo() { + Map>> pcms = readPCMHistory(store); + if (pcms.isEmpty()) { + setText("No persisted compaction map found."); + return; + } + RecordUsageAnalyser rua = new RecordUsageAnalyser(); + + StringBuilder sb = new StringBuilder(); + sb.append("Persisted compaction map info"); + sb.append(newline); + for(Entry>> pcm: pcms.entrySet()){ + sb.append(pcm.getKey()); + sb.append(" = "); + sb.append(pcm.getValue()); + sb.append(newline); + for(Entry e: pcm.getValue()){ + try { + rua.analyseNode(e.getKey()); + rua.analyseNode(e.getValue()); + } catch (IllegalStateException ex) { + ex.printStackTrace(); + } + } + } + sb.append(newline); + sb.append(rua.toString()); + sb.append(newline); + setText(sb.toString()); + } + private static class NamePathModel implements Comparable { private final FileStore store; Index: src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStateHelper.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStateHelper.java (revision 1711291) +++ src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStateHelper.java (working copy) @@ -18,6 +18,18 @@ */ package org.apache.jackrabbit.oak.plugins.segment; +import static com.google.common.collect.Maps.newHashMap; +import static com.google.common.collect.Sets.newHashSet; +import static org.apache.jackrabbit.oak.plugins.segment.PersistedCompactionMap.PERSISTED_COMPACTION_MAP; + +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; + +import org.apache.jackrabbit.oak.plugins.segment.file.FileStore; + public class SegmentNodeStateHelper { private SegmentNodeStateHelper() { @@ -28,4 +40,84 @@ return s.getTemplateId(); } + public static String readString(RecordId r) { + return Segment.readString(r); + } + + /** + * Extracts persisted compaction map information, if available, otherwise + * returs null + */ + public static Entry parsePCMInfo(String mapInfo, + FileStore store) { + if (mapInfo == null || !mapInfo.startsWith(PERSISTED_COMPACTION_MAP)) { + return null; + } + SegmentTracker tracker = store.getTracker(); + int idStartIndex = mapInfo.indexOf("id=") + 3; + int idEndIndex = mapInfo.indexOf(",", idStartIndex); + String id = mapInfo.substring(idStartIndex, idEndIndex); + RecordId rid = null; + try { + rid = RecordId.fromString(tracker, id); + } catch (IllegalArgumentException iae) { + // log a warn? + } + + int baseStartIndex = mapInfo.indexOf("baseId=") + 7; + String base = mapInfo.substring(baseStartIndex, mapInfo.length() - 1); + RecordId bid = null; + if (!"null".equals(base)) { + try { + bid = RecordId.fromString(tracker, base); + } catch (IllegalArgumentException iae) { + // log a warn? + } + } + return new SimpleImmutableEntry(rid, bid); + } + + public static Map>> readPCMHistory( + FileStore store) { + Map>> pcms = newHashMap(); + Map> index = store.getTarReaderIndex(); + for (String path : index.keySet()) { + Set segments = index.get(path); + + for (UUID id : segments) { + Segment s = store.readSegment(new SegmentId(store.getTracker(), + id.getMostSignificantBits(), id + .getLeastSignificantBits())); + if (s.getSegmentId().isBulkSegmentId()) { + continue; + } + + for (int r = 0; r < s.getRootCount(); r++) { + RecordType t = s.getRootType(r); + if (t == RecordType.VALUE) { + // ?? s.readRecordId(s.getRootOffset(r)); + RecordId nodeId = new RecordId(s.getSegmentId(), + s.getRootOffset(r)); + String v = readString(nodeId); + Entry pcm = parsePCMInfo(v, store); + + if (pcm != null) { + Set> pcmsByFile = pcms + .get(path); + if (pcmsByFile == null) { + pcmsByFile = newHashSet(); + pcms.put(path, pcmsByFile); + } + pcmsByFile.add(pcm); + pcmsByFile + .add(new SimpleImmutableEntry( + nodeId, null)); + } + } + } + } + } + return pcms; + } + }