drill-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sudhe...@apache.org
Subject [12/29] drill git commit: DRILL-5195: Publish Operator and MajorFragment Stats in Profile page
Date Sat, 25 Feb 2017 07:18:05 GMT
DRILL-5195: Publish Operator and MajorFragment Stats in Profile page

Improved UI
1. Introduction of Tooltips
2. Share of each operator as a percentages of the major fragment and of the query
  - This would help identify the most CPU intensive operators within a fragment and across
the query
3. Rows emitted by each operator
4. For a running query, changes to 'last update' and 'last progress' now shows the elapsed
time since.

closes #756


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

Branch: refs/heads/master
Commit: 8614bae5e881dadeb556206c1e2129e9b816a50c
Parents: 6892164
Author: Kunal Khatua <kkhatua@maprtech.com>
Authored: Fri Feb 24 17:18:40 2017 -0800
Committer: Sudheesh Katkam <sudheesh@apache.org>
Committed: Fri Feb 24 19:01:41 2017 -0800

----------------------------------------------------------------------
 .../server/rest/profile/FragmentWrapper.java    | 208 +++++++++++++++----
 .../server/rest/profile/OperatorWrapper.java    | 141 +++++++++----
 .../server/rest/profile/ProfileWrapper.java     |  42 +++-
 .../exec/server/rest/profile/TableBuilder.java  | 120 +++++++++--
 .../src/main/resources/rest/profile/profile.ftl |   8 +-
 5 files changed, 420 insertions(+), 99 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/drill/blob/8614bae5/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/FragmentWrapper.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/FragmentWrapper.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/FragmentWrapper.java
index ec0e2c8..b25b92a 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/FragmentWrapper.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/FragmentWrapper.java
@@ -17,9 +17,12 @@
  */
 package org.apache.drill.exec.server.rest.profile;
 
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
+import java.util.Locale;
 
 import org.apache.drill.exec.proto.UserBitShared.MajorFragmentProfile;
 import org.apache.drill.exec.proto.UserBitShared.MinorFragmentProfile;
@@ -49,75 +52,174 @@ public class FragmentWrapper {
     return String.format("fragment-%s", major.getMajorFragmentId());
   }
 
-  public static final String[] FRAGMENT_OVERVIEW_COLUMNS = {"Major Fragment", "Minor Fragments
Reporting",
-    "First Start", "Last Start", "First End", "Last End", "Min Runtime", "Avg Runtime", "Max
Runtime", "Last Update",
-    "Last Progress", "Max Peak Memory"};
+  public static final String[] ACTIVE_FRAGMENT_OVERVIEW_COLUMNS = {
+      OverviewTblTxt.MAJOR_FRAGMENT, OverviewTblTxt.MINOR_FRAGMENTS_REPORTING,
+      OverviewTblTxt.FIRST_START, OverviewTblTxt.LAST_START, OverviewTblTxt.FIRST_END, OverviewTblTxt.LAST_END,
+      OverviewTblTxt.MIN_RUNTIME, OverviewTblTxt.AVG_RUNTIME, OverviewTblTxt.MAX_RUNTIME,
+      OverviewTblTxt.PERCENT_BUSY,
+      OverviewTblTxt.LAST_UPDATE, OverviewTblTxt.LAST_PROGRESS,
+      OverviewTblTxt.MAX_PEAK_MEMORY
+  };
+
+  public static final String[] ACTIVE_FRAGMENT_OVERVIEW_COLUMNS_TOOLTIP = {
+      OverviewTblTooltip.MAJOR_FRAGMENT, OverviewTblTooltip.MINOR_FRAGMENTS_REPORTING,
+      OverviewTblTooltip.FIRST_START, OverviewTblTooltip.LAST_START, OverviewTblTooltip.FIRST_END,
OverviewTblTooltip.LAST_END,
+      OverviewTblTooltip.MIN_RUNTIME, OverviewTblTooltip.AVG_RUNTIME, OverviewTblTooltip.MAX_RUNTIME,
+      OverviewTblTooltip.PERCENT_BUSY,
+      OverviewTblTooltip.LAST_UPDATE, OverviewTblTooltip.LAST_PROGRESS,
+      OverviewTblTooltip.MAX_PEAK_MEMORY
+  };
 
   // Not including Major Fragment ID and Minor Fragments Reporting
-  public static final int NUM_NULLABLE_OVERVIEW_COLUMNS = FRAGMENT_OVERVIEW_COLUMNS.length
- 2;
+  public static final int NUM_NULLABLE_ACTIVE_OVERVIEW_COLUMNS = ACTIVE_FRAGMENT_OVERVIEW_COLUMNS.length
- 2;
 
   public void addSummary(TableBuilder tb) {
     // Use only minor fragments that have complete profiles
     // Complete iff the fragment profile has at least one operator profile, and start and
end times.
     final List<MinorFragmentProfile> complete = new ArrayList<>(
-      Collections2.filter(major.getMinorFragmentProfileList(), Filters.hasOperatorsAndTimes));
+        Collections2.filter(major.getMinorFragmentProfileList(), Filters.hasOperatorsAndTimes));
 
-    tb.appendCell(new OperatorPathBuilder().setMajor(major).build(), null);
-    tb.appendCell(complete.size() + " / " + major.getMinorFragmentProfileCount(), null);
+    tb.appendCell(new OperatorPathBuilder().setMajor(major).build());
+    tb.appendCell(complete.size() + " / " + major.getMinorFragmentProfileCount());
 
     // If there are no stats to aggregate, create an empty row
     if (complete.size() < 1) {
-      tb.appendRepeated("", null, NUM_NULLABLE_OVERVIEW_COLUMNS);
+      tb.appendRepeated("", null, NUM_NULLABLE_ACTIVE_OVERVIEW_COLUMNS);
       return;
     }
 
     final MinorFragmentProfile firstStart = Collections.min(complete, Comparators.startTime);
     final MinorFragmentProfile lastStart = Collections.max(complete, Comparators.startTime);
-    tb.appendMillis(firstStart.getStartTime() - start, null);
-    tb.appendMillis(lastStart.getStartTime() - start, null);
+    tb.appendMillis(firstStart.getStartTime() - start);
+    tb.appendMillis(lastStart.getStartTime() - start);
 
     final MinorFragmentProfile firstEnd = Collections.min(complete, Comparators.endTime);
     final MinorFragmentProfile lastEnd = Collections.max(complete, Comparators.endTime);
-    tb.appendMillis(firstEnd.getEndTime() - start, null);
-    tb.appendMillis(lastEnd.getEndTime() - start, null);
+    tb.appendMillis(firstEnd.getEndTime() - start);
+    tb.appendMillis(lastEnd.getEndTime() - start);
 
-    long total = 0;
+    long cumulativeFragmentDurationInMillis = 0L;
+    long cumulativeProcessInNanos = 0L;
+    long cumulativeWaitInNanos = 0L;
     for (final MinorFragmentProfile p : complete) {
-      total += p.getEndTime() - p.getStartTime();
+      cumulativeFragmentDurationInMillis += p.getEndTime() - p.getStartTime();
+      //Capture Busy & Wait Time
+      List<OperatorProfile> opProfileList = p.getOperatorProfileList();
+      for (OperatorProfile operatorProfile : opProfileList) {
+        cumulativeProcessInNanos += operatorProfile.getProcessNanos();
+        cumulativeWaitInNanos += operatorProfile.getWaitNanos();
+      }
     }
+    double totalProcessInMillis = Math.round(cumulativeProcessInNanos/1E6);
+    double totalWaitInMillis = Math.round(cumulativeWaitInNanos/1E6);
 
     final MinorFragmentProfile shortRun = Collections.min(complete, Comparators.runTime);
     final MinorFragmentProfile longRun = Collections.max(complete, Comparators.runTime);
-    tb.appendMillis(shortRun.getEndTime() - shortRun.getStartTime(), null);
-    tb.appendMillis(total / complete.size(), null);
-    tb.appendMillis(longRun.getEndTime() - longRun.getStartTime(), null);
+    tb.appendMillis(shortRun.getEndTime() - shortRun.getStartTime());
+    tb.appendMillis(cumulativeFragmentDurationInMillis / complete.size());
+    tb.appendMillis(longRun.getEndTime() - longRun.getStartTime());
+
+    tb.appendPercent(totalProcessInMillis / (totalProcessInMillis + totalWaitInMillis), null,
+        //#8721 is the summation sign: sum(Busy): ## + sum(Wait): ##
+        String.format("&#8721;Busy: %,.2fs + &#8721;Wait: %,.2fs", totalProcessInMillis/1E3,
totalWaitInMillis/1E3));
 
     final MinorFragmentProfile lastUpdate = Collections.max(complete, Comparators.lastUpdate);
-    tb.appendTime(lastUpdate.getLastUpdate(), null);
+    tb.appendMillis(System.currentTimeMillis()-lastUpdate.getLastUpdate());
 
     final MinorFragmentProfile lastProgress = Collections.max(complete, Comparators.lastProgress);
-    tb.appendTime(lastProgress.getLastProgress(), null);
+    tb.appendMillis(System.currentTimeMillis()-lastProgress.getLastProgress());
 
     // TODO(DRILL-3494): Names (maxMem, getMaxMemoryUsed) are misleading; the value is peak
memory allocated to fragment
     final MinorFragmentProfile maxMem = Collections.max(complete, Comparators.fragmentPeakMemory);
-    tb.appendBytes(maxMem.getMaxMemoryUsed(), null);
+    tb.appendBytes(maxMem.getMaxMemoryUsed());
+  }
+
+  public static final String[] COMPLETED_FRAGMENT_OVERVIEW_COLUMNS = {
+      OverviewTblTxt.MAJOR_FRAGMENT, OverviewTblTxt.MINOR_FRAGMENTS_REPORTING,
+      OverviewTblTxt.FIRST_START, OverviewTblTxt.LAST_START, OverviewTblTxt.FIRST_END, OverviewTblTxt.LAST_END,
+      OverviewTblTxt.MIN_RUNTIME, OverviewTblTxt.AVG_RUNTIME, OverviewTblTxt.MAX_RUNTIME,
+      OverviewTblTxt.PERCENT_BUSY, OverviewTblTxt.MAX_PEAK_MEMORY
+  };
+
+  public static final String[] COMPLETED_FRAGMENT_OVERVIEW_COLUMNS_TOOLTIP = {
+      OverviewTblTooltip.MAJOR_FRAGMENT, OverviewTblTooltip.MINOR_FRAGMENTS_REPORTING,
+      OverviewTblTooltip.FIRST_START, OverviewTblTooltip.LAST_START, OverviewTblTooltip.FIRST_END,
OverviewTblTooltip.LAST_END,
+      OverviewTblTooltip.MIN_RUNTIME, OverviewTblTooltip.AVG_RUNTIME, OverviewTblTooltip.MAX_RUNTIME,
+      OverviewTblTooltip.PERCENT_BUSY, OverviewTblTooltip.MAX_PEAK_MEMORY
+  };
+
+  //Not including Major Fragment ID and Minor Fragments Reporting
+  public static final int NUM_NULLABLE_COMPLETED_OVERVIEW_COLUMNS = COMPLETED_FRAGMENT_OVERVIEW_COLUMNS.length
- 2;
+
+  public void addFinalSummary(TableBuilder tb) {
+
+    // Use only minor fragments that have complete profiles
+    // Complete iff the fragment profile has at least one operator profile, and start and
end times.
+    final List<MinorFragmentProfile> complete = new ArrayList<>(
+        Collections2.filter(major.getMinorFragmentProfileList(), Filters.hasOperatorsAndTimes));
+
+    tb.appendCell(new OperatorPathBuilder().setMajor(major).build());
+    tb.appendCell(complete.size() + " / " + major.getMinorFragmentProfileCount());
+
+    // If there are no stats to aggregate, create an empty row
+    if (complete.size() < 1) {
+      tb.appendRepeated("", null, NUM_NULLABLE_COMPLETED_OVERVIEW_COLUMNS);
+      return;
+    }
+
+    final MinorFragmentProfile firstStart = Collections.min(complete, Comparators.startTime);
+    final MinorFragmentProfile lastStart = Collections.max(complete, Comparators.startTime);
+    tb.appendMillis(firstStart.getStartTime() - start);
+    tb.appendMillis(lastStart.getStartTime() - start);
+
+    final MinorFragmentProfile firstEnd = Collections.min(complete, Comparators.endTime);
+    final MinorFragmentProfile lastEnd = Collections.max(complete, Comparators.endTime);
+    tb.appendMillis(firstEnd.getEndTime() - start);
+    tb.appendMillis(lastEnd.getEndTime() - start);
+
+    long totalDuration = 0L;
+    double totalProcessInMillis = 0.0d;
+    double totalWaitInMillis = 0.0d;
+    for (final MinorFragmentProfile p : complete) {
+      totalDuration += p.getEndTime() - p.getStartTime();
+      //Capture Busy & Wait Time
+      List<OperatorProfile> opProfileList = p.getOperatorProfileList();
+      for (OperatorProfile operatorProfile : opProfileList) {
+        totalProcessInMillis += operatorProfile.getProcessNanos()/1E6;
+        totalWaitInMillis += operatorProfile.getWaitNanos()/1E6;
+      }
+    }
+
+    final MinorFragmentProfile shortRun = Collections.min(complete, Comparators.runTime);
+    final MinorFragmentProfile longRun = Collections.max(complete, Comparators.runTime);
+    tb.appendMillis(shortRun.getEndTime() - shortRun.getStartTime());
+    tb.appendMillis(totalDuration / complete.size());
+    tb.appendMillis(longRun.getEndTime() - longRun.getStartTime());
+
+    tb.appendPercent(totalProcessInMillis / (totalProcessInMillis + totalWaitInMillis), null,
+        //#8721 is the summation sign: sum(Busy): ## + sum(Wait): ##
+        String.format("&#8721;Busy: %,.2fs + &#8721;Wait: %,.2fs", totalProcessInMillis/1E3,
totalWaitInMillis/1E3));
+
+    // TODO(DRILL-3494): Names (maxMem, getMaxMemoryUsed) are misleading; the value is peak
memory allocated to fragment
+    final MinorFragmentProfile maxMem = Collections.max(complete, Comparators.fragmentPeakMemory);
+    tb.appendBytes(maxMem.getMaxMemoryUsed());
   }
 
   public static final String[] FRAGMENT_COLUMNS = {"Minor Fragment ID", "Host Name", "Start",
"End",
-    "Runtime", "Max Records", "Max Batches", "Last Update", "Last Progress", "Peak Memory",
"State"};
+      "Runtime", "Max Records", "Max Batches", "Last Update", "Last Progress", "Peak Memory",
"State"};
 
   // Not including minor fragment ID
   private static final int NUM_NULLABLE_FRAGMENTS_COLUMNS = FRAGMENT_COLUMNS.length - 1;
 
   public String getContent() {
-    final TableBuilder builder = new TableBuilder(FRAGMENT_COLUMNS);
+    final TableBuilder builder = new TableBuilder(FRAGMENT_COLUMNS, null);
 
     // Use only minor fragments that have complete profiles
     // Complete iff the fragment profile has at least one operator profile, and start and
end times.
     final List<MinorFragmentProfile> complete = new ArrayList<>(
-      Collections2.filter(major.getMinorFragmentProfileList(), Filters.hasOperatorsAndTimes));
+        Collections2.filter(major.getMinorFragmentProfileList(), Filters.hasOperatorsAndTimes));
     final List<MinorFragmentProfile> incomplete = new ArrayList<>(
-      Collections2.filter(major.getMinorFragmentProfileList(), Filters.missingOperatorsOrTimes));
+        Collections2.filter(major.getMinorFragmentProfileList(), Filters.missingOperatorsOrTimes));
 
     Collections.sort(complete, Comparators.minorId);
     for (final MinorFragmentProfile minor : complete) {
@@ -136,26 +238,60 @@ public class FragmentWrapper {
         biggestBatches = Math.max(biggestBatches, batches);
       }
 
-      builder.appendCell(new OperatorPathBuilder().setMajor(major).setMinor(minor).build(),
null);
-      builder.appendCell(minor.getEndpoint().getAddress(), null);
-      builder.appendMillis(minor.getStartTime() - start, null);
-      builder.appendMillis(minor.getEndTime() - start, null);
-      builder.appendMillis(minor.getEndTime() - minor.getStartTime(), null);
+      builder.appendCell(new OperatorPathBuilder().setMajor(major).setMinor(minor).build());
+      builder.appendCell(minor.getEndpoint().getAddress());
+      builder.appendMillis(minor.getStartTime() - start);
+      builder.appendMillis(minor.getEndTime() - start);
+      builder.appendMillis(minor.getEndTime() - minor.getStartTime());
 
-      builder.appendFormattedInteger(biggestIncomingRecords, null);
-      builder.appendFormattedInteger(biggestBatches, null);
+      builder.appendFormattedInteger(biggestIncomingRecords);
+      builder.appendFormattedInteger(biggestBatches);
 
-      builder.appendTime(minor.getLastUpdate(), null);
-      builder.appendTime(minor.getLastProgress(), null);
+      builder.appendTime(minor.getLastUpdate());
+      builder.appendTime(minor.getLastProgress());
 
-      builder.appendBytes(minor.getMaxMemoryUsed(), null);
-      builder.appendCell(minor.getState().name(), null);
+      builder.appendBytes(minor.getMaxMemoryUsed());
+      builder.appendCell(minor.getState().name());
     }
 
     for (final MinorFragmentProfile m : incomplete) {
-      builder.appendCell(major.getMajorFragmentId() + "-" + m.getMinorFragmentId(), null);
+      builder.appendCell(major.getMajorFragmentId() + "-" + m.getMinorFragmentId());
       builder.appendRepeated(m.getState().toString(), null, NUM_NULLABLE_FRAGMENTS_COLUMNS);
     }
     return builder.build();
   }
+
+  private class OverviewTblTxt {
+    static final String MAJOR_FRAGMENT = "Major Fragment";
+    static final String MINOR_FRAGMENTS_REPORTING = "Minor Fragments Reporting";
+    static final String FIRST_START = "First Start";
+    static final String LAST_START = "Last Start";
+    static final String FIRST_END = "First End";
+    static final String LAST_END = "Last End";
+    static final String MIN_RUNTIME = "Min Runtime";
+    static final String AVG_RUNTIME = "Avg Runtime";
+    static final String MAX_RUNTIME = "Max Runtime";
+    static final String PERCENT_BUSY = "% Busy";
+    static final String LAST_UPDATE = "Last Update";
+    static final String LAST_PROGRESS = "Last Progress";
+    static final String MAX_PEAK_MEMORY = "Max Peak Memory";
+  }
+
+  private class OverviewTblTooltip {
+    static final String MAJOR_FRAGMENT = "Major fragment ID seen in the visual plan";
+    static final String MINOR_FRAGMENTS_REPORTING = "Number of minor fragments started";
+    static final String FIRST_START = "Time at which the first fragment started";
+    static final String LAST_START = "Time at which the last fragment started";
+    static final String FIRST_END = "Time at which the first fragment completed";
+    static final String LAST_END = "Time at which the last fragment completed";
+    static final String MIN_RUNTIME = "Shortest fragment runtime";
+    static final String AVG_RUNTIME = "Average fragment runtime";
+    static final String MAX_RUNTIME = "Longest fragment runtime";
+    static final String PERCENT_BUSY = "Percentage of run time that fragments were busy doing
work";
+    static final String LAST_UPDATE = "Time since most recent heartbeat from a fragment";
+    static final String LAST_PROGRESS = "Time since most recent update from a fragment";
+    static final String MAX_PEAK_MEMORY = "Highest memory consumption by a fragment";
+  }
 }
+
+

http://git-wip-us.apache.org/repos/asf/drill/blob/8614bae5/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/OperatorWrapper.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/OperatorWrapper.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/OperatorWrapper.java
index 5d18911..5809e25 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/OperatorWrapper.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/OperatorWrapper.java
@@ -18,6 +18,7 @@
 package org.apache.drill.exec.server.rest.profile;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
@@ -34,6 +35,7 @@ import org.apache.drill.exec.proto.UserBitShared.StreamProfile;
  * Wrapper class for profiles of ALL operator instances of the same operator type within
a major fragment.
  */
 public class OperatorWrapper {
+  private static final String UNKNOWN_OPERATOR = "UNKNOWN_OPERATOR";
   private final int major;
   private final List<ImmutablePair<OperatorProfile, Integer>> ops; // operator
profile --> minor fragment number
   private final OperatorProfile firstProfile;
@@ -46,7 +48,7 @@ public class OperatorWrapper {
     this.major = major;
     firstProfile = ops.get(0).getLeft();
     operatorType = CoreOperatorType.valueOf(firstProfile.getOperatorType());
-    operatorName = operatorType == null ? "UNKNOWN_OPERATOR" : operatorType.toString();
+    operatorName = operatorType == null ? UNKNOWN_OPERATOR : operatorType.toString();
     this.ops = ops;
     size = ops.size();
   }
@@ -61,20 +63,20 @@ public class OperatorWrapper {
   }
 
   public static final String [] OPERATOR_COLUMNS = {"Minor Fragment", "Setup Time", "Process
Time", "Wait Time",
-    "Max Batches", "Max Records", "Peak Memory"};
+      "Max Batches", "Max Records", "Peak Memory"};
 
   public String getContent() {
-    TableBuilder builder = new TableBuilder(OPERATOR_COLUMNS);
+    TableBuilder builder = new TableBuilder(OPERATOR_COLUMNS, null);
 
     for (ImmutablePair<OperatorProfile, Integer> ip : ops) {
       int minor = ip.getRight();
       OperatorProfile op = ip.getLeft();
 
       String path = new OperatorPathBuilder().setMajor(major).setMinor(minor).setOperator(op).build();
-      builder.appendCell(path, null);
-      builder.appendNanos(op.getSetupNanos(), null);
-      builder.appendNanos(op.getProcessNanos(), null);
-      builder.appendNanos(op.getWaitNanos(), null);
+      builder.appendCell(path);
+      builder.appendNanos(op.getSetupNanos());
+      builder.appendNanos(op.getProcessNanos());
+      builder.appendNanos(op.getWaitNanos());
 
       long maxBatches = Long.MIN_VALUE;
       long maxRecords = Long.MIN_VALUE;
@@ -83,56 +85,82 @@ public class OperatorWrapper {
         maxRecords = Math.max(sp.getRecords(), maxRecords);
       }
 
-      builder.appendFormattedInteger(maxBatches, null);
-      builder.appendFormattedInteger(maxRecords, null);
-      builder.appendBytes(op.getPeakLocalMemoryAllocated(), null);
+      builder.appendFormattedInteger(maxBatches);
+      builder.appendFormattedInteger(maxRecords);
+      builder.appendBytes(op.getPeakLocalMemoryAllocated());
     }
     return builder.build();
   }
 
-  public static final String[] OPERATORS_OVERVIEW_COLUMNS = {"Operator ID", "Type", "Min
Setup Time", "Avg Setup Time",
-    "Max Setup Time", "Min Process Time", "Avg Process Time", "Max Process Time", "Min Wait
Time", "Avg Wait Time",
-    "Max Wait Time", "Avg Peak Memory", "Max Peak Memory"};
-
-  public void addSummary(TableBuilder tb) {
-
+  public static final String[] OPERATORS_OVERVIEW_COLUMNS = {
+      OverviewTblTxt.OPERATOR_ID, OverviewTblTxt.TYPE_OF_OPERATOR,
+      OverviewTblTxt.AVG_SETUP_TIME, OverviewTblTxt.MAX_SETUP_TIME,
+      OverviewTblTxt.AVG_PROCESS_TIME, OverviewTblTxt.MAX_PROCESS_TIME,
+      OverviewTblTxt.MIN_WAIT_TIME, OverviewTblTxt.AVG_WAIT_TIME, OverviewTblTxt.MAX_WAIT_TIME,
+      OverviewTblTxt.PERCENT_FRAGMENT_TIME, OverviewTblTxt.PERCENT_QUERY_TIME, OverviewTblTxt.ROWS,
+      OverviewTblTxt.AVG_PEAK_MEMORY, OverviewTblTxt.MAX_PEAK_MEMORY
+  };
+
+  public static final String[] OPERATORS_OVERVIEW_COLUMNS_TOOLTIP = {
+      OverviewTblTooltip.OPERATOR_ID, OverviewTblTooltip.TYPE_OF_OPERATOR,
+      OverviewTblTooltip.AVG_SETUP_TIME, OverviewTblTooltip.MAX_SETUP_TIME,
+      OverviewTblTooltip.AVG_PROCESS_TIME, OverviewTblTooltip.MAX_PROCESS_TIME,
+      OverviewTblTooltip.MIN_WAIT_TIME, OverviewTblTooltip.AVG_WAIT_TIME, OverviewTblTooltip.MAX_WAIT_TIME,
+      OverviewTblTooltip.PERCENT_FRAGMENT_TIME, OverviewTblTooltip.PERCENT_QUERY_TIME, OverviewTblTooltip.ROWS,
+      OverviewTblTooltip.AVG_PEAK_MEMORY, OverviewTblTooltip.MAX_PEAK_MEMORY
+  };
+
+  //Palette to help shade operators sharing a common major fragment
+  private static final String[] OPERATOR_OVERVIEW_BGCOLOR_PALETTE = {"#ffffff","#f2f2f2"};
+
+  public void addSummary(TableBuilder tb, HashMap<String, Long> majorFragmentBusyTally,
long majorFragmentBusyTallyTotal) {
+    //Select background color from palette
+    String opTblBgColor = OPERATOR_OVERVIEW_BGCOLOR_PALETTE[major%OPERATOR_OVERVIEW_BGCOLOR_PALETTE.length];
     String path = new OperatorPathBuilder().setMajor(major).setOperator(firstProfile).build();
-    tb.appendCell(path, null);
-    tb.appendCell(operatorName, null);
+    tb.appendCell(path, null, null, opTblBgColor);
+    tb.appendCell(operatorName);
+
+    //Get MajorFragment Busy+Wait Time Tally
+    long majorBusyNanos = majorFragmentBusyTally.get(new OperatorPathBuilder().setMajor(major).build());
 
     double setupSum = 0.0;
     double processSum = 0.0;
     double waitSum = 0.0;
     double memSum = 0.0;
+    long recordSum = 0L;
     for (ImmutablePair<OperatorProfile, Integer> ip : ops) {
       OperatorProfile profile = ip.getLeft();
       setupSum += profile.getSetupNanos();
       processSum += profile.getProcessNanos();
       waitSum += profile.getWaitNanos();
       memSum += profile.getPeakLocalMemoryAllocated();
+      for (final StreamProfile sp : profile.getInputProfileList()) {
+        recordSum += sp.getRecords();
+      }
     }
 
-    final ImmutablePair<OperatorProfile, Integer> shortSetup = Collections.min(ops,
Comparators.setupTime);
     final ImmutablePair<OperatorProfile, Integer> longSetup = Collections.max(ops,
Comparators.setupTime);
-    tb.appendNanos(shortSetup.getLeft().getSetupNanos(), null);
-    tb.appendNanos(Math.round(setupSum / size), null);
-    tb.appendNanos(longSetup.getLeft().getSetupNanos(), null);
+    tb.appendNanos(Math.round(setupSum / size));
+    tb.appendNanos(longSetup.getLeft().getSetupNanos());
 
-    final ImmutablePair<OperatorProfile, Integer> shortProcess = Collections.min(ops,
Comparators.processTime);
     final ImmutablePair<OperatorProfile, Integer> longProcess = Collections.max(ops,
Comparators.processTime);
-    tb.appendNanos(shortProcess.getLeft().getProcessNanos(), null);
-    tb.appendNanos(Math.round(processSum / size), null);
-    tb.appendNanos(longProcess.getLeft().getProcessNanos(), null);
+    tb.appendNanos(Math.round(processSum / size));
+    tb.appendNanos(longProcess.getLeft().getProcessNanos());
 
     final ImmutablePair<OperatorProfile, Integer> shortWait = Collections.min(ops,
Comparators.waitTime);
     final ImmutablePair<OperatorProfile, Integer> longWait = Collections.max(ops, Comparators.waitTime);
-    tb.appendNanos(shortWait.getLeft().getWaitNanos(), null);
-    tb.appendNanos(Math.round(waitSum / size), null);
-    tb.appendNanos(longWait.getLeft().getWaitNanos(), null);
+    tb.appendNanos(shortWait.getLeft().getWaitNanos());
+    tb.appendNanos(Math.round(waitSum / size));
+    tb.appendNanos(longWait.getLeft().getWaitNanos());
+
+    tb.appendPercent(processSum / majorBusyNanos);
+    tb.appendPercent(processSum / majorFragmentBusyTallyTotal);
+
+    tb.appendFormattedInteger(recordSum);
 
     final ImmutablePair<OperatorProfile, Integer> peakMem = Collections.max(ops, Comparators.operatorPeakMemory);
-    tb.appendBytes(Math.round(memSum / size), null);
-    tb.appendBytes(peakMem.getLeft().getPeakLocalMemoryAllocated(), null);
+    tb.appendBytes(Math.round(memSum / size));
+    tb.appendBytes(peakMem.getLeft().getPeakLocalMemoryAllocated());
   }
 
   public String getMetricsTable() {
@@ -150,17 +178,17 @@ public class OperatorWrapper {
     for (final String metricName : metricNames) {
       metricsTableColumnNames[i++] = metricName;
     }
-    final TableBuilder builder = new TableBuilder(metricsTableColumnNames);
+    final TableBuilder builder = new TableBuilder(metricsTableColumnNames, null);
+
     for (final ImmutablePair<OperatorProfile, Integer> ip : ops) {
       final OperatorProfile op = ip.getLeft();
 
       builder.appendCell(
           new OperatorPathBuilder()
-              .setMajor(major)
-              .setMinor(ip.getRight())
-              .setOperator(op)
-              .build(),
-          null);
+          .setMajor(major)
+          .setMinor(ip.getRight())
+          .setOperator(op)
+          .build());
 
       final Number[] values = new Number[metricNames.length];
       //Track new/Unknown Metrics
@@ -179,12 +207,47 @@ public class OperatorWrapper {
       }
       for (final Number value : values) {
         if (value != null) {
-          builder.appendFormattedNumber(value, null);
+          builder.appendFormattedNumber(value);
         } else {
-          builder.appendCell("", null);
+          builder.appendCell("");
         }
       }
     }
     return builder.build();
   }
+
+  private class OverviewTblTxt {
+    static final String OPERATOR_ID = "Operator ID";
+    static final String TYPE_OF_OPERATOR = "Type";
+    static final String AVG_SETUP_TIME = "Avg Setup Time";
+    static final String MAX_SETUP_TIME = "Max Setup Time";
+    static final String AVG_PROCESS_TIME = "Avg Process Time";
+    static final String MAX_PROCESS_TIME = "Max Process Time";
+    static final String MIN_WAIT_TIME = "Min Wait Time";
+    static final String AVG_WAIT_TIME = "Avg Wait Time";
+    static final String MAX_WAIT_TIME = "Max Wait Time";
+    static final String PERCENT_FRAGMENT_TIME = "% Fragment Time";
+    static final String PERCENT_QUERY_TIME = "% Query Time";
+    static final String ROWS = "Rows";
+    static final String AVG_PEAK_MEMORY = "Avg Peak Memory";
+    static final String MAX_PEAK_MEMORY = "Max Peak Memory";
+  }
+
+  private class OverviewTblTooltip {
+    static final String OPERATOR_ID = "Operator ID";
+    static final String TYPE_OF_OPERATOR = "Operator Type";
+    static final String AVG_SETUP_TIME = "Average time in setting up fragments";
+    static final String MAX_SETUP_TIME = "Longest time a fragment took in setup";
+    static final String AVG_PROCESS_TIME = "Average process time for a fragment";
+    static final String MAX_PROCESS_TIME = "Longest process time of any fragment";
+    static final String MIN_WAIT_TIME = "Shortest time a fragment spent in waiting";
+    static final String AVG_WAIT_TIME = "Average wait time for a fragment";
+    static final String MAX_WAIT_TIME = "Longest time a fragment spent in waiting";
+    static final String PERCENT_FRAGMENT_TIME = "Percentage of the total fragment time that
was spent on the operator";
+    static final String PERCENT_QUERY_TIME = "Percentage of the total query time that was
spent on the operator";
+    static final String ROWS = "Rows emitted by scans, or consumed by other operators";
+    static final String AVG_PEAK_MEMORY  =  "Average memory consumption by a fragment";
+    static final String MAX_PEAK_MEMORY  =  "Highest memory consumption by a fragment";
+  }
 }
+

http://git-wip-us.apache.org/repos/asf/drill/blob/8614bae5/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java
index cb80f76..fdeb1d4 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java
@@ -20,6 +20,7 @@ package org.apache.drill.exec.server.rest.profile;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -52,6 +53,8 @@ public class ProfileWrapper {
   private final List<FragmentWrapper> fragmentProfiles;
   private final List<OperatorWrapper> operatorProfiles;
   private OptionList options;
+  private final HashMap<String, Long> majorFragmentTallyMap;
+  private long majorFragmentTallyTotal;
 
   public ProfileWrapper(final QueryProfile profile) {
     this.profile = profile;
@@ -66,6 +69,8 @@ public class ProfileWrapper {
       fragmentProfiles.add(new FragmentWrapper(major, profile.getStart()));
     }
     this.fragmentProfiles = fragmentProfiles;
+    majorFragmentTallyMap = new HashMap<String, Long>(majors.size());
+    this.majorFragmentTallyTotal = tallyMajorFragmentCost(majors);
 
     final List<OperatorWrapper> ows = new ArrayList<>();
     // temporary map to store (major_id, operator_id) -> [(op_profile, minor_id)]
@@ -109,6 +114,22 @@ public class ProfileWrapper {
     }
   }
 
+  private long tallyMajorFragmentCost(List<MajorFragmentProfile> majorFragments) {
+    long globalProcessNanos = 0L;
+    for (MajorFragmentProfile majorFP : majorFragments) {
+      String majorFragmentId = new OperatorPathBuilder().setMajor(majorFP).build();
+      long processNanos = 0L;
+      for (MinorFragmentProfile minorFP : majorFP.getMinorFragmentProfileList()) {
+        for (OperatorProfile op : minorFP.getOperatorProfileList()) {
+          processNanos += op.getProcessNanos();
+        }
+      }
+      majorFragmentTallyMap.put(majorFragmentId, processNanos);
+      globalProcessNanos += processNanos;
+    }
+    return globalProcessNanos;
+  }
+
   public boolean hasError() {
     return profile.hasError() && profile.getError() != null;
   }
@@ -213,9 +234,18 @@ public class ProfileWrapper {
   }
 
   public String getFragmentsOverview() {
-    TableBuilder tb = new TableBuilder(FragmentWrapper.FRAGMENT_OVERVIEW_COLUMNS);
-    for (final FragmentWrapper fw : fragmentProfiles) {
-      fw.addSummary(tb);
+    TableBuilder tb;
+    if (profile.getState() == QueryState.STARTING
+        || profile.getState() == QueryState.RUNNING) {
+      tb = new TableBuilder(FragmentWrapper.ACTIVE_FRAGMENT_OVERVIEW_COLUMNS, FragmentWrapper.ACTIVE_FRAGMENT_OVERVIEW_COLUMNS_TOOLTIP);
+      for (final FragmentWrapper fw : fragmentProfiles) {
+        fw.addSummary(tb);
+      }
+    } else {
+      tb = new TableBuilder(FragmentWrapper.COMPLETED_FRAGMENT_OVERVIEW_COLUMNS, FragmentWrapper.COMPLETED_FRAGMENT_OVERVIEW_COLUMNS_TOOLTIP);
+      for (final FragmentWrapper fw : fragmentProfiles) {
+        fw.addFinalSummary(tb);
+      }
     }
     return tb.build();
   }
@@ -225,9 +255,11 @@ public class ProfileWrapper {
   }
 
   public String getOperatorsOverview() {
-    final TableBuilder tb = new TableBuilder(OperatorWrapper.OPERATORS_OVERVIEW_COLUMNS);
+    final TableBuilder tb = new TableBuilder(OperatorWrapper.OPERATORS_OVERVIEW_COLUMNS,
+        OperatorWrapper.OPERATORS_OVERVIEW_COLUMNS_TOOLTIP);
+
     for (final OperatorWrapper ow : operatorProfiles) {
-      ow.addSummary(tb);
+      ow.addSummary(tb, this.majorFragmentTallyMap, this.majorFragmentTallyTotal);
     }
     return tb.build();
   }

http://git-wip-us.apache.org/repos/asf/drill/blob/8614bae5/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/TableBuilder.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/TableBuilder.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/TableBuilder.java
index 7893be4..07598c5 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/TableBuilder.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/TableBuilder.java
@@ -33,24 +33,50 @@ public class TableBuilder {
   private int w = 0;
   private int width;
 
-  public TableBuilder(final String[] columns) {
+  public TableBuilder(final String[] columns, final String[] columnTooltip) {
     sb = new StringBuilder();
     width = columns.length;
 
     format.setMaximumFractionDigits(3);
 
     sb.append("<table class=\"table table-bordered text-right\">\n<tr>");
-    for (final String cn : columns) {
-      sb.append("<th>" + cn + "</th>");
+    for (int i = 0; i < columns.length; i++) {
+      String cn = columns[i];
+      String ctt = "";
+      if (columnTooltip != null) {
+        String tooltip = columnTooltip[i];
+        if (tooltip != null) {
+          ctt = " title=\""+tooltip+"\"";
+        }
+      }
+      sb.append("<th" + ctt + ">" + cn + "</th>");
     }
     sb.append("</tr>\n");
   }
 
+  public void appendCell(final String s) {
+    appendCell(s, null, null, null);
+  }
+
   public void appendCell(final String s, final String link) {
+    appendCell(s, link, null, null);
+  }
+
+  public void appendCell(final String s, final String link, final String titleText) {
+    appendCell(s, link, titleText, null);
+  }
+
+  public void appendCell(final String s, final String link, final String titleText, final
String backgroundColor) {
     if (w == 0) {
-      sb.append("<tr>");
+      sb.append("<tr"
+          + (backgroundColor == null ? "" : " style=\"background-color:"+backgroundColor+"\"")
+          + ">");
+    }
+    if (titleText != null && titleText.length() > 0) {
+      sb.append(String.format("<td title=\""+titleText+"\">%s%s</td>", s, link
!= null ? link : ""));
+    } else {
+      sb.append(String.format("<td>%s%s</td>", s, link != null ? link : ""));
     }
-    sb.append(String.format("<td>%s%s</td>", s, link != null ? link : ""));
     if (++w >= width) {
       sb.append("</tr>\n");
       w = 0;
@@ -58,37 +84,101 @@ public class TableBuilder {
   }
 
   public void appendRepeated(final String s, final String link, final int n) {
+    appendRepeated(s, link, n, null);
+  }
+
+  public void appendRepeated(final String s, final String link, final int n, final String
tooltip) {
     for (int i = 0; i < n; i++) {
-      appendCell(s, link);
+      appendCell(s, link, tooltip);
     }
   }
 
+  public void appendTime(final long d) {
+    appendCell(dateFormat.format(d), null, null);
+  }
+
   public void appendTime(final long d, final String link) {
-    appendCell(dateFormat.format(d), link);
+    appendCell(dateFormat.format(d), link, null);
+  }
+
+  public void appendTime(final long d, final String link, final String tooltip) {
+    appendCell(dateFormat.format(d), link, tooltip);
+  }
+
+  public void appendMillis(final long p) {
+    appendCell((new SimpleDurationFormat(0, p)).compact(), null, null);
   }
 
   public void appendMillis(final long p, final String link) {
-    appendCell((new SimpleDurationFormat(0, p)).compact(), link);
+    appendCell((new SimpleDurationFormat(0, p)).compact(), link, null);
+  }
+
+  public void appendMillis(final long p, final String link, final String tooltip) {
+    appendCell((new SimpleDurationFormat(0, p)).compact(), link, tooltip);
+  }
+
+  public void appendNanos(final long p) {
+    appendNanos(p, null, null);
   }
 
   public void appendNanos(final long p, final String link) {
-    appendMillis(Math.round(p / 1000.0 / 1000.0), link);
+    appendNanos(p, link, null);
+  }
+
+  public void appendNanos(final long p, final String link, final String tooltip) {
+    appendMillis(Math.round(p / 1000.0 / 1000.0), link, tooltip);
+  }
+
+  public void appendPercent(final double percentAsFraction) {
+    appendCell(dec.format(100*percentAsFraction).concat("%"), null, null);
+  }
+
+  public void appendPercent(final double percentAsFraction, final String link) {
+    appendCell(dec.format(100*percentAsFraction).concat("%"), link, null);
+  }
+
+  public void appendPercent(final double percentAsFraction, final String link, final String
tooltip) {
+    appendCell(dec.format(100*percentAsFraction).concat("%"), link, tooltip);
+  }
+
+  public void appendFormattedNumber(final Number n) {
+    appendCell(format.format(n), null, null);
   }
 
   public void appendFormattedNumber(final Number n, final String link) {
-    appendCell(format.format(n), link);
+    appendCell(format.format(n), link, null);
+  }
+
+  public void appendFormattedNumber(final Number n, final String link, final String tooltip)
{
+    appendCell(format.format(n), link, tooltip);
+  }
+
+  public void appendFormattedInteger(final long n) {
+    appendCell(intformat.format(n), null, null);
   }
 
   public void appendFormattedInteger(final long n, final String link) {
-    appendCell(intformat.format(n), link);
+    appendCell(intformat.format(n), link, null);
+  }
+
+  public void appendFormattedInteger(final long n, final String link, final String tooltip)
{
+    appendCell(intformat.format(n), link, tooltip);
+  }
+
+  public void appendInteger(final long l, final String link, final String tooltip) {
+    appendCell(Long.toString(l), link, tooltip);
+  }
+
+  public void appendBytes(final long l) {
+    appendCell(bytePrint(l), null, null);
   }
 
-  public void appendInteger(final long l, final String link) {
-    appendCell(Long.toString(l), link);
+  public void appendBytes(final long l, final String link) {
+    appendCell(bytePrint(l), link, null);
   }
 
-  public void appendBytes(final long l, final String link){
-    appendCell(bytePrint(l), link);
+  public void appendBytes(final long l, final String link, final String tooltip) {
+    appendCell(bytePrint(l), link, tooltip);
   }
 
   private String bytePrint(final long size) {

http://git-wip-us.apache.org/repos/asf/drill/blob/8614bae5/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 3956195..95d7d56 100644
--- a/exec/java-exec/src/main/resources/rest/profile/profile.ftl
+++ b/exec/java-exec/src/main/resources/rest/profile/profile.ftl
@@ -84,7 +84,7 @@
       </p>
       <p>Failure node: ${model.getProfile().errorNode}</p>
       <p>Error ID: ${model.getProfile().errorId}</p>
-      
+
         <h3 class="panel-title">
           <a data-toggle="collapse" href="#error-verbose">
             Verbose Error Message...
@@ -107,9 +107,9 @@
   <p>FOREMAN: ${model.getProfile().getForeman().getAddress()}</p>
   <p>TOTAL FRAGMENTS: ${model.getProfile().getTotalFragments()}</p>
   <p>DURATION: ${model.getProfileDuration()}</p>
-  <p>&nbsp;&nbsp;&nbsp;&nbsp;PLANNING: ${model.getPlanningDuration()}</p>
-  <p>&nbsp;&nbsp;&nbsp;&nbsp;QUEUED: ${model.getQueuedDuration()}</p>
-  <p>&nbsp;&nbsp;&nbsp;&nbsp;EXECUTION: ${model.getExecutionDuration()}</p>
+  <p style="text-indent:5em;">PLANNING: ${model.getPlanningDuration()}</p>
+  <p style="text-indent:5em;">QUEUED: ${model.getQueuedDuration()}</p>
+  <p style="text-indent:5em;">EXECUTION: ${model.getExecutionDuration()}</p>
 
   <#assign options = model.getOptions()>
   <#if (options?keys?size > 0)>


Mime
View raw message