Skip to content

Commit ad69f92

Browse files
authored
Test Windows Wheels and Fix symbol address resolution on Windows (#543)
This adds CI for testing out windows wheels against various versions of python. This also applies a fix to use RVA for symbol address resolution on windows (as identified by @akhramov in #672)
1 parent 969c4df commit ad69f92

File tree

5 files changed

+72
-14
lines changed

5 files changed

+72
-14
lines changed

.github/workflows/build.yml

+29-1
Original file line numberDiff line numberDiff line change
@@ -215,27 +215,55 @@ jobs:
215215
3.13.0,
216216
]
217217
# TODO: also test windows
218-
os: [ubuntu-20.04, macos-13]
218+
os: [ubuntu-20.04, macos-13, windows-latest]
219219
# some versions of python can't be tested on GHA with osx because of SIP:
220220
exclude:
221+
- os: windows-latest
222+
python-version: 3.6.15
223+
- os: windows-latest
224+
python-version: 3.7.17
225+
- os: windows-latest
226+
python-version: 3.8.18
227+
- os: windows-latest
228+
python-version: 3.9.20
229+
- os: windows-latest
230+
python-version: 3.10.15
221231
- os: macos-13
222232
python-version: 3.11.10
233+
- os: windows-latest
234+
python-version: 3.11.10
223235
- os: macos-13
224236
python-version: 3.12.0
237+
- os: windows-latest
238+
python-version: 3.12.0
225239
- os: macos-13
226240
python-version: 3.12.1
241+
- os: windows-latest
242+
python-version: 3.12.1
227243
- os: macos-13
228244
python-version: 3.12.2
245+
- os: windows-latest
246+
python-version: 3.12.2
229247
- os: macos-13
230248
python-version: 3.12.3
249+
- os: windows-latest
250+
python-version: 3.12.3
231251
- os: macos-13
232252
python-version: 3.12.4
253+
- os: windows-latest
254+
python-version: 3.12.4
233255
- os: macos-13
234256
python-version: 3.12.5
257+
- os: windows-latest
258+
python-version: 3.12.5
235259
- os: macos-13
236260
python-version: 3.12.6
261+
- os: windows-latest
262+
python-version: 3.12.6
237263
- os: macos-13
238264
python-version: 3.12.7
265+
- os: windows-latest
266+
python-version: 3.12.7
239267

240268
steps:
241269
- uses: actions/checkout@v2

ci/update_python_test_versions.py

+18-4
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ def parse_version(v):
1414

1515
def get_github_python_versions():
1616
versions_json = requests.get(_VERSIONS_URL).json()
17-
raw_versions = [v["version"] for v in versions_json]
1817

18+
# windows platform support isn't great for older versions of python
19+
# get a map of version: platform/arch so we can exclude here
20+
platforms = {}
21+
for v in versions_json:
22+
platforms[v["version"]] = set((f["platform"], f["arch"]) for f in v["files"])
23+
24+
raw_versions = [v["version"] for v in versions_json]
1925
minor_versions = defaultdict(list)
2026

2127
for version_str in raw_versions:
@@ -46,11 +52,13 @@ def get_github_python_versions():
4652

4753
versions.extend(f"{major}.{minor}.{patch}" for patch in patches)
4854

49-
return versions
55+
return versions, platforms
5056

5157

5258
def update_python_test_versions():
53-
versions = sorted(get_github_python_versions(), key=parse_version)
59+
versions, platforms = get_github_python_versions()
60+
versions = sorted(versions, key=parse_version)
61+
5462
build_yml_path = (
5563
pathlib.Path(__file__).parent.parent / ".github" / "workflows" / "build.yml"
5664
)
@@ -82,9 +90,15 @@ def update_python_test_versions():
8290
# since it currently fails in GHA on SIP errors
8391
exclusions = []
8492
for v in versions:
85-
if v.startswith("3.11.10") or v.startswith("3.12"):
93+
# if we don't have a python version for osx/windows skip
94+
if ("darwin", "x64") not in platforms[v] or v.startswith("3.12"):
8695
exclusions.append(" - os: macos-13\n")
8796
exclusions.append(f" python-version: {v}\n")
97+
98+
if ("win32", "x64") not in platforms[v] or v.startswith("3.12"):
99+
exclusions.append(" - os: windows-latest\n")
100+
exclusions.append(f" python-version: {v}\n")
101+
88102
first_exclude_line = lines.index(" exclude:\n", first_line)
89103
last_exclude_line = lines.index("\n", first_exclude_line)
90104
lines = lines[: first_exclude_line + 1] + exclusions + lines[last_exclude_line:]

src/binary_parser.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,8 @@ pub fn parse_binary(filename: &Path, addr: u64, size: u64) -> Result<BinaryInfo,
201201
Object::PE(pe) => {
202202
for export in pe.exports {
203203
if let Some(name) = export.name {
204-
if let Some(export_offset) = export.offset {
205-
if let Some(addr) = offset.checked_add(export_offset as u64) {
206-
symbols.insert(name.to_string(), addr);
207-
}
204+
if let Some(addr) = offset.checked_add(export.rva as u64) {
205+
symbols.insert(name.to_string(), addr);
208206
}
209207
}
210208
}

src/python_process_info.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,14 @@ impl PythonProcessInfo {
7575
let map = maps.iter().find(|m| {
7676
if let Some(pathname) = m.filename() {
7777
if let Some(pathname) = pathname.to_str() {
78-
return is_python_bin(pathname) && m.is_exec();
78+
#[cfg(not(windows))]
79+
{
80+
return is_python_bin(pathname) && m.is_exec();
81+
}
82+
#[cfg(windows)]
83+
{
84+
return is_python_bin(pathname);
85+
}
7986
}
8087
}
8188
false
@@ -139,7 +146,14 @@ impl PythonProcessInfo {
139146
let libmap = maps.iter().find(|m| {
140147
if let Some(pathname) = m.filename() {
141148
if let Some(pathname) = pathname.to_str() {
142-
return is_python_lib(pathname) && m.is_exec();
149+
#[cfg(not(windows))]
150+
{
151+
return is_python_lib(pathname) && m.is_exec();
152+
}
153+
#[cfg(windows)]
154+
{
155+
return is_python_lib(pathname);
156+
}
143157
}
144158
}
145159
false

tests/integration_test.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,25 @@ def _sample_process(self, script_name, options=None, include_profile_name=False)
3131
# record option, and setting different flags. To get the profile output
3232
# we're using the speedscope format (since we can read that in as json)
3333
with tempfile.NamedTemporaryFile() as profile_file:
34+
filename = profile_file.name
35+
if sys.platform.startswith("win"):
36+
filename = "profile.json"
37+
3438
cmdline = [
3539
PYSPY,
3640
"record",
3741
"-o",
38-
profile_file.name,
42+
filename,
3943
"--format",
4044
"speedscope",
4145
"-d",
4246
"2",
4347
]
4448
cmdline.extend(options or [])
4549
cmdline.extend(["--", sys.executable, script_name])
46-
env = dict(os.environ, RUST_LOG="debug")
50+
env = dict(os.environ, RUST_LOG="info")
4751
subprocess.check_output(cmdline, env=env)
48-
with open(profile_file.name) as f:
52+
with open(filename) as f:
4953
profiles = json.load(f)
5054

5155
frames = profiles["shared"]["frames"]

0 commit comments

Comments
 (0)