Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 1 | // Copyright (C) 2018 The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | import '../tracks/all_controller'; |
| 16 | |
Deepanjan Roy | 32ad0d7 | 2018-10-18 14:50:16 -0400 | [diff] [blame^] | 17 | import * as uuidv4 from 'uuid/v4'; |
| 18 | |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 19 | import {assertExists, assertTrue} from '../base/logging'; |
| 20 | import { |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 21 | Actions, |
Hector Dearman | 5ae8247 | 2018-10-03 08:30:35 +0100 | [diff] [blame] | 22 | DeferredAction, |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 23 | } from '../common/actions'; |
Deepanjan Roy | 32ad0d7 | 2018-10-18 14:50:16 -0400 | [diff] [blame^] | 24 | import {SCROLLING_TRACK_GROUP} from '../common/state'; |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 25 | import {TimeSpan} from '../common/time'; |
| 26 | import {QuantizedLoad, ThreadDesc} from '../frontend/globals'; |
| 27 | import {SLICE_TRACK_KIND} from '../tracks/chrome_slices/common'; |
| 28 | import {CPU_SLICE_TRACK_KIND} from '../tracks/cpu_slices/common'; |
Deepanjan Roy | 32ad0d7 | 2018-10-18 14:50:16 -0400 | [diff] [blame^] | 29 | import {PROCESS_SUMMARY_TRACK} from '../tracks/process_summary/common'; |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 30 | |
| 31 | import {Child, Children, Controller} from './controller'; |
| 32 | import {Engine} from './engine'; |
| 33 | import {globals} from './globals'; |
| 34 | import {QueryController, QueryControllerArgs} from './query_controller'; |
| 35 | import {TrackControllerArgs, trackControllerRegistry} from './track_controller'; |
| 36 | |
| 37 | type States = 'init'|'loading_trace'|'ready'; |
| 38 | |
| 39 | |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 40 | declare interface FileReaderSync { readAsArrayBuffer(blob: Blob): ArrayBuffer; } |
| 41 | |
| 42 | declare var FileReaderSync: |
| 43 | {prototype: FileReaderSync; new (): FileReaderSync;}; |
| 44 | |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 45 | // TraceController handles handshakes with the frontend for everything that |
| 46 | // concerns a single trace. It owns the WASM trace processor engine, handles |
| 47 | // tracks data and SQL queries. There is one TraceController instance for each |
| 48 | // trace opened in the UI (for now only one trace is supported). |
| 49 | export class TraceController extends Controller<States> { |
| 50 | private readonly engineId: string; |
| 51 | private engine?: Engine; |
| 52 | |
| 53 | constructor(engineId: string) { |
| 54 | super('init'); |
| 55 | this.engineId = engineId; |
| 56 | } |
| 57 | |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 58 | onDestroy() { |
| 59 | if (this.engine !== undefined) globals.destroyEngine(this.engine.id); |
| 60 | } |
| 61 | |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 62 | run() { |
| 63 | const engineCfg = assertExists(globals.state.engines[this.engineId]); |
| 64 | switch (this.state) { |
| 65 | case 'init': |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 66 | globals.dispatch(Actions.setEngineReady({ |
| 67 | engineId: this.engineId, |
| 68 | ready: false, |
| 69 | })); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 70 | this.loadTrace().then(() => { |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 71 | globals.dispatch(Actions.setEngineReady({ |
| 72 | engineId: this.engineId, |
| 73 | ready: true, |
| 74 | })); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 75 | }); |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 76 | this.updateStatus('Opening trace'); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 77 | this.setState('loading_trace'); |
| 78 | break; |
| 79 | |
| 80 | case 'loading_trace': |
| 81 | // Stay in this state until loadTrace() returns and marks the engine as |
| 82 | // ready. |
| 83 | if (this.engine === undefined || !engineCfg.ready) return; |
| 84 | this.setState('ready'); |
| 85 | break; |
| 86 | |
| 87 | case 'ready': |
| 88 | // At this point we are ready to serve queries and handle tracks. |
| 89 | const engine = assertExists(this.engine); |
| 90 | assertTrue(engineCfg.ready); |
| 91 | const childControllers: Children = []; |
| 92 | |
| 93 | // Create a TrackController for each track. |
| 94 | for (const trackId of Object.keys(globals.state.tracks)) { |
| 95 | const trackCfg = globals.state.tracks[trackId]; |
| 96 | if (trackCfg.engineId !== this.engineId) continue; |
| 97 | if (!trackControllerRegistry.has(trackCfg.kind)) continue; |
| 98 | const trackCtlFactory = trackControllerRegistry.get(trackCfg.kind); |
| 99 | const trackArgs: TrackControllerArgs = {trackId, engine}; |
| 100 | childControllers.push(Child(trackId, trackCtlFactory, trackArgs)); |
| 101 | } |
| 102 | |
| 103 | // Create a QueryController for each query. |
| 104 | for (const queryId of Object.keys(globals.state.queries)) { |
| 105 | const queryArgs: QueryControllerArgs = {queryId, engine}; |
| 106 | childControllers.push(Child(queryId, QueryController, queryArgs)); |
| 107 | } |
| 108 | |
| 109 | return childControllers; |
| 110 | |
| 111 | default: |
| 112 | throw new Error(`unknown state ${this.state}`); |
| 113 | } |
| 114 | return; |
| 115 | } |
| 116 | |
| 117 | private async loadTrace() { |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 118 | this.updateStatus('Creating trace processor'); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 119 | const engineCfg = assertExists(globals.state.engines[this.engineId]); |
Hector Dearman | 0315954 | 2018-09-19 18:37:00 +0100 | [diff] [blame] | 120 | this.engine = globals.createEngine(); |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 121 | |
| 122 | const statusHeader = 'Opening trace'; |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 123 | if (engineCfg.source instanceof File) { |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 124 | const blob = engineCfg.source as Blob; |
| 125 | const reader = new FileReaderSync(); |
| 126 | const SLICE_SIZE = 1024 * 1024; |
| 127 | for (let off = 0; off < blob.size; off += SLICE_SIZE) { |
| 128 | const slice = blob.slice(off, off + SLICE_SIZE); |
| 129 | const arrBuf = reader.readAsArrayBuffer(slice); |
| 130 | await this.engine.parse(new Uint8Array(arrBuf)); |
| 131 | const progress = Math.round((off + slice.size) / blob.size * 100); |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 132 | this.updateStatus(`${statusHeader} ${progress} %`); |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 133 | } |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 134 | } else { |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 135 | const resp = await fetch(engineCfg.source); |
| 136 | if (resp.status !== 200) { |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 137 | this.updateStatus(`HTTP error ${resp.status}`); |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 138 | throw new Error(`fetch() failed with HTTP error ${resp.status}`); |
| 139 | } |
| 140 | // tslint:disable-next-line no-any |
| 141 | const rd = (resp.body as any).getReader() as ReadableStreamReader; |
| 142 | const tStartMs = performance.now(); |
| 143 | let tLastUpdateMs = 0; |
| 144 | for (let off = 0;;) { |
| 145 | const readRes = await rd.read() as {value: Uint8Array, done: boolean}; |
| 146 | if (readRes.value !== undefined) { |
| 147 | off += readRes.value.length; |
| 148 | await this.engine.parse(readRes.value); |
| 149 | } |
| 150 | // For traces loaded from the network there doesn't seem to be a |
| 151 | // reliable way to compute the %. The content-length exposed by GCS is |
| 152 | // before compression (which is handled transparently by the browser). |
| 153 | const nowMs = performance.now(); |
| 154 | if (nowMs - tLastUpdateMs > 100) { |
| 155 | tLastUpdateMs = nowMs; |
| 156 | const mb = off / 1e6; |
| 157 | const tElapsed = (nowMs - tStartMs) / 1e3; |
| 158 | let status = `${statusHeader} ${mb.toFixed(1)} MB `; |
| 159 | status += `(${(mb / tElapsed).toFixed(1)} MB/s)`; |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 160 | this.updateStatus(status); |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 161 | } |
| 162 | if (readRes.done) break; |
| 163 | } |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 164 | } |
| 165 | |
Hector Dearman | 1d28921 | 2018-09-05 14:05:29 +0100 | [diff] [blame] | 166 | await this.engine.notifyEof(); |
| 167 | |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 168 | const traceTime = await this.engine.getTraceTimeBounds(); |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 169 | const traceTimeState = { |
| 170 | startSec: traceTime.start, |
| 171 | endSec: traceTime.end, |
| 172 | lastUpdate: Date.now() / 1000, |
| 173 | }; |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 174 | const actions = [ |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 175 | Actions.setTraceTime(traceTimeState), |
| 176 | Actions.navigate({route: '/viewer'}), |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 177 | ]; |
| 178 | |
| 179 | if (globals.state.visibleTraceTime.lastUpdate === 0) { |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 180 | actions.push(Actions.setVisibleTraceTime(traceTimeState)); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | globals.dispatchMultiple(actions); |
| 184 | |
Hector Dearman | e702a68 | 2018-10-16 16:29:28 +0100 | [diff] [blame] | 185 | { |
| 186 | // When we reload from a permalink don't create extra tracks: |
| 187 | const {pinnedTracks, scrollingTracks} = globals.state; |
Hector Dearman | bb55c9e | 2018-10-17 16:08:39 +0100 | [diff] [blame] | 188 | if (!pinnedTracks.length && !scrollingTracks.length) { |
Hector Dearman | e702a68 | 2018-10-16 16:29:28 +0100 | [diff] [blame] | 189 | await this.listTracks(); |
| 190 | } |
| 191 | } |
| 192 | |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 193 | await this.listThreads(); |
| 194 | await this.loadTimelineOverview(traceTime); |
| 195 | } |
| 196 | |
| 197 | private async listTracks() { |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 198 | this.updateStatus('Loading tracks'); |
Hector Dearman | e702a68 | 2018-10-16 16:29:28 +0100 | [diff] [blame] | 199 | |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 200 | const engine = assertExists<Engine>(this.engine); |
Hector Dearman | 5ae8247 | 2018-10-03 08:30:35 +0100 | [diff] [blame] | 201 | const addToTrackActions: DeferredAction[] = []; |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 202 | const numCpus = await engine.getNumberOfCpus(); |
Hector Dearman | e27a974 | 2018-10-12 09:31:34 +0100 | [diff] [blame] | 203 | |
| 204 | // TODO(hjd): Move this code out of TraceController. |
| 205 | for (const counterName of ['VSYNC-sf', 'VSYNC-app']) { |
| 206 | const hasVsync = |
| 207 | !!(await engine.query( |
| 208 | `select ts from counters where name like "${ |
| 209 | counterName |
| 210 | }" limit 1`)) |
| 211 | .numRecords; |
| 212 | if (!hasVsync) continue; |
| 213 | addToTrackActions.push(Actions.addTrack({ |
| 214 | engineId: this.engineId, |
| 215 | kind: 'VsyncTrack', |
| 216 | name: `${counterName}`, |
| 217 | config: { |
| 218 | counterName, |
| 219 | } |
| 220 | })); |
| 221 | } |
| 222 | |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 223 | for (let cpu = 0; cpu < numCpus; cpu++) { |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 224 | addToTrackActions.push(Actions.addTrack({ |
| 225 | engineId: this.engineId, |
| 226 | kind: CPU_SLICE_TRACK_KIND, |
| 227 | name: `Cpu ${cpu}`, |
Deepanjan Roy | 32ad0d7 | 2018-10-18 14:50:16 -0400 | [diff] [blame^] | 228 | trackGroup: SCROLLING_TRACK_GROUP, |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 229 | config: { |
| 230 | cpu, |
| 231 | } |
| 232 | })); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 233 | } |
| 234 | |
Deepanjan Roy | 32ad0d7 | 2018-10-18 14:50:16 -0400 | [diff] [blame^] | 235 | // Local experiments shows getting maxDepth separately is ~2x faster than |
| 236 | // joining with threads and processes. |
| 237 | const maxDepthQuery = |
| 238 | await engine.query('select utid, max(depth) from slices group by utid'); |
| 239 | |
| 240 | const utidToMaxDepth = new Map<number, number>(); |
| 241 | for (let i = 0; i < maxDepthQuery.numRecords; i++) { |
| 242 | const utid = maxDepthQuery.columns[0].longValues![i] as number; |
| 243 | const maxDepth = maxDepthQuery.columns[1].longValues![i] as number; |
| 244 | utidToMaxDepth.set(utid, maxDepth); |
| 245 | } |
| 246 | |
| 247 | const threadQuery = await engine.query( |
| 248 | 'select utid, tid, upid, pid, thread.name, process.name ' + |
| 249 | 'from thread inner join process using(upid)'); |
| 250 | |
| 251 | const upidToUuid = new Map<number, string>(); |
| 252 | const addSummaryTrackActions: DeferredAction[] = []; |
| 253 | const addTrackGroupActions: DeferredAction[] = []; |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 254 | for (let i = 0; i < threadQuery.numRecords; i++) { |
Deepanjan Roy | 32ad0d7 | 2018-10-18 14:50:16 -0400 | [diff] [blame^] | 255 | const utid = threadQuery.columns[0].longValues![i] as number; |
| 256 | |
| 257 | const maxDepth = utidToMaxDepth.get(utid); |
| 258 | if (maxDepth === undefined) { |
| 259 | // This thread does not have stackable slices. |
| 260 | continue; |
| 261 | } |
| 262 | |
| 263 | const tid = threadQuery.columns[1].longValues![i] as number; |
| 264 | const upid = threadQuery.columns[2].longValues![i] as number; |
| 265 | const pid = threadQuery.columns[3].longValues![i] as number; |
| 266 | const threadName = threadQuery.columns[4].stringValues![i]; |
| 267 | const processName = threadQuery.columns[5].stringValues![i]; |
| 268 | |
| 269 | let pUuid = upidToUuid.get(upid); |
| 270 | if (pUuid === undefined) { |
| 271 | pUuid = uuidv4(); |
| 272 | const summaryTrackId = uuidv4(); |
| 273 | upidToUuid.set(upid, pUuid); |
| 274 | addSummaryTrackActions.push(Actions.addTrack({ |
| 275 | id: summaryTrackId, |
| 276 | engineId: this.engineId, |
| 277 | kind: PROCESS_SUMMARY_TRACK, |
| 278 | name: `${pid} summary`, |
| 279 | config: {upid, pid, maxDepth, utid}, |
| 280 | })); |
| 281 | addTrackGroupActions.push(Actions.addTrackGroup({ |
| 282 | engineId: this.engineId, |
| 283 | summaryTrackId, |
| 284 | name: `${processName} ${pid}`, |
| 285 | id: pUuid, |
| 286 | collapsed: true, |
| 287 | })); |
| 288 | } |
| 289 | |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 290 | addToTrackActions.push(Actions.addTrack({ |
| 291 | engineId: this.engineId, |
| 292 | kind: SLICE_TRACK_KIND, |
Deepanjan Roy | 32ad0d7 | 2018-10-18 14:50:16 -0400 | [diff] [blame^] | 293 | name: threadName + `[${tid}]`, |
| 294 | trackGroup: pUuid, |
| 295 | config: {upid, utid, maxDepth}, |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 296 | })); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 297 | } |
Deepanjan Roy | 32ad0d7 | 2018-10-18 14:50:16 -0400 | [diff] [blame^] | 298 | const allActions = |
| 299 | addSummaryTrackActions.concat(addTrackGroupActions, addToTrackActions); |
| 300 | globals.dispatchMultiple(allActions); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 301 | } |
| 302 | |
| 303 | private async listThreads() { |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 304 | this.updateStatus('Reading thread list'); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 305 | const sqlQuery = 'select utid, tid, pid, thread.name, process.name ' + |
| 306 | 'from thread inner join process using(upid)'; |
Hector Dearman | e6dfe41 | 2018-09-20 14:12:13 +0100 | [diff] [blame] | 307 | const threadRows = await assertExists(this.engine).query(sqlQuery); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 308 | const threads: ThreadDesc[] = []; |
| 309 | for (let i = 0; i < threadRows.numRecords; i++) { |
| 310 | const utid = threadRows.columns[0].longValues![i] as number; |
| 311 | const tid = threadRows.columns[1].longValues![i] as number; |
| 312 | const pid = threadRows.columns[2].longValues![i] as number; |
| 313 | const threadName = threadRows.columns[3].stringValues![i]; |
| 314 | const procName = threadRows.columns[4].stringValues![i]; |
| 315 | threads.push({utid, tid, threadName, pid, procName}); |
| 316 | } // for (record ...) |
| 317 | globals.publish('Threads', threads); |
| 318 | } |
| 319 | |
| 320 | private async loadTimelineOverview(traceTime: TimeSpan) { |
| 321 | const engine = assertExists<Engine>(this.engine); |
| 322 | const numSteps = 100; |
| 323 | const stepSec = traceTime.duration / numSteps; |
| 324 | for (let step = 0; step < numSteps; step++) { |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 325 | this.updateStatus( |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 326 | 'Loading overview ' + |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 327 | `${Math.round((step + 1) / numSteps * 1000) / 10}%`); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 328 | const startSec = traceTime.start + step * stepSec; |
| 329 | const startNs = Math.floor(startSec * 1e9); |
| 330 | const endSec = startSec + stepSec; |
| 331 | const endNs = Math.ceil(endSec * 1e9); |
| 332 | |
| 333 | // Sched overview. |
Hector Dearman | e6dfe41 | 2018-09-20 14:12:13 +0100 | [diff] [blame] | 334 | const schedRows = await engine.query( |
| 335 | `select sum(dur)/${stepSec}/1e9, cpu from sched ` + |
| 336 | `where ts >= ${startNs} and ts < ${endNs} and utid != 0 ` + |
| 337 | 'group by cpu order by cpu'); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 338 | const schedData: {[key: string]: QuantizedLoad} = {}; |
| 339 | for (let i = 0; i < schedRows.numRecords; i++) { |
| 340 | const load = schedRows.columns[0].doubleValues![i]; |
| 341 | const cpu = schedRows.columns[1].longValues![i] as number; |
| 342 | schedData[cpu] = {startSec, endSec, load}; |
| 343 | } // for (record ...) |
| 344 | globals.publish('OverviewData', schedData); |
| 345 | |
| 346 | // Slices overview. |
Hector Dearman | e6dfe41 | 2018-09-20 14:12:13 +0100 | [diff] [blame] | 347 | const slicesRows = await engine.query( |
| 348 | `select sum(dur)/${stepSec}/1e9, process.name, process.pid, upid ` + |
| 349 | 'from slices inner join thread using(utid) ' + |
| 350 | 'inner join process using(upid) where depth = 0 ' + |
| 351 | `and ts >= ${startNs} and ts < ${endNs} ` + |
| 352 | 'group by upid'); |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 353 | const slicesData: {[key: string]: QuantizedLoad} = {}; |
| 354 | for (let i = 0; i < slicesRows.numRecords; i++) { |
| 355 | const load = slicesRows.columns[0].doubleValues![i]; |
| 356 | let procName = slicesRows.columns[1].stringValues![i]; |
| 357 | const pid = slicesRows.columns[2].longValues![i]; |
| 358 | procName += ` [${pid}]`; |
| 359 | slicesData[procName] = {startSec, endSec, load}; |
| 360 | } |
| 361 | globals.publish('OverviewData', slicesData); |
| 362 | } // for (step ...) |
| 363 | } |
Hector Dearman | 4d8d73d | 2018-10-04 13:16:29 +0100 | [diff] [blame] | 364 | |
| 365 | private updateStatus(msg: string): void { |
| 366 | globals.dispatch(Actions.updateStatus({ |
| 367 | msg, |
| 368 | timestamp: Date.now() / 1000, |
| 369 | })); |
| 370 | } |
Primiano Tucci | e36ca63 | 2018-08-21 14:32:23 +0200 | [diff] [blame] | 371 | } |