ui: switch UI to use track id instead of ref/ref_type

This CL changes the UI to consistently use track_id instead of
ref/ref_type when making queries on the counter tables.

This improves the performance of the worst performing tracks (the cpu
frequency track) by 2.5-3x (from 250-300ms to 100-150ms).

Change-Id: Ifb53c6c0262c8ddec7bc3a56e4690749746e5be7
diff --git a/src/trace_processor/span_join_operator_table.cc b/src/trace_processor/span_join_operator_table.cc
index 931289c..94c8ac1 100644
--- a/src/trace_processor/span_join_operator_table.cc
+++ b/src/trace_processor/span_join_operator_table.cc
@@ -169,8 +169,14 @@
   return std::unique_ptr<SpanJoinOperatorTable::Cursor>(new Cursor(this, db_));
 }
 
-int SpanJoinOperatorTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
+int SpanJoinOperatorTable::BestIndex(const QueryConstraints& qc,
+                                     BestIndexInfo* info) {
   // TODO(lalitm): figure out cost estimation.
+  const auto& ob = qc.order_by();
+  if (ob.size() == 1 && ob.front().iColumn == Column::kTimestamp &&
+      !ob.front().desc) {
+    info->sqlite_omit_order_by = true;
+  }
   return SQLITE_OK;
 }
 
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 7edcbba..8c5fb67 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -302,7 +302,8 @@
     //}
     const maxCpuFreq = await engine.query(`
      select max(value)
-     from counters
+     from counter c
+     inner join cpu_counter_track t on c.track_id = t.id
      where name = 'cpufreq';
     `);
 
@@ -325,13 +326,28 @@
       // cpu freq data.
       // TODO(taylori): Find a way to display cpu idle
       // events even if there are no cpu freq events.
-      const freqExists = await engine.query(`
-        select value
-        from counters
-        where name = 'cpufreq' and ref = ${cpu}
+      const cpuFreqIdle = await engine.query(`
+        select
+          id as cpu_freq_id,
+          (
+            select id
+            from cpu_counter_track
+            where name = 'cpuidle'
+            and cpu = ${cpu}
+            limit 1
+          ) as cpu_idle_id
+        from cpu_counter_track
+        where name = 'cpufreq' and cpu = ${cpu}
         limit 1;
       `);
-      if (freqExists.numRecords > 0) {
+      if (cpuFreqIdle.numRecords > 0) {
+        const freqTrackId = +cpuFreqIdle.columns[0].longValues![0];
+
+        const idleTrackExists: boolean = !cpuFreqIdle.columns[1].isNulls![0];
+        const idleTrackId = idleTrackExists ?
+            +cpuFreqIdle.columns[1].longValues![0] :
+            undefined;
+
         tracksToAdd.push({
           engineId: this.engineId,
           kind: CPU_FREQ_TRACK_KIND,
@@ -340,6 +356,8 @@
           config: {
             cpu,
             maximumValue: +maxCpuFreq.columns[0].doubleValues![0],
+            freqTrackId,
+            idleTrackId,
           }
         });
       }
@@ -414,46 +432,69 @@
       }
     }
 
-
-    const counters = await engine.query(`
-      select name, ref, ref_type
-      from counter_definitions
-      where ref is not null
-      group by name, ref, ref_type
-      order by ref_type desc
+    // Add global or GPU counter tracks that are not bound to any pid/tid.
+    const globalCounters = await engine.query(`
+      select name, id
+      from counter_track
+      where type = 'counter_track'
+      union
+      select name, id
+      from gpu_counter_track
+      where name != 'gpufreq'
     `);
-
-    interface CounterMap {
-      [index: number]: string[];
+    for (let i = 0; i < globalCounters.numRecords; i++) {
+      const name = globalCounters.columns[0].stringValues![i];
+      const trackId = +globalCounters.columns[1].longValues![i];
+      tracksToAdd.push({
+        engineId: this.engineId,
+        kind: 'CounterTrack',
+        name,
+        trackGroup: SCROLLING_TRACK_GROUP,
+        config: {
+          name,
+          trackId,
+        }
+      });
     }
 
-    const counterUpids: CounterMap = new Array();
-    const counterUtids: CounterMap = new Array();
-    for (let i = 0; i < counters.numRecords; i++) {
-      const name = counters.columns[0].stringValues![i];
-      const ref = +counters.columns[1].longValues![i];
-      const refType = counters.columns[2].stringValues![i];
-      if (refType === 'upid') {
-        const el = counterUpids[ref];
-        el === undefined ? counterUpids[ref] = [name] :
-                           counterUpids[ref].push(name);
-      } else if (refType === 'utid') {
-        const el = counterUtids[ref];
-        el === undefined ? counterUtids[ref] = [name] :
-                           counterUtids[ref].push(name);
-      } else if (
-          refType === '[NULL]' || (refType === 'gpu' && name !== 'gpufreq')) {
-        // Add global or GPU counter tracks that are not bound to any pid/tid.
-        tracksToAdd.push({
-          engineId: this.engineId,
-          kind: 'CounterTrack',
-          name,
-          trackGroup: SCROLLING_TRACK_GROUP,
-          config: {
-            name,
-            ref: 0,
-          }
-        });
+    interface CounterTrack {
+      name: string;
+      trackId: number;
+    }
+
+    const counterUtids = new Map<number, CounterTrack[]>();
+    const threadCounters = await engine.query(`
+      select name, utid, id
+      from thread_counter_track
+    `);
+    for (let i = 0; i < threadCounters.numRecords; i++) {
+      const name = threadCounters.columns[0].stringValues![i];
+      const utid = +threadCounters.columns[1].longValues![i];
+      const trackId = +threadCounters.columns[2].longValues![i];
+
+      const el = counterUtids.get(utid);
+      if (el === undefined) {
+        counterUtids.set(utid, [{name, trackId}]);
+      } else {
+        el.push({name, trackId});
+      }
+    }
+
+    const counterUpids = new Map<number, CounterTrack[]>();
+    const processCounters = await engine.query(`
+      select name, upid, id
+      from process_counter_track
+    `);
+    for (let i = 0; i < processCounters.numRecords; i++) {
+      const name = processCounters.columns[0].stringValues![i];
+      const upid = +processCounters.columns[1].longValues![i];
+      const trackId = +processCounters.columns[2].longValues![i];
+
+      const el = counterUpids.get(upid);
+      if (el === undefined) {
+        counterUpids.set(upid, [{name, trackId}]);
+      } else {
+        el.push({name, trackId});
       }
     }
 
@@ -526,8 +567,8 @@
       const threadTrack =
           utid === null ? undefined : utidToThreadTrack.get(utid);
       if (threadTrack === undefined &&
-          (upid === null || counterUpids[upid] === undefined) &&
-          counterUtids[utid] === undefined && !threadHasSched &&
+          (upid === null || counterUpids.get(upid) === undefined) &&
+          counterUtids.get(utid) === undefined && !threadHasSched &&
           (upid === null || upid !== null && !heapUpids.has(upid))) {
         continue;
       }
@@ -571,18 +612,15 @@
         }));
 
         if (upid !== null) {
-          const counterNames = counterUpids[upid];
+          const counterNames = counterUpids.get(upid);
           if (counterNames !== undefined) {
             counterNames.forEach(element => {
               tracksToAdd.push({
                 engineId: this.engineId,
                 kind: 'CounterTrack',
-                name: element,
+                name: element.name,
                 trackGroup: pUuid,
-                config: {
-                  name: element,
-                  ref: upid,
-                }
+                config: {name: element.name, trackId: element.trackId}
               });
             });
           }
@@ -612,17 +650,17 @@
           }
         }
       }
-      const counterThreadNames = counterUtids[utid];
+      const counterThreadNames = counterUtids.get(utid);
       if (counterThreadNames !== undefined) {
         counterThreadNames.forEach(element => {
           tracksToAdd.push({
             engineId: this.engineId,
             kind: 'CounterTrack',
-            name: element,
+            name: element.name,
             trackGroup: pUuid,
             config: {
-              name: element,
-              ref: utid,
+              name: element.name,
+              trackId: element.trackId,
             }
           });
         });
diff --git a/ui/src/tracks/counter/common.ts b/ui/src/tracks/counter/common.ts
index 0eb6dfb..53a603d 100644
--- a/ui/src/tracks/counter/common.ts
+++ b/ui/src/tracks/counter/common.ts
@@ -30,6 +30,6 @@
   name: string;
   maximumValue?: number;
   minimumValue?: number;
-  ref: number;
+  trackId: number;
   scale?: 'DEFAULT'|'RELATIVE';
 }
diff --git a/ui/src/tracks/counter/controller.ts b/ui/src/tracks/counter/controller.ts
index 4085845..0be0371 100644
--- a/ui/src/tracks/counter/controller.ts
+++ b/ui/src/tracks/counter/controller.ts
@@ -39,20 +39,22 @@
 
     if (!this.setup) {
       const result = await this.query(`
-      select max(value), min(value) from
-        counters where name = '${this.config.name}'
-        and ref = ${this.config.ref}`);
+        select max(value), min(value)
+        from counter
+        where track_id = ${this.config.trackId}`);
       this.maximumValueSeen = +result.columns[0].doubleValues![0];
       this.minimumValueSeen = +result.columns[1].doubleValues![0];
       await this.query(
         `create virtual table ${this.tableName('window')} using window;`);
 
-      await this.query(`create view ${this.tableName('counter_view')} as
-        select ts,
-        lead(ts, 1, ts) over (partition by ref_type order by ts) - ts as dur,
-        value, name, ref
-        from counters
-        where name = '${this.config.name}' and ref = ${this.config.ref};`);
+      await this.query(`
+        create view ${this.tableName('counter_view')} as
+        select
+          ts,
+          lead(ts, 1, ts) over (order by ts) - ts as dur,
+          value
+        from counter
+        where track_id = ${this.config.trackId};`);
 
       await this.query(`create virtual table ${this.tableName('span')} using
         span_join(${this.tableName('counter_view')},
@@ -60,13 +62,16 @@
       this.setup = true;
     }
 
-    const result = await this.engine.queryOneRow(`select count(*)
-    from (select
-      ts,
-      lead(ts, 1, ts) over (partition by ref_type order by ts) as ts_end,
-      from counters
-      where name = '${this.config.name}' and ref = ${this.config.ref})
-    where ts <= ${endNs} and ${startNs} <= ts_end`);
+    const result = await this.engine.queryOneRow(`
+      select count(*)
+      from (
+        select
+          ts,
+          lead(ts, 1, ts) over (order by ts) as ts_end,
+        from counter
+        where track_id = ${this.config.trackId}
+      )
+      where ts <= ${endNs} and ${startNs} <= ts_end`);
 
     // Only quantize if we have too much data to draw.
     const isQuantized = result[0] > LIMIT;
@@ -94,18 +99,32 @@
       // Union that with the query that finds all the counters within
       // the current query range.
       query = `
-      select * from (select ts, value, counter_id from counters
-      where name = '${this.config.name}' and ref = ${this.config.ref} and
-      ts <= ${startNs} order by ts desc limit 1)
-      UNION
-      select * from (select ts, value, counter_id
-        from (select
-          ts,
-          lead(ts, 1, ts) over (partition by ref_type order by ts) as ts_end,
-          value, counter_id
-          from counters
-          where name = '${this.config.name}' and ref = ${this.config.ref})
-      where ts <= ${endNs} and ${startNs} <= ts_end limit ${LIMIT});`;
+      select *
+      from (
+        select ts, value, track_id
+        from counter
+        where
+          track_id = ${this.config.trackId} and
+          ts <= ${startNs}
+        order by ts desc
+        limit 1
+      )
+      union
+      select *
+      from (
+        select ts, value, track_id
+        from (
+          select
+            ts,
+            lead(ts, 1, ts) over (order by ts) as ts_end,
+            value,
+            track_id
+          from counter
+          where track_id = ${this.config.trackId}
+        )
+        where ts <= ${endNs} and ${startNs} <= ts_end
+        limit ${LIMIT}
+      );`;
     }
 
     const rawResult = await this.query(query);
diff --git a/ui/src/tracks/cpu_freq/common.ts b/ui/src/tracks/cpu_freq/common.ts
index 8968907..fff0c8f 100644
--- a/ui/src/tracks/cpu_freq/common.ts
+++ b/ui/src/tracks/cpu_freq/common.ts
@@ -28,5 +28,8 @@
 
 export interface Config {
   cpu: number;
+  freqTrackId: number;
+  idleTrackId?: number;
   maximumValue?: number;
-  minimumValue?: number;}
+  minimumValue?: number;
+}
diff --git a/ui/src/tracks/cpu_freq/controller.ts b/ui/src/tracks/cpu_freq/controller.ts
index 190a3d9..bdfbdae 100644
--- a/ui/src/tracks/cpu_freq/controller.ts
+++ b/ui/src/tracks/cpu_freq/controller.ts
@@ -38,9 +38,9 @@
 
     if (!this.setup) {
       const result = await this.query(`
-      select max(value) from
-        counters where name = 'cpufreq'
-        and ref = ${this.config.cpu}`);
+        select max(value)
+        from counter
+        where track_id = ${this.config.freqTrackId}`);
       this.maximumValueSeen = +result.columns[0].doubleValues![0];
 
       await this.query(
@@ -49,36 +49,39 @@
       await this.query(`create view ${this.tableName('freq')}
           as select
             ts,
-            lead(ts) over (order by ts) - ts as dur,
-            ref as cpu,
-            name as freq_name,
+            lead(ts) over () - ts as dur,
             value as freq_value
-          from counters
-          where name = 'cpufreq'
-            and ref = ${this.config.cpu}
-            and ref_type = 'cpu';
+          from counter c
+          where track_id = ${this.config.freqTrackId};
       `);
 
-      await this.query(`create view ${this.tableName('idle')}
-        as select
-          ts,
-          lead(ts) over (order by ts) - ts as dur,
-          ref as cpu,
-          name as idle_name,
-          value as idle_value
-        from counters
-        where name = 'cpuidle'
-          and ref = ${this.config.cpu}
-          and ref_type = 'cpu';
-      `);
+      // If there is no idle track, just make the idle track a single row
+      // which spans the entire time range.
+      if (this.config.idleTrackId === undefined) {
+        await this.query(`create view ${this.tableName('idle')} as
+           select
+             0 as ts,
+             ${Number.MAX_SAFE_INTEGER} as dur,
+             -1 as idle_value;
+          `);
+      } else {
+        await this.query(`create view ${this.tableName('idle')}
+          as select
+            ts,
+            lead(ts) over () - ts as dur,
+            value as idle_value
+          from counter c
+          where track_id = ${this.config.idleTrackId};
+        `);
+      }
 
       await this.query(`create virtual table ${this.tableName('freq_idle')}
-              using span_join(${this.tableName('freq')} PARTITIONED cpu,
-                              ${this.tableName('idle')} PARTITIONED cpu);`);
+              using span_join(${this.tableName('freq')},
+                              ${this.tableName('idle')});`);
 
       await this.query(`create virtual table ${this.tableName('span_activity')}
-      using span_join(${this.tableName('freq_idle')} PARTITIONED cpu,
-                      ${this.tableName('window')});`);
+              using span_join(${this.tableName('freq_idle')},
+                              ${this.tableName('window')});`);
 
       // TODO(taylori): Move the idle value processing to the TP.
       await this.query(`create view ${this.tableName('activity')}
@@ -86,7 +89,6 @@
         ts,
         dur,
         quantum_ts,
-        cpu,
         case idle_value
           when 4294967295 then -1
           else idle_value