Coverage for procpath/sqliteviz.py: 100%

53 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2025-09-03 17:55 +0000

1import hashlib 

2import http.server 

3import io 

4import json 

5import logging 

6import textwrap 

7import zipfile 

8from functools import partial 

9from pathlib import Path 

10from urllib.request import urlopen 

11 

12from . import procret 

13 

14 

15__all__ = 'get_visualisation_bundle', 'install_sqliteviz', 'serve_dir', 'symlink_database' 

16 

17logger = logging.getLogger(__package__) 

18 

19 

20def install_sqliteviz(zip_url: str, target_dir: Path): 

21 response = urlopen(zip_url) (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missing

22 with zipfile.ZipFile(io.BytesIO(response.read())) as z: (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missing

23 z.extractall(target_dir) (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missing

24 

25 bundle = json.dumps(get_visualisation_bundle(), sort_keys=True) (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missing

26 (target_dir / 'inquiries.json').write_text(bundle) (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missing

27 

28 

29def _get_line_chart_config(title: str) -> dict: 

30 return { (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missingprocpath.test.unit.TestSqlitevizQuery.test_total_cpu_usageprocpath.test.unit.TestSqlitevizQuery.test_total_disk_ioprocpath.test.unit.TestSqlitevizQuery.test_total_memory_consumption

31 'data': [{ 

32 'meta': {'columnNames': {'x': 'ts', 'y': 'value'}}, 

33 'mode': 'lines', 

34 'type': 'scatter', 

35 'x': None, 

36 'xsrc': 'ts', 

37 'y': None, 

38 'ysrc': 'value', 

39 'transforms': [{ 

40 'groups': None, 

41 'groupssrc': 'pid', 

42 'meta': {'columnNames': {'groups': 'pid'}}, 

43 'styles': [], 

44 'type': 'groupby', 

45 }], 

46 }], 

47 'frames': [], 

48 'layout': { 

49 'autosize': True, 

50 'title': {'text': title}, 

51 'xaxis': { 

52 'autorange': True, 

53 'range': [], 

54 'type': 'date' 

55 }, 

56 'yaxis': { 

57 'autorange': True, 

58 'range': [], 

59 'type': 'linear' 

60 }, 

61 }, 

62 } 

63 

64 

65def _get_sqliteviz_only_charts(): 

66 return [ (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missingprocpath.test.unit.TestSqlitevizQuery.test_total_cpu_usageprocpath.test.unit.TestSqlitevizQuery.test_total_disk_ioprocpath.test.unit.TestSqlitevizQuery.test_total_memory_consumption

67 # Process Timeline PID 

68 { 

69 'id': 'csfOTEpzlFfYz7OUc2aGI', 

70 'createdAt': '2023-09-03T12:00:00Z', 

71 'name': 'Process Timeline, PID', 

72 'query': textwrap.dedent(''' 

73 WITH RECURSIVE tree(pid, ppid, pid_comm) AS ( 

74 SELECT stat_pid, stat_ppid, stat_pid || ' ' || stat_comm 

75 FROM record 

76 GROUP BY 1 

77 UNION 

78 SELECT pid, stat_ppid, stat_pid || ' ' || stat_comm 

79 FROM record, tree 

80 WHERE record.stat_pid = tree.ppid 

81 ), lookup AS ( 

82 SELECT pid, group_concat(pid_comm, ' / ') path_to_root 

83 FROM tree 

84 GROUP BY 1 

85 ) 

86 SELECT 

87 ts * 1000 AS ts, 

88 stat_pid, 

89 stat_pid || ' ' || stat_comm AS pid_comm, 

90 iif( 

91 length(cmdline) > 0, 

92 substr(cmdline, 0, 75) || iif(length(cmdline) > 75, '...', ''), 

93 stat_comm 

94 ) || '<br>' || path_to_root AS cmd 

95 FROM record 

96 JOIN lookup ON stat_pid = pid 

97 ''').strip(), 

98 'viewType': 'chart', 

99 'viewOptions': { 

100 'data': [{ 

101 'type': 'scattergl', 

102 'mode': 'markers', 

103 'meta': {'columnNames': {'x': 'ts', 'y': 'stat_pid', 'text': 'cmd'}}, 

104 'transforms': [{ 

105 'type': 'groupby', 

106 'styles': [], 

107 'meta': {'columnNames': {'groups': 'pid_comm'}}, 

108 'groups': None, 

109 'groupssrc': 'pid_comm', 

110 }], 

111 'y': None, 

112 'ysrc': 'stat_pid', 

113 'x': None, 

114 'xsrc': 'ts', 

115 'text': None, 

116 'textsrc': 'cmd', 

117 'marker': {'size': 12, 'maxdisplayed': 0}, 

118 'line': {'width': 3}, 

119 'hoverinfo': 'x+text', 

120 }], 

121 'layout': { 

122 'xaxis': { 

123 'type': 'date', 

124 'range': [], 

125 'autorange': True, 

126 }, 

127 'yaxis': { 

128 'type': 'category', 

129 'range': [], 

130 'autorange': True, 

131 'showticklabels': False, 

132 }, 

133 'title': {'text': 'Process Timeline, PID'}, 

134 'hovermode': 'closest', 

135 }, 

136 'frames': [], 

137 }, 

138 }, 

139 # Process Timeline CPU 

140 { 

141 'id': '4PBtpi7inEAe-yjtRHCi0', 

142 'createdAt': '2023-09-03T12:00:00Z', 

143 'name': 'Process Timeline, CPU', 

144 'query': textwrap.dedent(''' 

145 WITH RECURSIVE tree(pid, ppid, pid_comm) AS ( 

146 SELECT stat_pid, stat_ppid, stat_pid || ' ' || stat_comm 

147 FROM record 

148 GROUP BY 1 

149 UNION 

150 SELECT pid, stat_ppid, stat_pid || ' ' || stat_comm 

151 FROM record, tree 

152 WHERE record.stat_pid = tree.ppid 

153 ), path_lookup AS ( 

154 SELECT pid, group_concat(pid_comm, ' / ') path_to_root 

155 FROM tree 

156 GROUP BY 1 

157 ), cpu_diff AS ( 

158 SELECT 

159 ts, 

160 stat_pid, 

161 stat_ppid, 

162 stat_priority, 

163 stat_comm, 

164 cmdline, 

165 stat_utime + stat_stime - LAG(stat_utime + stat_stime) OVER ( 

166 PARTITION BY stat_pid 

167 ORDER BY record_id 

168 ) tick_diff, 

169 ts - LAG(ts) OVER ( 

170 PARTITION BY stat_pid 

171 ORDER BY record_id 

172 ) ts_diff 

173 FROM record 

174 ), record_ext AS ( 

175 SELECT 

176 *, 

177 100.0 * tick_diff / ( 

178 SELECT value FROM meta WHERE key = 'clock_ticks' 

179 ) / ts_diff cpu_usage 

180 FROM cpu_diff 

181 WHERE tick_diff IS NOT NULL 

182 ) 

183 SELECT 

184 ts * 1000 AS ts, 

185 stat_pid, 

186 stat_pid || ' ' || stat_comm AS pid_comm, 

187 power(1.02, -r.stat_priority) priority_size, 

188 cpu_usage, 

189 iif( 

190 length(cmdline) > 0, 

191 substr(cmdline, 0, 75) || iif(length(cmdline) > 75, '...', ''), 

192 stat_comm 

193 ) 

194 || '<br>' || path_to_root 

195 || '<br>' || 'CPU, %: ' || printf('%.2f', cpu_usage) 

196 || '<br>' || 'priority: ' || stat_priority AS cmd 

197 FROM record_ext r 

198 JOIN path_lookup p ON r.stat_pid = p.pid 

199 -- Tune the following CPU usage inequality for a clearer figure 

200 WHERE cpu_usage > 0 

201 ''').strip(), 

202 'viewType': 'chart', 

203 'viewOptions': { 

204 'data': [{ 

205 'type': 'scattergl', 

206 'mode': 'markers', 

207 'meta': { 

208 'columnNames': { 

209 'text': 'cmd', 

210 'x': 'ts', 

211 'y': 'stat_pid', 

212 'marker': { 

213 'color': 'cpu_usage', 

214 'size': 'priority_size', 

215 }, 

216 }, 

217 }, 

218 'y': None, 

219 'ysrc': 'stat_pid', 

220 'x': None, 

221 'xsrc': 'ts', 

222 'text': None, 

223 'textsrc': 'cmd', 

224 'marker': { 

225 'maxdisplayed': 0, 

226 'color': None, 

227 'colorsrc': 'cpu_usage', 

228 'size': None, 

229 'sizesrc': 'priority_size', 

230 'sizeref': 0.00667, 

231 'sizemode': 'area', 

232 'showscale': True, 

233 'colorbar': {'title': {'text': 'CPU, %'}}, 

234 'line': {'width': 0}, 

235 }, 

236 'line': {'width': 3}, 

237 'hoverinfo': 'x+text', 

238 }], 

239 'layout': { 

240 'xaxis': { 

241 'type': 'date', 

242 'range': [], 

243 'autorange': True, 

244 }, 

245 'yaxis': { 

246 'type': 'category', 

247 'range': [], 

248 'autorange': True, 

249 'showticklabels': False, 

250 }, 

251 'title': {'text': 'Process Timeline, CPU'}, 

252 'hovermode': 'closest', 

253 }, 

254 'frames': [], 

255 }, 

256 }, 

257 # Process Tree 

258 { 

259 'id': '3XXe7a80GvD6Trk9FyXRz', 

260 'name': 'Process Tree', 

261 'createdAt': '2023-09-03T12:00:00Z', 

262 'query': textwrap.dedent(''' 

263 WITH lookup(pid, num) AS ( 

264 SELECT stat_pid, ROW_NUMBER() OVER(ORDER BY stat_pid) - 1 

265 FROM record 

266 GROUP BY 1 

267 ), nodes AS ( 

268 SELECT 

269 stat_pid, 

270 -- Opt-in for special bare column processing to prefer the 

271 -- first values (the minimum value is not used per se) 

272 MIN(ts), 

273 stat_ppid, 

274 stat_pid || ' ' || stat_comm AS pid_comm, 

275 iif( 

276 length(cmdline) > 0, 

277 substr(cmdline, 0, 75) || iif(length(cmdline) > 75, '...', ''), 

278 stat_comm 

279 ) cmd 

280 FROM record 

281 GROUP BY 1 

282 ) 

283 SELECT p.num p_num, pp.num pp_num, pid_comm, cmd, 1 value 

284 FROM nodes 

285 JOIN lookup p ON stat_pid = p.pid 

286 LEFT JOIN lookup pp ON stat_ppid = pp.pid 

287 ORDER BY p.num 

288 ''').strip(), 

289 'viewType': 'chart', 

290 'viewOptions': { 

291 'data': [ 

292 { 

293 'type': 'sankey', 

294 'mode': 'markers', 

295 'node': {'labelsrc': 'pid_comm'}, 

296 'link': { 

297 'valuesrc': 'value', 

298 'targetsrc': 'p_num', 

299 'sourcesrc': 'pp_num', 

300 'labelsrc': 'cmd' 

301 }, 

302 'meta': { 

303 'columnNames': { 

304 'node': {'label': 'pid_comm'}, 

305 'link': { 

306 'source': 'pp_num', 

307 'target': 'p_num', 

308 'value': 'value', 

309 'label': 'cmd' 

310 } 

311 } 

312 }, 

313 'orientation': 'h', 

314 'hoverinfo': 'name', 

315 'arrangement': 'freeform' 

316 } 

317 ], 

318 'layout': { 

319 'xaxis': {'range': [], 'autorange': True}, 

320 'yaxis': {'range': [], 'autorange': True}, 

321 'autosize': True, 

322 'title': {'text': 'Process Tree'} 

323 }, 

324 'frames': [] 

325 } 

326 }, 

327 # Total Memory Consumption 

328 { 

329 'id': 'boSs15w7Endl5V9bABjXv', 

330 'createdAt': '2023-09-03T12:00:00Z', 

331 'name': 'Total Resident Set Size, MiB', 

332 'query': textwrap.dedent(''' 

333 WITH downsampled_record AS ( 

334 SELECT 

335 stat_pid, 

336 -- Adjust downsampling factor 

337 CAST(ts / 10 as INT) * 10 ts, 

338 stat_comm, 

339 cmdline, 

340 MAX(stat_rss) stat_rss 

341 FROM record 

342 -- Adjust time range 

343 -- WHERE ts BETWEEN 

344 -- unixepoch('2025-01-20 00:00:00', 'utc') 

345 -- AND unixepoch('2025-01-30 00:00:00', 'utc') 

346 GROUP BY 1, 2 

347 ), proc_group AS ( 

348 SELECT 

349 -- Comment "stat_comm" group and uncomment this to have coarser grouping 

350 -- CASE 

351 -- WHEN cmdline LIKE '%firefox%' THEN '1. firefox' 

352 -- WHEN cmdline LIKE '%chromium%' THEN '2. chromium' 

353 -- ELSE '3. other' 

354 -- END pgroup, 

355 stat_comm pgroup, 

356 ts, 

357 SUM(stat_rss) 

358 / 1024.0 / 1024 * (SELECT value FROM meta WHERE key = 'page_size') value 

359 FROM downsampled_record 

360 GROUP BY ts, 1 

361 ORDER BY ts 

362 ), proc_group_avg AS ( 

363 SELECT 

364 ts, 

365 pgroup, 

366 AVG(value) OVER ( 

367 PARTITION BY pgroup 

368 ORDER BY ts 

369 -- Adjust centred moving average window 

370 RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING 

371 ) value 

372 FROM proc_group 

373 ), total_lookup(ts, total) AS ( 

374 SELECT ts, SUM(value) 

375 FROM proc_group_avg 

376 GROUP BY 1 

377 ) 

378 SELECT 

379 proc_group_avg.ts * 1000 ts, 

380 pgroup, 

381 value, 

382 'total: ' || round(total, 1) || ' MiB' total 

383 FROM proc_group_avg 

384 JOIN total_lookup ON proc_group_avg.ts = total_lookup.ts 

385 ORDER BY ts 

386 ''').strip(), 

387 'viewType': 'chart', 

388 'viewOptions': { 

389 'data': [{ 

390 'type': 'scatter', 

391 'mode': 'lines', 

392 'meta': {'columnNames': {'x': 'ts', 'y': 'value'}}, 

393 'transforms': [{ 

394 'type': 'groupby', 

395 'groupssrc': 'pgroup', 

396 'groups': None, 

397 'styles': [], 

398 'meta': {'columnNames': {'groups': 'pgroup'}}, 

399 }], 

400 'stackgroup': 1, 

401 'x': None, 

402 'xsrc': 'ts', 

403 'y': None, 

404 'ysrc': 'value', 

405 'text': None, 

406 'textsrc': 'total', 

407 'hoverinfo': 'x+text+name', 

408 }], 

409 'layout': { 

410 'xaxis': { 

411 'type': 'date', 

412 'range': [], 

413 'autorange': True, 

414 }, 

415 'yaxis': { 

416 'type': 'linear', 

417 'range': [], 

418 'autorange': True, 

419 'separatethousands': True, 

420 }, 

421 'title': {'text': 'Total Resident Set Size, MiB'}, 

422 'hovermode': 'closest', 

423 }, 

424 'frames': [] 

425 }, 

426 }, 

427 # Total CPU Usage 

428 { 

429 'id': 'kd17-XGI85L2Oogj74Uyb', 

430 'createdAt': '2023-09-03T12:00:00Z', 

431 'name': 'Total CPU Usage, %', 

432 'query': textwrap.dedent(''' 

433 WITH downsampled_record AS ( 

434 SELECT 

435 stat_pid, 

436 -- Adjust downsampling factor 

437 CAST(ts / 10 as INT) * 10 ts, 

438 stat_comm, 

439 cmdline, 

440 MAX(stat_utime) stat_utime, 

441 MAX(stat_stime) stat_stime 

442 FROM record 

443 -- Adjust time range 

444 -- WHERE ts BETWEEN 

445 -- unixepoch('2025-01-20 00:00:00', 'utc') 

446 -- AND unixepoch('2025-01-30 00:00:00', 'utc') 

447 GROUP BY 1, 2 

448 ), proc_cpu_diff AS ( 

449 SELECT 

450 stat_pid, 

451 ts, 

452 stat_comm, 

453 cmdline, 

454 stat_utime + stat_stime - LAG(stat_utime + stat_stime) OVER ( 

455 PARTITION BY stat_pid 

456 ORDER BY ts 

457 ) tick_diff, 

458 ts - LAG(ts) OVER ( 

459 PARTITION BY stat_pid 

460 ORDER BY ts 

461 ) ts_diff 

462 FROM downsampled_record 

463 ), proc_group AS ( 

464 SELECT 

465 -- Comment "stat_comm" group and uncomment this to have coarser grouping 

466 -- CASE 

467 -- WHEN cmdline LIKE '%firefox%' THEN '1. firefox' 

468 -- WHEN cmdline LIKE '%chromium%' THEN '2. chromium' 

469 -- ELSE '3. other' 

470 -- END pgroup, 

471 stat_comm pgroup, 

472 ts, 

473 SUM(tick_diff) tick_diff, 

474 AVG(ts_diff) ts_diff 

475 FROM proc_cpu_diff 

476 WHERE tick_diff IS NOT NULL 

477 GROUP BY ts, 1 

478 ORDER BY ts 

479 ), proc_group_avg AS ( 

480 SELECT 

481 ts, 

482 pgroup, 

483 AVG( 

484 100.0 

485 * tick_diff 

486 / ts_diff 

487 / (SELECT value FROM meta WHERE key = 'clock_ticks') 

488 ) OVER ( 

489 PARTITION BY pgroup 

490 ORDER BY ts 

491 -- Adjust centred moving average window 

492 RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING 

493 ) value 

494 FROM proc_group 

495 ), total_lookup(ts, total) AS ( 

496 SELECT ts, SUM(value) 

497 FROM proc_group_avg 

498 GROUP BY 1 

499 ) 

500 SELECT 

501 proc_group_avg.ts * 1000 ts, 

502 pgroup, 

503 value, 

504 'total: ' || round(total, 1) || ' %' total 

505 FROM proc_group_avg 

506 JOIN total_lookup ON proc_group_avg.ts = total_lookup.ts 

507 ORDER BY ts 

508 ''').strip(), 

509 'viewType': 'chart', 

510 'viewOptions': { 

511 'data': [{ 

512 'type': 'scatter', 

513 'mode': 'lines', 

514 'meta': {'columnNames': {'x': 'ts', 'y': 'value'}}, 

515 'transforms': [{ 

516 'type': 'groupby', 

517 'groupssrc': 'pgroup', 

518 'groups': None, 

519 'styles': [], 

520 'meta': {'columnNames': {'groups': 'pgroup'}}, 

521 }], 

522 'stackgroup': 1, 

523 'x': None, 

524 'xsrc': 'ts', 

525 'y': None, 

526 'ysrc': 'value', 

527 'text': None, 

528 'textsrc': 'total', 

529 'hoverinfo': 'x+text+name', 

530 }], 

531 'layout': { 

532 'xaxis': { 

533 'type': 'date', 

534 'range': [], 

535 'autorange': True, 

536 }, 

537 'yaxis': { 

538 'type': 'linear', 

539 'range': [], 

540 'autorange': True, 

541 'separatethousands': True, 

542 }, 

543 'title': {'text': 'Total CPU Usage, %'}, 

544 'hovermode': 'closest', 

545 }, 

546 'frames': [] 

547 }, 

548 }, 

549 # Total Disk IO 

550 { 

551 'id': 'ZXYEQObtemObLtygW731A', 

552 'createdAt': '2023-09-03T12:00:00Z', 

553 'name': 'Total Disk IO, B/s and % IO wait', 

554 'query': textwrap.dedent(''' 

555 WITH downsampled_record AS ( 

556 SELECT 

557 stat_pid, 

558 -- Adjust downsampling factor 

559 CAST(ts / 10 as INT) * 10 ts, 

560 stat_comm, 

561 cmdline, 

562 MAX(io_read_bytes) io_read_bytes, 

563 MAX(io_write_bytes) io_write_bytes, 

564 MAX(stat_delayacct_blkio_ticks) stat_delayacct_blkio_ticks 

565 FROM record 

566 -- Adjust time range 

567 -- WHERE ts BETWEEN 

568 -- unixepoch('2025-01-20 00:00:00', 'utc') 

569 -- AND unixepoch('2025-01-30 00:00:00', 'utc') 

570 GROUP BY 1, 2 

571 ), proc_io_diff AS ( 

572 SELECT 

573 stat_pid, 

574 ts, 

575 stat_comm, 

576 cmdline, 

577 io_read_bytes - LAG(io_read_bytes) OVER ( 

578 PARTITION BY stat_pid 

579 ORDER BY ts 

580 ) rb, 

581 io_write_bytes - LAG(io_write_bytes) OVER ( 

582 PARTITION BY stat_pid 

583 ORDER BY ts 

584 ) wb, 

585 stat_delayacct_blkio_ticks - LAG(stat_delayacct_blkio_ticks) OVER ( 

586 PARTITION BY stat_pid 

587 ORDER BY ts 

588 ) tick_diff, 

589 ts - LAG(ts) OVER ( 

590 PARTITION BY stat_pid 

591 ORDER BY ts 

592 ) ts_diff 

593 FROM downsampled_record 

594 ), proc_group AS ( 

595 SELECT 

596 -- Comment "stat_comm" group and uncomment this to have coarser grouping 

597 -- CASE 

598 -- WHEN cmdline LIKE '%firefox%' THEN '1. firefox' 

599 -- WHEN cmdline LIKE '%chromium%' THEN '2. chromium' 

600 -- ELSE '3. other' 

601 -- END pgroup, 

602 stat_comm pgroup, 

603 ts, 

604 SUM(rb) rb, 

605 SUM(wb) wb, 

606 SUM(tick_diff) tick_diff, 

607 AVG(ts_diff) ts_diff 

608 FROM proc_io_diff 

609 WHERE tick_diff IS NOT NULL 

610 GROUP BY ts, 1 

611 ORDER BY ts 

612 ), proc_group_avg AS ( 

613 SELECT 

614 ts, 

615 pgroup, 

616 AVG(rb / ts_diff) OVER ( 

617 PARTITION BY pgroup 

618 ORDER BY ts 

619 -- Adjust centred moving average window 

620 RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING 

621 ) rbs, 

622 AVG(-wb / ts_diff) OVER ( 

623 PARTITION BY pgroup 

624 ORDER BY ts 

625 -- Adjust centred moving average window 

626 RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING 

627 ) wbs, 

628 AVG( 

629 100.0 

630 * tick_diff 

631 / ts_diff 

632 / (SELECT value FROM meta WHERE key = 'clock_ticks') 

633 ) OVER ( 

634 PARTITION BY pgroup 

635 ORDER BY ts 

636 -- Adjust centred moving average window 

637 RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING 

638 ) iowait 

639 FROM proc_group 

640 ) 

641 SELECT 

642 proc_group_avg.ts * 1000 ts, 

643 pgroup, 

644 rbs, 

645 wbs, 

646 iowait 

647 FROM proc_group_avg 

648 ORDER BY ts 

649 ''').strip(), 

650 'viewType': 'chart', 

651 'viewOptions': { 

652 'data': [ 

653 { 

654 'type': 'scatter', 

655 'mode': 'lines', 

656 'name': 'read', 

657 'meta': {'columnNames': {'x': 'ts', 'y': 'rbs'}}, 

658 'transforms': [{ 

659 'type': 'groupby', 

660 'groupssrc': 'pgroup', 

661 'groups': None, 

662 'styles': [], 

663 'meta': {'columnNames': {'groups': 'pgroup'}}, 

664 }], 

665 'stackgroup': 1, 

666 'x': None, 

667 'xsrc': 'ts', 

668 'y': None, 

669 'ysrc': 'rbs', 

670 }, 

671 { 

672 'type': 'scatter', 

673 'mode': 'lines', 

674 'name': 'write', 

675 'meta': {'columnNames': {'x': 'ts', 'y': 'wbs'}}, 

676 'transforms': [{ 

677 'type': 'groupby', 

678 'groupssrc': 'pgroup', 

679 'groups': None, 

680 'styles': [], 

681 'meta': {'columnNames': {'groups': 'pgroup'}}, 

682 }], 

683 'stackgroup': 2, 

684 'x': None, 

685 'xsrc': 'ts', 

686 'y': None, 

687 'ysrc': 'wbs', 

688 }, 

689 { 

690 'type': 'scatter', 

691 'mode': 'lines', 

692 'name': 'iowait', 

693 'meta': {'columnNames': {'x': 'ts', 'y': 'iowait'}}, 

694 'transforms': [{ 

695 'type': 'groupby', 

696 'groupssrc': 'pgroup', 

697 'groups': None, 

698 'styles': [], 

699 'meta': {'columnNames': {'groups': 'pgroup'}}, 

700 }], 

701 'x': None, 

702 'xsrc': 'ts', 

703 'y': None, 

704 'ysrc': 'iowait', 

705 'yaxis': 'y2', 

706 'line': {'dash': 'dot'}, 

707 }, 

708 ], 

709 'layout': { 

710 'xaxis': { 

711 'type': 'date', 

712 'range': [], 

713 'autorange': True, 

714 'domain': [0, 0.96], 

715 }, 

716 'yaxis': { 

717 'type': 'linear', 

718 'range': [], 

719 'autorange': True, 

720 'separatethousands': True, 

721 'title': {'text': '-write|+read byte/second'}, 

722 }, 

723 'yaxis2': { 

724 'side': 'right', 

725 'overlaying': 'y', 

726 'type': 'linear', 

727 'range': [0, 100], 

728 'autorange': False, 

729 'title': {'text': 'IO wait, %'}, 

730 }, 

731 'title': {'text': 'Total Disk IO, throughput and IO wait'}, 

732 'hovermode': 'closest', 

733 'legend': {'traceorder': 'normal'}, 

734 }, 

735 'frames': [], 

736 }, 

737 }, 

738 ] 

739 

740 

741def get_visualisation_bundle() -> dict: 

742 """Get Sqliteviz import-able visualisation bundle.""" 

743 

744 inquiries = [] (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missingprocpath.test.unit.TestSqlitevizQuery.test_total_cpu_usageprocpath.test.unit.TestSqlitevizQuery.test_total_disk_ioprocpath.test.unit.TestSqlitevizQuery.test_total_memory_consumption

745 result = {'version': 2, 'inquiries': inquiries} (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missingprocpath.test.unit.TestSqlitevizQuery.test_total_cpu_usageprocpath.test.unit.TestSqlitevizQuery.test_total_disk_ioprocpath.test.unit.TestSqlitevizQuery.test_total_memory_consumption

746 

747 for query in procret.registry.values(): (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missingprocpath.test.unit.TestSqlitevizQuery.test_total_cpu_usageprocpath.test.unit.TestSqlitevizQuery.test_total_disk_ioprocpath.test.unit.TestSqlitevizQuery.test_total_memory_consumption

748 query_text = query.get_short_query(ts_as_milliseconds=True) (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missingprocpath.test.unit.TestSqlitevizQuery.test_total_cpu_usageprocpath.test.unit.TestSqlitevizQuery.test_total_disk_ioprocpath.test.unit.TestSqlitevizQuery.test_total_memory_consumption

749 inquiries.append({ (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missingprocpath.test.unit.TestSqlitevizQuery.test_total_cpu_usageprocpath.test.unit.TestSqlitevizQuery.test_total_disk_ioprocpath.test.unit.TestSqlitevizQuery.test_total_memory_consumption

750 'id': hashlib.md5(query_text.encode()).hexdigest()[:21], 

751 'createdAt': '2023-09-03T12:00:00Z', 

752 'name': query.title, 

753 'query': textwrap.dedent(query_text).strip(), 

754 'viewType': 'chart', 

755 'viewOptions': _get_line_chart_config(query.title), 

756 }) 

757 

758 inquiries.extend(_get_sqliteviz_only_charts()) (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missingprocpath.test.unit.TestSqlitevizQuery.test_total_cpu_usageprocpath.test.unit.TestSqlitevizQuery.test_total_disk_ioprocpath.test.unit.TestSqlitevizQuery.test_total_memory_consumption

759 

760 return result (empty)procpath.test.cmd.TestExploreCommand.test_exploreprocpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missingprocpath.test.unit.TestSqlitevizQuery.test_total_cpu_usageprocpath.test.unit.TestSqlitevizQuery.test_total_disk_ioprocpath.test.unit.TestSqlitevizQuery.test_total_memory_consumption

761 

762 

763class HttpRequestHandler(http.server.SimpleHTTPRequestHandler): 

764 def send_head(self): 

765 # Disable cache validation based on modified timestamp of the 

766 # file because it's a symlink pointing to different files, and 

767 # next one can easily be older than current one 

768 if self.path == '/db.sqlite': 

769 del self.headers['If-Modified-Since'] 

770 

771 return super().send_head() 

772 

773 def end_headers(self): 

774 if self.path == '/db.sqlite': 

775 # The "no-store" response directive indicates that caches 

776 # should not store this response. No point to try to cache 

777 # big database files 

778 self.send_header('Cache-Control', 'no-store') 

779 else: 

780 # The "no-cache" response directive indicates that the 

781 # response can be stored in caches, but the response must 

782 # be validated with the origin server before each reuse 

783 self.send_header('Cache-Control', 'no-cache') 

784 

785 super().end_headers() 

786 

787 

788def serve_dir( 

789 bind: str, port: int, directory: str, *, server_cls=http.server.ThreadingHTTPServer 

790): 

791 handler_cls = partial(HttpRequestHandler, directory=directory) 

792 with server_cls((bind, port), handler_cls) as httpd: 

793 httpd.serve_forever() 

794 

795 

796def symlink_database(database_file: str, sqliteviz_dir: Path) -> Path: 

797 db_path = Path(database_file).absolute() procpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missingprocpath.test.cmd.TestExploreCommand.test_explore_serveprocpath.test.cmd.TestExploreCommand.test_symlink_database

798 if not db_path.exists(): procpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_preload_database_missingprocpath.test.cmd.TestExploreCommand.test_explore_serveprocpath.test.cmd.TestExploreCommand.test_symlink_database

799 raise FileNotFoundError procpath.test.cmd.TestExploreCommand.test_explore_preload_database_missing

800 

801 sym_path = sqliteviz_dir / 'db.sqlite' procpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_serveprocpath.test.cmd.TestExploreCommand.test_symlink_database

802 sym_path.unlink(missing_ok=True) procpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_serveprocpath.test.cmd.TestExploreCommand.test_symlink_database

803 sym_path.symlink_to(db_path) procpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_serveprocpath.test.cmd.TestExploreCommand.test_symlink_database

804 return sym_path procpath.test.cmd.TestExploreCommand.test_explore_preload_databaseprocpath.test.cmd.TestExploreCommand.test_explore_serveprocpath.test.cmd.TestExploreCommand.test_symlink_database