Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@ jobs:
os: [ubuntu-latest]
python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15.0-rc.2", "pypy-3.11"]
experimental: [false]
# include:
# - os: macos-latest
# python: "3.10"
# experimental: false
# - os: windows-latest
# python: "3.10"
# experimental: false
include:
# Windows on the earliest and latest supported versions:
- os: windows-latest
python: "3.9"
experimental: false
- os: windows-latest
python: "3.15.0-rc.2"
experimental: false
steps:
- uses: actions/checkout@v7
- name: Install libunwind
if: runner.os == 'Linux'
run: |
sudo apt install -y libunwind-dev
pkg-config libunwind --cflags --libs-only-l
Expand Down
1 change: 0 additions & 1 deletion dev_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
six
requests
backports.shutil_which
pytz
colorama
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ requires-python = ">=3.9,<3.16"
dependencies = [
"requests",
"six",
"pytz",
"colorama",
]
classifiers = [
Expand Down
39 changes: 32 additions & 7 deletions src/vmprof_win.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

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

int prepare_concurrent_bufs(void)
{
if (!(write_mutex = CreateMutex(NULL, FALSE, NULL)))
return -1;
if (write_mutex == NULL) {
if (!(write_mutex = CreateMutex(NULL, FALSE, NULL)))
return -1;
}
return 0;
}

/* Wait for the sampler thread to finish the sample it may be in the middle
of. Callers clear `enabled` first so that no new sample starts; without
this wait a stale sample could be written into the next profile, ahead of
its header. */
static void wait_for_sampler(void)
{
MemoryBarrier();
while (sampler_busy) {
SwitchToThread();
}
}

int vmprof_register_virtual_function(char *code_name, intptr_t code_uid,
int auto_retry)
{
Expand Down Expand Up @@ -126,8 +142,9 @@ int vmprof_snapshot_thread(DWORD thread_id, PY_WIN_THREAD_STATE *tstate, prof_st
#else
frame = PyThreadState_GetFrame(tstate);
#endif
/* leave room for the thread id appended below */
depth = vmp_walk_and_record_stack(frame, stack->stack,
MAX_STACK_DEPTH, 0, 0);
MAX_STACK_DEPTH - 1, 0, 0);
#ifdef _MSC_VER
} __except (EXCEPTION_EXECUTE_HANDLER) {
depth = -1;
Expand Down Expand Up @@ -193,19 +210,25 @@ long __stdcall vmprof_mainloop(void *arg)
// cpython version
while (1) {
Sleep(vmprof_get_profile_interval_usec() * 1000);
sampler_busy = 1;
MemoryBarrier();
if (!enabled) {
sampler_busy = 0;
continue;
}
tstate = get_current_thread_state();
if (!tstate)
tstate = target_tstate;
if (!tstate)
if (!tstate) {
sampler_busy = 0;
continue;
}
depth = vmprof_snapshot_thread(tstate->thread_id, tstate, stack);
if (depth > 0) {
vmp_write_all((char*)stack + offsetof(prof_stacktrace_s, marker),
SIZEOF_PROF_STACKTRACE + depth * sizeof(void*));
}
sampler_busy = 0;
}
#else
// pypy version
Expand Down Expand Up @@ -255,10 +278,9 @@ int vmprof_enable(int memory, int native, int real_time)
RPY_EXTERN
int vmprof_disable(void)
{
char marker = MARKER_TRAILER;
(void)vmp_write_time_now(MARKER_TRAILER);

enabled = 0;
wait_for_sampler();
(void)vmp_write_time_now(MARKER_TRAILER);
#ifndef RPYTHON_VMPROF
target_tstate = NULL;
#endif
Expand All @@ -270,6 +292,9 @@ RPY_EXTERN
void vmprof_ignore_signals(int ignored)
{
enabled = !ignored;
if (ignored) {
wait_for_sampler();
}
}

int vmp_native_enable(void)
Expand Down
1 change: 0 additions & 1 deletion test_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ setuptools>=77
pytest
hypothesis
colorama
pytz
attrs
py
11 changes: 6 additions & 5 deletions vmprof/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,16 @@ def detect_file_sizes(self):
little = False
self.setup_once(little_endian=little, word_size=4, addr_size=4)
else:
firstbytes = self.read(8)
if firstbytes[0] == three:
secondbytes = self.read(8)
if secondbytes[0] == three:
little = True
self.setup_once(little_endian=little, word_size=8, addr_size=8)
elif firstbytes[7] == three:
elif secondbytes[7] == three:
little = False
self.setup_once(little_endian=little, word_size=8, addr_size=8)
else:
raise NotImplementedError("could not determine word and addr size")
raise NotImplementedError("could not determine word and addr size, "
"file starts with %r" % (firstbytes + secondbytes,))

# determine if it is windows 64 bit
# even though it migt be a 64bit log, teh addr_size is now 4
Expand Down Expand Up @@ -243,7 +244,7 @@ def read_timeval(self):

def read_timezone(self):
timezone = self.read(8).strip(b'\x00')
# we should use pytz and parse iso8601 if we really support time zones
# we should use zoneinfo and parse iso8601 if we really support time zones
return None

def read_all(self):
Expand Down
1 change: 0 additions & 1 deletion vmprof/test/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import time
import gzip
import time
import pytz
import vmprof
import six
from cffi import FFI
Expand Down
7 changes: 5 additions & 2 deletions vmshare/binary.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys
import struct
import array
import pytz
import zoneinfo

WORD_SIZE = struct.calcsize('L')
if sys.maxsize == 2**63-1:
Expand Down Expand Up @@ -82,7 +82,10 @@ def read_timezone(fileobj):
timezone = fileobj.read(8).strip(b'\x00')
timezone = timezone.decode('ascii')
if timezone:
return pytz.timezone(timezone)
try:
return zoneinfo.ZoneInfo(timezone)
except (zoneinfo.ZoneInfoNotFoundError, ValueError):
return None
return None

def encode_le_u16(value):
Expand Down
Loading