package edu.brown.catalog.conflicts; import java.io.File; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import org.voltdb.CatalogContext; import org.voltdb.catalog.CatalogType; import org.voltdb.catalog.ConflictPair; import org.voltdb.catalog.Procedure; import org.voltdb.catalog.Statement; import org.voltdb.catalog.Table; import org.voltdb.utils.Pair; import edu.brown.catalog.CatalogUtil; import edu.brown.graphs.AbstractDirectedGraph; import edu.brown.graphs.AbstractEdge; import edu.brown.graphs.AbstractVertex; import edu.brown.graphs.GraphUtil; import edu.brown.graphs.GraphvizExport; import edu.brown.utils.ArgumentsParser; import edu.brown.utils.FileUtil; /** * Dump out all conflicts * @author pavlo */ public abstract class ConflictSetTableDumper { private static final Logger LOG = Logger.getLogger(ConflictSetTableDumper.class); private static class ProcedureTable extends Procedure { private final Pair<Procedure, Table> pair; private ProcedureTable(Procedure proc, Table table) { assert(proc != null); assert(table != null); this.pair = Pair.of(proc, table); } @Override public String toString() { return this.pair.toString(); } @Override public int hashCode() { return this.pair.hashCode(); } @Override public boolean equals(Object obj) { return this.pair.equals(obj); } @Override public int compareTo(CatalogType o) { return this.pair.compareTo(((ProcedureTable)o).pair); } @SuppressWarnings("unchecked") @Override public <T extends CatalogType> T getParent() { return (T)this.pair.getFirst(); } } // CLASS private static class Vertex extends AbstractVertex { private Vertex(Procedure proc, Table table) { super(new ProcedureTable(proc, table)); } @Override public String toString() { return this.getCatalogItem().toString(); } @Override public int hashCode() { return this.getCatalogItem().hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof Vertex) { return this.getCatalogItem().equals(((Vertex)obj).getCatalogItem()); } return false; } } // CLASS private static class Edge extends AbstractEdge { private final Pair<Statement, Statement> pair; private Edge(DumpGraph graph, Statement stmt0, Statement stmt1) { super(graph); assert(stmt0 != null); assert(stmt0 != null); this.pair = Pair.of(stmt0, stmt1); } @Override public String toString() { return this.pair.toString(); } @Override public int hashCode() { return this.pair.hashCode(); } @Override public boolean equals(Object obj) { return this.pair.equals(obj); } } // CLASS private static class DumpGraph extends AbstractDirectedGraph<Vertex, Edge> { private static final long serialVersionUID = 1; final Map<Pair<Procedure, Table>, Vertex> procTblXref = new HashMap<Pair<Procedure,Table>, Vertex>(); final Map<Procedure, Set<Vertex>> procXref = new HashMap<Procedure, Set<Vertex>>(); private DumpGraph(CatalogContext catalogContext) { super(catalogContext.database); for (Procedure proc : catalogContext.procedures) { if (proc.getName().equalsIgnoreCase("neworder") == false) continue; this.populateProcedure(proc); } // FOR } public Vertex getVertex(Procedure proc, Table tbl) { return this.procTblXref.get(Pair.of(proc, tbl)); } @Override public boolean addVertex(Vertex vertex) { boolean ret = super.addVertex(vertex); if (ret) { LOG.debug("Storing xref entry for " + vertex); ProcedureTable pt = vertex.getCatalogItem(); this.procTblXref.put(pt.pair, vertex); Set<Vertex> s = this.procXref.get(pt.pair.getFirst()); if (s == null) { s = new HashSet<Vertex>(); this.procXref.put(pt.pair.getFirst(), s); } s.add(vertex); } return (ret); } private void populateProcedure(Procedure proc0) { for (ConflictPair cp : ConflictSetUtil.getAllConflictPairs(proc0)) { Statement stmt0 = cp.getStatement0(); Statement stmt1 = cp.getStatement1(); Procedure proc1 = stmt1.getParent(); LOG.debug(String.format("%s -> %s+%s", cp, stmt0, stmt1)); for (Table tbl : CatalogUtil.getReferencedTables(stmt0)) { Vertex v0 = this.getVertex(proc0, tbl); if (v0 == null) { v0 = new Vertex(proc0, tbl); this.addVertex(v0); } Vertex v1 = this.getVertex(proc1, tbl); if (v1 == null) { v1 = new Vertex(proc1, tbl); this.addVertex(v1); } Edge e = new Edge(this, stmt0, stmt1); LOG.debug(String.format("%s ---%s---> %s\n", v0, e, v1)); this.addEdge(e, v0, v1); } // FOR } // FOR } } // CLASS public static void main(String[] vargs) throws Exception { ArgumentsParser args = ArgumentsParser.load(vargs, ArgumentsParser.PARAM_CATALOG ); ConflictSetCalculator calculator = new ConflictSetCalculator(args.catalog); // Procedures to exclude in ConflictGraph if (args.hasParam(ArgumentsParser.PARAM_CONFLICTS_EXCLUDE_PROCEDURES)) { String param = args.getParam(ArgumentsParser.PARAM_CONFLICTS_EXCLUDE_PROCEDURES); for (String procName : param.split(",")) { Procedure catalog_proc = args.catalogContext.procedures.getIgnoreCase(procName); if (catalog_proc != null) { calculator.ignoreProcedure(catalog_proc); } else { LOG.warn("Invalid procedure name to exclude '" + procName + "'"); } } // FOR } // Statements to exclude in ConflictGraph if (args.hasParam(ArgumentsParser.PARAM_CONFLICTS_EXCLUDE_STATEMENTS)) { String param = args.getParam(ArgumentsParser.PARAM_CONFLICTS_EXCLUDE_STATEMENTS); for (String name : param.split(",")) { String splits[] = name.split("\\."); if (splits.length != 2) { LOG.warn("Invalid procedure name to exclude '" + name + "': " + Arrays.toString(splits)); continue; } Procedure catalog_proc = args.catalogContext.procedures.getIgnoreCase(splits[0]); if (catalog_proc == null) { LOG.warn("Invalid procedure name to exclude '" + name + "'"); continue; } Statement catalog_stmt = catalog_proc.getStatements().getIgnoreCase(splits[1]); if (catalog_stmt != null) { calculator.ignoreStatement(catalog_stmt); } else { LOG.warn("Invalid statement name to exclude '" + name + "'"); } } // FOR } calculator.process(); DumpGraph graph = new DumpGraph(args.catalogContext); // If we have a Procedure to "focus" on, then we need to remove any edges // that don't involve that Procedure if (args.hasParam(ArgumentsParser.PARAM_CONFLICTS_FOCUS_PROCEDURE)) { String procName = args.getParam(ArgumentsParser.PARAM_CONFLICTS_FOCUS_PROCEDURE); Procedure catalog_proc = args.catalogContext.procedures.getIgnoreCase(procName); if (catalog_proc != null) { if (graph.procXref.containsKey(catalog_proc)) { Vertex vertices[] = graph.procXref.get(catalog_proc).toArray(new Vertex[0]); LOG.debug("Remove Edges Without: " + Arrays.toString(vertices)); GraphUtil.removeEdgesWithoutVertex(graph, vertices); GraphUtil.removeLoopEdges(graph); GraphUtil.removeDisconnectedVertices(graph); } } else { LOG.warn("Invalid procedure name to focus '" + procName + "'"); } } // Export! GraphvizExport<Vertex, Edge> gvx = new GraphvizExport<Vertex, Edge>(graph); gvx.setEdgeLabels(true); for (Vertex v : graph.getVertices()) { // Generate subgraphs based on procedure ProcedureTable procTbl = v.getCatalogItem(); Procedure proc = procTbl.pair.getFirst(); gvx.addSubgraph(proc.getName(), v); } // FOR String graphviz = gvx.export(args.catalog_type.name()); if (!graphviz.isEmpty()) { String output = args.getOptParam(0); if (output == null) { output = args.catalog_type.name().toLowerCase() + "-conflicts.dot"; } File path = new File(output); FileUtil.writeStringToFile(path, graphviz); System.out.println("Wrote contents to '" + path.getAbsolutePath() + "'"); } else { System.err.println("ERROR: Failed to generate graphviz data"); System.exit(1); } } }