summaryrefslogtreecommitdiff
path: root/tools/perf/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/scripts')
-rw-r--r--tools/perf/scripts/Build26
-rw-r--r--tools/perf/scripts/Makefile.syscalls61
-rwxr-xr-xtools/perf/scripts/python/bin/flamegraph-report2
-rwxr-xr-xtools/perf/scripts/python/exported-sql-viewer.py5
-rwxr-xr-xtools/perf/scripts/python/flamegraph.py82
-rwxr-xr-xtools/perf/scripts/syscalltbl.sh86
6 files changed, 84 insertions, 178 deletions
diff --git a/tools/perf/scripts/Build b/tools/perf/scripts/Build
index 46f0c6f76dbf..91229a1fe3ff 100644
--- a/tools/perf/scripts/Build
+++ b/tools/perf/scripts/Build
@@ -2,3 +2,29 @@ ifeq ($(CONFIG_LIBTRACEEVENT),y)
perf-util-$(CONFIG_LIBPERL) += perl/Perf-Trace-Util/
endif
perf-util-$(CONFIG_LIBPYTHON) += python/Perf-Trace-Util/
+
+ifdef MYPY
+ PY_TESTS := $(shell find python -type f -name '*.py')
+ MYPY_TEST_LOGS := $(PY_TESTS:python/%=python/%.mypy_log)
+else
+ MYPY_TEST_LOGS :=
+endif
+
+$(OUTPUT)%.mypy_log: %
+ $(call rule_mkdir)
+ $(Q)$(call echo-cmd,test)mypy "$<" > $@ || (cat $@ && rm $@ && false)
+
+perf-y += $(MYPY_TEST_LOGS)
+
+ifdef PYLINT
+ PY_TESTS := $(shell find python -type f -name '*.py')
+ PYLINT_TEST_LOGS := $(PY_TESTS:python/%=python/%.pylint_log)
+else
+ PYLINT_TEST_LOGS :=
+endif
+
+$(OUTPUT)%.pylint_log: %
+ $(call rule_mkdir)
+ $(Q)$(call echo-cmd,test)pylint "$<" > $@ || (cat $@ && rm $@ && false)
+
+perf-y += $(PYLINT_TEST_LOGS)
diff --git a/tools/perf/scripts/Makefile.syscalls b/tools/perf/scripts/Makefile.syscalls
deleted file mode 100644
index 8bf55333262e..000000000000
--- a/tools/perf/scripts/Makefile.syscalls
+++ /dev/null
@@ -1,61 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-# This Makefile generates headers in
-# tools/perf/arch/$(SRCARCH)/include/generated/asm from the architecture's
-# syscall table. This will either be from the generic syscall table, or from a
-# table that is specific to that architecture.
-
-PHONY := all
-all:
-
-obj := $(OUTPUT)arch/$(SRCARCH)/include/generated/asm
-
-syscall_abis_32 := common,32
-syscall_abis_64 := common,64
-syscalltbl := $(srctree)/tools/scripts/syscall.tbl
-
-# let architectures override $(syscall_abis_%) and $(syscalltbl)
--include $(srctree)/tools/perf/arch/$(SRCARCH)/entry/syscalls/Makefile.syscalls
-include $(srctree)/tools/build/Build.include
--include $(srctree)/tools/perf/arch/$(SRCARCH)/entry/syscalls/Kbuild
-
-systbl := $(srctree)/tools/perf/scripts/syscalltbl.sh
-
-syscall-y := $(addprefix $(obj)/, $(syscall-y))
-
-# Remove stale wrappers when the corresponding files are removed from generic-y
-old-headers := $(wildcard $(obj)/*.h)
-unwanted := $(filter-out $(syscall-y),$(old-headers))
-
-quiet_cmd_remove = REMOVE $(unwanted)
- cmd_remove = rm -f $(unwanted)
-
-quiet_cmd_systbl = SYSTBL $@
- cmd_systbl = $(CONFIG_SHELL) $(systbl) \
- $(if $(systbl-args-$*),$(systbl-args-$*),$(systbl-args)) \
- --abis $(subst $(space),$(comma),$(strip $(syscall_abis_$*))) \
- $< $@
-
-all: $(syscall-y)
- $(if $(unwanted),$(call cmd,remove))
- @:
-
-$(obj)/syscalls_%.h: $(syscalltbl) $(systbl) FORCE
- $(call if_changed,systbl)
-
-targets := $(syscall-y)
-
-# Create output directory. Skip it if at least one old header exists
-# since we know the output directory already exists.
-ifeq ($(old-headers),)
-$(shell mkdir -p $(obj))
-endif
-
-PHONY += FORCE
-
-FORCE:
-
-existing-targets := $(wildcard $(sort $(targets)))
-
--include $(foreach f,$(existing-targets),$(dir $(f)).$(notdir $(f)).cmd)
-
-.PHONY: $(PHONY)
diff --git a/tools/perf/scripts/python/bin/flamegraph-report b/tools/perf/scripts/python/bin/flamegraph-report
index 53c5dc90c87e..453a6918afbe 100755
--- a/tools/perf/scripts/python/bin/flamegraph-report
+++ b/tools/perf/scripts/python/bin/flamegraph-report
@@ -1,3 +1,3 @@
#!/bin/bash
# description: create flame graphs
-perf script -s "$PERF_EXEC_PATH"/scripts/python/flamegraph.py -- "$@"
+perf script -s "$PERF_EXEC_PATH"/scripts/python/flamegraph.py "$@"
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py
index 121cf61ba1b3..e0b2e7268ef6 100755
--- a/tools/perf/scripts/python/exported-sql-viewer.py
+++ b/tools/perf/scripts/python/exported-sql-viewer.py
@@ -680,7 +680,10 @@ class CallGraphModelBase(TreeModel):
s = value.replace("%", "\\%")
s = s.replace("_", "\\_")
# Translate * and ? into SQL LIKE pattern characters % and _
- trans = string.maketrans("*?", "%_")
+ if sys.version_info[0] == 3:
+ trans = str.maketrans("*?", "%_")
+ else:
+ trans = string.maketrans("*?", "%_")
match = " LIKE '" + str(s).translate(trans) + "'"
else:
match = " GLOB '" + str(value) + "'"
diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/python/flamegraph.py
index cf7ce8229a6c..ad735990c5be 100755
--- a/tools/perf/scripts/python/flamegraph.py
+++ b/tools/perf/scripts/python/flamegraph.py
@@ -18,7 +18,6 @@
# pylint: disable=missing-class-docstring
# pylint: disable=missing-function-docstring
-from __future__ import print_function
import argparse
import hashlib
import io
@@ -26,9 +25,10 @@ import json
import os
import subprocess
import sys
+from typing import Dict, Optional, Union
import urllib.request
-minimal_html = """<head>
+MINIMAL_HTML = """<head>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.css">
</head>
<body>
@@ -50,20 +50,20 @@ minimal_html = """<head>
# pylint: disable=too-few-public-methods
class Node:
- def __init__(self, name, libtype):
+ def __init__(self, name: str, libtype: str):
self.name = name
# "root" | "kernel" | ""
# "" indicates user space
self.libtype = libtype
- self.value = 0
- self.children = []
+ self.value: int = 0
+ self.children: list[Node] = []
- def to_json(self):
+ def to_json(self) -> Dict[str, Union[str, int, list[Dict]]]:
return {
"n": self.name,
"l": self.libtype,
"v": self.value,
- "c": self.children
+ "c": [x.to_json() for x in self.children]
}
@@ -73,7 +73,7 @@ class FlameGraphCLI:
self.stack = Node("all", "root")
@staticmethod
- def get_libtype_from_dso(dso):
+ def get_libtype_from_dso(dso: Optional[str]) -> str:
"""
when kernel-debuginfo is installed,
dso points to /usr/lib/debug/lib/modules/*/vmlinux
@@ -84,7 +84,7 @@ class FlameGraphCLI:
return ""
@staticmethod
- def find_or_create_node(node, name, libtype):
+ def find_or_create_node(node: Node, name: str, libtype: str) -> Node:
for child in node.children:
if child.name == name:
return child
@@ -93,7 +93,12 @@ class FlameGraphCLI:
node.children.append(child)
return child
- def process_event(self, event):
+ def process_event(self, event) -> None:
+ # ignore events where the event name does not match
+ # the one specified by the user
+ if self.args.event_name and event.get("ev_name") != self.args.event_name:
+ return
+
pid = event.get("sample", {}).get("pid", 0)
# event["dso"] sometimes contains /usr/lib/debug/lib/modules/*/vmlinux
# for user-space processes; let's use pid for kernel or user-space distinction
@@ -101,7 +106,7 @@ class FlameGraphCLI:
comm = event["comm"]
libtype = "kernel"
else:
- comm = "{} ({})".format(event["comm"], pid)
+ comm = f"{event['comm']} ({pid})"
libtype = ""
node = self.find_or_create_node(self.stack, comm, libtype)
@@ -116,20 +121,30 @@ class FlameGraphCLI:
node = self.find_or_create_node(node, name, libtype)
node.value += 1
- def get_report_header(self):
+ def get_report_header(self) -> str:
if self.args.input == "-":
# when this script is invoked with "perf script flamegraph",
# no perf.data is created and we cannot read the header of it
return ""
try:
- output = subprocess.check_output(["perf", "report", "--header-only"])
- return output.decode("utf-8")
+ # if the file name other than perf.data is given,
+ # we read the header of that file
+ if self.args.input:
+ output = subprocess.check_output(["perf", "report", "--header-only",
+ "-i", self.args.input])
+ else:
+ output = subprocess.check_output(["perf", "report", "--header-only"])
+
+ result = output.decode("utf-8")
+ if self.args.event_name:
+ result += "\nFocused event: " + self.args.event_name
+ return result
except Exception as err: # pylint: disable=broad-except
- print("Error reading report header: {}".format(err), file=sys.stderr)
+ print(f"Error reading report header: {err}", file=sys.stderr)
return ""
- def trace_end(self):
+ def trace_end(self) -> None:
stacks_json = json.dumps(self.stack, default=lambda x: x.to_json())
if self.args.format == "html":
@@ -153,7 +168,8 @@ graph template (--template PATH) or use another output format (--format
FORMAT).""",
file=sys.stderr)
if self.args.input == "-":
- print("""Not attempting to download Flame Graph template as script command line
+ print(
+"""Not attempting to download Flame Graph template as script command line
input is disabled due to using live mode. If you want to download the
template retry without live mode. For example, use 'perf record -a -g
-F 99 sleep 60' and 'perf script report flamegraph'. Alternatively,
@@ -162,37 +178,40 @@ https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-b
and place it at:
/usr/share/d3-flame-graph/d3-flamegraph-base.html""",
file=sys.stderr)
- quit()
+ sys.exit(1)
s = None
- while s != "y" and s != "n":
- s = input("Do you wish to download a template from cdn.jsdelivr.net? (this warning can be suppressed with --allow-download) [yn] ").lower()
+ while s not in ["y", "n"]:
+ s = input("Do you wish to download a template from cdn.jsdelivr.net?" +
+ "(this warning can be suppressed with --allow-download) [yn] "
+ ).lower()
if s == "n":
- quit()
- template = "https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-base.html"
+ sys.exit(1)
+ template = ("https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/"
+ "d3-flamegraph-base.html")
template_md5sum = "143e0d06ba69b8370b9848dcd6ae3f36"
try:
- with urllib.request.urlopen(template) as template:
+ with urllib.request.urlopen(template) as url_template:
output_str = "".join([
- l.decode("utf-8") for l in template.readlines()
+ l.decode("utf-8") for l in url_template.readlines()
])
except Exception as err:
print(f"Error reading template {template}: {err}\n"
"a minimal flame graph will be generated", file=sys.stderr)
- output_str = minimal_html
+ output_str = MINIMAL_HTML
template_md5sum = None
if template_md5sum:
download_md5sum = hashlib.md5(output_str.encode("utf-8")).hexdigest()
if download_md5sum != template_md5sum:
s = None
- while s != "y" and s != "n":
+ while s not in ["y", "n"]:
s = input(f"""Unexpected template md5sum.
{download_md5sum} != {template_md5sum}, for:
{output_str}
continue?[yn] """).lower()
if s == "n":
- quit()
+ sys.exit(1)
output_str = output_str.replace("/** @options_json **/", options_json)
output_str = output_str.replace("/** @flamegraph_json **/", stacks_json)
@@ -206,12 +225,12 @@ continue?[yn] """).lower()
with io.open(sys.stdout.fileno(), "w", encoding="utf-8", closefd=False) as out:
out.write(output_str)
else:
- print("dumping data to {}".format(output_fn))
+ print(f"dumping data to {output_fn}")
try:
with io.open(output_fn, "w", encoding="utf-8") as out:
out.write(output_str)
except IOError as err:
- print("Error writing output file: {}".format(err), file=sys.stderr)
+ print(f"Error writing output file: {err}", file=sys.stderr)
sys.exit(1)
@@ -235,6 +254,11 @@ if __name__ == "__main__":
default=False,
action="store_true",
help="allow unprompted downloading of HTML template")
+ parser.add_argument("-e", "--event",
+ default="",
+ dest="event_name",
+ type=str,
+ help="specify the event to generate flamegraph for")
cli_args = parser.parse_args()
cli = FlameGraphCLI(cli_args)
diff --git a/tools/perf/scripts/syscalltbl.sh b/tools/perf/scripts/syscalltbl.sh
deleted file mode 100755
index 1ce0d5aa8b50..000000000000
--- a/tools/perf/scripts/syscalltbl.sh
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0
-#
-# Generate a syscall table header.
-#
-# Each line of the syscall table should have the following format:
-#
-# NR ABI NAME [NATIVE] [COMPAT]
-#
-# NR syscall number
-# ABI ABI name
-# NAME syscall name
-# NATIVE native entry point (optional)
-# COMPAT compat entry point (optional)
-
-set -e
-
-usage() {
- echo >&2 "usage: $0 [--abis ABIS] INFILE OUTFILE" >&2
- echo >&2
- echo >&2 " INFILE input syscall table"
- echo >&2 " OUTFILE output header file"
- echo >&2
- echo >&2 "options:"
- echo >&2 " --abis ABIS ABI(s) to handle (By default, all lines are handled)"
- exit 1
-}
-
-# default unless specified by options
-abis=
-
-while [ $# -gt 0 ]
-do
- case $1 in
- --abis)
- abis=$(echo "($2)" | tr ',' '|')
- shift 2;;
- -*)
- echo "$1: unknown option" >&2
- usage;;
- *)
- break;;
- esac
-done
-
-if [ $# -ne 2 ]; then
- usage
-fi
-
-infile="$1"
-outfile="$2"
-
-nxt=0
-
-syscall_macro() {
- nr="$1"
- name="$2"
-
- echo " [$nr] = \"$name\","
-}
-
-emit() {
- nr="$1"
- entry="$2"
-
- syscall_macro "$nr" "$entry"
-}
-
-echo "static const char *const syscalltbl[] = {" > $outfile
-
-sorted_table=$(mktemp /tmp/syscalltbl.XXXXXX)
-grep -E "^[0-9]+[[:space:]]+$abis" "$infile" | sort -n > $sorted_table
-
-max_nr=0
-# the params are: nr abi name entry compat
-# use _ for intentionally unused variables according to SC2034
-while read nr _ name _ _; do
- emit "$nr" "$name" >> $outfile
- max_nr=$nr
-done < $sorted_table
-
-rm -f $sorted_table
-
-echo "};" >> $outfile
-
-echo "#define SYSCALLTBL_MAX_ID ${max_nr}" >> $outfile