Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,6 @@ patches/
.pdm-python

.vscode

linux-stable/
kernel-euler/
57 changes: 56 additions & 1 deletion pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "ppatch"
version = "0.0.5"
version = "0.0.6b2.post1"
description = "Universal tool to analyze patches"
authors = [
{name = "jingfelix", email = "jingfelix@outlook.com"},
Expand All @@ -11,6 +11,8 @@ dependencies = [
"pydantic-settings>=2.2.1",
"cscopy>=0.0.3",
"whatthepatch-pydantic==1.0.6a2",
"tree-sitter>=0.24.0",
"tree-sitter-c==0.23.2",
]
requires-python = ">=3.10"
readme = "README.md"
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ shellingham==1.5.4
stack-data==0.6.3; python_version > "3.6"
tomli==2.2.1; python_version > "3.6" and python_version < "3.11"
traitlets==5.14.3; python_version > "3.6"
tree-sitter==0.24.0
tree-sitter-c==0.23.2
typer[all]==0.15.1
typing-extensions==4.12.2
viztracer==1.0.0
Expand Down
15 changes: 11 additions & 4 deletions scripts/run_one_ppatch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ use_config=$2
# 定义三个命令,使用该变量
if [[ $use_config == "true" ]]; then
echo "Use config = True"
command1="ppatch auto /home/laboratory/workspace/archive/patches/poc_$variable.patch -o ../6.1-result/$variable.patch -c /home/laboratory/workspace/exps/ppatch/example_extra_config.json"
# command1="ppatch --verbose auto /home/laboratory/workspace/archive/patches/poc_$variable.patch -o ../6.1-result/$variable.patch -c /home/laboratory/workspace/exps/ppatch/example_extra_config.json"
command1="ppatch auto -O /home/laboratory/workspace/archive/patches/poc_$variable.patch -o ../6.1-result/$variable.patch -c /home/laboratory/workspace/exps/ppatch/example_extra_config.json"
else
command1="ppatch auto /home/laboratory/workspace/archive/patches/poc_$variable.patch -o ../6.1-result/$variable.patch"
# command1="ppatch --verbose auto /home/laboratory/workspace/archive/patches/poc_$variable.patch -o ../6.1-result/$variable.patch"
command1="ppatch auto -O /home/laboratory/workspace/archive/patches/poc_$variable.patch -o ../6.1-result/$variable.patch"
fi

command2="patch -p 1 -F 3 -i ../6.1-result/$variable.patch"
command3=" patch -R -p 1 -F 3 -f -i /home/laboratory/workspace/archive/patches/poc_$variable.patch"
command3="patch -R -p 1 -F 3 -f -i /home/laboratory/workspace/archive/patches/poc_$variable.patch"

# 执行第一个命令并检查返回值
$command1
Expand Down Expand Up @@ -42,6 +44,11 @@ read -p "命令3执行成功,按任意键继续..."

git diff > $variable.patch

scp pride:/mnt/cd7/ppatch_autoset/auto_linux_6.9.5_back/exps/poc_$variable/linux_6_lts/x86_64/linux_6_lts_kernel/upper_dir/.config .config.$variable
# scp pride:/mnt/cd7/ppatch_autoset/auto_linux_6.9.5_back/exps/poc_$variable/linux_6_lts/x86_64/linux_6_lts_kernel/upper_dir/.config .config.$variable
command4="scp pride:/mnt/cd7/ppatch_autoset/ppatch_partner/src/exps/poc_$variable/longterm/x86_64/longterm_kernel/upper_dir/.config .config.$variable"
$command4
if [ $? -ne 0 ]; then
scp pride:/mnt/cd7/ppatch_autoset/ppatch_partner/src/configs/x86_64_common_config .config.$variable
fi

read -p "patch 生成与 config 下载结束,按任意键继续..."
86 changes: 86 additions & 0 deletions scripts/test_find_changed_funcs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# input 1: current file
# input 2: current patch
# input 3: original file
# input 4: original patch

from ppatch.model import File, Patch
from ppatch.utils.ast import File as FileAST
from ppatch.utils.ast import Func
from ppatch.utils.parse import parse_patch

file_path = ""

original_file = ""
original_patch = ""

current_file = ""
current_patch = ""


def find_changed_funcs(file: str, patch: str) -> list[Func]:

original_lines = File(content=file).line_list
current_patch: Patch = parse_patch(patch)

# 从 Patch 确定要产生修改的行
# 前提在于选定的 patch 是正确的(如何保证呢,需要使用 Diff)
current_diff = None

for diff in current_patch.diff:
if diff.header.old_path == file_path:
current_diff = diff
break

changed_lines = []
for change in current_diff.changes:
if change.old is not None:
original_lines[change.old].changed = True
changed_lines.append(change.old)

ast_file = FileAST("".join([str(line) + "\n" for line in original_lines]))

changed_funcs: list[Func] = []
for line in changed_lines:
func = ast_file.line_func_map[line]
if func and func not in changed_funcs:
changed_funcs.append(func)

return changed_funcs


def check(
original_file: str, original_patch: str, current_file: str, current_patch: str
):

old_changed_funcs = find_changed_funcs(original_file, original_patch)
new_changed_funcs = find_changed_funcs(current_file, current_patch)

old_funcs = FileAST(original_file).funcs
# new_funcs = FileAST(current_file).funcs

old_changed_func_names = [func["name"] for func in old_changed_funcs]
old_func_names = [func["name"] for func in old_funcs]

new_changed_func_names = [func["name"] for func in new_changed_funcs]
new_func_names = [func["name"] for func in FileAST(current_file).funcs]

for func in new_changed_funcs:
if func["name"] not in old_changed_func_names:
print(f"Warning: found new changed function {func['name']}")
# Check if the function actually exists in the old file
if func["name"] in old_func_names:
print(f"Warning: found inconsistent changed function {func['name']}")

# for func in old_changed_funcs:
# if func["name"] not in new_changed_func_names:
# print(f"Warning: found missing changed function {func['name']}")
# # Check if the function actually exists in the new file
# if func["name"] not in new_func_names:
# print(f"Warning: found inconsistent changed function {func['name']}")

# for func in old_funcs:
# if func["name"] == "hci_get_route" or func["name"] == "hci_conn_enter_active_mode":
# print(f"Warning: found changed function {func['name']}")


check(original_file, original_patch, current_file, current_patch)
5 changes: 4 additions & 1 deletion src/ppatch/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@
import importlib_metadata


__version__ = importlib_metadata.version("ppatch")
try:
__version__ = importlib_metadata.version("ppatch")
except importlib_metadata.PackageNotFoundError:
__version__ = "0.0.0"
111 changes: 101 additions & 10 deletions src/ppatch/commands/apply.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import os
import subprocess
from typing import Annotated

import typer
import whatthepatch

from ppatch.app import app, logger
from ppatch.model import File
from ppatch.commands.get import getpatches
from ppatch.config import settings
from ppatch.model import SHA, File
from ppatch.utils.ast import File as FileAST
from ppatch.utils.common import get_changed_funcs, process_title
from ppatch.utils.parse import parse_patch
from ppatch.utils.resolve import apply_change

Expand All @@ -16,6 +20,7 @@ def apply(
patch_path: str,
reverse: Annotated[bool, typer.Option("-R", "--reverse")] = False,
fuzz: Annotated[int, typer.Option("-F", "--fuzz")] = 0,
function_check: bool = typer.Option(False, "--function-check", "-f"),
):
"""
Apply a patch to a file.
Expand All @@ -31,12 +36,49 @@ def apply(
has_failed = False

with open(patch_path, mode="r", encoding="utf-8") as (f):
diffes = parse_patch(f.read()).diff
parser = parse_patch(f.read())
diffes = parser.diff
sha_list: list[SHA] = []

if function_check:
file_name = diffes[0].header.old_path # No need to distinguish reverse here

subject = parser.subject
hit_list, sha_list = getpatches(file_name, subject, save=True)
sha_for_sure = None

for sha in hit_list:
with open(
os.path.join(
settings.base_dir,
settings.patch_store_dir,
f"{sha}-{process_title(file_name)}.patch",
),
mode="r",
encoding="utf-8",
) as (f):
text = f.read()

# logger.debug(f"1st: {parse_patch(text).subject}")
# logger.debug(f"2nd: {subject}")

target_subject = parse_patch(text).subject
if target_subject in subject or subject in target_subject:
sha_for_sure = sha
break

if sha_for_sure is None:
logger.error(
f"function_check: Cannot find a patch with subject '{subject}' for file '{file_name}'."
)
raise typer.Exit(code=1)

for diff in diffes:

old_filename = diff.header.old_path
new_filename = diff.header.new_path
if reverse:
old_filename, new_filename = new_filename, old_filename

if os.path.exists(old_filename):

Expand All @@ -54,13 +96,62 @@ def apply(
logger.error(f"Failed hunk: {failed_hunk.index}")
else:
logger.error(f"{old_filename} not found!")
raise typer.Exit(code=1)

# 写入文件
if not has_failed:
with open(new_filename, mode="w+", encoding="utf-8") as f:
for line in new_line_list:
if line.status:
f.write(line.content + "\n")
# git log --oneline --diff-filter=R -- <old_filename>
output: str = subprocess.run(
["git", "log", "--oneline", "--diff-filter=R", "--", old_filename],
capture_output=True,
).stdout.decode("utf-8", errors="ignore")
if len(output) > 0:
logger.warning(f"File {old_filename} has been renamed.")

raise typer.Exit(code=2)

# 写入文件:与 GNU patch 一致,成功 hunk 即使存在失败 hunk 也会落盘
if function_check:
# Note that file won't be saved if function_check is True

# 记录当前修改所属的函数列表和所有函数列表
patched_text = "\n".join([line.content for line in new_line_list])
file_ast = FileAST(patched_text)

patched_all_funcs = file_ast.funcs
patched_changed_funcs = get_changed_funcs(new_line_list, file_ast)

# Get to the original file
# git show sha:filename
# 利用 sha_for_sure 获取指定 sha 的前一个 sha
sha_for_sure_index = sha_list.index(sha_for_sure)
sha_before_sure = sha_list[sha_for_sure_index + 1] # TODO: check this

before_original_file: str = subprocess.run(
[
"git",
"show",
f"{sha_before_sure}:{diff.header.old_path}",
], # No need to distinguish reverse here
capture_output=True,
).stdout.decode("utf-8", errors="ignore")

# 尝试 apply 原补丁来确定原始修改所属的函数
original_file_line_list = apply_change(
diff.hunks,
File(content=before_original_file).line_list,
reverse=reverse,
fuzz=fuzz,
).new_line_list

original_file_ast = FileAST(before_original_file)
original_changed_funcs = get_changed_funcs(
original_file_line_list, original_file_ast
)

logger.info(f"Patched changed funcs: {patched_changed_funcs}")
logger.info(f"Original changed funcs: {original_changed_funcs}")

with open(new_filename, mode="w+", encoding="utf-8") as f:
for line in new_line_list:
if line.status:
f.write(line.content + "\n")

raise typer.Exit(code=1 if has_failed else 0)
Loading
Loading