Skip to content

Commit de872f5

Browse files
vmagrometa-codesync[bot]
authored andcommitted
[antlir] move debuginfo splitter to its own file
Summary: Moving the split file into its own file and not as part of the bzl file... make edit and using linter much easier, also the testing and debugging story is much better this way Test Plan: ``` ❯ buck2 test fbcode//antlir/antlir2/features/install/... fbcode//antlir/antlir2/features/extract/... Buck UI: https://www.internalfb.com/buck2/d6837da1-66d8-4051-b5fb-7d08cb435390 Tests finished: Pass 35. Fail 0. Fatal 0. Skip 0. Infra Failure 0. Build failure 0 ``` https://www.internalfb.com/intern/testinfra/testrun/14636698917489311 Reviewed By: naveedgol Differential Revision: D85864708 fbshipit-source-id: d8091c2406edbab1085f4f95088eeadf114d20cb
1 parent 0d2f489 commit de872f5

File tree

5 files changed

+144
-120
lines changed

5 files changed

+144
-120
lines changed

antlir/antlir2/bzl/debuginfo.bzl

Lines changed: 5 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -33,127 +33,9 @@ def _split_binary_impl(ctx: AnalysisContext) -> list[Provider]:
3333
# to write
3434
objcopy_tmp = ctx.actions.declare_output("objcopy_tmp")
3535

36-
split = ctx.actions.write("split.py", """#!/usr/bin/env python3
37-
import argparse
38-
import json
39-
import os
40-
import shutil
41-
import subprocess
42-
import sys
43-
from pathlib import Path
44-
45-
parser = argparse.ArgumentParser()
46-
parser.add_argument("--objcopy", required=True)
47-
parser.add_argument("--binary", required=True, type=Path)
48-
parser.add_argument("--binary-dwp", type=Path)
49-
parser.add_argument("--stripped", required=True, type=Path)
50-
parser.add_argument("--debuginfo", required=True, type=Path)
51-
parser.add_argument("--dwp", required=True, type=Path)
52-
parser.add_argument("--metadata", required=True, type=Path)
53-
parser.add_argument("--objcopy-tmp", required=True, type=Path)
54-
55-
args = parser.parse_args()
56-
57-
# ensure this exists or buck2 will get mad
58-
args.objcopy_tmp.touch()
59-
60-
if args.binary.is_dir():
61-
is_elf = False
62-
else:
63-
with open(args.binary, mode="rb") as src_f:
64-
first_4 = src_f.read(4)
65-
is_elf = first_4 == b"\x7fELF"
66-
67-
if args.binary_dwp:
68-
shutil.copy2(args.binary_dwp, args.dwp)
69-
else:
70-
with open(args.dwp, "w") as _f:
71-
pass
72-
73-
# If this is not an ELF binary, it can't be stripped so just copy the original
74-
if not is_elf:
75-
if args.binary.is_dir():
76-
shutil.copytree(args.binary, args.stripped, symlinks=True)
77-
else:
78-
shutil.copy2(args.binary, args.stripped)
79-
with open(args.debuginfo, "w") as _f:
80-
pass
81-
with open(args.metadata, "w") as f:
82-
json.dump({}, f)
83-
sys.exit(0)
84-
85-
# Save debug symbols to a separate debuginfo file
86-
proc = subprocess.run(
87-
[
88-
args.objcopy,
89-
"--only-keep-debug",
90-
args.binary,
91-
args.debuginfo,
92-
],
93-
capture_output=True,
94-
)
95-
if proc.returncode != 0:
96-
raise RuntimeError("Failed to extract debug symbols for {}:\\n{}\\n{}".format(
97-
args.binary,
98-
proc.stdout.decode("utf-8", errors = "surrogateescape"),
99-
proc.stderr.decode("utf-8", errors = "surrogateescape"),
100-
))
101-
102-
# Remove the debug symbols from the stripped binary
103-
proc = subprocess.run(
104-
[
105-
args.objcopy,
106-
"--strip-debug",
107-
"--remove-section=.pseudo_probe",
108-
"--remove-section=.pseudo_probe_desc",
109-
args.binary,
110-
args.stripped,
111-
],
112-
capture_output=True,
113-
)
114-
if proc.returncode != 0:
115-
raise RuntimeError("Failed to extract debug symbols for {}:\\n{}\\n{}".format(
116-
args.binary,
117-
proc.stdout.decode("utf-8", errors = "surrogateescape"),
118-
proc.stderr.decode("utf-8", errors = "surrogateescape"),
119-
))
120-
121-
# Find the BuildID of the binary. This determines where it should go for gdb to
122-
# look it up under /usr/lib/debug
123-
# https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
124-
buildid_proc = subprocess.run(
125-
[
126-
args.objcopy,
127-
"--dump-section",
128-
".note.gnu.build-id=/dev/stdout",
129-
args.binary,
130-
args.objcopy_tmp,
131-
],
132-
capture_output=True,
133-
)
134-
if buildid_proc.returncode != 0:
135-
raise RuntimeError("Failed to get build-id for {}:\\n{}\\n{}".format(
136-
args.binary,
137-
buildid_proc.stdout.decode("utf-8", errors = "surrogateescape"),
138-
buildid_proc.stderr.decode("utf-8", errors = "surrogateescape"),
139-
))
140-
buildid = buildid_proc.stdout
141-
142-
# Prefer to install the debug info by BuildID since it does not require another
143-
# objcopy invocation and is more standard
144-
with open(args.metadata, "w") as f:
145-
if buildid := buildid[len(buildid) - 20 :].hex():
146-
json.dump({"buildid": buildid}, f)
147-
else:
148-
# Can't setup debuglink here as we don't reliably know the location the binary
149-
# will end up being placed under, which debuglink relies on, so opt to no-op
150-
# here and linking will ultimately be handled in the install feature.
151-
json.dump({}, f)
152-
""", is_executable = True)
153-
15436
ctx.actions.run(
15537
cmd_args(
156-
split,
38+
ctx.attrs.debuginfo_splitter[RunInfo],
15739
cmd_args(objcopy, format = "--objcopy={}"),
15840
cmd_args(src, format = "--binary={}"),
15941
(cmd_args(src_dwp, format = "--binary-dwp={}") if src_dwp else []),
@@ -185,6 +67,7 @@ split_binary = anon_rule(
18567
impl = _split_binary_impl,
18668
attrs = {
18769
"cxx_toolchain": attrs.option(attrs.toolchain_dep(default = "toolchains//:cxx", providers = [CxxToolchainInfo]), default = None),
70+
"debuginfo_splitter": attrs.exec_dep(default = "antlir//antlir/antlir2/tools:debuginfo-splitter"),
18871
"objcopy": attrs.option(attrs.exec_dep(), default = None),
18972
"src": attrs.dep(),
19073
},
@@ -200,8 +83,10 @@ def split_binary_anon(
20083
*,
20184
ctx: AnalysisContext,
20285
src: Dependency,
203-
objcopy: Dependency) -> AnonTarget:
86+
objcopy: Dependency,
87+
debuginfo_splitter: Dependency) -> AnonTarget:
20488
return ctx.actions.anon_target(split_binary, {
89+
"debuginfo_splitter": debuginfo_splitter,
20590
"name": "debuginfo//" + src.label.package + ":" + src.label.name,
20691
"objcopy": objcopy,
20792
"src": src,

antlir/antlir2/features/extract/extract.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ def extract_buck_binary(
101101
},
102102
exec_deps = {
103103
"_analyze": "antlir//antlir/antlir2/features/extract:extract-buck-binary-analyze",
104+
"_debuginfo_splitter": "fbcode//antlir/antlir2/tools:debuginfo-splitter",
104105
"_objcopy": internal_external(
105106
fb = "fbsource//third-party/binutils:objcopy",
106107
oss = "toolchains//:objcopy",
@@ -141,6 +142,7 @@ def _extract_buck_binary_impl(ctx: AnalysisContext) -> list[Provider]:
141142
ctx = ctx,
142143
src = ctx.attrs.src,
143144
objcopy = ctx.attrs._objcopy,
145+
debuginfo_splitter = ctx.attrs._debuginfo_splitter,
144146
)
145147
src = split_anon_target.artifact("src")
146148
else:
@@ -189,6 +191,7 @@ extract_buck_binary_rule = rule(
189191
"strip": attrs.bool(default = True),
190192
"target_arch": attrs.string(),
191193
"_analyze": attrs.exec_dep(),
194+
"_debuginfo_splitter": attrs.option(attrs.exec_dep(), default = None),
192195
"_objcopy": attrs.option(attrs.exec_dep(), default = None),
193196
},
194197
)

antlir/antlir2/features/install/install.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def install(
102102
fail("always_use_gnu_debuglink requires split_debuginfo=True")
103103

104104
exec_deps = {
105+
"_debuginfo_splitter": "fbcode//antlir/antlir2/tools:debuginfo-splitter",
105106
"_mac_signer": "fbcode//python/runtime/tools:recursive_mac_signer",
106107
"_objcopy": internal_external(
107108
fb = "fbsource//third-party/binutils:objcopy",
@@ -384,6 +385,7 @@ def _impl(ctx: AnalysisContext) -> list[Provider] | Promise:
384385
ctx = ctx,
385386
src = src,
386387
objcopy = ctx.attrs._objcopy,
388+
debuginfo_splitter = ctx.attrs._debuginfo_splitter,
387389
)
388390
binary_info = binary_record(
389391
installed = installed_binary(
@@ -549,6 +551,7 @@ install_rule = rule(
549551
),
550552
"xattrs": attrs.dict(attrs.string(), attrs.string(), default = {}),
551553
"_binaries_require_repo": binaries_require_repo.optional_attr,
554+
"_debuginfo_splitter": attrs.option(attrs.exec_dep(), default = None),
552555
"_ensure_dir_exists_plugin": attrs.option(attrs.label(), default = None),
553556
"_mac_signer": attrs.option(attrs.exec_dep(), default = None),
554557
"_objcopy": attrs.option(attrs.exec_dep(), default = None),

antlir/antlir2/tools/BUCK

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
oncall("antlir")
2+
3+
python_bootstrap_binary(
4+
name = "debuginfo-splitter",
5+
main = "debuginfo-splitter.py",
6+
visibility = ["PUBLIC"],
7+
)
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) Meta Platforms, Inc. and affiliates.
3+
#
4+
# This source code is licensed under the MIT license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
import argparse
8+
import json
9+
import shutil
10+
import subprocess
11+
import sys
12+
from pathlib import Path
13+
14+
parser = argparse.ArgumentParser()
15+
parser.add_argument("--objcopy", required=True)
16+
parser.add_argument("--binary", required=True, type=Path)
17+
parser.add_argument("--binary-dwp", type=Path)
18+
parser.add_argument("--stripped", required=True, type=Path)
19+
parser.add_argument("--debuginfo", required=True, type=Path)
20+
parser.add_argument("--dwp", required=True, type=Path)
21+
parser.add_argument("--metadata", required=True, type=Path)
22+
parser.add_argument("--objcopy-tmp", required=True, type=Path)
23+
24+
args = parser.parse_args()
25+
26+
# ensure this exists or buck2 will get mad
27+
args.objcopy_tmp.touch()
28+
29+
if args.binary.is_dir():
30+
is_elf = False
31+
else:
32+
with open(args.binary, mode="rb") as src_f:
33+
first_4 = src_f.read(4)
34+
is_elf = first_4 == b"\x7fELF"
35+
36+
if args.binary_dwp:
37+
shutil.copy2(args.binary_dwp, args.dwp)
38+
else:
39+
with open(args.dwp, "w"):
40+
pass
41+
42+
# If this is not an ELF binary, it can't be stripped so just copy the original
43+
if not is_elf:
44+
if args.binary.is_dir():
45+
shutil.copytree(args.binary, args.stripped, symlinks=True)
46+
else:
47+
shutil.copy2(args.binary, args.stripped)
48+
with open(args.debuginfo, "w"):
49+
pass
50+
with open(args.metadata, "w") as f:
51+
json.dump({}, f)
52+
sys.exit(0)
53+
54+
# Save debug symbols to a separate debuginfo file
55+
proc = subprocess.run(
56+
[
57+
args.objcopy,
58+
"--only-keep-debug",
59+
args.binary,
60+
args.debuginfo,
61+
],
62+
capture_output=True,
63+
)
64+
if proc.returncode != 0:
65+
raise RuntimeError(
66+
"Failed to extract debug symbols for {}:\\n{}\\n{}".format(
67+
args.binary,
68+
proc.stdout.decode("utf-8", errors="surrogateescape"),
69+
proc.stderr.decode("utf-8", errors="surrogateescape"),
70+
)
71+
)
72+
73+
# Remove the debug symbols from the stripped binary
74+
proc = subprocess.run(
75+
[
76+
args.objcopy,
77+
"--strip-debug",
78+
"--remove-section=.pseudo_probe",
79+
"--remove-section=.pseudo_probe_desc",
80+
args.binary,
81+
args.stripped,
82+
],
83+
capture_output=True,
84+
)
85+
if proc.returncode != 0:
86+
raise RuntimeError(
87+
"Failed to extract debug symbols for {}:\\n{}\\n{}".format(
88+
args.binary,
89+
proc.stdout.decode("utf-8", errors="surrogateescape"),
90+
proc.stderr.decode("utf-8", errors="surrogateescape"),
91+
)
92+
)
93+
94+
# Find the BuildID of the binary. This determines where it should go for gdb to
95+
# look it up under /usr/lib/debug
96+
# https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
97+
buildid_proc = subprocess.run(
98+
[
99+
args.objcopy,
100+
"--dump-section",
101+
".note.gnu.build-id=/dev/stdout",
102+
args.binary,
103+
args.objcopy_tmp,
104+
],
105+
capture_output=True,
106+
)
107+
if buildid_proc.returncode != 0:
108+
raise RuntimeError(
109+
"Failed to get build-id for {}:\\n{}\\n{}".format(
110+
args.binary,
111+
buildid_proc.stdout.decode("utf-8", errors="surrogateescape"),
112+
buildid_proc.stderr.decode("utf-8", errors="surrogateescape"),
113+
)
114+
)
115+
buildid = buildid_proc.stdout
116+
117+
# Prefer to install the debug info by BuildID since it does not require another
118+
# objcopy invocation and is more standard
119+
with open(args.metadata, "w") as f:
120+
if buildid := buildid[len(buildid) - 20 :].hex():
121+
json.dump({"buildid": buildid}, f)
122+
else:
123+
# Can't setup debuglink here as we don't reliably know the location the binary
124+
# will end up being placed under, which debuglink relies on, so opt to no-op
125+
# here and linking will ultimately be handled in the install feature.
126+
json.dump({}, f)

0 commit comments

Comments
 (0)