DRILL-1217: Enhance execution stats web UI

Added Gantt chart
Improved organization
Decrease number of tables and overall page size
Implement general javascript functions for profile processing


Project: http://git-wip-us.apache.org/repos/asf/incubator-drill/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-drill/commit/6d5ccc81
Tree: http://git-wip-us.apache.org/repos/asf/incubator-drill/tree/6d5ccc81
Diff: http://git-wip-us.apache.org/repos/asf/incubator-drill/diff/6d5ccc81

Branch: refs/heads/master
Commit: 6d5ccc81881ad779f276bed03cf8c5246e214e1f
Parents: 204aa5d
Author: Cliff Buchanan <[email protected]>
Authored: Mon Jul 28 10:01:15 2014 -0700
Committer: Jacques Nadeau <[email protected]>
Committed: Tue Jul 29 16:49:33 2014 -0700

----------------------------------------------------------------------
 .../drill/exec/server/rest/ProfileWrapper.java  | 361 +++++++++--------
 .../src/main/resources/rest/profile/profile.ftl | 177 ++++++---
 .../src/main/resources/rest/www/graph.js        | 383 +++++++++++++++----
 .../src/main/resources/rest/www/style.css       |  27 --
 4 files changed, 640 insertions(+), 308 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/6d5ccc81/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ProfileWrapper.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ProfileWrapper.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ProfileWrapper.java
index 706f9a3..777779f 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ProfileWrapper.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ProfileWrapper.java
@@ -25,23 +25,24 @@ import 
org.apache.drill.exec.proto.UserBitShared.MinorFragmentProfile;
 import org.apache.drill.exec.proto.UserBitShared.OperatorProfile;
 import org.apache.drill.exec.proto.UserBitShared.QueryProfile;
 import org.apache.drill.exec.proto.UserBitShared.StreamProfile;
+import org.apache.drill.exec.proto.helper.QueryIdHelper;
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Collections2;
 import org.apache.drill.exec.proto.helper.QueryIdHelper;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 import java.text.DateFormat;
 import java.text.NumberFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
-import java.util.TreeMap;
+import java.util.Map;
 
 public class ProfileWrapper {
   public QueryProfile profile;
@@ -60,78 +61,70 @@ public class ProfileWrapper {
     return id;
   }
 
-  @Override
-  public String toString() {
-    StringBuilder builder = new StringBuilder();
-    ArrayList<MajorFragmentProfile> majors = new 
ArrayList<MajorFragmentProfile>(profile.getFragmentProfileList());
+  public String getQueryId() {
+    return QueryIdHelper.getQueryId(profile.getId());
+  }
+  
+  public List<OperatorWrapper> getOperatorProfiles() {
+    List<OperatorWrapper> ows = Lists.newArrayList();
+    Map<ImmutablePair<Integer, Integer>, List<ImmutablePair<OperatorProfile, 
Integer>>> opmap = Maps.newHashMap();
     
+    List<MajorFragmentProfile> majors = new 
ArrayList<>(profile.getFragmentProfileList());
     Collections.sort(majors, Comparators.majorIdCompare);
-    builder.append(queryTimingProfile(majors));
-    for (MajorFragmentProfile m : majors) {
-      builder.append(majorFragmentOperatorProfile(m));
-    }
-    for (MajorFragmentProfile m : majors) {
-      builder.append(majorFragmentTimingProfile(m));
-    }
-    for (MajorFragmentProfile m : majors) {
-      Collection<MinorFragmentProfile> minors = 
Collections2.filter(m.getMinorFragmentProfileList(), Filters.hasOperators);
-      for (MinorFragmentProfile mi : minors) {
-        builder.append(minorFragmentOperatorProfile(m.getMajorFragmentId(), 
mi));
+    for (MajorFragmentProfile major : majors) {
+      
+      List<MinorFragmentProfile> minors = new 
ArrayList<>(major.getMinorFragmentProfileList());
+      Collections.sort(minors, Comparators.minorIdCompare);
+      for (MinorFragmentProfile minor : minors) {
+        
+        List<OperatorProfile> ops = new 
ArrayList<>(minor.getOperatorProfileList());
+        Collections.sort(ops, Comparators.operatorIdCompare);
+        for (OperatorProfile op : ops) {
+          
+          ImmutablePair<Integer, Integer> ip = new ImmutablePair<>(
+              major.getMajorFragmentId(), op.getOperatorId());
+          if (!opmap.containsKey(ip)) {
+            List<ImmutablePair<OperatorProfile, Integer>> l = 
Lists.newArrayList();
+            opmap.put(ip, l);
+          }
+          opmap.get(ip).add(new ImmutablePair<>(op, 
minor.getMinorFragmentId()));
+        }
       }
     }
-    return builder.toString();
+    
+    List<ImmutablePair<Integer, Integer>> keys = new 
ArrayList<>(opmap.keySet());
+    Collections.sort(keys);
+    for (ImmutablePair<Integer, Integer> ip : keys) {
+      ows.add(new OperatorWrapper(ip.getLeft(), opmap.get(ip)));
+    }
+    
+    return ows;
   }
-
-  public String queryTimingProfile(ArrayList<MajorFragmentProfile> majors) {
-    final String[] columns = {"major id", "fragments reporting", "first 
start", "last start", "first end", "last end", "tmin", "tavg", "tmax"};
-    TableBuilder builder = new TableBuilder("Query Timing Profile", 
"QueryTimingProfile", columns);
-
+  
+  public List<FragmentWrapper> getFragmentProfiles() {
+    List<FragmentWrapper> fws = Lists.newArrayList();
     
-    long t0 = profile.getStart();
-    for (MajorFragmentProfile m : majors) {
-      final String fmt = " (<a href=\"#MinorFragment" + m.getMajorFragmentId() 
+ "_%1$dOperatorProfile\">%1$d</a>)";
-      
-      ArrayList<MinorFragmentProfile> complete = new 
ArrayList<MinorFragmentProfile>(
-          Collections2.filter(m.getMinorFragmentProfileList(), 
Filters.hasOperatorsAndTimes));
-
-      builder.appendInteger(m.getMajorFragmentId(), null);
-      builder.appendCell(complete.size() + " / " + 
m.getMinorFragmentProfileCount(), null);
-      
-      if (complete.size() < 1) {
-        builder.appendRepeated("", null, 7);
-        continue;
-      }
-
-      int li = complete.size() - 1;
-
-      Collections.sort(complete, Comparators.startTimeCompare);
-      builder.appendMillis(complete.get(0).getStartTime() - t0, 
String.format(fmt, complete.get(0).getMinorFragmentId()));
-      builder.appendMillis(complete.get(li).getStartTime() - t0, 
String.format(fmt, complete.get(li).getMinorFragmentId()));
+    List<MajorFragmentProfile> majors = new 
ArrayList<>(profile.getFragmentProfileList());
+    Collections.sort(majors, Comparators.majorIdCompare);
+    for (MajorFragmentProfile major : majors) {
+      fws.add(new FragmentWrapper(major));
+    }
+    
+    return fws;
+  }
 
-      Collections.sort(complete, Comparators.endTimeCompare);
-      builder.appendMillis(complete.get(0).getEndTime() - t0, 
String.format(fmt, complete.get(0).getMinorFragmentId()));
-      builder.appendMillis(complete.get(li).getEndTime() - t0, 
String.format(fmt, complete.get(li).getMinorFragmentId()));
-      
-      long total = 0;
-      for (MinorFragmentProfile p : complete) {
-        total += p.getEndTime() - p.getStartTime();
-      }
-      Collections.sort(complete, Comparators.runTimeCompare);
-      builder.appendMillis(complete.get(0).getEndTime() - 
complete.get(0).getStartTime(),
-          String.format(fmt, complete.get(0).getMinorFragmentId()));
-      builder.appendMillis((long) (total / complete.size()), null);
-      builder.appendMillis(complete.get(li).getEndTime() - 
complete.get(li).getStartTime(),
-          String.format(fmt, complete.get(li).getMinorFragmentId()));
+  public String getFragmentsOverview() {
+    final String[] columns = {"Fragment", "Minor Fragments Reporting", "First 
Start", "Last Start", "First End", "Last End", "tmin", "tavg", "tmax"};
+    TableBuilder tb = new TableBuilder(columns);
+    for (FragmentWrapper fw : getFragmentProfiles()) {
+      fw.addSummary(tb);
     }
-    return builder.toString();
+    return tb.toString();
   }
 
   public String majorFragmentTimingProfile(MajorFragmentProfile 
majorFragmentProfile) {
-    final String[] columns = {"id", "start", "end", "total time", "max 
records", "max batches"};
-    TableBuilder builder = new TableBuilder(
-        "Major Fragment #" + majorFragmentProfile.getMajorFragmentId() + " 
Timing Profile",
-        "MajorFragment" + majorFragmentProfile.getMajorFragmentId() + 
"TimingProfile",
-        columns);
+    final String[] columns = {"Minor", "Start", "End", "Total Time", "Max 
Records", "Max Batches"};
+    TableBuilder builder = new TableBuilder(columns);
 
     ArrayList<MinorFragmentProfile> complete, incomplete;
     complete = new ArrayList<MinorFragmentProfile>(Collections2.filter(
@@ -158,14 +151,11 @@ public class ProfileWrapper {
         biggestBatches = Math.max(biggestBatches, batches);
       }
 
-      builder.appendCell(
-          majorFragmentProfile.getMajorFragmentId() + "-"
-              + m.getMinorFragmentId(), null);
+      builder.appendCell(String.format("#%d - %d", 
majorFragmentProfile.getMajorFragmentId(), m.getMinorFragmentId()), null);
       builder.appendMillis(m.getStartTime() - t0, null);
       builder.appendMillis(m.getEndTime() - t0, null);
       builder.appendMillis(m.getEndTime() - m.getStartTime(), null);
       
-      Collections.sort(ops, Comparators.incomingRecordCompare);
       builder.appendInteger(biggestIncomingRecords, null);
       builder.appendInteger(biggestBatches, null);
     }
@@ -177,155 +167,213 @@ public class ProfileWrapper {
     }
     return builder.toString();
   }
+  
+  public String getOperatorsOverview() {
+    final String [] columns = {"Operator", "Type", "Setup (min)", "Setup 
(avg)", "Setup (max)", "Process (min)", "Process (avg)", "Process (max)", "Wait 
(min)", "Wait (avg)", "Wait (max)"};
+    TableBuilder tb = new TableBuilder(columns);
+    for (OperatorWrapper ow : getOperatorProfiles()) {
+      ow.addSummary(tb);
+    }
+    return tb.toString();
+  }
+  
+  public String getOperatorsJSON() {
+    StringBuilder sb = new StringBuilder("{");
+    String sep = "";
+    for (CoreOperatorType op : CoreOperatorType.values()) {
+      sb.append(String.format("%s\"%d\" : \"%s\"", sep, op.ordinal(), op));
+      sep = ", ";
+    }
+    return sb.append("}").toString();
+  }
+  
+  public class FragmentWrapper {
+    MajorFragmentProfile major;
+    public FragmentWrapper(MajorFragmentProfile major) {
+      this.major = major;
+    }
+    
+    public String getDisplayName() {
+      return "Fragment #" + major.getMajorFragmentId();
+    }
+    
+    public String getId() {
+      return "fragment-" + major.getMajorFragmentId();
+    }
+    
+    public void addSummary(TableBuilder tb) {
+      final String fmt = " (%d)";
+      long t0 = profile.getStart();
+      
+      ArrayList<MinorFragmentProfile> complete = new 
ArrayList<MinorFragmentProfile>(
+          Collections2.filter(major.getMinorFragmentProfileList(), 
Filters.hasOperatorsAndTimes));
 
-  public String majorFragmentOperatorProfile(MajorFragmentProfile major) {
-    TreeMap<Integer, ArrayList<Pair<OperatorProfile, Integer>>> opmap =
-        new TreeMap<Integer, ArrayList<Pair<OperatorProfile, Integer>>>();
+      tb.appendCell("#" + major.getMajorFragmentId(), null);
+      tb.appendCell(complete.size() + " / " + 
major.getMinorFragmentProfileCount(), null);
+      
+      if (complete.size() < 1) {
+        tb.appendRepeated("", null, 7);
+        return;
+      }
+
+      int li = complete.size() - 1;
+
+      Collections.sort(complete, Comparators.startTimeCompare);
+      tb.appendMillis(complete.get(0).getStartTime() - t0, String.format(fmt, 
complete.get(0).getMinorFragmentId()));
+      tb.appendMillis(complete.get(li).getStartTime() - t0, String.format(fmt, 
complete.get(li).getMinorFragmentId()));
 
+      Collections.sort(complete, Comparators.endTimeCompare);
+      tb.appendMillis(complete.get(0).getEndTime() - t0, String.format(fmt, 
complete.get(0).getMinorFragmentId()));
+      tb.appendMillis(complete.get(li).getEndTime() - t0, String.format(fmt, 
complete.get(li).getMinorFragmentId()));
+      
+      long total = 0;
+      for (MinorFragmentProfile p : complete) {
+        total += p.getEndTime() - p.getStartTime();
+      }
+      Collections.sort(complete, Comparators.runTimeCompare);
+      tb.appendMillis(complete.get(0).getEndTime() - 
complete.get(0).getStartTime(),
+          String.format(fmt, complete.get(0).getMinorFragmentId()));
+      tb.appendMillis((long) (total / complete.size()), null);
+      tb.appendMillis(complete.get(li).getEndTime() - 
complete.get(li).getStartTime(),
+          String.format(fmt, complete.get(li).getMinorFragmentId()));
+    }
     
+    public String getContent() {
+      return majorFragmentTimingProfile(major);
+    }
+  }
+  
+  public class OperatorWrapper {
+    int major;
+    List<ImmutablePair<OperatorProfile, Integer>> ops;
     
-    final String [] columns = {"id", "type", "setup min", "setup avg", "setup 
max", "process min", "process avg", "process max", "wait min", "wait avg", 
"wait max"};
-    TableBuilder builder = new TableBuilder(
-        String.format("Major Fragment #%d Operator Profile", 
major.getMajorFragmentId()),
-        String.format("MajorFragment%dOperatorProfile", 
major.getMajorFragmentId()),
-        columns);
+    public OperatorWrapper(int major, List<ImmutablePair<OperatorProfile, 
Integer>> ops) {
+      assert ops.size() > 0;
+      this.major = major;
+      this.ops = ops;
+    }
+    
+    public String getDisplayName() {
+      return String.format("Fragment #%d - Operator %d (%s)",
+          major, ops.get(0).getLeft().getOperatorId(),
+          CoreOperatorType.valueOf(ops.get(0).getLeft().getOperatorType() 
).toString());
+    }
     
+    public String getId() {
+      return String.format("operator-%d-%d", major, 
ops.get(0).getLeft().getOperatorId());
+    }
     
-    for (MinorFragmentProfile m : major.getMinorFragmentProfileList()) {
-      int mid = m.getMinorFragmentId();
+    public String getContent() {
+      final String [] columns = {"Fragment", "Setup", "Process", "Wait", "Max 
Batches", "Max Records"};
+      TableBuilder builder = new TableBuilder(columns);
       
-      for (OperatorProfile op : m.getOperatorProfileList()) {
-        int opid = op.getOperatorId();
+      for (ImmutablePair<OperatorProfile, Integer> ip : ops) {
+        int minor = ip.getRight();
+        OperatorProfile op = ip.getLeft();
         
-        if (!opmap.containsKey(opid)) {
-          opmap.put(opid, new ArrayList<Pair<OperatorProfile, Integer>>());
+        builder.appendCell(String.format("#%d - %d", major, minor), null);
+        builder.appendNanos(op.getSetupNanos(), null);
+        builder.appendNanos(op.getProcessNanos(), null);
+        builder.appendNanos(op.getWaitNanos(), null);
+        
+        long maxBatches = Long.MIN_VALUE;
+        long maxRecords = Long.MIN_VALUE;
+        for (StreamProfile sp : op.getInputProfileList()) {
+          maxBatches = Math.max(sp.getBatches(), maxBatches);
+          maxRecords = Math.max(sp.getRecords(), maxRecords);
         }
-        opmap.get(opid).add(new ImmutablePair<OperatorProfile, Integer>(op, 
mid));
+        
+        builder.appendInteger(maxBatches, null);
+        builder.appendInteger(maxRecords, null);
       }
+      return builder.toString();
     }
     
-    for (Integer opid : opmap.keySet()) {
-      ArrayList<Pair<OperatorProfile, Integer>> oplist = opmap.get(opid);
-      final String fmt = " (<a href=\"#MinorFragment" + 
major.getMajorFragmentId() + "_%1$dOperatorProfile\">%1$d</a>)";
-      int li = oplist.size() - 1;
-      double totalsetup = 0;
-      double totalprocess = 0;
-      double totalwait = 0;
+    public void addSummary(TableBuilder tb) {
+      tb.appendCell(String.format("#%d - Op %d", major, 
ops.get(0).getLeft().getOperatorId()), null);
+      
tb.appendCell(CoreOperatorType.valueOf(ops.get(0).getLeft().getOperatorType() 
).toString(), null);
 
-      for (Pair<OperatorProfile, Integer> opint : oplist) {
-        totalsetup += opint.getLeft().getSetupNanos();
-        totalprocess += opint.getLeft().getProcessNanos();
-        totalwait += opint.getLeft().getWaitNanos();
+      int li = ops.size() - 1;
+      String fmt = " (%s)";
+      
+      double setupSum = 0.0;
+      double processSum = 0.0;
+      double waitSum = 0.0;
+      for (ImmutablePair<OperatorProfile, Integer> ip : ops) {
+        setupSum += ip.getLeft().getSetupNanos();
+        processSum += ip.getLeft().getProcessNanos();
+        waitSum += ip.getLeft().getWaitNanos();
       }
       
-      builder.appendInteger(oplist.get(0).getLeft().getOperatorId(), null);
-      
builder.appendCell(CoreOperatorType.valueOf(oplist.get(0).getLeft().getOperatorType()).toString(),
 null);
+      Collections.sort(ops, Comparators.setupTimeSort);
+      tb.appendNanos(ops.get(0).getLeft().getSetupNanos(), String.format(fmt, 
ops.get(0).getRight()));
+      tb.appendNanos((long) (setupSum / ops.size()), null);
+      tb.appendNanos(ops.get(li).getLeft().getSetupNanos(), String.format(fmt, 
ops.get(li).getRight()));
       
-      Collections.sort(oplist, Comparators.setupTimeSort);
-      builder.appendNanos(oplist.get(0).getLeft().getSetupNanos(), 
String.format(fmt, oplist.get(0).getRight()));
-      builder.appendNanos((long) (totalsetup / oplist.size()), null);
-      builder.appendNanos(oplist.get(li).getLeft().getSetupNanos(), 
String.format(fmt, oplist.get(li).getRight()));
-
-      Collections.sort(opmap.get(opid), Comparators.processTimeSort);
-      builder.appendNanos(oplist.get(0).getLeft().getProcessNanos(), 
String.format(fmt, oplist.get(0).getRight()));
-      builder.appendNanos((long) (totalprocess / oplist.size()), null);
-      builder.appendNanos(oplist.get(li).getLeft().getProcessNanos(), 
String.format(fmt, oplist.get(li).getRight()));
+      Collections.sort(ops, Comparators.processTimeSort);
+      tb.appendNanos(ops.get(0).getLeft().getProcessNanos(), 
String.format(fmt, ops.get(0).getRight()));
+      tb.appendNanos((long) (processSum / ops.size()), null);
+      tb.appendNanos(ops.get(li).getLeft().getProcessNanos(), 
String.format(fmt, ops.get(li).getRight()));
       
-      Collections.sort(opmap.get(opid), Comparators.waitTimeSort);
-      builder.appendNanos(oplist.get(0).getLeft().getWaitNanos(), 
String.format(fmt, oplist.get(0).getRight()));
-      builder.appendNanos((long) (totalwait / oplist.size()), null);
-      builder.appendNanos(oplist.get(li).getLeft().getWaitNanos(), 
String.format(fmt, oplist.get(li).getRight()));
+      Collections.sort(ops, Comparators.waitTimeSort);
+      tb.appendNanos(ops.get(0).getLeft().getWaitNanos(), String.format(fmt, 
ops.get(0).getRight()));
+      tb.appendNanos((long) (waitSum / ops.size()), null);
+      tb.appendNanos(ops.get(li).getLeft().getWaitNanos(), String.format(fmt, 
ops.get(li).getRight()));
     }
-    return builder.toString();
   }
-  
-  public String minorFragmentOperatorProfile(int majorId, MinorFragmentProfile 
minorFragmentProfile) {
-    ArrayList<OperatorProfile> oplist = new 
ArrayList<OperatorProfile>(minorFragmentProfile.getOperatorProfileList());
-    
-    final String[] columns = {"id", "type", "setup", "process", "wait"};
-    TableBuilder builder = new TableBuilder(
-        String.format("Minor Fragment #%d-%d Operator Profile", majorId, 
minorFragmentProfile.getMinorFragmentId()),
-        String.format("MinorFragment%d_%dOperatorProfile", majorId, 
minorFragmentProfile.getMinorFragmentId()),
-        columns);
 
-    Collections.sort(oplist, Comparators.operatorIdCompare);
-    for (OperatorProfile op : oplist) {
-      builder.appendInteger(op.getOperatorId(), null);
-      
builder.appendCell(CoreOperatorType.valueOf(op.getOperatorType()).toString(), 
null);
-      builder.appendNanos(op.getSetupNanos(), null);
-      builder.appendNanos(op.getProcessNanos(), null);
-      builder.appendNanos(op.getWaitNanos(), null);
-    }
-    
-    return builder.toString();
-  }
-
-  private static class Comparators {
+  static class Comparators {
     final static Comparator<MajorFragmentProfile> majorIdCompare = new 
Comparator<MajorFragmentProfile>() {
       public int compare(MajorFragmentProfile o1, MajorFragmentProfile o2) {
-        return o1.getMajorFragmentId() < o2.getMajorFragmentId() ? -1 : 1;
+        return Long.compare(o1.getMajorFragmentId(), o2.getMajorFragmentId());
       }
     };
     
     final static Comparator<MinorFragmentProfile> minorIdCompare = new 
Comparator<MinorFragmentProfile>() {
       public int compare(MinorFragmentProfile o1, MinorFragmentProfile o2) {
-        return o1.getMinorFragmentId() < o2.getMinorFragmentId() ? -1 : 1;
+        return Long.compare(o1.getMinorFragmentId(), o2.getMinorFragmentId());
       }
     };
     
     final static Comparator<MinorFragmentProfile> startTimeCompare = new 
Comparator<MinorFragmentProfile>() {
       public int compare(MinorFragmentProfile o1, MinorFragmentProfile o2) {
-        return o1.getStartTime() < o2.getStartTime() ? -1 : 1;
+        return Long.compare(o1.getStartTime(), o2.getStartTime());
       }
     };
 
     final static Comparator<MinorFragmentProfile> endTimeCompare = new 
Comparator<MinorFragmentProfile>() {
       public int compare(MinorFragmentProfile o1, MinorFragmentProfile o2) {
-        return o1.getEndTime() < o2.getEndTime() ? -1 : 1;
+        return Long.compare(o1.getEndTime(), o2.getEndTime());
       }
     };
 
     final static Comparator<MinorFragmentProfile> runTimeCompare = new 
Comparator<MinorFragmentProfile>() {
       public int compare(MinorFragmentProfile o1, MinorFragmentProfile o2) {
-        return o1.getEndTime() - o1.getStartTime() < o2.getEndTime() - 
o2.getStartTime() ? -1 : 1;
+        return Long.compare(o1.getEndTime() - o1.getStartTime(), 
o2.getEndTime() - o2.getStartTime());
       }
     };
     
     final static Comparator<OperatorProfile> operatorIdCompare = new 
Comparator<OperatorProfile>() {
       public int compare(OperatorProfile o1, OperatorProfile o2) {
-        return o1.getOperatorId() < o2.getOperatorId() ? -1 : 1;
-      }
-    };
-    
-    final static Comparator<OperatorProfile> incomingRecordCompare = new 
Comparator<OperatorProfile>() {
-      public long incomingRecordCount(OperatorProfile op) {
-        long count = 0;
-        for (StreamProfile sp : op.getInputProfileList()) {
-          count += sp.getRecords();
-        }
-        return count;
-      }
-      
-      public int compare(OperatorProfile o1, OperatorProfile o2) {
-        return incomingRecordCount(o1) > incomingRecordCount(o2) ? -1 : 1;
+        return Long.compare(o1.getOperatorId(), o2.getOperatorId());
       }
     };
     
     final static Comparator<Pair<OperatorProfile, Integer>> setupTimeSort = 
new Comparator<Pair<OperatorProfile, Integer>>() {
       public int compare(Pair<OperatorProfile, Integer> o1, 
Pair<OperatorProfile, Integer> o2) {
-        return o1.getLeft().getSetupNanos() < o2.getLeft().getSetupNanos() ? 
-1 : 1;
+        return Long.compare(o1.getLeft().getSetupNanos(), 
o2.getLeft().getSetupNanos());
       }
     };
     
     final static Comparator<Pair<OperatorProfile, Integer>> processTimeSort = 
new Comparator<Pair<OperatorProfile, Integer>>() {
       public int compare(Pair<OperatorProfile, Integer> o1, 
Pair<OperatorProfile, Integer> o2) {
-        return o1.getLeft().getProcessNanos() < o2.getLeft().getProcessNanos() 
? -1 : 1;
+        return Long.compare(o1.getLeft().getProcessNanos(), 
o2.getLeft().getProcessNanos());
       }
     };
     
     final static Comparator<Pair<OperatorProfile, Integer>> waitTimeSort = new 
Comparator<Pair<OperatorProfile, Integer>>() {
       public int compare(Pair<OperatorProfile, Integer> o1, 
Pair<OperatorProfile, Integer> o2) {
-        return o1.getLeft().getWaitNanos() < o2.getLeft().getWaitNanos() ? -1 
: 1;
+        return Long.compare(o1.getLeft().getWaitNanos(), 
o2.getLeft().getWaitNanos());
       }
     };
   }
@@ -356,14 +404,13 @@ public class ProfileWrapper {
     int w = 0;
     int width;
     
-    public TableBuilder(String title, String id, String[] columns) {
+    public TableBuilder(String[] columns) {
       sb = new StringBuilder();
       width = columns.length;
       
       format.setMaximumFractionDigits(3);
       format.setMinimumFractionDigits(3);
       
-      sb.append(String.format("<h3 id=\"%s\">%s</h3>\n", id, title));
       sb.append("<table class=\"table table-bordered text-right\">\n<tr>");
       for (String cn : columns) {
         sb.append("<th>" + cn + "</th>");

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/6d5ccc81/exec/java-exec/src/main/resources/rest/profile/profile.ftl
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/resources/rest/profile/profile.ftl 
b/exec/java-exec/src/main/resources/rest/profile/profile.ftl
index ffe7e46..3cd214c 100644
--- a/exec/java-exec/src/main/resources/rest/profile/profile.ftl
+++ b/exec/java-exec/src/main/resources/rest/profile/profile.ftl
@@ -11,68 +11,153 @@
 
 <#include "*/generic.ftl">
 <#macro page_head>
-<link href="/www/style.css" rel="stylesheet">
-
-<script src="http://d3js.org/d3.v3.min.js";></script>
+<script src="http://d3js.org/d3.v3.js";></script>
 <script 
src="http://cpettitt.github.io/project/dagre-d3/latest/dagre-d3.js";></script>
 <script src="/www/graph.js"></script>
+<script>
+    var globalconfig = {
+        "queryid" : "${model.getQueryId()}",
+        "operators" : ${model.getOperatorsJSON()}
+    };
+</script>
 </#macro>
 
 <#macro page_body>
   <a href="/queries">back</a><br/>
   <div class="page-header">
   </div>
-  <h3>Query</h3>
-  <form role="form" action="/query" method="POST">
-    <div class="form-group">
-      <textarea class="form-control" id="query" name="query" 
style="font-family: Courier;">${model.getProfile().query}</textarea>
+  <h3>Query and Planning</h3>
+  <ul id="query-tabs" class="nav nav-tabs" role="tablist">
+    <li><a href="#query-query" role="tab" data-toggle="tab">Query</a></li>
+    <li><a href="#query-physical" role="tab" data-toggle="tab">Physical 
Plan</a></li>
+    <li><a href="#query-visual" role="tab" data-toggle="tab">Visualized 
Plan</a></li>
+    <li><a href="#query-edit" role="tab" data-toggle="tab">Edit Query</a></li>
+  </ul>
+  <div id="query-content" class="tab-content">
+    <div id="query-query" class="tab-pane">
+      <p><pre>${model.getProfile().query}</pre></p>
+    </div>
+    <div id="query-physical" class="tab-pane">
+      <p><pre>${model.profile.plan}</pre></p>
+    </div>
+    <div id="query-visual" class="tab-pane">
+      <svg id="query-visual-canvas" class="center-block"></svg>
     </div>
-    <div class="form-group">
-      <div class="radio-inline">
-        <label>
-          <input type="radio" name="queryType" id="sql" value="SQL" checked>
-          SQL
-        </label>
+    <div id="query-edit" class="tab-pane">
+      <form role="form" action="/query" method="POST">
+        <div class="form-group">
+          <textarea class="form-control" id="query" name="query" 
style="font-family: Courier;">${model.getProfile().query}</textarea>
+        </div>
+        <div class="form-group">
+          <div class="radio-inline">
+            <label>
+              <input type="radio" name="queryType" id="sql" value="SQL" 
checked>
+              SQL
+            </label>
+          </div>
+          <div class="radio-inline">
+            <label>
+              <input type="radio" name="queryType" id="physical" 
value="PHYSICAL">
+              PHYSICAL
+            </label>
+          </div>
+          <div class="radio-inline">
+            <label>
+              <input type="radio" name="queryType" id="logical" 
value="LOGICAL">
+              LOGICAL
+            </label>
+          </div>
+        </div>
+        <button type="submit" class="btn btn-default">Re-run query</button>
+      </form>
+      <form action="/profiles/cancel/${model.id}" method="GET">
+        <button type="link" class="btn btn-default">Cancel query</button>
+      </form>
+    </div>
+  </div>
+  
+  <div class="page-header"></div>
+  <h3>Fragment Profiles</h3>
+  
+  <div class="panel-group" id="fragment-accordion">
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <h4 class="panel-title">
+          <a data-toggle="collapse" href="#fragment-overview">
+            Overview
+          </a>
+        </h4>
       </div>
-      <div class="radio-inline">
-        <label>
-          <input type="radio" name="queryType" id="physical" value="PHYSICAL">
-          PHYSICAL
-        </label>
+      <div id="fragment-overview" class="panel-collapse collapse">
+        <div class="panel-body">
+          <svg id="fragment-overview-canvas" class="center-block"></svg>
+          ${model.getFragmentsOverview()}
+        </div>
       </div>
-      <div class="radio-inline">
-        <label>
-          <input type="radio" name="queryType" id="logical" value="LOGICAL">
-          LOGICAL
-        </label>
+    </div>
+    <#list model.getFragmentProfiles() as frag>
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <h4 class="panel-title">
+          <a data-toggle="collapse" href="#${frag.getId()}">
+            ${frag.getDisplayName()}
+          </a>
+        </h4>
+      </div>
+      <div id="${frag.getId()}" class="panel-collapse collapse">
+        <div class="panel-body">
+          ${frag.getContent()}
+        </div>
       </div>
     </div>
-    <button type="submit" class="btn btn-default">Re-run query</button>
-  </form>
-  <form action="/profiles/cancel/${model.id}" method="GET">
-    <button type="link" class="btn btn-default">Cancel query</button>
-  </form>
-  <div class="page-header">
-  </div>
-  <h3>Visualized Plan</h3>
-  <button id="renderbutton" class="btn btn-default">Generate</button>
-  <svg id="svg-canvas" style="margin: auto; display: block;">
-    <g transform="translate(20, 20)"/>
-  </svg>
-  <div class="page-header">
+    </#list>
   </div>
-  <h3>Physical Plan</h3>
-  <p><pre>${model.profile.plan}</pre></p>
-  <div class="page-header">
-  </div>
-  <h3>Profile Summary</h3>
-  <p>${model.toString()}</p>
-  <div class="page-header">
+  
+  <div class="page-header"></div>
+  <h3>Operator Profiles</h3>
+  
+  <div class="panel-group" id="operator-accordion">
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <h4 class="panel-title">
+          <a data-toggle="collapse" href="#operator-overview">
+            Overview
+          </a>
+        </h4>
+      </div>
+      <div id="operator-overview" class="panel-collapse collapse">
+        <div class="panel-body">
+          ${model.getOperatorsOverview()}
+        </div>
+      </div>
+    </div>
+
+    <#list model.getOperatorProfiles() as op>
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <h4 class="panel-title">
+          <a data-toggle="collapse" href="#${op.getId()}">
+            ${op.getDisplayName()}
+          </a>
+        </h4>
+      </div>
+      <div id="${op.getId()}" class="panel-collapse collapse">
+        <div class="panel-body">
+          ${op.getContent()}
+        </div>
+      </div>
+    </div>
+    </#list>
   </div>
-  <div class="span4 collapse-group">
-    <a class="btn btn-default" data-toggle="collapse" 
data-target="#viewdetails">View complete profile</a>
+  
+  <div class="page-header"></div>
+  <h3>Full JSON Profile</h3>
+  
+  <div class="span4 collapse-group" id="full-json-profile">
+    <a class="btn btn-default" data-toggle="collapse" 
data-target="#full-json-profile-json">JSON profile</a>
     <br> <br>
-    <pre class="collapse" id="viewdetails">${model.profile.toString()}</pre>
+    <pre class="collapse" id="full-json-profile-json">
+    </pre>
   </div>
   <div class="page-header">
   </div> <br>

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/6d5ccc81/exec/java-exec/src/main/resources/rest/www/graph.js
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/resources/rest/www/graph.js 
b/exec/java-exec/src/main/resources/rest/www/graph.js
index 0173da5..76f39fa 100644
--- a/exec/java-exec/src/main/resources/rest/www/graph.js
+++ b/exec/java-exec/src/main/resources/rest/www/graph.js
@@ -1,82 +1,309 @@
 /*
-  Licensed to the Apache Software Foundation (ASF) under one or more 
contributor
-  license agreements. See the NOTICE file distributed with this work for 
additional
-  information regarding copyright ownership. The ASF licenses this file to
-  You under the Apache License, Version 2.0 (the "License"); you may not use
-  this file except in compliance with the License. You may obtain a copy of
-  the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
-  by applicable law or agreed to in writing, software distributed under the
-  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
-  OF ANY KIND, either express or implied. See the License for the specific
-  language governing permissions and limitations under the License. */
-
+ *  Licensed to the Apache Software Foundation (ASF) under one or more 
contributor
+ *  license agreements. See the NOTICE file distributed with this work for 
additional
+ *  information regarding copyright ownership. The ASF licenses this file to
+ *  You under the Apache License, Version 2.0 (the "License"); you may not use
+ *  this file except in compliance with the License. You may obtain a copy of
+ *  the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
+ *  by applicable law or agreed to in writing, software distributed under the
+ *  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
CONDITIONS
+ *  OF ANY KIND, either express or implied. See the License for the specific
+ *  language governing permissions and limitations under the License.
+ */
 
 $(window).load(function () {
-    document.getElementById("renderbutton").addEventListener("click", function 
() {
-       this.remove();
-       var queryid = window.location.href.split("/").splice(-1);
-       $.ajax({
-           type: "GET",
-           dataType: "json",
-           url: "/profiles/" + queryid + ".json",
-           success: function (profile) {
-               var colors = ["red", "green", "blue", "orange", "yellow", 
"pink", "purple", "gray"];
-
-               var plan = $.map(profile.plan.trim().split("\n"), function (s) {
-                   return [/^([0-9-]+)( *)([a-zA-Z]*)/.exec(s).slice(1)];
-               });
-
-               // nodes
-               var g = new dagreD3.Digraph();
-               for (var i = 0; i < plan.length; i++) {
-                   g.addNode(plan[i][0], {
-                       label: plan[i][2],
-                       fragment: parseInt(plan[i][0].split("-")[0])
-                   });
-               }
-
-               // edges
-               var st = [plan[0]];
-               for (var i = 1; i < plan.length; i++) {
-                   var top = st.pop();
-                   while (top[1].length >= plan[i][1].length)
-                       top = st.pop();
-
-                   g.addEdge(null, plan[i][0], top[0]);
-
-                   if (plan[i][1].length != top[1].length)
-                       st.push(top);
-                   if (plan[i][1].length >= top[1].length)
-                       st.push(plan[i]);
-               }
-
-               // rendering
-               var renderer = new dagreD3.Renderer();
-               renderer.zoom(function () {return function (graph, root) {}});
-               var oldDrawNodes = renderer.drawNodes();
-               renderer.drawNodes(function(graph, root) {
-                   var svgNodes = oldDrawNodes(graph, root);
-                   svgNodes.each(function(u) {
-                       d3.select(this).style("fill", 
colors[graph.node(u).fragment % colors.length]);
-                   });
-                   return svgNodes;
-               });
-
-               // page placement
-               var layout = dagreD3.layout()
-                    .nodeSep(20);//.rankDir("LR");
-               var layout = renderer.layout(layout).run(g, d3.select("svg g"));
-
-               //var layout = renderer.run(g, d3.select("svg g"));
-               d3.select("svg")
-                   .attr("width", layout.graph().width + 40)
-                   .attr("height", layout.graph().height + 40);
-           },
-           error: function (x, y, z) {
-               console.log(x);
-               console.log(y);
-               console.log(z);
-           }
-       });
-    }, false);
+    // for each record, unroll the array pointed to by "fieldpath" into a new
+    // record for each element of the array
+    function unnest (table, fieldpath, dest) {
+        var faccess = accessor(fieldpath);
+        return $.map(table, function (record, index) {
+            var ra = [];
+            var nested = faccess(record);
+            for (var i = 0; i < nested.length; i++) {
+                var newrec = $.extend({}, record);
+                newrec[dest] = nested[i];
+                ra.push(newrec);
+            }
+            return ra;
+        });
+    }
+
+    // for each record, project "fieldpath" into "dest".
+    function extract (table, fieldpath, dest) {
+        var faccess = accessor(fieldpath);
+        return $.map(table, function (record, index) {
+            var newrec = $.extend({}, record);
+            newrec[dest] = faccess(newrec);
+            return newrec;
+        });
+    }
+
+    // creates a function that will traverse tree of objects based the '.'
+    // delimited "path"
+    function accessor (path) {
+        path = path.split(".");
+        return function (obj) {
+            for (var i = 0; i < path.length; i++)
+                obj = obj[path[i]];
+            return obj;
+        }
+    }
+
+    // sample use of unnest/extract
+    function extractminortimes (profile) {
+        var t1 = unnest([profile], "fragmentProfile", "ma");
+        var t2 = unnest(t1, "ma.minorFragmentProfile", "mi");
+
+        var timetable = $.map(t2, function (record, index) {
+            var newrec = {
+                "name" : record.ma.majorFragmentId + "-" +
+                    record.mi.minorFragmentId,
+                "category" : record.ma.majorFragmentId,
+                "start" : (record.mi.startTime - record.start) / 1000.0,
+                "end" : (record.mi.endTime - record.start) / 1000.0
+            };
+            return newrec;
+        });
+
+        timetable.sort(function (r1, r2) {
+            if (r1.category == r2.category) {
+                //return r1.name > r2.name;
+                return r1.start + r2.end > r1.end + r2.start;
+            }
+            else return r1.category > r2.category;
+            
+        });
+        return timetable;
+    }
+
+    // write the "fieldpaths" for the table "table" into the "domtable"
+    function builddomtable (domtable, table, fieldpaths) {
+        var faccessors = $.map(fieldpaths, function (d, i) {
+            return accessor(d);
+        });
+
+        var domrow = domtable.append("tr");
+        for (var i = 0; i < fieldpaths.length; i++)
+            domrow.append("th").text(fieldpaths[i]);
+        for (var i = 0; i < table.length; i++) {
+            domrow = domtable.append("tr");
+            for (var j = 0; j < faccessors.length; j++)
+                domrow.append("td").text(faccessors[j](table[i]));
+        }
+    }
+
+    // parse the short physical plan into a dagreeD3 structure
+    function parseplan (planstring) {
+        var g = new dagreD3.Digraph();
+        var ps = $.map(planstring.trim().split("\n"), function (s) {
+            return [/^([0-9-]+)( *)([a-zA-Z]*)/.exec(s).slice(1)];
+        });
+
+        // nodes
+        for (var i = 0; i < ps.length; i++) {
+            g.addNode(ps[i][0], {
+                label: ps[i][2],
+                fragment: parseInt(ps[i][0].split("-")[0])
+            });
+        }
+        
+        // edges
+        var st = [ps[0]];
+        for (var i = 1; i < ps.length; i++) {
+            var top = st.pop();
+            while (top[1].length >= ps[i][1].length)
+                top = st.pop();
+
+            g.addEdge(null, ps[i][0], top[0]);
+
+            if (ps[i][1].length != top[1].length)
+                st.push(top);
+            if (ps[i][1].length >= top[1].length)
+                st.push(ps[i]);
+        }
+        return g;
+    }
+
+    // graph a "planstring" into the d3 svg handle "svg"
+    function buildplangraph (svg, planstring) {
+        var padding = 20;
+        var graph = parseplan(planstring);
+
+        var renderer = new dagreD3.Renderer();
+        renderer.zoom(function () {return function (graph, root) {}});
+
+        var oldDrawNodes = renderer.drawNodes();
+        renderer.drawNodes(function(graph, root) {
+            var svgNodes = oldDrawNodes(graph, root);
+            svgNodes.each(function(u) {
+                var fc = 
d3.rgb(globalconfig.majorcolorscale(graph.node(u).fragment));
+                d3.select(this).select("rect")
+                    .style("fill", graph.node(u).label.endsWith("Exchange") ? 
"white" : fc)
+                    .style("stroke", "#000")
+                    .style("stroke-width", "1px")
+            });
+            return svgNodes;
+        });
+
+        var oldDrawEdgePaths = renderer.drawEdgePaths();
+        renderer.drawEdgePaths(function(graph, root) {
+            var svgEdgePaths = oldDrawEdgePaths(graph, root);
+            svgEdgePaths.each(function(u) {
+                d3.select(this).select("path")
+                    .style("fill", "none")
+                    .style("stroke", "#000")
+                    .style("stroke-width", "1px")
+            });
+            return svgEdgePaths;
+        });
+
+        var shiftedgroup = svg.append("g")
+            .attr("transform", "translate(" + padding + "," + padding + ")");
+        var layout = dagreD3.layout().nodeSep(20).rankDir("BT");
+        var result = renderer.layout(layout).run(graph, shiftedgroup);
+
+        svg.attr("width", result.graph().width + 2 * padding)
+            .attr("height", result.graph().height + 2 * padding);
+    }
+
+    function buildtimingchart (svgdest, timetable) {
+        var chartprops = {
+            "w" : 800,
+            "h" : -1,
+            "svg" : svgdest,
+            "bheight" : 2,
+            "bpad" : 0,
+            "margin" : 50,
+            "scaler" : null,
+            "colorer" : null,
+        };
+
+        chartprops.h = timetable.length * (chartprops.bheight + 
chartprops.bpad * 2)
+
+        chartprops.svg
+            .attr("width", chartprops.w + 2 * chartprops.margin)
+            .attr("height", chartprops.h + 2 * chartprops.margin)
+            .attr("class", "svg");
+
+        chartprops.scaler = d3.scale.linear()
+            .domain([d3.min(timetable, accessor("start")),
+                     d3.max(timetable, accessor("end"))])
+            .range([0, chartprops.w - chartprops.bpad * 2]);
+        chartprops.colorer = globalconfig.majorcolorscale;
+
+        // backdrop
+        chartprops.svg.append("g")
+            .selectAll("rect")
+            .data(timetable)
+            .enter()
+            .append("rect")
+            .attr("x", 0)
+            .attr("y", function(d, i) {return i * (chartprops.bheight + 2 * 
chartprops.bpad);})
+            .attr("width", chartprops.w)
+            .attr("height", chartprops.bheight + chartprops.bpad * 2)
+            .attr("stroke", "none")
+            .attr("fill", function(d) {return 
d3.rgb(chartprops.colorer(d.category));})
+            .attr("opacity", 0.1)
+            .attr("transform", "translate(" + chartprops.margin + "," +
+                  chartprops.margin + ")");
+
+        // bars
+        chartprops.svg.append('g')
+            .selectAll("rect")
+            .data(timetable)
+            .enter()
+            .append("rect")
+             //.attr("rx", 3)
+             //.attr("ry", 3)
+            .attr("x", function(d) {return chartprops.scaler(d.start) + 
chartprops.bpad;})
+            .attr("y", function(d, i) {return i * (chartprops.bheight + 2 * 
chartprops.bpad) + chartprops.bpad;})
+            .attr("width", function(d) {return (chartprops.scaler(d.end) - 
chartprops.scaler(d.start));})
+            .attr("height", chartprops.bheight)
+            .attr("stroke", "none")
+            .attr("fill", function(d) {return 
d3.rgb(chartprops.colorer(d.category));})
+            .attr("transform", "translate(" + chartprops.margin + "," +
+                  chartprops.margin + ")");
+
+        // grid lines
+        chartprops.svg.append("g")
+            .attr("transform", "translate(" +
+                  (chartprops.bpad + chartprops.margin) + "," +
+                  (chartprops.h + chartprops.margin) + ")")
+            .attr("class", "grid")
+            .call(d3.svg.axis()
+                  .scale(chartprops.scaler)
+                  .tickSize(-chartprops.h, 0)
+                  .tickFormat(""))
+            .style("stroke", "#000")
+            .style("opacity", 0.2);
+
+        // ticks
+        chartprops.svg.append("g")
+            .attr("transform", "translate(" +
+                  (chartprops.bpad + chartprops.margin) + "," +
+                  (chartprops.h + chartprops.margin) + ")")
+            .attr("class", "grid")
+            .call(d3.svg.axis()
+                  .scale(chartprops.scaler)
+                  .orient('bottom')
+                  .tickSize(0, 0)
+                  .tickFormat(d3.format(".2f")));
+    }
+
+    function loadprofile (queryid, callback) {
+        $.ajax({
+            type: "GET",
+            dataType: "json",
+            url: "/profiles/" + queryid + ".json",
+            success: callback,
+            error: function (x, y, z) {
+                console.log(x);
+                console.log(y);
+                console.log(z);
+            }
+        });
+    }
+
+    function setupglobalconfig (profile) {
+        globalconfig.profile = profile;
+        globalconfig.majorcolorscale = d3.scale.category20()
+            .domain([0, d3.max(profile.fragmentProfile, 
accessor("majorFragmentId"))]);
+
+    }
+
+    loadprofile(globalconfig.queryid, function (profile) {
+        setupglobalconfig(profile);
+
+        var queryvisualdrawn = false;
+        var timingoverviewdrawn = false;
+        var jsonprofileshown = false;
+
+        // trigger svg drawing when visible
+        $('#query-tabs').on('shown.bs.tab', function (e) {
+            if (queryvisualdrawn || !e.target.href.endsWith("#query-visual")) 
return;
+            buildplangraph(d3.select("#query-visual-canvas"), profile.plan);
+            queryvisualdrawn = true;
+        })
+        $('#fragment-accordion').on('shown.bs.collapse', function (e) {
+            if (timingoverviewdrawn || e.target.id != "fragment-overview") 
return;
+            buildtimingchart(d3.select("#fragment-overview-canvas"), 
extractminortimes(profile));
+            timingoverviewdrawn = true;
+        });
+
+        // select default tabs
+        $('#fragment-overview').collapse('show');
+        $('#operator-overview').collapse('show');
+        $('#query-tabs a[href="#query-query"]').tab('show');
+
+
+        // add json profile on click
+        $('#full-json-profile-json').on('shown.bs.collapse', function (e) {
+            if (jsonprofileshown) return;
+            
$('#full-json-profile-json').html(JSON.stringify(globalconfig.profile, null, 
4));
+        });
+
+        //builddomtable(d3.select("#timing-table")
+        //            .append("tbody"), extractminortimes(profile),
+        //            ["name", "start", "end"]);
+    });
 });

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/6d5ccc81/exec/java-exec/src/main/resources/rest/www/style.css
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/resources/rest/www/style.css 
b/exec/java-exec/src/main/resources/rest/www/style.css
deleted file mode 100644
index 46ff811..0000000
--- a/exec/java-exec/src/main/resources/rest/www/style.css
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-  Licensed to the Apache Software Foundation (ASF) under one or more 
contributor
-  license agreements. See the NOTICE file distributed with this work for 
additional
-  information regarding copyright ownership. The ASF licenses this file to
-  You under the Apache License, Version 2.0 (the "License"); you may not use
-  this file except in compliance with the License. You may obtain a copy of
-  the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
-  by applicable law or agreed to in writing, software distributed under the
-  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
-  OF ANY KIND, either express or implied. See the License for the specific
-  language governing permissions and limitations under the License. */
-
-
-.node rect {
-    stroke: #000;
-    stroke-width: 1px;
-}
-
-.node text {
-    fill: #000;
-}
-
-.edgePath path {
-    stroke: #333;
-    stroke-width: 1.5px;
-    fill: none;
-}

Reply via email to