Coverage for procpath/sqliteviz.py: 100%
53 statements
« prev ^ index » next coverage.py v6.5.0, created at 2025-09-03 17:55 +0000
« 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
12from . import procret
15__all__ = 'get_visualisation_bundle', 'install_sqliteviz', 'serve_dir', 'symlink_database'
17logger = logging.getLogger(__package__)
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
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
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 }
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 ]
741def get_visualisation_bundle() -> dict:
742 """Get Sqliteviz import-able visualisation bundle."""
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
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 })
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
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
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']
771 return super().send_head()
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')
785 super().end_headers()
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()
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
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