diff --git a/perun/collect/kperf/run.py b/perun/collect/kperf/run.py index f354767c2..28c119c39 100755 --- a/perun/collect/kperf/run.py +++ b/perun/collect/kperf/run.py @@ -2,9 +2,7 @@ from __future__ import annotations # Standard Imports -from pathlib import Path from typing import Any -import os import subprocess import time @@ -16,6 +14,7 @@ from perun.collect.kperf import parser from perun.logic import runner from perun.utils import log +from perun.utils.common import script_kit from perun.utils.structs import Executable, CollectStatus from perun.utils.external import commands @@ -32,8 +31,7 @@ def before(**_: Any) -> tuple[CollectStatus, str, dict[str, Any]]: log.minor_fail(f"{log.cmd_style('perf')}", "not-executable") # Check that helper script can be run - script_dir = Path(Path(__file__).resolve().parent, "scripts") - parse_script = os.path.join(script_dir, "stackcollapse-perf.pl") + parse_script = script_kit.get_script("stackcollapse-perf.pl") if commands.is_executable(f'echo "" | {parse_script}'): log.minor_success(f"{log.cmd_style(parse_script)}", "executable") else: @@ -42,7 +40,7 @@ def before(**_: Any) -> tuple[CollectStatus, str, dict[str, Any]]: if not all_found: log.minor_fail("Checking dependencies") - return CollectStatus.ERROR, "Some depedencies cannot be run", {} + return CollectStatus.ERROR, "Some dependencies cannot be run", {} else: log.minor_success("Checking dependencies") @@ -56,8 +54,7 @@ def run_perf(executable: Executable, run_with_sudo: bool = False) -> str: :param run_with_sudo: if the command should be run with sudo :return: parsed output of perf """ - script_dir = Path(Path(__file__).resolve().parent, "scripts") - parse_script = os.path.join(script_dir, "stackcollapse-perf.pl") + parse_script = script_kit.get_script("stackcollapse-perf.pl") if run_with_sudo: perf_record_command = f"sudo perf record -q -g -o collected.data {executable}" diff --git a/perun/scripts/difffolded.pl b/perun/scripts/difffolded.pl new file mode 100755 index 000000000..4c76c2ecf --- /dev/null +++ b/perun/scripts/difffolded.pl @@ -0,0 +1,115 @@ +#!/usr/bin/perl -w +# +# difffolded.pl diff two folded stack files. Use this for generating +# flame graph differentials. +# +# USAGE: ./difffolded.pl [-hns] folded1 folded2 | ./flamegraph.pl > diff2.svg +# +# Options are described in the usage message (-h). +# +# The flamegraph will be colored based on higher samples (red) and smaller +# samples (blue). The frame widths will be based on the 2nd folded file. +# This might be confusing if stack frames disappear entirely; it will make +# the most sense to ALSO create a differential based on the 1st file widths, +# while switching the hues; eg: +# +# ./difffolded.pl folded2 folded1 | ./flamegraph.pl --negate > diff1.svg +# +# Here's what they mean when comparing a before and after profile: +# +# diff1.svg: widths show the before profile, colored by what WILL happen +# diff2.svg: widths show the after profile, colored by what DID happen +# +# INPUT: See stackcollapse* programs. +# +# OUTPUT: The full list of stacks, with two columns, one from each file. +# If a stack wasn't present in a file, the column value is zero. +# +# folded_stack_trace count_from_folded1 count_from_folded2 +# +# eg: +# +# funca;funcb;funcc 31 33 +# ... +# +# COPYRIGHT: Copyright (c) 2014 Brendan Gregg. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# (http://www.gnu.org/copyleft/gpl.html) +# +# 28-Oct-2014 Brendan Gregg Created this. + +use strict; +use Getopt::Std; + +# defaults +my $normalize = 0; # make sample counts equal +my $striphex = 0; # strip hex numbers + +sub usage { + print STDERR < diff2.svg + -h # help message + -n # normalize sample counts + -s # strip hex numbers (addresses) +See stackcollapse scripts for generating folded files. +Also consider flipping the files and hues to highlight reduced paths: +$0 folded2 folded1 | ./flamegraph.pl --negate > diff1.svg +USAGE_END + exit 2; +} + +usage() if @ARGV < 2; +our($opt_h, $opt_n, $opt_s); +getopts('ns') or usage(); +usage() if $opt_h; +$normalize = 1 if defined $opt_n; +$striphex = 1 if defined $opt_s; + +my ($total1, $total2) = (0, 0); +my %Folded; + +my $file1 = $ARGV[0]; +my $file2 = $ARGV[1]; + +open FILE, $file1 or die "ERROR: Can't read $file1\n"; +while () { + chomp; + my ($stack, $count) = (/^(.*)\s+?(\d+(?:\.\d*)?)$/); + $stack =~ s/0x[0-9a-fA-F]+/0x.../g if $striphex; + $Folded{$stack}{1} += $count; + $total1 += $count; +} +close FILE; + +open FILE, $file2 or die "ERROR: Can't read $file2\n"; +while () { + chomp; + my ($stack, $count) = (/^(.*)\s+?(\d+(?:\.\d*)?)$/); + $stack =~ s/0x[0-9a-fA-F]+/0x.../g if $striphex; + $Folded{$stack}{2} += $count; + $total2 += $count; +} +close FILE; + +foreach my $stack (keys %Folded) { + $Folded{$stack}{1} = 0 unless defined $Folded{$stack}{1}; + $Folded{$stack}{2} = 0 unless defined $Folded{$stack}{2}; + if ($normalize && $total1 != $total2) { + $Folded{$stack}{1} = int($Folded{$stack}{1} * $total2 / $total1); + } + print "$stack $Folded{$stack}{1} $Folded{$stack}{2}\n"; +} diff --git a/perun/view/flamegraph/flamegraph.pl b/perun/scripts/flamegraph.pl similarity index 100% rename from perun/view/flamegraph/flamegraph.pl rename to perun/scripts/flamegraph.pl diff --git a/perun/scripts/meson.build b/perun/scripts/meson.build new file mode 100755 index 000000000..22fb9289b --- /dev/null +++ b/perun/scripts/meson.build @@ -0,0 +1,12 @@ +perun_scripts_dir = perun_dir / 'scripts' + +perun_scripts_files = files( + 'difffolded.pl', + 'flamegraph.pl', + 'stackcollapse-perf.pl', +) + +py3.install_sources( + perun_scripts_files, + subdir: perun_scripts_dir +) diff --git a/perun/collect/kperf/scripts/stackcollapse-perf.pl b/perun/scripts/stackcollapse-perf.pl similarity index 100% rename from perun/collect/kperf/scripts/stackcollapse-perf.pl rename to perun/scripts/stackcollapse-perf.pl diff --git a/perun/templates/diff_view_flamegraph.html.jinja2 b/perun/templates/diff_view_flamegraph.html.jinja2 index 4974bcf62..879b38652 100755 --- a/perun/templates/diff_view_flamegraph.html.jinja2 +++ b/perun/templates/diff_view_flamegraph.html.jinja2 @@ -10,6 +10,16 @@ html { font-family: "Courier New", Courier, monospace; } + html, body { + margin: 0; + padding: 0; + } + .middle { + width: 98%; + float: left; + margin: 0 1%; + justify-content: center; + } .column { width: 48%; margin: 0 1%; @@ -23,7 +33,6 @@ } .column-head { border-bottom: 1px solid #ddd; - border-top: 1px solid #ddd; text-align: center; } .clear { @@ -34,11 +43,18 @@ display: inline-block; position: relative; width: 100%; - padding-bottom: 100%; vertical-align: top; overflow: hidden; } + div.column .svg-container { + padding-bottom: 84%; + } + + div.middle .svg-container { + padding-bottom: 42%; + } + .svg-content { display: inline-block; position: absolute; @@ -46,10 +62,12 @@ left: 0; } .help { - border-top: 1px solid #ddd; margin: 0 auto 2em auto; text-align: center; } + .help h2 { + border-bottom: 1px solid #ddd; + } .help ul { list-style-type: none; margin: 0; @@ -83,19 +101,25 @@ -
+
+

Difference of profiles

+
+ {{ diff_flamegraph }} +
-
-

Help

-
    -
  • > Click on the square to nested into selected trace.
  • -
  • > The size of the rectangle represents relative consumption with respect to parent.
  • -
  • > The color of the rectangle represents nothing.
  • -
  • > Use reset zoom (top left) to return to original view.
  • -
  • > Use search (top right) to highlight selected functions.
  • -
+
+

Help

+
    +
  • > Click on the square to nested into selected trace.
  • +
  • > The size of the rectangle represents relative consumption with respect to parent.
  • +
  • > The color of the rectangle represents nothing.
  • +
  • > Use reset zoom (top left) to return to original view.
  • +
  • > Use search (top right) to highlight selected functions.
  • +
+
+