Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Git Bisection Automated for build of a kernel #810

Closed
wants to merge 4 commits into from
Closed
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: 2 additions & 1 deletion OpTestConfiguration.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,8 @@ def get_parser():
help="Don't exit if we find unknown command line arguments")
abdhaleegit marked this conversation as resolved.
Show resolved Hide resolved
misc_group.add_argument("--secvar-payload-url",
help="Specify a URL for the secvar test data payload")

abdhaleegit marked this conversation as resolved.
Show resolved Hide resolved
misc_group.add_argument("--bisect-flag",
help="Specify if bisection is to be done or not")
return parser


Expand Down
22 changes: 22 additions & 0 deletions test_binaries/make.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
# OpenPOWER Automated Test Project
#
# Contributors Listed Below - COPYRIGHT 2024
# [+] International Business Machines Corp.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.
nproc=`nproc`
yes "" | make olddefconfig /dev/null 2>&1
make -j$nproc -S vmlinux > /dev/null 2>&1
make modules && make install 2>&1
abdhaleegit marked this conversation as resolved.
Show resolved Hide resolved
111 changes: 111 additions & 0 deletions testcases/Build_bisector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env python3
# OpenPOWER Automated Test Project
#
# Contributors Listed Below - COPYRIGHT 2024
# [+] International Business Machines Corp.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.
import json
import os
import re
import unittest
from urllib.parse import urlparse
import subprocess
import OpTestConfiguration
import OpTestLogger
from common.OpTestSystem import OpSystemState
from common.OpTestSOL import OpSOLMonitorThread
from common.OpTestInstallUtil import InstallUtil
from common.Exceptions import CommandFailed
import test_binaries
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move import to above and arrange all imports in alphabaticle order

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and add one empty line below

log = OpTestLogger.optest_logger_glob.get_logger(__name__)


class Buil_bisector(unittest.TestCase):
"""
Test case for bisecting the Linux kernel using Git Bisect.
This test downloads the Linux kernel from a specified repository,
configures and compiles it, and then uses Git Bisect to find the
commit that introduced a build failure.
"""
def setUp(self):
"""
Set up the test environment.
Initializes test parameters and checks required configurations.
"""
self.conf = OpTestConfiguration.conf
self.cv_HOST = self.conf.host()
self.cv_SYSTEM = self.conf.system()
self.connection = self.cv_SYSTEM.cv_HOST.get_ssh_connection()
self.console_thread = OpSOLMonitorThread(1, "console")
self.host_cmd_timeout = self.conf.args.host_cmd_timeout
self.repo = self.conf.args.git_repo
self.repo_reference = self.conf.args.git_repo_reference
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why 2 params for repo ?

self.branch = self.conf.args.git_branch
self.home = self.conf.args.git_home
self.config_path = self.conf.args.git_repoconfigpath
self.config = self.conf.args.git_repoconfig
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it can be single config parameter that can take both path or http link

self.good_commit = self.conf.args.good_commit
self.bad_commit = self.conf.args.bad_commit
self.bisect_script = self.conf.args.bisect_script
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we need script parameter ?

self.bisect_category = self.conf.args.bisect_category
self.append_kernel_cmdline = self.conf.args.append_kernel_cmdline
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this parameter can be taken explicitly ? than no need to have it as class param

self.linux_path = os.path.join(self.home, "linux")
if self.config_path:
self.config = "olddefconfig"
if not self.repo:
self.fail("Provide git repo of kernel to install")
if not (self.conf.args.host_ip and self.conf.args.host_user and self.conf.args.host_password):
self.fail(
"Provide host ip user details refer, --host-{ip,user,password}")

def get_email(self, commit_id):
abdhaleegit marked this conversation as resolved.
Show resolved Hide resolved
"""
To get email of Author from the identified bad commit
"""
try :
self.connection.run_command("git config --global color.ui true")
result = self.connection.run_command("git show --format=%ce {} | sed -r 's/\x1B\[[0-9:]*[JKsu]//g'".format(commit_id))
email = result[0]
return email
except subprocess.CalledProcessError as e:
return None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move this to common lib ?


def runTest(self):
Tejas3772 marked this conversation as resolved.
Show resolved Hide resolved
self.connection.run_command(" if [ '$(pwd)' != {} ]; then cd {} || exit 1 ; fi ".format(self.linux_path,self.linux_path))
shallow = self.connection.run_command("git rev-parse --is-shallow-repository")
print(type(shallow[-1]))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is print necessay ? if not than remove

if shallow[-1] == True or shallow[-1] == 'true':
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the value is different for different repo ? is just True not enough

self.connection.run_command("git fetch --unshallow",timeout=3000)
makefile_path = os.path.join(self.conf.basedir, "make.sh")
self.cv_HOST.copy_test_file_to_host(makefile_path, dstdir=self.linux_path)
self.connection.run_command("git bisect start")
folder_type=re.split(r'[\/\\.]',str(self.repo))[-2]
print("FOLDER",folder_type)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also try to not have print statements .. instead log.info if it is really needed

if folder_type == 'linux-next':
self.connection.run_command("git fetch --tags")
good_tag=self.connection.run_command("git tag -l 'v[0-9]*' | sort -V | tail -n 1")
self.connection.run_command("git bisect good {} ".format(good_tag))
else:
self.connection.run_command("git bisect good {} ".format(self.good_commit))
self.connection.run_command(" git bisect bad ")
Tejas3772 marked this conversation as resolved.
Show resolved Hide resolved
self.connection.run_command("chmod +x ./make.sh ")
commit = self.connection.run_command(" git bisect run ./make.sh")
badCommit = [word for word in commit if word.endswith("is the first bad commit")]
badCommit= badCommit[0].split()[0]
email = self.get_email(badCommit)
log.info("LOGGG")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you added this info for debug... please remove

self.connection.run_command("git bisect log")
self.connection.run_command("git bisect reset")
return email , badCommit
147 changes: 147 additions & 0 deletions testcases/Email_git.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#Email_git.py: to make boot of the repo and test it through exit code

#!/usr/bin/env python3
# OpenPOWER Automated Test Project
#
# Contributors Listed Below - COPYRIGHT 2024
# [+] International Business Machines Corp.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.
import json
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add one empty line above import

# import requests
import os
import uuid
from datetime import datetime
import re
import unittest
import os
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicate os import ... also check if unused improts can be removed

from urllib.parse import urlparse
from enum import Enum
import subprocess
import OpTestConfiguration
import OpTestLogger
import configparser
import sys
from testcases import BisectKernel
from testcases import Test_build
from common.OpTestSystem import OpSystemState
from common.OpTestSOL import OpSOLMonitorThread
from common.OpTestInstallUtil import InstallUtil
from common.Exceptions import CommandFailed
# from testcases import Boot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this comented line and add one empty line

log = OpTestLogger.optest_logger_glob.get_logger(__name__)
class Email_git(unittest.TestCase):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add 2 empty line above class

"""
Test case for bisecting the Linux kernel using Git Bisect.
This test downloads the Linux kernel from a specified repository,
configures and compiles it, and then uses Git Bisect to find the
commit that introduced a specific issue.
"""
def setUp(self):
"""
Set up the test environment.
Initializes test parameters and checks required configurations.
"""
self.conf = OpTestConfiguration.conf
self.cv_HOST = self.conf.host()
self.cv_SYSTEM = self.conf.system()
self.connection = self.cv_SYSTEM.cv_HOST.get_ssh_connection()
self.console_thread = OpSOLMonitorThread(1, "console")
self.host_cmd_timeout = self.conf.args.host_cmd_timeout
self.repo = self.conf.args.git_repo
self.repo_reference = self.conf.args.git_repo_reference
self.branch = self.conf.args.git_branch
self.home = self.conf.args.git_home
# self.config_path = self.conf.args.git_repoconfigpatih
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove if not needed

self.config = self.conf.args.git_repoconfig
self.good_commit = self.conf.args.good_commit
self.bad_commit = self.conf.args.bad_commit
self.bisect_script = self.conf.args.bisect_script
self.bisect_category = self.conf.args.bisect_category
self.append_kernel_cmdline = self.conf.args.append_kernel_cmdline
self.linux_path = os.path.join(self.home, "linux")
if not self.repo:
self.fail("Provide git repo of kernel to install")
if not (self.conf.args.host_ip and self.conf.args.host_user and self.conf.args.host_password):
self.fail(
"Provide host ip user details refer, --host-{ip,user,password}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty line before next method

def get_commit_message(self,commit_sha):
try:
self.connection.run_command(" if [ '$(pwd)' != {} ]; then cd {} || exit 1 ; fi ".format(self.linux_path,self.linux_path))

commit_message = self.connection.run_command("git log -n 1 --pretty=format:%s {} | sed -r 's/\x1B\[[0-9:]*[JKsu]//g'".format(commit_sha))
print(commit_message)
except subprocess.CalledProcessError:
commit_message = None
print(commit_message[0].strip())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why print messages, if you plan to return the message

return commit_message[0].strip()
def runTest(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add empty line above the method

# def generate_email_template(machine_type, gcc_version, commit, error_message):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why need commented line

machine_type = self.connection.run_command("uname -m")
gcc_version = self.connection.run_command("gcc --version")[0]
kernel_version = self.connection.run_command("uname -r")
try:
with open("output.json", "r") as file:
data = json.load(file)
error_message = data.get("error", "")
commit = str(data.get("commit", ""))[:7]
except FileNotFoundError:
print("Error: output.json not found.")
error_message = ""
commit = ""

fix_description = self.get_commit_message(commit)
# self.repo = "https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove if not needed


if "https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git" in self.repo:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the link can be different for same repo, so can we do this

if 'netdev' in self.repo:
    linux = "netdev/net"

similarly to all repos.. .just look for unique string and decide the linux name
like
if torvalds in self.repo --> mainline
if linux-next in self.repo --> linux-next
if scsi in self.repo --> mkpscsi/scsi-quueue

etc

linux = "netdev/net"
elif "https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git" in self.repo:
linux = "netdev/net-next"
elif "https://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git" in self.repo or "git://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git" in self.repo:
linux = "scsi/scsi-queue"
elif "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git" in self.repo:
linux = "mainline/master"
elif "https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git" in self.repo:
linux = "linux-next/master"
else:
linux = "linux"

subject = "[{}][bisected {}][PPC {}] build fail with error: {}".format(linux,commit, machine_type, error_message)

body = """
Greetings,

Today's next kernel fails to build with gcc {} on {} machine.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

linux repo should be generic . it can be any linux which we identified above in if elif code
Today's {} kernel fails to build with gcc {} on {} machine


Kernel build fail at error: {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generic : based on what failure.. we decide build or boot or test failure


Kernel Version: {}
Machine Type: {}
gcc: {}
Commit: {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad commit: {}


kernel builds fine when the bad commit ({}) is reverted
{} - {}

--
Regards
PPC KBOT
""".format(gcc_version, machine_type, error_message,kernel_version, machine_type, gcc_version, commit,commit, commit, fix_description)
print(subject)
print(body)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is print require or for debug only .. you decide to keep or remove


with open("email.json","w") as email:
json.dump({"subject":subject,"body":body},email)
# return subject, body
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should uncomment ? or remove


Loading