Add out/in thread+process info to flow events

Screenshot: https://screenshot.googleplex.com/9XoghXsgQzLbJqk

NULL in the selection case as the UI doesn't display
per-event info in that case so the join isn't worth it.

Clicking any column currently toggles the selection (same as
before). In a follow-up I plan to have Duration select the
begin<=>end range and Thread/Process scroll to the
source/destination of the flow.

Dropped the .half-width CSS property from this table as it
was unnecessarily collapsing the added info.

Bug: b/224527755
Change-Id: I6229209b388eca34d098d75f8b7ba6abdea3c98d
diff --git a/ui/src/controller/flow_events_controller.ts b/ui/src/controller/flow_events_controller.ts
index 8e9254c..20ae793 100644
--- a/ui/src/controller/flow_events_controller.ts
+++ b/ui/src/controller/flow_events_controller.ts
@@ -64,6 +64,8 @@
         beginSliceStartTs: NUM,
         beginSliceEndTs: NUM,
         beginDepth: NUM,
+        beginThreadName: STR_NULL,
+        beginProcessName: STR_NULL,
         endSliceId: NUM,
         endTrackId: NUM,
         endSliceName: STR_NULL,
@@ -71,6 +73,8 @@
         endSliceStartTs: NUM,
         endSliceEndTs: NUM,
         endDepth: NUM,
+        endThreadName: STR_NULL,
+        endProcessName: STR_NULL,
         name: STR_NULL,
         category: STR_NULL,
         id: NUM,
@@ -85,6 +89,10 @@
         const beginSliceStartTs = fromNs(it.beginSliceStartTs);
         const beginSliceEndTs = fromNs(it.beginSliceEndTs);
         const beginDepth = it.beginDepth;
+        const beginThreadName =
+            it.beginThreadName === null ? 'NULL' : it.beginThreadName;
+        const beginProcessName =
+            it.beginProcessName === null ? 'NULL' : it.beginProcessName;
 
         const endSliceId = it.endSliceId;
         const endTrackId = it.endTrackId;
@@ -95,6 +103,10 @@
         const endSliceStartTs = fromNs(it.endSliceStartTs);
         const endSliceEndTs = fromNs(it.endSliceEndTs);
         const endDepth = it.endDepth;
+        const endThreadName =
+            it.endThreadName === null ? 'NULL' : it.endThreadName;
+        const endProcessName =
+            it.endProcessName === null ? 'NULL' : it.endProcessName;
 
         // Category and name present only in version 1 flow events
         // It is most likelly NULL for all other versions
@@ -111,7 +123,9 @@
             sliceCategory: beginSliceCategory,
             sliceStartTs: beginSliceStartTs,
             sliceEndTs: beginSliceEndTs,
-            depth: beginDepth
+            depth: beginDepth,
+            threadName: beginThreadName,
+            processName: beginProcessName
           },
           end: {
             trackId: endTrackId,
@@ -120,7 +134,9 @@
             sliceCategory: endSliceCategory,
             sliceStartTs: endSliceStartTs,
             sliceEndTs: endSliceEndTs,
-            depth: endDepth
+            depth: endDepth,
+            threadName: endThreadName,
+            processName: endProcessName
           },
           dur: endSliceStartTs - beginSliceEndTs,
           category,
@@ -156,6 +172,8 @@
       t1.ts as beginSliceStartTs,
       (t1.ts+t1.dur) as beginSliceEndTs,
       t1.depth as beginDepth,
+      (thread_out.name || ' ' || thread_out.tid) as beginThreadName,
+      (process_out.name || ' ' || process_out.pid) as beginProcessName,
       f.slice_in as endSliceId,
       t2.track_id as endTrackId,
       t2.name as endSliceName,
@@ -163,12 +181,20 @@
       t2.ts as endSliceStartTs,
       (t2.ts+t2.dur) as endSliceEndTs,
       t2.depth as endDepth,
+      (thread_in.name || ' ' || thread_in.tid) as endThreadName,
+      (process_in.name || ' ' || process_in.pid) as endProcessName,
       extract_arg(f.arg_set_id, 'cat') as category,
       extract_arg(f.arg_set_id, 'name') as name,
       f.id as id
     from ${connectedFlows} f
     join slice t1 on f.slice_out = t1.slice_id
     join slice t2 on f.slice_in = t2.slice_id
+    join thread_track track_out on track_out.id = t1.track_id
+    join thread thread_out on thread_out.utid = track_out.utid
+    join thread_track track_in on track_in.id = t2.track_id
+    join thread thread_in on thread_in.utid = track_in.utid
+    join process process_out on process_out.upid = thread_out.upid
+    join process process_in on process_in.upid = thread_in.upid
     `;
     this.queryFlowEvents(
         query, (flows: Flow[]) => publishConnectedFlows(flows));
@@ -217,6 +243,8 @@
       t1.ts as beginSliceStartTs,
       (t1.ts+t1.dur) as beginSliceEndTs,
       t1.depth as beginDepth,
+      NULL as beginThreadName,
+      NULL as beginProcessName,
       f.slice_in as endSliceId,
       t2.track_id as endTrackId,
       t2.name as endSliceName,
@@ -224,6 +252,8 @@
       t2.ts as endSliceStartTs,
       (t2.ts+t2.dur) as endSliceEndTs,
       t2.depth as endDepth,
+      NULL as endThreadName,
+      NULL as endProcessName,
       extract_arg(f.arg_set_id, 'cat') as category,
       extract_arg(f.arg_set_id, 'name') as name,
       f.id as id
diff --git a/ui/src/frontend/flow_events_panel.ts b/ui/src/frontend/flow_events_panel.ts
index 500148c..c7783b9 100644
--- a/ui/src/frontend/flow_events_panel.ts
+++ b/ui/src/frontend/flow_events_panel.ts
@@ -61,7 +61,11 @@
       m('th', 'Direction'),
       m('th', 'Duration'),
       m('th', 'Connected Slice ID'),
-      m('th', 'Connected Slice Name')
+      m('th', 'Connected Slice Name'),
+      m('th', 'Thread Out'),
+      m('th', 'Thread In'),
+      m('th', 'Process Out'),
+      m('th', 'Process In')
     ];
 
     if (haveCategories) {
@@ -93,7 +97,11 @@
         m('td.flow-link', args, outgoing ? 'Outgoing' : 'Incoming'),
         m('td.flow-link', args, timeToCode(flow.dur)),
         m('td.flow-link', args, otherEnd.sliceId.toString()),
-        m('td.flow-link', args, otherEnd.sliceName)
+        m('td.flow-link', args, otherEnd.sliceName),
+        m('td.flow-link', args, flow.begin.threadName),
+        m('td.flow-link', args, flow.end.threadName),
+        m('td.flow-link', args, flow.begin.processName),
+        m('td.flow-link', args, flow.end.processName)
       ];
 
       if (haveCategories) {
@@ -106,7 +114,7 @@
 
     return m('.details-panel', [
       m('.details-panel-heading', m('h2', `Flow events`)),
-      m('.flow-events-table', m('table.half-width', rows))
+      m('.flow-events-table', m('table', rows))
     ]);
   }
 
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 473a945..71e2a34 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -77,6 +77,12 @@
   sliceId: number;
   sliceStartTs: number;
   sliceEndTs: number;
+  // Thread and process info. Only set in sliceSelected not in areaSelected as
+  // the latter doesn't display per-flow info and it'd be a waste to join
+  // additional tables for undisplayed info in that case. Nothing precludes
+  // adding this in a future iteration however.
+  threadName: string;
+  processName: string;
 
   depth: number;
 }