toolchain_util: Add function to modify kernel afdo metadata
Update kernel json metadata file in bundle function, as described
in go/cros-recipe-builder-commits.
BUG=chromium:1081332
TEST=unittest
Change-Id: I8c0ab0482cf97326e88b6aab28af09c17833f651
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/2606285
Commit-Queue: Tiancong Wang <tcwang@google.com>
Tested-by: Tiancong Wang <tcwang@google.com>
Reviewed-by: LaMont Jones <lamontjones@chromium.org>
diff --git a/lib/toolchain_util.py b/lib/toolchain_util.py
index a5aaf04..ca24cf6 100644
--- a/lib/toolchain_util.py
+++ b/lib/toolchain_util.py
@@ -79,8 +79,8 @@
BZ2_COMPRESSION_SUFFIX = '.bz2'
XZ_COMPRESSION_SUFFIX = '.xz'
KERNEL_AFDO_COMPRESSION_SUFFIX = '.gcov.xz'
-TOOLCHAIN_UTILS_PATH = os.path.join(
- constants.SOURCE_ROOT, 'src/third_party/toolchain-utils')
+TOOLCHAIN_UTILS_PATH = os.path.join(constants.SOURCE_ROOT,
+ 'src/third_party/toolchain-utils')
AFDO_PROFILE_PATH_IN_CHROMIUM = 'src/chromeos/profiles/%s.afdo.newest.txt'
MERGED_AFDO_NAME = 'chromeos-chrome-amd64-%s'
@@ -2344,16 +2344,19 @@
if not kernel_version:
raise BundleArtifactsHandlerError('kernel_version not provided.')
kernel_version = kernel_version.replace('.', '_')
- profile_name = self._GetArtifactVersionInEbuild(
- 'chromeos-kernel-%s' % kernel_version,
- 'AFDO_PROFILE_VERSION') + KERNEL_AFDO_COMPRESSION_SUFFIX
+ profile_version = self._GetArtifactVersionInEbuild(
+ f'chromeos-kernel-{kernel_version}', 'AFDO_PROFILE_VERSION')
+ profile_name = profile_version + KERNEL_AFDO_COMPRESSION_SUFFIX
# The verified profile is in the sysroot with a name similar to:
# /usr/lib/debug/boot/chromeos-kernel-4_4-R82-12874.0-1581935639.gcov.xz
profile_path = os.path.join(
self.chroot.path, self.sysroot_path[1:], 'usr', 'lib', 'debug', 'boot',
- 'chromeos-kernel-%s-%s' % (kernel_version, profile_name))
+ f'chromeos-kernel-{kernel_version}-{profile_name}')
verified_profile = os.path.join(self.output_dir, profile_name)
shutil.copy2(profile_path, verified_profile)
+
+ # Also change the kernel JSON file, in order for PUpr to pick up the change
+ self._UpdateKernelMetadata(kernel_version, profile_version)
return [verified_profile]
def _BundleUnverifiedChromeCwpAfdoFile(self):
@@ -2375,6 +2378,28 @@
XZ_COMPRESSION_SUFFIX)
@staticmethod
+ def _UpdateKernelMetadata(kernel_version: str, profile_version: str):
+ """Update afdo_metadata json file"""
+ json_file = os.path.join(TOOLCHAIN_UTILS_PATH, 'afdo_metadata',
+ 'kernel_afdo.json')
+ afdo_versions = json.loads(osutils.ReadFile(json_file))
+ assert kernel_version in afdo_versions, \
+ f'To update {kernel_version}, the entry should be in kernel_afdo.json'
+ old_value = afdo_versions[kernel_version]['name']
+ update_to_newer_profile = _RankValidCWPProfiles(
+ old_value) < _RankValidCWPProfiles(profile_version)
+ # This function is called by Bundle, so normally the profile is newer
+ # is guaranteed because Bundle function only runs when a new profile is
+ # needed to verify at the beginning of the builder. This check is to
+ # make sure there's no other updates happen between the start of the
+ # builder and the time of this function call.
+ assert update_to_newer_profile, (
+ f'Failed to update JSON file because {profile_version} is not '
+ f'newer than {old_value}')
+ afdo_versions[kernel_version]['name'] = profile_version
+ pformat.json(afdo_versions, fp=json_file)
+
+ @staticmethod
def _ListTransitiveFiles(base_directory: str):
for dir_path, _dir_names, file_names in os.walk(base_directory):
for file_name in file_names:
diff --git a/lib/toolchain_util_unittest.py b/lib/toolchain_util_unittest.py
index 2873ac9..8bdde76 100644
--- a/lib/toolchain_util_unittest.py
+++ b/lib/toolchain_util_unittest.py
@@ -528,7 +528,10 @@
self.artifact_type = 'Unspecified'
self.outdir = None
self.afdo_tmp_path = None
- self.profile_info = {}
+ self.kernel_version = '4_4'
+ self.profile_info = {
+ 'kernel_version': self.kernel_version.replace('_', '.'),
+ }
self.orderfile_name = (
'chromeos-chrome-orderfile-field-78-3877.0-1567418235-'
'benchmark-78.0.3893.0-r1.orderfile')
@@ -537,6 +540,7 @@
self.debug_binary_name = 'chromeos-chrome-amd64-78.0.3893.0_rc-r1.debug'
self.merged_afdo_name = (
'chromeos-chrome-amd64-78.0.3893.0_rc-r1-merged.afdo')
+ self.kernel_name = 'R89-13638.0-1607337135'
self.gen_order = self.PatchObject(
toolchain_util.GenerateChromeOrderfile, 'Bundle', new=_Bundle)
@@ -725,6 +729,24 @@
print_cmd=True,
)
+ def testBundleVerifiedKernelCwpAfdoFile(self):
+ self.SetUpBundle('VerifiedKernelCwpAfdoFile')
+ mock_update = self.PatchObject(self.obj, '_UpdateKernelMetadata')
+ mock_ebuild = self.PatchObject(
+ self.obj, '_GetArtifactVersionInEbuild', return_value=self.kernel_name)
+ ret = self.obj.Bundle()
+ profile_name = self.kernel_name + \
+ toolchain_util.KERNEL_AFDO_COMPRESSION_SUFFIX
+ verified_profile = os.path.join(self.outdir, profile_name)
+ self.assertEqual([verified_profile], ret)
+ mock_update.assert_called_once_with(self.kernel_version, self.kernel_name)
+ mock_ebuild.assert_called_once_with(
+ f'chromeos-kernel-{self.kernel_version}', 'AFDO_PROFILE_VERSION')
+ profile_path = os.path.join(
+ self.chroot.path, self.sysroot[1:], 'usr', 'lib', 'debug', 'boot',
+ f'chromeos-kernel-{self.kernel_version}-{profile_name}')
+ self.copy2.assert_called_once_with(profile_path, verified_profile)
+
def runToolchainBundleTest(self, artifact_path, tarball_name, input_files,
expected_output_files):
"""Asserts that the given artifact_path is tarred up properly.
@@ -796,6 +818,58 @@
)
+class UpdateKernelMetadataTest(PrepareBundleTest):
+ """Test _UpdateKernelMetadata() function in BundleArtifactHandler."""
+
+ def setUp(self):
+ # Prepare a JSON file containing metadata
+ toolchain_util.TOOLCHAIN_UTILS_PATH = self.tempdir
+ osutils.SafeMakedirs(os.path.join(self.tempdir, 'afdo_metadata'))
+ self.json_file = os.path.join(self.tempdir,
+ 'afdo_metadata/kernel_afdo.json')
+ self.kernel = '4_14'
+ self.kernel2 = '4_4'
+ self.afdo_sorted_by_freshness = [
+ 'R78-3865.0-1560000000.afdo', 'R78-3869.38-1562580965.afdo',
+ 'R78-3866.0-1570000000.afdo'
+ ]
+ self.afdo_versions = {
+ self.kernel: {
+ 'name': self.afdo_sorted_by_freshness[0],
+ },
+ self.kernel2: {
+ 'name': self.afdo_sorted_by_freshness[1],
+ },
+ }
+
+ with open(self.json_file, 'w') as f:
+ json.dump(self.afdo_versions, f)
+
+ def testUpdateKernelMetadataFailureWithInvalidKernel(self):
+ with self.assertRaises(AssertionError) as context:
+ toolchain_util.BundleArtifactHandler._UpdateKernelMetadata('3_8', None)
+ self.assertIn('the entry should be in', str(context.exception))
+
+ def testUpdateKernelMetadataFailureWithOlderProfile(self):
+ with self.assertRaises(AssertionError) as context:
+ toolchain_util.BundleArtifactHandler._UpdateKernelMetadata(
+ self.kernel2, self.afdo_sorted_by_freshness[0])
+ self.assertIn('is not newer than', str(context.exception))
+
+ def testUpdateKernelMetadataPass(self):
+ toolchain_util.BundleArtifactHandler._UpdateKernelMetadata(
+ self.kernel, self.afdo_sorted_by_freshness[2])
+ # Check changes in JSON file
+ new_afdo_versions = json.loads(osutils.ReadFile(self.json_file))
+ self.assertEqual(len(self.afdo_versions), len(new_afdo_versions))
+ self.assertEqual(new_afdo_versions[self.kernel]['name'],
+ self.afdo_sorted_by_freshness[2])
+ for k in self.afdo_versions:
+ # Make sure other fields are not changed
+ if k != self.kernel:
+ self.assertEqual(self.afdo_versions[k], new_afdo_versions[k])
+
+
class ReleaseChromeAFDOProfileTest(PrepareBundleTest):
"""Test functions related to create a release CrOS profile.
@@ -2303,8 +2377,8 @@
with open(self.json_file, 'w') as f:
json.dump(self.afdo_versions, f)
- self.PatchObject(git, 'GetTrackingBranch',
- return_value=git.RemoteRef('origin', 'main'))
+ self.PatchObject(
+ git, 'GetTrackingBranch', return_value=git.RemoteRef('origin', 'main'))
GitStatus = collections.namedtuple('GitStatus', ['output'])
self.mock_git = self.PatchObject(
git, 'RunGit', return_value=GitStatus(output='non-empty'))
@@ -2338,8 +2412,7 @@
self.afdo_sorted_by_freshness[1],
self.afdo_sorted_by_freshness[2])
calls = [
- mock.call(
- self.tempdir, ['pull', 'origin'], print_cmd=True),
+ mock.call(self.tempdir, ['pull', 'origin'], print_cmd=True),
mock.call(
self.tempdir, ['status', '--porcelain', '-uno'],
capture_output=True,
@@ -2348,10 +2421,7 @@
mock.call(
self.tempdir, ['commit', '-a', '-m', message], print_cmd=True),
mock.call(
- self.tempdir, [
- 'push', 'origin',
- 'HEAD:main%submit'
- ],
+ self.tempdir, ['push', 'origin', 'HEAD:main%submit'],
capture_output=True,
print_cmd=True)
]