Skip to content

Commit 5632278

Browse files
Implement FEATURES=packdebug
Create a tarball of debug information and source files for use with binary packages and debuginfod. The tarball is owned by the package, but we add it to UNINSTALL_IGNORE so it is kept around on upgrades etc., and we add it to PKG_INSTALL_MASK so that it is not part of binpkgs themselves (should be fetched via debuginfod). That is, for FEATURES=packdebug, we: * Create a tarball in /usr/lib/debug/.tarball/${CATEGORY}/${PN}/${PF}-${BUILD_ID}-debug.tar.xz with debug information from /usr/lib/debug/*, /usr/src/debug/*. This is installed but not part of the binpkg. * Set UNINSTALL_IGNORE="/usr/lib/debug/.tarball" to keep old debug information tarballs around to serve. * Set COLLISION_IGNORE="/usr/lib/debug/.tarball" to ignore collisions from such orphaned debug information tarballs left around by UNINSTALL_IGNORE. * Set PKG_INSTALL_MASK="/usr/lib/debug/* /usr/src/debug/*" so binpkgs do not contain debug information (they should fetch it via debuginfod instead). The model is as follows: * binhost Builds packages with -g* in *FLAGS and has FEATURES=splitdebug (and possibly FEATURES=installsources) to get debuginfo. Must also have build IDs enabled in the compiler. Uses FEATURES=packdebug to create tarballs containing that info. Runs `debuginfod ... -Z tar.xz \ /usr/lib/debug/.tarball \ -Z.gpkg.tar='(bsdtar -xf- -O '*/image.tar.*' | bsdtar --strip-components 1 -cf- @-)<' \ /var/cache/binpkgs` to serve the tarballs generated by packdebug, as well as binpkgs themselves for clients to request full binaries corresponding to coredumps etc. Users may want to set INSTALL_MASK in make.conf on the binhost side to avoid duplicate copies of debug information on the live filesystem (/usr/lib/debug, /usr/src/debug) and in the packdebug tarballs: ``` INSTALL_MASK="${INSTALL_MASK} /usr/lib/debug/ /usr/src/debug/ -/usr/lib/debug/.tarball" ``` (That is not done by default because one may wish to have non-debuginfod- aware software work, and it also feels like it's a bit surprising for us to do automatically.) * binpkg client Sets the `DEBUGINFOD_URLS` environment variable to the httpd that `debuginfod` runs. Thanks to Arsen for the original bashrc-based implementation which the estrip impl. is based upon, motivation for getting this done, and the idea. See also https://wiki.gentoo.org/wiki/User:Arsen/Deguginfod. This is also really useful for not just serving binpkgs to clients and letting them get debuginfo (of course the main usecase), but also getting debuginfo for local binaries you don't even publish anywhere after an upgrade when an old process is still running. For this part to work well, you need the binaries themselves too which the above `debuginfod` invocation covers. Note that xz is used for the debug tarball because it supports [0][1] random access. [0] https://blog.osandov.com/2024/07/25/making-debuginfod-viable-for-the-linux-kernel.html [1] https://developers.redhat.com/articles/2025/01/14/debuginfod-project-update-2024#addressing_kernel_vdso_extraction_bottlenecks Bug: https://sourceware.org/PR33693 Bug: https://bugs.gentoo.org/639376 Bug: https://bugs.gentoo.org/728818 Bug: https://bugs.gentoo.org/953869 Co-authored-by: Arsen Arsenović <[email protected]> Signed-off-by: Sam James <[email protected]>
1 parent 0c21808 commit 5632278

File tree

6 files changed

+109
-3
lines changed

6 files changed

+109
-3
lines changed

bin/misc-functions.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,41 @@ preinst_selinux_labels() {
502502
fi
503503
}
504504

505+
# Generate a separate tarball with debug information (and sources) for
506+
# use with debuginfod.
507+
__generate_packdebug() {
508+
local debugpath="${T}"/.tarball
509+
if ! [[ -d "${ED}"/usr/src/debug || -d "${ED}"/usr/lib/debug ]]; then
510+
return
511+
fi
512+
513+
install -d "${debugpath}"/"${CATEGORY}"{,/"${PN}"} \
514+
|| die "Failed to generate target debug directory"
515+
516+
(
517+
unset IFS
518+
local tarfile="${debugpath}/${CATEGORY}/${PN}/${PF}-${BUILD_ID}-debug.tar.xz"
519+
cd "${ED}" || die
520+
tar -cJf - > "${tarfile}" \
521+
$([[ -d ./usr/src/debug ]] && echo ./usr/src/debug) \
522+
$([[ -d ./usr/lib/debug ]] && echo ./usr/lib/debug) \
523+
|| die "Failed to pack up debug info for FEATURES=packdebug"
524+
)
525+
526+
# The package may not have any splitdebug info available, but still
527+
# have sources.
528+
mkdir -p \
529+
"${PORTAGE_TMPDIR}"/portage/${CATEGORY}/${PF}/image/${EPREFIX}/usr/lib/debug \
530+
|| die
531+
# We don't use ${D} here because __generate_packdebug is called from
532+
# __dyn_package where ${D} points to a pretend ${D}. We want these files
533+
# in the real image but not in the binpkg. Unfortunately, we can't
534+
# easily leverage PKG_INSTALL_MASK because of when it runs.
535+
mv "${debugpath}" \
536+
"${PORTAGE_TMPDIR}/portage/${CATEGORY}/${PF}/image/${EPREFIX}/usr/lib/debug/". \
537+
|| die
538+
}
539+
505540
__dyn_package() {
506541
if ! ___eapi_has_prefix_variables; then
507542
local EPREFIX=
@@ -524,6 +559,24 @@ __dyn_package() {
524559

525560
if [[ ! -z "${BUILD_ID}" ]]; then
526561
echo -n "${BUILD_ID}" > "${PORTAGE_BUILDDIR}"/build-info/BUILD_ID
562+
563+
# We generate the packdebug tarball at this point as we need
564+
# the BUILD_ID, but it's not installed as part of the binpkg
565+
# by design. We install it later when merging.
566+
if contains_word packdebug "${FEATURES}" ; then
567+
__generate_packdebug
568+
569+
# The injected PKG_INSTALL_MASK combined with us
570+
# having splitdebug/installsources on may mean we
571+
# have an empty /usr/src or /usr/lib left. Prune those.
572+
#
573+
# XXX: We use ${D}/${EPREFIX} here because we don't set
574+
# ${ED} to the fake ${D}.
575+
find \
576+
"${D}/${EPREFIX}/usr/lib" \
577+
"${D}/${EPREFIX}/usr/src" \
578+
-type d -empty -delete 2>/dev/null
579+
fi
527580
fi
528581

529582
if [[ "${BINPKG_FORMAT}" == "xpak" ]]; then

lib/_emerge/EbuildPhase.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ class EbuildPhase(CompositeTask):
128128
"network-sandbox",
129129
"network-sandbox-proxy",
130130
"nostrip",
131+
"packdebug",
131132
"preserve-libs",
132133
"sandbox",
133134
"selinux",

lib/_emerge/PackagePhase.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ class PackagePhase(CompositeTask):
3232
_shell_binary = portage.const.BASH_BINARY
3333

3434
def _start(self):
35+
packdebug_maskstr = ""
36+
if "packdebug" in self.settings.features:
37+
# We don't want to include debug information in binpkgs themselves
38+
# w/ packdebug as binpkg consumers should fetch them via debuginfod.
39+
packdebug_maskstr += " /usr/src/debug/ /usr/lib/debug/"
40+
3541
try:
3642
with open(
3743
_unicode_encode(
@@ -46,9 +52,10 @@ def _start(self):
4652
encoding=_encodings["repo.content"],
4753
errors="replace",
4854
) as f:
49-
self._pkg_install_mask = InstallMask(f.read())
55+
self._pkg_install_mask = InstallMask(f.read() + packdebug_maskstr)
5056
except OSError:
51-
self._pkg_install_mask = None
57+
self._pkg_install_mask = InstallMask(packdebug_maskstr)
58+
5259
if self._pkg_install_mask:
5360
self._proot = os.path.join(self.settings["T"], "packaging")
5461
self._start_task(

lib/portage/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@
212212
"noman",
213213
"nostrip",
214214
"notitles",
215+
"packdebug",
215216
"parallel-fetch",
216217
"parallel-install",
217218
"pid-sandbox",

lib/portage/dbapi/vartree.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2750,7 +2750,14 @@ def _unmerge_pkgfiles(self, pkgfiles, others_in_slot):
27502750
# process symlinks second-to-last, directories last.
27512751
mydirs = set()
27522752

2753-
uninstall_ignore = shlex.split(self.settings.get("UNINSTALL_IGNORE", ""))
2753+
uninstall_ignore = self.settings.get("UNINSTALL_IGNORE", "")
2754+
if "packdebug" in self.settings.features:
2755+
# For packdebug, we don't want to clean up old debuginfo
2756+
# tarballs that we made, as we want to keep serving them
2757+
# to binhost clients for some time.
2758+
uninstall_ignore += " /usr/lib/debug/.tarball/*"
2759+
2760+
uninstall_ignore = shlex.split(uninstall_ignore)
27542761

27552762
def unlink(file_name, lstatobj):
27562763
if bsd_chflags:
@@ -3940,6 +3947,15 @@ def _collision_protect(self, srcroot, destroot, mypkglist, file_list, symlink_li
39403947
x += "/*"
39413948
collision_ignore.append(x)
39423949

3950+
# packdebug tarballs are orphaned for management outside of Portage,
3951+
# so it's expected that we may collide with an orphaned file. Ignore it.
3952+
if "packdebug" in self.settings.features:
3953+
x = "/usr/lib/debug/.tarball"
3954+
if os.path.isdir(os.path.join(self._eroot, x.lstrip(os.sep))):
3955+
x = normalize_path(x)
3956+
x += "/*"
3957+
collision_ignore.append(x)
3958+
39433959
# For collisions with preserved libraries, the current package
39443960
# will assume ownership and the libraries will be unregistered.
39453961
if self.vartree.dbapi._plib_registry is None:

man/make.conf.5

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,34 @@ Prevents the stripping of binaries that are merged to the live filesystem.
666666
.B notitles
667667
Disables xterm titlebar updates (which contains status info).
668668
.TP
669+
.B packdebug
670+
Create a tarball of debug information and source files for use with
671+
debuginfod. Debug tarballs are placed at
672+
\fI/usr/lib/debug/.tarball/${CATEGORY}/${PF}-${BUILD_ID}.tar.xz\fR.
673+
The debug tarball is installed but is not included in binary packages.
674+
675+
\fIUNINSTALL_IGNORE\fR is automatically modified to leave old debug
676+
tarballs around to allow debuginfod to serve them after upgrades. See
677+
\fBsplitdebug\fR for general split debug information (upon which this
678+
feature depends). Similarly, \fICOLLISION_IGNORE\fR is automatically
679+
modified to ignore collisions on these files.
680+
681+
Setting \fBpackdebug\fR temporarily on the command-line or per\-package is
682+
discouraged because the new (unset) value will take effect when the package
683+
is removed, meaning the automatic adjustments made to accommodate
684+
\fBpackdebug\fR cannot apply.
685+
686+
Users may wish to avoid double storage of both the live debug information
687+
in \fI/usr/lib/debug\fR and \fI/usr/src/debug\fR as well as inside of
688+
the \fIpackdebug\fR tarballs at \fI/usr/lib/debug/.tarball\fR. This can
689+
be done by setting \fIINSTALL_MASK\fR:
690+
691+
.nf
692+
# Debug information is inside of packdebug tarballs already, don't install
693+
# twice.
694+
INSTALL_MASK="${INSTALL_MASK} /usr/lib/debug/ /usr/src/debug/ -/usr/lib/debug/.tarball"
695+
.fi
696+
.TP
669697
.B parallel\-fetch
670698
Fetch in the background while compiling. Run
671699
`tail \-f /var/log/emerge\-fetch.log` in a

0 commit comments

Comments
 (0)