From: Arnaldo Carvalho de Melo <acme@kernel.org>
To: Namhyung Kim <namhyung@kernel.org>
Cc: Ingo Molnar <mingo@kernel.org>,
Thomas Gleixner <tglx@linutronix.de>,
James Clark <james.clark@linaro.org>,
Jiri Olsa <jolsa@kernel.org>, Ian Rogers <irogers@google.com>,
Adrian Hunter <adrian.hunter@intel.com>,
Clark Williams <williams@redhat.com>,
linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org,
Arnaldo Carvalho de Melo <acme@redhat.com>
Subject: [PATCH v1 01/12] perf tools: Add script and make target to sync the header copies with the kernel
Date: Wed, 21 Oct 2026 18:29:55 -0300 [thread overview]
Message-ID: <20261021213007.21046-2-acme@kernel.org> (raw)
In-Reply-To: <20261021213007.21046-1-acme@kernel.org>
From: Arnaldo Carvalho de Melo <acme@redhat.com>
This is to be used by the perf maintainers to sync the header copies
with the kernel originals, see the description below for how it works.
$ tools/perf/scripts/sync-headers.sh --help
Usage: sync-headers.sh [OPTIONS]
Sync tools/ header copies with the kernel sources and create commits.
Options:
-n, --dry-run Show what would be done without making changes
-N, --no-build Skip the perf build verification step
-C, --no-commit Copy files but do not create git commits
-f, --filter PAT Only process headers matching PAT (substring match
against the kernel-side path)
-o, --output DIR Build output directory (default: /tmp/build/perf-tools-1000)
-v, --verbose Show more details
-h, --help Show this help
Environment:
SIGN_OFF_NAME Override git user.name for Signed-off-by
SIGN_OFF_EMAIL Override git user.email for Signed-off-by
OUTPUT_DIR Build output directory
Through make:
SYNC_HEADERS_ARGS Options to pass to this script, e.g.:
make -C tools/perf sync-headers SYNC_HEADERS_ARGS='-n'
The script:
1. Runs 'make -C tools/perf check-headers' to find stale copies.
2. For each stale header, identifies the kernel commits that changed it.
3. Copies the kernel header into the tools/ tree.
4. For beauty headers, runs the relevant beauty script(s) before and
after, showing new/changed string table entries.
5. Builds perf and the other consumers of the header to verify nothing
breaks.
6. Creates a git commit per header (or group) with the standard commit
message format.
$
It is also available as:
$ make -C tools/perf sync-headers
==> Running check-headers to detect stale copies...
Stale headers found:
tools/include/uapi/linux/if_addr.h include/uapi/linux/if_addr.h
tools/include/uapi/linux/kvm.h include/uapi/linux/kvm.h
tools/include/uapi/linux/perf_event.h include/uapi/linux/perf_event.h
...
tools/perf/trace/beauty/include/uapi/sound/asound.h include/uapi/sound/asound.h
==> Processing: include/uapi/linux/if_addr.h
Kernel commits since last sync:
7cb8198761e6 ("net: ipv4: report multicast group user count")
==> Copied include/uapi/linux/if_addr.h -> tools/include/uapi/linux/if_addr.h
==> Build-testing consumers of if_addr.h:
perf (tools/perf)
==> Build OK.
==> Committed: tools headers UAPI: Sync linux/if_addr.h with the kernel sources
==> Processing: include/uapi/linux/perf_event.h
Warning: tools/include/uapi/linux/perf_event.h matches a previous tools/ state, leaving it alone
==> Processing: include/uapi/linux/rtnetlink.h
Kernel commits since last sync:
cc17e386f8a5 ("ipv6: add ip6_del_rt_reason()")
==> Copied include/uapi/linux/rtnetlink.h -> tools/include/uapi/linux/rtnetlink.h
==> Build-testing consumers of rtnetlink.h:
libbpf (tools/lib/bpf)
bpftool (tools/bpf/bpftool)
perf (tools/perf)
==> Build OK.
==> Committed: tools headers UAPI: Sync linux/rtnetlink.h with the kernel sources
...
==> Processing: include/linux/socket.h
Kernel commits since last sync:
be3a5e06b962 ("net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD")
5bd369c05576 ("net: Add connect_socket() helper")
==> Copied include/linux/socket.h -> tools/perf/trace/beauty/include/linux/socket.h
==> Build-testing consumers of socket.h:
perf (tools/perf)
==> Build OK.
==> Committed: perf beauty: Update copy of linux/socket.h with the kernel sources
...
==> Processing group: KVM headers (3 files)
...
==> Build-testing consumers of group KVM headers:
perf (tools/perf)
==> Build OK.
...
=========================================
Summary
=========================================
==> Synced successfully (13):
tools/include/uapi/linux/if_addr.h
tools/include/uapi/linux/rtnetlink.h
tools/include/linux/gfp_types.h
tools/arch/x86/include/asm/cpufeatures.h
tools/arch/x86/include/asm/msr-index.h
tools/perf/trace/beauty/include/linux/socket.h
tools/perf/trace/beauty/include/uapi/linux/fcntl.h
tools/perf/trace/beauty/include/uapi/linux/fs.h
tools/perf/trace/beauty/include/uapi/linux/mount.h
tools/perf/trace/beauty/include/uapi/sound/asound.h
tools/include/uapi/linux/kvm.h
tools/arch/powerpc/include/uapi/asm/kvm.h
tools/arch/arm64/include/uapi/asm/kvm.h
Warning: Headers still out of sync:
tools/include/uapi/linux/perf_event.h include/uapi/linux/perf_event.h
tools/arch/x86/include/uapi/asm/perf_regs.h arch/x86/include/uapi/asm/perf_regs.h
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/Makefile | 5 +-
tools/perf/Makefile.perf | 3 +
| 1256 ++++++++++++++++++++++++++++
3 files changed, 1263 insertions(+), 1 deletion(-)
create mode 100755 tools/perf/scripts/sync-headers.sh
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index c1818bd5f2879ac9..00baf90e5bda7123 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -137,6 +137,9 @@ build-test-tarball:
check-headers:
@./check-headers.sh
+sync-headers:
+ @./scripts/sync-headers.sh $(SYNC_HEADERS_ARGS)
+
#
# All other targets get passed through:
#
@@ -144,4 +147,4 @@ check-headers:
$(print_msg)
$(make)
-.PHONY: all clean tags TAGS FORCE Makefile build-test build-test-tarball check-headers
+.PHONY: all clean tags TAGS FORCE Makefile build-test build-test-tarball check-headers sync-headers
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 2d9379fea2672755..b74b534e1153ffcb 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -870,6 +870,9 @@ help:
@echo ''
@echo 'Perf maintainer targets:'
@echo ' clean - clean all binary objects and build output'
+ @echo ' check-headers - check which header copies are out of sync with the kernel'
+ @echo ' sync-headers - sync those header copies with the kernel, creating commits'
+ @echo ' (SYNC_HEADERS_ARGS="..." passes options to the script)'
DOC_TARGETS := doc man html info pdf
--git a/tools/perf/scripts/sync-headers.sh b/tools/perf/scripts/sync-headers.sh
new file mode 100755
index 0000000000000000..9a35a3d2095a7342
--- /dev/null
+++ b/tools/perf/scripts/sync-headers.sh
@@ -0,0 +1,1256 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2026, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+#
+# Sync tools/ header copies with the kernel sources, generate beauty
+# string table diffs, verify perf still builds, and create git commits.
+
+set -euo pipefail
+
+YELLOW='\033[0;33m'
+GREEN='\033[0;32m'
+RED='\033[0;31m'
+NC='\033[0m'
+
+PERF_DIR=$(cd "$(dirname "$0")/.." && pwd)
+KERNEL_DIR=$(cd "$PERF_DIR/../.." && pwd)
+DRY_RUN=0
+BUILD=1
+COMMIT=1
+OUTPUT_DIR="${OUTPUT_DIR:-${TMPDIR:-/tmp}/build/perf-tools-$(id -u)}"
+SIGN_OFF_NAME="${SIGN_OFF_NAME:-}"
+SIGN_OFF_EMAIL="${SIGN_OFF_EMAIL:-}"
+HEADER_FILTER=""
+VERBOSE=0
+
+# Track build successes and failures for end-of-run summary
+declare -a SYNCED_OK=()
+declare -a BUILD_FAILED=()
+declare -a COPY_FAILED=()
+declare -a COMMIT_FAILED=()
+
+info() { echo -e "${GREEN}==>${NC} $*"; }
+warn() { echo -e "${YELLOW}Warning:${NC} $*"; }
+err() { echo -e "${RED}Error:${NC} $*" >&2; }
+detail(){ [[ $VERBOSE -eq 1 ]] && echo -e " $*"; return 0; }
+
+usage() {
+ local status="${1:-0}"
+
+ cat <<EOF
+Usage: $(basename "$0") [OPTIONS]
+
+Sync tools/ header copies with the kernel sources and create commits.
+
+Options:
+ -n, --dry-run Show what would be done without making changes
+ -N, --no-build Skip the perf build verification step
+ -C, --no-commit Copy files but do not create git commits
+ -f, --filter PAT Only process headers matching PAT (substring match
+ against the kernel-side path)
+ -o, --output DIR Build output directory (default: $OUTPUT_DIR)
+ -v, --verbose Show more details
+ -h, --help Show this help
+
+Environment:
+ SIGN_OFF_NAME Override git user.name for Signed-off-by
+ SIGN_OFF_EMAIL Override git user.email for Signed-off-by
+ OUTPUT_DIR Build output directory
+
+Through make:
+ SYNC_HEADERS_ARGS Options to pass to this script, e.g.:
+ make -C tools/perf sync-headers SYNC_HEADERS_ARGS='-n'
+
+The script:
+ 1. Runs 'make -C tools/perf check-headers' to find stale copies.
+ 2. For each stale header, identifies the kernel commits that changed it.
+ 3. Copies the kernel header into the tools/ tree.
+ 4. For beauty headers, runs the relevant beauty script(s) before and
+ after, showing new/changed string table entries.
+ 5. Builds perf and the other consumers of the header to verify nothing
+ breaks.
+ 6. Creates a git commit per header (or group) with the standard commit
+ message format.
+
+EOF
+ exit "$status"
+}
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -n|--dry-run) DRY_RUN=1; shift ;;
+ -N|--no-build) BUILD=0; shift ;;
+ -C|--no-commit) COMMIT=0; shift ;;
+ -f|--filter)
+ [[ $# -ge 2 ]] || { err "$1 needs an argument"; exit 1; }
+ HEADER_FILTER="$2"; shift 2 ;;
+ -o|--output)
+ [[ $# -ge 2 ]] || { err "$1 needs an argument"; exit 1; }
+ OUTPUT_DIR="$2"; shift 2 ;;
+ -v|--verbose) VERBOSE=1; shift ;;
+ -h|--help) usage ;;
+ *) err "Unknown option: $1"; usage 1 ;;
+ esac
+done
+
+sign_off() {
+ local name="${SIGN_OFF_NAME:-$(git config user.name)}"
+ local email="${SIGN_OFF_EMAIL:-$(git config user.email)}"
+ echo "Signed-off-by: $name <$email>"
+}
+
+# --- Detect stale headers ------------------------------------------------
+
+detect_stale_headers() {
+ local tmpfile
+ tmpfile=$(mktemp)
+
+ # check-headers.sh must be run from tools/perf/ (it does cd ../.. internally)
+ (cd "$PERF_DIR" && bash check-headers.sh 2>/dev/null) | \
+ grep '^ *diff -u ' | \
+ sed 's/^ *diff -u //' > "$tmpfile"
+
+ if [[ ! -s "$tmpfile" ]]; then
+ rm -f "$tmpfile"
+ return 1
+ fi
+
+ cat "$tmpfile"
+ rm -f "$tmpfile"
+ return 0
+}
+
+# --- Classify a header ---------------------------------------------------
+#
+# Returns via globals:
+# HDR_CLASS: "files" | "beauty"
+classify_header() {
+ local tools_path="$1"
+
+ if [[ "$tools_path" == tools/perf/trace/beauty/* ]]; then
+ HDR_CLASS="beauty"
+ else
+ HDR_CLASS="files"
+ fi
+}
+
+# --- Find kernel commits that changed a header since our copy ------------
+
+# The last commit that left the two copies equal, found by content: an
+# in-tree kernel commit can update the copy from a tree state that
+# already misses an earlier kernel change to the header (the
+# linux/gfp_types.h doc fix), so the last commit touching the copy can be
+# newer than the first commit still missing from it. Falls back to the
+# last commit that touched the tools copy.
+#
+# The walk can run to the end of the history when no commit ever left the
+# two byte-identical (copies compared with -I patterns are rewritten on
+# purpose), so all the blob comparisons go through one
+# git cat-file --batch-check, no forks per commit.
+find_sync_point() {
+ local kernel_path="$1"
+ local tools_path="$2"
+ local -a candidates blobs
+ local i
+
+ mapfile -t candidates < <(git log --no-merges --format='%H' -- "$kernel_path" "$tools_path" 2>/dev/null)
+ mapfile -t blobs < <(
+ printf '%s\n' "${candidates[@]}" | \
+ awk -v k="$kernel_path" -v t="$tools_path" '{ print $1 ":" k ORS $1 ":" t }' | \
+ git cat-file --batch-check)
+
+ for ((i = 0; i < ${#candidates[@]}; i++)); do
+ # "<spec> missing" keeps the ':' of the spec as its first field,
+ # which no object id has.
+ if [[ "${blobs[2*i]%% *}" == "${blobs[2*i+1]%% *}" && \
+ "${blobs[2*i]%% *}" != *:* ]]; then
+ echo "${candidates[i]}"
+ return
+ fi
+ done
+
+ git log --no-merges --format='%H' -1 -- "$tools_path" 2>/dev/null
+}
+
+# A kernel commit that changed both the header and the tools copy already
+# carried its change into tools/, so it has nothing left to pick up here.
+find_kernel_commits() {
+ local kernel_path="$1"
+ local tools_path="$2"
+ local sync_point="$3"
+ local copied sha
+
+ copied=$(git log --no-merges --format='%H' \
+ "${sync_point}..HEAD" -- "$tools_path" 2>/dev/null)
+ [[ -n "$copied" ]] && copied+=$'\n'
+
+ while IFS= read -r sha; do
+ [[ "$copied" == *"$sha"$'\n'* ]] && continue
+ echo "$sha"
+ done < <(git log --no-merges --format='%H' \
+ "${sync_point}..HEAD" -- "$kernel_path" 2>/dev/null)
+}
+
+# Render commit ids with FORMAT at the usual 12 chars, keeping their order.
+format_commits() {
+ local format="$1"
+ local ids="$2"
+
+ [[ -n "$ids" ]] || return 0
+ printf '%s\n' "$ids" | git log --no-walk=unsorted --abbrev=12 \
+ --format="$format" --stdin 2>/dev/null
+}
+
+# The kernel's current content can match a previous tools/ copy state when
+# the kernel reverted the header; there is nothing to pick up then.
+kernel_matches_previous_tools_state() {
+ local kernel_path="$1"
+ local tools_path="$2"
+ local kernel_blob
+
+ kernel_blob=$(git hash-object "$kernel_path" 2>/dev/null) || return 1
+ [[ -n "$(git log --format=%h -1 --find-object="$kernel_blob" -- "$tools_path" 2>/dev/null)" ]]
+}
+
+# --- Map a kernel header path to its beauty script(s) --------------------
+#
+# A beauty script is relevant if it reads from the directory containing
+# the header being updated.
+
+find_beauty_scripts() {
+ local kernel_path="$1"
+ local header_basename
+ header_basename=$(basename "$kernel_path")
+
+ local scripts=()
+
+ # Search all beauty .sh scripts (including subdirs) for references to this header
+ while IFS= read -r script; do
+ if grep -F -q -e "/$header_basename" -e "\"$header_basename\"" \
+ "$script" 2>/dev/null; then
+ scripts+=("$script")
+ fi
+ done < <(find "$PERF_DIR"/trace/beauty -name '*.sh')
+
+ printf '%s\n' "${scripts[@]}"
+}
+
+# --- Find buildable targets that consume a header -------------------------
+#
+# Given a tools/ header path, grep tools/ source files to find which
+# buildable directories include it, then return them as build commands.
+# This is discovered dynamically so new consumers are picked up automatically.
+
+# C sources include tools/ headers by their include-relative path, e.g.
+# <linux/socket.h> for tools/include/uapi/linux/socket.h.
+include_relative_path() {
+ local tools_path="$1"
+
+ case "$tools_path" in
+ tools/include/uapi/*)
+ echo "${tools_path#tools/include/uapi/}"
+ ;;
+ tools/include/*)
+ echo "${tools_path#tools/include/}"
+ ;;
+ tools/arch/*/include/uapi/*)
+ echo "${tools_path#tools/arch/*/include/uapi/}"
+ ;;
+ tools/arch/*/include/*)
+ echo "${tools_path#tools/arch/*/include/}"
+ ;;
+ *)
+ basename "$tools_path"
+ ;;
+ esac
+}
+
+# Map from a source file path to its buildable target directory and make command.
+# Returns empty if the file doesn't belong to a known buildable target.
+buildable_target() {
+ local src="$1"
+ case "$src" in
+ tools/perf/*)
+ echo "perf:$PERF_DIR"
+ ;;
+ tools/lib/bpf/*)
+ echo "libbpf:$KERNEL_DIR/tools/lib/bpf"
+ ;;
+ tools/bpf/bpftool/*)
+ echo "bpftool:$KERNEL_DIR/tools/bpf/bpftool"
+ ;;
+ tools/objtool/*)
+ echo "objtool:$KERNEL_DIR/tools/objtool"
+ ;;
+ tools/lib/subcmd/*)
+ echo "libsubcmd:$KERNEL_DIR/tools/lib/subcmd"
+ ;;
+ tools/lib/api/*)
+ echo "libapi:$KERNEL_DIR/tools/lib/api"
+ ;;
+ tools/lib/perf/*)
+ echo "libperf:$KERNEL_DIR/tools/lib/perf"
+ ;;
+ tools/lib/symbol/*)
+ echo "libsymbol:$KERNEL_DIR/tools/lib/symbol"
+ ;;
+ tools/tracing/*)
+ echo "tracing:$KERNEL_DIR/tools/tracing"
+ ;;
+ esac
+}
+
+find_build_targets() {
+ local tools_path="$1"
+ local header_class="$2"
+ local include_path
+ local targets=()
+ local seen=()
+
+ # Beauty copies are only on the include path of the beauty scripts.
+ if [[ "$header_class" == "beauty" ]]; then
+ echo "perf:$PERF_DIR"
+ return
+ fi
+
+ include_path=$(include_relative_path "$tools_path")
+
+ while IFS= read -r src; do
+ [[ -z "$src" ]] && continue
+ local target
+ target=$(buildable_target "$src")
+ [[ -z "$target" ]] && continue
+
+ # Deduplicate
+ local already=0
+ for s in "${seen[@]+"${seen[@]}"}"; do
+ if [[ "$s" == "$target" ]]; then
+ already=1
+ break
+ fi
+ done
+ if [[ $already -eq 0 ]]; then
+ seen+=("$target")
+ targets+=("$target")
+ fi
+ done < <(grep -rlF -e "<$include_path>" -e "\"$include_path\"" -e "/$include_path" \
+ --include='*.c' --include='*.h' --include='*.S' \
+ "$KERNEL_DIR/tools/" 2>/dev/null | \
+ sed "s|^$KERNEL_DIR/||" | grep -v '^tools/include/')
+
+ # Always include perf
+ local has_perf=0
+ for t in "${targets[@]+"${targets[@]}"}"; do
+ [[ "$t" == perf:* ]] && has_perf=1
+ done
+ if [[ $has_perf -eq 0 ]]; then
+ targets+=("perf:$PERF_DIR")
+ fi
+
+ printf '%s\n' "${targets[@]}"
+}
+
+# Print the consumers that will be built for a header or group.
+show_build_targets() {
+ local what="$1"
+ local target_list="$2"
+
+ info "Build-testing consumers of $what:"
+ while IFS=: read -r name dir; do
+ [[ -z "$name" ]] && continue
+ echo " $name ($dir)"
+ done <<< "$target_list"
+}
+
+# Keep build output out of the source tree; perf, tracing and objtool take
+# O=, the libraries and bpftool take OUTPUT=.
+build_target() {
+ local name="$1"
+ local dir="$2"
+
+ case "$name" in
+ perf)
+ make -C "$dir" O="$OUTPUT_DIR" -j"$(nproc)"
+ ;;
+ tracing|objtool)
+ (umask 077 && mkdir -p "$OUTPUT_DIR/$name")
+ make -C "$dir" O="$OUTPUT_DIR/$name" -j"$(nproc)"
+ ;;
+ *)
+ (umask 077 && mkdir -p "$OUTPUT_DIR/$name")
+ make -C "$dir" OUTPUT="$OUTPUT_DIR/$name/" -j"$(nproc)"
+ ;;
+ esac
+}
+
+# Run build tests for all targets that consume a header.
+# Returns 0 if all builds pass, 1 if any fail.
+build_test_targets() {
+ local tools_path="$1"
+ local header_class="$2"
+ local header_basename log_name
+ local target_list failed=0
+
+ header_basename=$(basename "$tools_path")
+ # Key the log by tools path, same-basename headers in different
+ # directories would otherwise share a log.
+ log_name="${tools_path//\//-}"
+ target_list=$(find_build_targets "$tools_path" "$header_class")
+ show_build_targets "$header_basename" "$target_list"
+
+ while IFS=: read -r name dir; do
+ [[ -z "$name" ]] && continue
+ local log="$OUTPUT_DIR/$name.$log_name.log"
+ if ! build_target "$name" "$dir" > "$log" 2>&1; then
+ err " $name build failed, see $log"
+ failed=1
+ fi
+ done <<< "$target_list"
+
+ return $failed
+}
+
+# --- Run a beauty script capturing output --------------------------------
+#
+# The build invokes these scripts with the directories they read from
+# (tools/perf/trace/beauty/Build); run bare, arch_errno_names.sh misses
+# the header copies and the tables miss the changes.
+
+declare -A BEAUTY_SCRIPT_ARGS=()
+declare -A BEAUTY_MAKE_DIRS=()
+declare -A BEAUTY_MAKE_SCRIPTS=()
+
+# Read the beauty script arguments out of the same Build file the build
+# uses, so a new script or a changed directory needs no edit here.
+# The single quotes below hold literal make syntax from the Build
+# file, not shell expressions.
+# shellcheck disable=SC2016
+setup_beauty_args() {
+ local build="$PERF_DIR/trace/beauty/Build"
+ local -a rule_lines=()
+ local line var script args token value
+ local re_dir re_tbl srcarch
+
+ # _dir :=, _tbl := assignments, and the $(SHELL) rules using them.
+ re_dir='^([a-z0-9_]+_(dir|dirs))[[:space:]]*(:=|\?=)[[:space:]]*'
+ re_dir+='\$\(srctree\)/(.*)$'
+ re_tbl='^([a-z0-9_]+_tbl[s]?)[[:space:]]*(:=|\?=)[[:space:]]*'
+ re_tbl+='\$\(srctree\)/((.*\.sh))[[:space:]]*$'
+
+ srcarch=$(uname -m)
+ case "$srcarch" in
+ x86_64|i[3-6]86) srcarch=x86 ;;
+ aarch64|arm64) srcarch=arm64 ;;
+ s390*) srcarch=s390 ;;
+ riscv*) srcarch=riscv ;;
+ loongarch*) srcarch=loongarch ;;
+ ppc*) srcarch=powerpc ;;
+ sparc*) srcarch=sparc ;;
+ mips*) srcarch=mips ;;
+ esac
+
+ [[ -r "$build" ]] || {
+ detail "No beauty Build file, running the scripts without arguments"
+ return 1
+ }
+
+ while IFS= read -r line; do
+ if [[ "$line" =~ $re_dir ]]; then
+ BEAUTY_MAKE_DIRS["${BASH_REMATCH[1]}"]="${BASH_REMATCH[4]}"
+ continue
+ fi
+ if [[ "$line" =~ $re_tbl ]]; then
+ BEAUTY_MAKE_SCRIPTS["${BASH_REMATCH[1]}"]="${BASH_REMATCH[4]}"
+ continue
+ fi
+ if [[ "$line" == *'$(SHELL)'* ]]; then
+ rule_lines+=("$line")
+ fi
+ done < "$build"
+
+ local rule
+ for rule in "${rule_lines[@]+"${rule_lines[@]}"}"; do
+ IFS="'" read -r _ var args <<< "$rule" || continue
+ [[ "$var" == '$('* ]] || continue
+ var="${var:2:${#var}-3}"
+ script="${BEAUTY_MAKE_SCRIPTS[$var]:-}"
+ [[ -n "$script" ]] || continue
+ script="${script##*/}"
+
+ # $(patsubst -%,,$(CC)) yields the compiler name
+ args=$(printf '%s\n' "$args" | sed 's/\$(patsubst -%,,\$(CC))/gcc/g')
+ args="${args//\'}"
+ args="${args%' > $@'}"
+ value=""
+ for token in $args; do
+ case "$token" in
+ '$@')
+ continue
+ ;;
+ '$(srctree)'*)
+ token="${token//\$(srctree)/$KERNEL_DIR}"
+ ;;
+ '$('*)
+ token="$KERNEL_DIR/${BEAUTY_MAKE_DIRS["${token:2:${#token}-3}"]:-}"
+ [[ "$token" == "$KERNEL_DIR/" ]] && { value=""; break; }
+ ;;
+ *) ;;
+ esac
+ token=$(printf '%s' "$token" | sed "s/\$(SRCARCH)/$srcarch/g")
+ if [[ "$token" == *'$('* ]]; then
+ value=""
+ break
+ fi
+ value+="${value:+ }$token"
+ done
+ [[ -n "$value" ]] || continue
+ BEAUTY_SCRIPT_ARGS["$script"]="$value"
+ done
+}
+
+run_beauty_script() {
+ local script="$1"
+ local -a cmd=()
+
+ read -ra cmd <<< "${BEAUTY_SCRIPT_ARGS["${script##*/}"]:-}"
+ bash "$script" "${cmd[@]+"${cmd[@]}"}" 2>/dev/null || true
+}
+
+# Run one beauty script against the kernel content swapped into the tools
+# copy, capturing its output before and after; the saved copy restores the
+# tools copy, also when the script is interrupted.
+run_beauty_pass() {
+ local script="$1"
+ local kernel_path="$2"
+ local tools_path="$3"
+ local tmpdir="$4"
+ local idx="$5"
+ local before="$tmpdir/beauty.$idx.before"
+ local after="$tmpdir/beauty.$idx.after"
+ local saved="$tmpdir/beauty.$idx.saved"
+ local restore
+
+ if ! cp -p "$KERNEL_DIR/$tools_path" "$saved"; then
+ err "Failed to save $tools_path"
+ return 1
+ fi
+ restore=$(printf 'cp -p %q %q' "$saved" "$KERNEL_DIR/$tools_path")
+ # shellcheck disable=SC2064 # expand now, locals may be gone
+ trap "$restore" EXIT
+ # shellcheck disable=SC2064 # expand now, locals may be gone
+ trap "$restore; trap - EXIT; exit 130" INT
+ # shellcheck disable=SC2064 # expand now, locals may be gone
+ trap "$restore; trap - EXIT; exit 143" TERM
+ (cd "$KERNEL_DIR" && run_beauty_script "$script") > "$before"
+ if ! cp "$KERNEL_DIR/$kernel_path" "$KERNEL_DIR/$tools_path"; then
+ err "Failed to copy $kernel_path -> $tools_path"
+ cp -p "$saved" "$KERNEL_DIR/$tools_path" || \
+ err "Failed to restore $tools_path"
+ trap - INT TERM EXIT
+ return 1
+ fi
+ (cd "$KERNEL_DIR" && run_beauty_script "$script") > "$after"
+ if ! cp -p "$saved" "$KERNEL_DIR/$tools_path"; then
+ err "Failed to restore $tools_path"
+ trap - INT TERM EXIT
+ return 1
+ fi
+ trap - INT TERM EXIT
+}
+
+# The tools/perf/trace/beauty/ relative path of a beauty script, as shown
+# in the commit messages.
+beauty_script_relpath() {
+ echo "tools/perf/trace/beauty/${1##*/trace/beauty/}"
+}
+
+# The commit message fragment for a beauty script whose table changed:
+# the recipe the pre-pass ran and the difference between its two runs.
+beauty_body_fragment() {
+ local script="$1"
+ local kernel_path="$2"
+ local tools_path="$3"
+ local before="$4"
+ local after="$5"
+ local rel_script args
+
+ rel_script=$(beauty_script_relpath "$script")
+ # The runs are from the tree root, show the dirs relative to it.
+ args="${BEAUTY_SCRIPT_ARGS["${script##*/}"]:-}"
+ args="${args//"$KERNEL_DIR/"/}"
+
+ printf '\n'
+ printf ' $ %s%s > before.txt\n' "$rel_script" "${args:+ $args}"
+ printf ' $ cp %s %s\n' "$kernel_path" "$tools_path"
+ printf ' $ %s%s > after.txt\n' "$rel_script" "${args:+ $args}"
+ printf ' $ diff -u before.txt after.txt\n'
+ printf '\n'
+ diff -u "$before" "$after" | tail -n +3 | sed 's/^/ /'
+ printf '\n'
+}
+
+cleanup_tmpdir() {
+ [[ -n "$1" ]] || return 0
+ rm -rf "$1"
+}
+
+# --- Copy header and create commit ---------------------------------------
+
+process_header() {
+ local tools_path="$1"
+ local kernel_path="$2"
+
+ classify_header "$tools_path"
+
+ if [[ -n "$HEADER_FILTER" ]] && [[ "$kernel_path" != *"$HEADER_FILTER"* ]]; then
+ detail "Skipping $kernel_path (does not match filter)"
+ return
+ fi
+
+ info "Processing: $kernel_path"
+ detail " Class: $HDR_CLASS"
+ detail " Tools: $tools_path"
+ detail " Kernel: $kernel_path"
+
+ if kernel_matches_previous_tools_state "$kernel_path" "$tools_path"; then
+ warn "$tools_path matches a previous tools/ state, leaving it alone"
+ return
+ fi
+
+ # Find kernel commits that changed this file
+ local sync_point pickup_commits=""
+ sync_point=$(find_sync_point "$kernel_path" "$tools_path")
+ if [[ -n "$sync_point" ]]; then
+ pickup_commits=$(find_kernel_commits "$kernel_path" \
+ "$tools_path" "$sync_point")
+ else
+ warn "No previous sync commit found for $tools_path"
+ fi
+
+ local kernel_commits=""
+ if [[ -n "$pickup_commits" ]]; then
+ kernel_commits=$(format_commits ' %h ("%s")' "$pickup_commits")
+ echo ""
+ echo " Kernel commits since last sync:"
+ echo "$kernel_commits"
+ fi
+
+ # For beauty files, capture the string table changes and keep the
+ # before/after files for the commit message.
+ local beauty_changed=0
+ local beauty_tmpdir=""
+ local beauty_scripts
+ local -a beauty_script_list=()
+ beauty_scripts=$(find_beauty_scripts "$kernel_path")
+ while IFS= read -r script; do
+ [[ -z "$script" ]] && continue
+ beauty_script_list+=("$script")
+ done <<< "$beauty_scripts"
+ if [[ ${#beauty_script_list[@]} -gt 0 ]]; then
+ beauty_tmpdir=$(mktemp -d)
+
+ local idx=0
+ for script in "${beauty_script_list[@]}"; do
+ local script_name
+ script_name=$(basename "$script")
+ local before="$beauty_tmpdir/beauty.$idx.before"
+ local after="$beauty_tmpdir/beauty.$idx.after"
+
+ # The script reads the tools/ copy, so swap in the kernel one
+ # and restore the saved copy, also in a dry run.
+ if ! run_beauty_pass "$script" "$kernel_path" "$tools_path" \
+ "$beauty_tmpdir" "$idx"; then
+ COPY_FAILED+=("$tools_path")
+ cleanup_tmpdir "$beauty_tmpdir"
+ return 1
+ fi
+
+ if ! diff -q "$before" "$after" > /dev/null 2>&1; then
+ echo ""
+ info "Beauty string changes from $script_name:"
+ diff -u "$before" "$after" | head -60 || true
+ beauty_changed=1
+ fi
+ idx=$((idx + 1))
+ done
+ fi
+
+ if [[ $DRY_RUN -eq 1 ]]; then
+ cleanup_tmpdir "$beauty_tmpdir"
+ info "(dry run) Would copy $kernel_path -> $tools_path"
+ return
+ fi
+
+ # Do the actual copy
+ if ! cp "$KERNEL_DIR/$kernel_path" "$KERNEL_DIR/$tools_path"; then
+ err "Failed to copy $kernel_path -> $tools_path"
+ COPY_FAILED+=("$tools_path")
+ cleanup_tmpdir "$beauty_tmpdir"
+ return 1
+ fi
+ info "Copied $kernel_path -> $tools_path"
+
+ # Verify the consumers still build
+ if [[ $BUILD -eq 1 ]]; then
+ if ! build_test_targets "$tools_path" "$HDR_CLASS"; then
+ err "Build failed after copying $kernel_path — reverting."
+ git -C "$KERNEL_DIR" checkout -- "$tools_path" || \
+ err "Failed to revert $tools_path"
+ BUILD_FAILED+=("$tools_path")
+ cleanup_tmpdir "$beauty_tmpdir"
+ return 1
+ fi
+ info "Build OK."
+ fi
+
+ if [[ $COMMIT -eq 0 ]]; then
+ SYNCED_OK+=("$tools_path")
+ cleanup_tmpdir "$beauty_tmpdir"
+ return
+ fi
+
+ # Compose commit message
+ local header_short
+ header_short=$(basename "$kernel_path")
+ local header_dir
+ header_dir=$(dirname "$kernel_path" | sed 's|^include/uapi/||;s|^include/||;s|^arch/||')
+
+ # Determine the commit subject per header type
+ local subject
+ case "$kernel_path" in
+ arch/x86/include/asm/cpufeatures.h)
+ subject="tools headers x86 cpufeatures: Sync with the kernel sources"
+ ;;
+ arch/x86/include/asm/msr-index.h)
+ subject="tools arch x86: Sync the msr-index.h copy with the kernel sources"
+ ;;
+ arch/x86/include/asm/irq_vectors.h)
+ subject="perf beauty: Update the arch/x86/include/asm/irq_vectors.h copy with the kernel sources"
+ ;;
+ arch/x86/include/uapi/asm/prctl.h)
+ subject="perf beauty: Sync x86 uapi/asm/prctl.h with the kernel sources"
+ ;;
+ include/uapi/drm/*|include/uapi/sound/*)
+ subject="tools headers: Sync UAPI ${header_dir}/${header_short} with kernel sources"
+ ;;
+ include/uapi/linux/perf_event.h)
+ subject="tools headers UAPI: Sync linux/perf_event.h with the kernel sources"
+ ;;
+ include/uapi/linux/*)
+ if [[ "$HDR_CLASS" == "beauty" ]]; then
+ subject="tools headers: Sync UAPI linux/${header_short} with the kernel sources"
+ else
+ subject="tools headers UAPI: Sync linux/${header_short} with the kernel sources"
+ fi
+ ;;
+ include/uapi/asm-generic/*)
+ subject="tools headers UAPI: Sync asm-generic/${header_short} with the kernel sources"
+ ;;
+ include/uapi/*)
+ local uapi_subdir
+ uapi_subdir=$(echo "$kernel_path" | sed 's|^include/uapi/||;s|/[^/]*$||')
+ if [[ "$HDR_CLASS" == "beauty" ]]; then
+ subject="tools headers: Sync UAPI ${uapi_subdir}/${header_short} with kernel sources"
+ else
+ subject="tools headers UAPI: Sync ${uapi_subdir}/${header_short} with the kernel sources"
+ fi
+ ;;
+ include/linux/socket.h)
+ subject="perf beauty: Update copy of linux/socket.h with the kernel sources"
+ ;;
+ include/linux/*)
+ subject="tools headers: Sync linux/${header_short} with the kernel sources"
+ ;;
+ include/vdso/*)
+ subject="tools headers: Sync vdso/${header_short} with the kernel sources"
+ ;;
+ include/asm-generic/*)
+ subject="tools headers: Sync asm-generic/${header_short} with the kernel sources"
+ ;;
+ arch/*/include/uapi/asm/perf_regs.h)
+ local arch
+ arch=$(echo "$kernel_path" | cut -d/ -f2)
+ subject="tools headers UAPI: Sync ${arch}/asm/perf_regs.h with the kernel sources"
+ ;;
+ arch/*/include/uapi/*)
+ subject="tools headers UAPI: Sync ${header_short} with the kernel sources"
+ ;;
+ arch/*)
+ local arch
+ arch=$(echo "$kernel_path" | cut -d/ -f2)
+ subject="tools arch ${arch}: Sync the ${header_short} copy with the kernel sources"
+ ;;
+ scripts/*|lib/*)
+ subject="tools headers: Sync ${header_short} with the kernel sources"
+ ;;
+ *)
+ subject="tools headers: Sync ${header_short} with the kernel sources"
+ ;;
+ esac
+
+ # Build commit body
+ local body=""
+
+ if [[ -n "$kernel_commits" ]]; then
+ body+="To pick up the changes in:"
+ body+=$'\n'
+ body+=$'\n'
+ body+="$kernel_commits"
+ body+=$'\n'
+ fi
+
+ if [[ $beauty_changed -eq 1 ]]; then
+ # Show the beauty diff in the commit message.
+ body+=$'\n'
+ body+="That introduce these new entries in the string tables:"
+ body+=$'\n'
+
+ local idx=0
+ for script in "${beauty_script_list[@]}"; do
+ local before_msg="$beauty_tmpdir/beauty.$idx.before"
+ local after_msg="$beauty_tmpdir/beauty.$idx.after"
+
+ if ! diff -q "$before_msg" "$after_msg" > /dev/null 2>&1; then
+ body+=$(beauty_body_fragment "$script" "$kernel_path" \
+ "$tools_path" "$before_msg" "$after_msg")
+ body+=$'\n'
+ fi
+ idx=$((idx + 1))
+ done
+ elif [[ ${#beauty_script_list[@]} -gt 0 ]]; then
+ body+=$'\n'
+ body+="That don't result in changes to the string tables generated"
+ body+=" from this header."
+ else
+ body+=$'\n'
+ body+="That just rebuilds perf, silencing this build warning."
+ fi
+
+ body+=$'\n'
+ body+=$'\n'
+ body+="This addresses this perf build warning:"
+ body+=$'\n'
+ body+=$'\n'
+ body+=" Warning: Kernel ABI header differences:"
+ body+=$'\n'
+ body+=" diff -u $tools_path $kernel_path"
+ body+=$'\n'
+ body+=$'\n'
+ body+="Please see tools/include/uapi/README for further details."
+
+ # Add Cc for kernel commit authors
+ local cc_lines=""
+ if [[ -n "$pickup_commits" ]]; then
+ cc_lines=$(format_commits 'Cc: %an <%ae>' "$pickup_commits" | sort -u)
+ fi
+
+ body+=$'\n'
+ body+=$'\n'
+ if [[ -n "$cc_lines" ]]; then
+ body+="$cc_lines"$'\n'
+ fi
+ body+="$(sign_off)"
+
+ cleanup_tmpdir "$beauty_tmpdir"
+
+ if ! git -C "$KERNEL_DIR" add "$tools_path"; then
+ err "Failed to stage $tools_path"
+ COMMIT_FAILED+=("$tools_path")
+ return 1
+ fi
+ if ! git -C "$KERNEL_DIR" commit "$tools_path" \
+ -m "$(printf '%s\n\n%s\n' "$subject" "$body")"; then
+ err "Commit failed for $tools_path"
+ COMMIT_FAILED+=("$tools_path")
+ return 1
+ fi
+
+ SYNCED_OK+=("$tools_path")
+ info "Committed: $subject"
+ echo ""
+}
+
+# --- Group related headers -----------------------------------------------
+#
+# Some headers should be committed together (e.g. all KVM arch headers,
+# all syscall tables). This function processes groups.
+
+process_group() {
+ local group_name="$1"
+ shift
+ local pairs=("$@")
+
+ if [[ ${#pairs[@]} -eq 0 ]]; then
+ return
+ fi
+
+ local file_count
+ file_count=$((${#pairs[@]} / 2))
+ info "Processing group: $group_name ($file_count files)"
+
+ local group_slug="${group_name// /-}"
+
+ local all_kernel_commits=""
+ local all_cc_lines=""
+ local all_warnings=""
+ local group_beauty_tmpdir=""
+ local group_beauty_body=""
+ local group_beauty_scripts=0
+ local files_to_add=()
+
+ # Collect info for all files in the group
+ local i=0
+ while [[ $i -lt ${#pairs[@]} ]]; do
+ local tools_path="${pairs[$i]}"
+ local kernel_path="${pairs[$((i+1))]}"
+ local fidx=$i
+ i=$((i + 2))
+
+ if [[ -n "$HEADER_FILTER" ]] && [[ "$kernel_path" != *"$HEADER_FILTER"* ]]; then
+ continue
+ fi
+
+ if kernel_matches_previous_tools_state "$kernel_path" "$tools_path"; then
+ warn "$tools_path matches a previous tools/ state, leaving it alone"
+ continue
+ fi
+
+ local sync_point pickup_commits=""
+ sync_point=$(find_sync_point "$kernel_path" "$tools_path")
+ if [[ -n "$sync_point" ]]; then
+ pickup_commits=$(find_kernel_commits "$kernel_path" \
+ "$tools_path" "$sync_point")
+ else
+ warn "No previous sync commit found for $tools_path"
+ fi
+ if [[ -n "$pickup_commits" ]]; then
+ all_kernel_commits+=$(format_commits ' %h ("%s")' "$pickup_commits")
+ all_kernel_commits+=$'\n'
+
+ local ccs
+ ccs=$(format_commits 'Cc: %an <%ae>' "$pickup_commits")
+ if [[ -n "$ccs" ]]; then
+ all_cc_lines+="$ccs"$'\n'
+ fi
+ fi
+
+ all_warnings+=" diff -u $tools_path $kernel_path"$'\n'
+
+ # Beauty string tables; the script reads the tools/ copy, so this
+ # runs before the copy too, also in a dry run.
+ local -a group_script_list=()
+ while IFS= read -r script; do
+ [[ -z "$script" ]] && continue
+ group_script_list+=("$script")
+ done < <(find_beauty_scripts "$kernel_path")
+ if [[ ${#group_script_list[@]} -gt 0 ]]; then
+ group_beauty_scripts=1
+ [[ -z "$group_beauty_tmpdir" ]] && group_beauty_tmpdir=$(mktemp -d)
+ local sidx=0
+ for script in "${group_script_list[@]}"; do
+ local script_name
+ script_name=$(basename "$script")
+ local before="$group_beauty_tmpdir/beauty.$fidx.$sidx.before"
+ local after="$group_beauty_tmpdir/beauty.$fidx.$sidx.after"
+
+ if ! run_beauty_pass "$script" "$kernel_path" "$tools_path" \
+ "$group_beauty_tmpdir" "$fidx.$sidx"; then
+ COPY_FAILED+=("$tools_path")
+ for f in "${files_to_add[@]}"; do
+ git -C "$KERNEL_DIR" checkout -- "$f" || \
+ err "Failed to revert $f"
+ done
+ cleanup_tmpdir "$group_beauty_tmpdir"
+ return 1
+ fi
+
+ if ! diff -q "$before" "$after" > /dev/null 2>&1; then
+ echo ""
+ info "Beauty string changes from $script_name:"
+ diff -u "$before" "$after" | head -60 || true
+ group_beauty_body+=$(beauty_body_fragment "$script" \
+ "$kernel_path" "$tools_path" "$before" "$after")
+ group_beauty_body+=$'\n'
+ fi
+ sidx=$((sidx + 1))
+ done
+ fi
+
+ if [[ $DRY_RUN -eq 0 ]]; then
+ if ! cp "$KERNEL_DIR/$kernel_path" "$KERNEL_DIR/$tools_path"; then
+ err "Failed to copy $kernel_path -> $tools_path"
+ COPY_FAILED+=("$tools_path")
+ for f in "${files_to_add[@]}"; do
+ git -C "$KERNEL_DIR" checkout -- "$f" || \
+ err "Failed to revert $f"
+ done
+ cleanup_tmpdir "$group_beauty_tmpdir"
+ return 1
+ fi
+ files_to_add+=("$tools_path")
+ info "Copied $kernel_path -> $tools_path"
+ else
+ info "(dry run) Would copy $kernel_path -> $tools_path"
+ fi
+ done
+
+ # The beauty before/after files are captured into the fragments above.
+ cleanup_tmpdir "$group_beauty_tmpdir"
+
+ if [[ ${#files_to_add[@]} -eq 0 ]]; then
+ return
+ fi
+
+ if [[ $BUILD -eq 1 ]]; then
+ # Collect all unique build targets for the group
+ local group_targets=""
+ for f in "${files_to_add[@]}"; do
+ classify_header "$f"
+ group_targets+=$(find_build_targets "$f" "$HDR_CLASS")
+ group_targets+=$'\n'
+ done
+ group_targets=$(echo "$group_targets" | sort -u | grep -v '^$')
+
+ show_build_targets "group $group_name" "$group_targets"
+ local build_failed=0
+ while IFS=: read -r name dir; do
+ [[ -z "$name" ]] && continue
+ detail " Build-testing $name ($dir)..."
+ local log="$OUTPUT_DIR/$name.$group_slug.log"
+ if ! build_target "$name" "$dir" > "$log" 2>&1; then
+ err " $name build failed, see $log"
+ build_failed=1
+ fi
+ done <<< "$group_targets"
+
+ if [[ $build_failed -eq 1 ]]; then
+ err "Build failed after copying group $group_name — reverting."
+ for f in "${files_to_add[@]}"; do
+ git -C "$KERNEL_DIR" checkout -- "$f" || \
+ err "Failed to revert $f"
+ BUILD_FAILED+=("$f (group: $group_name)")
+ done
+ return 1
+ fi
+ info "Build OK."
+ fi
+
+ if [[ $DRY_RUN -eq 1 ]]; then
+ return
+ fi
+
+ if [[ $COMMIT -eq 0 ]]; then
+ for f in "${files_to_add[@]}"; do
+ SYNCED_OK+=("$f")
+ done
+ return
+ fi
+
+ # Deduplicate kernel commits and Cc lines; the commits keep the git log
+ # order they came in, the Cc lines stay sorted.
+ all_kernel_commits=$(printf '%s\n' "$all_kernel_commits" | \
+ awk 'NF && !seen[$0]++')
+ all_cc_lines=$(echo "$all_cc_lines" | sort -u | grep -v '^$' || true)
+
+ local subject="tools headers: Sync $group_name with the kernel sources"
+ local body=""
+
+ if [[ -n "$all_kernel_commits" ]]; then
+ body+="To pick up the changes in:"
+ body+=$'\n'
+ body+=$'\n'
+ body+="$all_kernel_commits"
+ body+=$'\n'
+ fi
+
+ if [[ -n "$group_beauty_body" ]]; then
+ body+=$'\n'
+ body+="That introduce these new entries in the string tables:"
+ body+=$'\n'
+ body+="$group_beauty_body"
+ elif [[ $group_beauty_scripts -eq 1 ]]; then
+ body+=$'\n'
+ body+="That don't result in changes to the string tables generated"
+ body+=" from these headers."
+ fi
+
+ body+=$'\n'
+ body+=$'\n'
+ body+="This addresses these perf build warnings:"
+ body+=$'\n'
+ body+=$'\n'
+ body+=" Warning: Kernel ABI header differences:"
+ body+=$'\n'
+ body+="$all_warnings"
+ body+=$'\n'
+ body+="Please see tools/include/uapi/README for further details."
+ body+=$'\n'
+ body+=$'\n'
+ if [[ -n "$all_cc_lines" ]]; then
+ body+="$all_cc_lines"$'\n'
+ fi
+ body+="$(sign_off)"
+
+ for f in "${files_to_add[@]}"; do
+ if ! git -C "$KERNEL_DIR" add "$f"; then
+ err "Failed to stage $f"
+ for g in "${files_to_add[@]}"; do
+ COMMIT_FAILED+=("$g")
+ done
+ return 1
+ fi
+ done
+
+ if ! git -C "$KERNEL_DIR" commit "${files_to_add[@]}" \
+ -m "$(printf '%s\n\n%s\n' "$subject" "$body")"; then
+ err "Commit failed for group $group_name"
+ for f in "${files_to_add[@]}"; do
+ COMMIT_FAILED+=("$f")
+ done
+ return 1
+ fi
+
+ for f in "${files_to_add[@]}"; do
+ SYNCED_OK+=("$f")
+ done
+
+ info "Committed: $subject"
+ echo ""
+}
+
+# --- Main -----------------------------------------------------------------
+
+main() {
+ cd "$KERNEL_DIR"
+
+ if [[ ! -d include/uapi ]]; then
+ err "Must be run from a kernel tree root (or tools/perf/)"
+ exit 1
+ fi
+
+ if [[ ! -d tools/perf ]]; then
+ err "Cannot find tools/perf/ in kernel tree"
+ exit 1
+ fi
+
+ setup_beauty_args || true
+
+ if [[ $BUILD -eq 1 ]]; then
+ # umask covers every level created, -m with -p only the last.
+ (umask 077 && mkdir -p "$OUTPUT_DIR")
+ fi
+
+ # Check for uncommitted changes in tools/
+ if ! git diff --quiet -- tools/include/ tools/perf/trace/beauty/ tools/perf/util/ tools/perf/arch/*/entry/syscalls/ tools/scripts/ tools/lib/ tools/arch/; then
+ warn "You have uncommitted changes in tools/. Commit or stash them first."
+ if [[ $DRY_RUN -eq 0 ]]; then
+ exit 1
+ fi
+ fi
+
+ info "Running check-headers to detect stale copies..."
+ echo ""
+
+ local stale
+ if ! stale=$(detect_stale_headers); then
+ info "All headers are up to date."
+ exit 0
+ fi
+
+ echo "Stale headers found:"
+ echo "$stale"
+ echo ""
+
+ # Parse the stale header pairs and group them
+ # Each line is: tools_path kernel_path
+ local kvm_group=()
+ local syscall_group=()
+ local singles=()
+
+ while IFS=' ' read -r tools_path kernel_path; do
+ case "$kernel_path" in
+ *kvm.h|*svm.h|*vmx.h|*sie.h)
+ kvm_group+=("$tools_path" "$kernel_path")
+ ;;
+ *syscall*.tbl|*unistd.h)
+ syscall_group+=("$tools_path" "$kernel_path")
+ ;;
+ *)
+ singles+=("$tools_path" "$kernel_path")
+ ;;
+ esac
+ done <<< "$stale"
+
+ # Process individual headers first (beauty and simple copies).
+ # || true: continue on failure; failures are tracked in BUILD_FAILED[],
+ # COPY_FAILED[] and COMMIT_FAILED[] and reported in the summary.
+ local i=0
+ while [[ $i -lt ${#singles[@]} ]]; do
+ process_header "${singles[$i]}" "${singles[$((i+1))]}" || true
+ i=$((i + 2))
+ done
+
+ # Process grouped headers
+ if [[ ${#kvm_group[@]} -gt 0 ]]; then
+ process_group "KVM headers" "${kvm_group[@]}" || true
+ fi
+
+ if [[ ${#syscall_group[@]} -gt 0 ]]; then
+ process_group "syscall tables and unistd.h" "${syscall_group[@]}" || true
+ fi
+
+ # Summary
+ echo ""
+ echo "========================================="
+ echo " Summary"
+ echo "========================================="
+
+ if [[ ${#SYNCED_OK[@]} -gt 0 ]]; then
+ info "Synced successfully (${#SYNCED_OK[@]}):"
+ for f in "${SYNCED_OK[@]}"; do
+ echo " $f"
+ done
+ fi
+
+ if [[ ${#BUILD_FAILED[@]} -gt 0 ]]; then
+ echo ""
+ err "Build failures (${#BUILD_FAILED[@]}) — reverted, need manual attention:"
+ for f in "${BUILD_FAILED[@]}"; do
+ echo " $f"
+ done
+ fi
+
+ if [[ ${#COPY_FAILED[@]} -gt 0 ]]; then
+ echo ""
+ err "Copy failures (${#COPY_FAILED[@]}) — need manual attention:"
+ for f in "${COPY_FAILED[@]}"; do
+ echo " $f"
+ done
+ fi
+
+ if [[ ${#COMMIT_FAILED[@]} -gt 0 ]]; then
+ echo ""
+ err "Commit failures (${#COMMIT_FAILED[@]}) — need manual attention:"
+ for f in "${COMMIT_FAILED[@]}"; do
+ echo " $f"
+ done
+ fi
+
+ # Final check-headers run to see what's still out of sync
+ echo ""
+ local remaining
+ if remaining=$(detect_stale_headers); then
+ warn "Headers still out of sync:"
+ echo "$remaining"
+ else
+ info "All headers are now in sync."
+ fi
+
+ if [[ ${#BUILD_FAILED[@]} -gt 0 || ${#COPY_FAILED[@]} -gt 0 || \
+ ${#COMMIT_FAILED[@]} -gt 0 ]]; then
+ exit 1
+ fi
+}
+
+main "$@"
--
2.56.0
next prev parent reply other threads:[~2026-10-21 21:30 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-21 21:29 [PATCH v1 00/12] perf tools: Add a script to sync the header copies with the kernel sources Arnaldo Carvalho de Melo
2026-10-21 21:29 ` Arnaldo Carvalho de Melo [this message]
2026-10-21 21:29 ` [PATCH v1 02/12] tools headers UAPI: Sync linux/if_addr.h " Arnaldo Carvalho de Melo
2026-10-21 21:29 ` [PATCH v1 03/12] tools headers UAPI: Sync linux/rtnetlink.h " Arnaldo Carvalho de Melo
2026-10-21 21:29 ` [PATCH v1 04/12] tools headers: Sync linux/gfp_types.h " Arnaldo Carvalho de Melo
2026-10-21 21:29 ` [PATCH v1 05/12] tools headers x86 cpufeatures: Sync " Arnaldo Carvalho de Melo
2026-10-21 21:30 ` [PATCH v1 06/12] tools arch x86: Sync the msr-index.h copy " Arnaldo Carvalho de Melo
2026-10-21 21:30 ` [PATCH v1 07/12] perf beauty: Update copy of linux/socket.h " Arnaldo Carvalho de Melo
2026-10-21 21:58 ` Mahe Tardy
2026-10-21 21:30 ` [PATCH v1 08/12] tools headers: Sync UAPI linux/fcntl.h " Arnaldo Carvalho de Melo
2026-10-21 21:30 ` [PATCH v1 09/12] tools headers: Sync UAPI linux/fs.h " Arnaldo Carvalho de Melo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261021213007.21046-2-acme@kernel.org \
--to=acme@kernel.org \
--cc=acme@redhat.com \
--cc=adrian.hunter@intel.com \
--cc=irogers@google.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mingo@kernel.org \
--cc=namhyung@kernel.org \
--cc=tglx@linutronix.de \
--cc=williams@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®