Skip to content

Commit 3c20d49

Browse files
committed
add windows to CI testing and fix race condition
1 parent 25bf5ad commit 3c20d49

3 files changed

Lines changed: 46 additions & 18 deletions

File tree

‎.github/workflows/tests.yml‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@ jobs:
2020
os: [ubuntu-latest]
2121
python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15.0-rc.2", "pypy-3.11"]
2222
experimental: [false]
23-
# include:
24-
# - os: macos-latest
25-
# python: "3.10"
26-
# experimental: false
27-
# - os: windows-latest
28-
# python: "3.10"
29-
# experimental: false
23+
include:
24+
# Windows on the earliest and latest supported versions:
25+
- os: windows-latest
26+
python: "3.9"
27+
experimental: false
28+
- os: windows-latest
29+
python: "3.15.0-rc.2"
30+
experimental: false
3031
steps:
3132
- uses: actions/checkout@v7
3233
- name: Install libunwind
34+
if: runner.os == 'Linux'
3335
run: |
3436
sudo apt install -y libunwind-dev
3537
pkg-config libunwind --cflags --libs-only-l

‎src/vmprof_win.c‎

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
volatile int thread_started = 0;
1010
volatile int enabled = 0;
11+
/* Set by the sampler thread while it takes and writes one sample. */
12+
static volatile LONG sampler_busy = 0;
1113
#ifndef RPYTHON_VMPROF
1214
static PY_WIN_THREAD_STATE *target_tstate = NULL;
1315
#endif
@@ -16,11 +18,25 @@ HANDLE write_mutex;
1618

1719
int prepare_concurrent_bufs(void)
1820
{
19-
if (!(write_mutex = CreateMutex(NULL, FALSE, NULL)))
20-
return -1;
21+
if (write_mutex == NULL) {
22+
if (!(write_mutex = CreateMutex(NULL, FALSE, NULL)))
23+
return -1;
24+
}
2125
return 0;
2226
}
2327

28+
/* Wait for the sampler thread to finish the sample it may be in the middle
29+
of. Callers clear `enabled` first so that no new sample starts; without
30+
this wait a stale sample could be written into the next profile, ahead of
31+
its header. */
32+
static void wait_for_sampler(void)
33+
{
34+
MemoryBarrier();
35+
while (sampler_busy) {
36+
SwitchToThread();
37+
}
38+
}
39+
2440
int vmprof_register_virtual_function(char *code_name, intptr_t code_uid,
2541
int auto_retry)
2642
{
@@ -126,8 +142,9 @@ int vmprof_snapshot_thread(DWORD thread_id, PY_WIN_THREAD_STATE *tstate, prof_st
126142
#else
127143
frame = PyThreadState_GetFrame(tstate);
128144
#endif
145+
/* leave room for the thread id appended below */
129146
depth = vmp_walk_and_record_stack(frame, stack->stack,
130-
MAX_STACK_DEPTH, 0, 0);
147+
MAX_STACK_DEPTH - 1, 0, 0);
131148
#ifdef _MSC_VER
132149
} __except (EXCEPTION_EXECUTE_HANDLER) {
133150
depth = -1;
@@ -193,19 +210,25 @@ long __stdcall vmprof_mainloop(void *arg)
193210
// cpython version
194211
while (1) {
195212
Sleep(vmprof_get_profile_interval_usec() * 1000);
213+
sampler_busy = 1;
214+
MemoryBarrier();
196215
if (!enabled) {
216+
sampler_busy = 0;
197217
continue;
198218
}
199219
tstate = get_current_thread_state();
200220
if (!tstate)
201221
tstate = target_tstate;
202-
if (!tstate)
222+
if (!tstate) {
223+
sampler_busy = 0;
203224
continue;
225+
}
204226
depth = vmprof_snapshot_thread(tstate->thread_id, tstate, stack);
205227
if (depth > 0) {
206228
vmp_write_all((char*)stack + offsetof(prof_stacktrace_s, marker),
207229
SIZEOF_PROF_STACKTRACE + depth * sizeof(void*));
208230
}
231+
sampler_busy = 0;
209232
}
210233
#else
211234
// pypy version
@@ -255,10 +278,9 @@ int vmprof_enable(int memory, int native, int real_time)
255278
RPY_EXTERN
256279
int vmprof_disable(void)
257280
{
258-
char marker = MARKER_TRAILER;
259-
(void)vmp_write_time_now(MARKER_TRAILER);
260-
261281
enabled = 0;
282+
wait_for_sampler();
283+
(void)vmp_write_time_now(MARKER_TRAILER);
262284
#ifndef RPYTHON_VMPROF
263285
target_tstate = NULL;
264286
#endif
@@ -270,6 +292,9 @@ RPY_EXTERN
270292
void vmprof_ignore_signals(int ignored)
271293
{
272294
enabled = !ignored;
295+
if (ignored) {
296+
wait_for_sampler();
297+
}
273298
}
274299

275300
int vmp_native_enable(void)

‎vmprof/reader.py‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,16 @@ def detect_file_sizes(self):
114114
little = False
115115
self.setup_once(little_endian=little, word_size=4, addr_size=4)
116116
else:
117-
firstbytes = self.read(8)
118-
if firstbytes[0] == three:
117+
secondbytes = self.read(8)
118+
if secondbytes[0] == three:
119119
little = True
120120
self.setup_once(little_endian=little, word_size=8, addr_size=8)
121-
elif firstbytes[7] == three:
121+
elif secondbytes[7] == three:
122122
little = False
123123
self.setup_once(little_endian=little, word_size=8, addr_size=8)
124124
else:
125-
raise NotImplementedError("could not determine word and addr size")
125+
raise NotImplementedError("could not determine word and addr size, "
126+
"file starts with %r" % (firstbytes + secondbytes,))
126127

127128
# determine if it is windows 64 bit
128129
# even though it migt be a 64bit log, teh addr_size is now 4

0 commit comments

Comments
 (0)