(node: Node)
| 577 | |
| 578 | def render_process_resource_usage(report: ClusterReport, per_process_df: pd.DataFrame): |
| 579 | def render_node(node: Node) -> Optional[Model]: |
| 580 | node_data = per_process_df[per_process_df[HOSTNAME_KEY] == node.hostname] |
| 581 | pids = sorted(node_data[PID_KEY].unique()) |
| 582 | |
| 583 | def render_process(pid: int) -> Optional[Model]: |
| 584 | process_data = node_data[node_data[PID_KEY] == pid] |
| 585 | max_rss = process_data[RSS_KEY].max() |
| 586 | avg_cpu = process_data[AVG_CPU_KEY].mean() |
| 587 | process = [p for p in report.cluster.nodes[node.hostname].processes if p.pid == pid] |
| 588 | if not process: |
| 589 | logging.warning(f"Process {node.hostname}/{pid} not found in cluster") |
| 590 | return |
| 591 | process = process[0] |
| 592 | |
| 593 | range = get_time_range(process_data[DATETIME_KEY]) |
| 594 | |
| 595 | mib_divisor = 1024.0 * 1024 |
| 596 | |
| 597 | time = process_data[DATETIME_KEY] |
| 598 | mem = resample(process_data[RSS_KEY] / mib_divisor, time) |
| 599 | mem_figure = prepare_time_range_figure(range) |
| 600 | mem_figure.yaxis[0].formatter = NumeralTickFormatter() |
| 601 | mem_figure.yaxis.axis_label = "RSS (MiB)" |
| 602 | mem_figure.y_range = Range1d(0, max_rss / mib_divisor + 100.0) |
| 603 | mem_figure.add_tools(HoverTool(tooltips=[("RSS (MiB)", "@rss")])) |
| 604 | |
| 605 | data = ColumnDataSource(dict(rss=mem, x=mem.index)) |
| 606 | mem_figure.line(x="x", y="rss", color="red", legend_label="Memory", source=data) |
| 607 | |
| 608 | cpu = resample(process_data[AVG_CPU_KEY], time) |
| 609 | cpu_figure = prepare_time_range_figure(range) |
| 610 | cpu_figure.yaxis[0].formatter = NumeralTickFormatter(format="0 %") |
| 611 | cpu_figure.yaxis.axis_label = "Avg. CPU usage (%)" |
| 612 | cpu_figure.y_range = Range1d(0, 1) |
| 613 | cpu_figure.add_tools(HoverTool(tooltips=[("Avg. CPU usage", "@cpu")])) |
| 614 | |
| 615 | data = ColumnDataSource(dict(cpu=cpu / 100.0, x=cpu.index)) |
| 616 | cpu_figure.line(x="x", y="cpu", color="blue", legend_label="CPU usage (%)", source=data) |
| 617 | |
| 618 | cpu_times = [ |
| 619 | ("User CPU time", PROCESS_CPU_TIME_USER_KEY), |
| 620 | ("System CPU time", PROCESS_CPU_TIME_SYSTEM_KEY), |
| 621 | ("User + children CPU time", PROCESS_CPU_TIME_USER_CHILDREN_KEY), |
| 622 | ("System + children CPU time", PROCESS_CPU_TIME_SYSTEM_CHILDREN_KEY), |
| 623 | ] |
| 624 | cpu_time_figures = [render_process_cpu_time(process_data, key, time, label) for (label, key) in cpu_times] |
| 625 | |
| 626 | summary = PreText( |
| 627 | text=f""" |
| 628 | PID: {pid} |
| 629 | Key: {process.key} |
| 630 | Max. RSS: {humanize.naturalsize(max_rss, binary=True)} |
| 631 | Avg. CPU: {avg_cpu:.02f} % |
| 632 | """.strip() |
| 633 | ) |
| 634 | |
| 635 | right_col = Column(children=cpu_time_figures) |
| 636 | left_col = Column(children=[summary, mem_figure, cpu_figure]) |
nothing calls this directly
no test coverage detected