Make it easier to identify trace by adding text below the graph

Change-Id: I3742a4226d6fec0556dc6717fafe4c119c638216
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/769118
Reviewed-by: Joe Gregorio <jcgregorio@google.com>
Commit-Queue: Ashwin Verleker <ashwinpv@google.com>
diff --git a/perf/configs/chrome-perf-non-public.json b/perf/configs/chrome-perf-non-public.json
index 0b807c3..e639ef4 100644
--- a/perf/configs/chrome-perf-non-public.json
+++ b/perf/configs/chrome-perf-non-public.json
@@ -62,5 +62,6 @@
             "summary": "true"
         }
     },
-    "help_url_override": "http://go/chrome-perf-user-doc"
+    "help_url_override": "http://go/chrome-perf-user-doc",
+    "trace_format": "chrome"
 }
\ No newline at end of file
diff --git a/perf/configs/chrome-perf-public.json b/perf/configs/chrome-perf-public.json
index 7864614..e8c9b68 100644
--- a/perf/configs/chrome-perf-public.json
+++ b/perf/configs/chrome-perf-public.json
@@ -83,5 +83,6 @@
         "default_url_values": {
             "summary": "true"
         }
-    }
+    },
+    "trace_format": "chrome"
 }
\ No newline at end of file
diff --git a/perf/go/config/config.go b/perf/go/config/config.go
index dbcbf63..fe079e7 100644
--- a/perf/go/config/config.go
+++ b/perf/go/config/config.go
@@ -317,6 +317,19 @@
 	CommitNumberRegex string `json:"commit_number_regex,omitempty"`
 }
 
+// TraceFormat is the format used to display trace info on the instance.
+type TraceFormat string
+
+const (
+	ChromeTraceFormat  TraceFormat = "chrome"
+	DefaultTraceFormat TraceFormat = ""
+)
+
+var AllTraceFormats []TraceFormat = []TraceFormat{
+	ChromeTraceFormat,
+	DefaultTraceFormat,
+}
+
 // DurationAsString allows serializing a Duration as a string, and also handles
 // deserializing the empty string.
 type DurationAsString time.Duration
@@ -671,6 +684,10 @@
 	// which percentage of traces get uploaded
 	TraceSampleProportion float32 `json:"trace_sample_proportion,omitempty"`
 
+	// TraceFormat is string that specifies the format to use to display
+	// trace information for the instance.
+	TraceFormat TraceFormat `json:"trace_format,omitempty"`
+
 	AuthConfig      AuthConfig      `json:"auth_config,omitempty"`
 	DataStoreConfig DataStoreConfig `json:"data_store_config"`
 	IngestionConfig IngestionConfig `json:"ingestion_config"`
diff --git a/perf/go/config/validate/instanceConfigSchema.json b/perf/go/config/validate/instanceConfigSchema.json
index fe8406f..56247dc 100644
--- a/perf/go/config/validate/instanceConfigSchema.json
+++ b/perf/go/config/validate/instanceConfigSchema.json
@@ -230,6 +230,9 @@
         "trace_sample_proportion": {
           "type": "number"
         },
+        "trace_format": {
+          "type": "string"
+        },
         "auth_config": {
           "$ref": "#/$defs/AuthConfig"
         },
diff --git a/perf/go/frontend/frontend.go b/perf/go/frontend/frontend.go
index faabaae..722829c 100644
--- a/perf/go/frontend/frontend.go
+++ b/perf/go/frontend/frontend.go
@@ -217,20 +217,21 @@
 // SkPerfConfig is the configuration data that will appear
 // in Javascript under the window.perf variable.
 type SkPerfConfig struct {
-	Radius                     int              `json:"radius"`                          // The number of commits when doing clustering.
-	KeyOrder                   []string         `json:"key_order"`                       // The order of the keys to appear first in query-sk elements.
-	NumShift                   int              `json:"num_shift"`                       // The number of commits the shift navigation buttons should jump.
-	Interesting                float32          `json:"interesting"`                     // The threshold for a cluster to be interesting.
-	StepUpOnly                 bool             `json:"step_up_only"`                    // If true then only regressions that are a step up are displayed.
-	CommitRangeURL             string           `json:"commit_range_url"`                // A URI Template to be used for expanding details on a range of commits. See cluster-summary2-sk.
-	Demo                       bool             `json:"demo"`                            // True if this is a demo page, as opposed to being in production. Used to make puppeteer tests deterministic.
-	DisplayGroupBy             bool             `json:"display_group_by"`                // True if the Group By section of Alert config should be displayed.
-	HideListOfCommitsOnExplore bool             `json:"hide_list_of_commits_on_explore"` // True if the commit-detail-panel-sk element on the Explore details tab should be hidden.
-	Notifications              notifytypes.Type `json:"notifications"`                   // The type of notifications that can be sent.
-	FetchChromePerfAnomalies   bool             `json:"fetch_chrome_perf_anomalies"`     // If true explore-sk will show the bisect button
-	FeedbackURL                string           `json:"feedback_url"`                    // The URL for the Provide Feedback link
-	ChatURL                    string           `json:"chat_url"`                        // The URL for the Ask the Team link
-	HelpURLOverride            string           `json:"help_url_override"`               // If specified, this URL will override the help link
+	Radius                     int                `json:"radius"`                          // The number of commits when doing clustering.
+	KeyOrder                   []string           `json:"key_order"`                       // The order of the keys to appear first in query-sk elements.
+	NumShift                   int                `json:"num_shift"`                       // The number of commits the shift navigation buttons should jump.
+	Interesting                float32            `json:"interesting"`                     // The threshold for a cluster to be interesting.
+	StepUpOnly                 bool               `json:"step_up_only"`                    // If true then only regressions that are a step up are displayed.
+	CommitRangeURL             string             `json:"commit_range_url"`                // A URI Template to be used for expanding details on a range of commits. See cluster-summary2-sk.
+	Demo                       bool               `json:"demo"`                            // True if this is a demo page, as opposed to being in production. Used to make puppeteer tests deterministic.
+	DisplayGroupBy             bool               `json:"display_group_by"`                // True if the Group By section of Alert config should be displayed.
+	HideListOfCommitsOnExplore bool               `json:"hide_list_of_commits_on_explore"` // True if the commit-detail-panel-sk element on the Explore details tab should be hidden.
+	Notifications              notifytypes.Type   `json:"notifications"`                   // The type of notifications that can be sent.
+	FetchChromePerfAnomalies   bool               `json:"fetch_chrome_perf_anomalies"`     // If true explore-sk will show the bisect button
+	FeedbackURL                string             `json:"feedback_url"`                    // The URL for the Provide Feedback link
+	ChatURL                    string             `json:"chat_url"`                        // The URL for the Ask the Team link
+	HelpURLOverride            string             `json:"help_url_override"`               // If specified, this URL will override the help link
+	TraceFormat                config.TraceFormat `json:"trace_format"`                    // Trace formatter to use
 }
 
 // getPageContext returns the value of `window.perf` serialized as JSON.
@@ -254,11 +255,13 @@
 		FeedbackURL:                config.Config.FeedbackURL,
 		ChatURL:                    config.Config.ChatURL,
 		HelpURLOverride:            config.Config.HelpURLOverride,
+		TraceFormat:                config.Config.TraceFormat,
 	}
 	b, err := json.MarshalIndent(pc, "", "  ")
 	if err != nil {
 		sklog.Errorf("Failed to JSON encode window.perf context: %s", err)
 	}
+
 	return template.JS(string(b)), nil
 }
 
diff --git a/perf/go/ts/main.go b/perf/go/ts/main.go
index c0f7ff9..6f7d364 100644
--- a/perf/go/ts/main.go
+++ b/perf/go/ts/main.go
@@ -105,6 +105,7 @@
 		{results.AllRequestKind, "TryBotRequestKind"},
 		{frame.AllResponseDisplayModes, "FrameResponseDisplayMode"},
 		{notifytypes.AllNotifierTypes, "NotifierTypes"},
+		{config.AllTraceFormats, "TraceFormat"},
 	})
 
 	generator.AddUnionToNamespace(progress.AllStatus, "progress")
diff --git a/perf/modules/cluster-page-sk/cluster-page-sk-demo.ts b/perf/modules/cluster-page-sk/cluster-page-sk-demo.ts
index 25aeb43..d302401 100644
--- a/perf/modules/cluster-page-sk/cluster-page-sk-demo.ts
+++ b/perf/modules/cluster-page-sk/cluster-page-sk-demo.ts
@@ -125,4 +125,5 @@
   feedback_url: '',
   chat_url: '',
   help_url_override: '',
+  trace_format: '',
 };
diff --git a/perf/modules/commit-range-sk/commit-range-sk-demo.ts b/perf/modules/commit-range-sk/commit-range-sk-demo.ts
index 718f40b..e7c6357 100644
--- a/perf/modules/commit-range-sk/commit-range-sk-demo.ts
+++ b/perf/modules/commit-range-sk/commit-range-sk-demo.ts
@@ -20,6 +20,7 @@
   feedback_url: '',
   chat_url: '',
   help_url_override: '',
+  trace_format: '',
 };
 
 // The response to a POST of [64809, 64811] to /_/cid/.
diff --git a/perf/modules/commit-range-sk/commit-range-sk_test.ts b/perf/modules/commit-range-sk/commit-range-sk_test.ts
index 64c0c3d..78e0d97 100644
--- a/perf/modules/commit-range-sk/commit-range-sk_test.ts
+++ b/perf/modules/commit-range-sk/commit-range-sk_test.ts
@@ -25,6 +25,7 @@
       feedback_url: '',
       chat_url: '',
       help_url_override: '',
+      trace_format: '',
     };
 
     element = newInstance((el: CommitRangeSk) => {
diff --git a/perf/modules/explore-simple-sk/BUILD.bazel b/perf/modules/explore-simple-sk/BUILD.bazel
index f85f704..223cb1d 100644
--- a/perf/modules/explore-simple-sk/BUILD.bazel
+++ b/perf/modules/explore-simple-sk/BUILD.bazel
@@ -47,6 +47,7 @@
         "//infra-sk/modules:jsonorthrow_ts_lib",
         "//infra-sk/modules:query_ts_lib",
         "//infra-sk/modules/json:index_ts_lib",
+        "//perf/modules/trace-details-formatter:traceformatter_ts_lib",
     ],
     ts_srcs = [
         "explore-simple-sk.ts",
diff --git a/perf/modules/explore-simple-sk/explore-simple-sk.scss b/perf/modules/explore-simple-sk/explore-simple-sk.scss
index 923a9cd..a321a33 100644
--- a/perf/modules/explore-simple-sk/explore-simple-sk.scss
+++ b/perf/modules/explore-simple-sk/explore-simple-sk.scss
@@ -321,4 +321,10 @@
       pointer-events: none;
     }
   }
+
+  #traceDetails {
+    font-family: 'Courier New', Courier, monospace;
+    font-size: 15px;
+    color: var(--on-surface);
+  }
 }
diff --git a/perf/modules/explore-simple-sk/explore-simple-sk.ts b/perf/modules/explore-simple-sk/explore-simple-sk.ts
index 235ba25..ecd8da7 100644
--- a/perf/modules/explore-simple-sk/explore-simple-sk.ts
+++ b/perf/modules/explore-simple-sk/explore-simple-sk.ts
@@ -102,6 +102,10 @@
 import { LoggedIn } from '../../../infra-sk/modules/alogin-sk/alogin-sk';
 import { Status as LoginStatus } from '../../../infra-sk/modules/json';
 import { CollapseSk } from '../../../elements-sk/modules/collapse-sk/collapse-sk';
+import {
+  TraceFormatter,
+  GetTraceFormatter,
+} from '../trace-details-formatter/traceformatter';
 
 /** The type of trace we are adding to a plot. */
 type addPlotType = 'query' | 'formula' | 'pivot';
@@ -392,8 +396,6 @@
 
   private summary: ParamSetSk | null = null;
 
-  private traceID: HTMLSpanElement | null = null;
-
   private commitTime: HTMLSpanElement | null = null;
 
   private csvDownload: HTMLAnchorElement | null = null;
@@ -426,8 +428,13 @@
 
   private collapseDetails: CollapseSk | null = null;
 
+  private traceDetails: HTMLSpanElement | null = null;
+
+  private traceFormatter: TraceFormatter | null = null;
+
   constructor() {
     super(ExploreSimpleSk.template);
+    this.traceFormatter = GetTraceFormatter();
   }
 
   private static template = (ele: ExploreSimpleSk) => html`
@@ -540,6 +547,7 @@
         <spinner-sk id=spinner active></spinner-sk>
         <pre id=percent></pre>
       </div>
+      <span id=traceDetails />
     </div>
 
     <pivot-table-sk
@@ -715,9 +723,6 @@
         <tabs-panel-sk>
           <div>
             <p>
-              <b>Trace ID</b>: <span title='Trace ID' id=trace_id></span>
-            </p>
-            <p>
               <b>Time</b>: <span title='Commit Time' id=commit_time></span>
             </p>
 
@@ -794,7 +799,6 @@
     this.simpleParamset = this.querySelector('#simple_paramset');
     this.spinner = this.querySelector('#spinner');
     this.summary = this.querySelector('#summary');
-    this.traceID = this.querySelector('#trace_id');
     this.commitTime = this.querySelector('#commit_time');
     this.csvDownload = this.querySelector('#csv_download');
     this.queryDialog = this.querySelector('#query-dialog');
@@ -809,6 +813,7 @@
     this.bisectButton = this.querySelector('#bisect-button');
     this.collapseButton = this.querySelector('#collapseButton');
     this.collapseDetails = this.querySelector('#collapseDetails');
+    this.traceDetails = this.querySelector('#traceDetails');
 
     // Populate the query element.
     const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
@@ -1226,10 +1231,13 @@
   /** Reflect the focused trace in the paramset. */
   private plotTraceFocused(e: CustomEvent<PlotSimpleSkTraceEventDetails>) {
     this.paramset!.highlight = fromKey(e.detail.name);
-    this.traceID!.textContent = e.detail.name;
     this.commitTime!.textContent = new Date(
       this._dataframe.header![e.detail.x]!.timestamp * 1000
     ).toLocaleString();
+    let formattedTrace = this.traceFormatter!.formatTrace(
+      fromKey(e.detail.name)
+    );
+    this.traceDetails!.textContent = formattedTrace;
   }
 
   /** User has zoomed in on the graph. */
@@ -1491,10 +1499,7 @@
       return;
     }
 
-    if (this.traceID) {
-      this.traceID.textContent = '';
-      this.commitTime!.textContent = '';
-    }
+    this.commitTime!.textContent = '';
     const body = this.requestFrameBodyFullFromState();
     const switchToTab =
       body.formulas!.length > 0 || body.queries!.length > 0 || body.keys !== '';
@@ -1815,7 +1820,6 @@
     this.plot!.removeAll();
     this._dataframe.traceset = {};
     this.paramset!.paramsets = [];
-    this.traceID!.textContent = '';
     this.commitTime!.textContent = '';
     this.detailTab!.selected = PARAMS_TAB_INDEX;
     this.displayMode = 'display_query_only';
diff --git a/perf/modules/explore-simple-sk/explore-simple-sk_test.ts b/perf/modules/explore-simple-sk/explore-simple-sk_test.ts
index 7efed56..d631b88 100644
--- a/perf/modules/explore-simple-sk/explore-simple-sk_test.ts
+++ b/perf/modules/explore-simple-sk/explore-simple-sk_test.ts
@@ -92,6 +92,7 @@
     feedback_url: '',
     chat_url: '',
     help_url_override: '',
+    trace_format: '',
   };
 
   // Create a common element-sk to be used by all the tests.
diff --git a/perf/modules/explore-sk/explore-sk-demo.ts b/perf/modules/explore-sk/explore-sk-demo.ts
index 921a05d..62a593a 100644
--- a/perf/modules/explore-sk/explore-sk-demo.ts
+++ b/perf/modules/explore-sk/explore-sk-demo.ts
@@ -460,6 +460,7 @@
   feedback_url: '',
   chat_url: '',
   help_url_override: '',
+  trace_format: 'chrome',
 };
 
 customElements.whenDefined('explore-sk').then(() => {
diff --git a/perf/modules/explore-sk/explore-sk_puppeteer_test.ts b/perf/modules/explore-sk/explore-sk_puppeteer_test.ts
index 550c8cc..e21c0a6 100644
--- a/perf/modules/explore-sk/explore-sk_puppeteer_test.ts
+++ b/perf/modules/explore-sk/explore-sk_puppeteer_test.ts
@@ -92,5 +92,15 @@
       await testBed.page.click('#collapseButton');
       await takeScreenshot(testBed.page, 'perf', 'explore-sk-collapse-details');
     });
+
+    it('loads the traces and hovers over a trace to display trace information', async () => {
+      await testBed.page.click('#demo-load-traces');
+      await testBed.page.waitForSelector('#traceButtons', {
+        visible: true,
+      });
+
+      await testBed.page.hover('plot-simple-sk');
+      await takeScreenshot(testBed.page, 'perf', 'explore-sk-trace-details');
+    });
   });
 });
diff --git a/perf/modules/json/index.ts b/perf/modules/json/index.ts
index e878882..71f08d4 100644
--- a/perf/modules/json/index.ts
+++ b/perf/modules/json/index.ts
@@ -241,6 +241,7 @@
 	feedback_url: string;
 	chat_url: string;
 	help_url_override: string;
+	trace_format: TraceFormat;
 }
 
 export interface TriageRequest {
@@ -419,6 +420,8 @@
 
 export type NotifierTypes = 'html_email' | 'markdown_issuetracker' | 'none';
 
+export type TraceFormat = 'chrome' | '';
+
 export type TryBotRequestKind = 'trybot' | 'commit';
 
 export type CL = string;
diff --git a/perf/modules/perf-scaffold-sk/perf-scaffold-sk-demo.ts b/perf/modules/perf-scaffold-sk/perf-scaffold-sk-demo.ts
index 9e57ab0..c5705c0 100644
--- a/perf/modules/perf-scaffold-sk/perf-scaffold-sk-demo.ts
+++ b/perf/modules/perf-scaffold-sk/perf-scaffold-sk-demo.ts
@@ -15,6 +15,7 @@
   feedback_url: '',
   chat_url: '',
   help_url_override: '',
+  trace_format: '',
 };
 
 import './index';
diff --git a/perf/modules/trace-details-formatter/BUILD.bazel b/perf/modules/trace-details-formatter/BUILD.bazel
new file mode 100644
index 0000000..dee5b12
--- /dev/null
+++ b/perf/modules/trace-details-formatter/BUILD.bazel
@@ -0,0 +1,12 @@
+load("//infra-sk:index.bzl", "ts_library")
+
+ts_library(
+    name = "traceformatter_ts_lib",
+    srcs = ["traceformatter.ts"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//perf/modules/json:index_ts_lib",
+        "//perf/modules/paramtools:index_ts_lib",
+        "//perf/modules/window:window_ts_lib",
+    ],
+)
diff --git a/perf/modules/trace-details-formatter/traceformatter.ts b/perf/modules/trace-details-formatter/traceformatter.ts
new file mode 100644
index 0000000..8f6be52
--- /dev/null
+++ b/perf/modules/trace-details-formatter/traceformatter.ts
@@ -0,0 +1,53 @@
+import { Params, TraceFormat } from '../json';
+import { makeKey } from '../paramtools';
+
+import '../window/window';
+
+// TraceFormatter provides an interface to format trace details.
+export interface TraceFormatter {
+  // formatTrace returns a formatted string for the given param set
+  formatTrace(params: Params): string;
+}
+
+// DefaultTraceFormatter provides default trace formatting
+export class DefaultTraceFormatter implements TraceFormatter {
+  formatTrace(params: Params): string {
+    return `Trace ID: ${makeKey(params)}`;
+  }
+}
+
+// ChromeTraceFormatter formats the trace details for chrome instances
+export class ChromeTraceFormatter implements TraceFormatter {
+  // formatTrace formats the param set in the form
+  // master/bot/benchmark/test/subtest_1/subtest_2/subtest_3
+  formatTrace(params: Params): string {
+    let keys = [
+      'master',
+      'bot',
+      'benchmark',
+      'test',
+      'subtest_1',
+      'subtest_2',
+      'subtest_3',
+    ];
+    let resultParts = [];
+    for (let key of keys) {
+      if (key in params) {
+        resultParts.push(params[key]);
+      }
+    }
+
+    return resultParts.join('/');
+  }
+}
+
+// traceFormatterRecords specifies TraceFormat to TraceFormatter mapping records
+const traceFormatterRecords: Record<TraceFormat, TraceFormatter> = {
+  '': new DefaultTraceFormatter(),
+  chrome: new ChromeTraceFormatter(),
+};
+
+// GetTraceFormatter returns a TraceFormatter instance based on config.
+export function GetTraceFormatter(): TraceFormatter {
+  return traceFormatterRecords[window.perf.trace_format];
+}
diff --git a/perf/modules/trybot-page-sk/trybot-page-sk-demo.ts b/perf/modules/trybot-page-sk/trybot-page-sk-demo.ts
index f402b8a..4d94d2f 100644
--- a/perf/modules/trybot-page-sk/trybot-page-sk-demo.ts
+++ b/perf/modules/trybot-page-sk/trybot-page-sk-demo.ts
@@ -21,6 +21,7 @@
   feedback_url: '',
   chat_url: '',
   help_url_override: '',
+  trace_format: '',
 };
 
 Date.now = () => Date.parse('2020-03-22T00:00:00.000Z');