Skip to content

Commit a7e3bbb

Browse files
fix(profiling): stash heap-gotter cdylib out of wheel across auditwheel repair
1 parent 71f235f commit a7e3bbb

1 file changed

Lines changed: 63 additions & 7 deletions

File tree

.gitlab/scripts/build-wheel-helpers.sh

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,69 @@ PY
151151
# Repair wheel (ONLY PLATFORM-SPECIFIC CODE)
152152
section_start "repair_wheel" "Repairing wheel"
153153
if [[ "$(uname -s)" == "Linux" ]]; then
154-
# The opt-in heap-gotter cdylib (DD_PROFILING_NATIVE_HEAP_BUILD=1) is a
155-
# statically-linked Rust artifact with non-standard ELF versioning sections
156-
# that trip auditwheel's iter_versions parser. Exclude it from repair —
157-
# same rationale as skipping it in extract_debug_symbols above.
158-
auditwheel repair -w "${TMP_WHEEL_DIR}" \
159-
--exclude 'libdd_heap_gotter*.so' \
160-
"${BUILT_WHEEL_FILE}"
154+
# The opt-in heap-gotter cdylib (DD_PROFILING_NATIVE_HEAP_BUILD=1) has
155+
# non-standard ELF versioning sections that trip auditwheel's iter_versions
156+
# parser. --exclude does not help: it only drops a SONAME from dependency
157+
# grafting, while repair still parses every ELF listed in the wheel's RECORD.
158+
# So the cdylib has to leave the wheel entirely and be reinserted after.
159+
GOTTER_STASH_DIR="${WORK_DIR}/heap_gotter_stash"
160+
GOTTER_PATTERN='*libdd_heap_gotter*.so'
161+
if unzip -l "${BUILT_WHEEL_FILE}" | grep -q 'libdd_heap_gotter.*\.so$'; then
162+
mkdir -p "${GOTTER_STASH_DIR}"
163+
unzip -q "${BUILT_WHEEL_FILE}" "${GOTTER_PATTERN}" -d "${GOTTER_STASH_DIR}"
164+
uv run --no-project scripts/zip_filter.py "${BUILT_WHEEL_FILE}" "${GOTTER_PATTERN}"
165+
fi
166+
167+
auditwheel repair -w "${TMP_WHEEL_DIR}" "${BUILT_WHEEL_FILE}"
168+
169+
if [[ -d "${GOTTER_STASH_DIR}" ]]; then
170+
REPAIRED_WHEEL_FILE=$(ls "${TMP_WHEEL_DIR}"/*.whl | head -n 1)
171+
GOTTER_STASH_DIR="${GOTTER_STASH_DIR}" REPAIRED_WHEEL_FILE="${REPAIRED_WHEEL_FILE}" \
172+
uv run --no-project python - <<'PY'
173+
import base64
174+
import csv
175+
import hashlib
176+
import io
177+
import os
178+
import zipfile
179+
from pathlib import Path
180+
181+
wheel = Path(os.environ["REPAIRED_WHEEL_FILE"])
182+
stash = Path(os.environ["GOTTER_STASH_DIR"])
183+
184+
additions = {str(p.relative_to(stash)): p for p in sorted(stash.rglob("*")) if p.is_file()}
185+
if not additions:
186+
print("No stashed heap-gotter cdylib to reinsert")
187+
raise SystemExit(0)
188+
189+
tmp_wheel = Path(f"{wheel}.tmp")
190+
with (
191+
zipfile.ZipFile(wheel, "r") as source_zip,
192+
zipfile.ZipFile(tmp_wheel, "w", zipfile.ZIP_DEFLATED) as temp_zip,
193+
):
194+
record = next((f for f in source_zip.infolist() if f.filename.endswith(".dist-info/RECORD")), None)
195+
if record is None:
196+
raise SystemExit(f"no RECORD found in {wheel}")
197+
# DEV: Use ZipInfo objects to ensure original file attributes are preserved
198+
for file in source_zip.infolist():
199+
if file.filename == record.filename or file.filename in additions:
200+
continue
201+
temp_zip.writestr(file, source_zip.read(file.filename))
202+
rows = [r for r in csv.reader(io.StringIO(source_zip.read(record.filename).decode("utf-8"))) if r]
203+
rows = [r for r in rows if r[0] != record.filename and r[0] not in additions]
204+
for arcname, path in additions.items():
205+
data = path.read_bytes()
206+
temp_zip.writestr(arcname, data)
207+
digest = base64.urlsafe_b64encode(hashlib.sha256(data).digest()).rstrip(b"=").decode("ascii")
208+
rows.append([arcname, f"sha256={digest}", str(len(data))])
209+
print(f"Reinserted heap-gotter cdylib: {arcname}")
210+
rows.append([record.filename, "", ""])
211+
output = io.StringIO()
212+
csv.writer(output, lineterminator="\n").writerows(rows)
213+
temp_zip.writestr(record, output.getvalue())
214+
os.replace(tmp_wheel, wheel)
215+
PY
216+
fi
161217
else
162218
# macOS
163219
MACOSX_DEPLOYMENT_TARGET=14.7 uvx --from="delocate" delocate-wheel \

0 commit comments

Comments
 (0)