diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d889fa128e261..c43ec75fba3619 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -706,6 +706,7 @@ jobs: uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe with: allowed-failures: >- + build-ios, build-windows-msi, build-ubuntu-ssltests-awslc, build-ubuntu-ssltests-openssl, diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index f6fa52b36c5ab3..fed795b1e8c963 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -232,6 +232,10 @@ The :c:member:`~PyTypeObject.tp_traverse` handler must have the following type: object argument. If *visit* returns a non-zero value that value should be returned immediately. + The traversal function must not have any side effects. Implementations + may not modify the reference counts of any Python objects nor create or + destroy any Python objects. + To simplify writing :c:member:`~PyTypeObject.tp_traverse` handlers, a :c:func:`Py_VISIT` macro is provided. In order to use this macro, the :c:member:`~PyTypeObject.tp_traverse` implementation must name its arguments exactly *visit* and *arg*: diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 49fe02d919df8b..efac86078f9af5 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1569,6 +1569,11 @@ and :c:data:`PyType_Type` effectively act as defaults.) but the instance has no strong reference to the elements inside it, as they are allowed to be removed even if the instance is still alive). + .. warning:: + The traversal function must not have any side effects. It must not + modify the reference counts of any Python objects nor create or destroy + any Python objects. + Note that :c:func:`Py_VISIT` requires the *visit* and *arg* parameters to :c:func:`!local_traverse` to have these specific names; don't name them just anything. diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 1bd82545e588fa..0c892e63393ad1 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1236,6 +1236,12 @@ Build changes modules that are missing or packaged separately. (Contributed by Stan Ulbrych and Petr Viktorin in :gh:`139707`.) +* Annotating anonymous mmap usage is now supported if Linux kernel supports + :manpage:`PR_SET_VMA_ANON_NAME ` (Linux 5.17 or newer). + Annotations are visible in ``/proc//maps`` if the kernel supports the feature + and :option:`-X dev <-X>` is passed to the Python or Python is built in :ref:`debug mode `. + (Contributed by Donghee Na in :gh:`141770`) + Porting to Python 3.15 ====================== diff --git a/Include/internal/pycore_mmap.h b/Include/internal/pycore_mmap.h new file mode 100644 index 00000000000000..214fd4362a55fe --- /dev/null +++ b/Include/internal/pycore_mmap.h @@ -0,0 +1,45 @@ +#ifndef Py_INTERNAL_MMAP_H +#define Py_INTERNAL_MMAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "pycore_pystate.h" + +#if defined(HAVE_PR_SET_VMA_ANON_NAME) && defined(__linux__) +# include +# include +#endif + +#if defined(HAVE_PR_SET_VMA_ANON_NAME) && defined(__linux__) +static inline void +_PyAnnotateMemoryMap(void *addr, size_t size, const char *name) +{ +#ifndef Py_DEBUG + if (!_Py_GetConfig()->dev_mode) { + return; + } +#endif + assert(strlen(name) < 80); + int old_errno = errno; + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)addr, size, name); + /* Ignore errno from prctl */ + /* See: https://bugzilla.redhat.com/show_bug.cgi?id=2302746 */ + errno = old_errno; +} +#else +static inline void +_PyAnnotateMemoryMap(void *Py_UNUSED(addr), size_t Py_UNUSED(size), const char *Py_UNUSED(name)) +{ +} +#endif + +#ifdef __cplusplus +} +#endif +#endif // !Py_INTERNAL_MMAP_H diff --git a/Include/internal/pycore_stackref.h b/Include/internal/pycore_stackref.h index e59611c07fa793..c86beebe6554c3 100644 --- a/Include/internal/pycore_stackref.h +++ b/Include/internal/pycore_stackref.h @@ -479,13 +479,6 @@ PyStackRef_AsPyObjectBorrow(_PyStackRef stackref) #define PyStackRef_IsDeferred(ref) (((ref).bits & Py_TAG_BITS) == Py_TAG_DEFERRED) -static inline PyObject * -PyStackRef_NotDeferred_AsPyObject(_PyStackRef stackref) -{ - assert(!PyStackRef_IsDeferred(stackref)); - return (PyObject *)stackref.bits; -} - static inline PyObject * PyStackRef_AsPyObjectSteal(_PyStackRef stackref) { diff --git a/InternalDocs/README.md b/InternalDocs/README.md index a6e2df5ae4a9c3..06f67b3cfc124a 100644 --- a/InternalDocs/README.md +++ b/InternalDocs/README.md @@ -36,6 +36,8 @@ Program Execution - [The Bytecode Interpreter](interpreter.md) +- [Stack references (_PyStackRef)](stackrefs.md) + - [The JIT](jit.md) - [Garbage Collector Design](garbage_collector.md) diff --git a/InternalDocs/stackrefs.md b/InternalDocs/stackrefs.md new file mode 100644 index 00000000000000..2d8810262d45f7 --- /dev/null +++ b/InternalDocs/stackrefs.md @@ -0,0 +1,80 @@ +# Stack references (`_PyStackRef`) + +Stack references are the interpreter's tagged representation of values on the evaluation stack. +They carry metadata to track ownership and support optimizations such as tagged small ints. + +## Shape and tagging + +- A `_PyStackRef` is a tagged pointer-sized value (see `Include/internal/pycore_stackref.h`). +- Tag bits distinguish three cases: + - `Py_TAG_REFCNT` unset - reference count lives on the pointed-to object. + - `Py_TAG_REFCNT` set - ownership is "borrowed" (no refcount to drop on close) or the object is immortal. + - `Py_INT_TAG` set - tagged small integer stored directly in the stackref (no heap allocation). +- Special constants: `PyStackRef_NULL`, `PyStackRef_ERROR`, and embedded `None`/`True`/`False`. + +In GIL builds, most objects carry their refcount; tagged borrowed refs skip decref on close. In free +threading builds, the tag is also used to mark deferred refcounted objects so the GC can see them and +to avoid refcount contention on commonly shared objects. + +## Converting to and from PyObject* + +Three conversions control ownership: + +- `PyStackRef_FromPyObjectNew(obj)` - create a new reference (INCREF if mortal). +- `PyStackRef_FromPyObjectSteal(obj)` - take over ownership without changing the count unless the + object is immortal. +- `PyStackRef_FromPyObjectBorrow(obj)` - create a borrowed stackref (never decref on close). + +The `obj` argument must not be `NULL`. + +Going back to `PyObject*` mirrors this: + +- `PyStackRef_AsPyObjectBorrow(ref)` - borrow the underlying pointer +- `PyStackRef_AsPyObjectSteal(ref)` - transfer ownership from the stackref; if ref is borrowed or + deferred, this creates a new owning `PyObject*` reference. +- `PyStackRef_AsPyObjectNew(ref)` - create a new owning reference + +Only `PyStackRef_AsPyObjectBorrow` allows ref to be `PyStackRef_NULL`. + +## Operations on stackrefs + +The interpreter treats `_PyStackRef` as the unit of stack storage. Ownership must be managed with +the stackref primitives: + +- `PyStackRef_DUP` - like `Py_NewRef` for stackrefs; preserves the original. +- `PyStackRef_Borrow` - create a borrowed stackref from another stackref. +- `PyStackRef_CLOSE` / `PyStackRef_XCLOSE` - like `Py_DECREF`; invalidates the stackref. +- `PyStackRef_CLEAR` - like `Py_CLEAR`; closes and sets the stackref to `PyStackRef_NULL` +- `PyStackRef_MakeHeapSafe` - converts borrowed reference to owning reference + +Borrow tracking (for debug builds with `Py_STACKREF_DEBUG`) records who you borrowed from and reports +double-close, leaked borrows, or use-after-close via fatal errors. + +## Borrow-friendly opcodes + +The interpreter can push borrowed references directly. For example, `LOAD_FAST_BORROW` loads a local +variable as a borrowed `_PyStackRef`, avoiding both INCREF and DECREF for the temporary lifetime on +the evaluation stack. + +## Tagged integers on the stack + +Small ints can be stored inline with `Py_INT_TAG`, so no heap object is involved. Helpers like +`PyStackRef_TagInt`, `PyStackRef_UntagInt`, and `PyStackRef_IncrementTaggedIntNoOverflow` operate on +these values. Type checks use `PyStackRef_IsTaggedInt` and `PyStackRef_LongCheck`. + +## Free threading considerations + +With `Py_GIL_DISABLED`, `Py_TAG_DEFERRED` is an alias for `Py_TAG_REFCNT`. +Objects that support deferred reference counting can be pushed to the evaluation +stack and stored in local variables without directly incrementing the reference +count because they are only freed during cyclic garbage collection. This avoids +reference count contention on commonly shared objects such as methods and types. The GC +scans each thread's locals and evaluation stack to keep objects that use +deferred reference counting alive. + +## Debugging support + +`Py_STACKREF_DEBUG` builds replace the inline tags with table-backed IDs so the runtime can track +creation sites, borrows, closes, and leaks. Enabling `Py_STACKREF_CLOSE_DEBUG` additionally records +double closes. The tables live on `PyInterpreterState` and are initialized in `pystate.c`; helper +routines reside in `Python/stackrefs.c`. diff --git a/Lib/_py_warnings.py b/Lib/_py_warnings.py index 67c74fdd2d0b42..d5a9cec86f3674 100644 --- a/Lib/_py_warnings.py +++ b/Lib/_py_warnings.py @@ -563,7 +563,6 @@ def warn_explicit(message, category, filename, lineno, else: text = message message = category(message) - modules = None key = (text, category, lineno) with _wm._lock: if registry is None: diff --git a/Lib/_threading_local.py b/Lib/_threading_local.py index 0b9e5d3bbf6ef6..2af3885458b54f 100644 --- a/Lib/_threading_local.py +++ b/Lib/_threading_local.py @@ -57,7 +57,7 @@ def thread_deleted(_, idt=idt): # as soon as the OS-level thread ends instead. local = wrlocal() if local is not None: - dct = local.dicts.pop(idt) + local.dicts.pop(idt) wrlocal = ref(self, local_deleted) wrthread = ref(thread, thread_deleted) thread.__dict__[key] = wrlocal diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 8cbb71f708537f..6619c87bcf5b93 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -949,7 +949,7 @@ async def sock_sendfile(self, sock, file, offset=0, count=None, try: return await self._sock_sendfile_native(sock, file, offset, count) - except exceptions.SendfileNotAvailableError as exc: + except exceptions.SendfileNotAvailableError: if not fallback: raise return await self._sock_sendfile_fallback(sock, file, @@ -1270,7 +1270,7 @@ async def sendfile(self, transport, file, offset=0, count=None, try: return await self._sendfile_native(transport, file, offset, count) - except exceptions.SendfileNotAvailableError as exc: + except exceptions.SendfileNotAvailableError: if not fallback: raise diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index f404273c3ae5c1..3fa93b14a6787f 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -733,7 +733,7 @@ async def sock_accept(self, sock): async def _sock_sendfile_native(self, sock, file, offset, count): try: fileno = file.fileno() - except (AttributeError, io.UnsupportedOperation) as err: + except (AttributeError, io.UnsupportedOperation): raise exceptions.SendfileNotAvailableError("not a regular file") try: fsize = os.fstat(fileno).st_size diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 1d463ea09ba5b8..f9b8a4ee56c5c1 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -244,7 +244,7 @@ def _get_awaited_by_tasks(pid: int) -> list: e = e.__context__ print(f"Error retrieving tasks: {e}") sys.exit(1) - except PermissionError as e: + except PermissionError: exit_with_permission_help_text() diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 1c1458127db5ac..49e8067ee7b4e5 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -359,7 +359,7 @@ async def _sock_sendfile_native(self, sock, file, offset, count): "os.sendfile() is not available") try: fileno = file.fileno() - except (AttributeError, io.UnsupportedOperation) as err: + except (AttributeError, io.UnsupportedOperation): raise exceptions.SendfileNotAvailableError("not a regular file") try: fsize = os.fstat(fileno).st_size diff --git a/Lib/codeop.py b/Lib/codeop.py index 8cac00442d99e3..40e88423119bc4 100644 --- a/Lib/codeop.py +++ b/Lib/codeop.py @@ -66,9 +66,9 @@ def _maybe_compile(compiler, source, filename, symbol, flags): try: compiler(source + "\n", filename, symbol, flags=flags) return None - except _IncompleteInputError as e: + except _IncompleteInputError: return None - except SyntaxError as e: + except SyntaxError: pass # fallthrough diff --git a/Lib/compileall.py b/Lib/compileall.py index 67fe370451e1ef..9519a5ac16f024 100644 --- a/Lib/compileall.py +++ b/Lib/compileall.py @@ -223,7 +223,7 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, cfile = importlib.util.cache_from_source(fullname) opt_cfiles[opt_level] = cfile - head, tail = name[:-3], name[-3:] + tail = name[-3:] if tail == '.py': if not force: try: diff --git a/Lib/concurrent/interpreters/_queues.py b/Lib/concurrent/interpreters/_queues.py index b5cc0b8944940d..ee159d7de63827 100644 --- a/Lib/concurrent/interpreters/_queues.py +++ b/Lib/concurrent/interpreters/_queues.py @@ -223,7 +223,7 @@ def put(self, obj, block=True, timeout=None, *, while True: try: _queues.put(self._id, obj, unboundop) - except QueueFull as exc: + except QueueFull: if timeout is not None and time.time() >= end: raise # re-raise time.sleep(_delay) @@ -258,7 +258,7 @@ def get(self, block=True, timeout=None, *, while True: try: obj, unboundop = _queues.get(self._id) - except QueueEmpty as exc: + except QueueEmpty: if timeout is not None and time.time() >= end: raise # re-raise time.sleep(_delay) @@ -277,7 +277,7 @@ def get_nowait(self): """ try: obj, unboundop = _queues.get(self._id) - except QueueEmpty as exc: + except QueueEmpty: raise # re-raise if unboundop is not None: assert obj is None, repr(obj) diff --git a/Lib/dis.py b/Lib/dis.py index d6d2c1386dd785..8c257d118fb23b 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -530,7 +530,6 @@ def print_instruction_line(self, instr, mark_as_current): fields.append(instr.opname.ljust(_OPNAME_WIDTH)) # Column: Opcode argument if instr.arg is not None: - arg = repr(instr.arg) # If opname is longer than _OPNAME_WIDTH, we allow it to overflow into # the space reserved for oparg. This results in fewer misaligned opargs # in the disassembly output. diff --git a/Lib/encodings/uu_codec.py b/Lib/encodings/uu_codec.py index 4e58c62fe9ef0f..4f8704016e2131 100644 --- a/Lib/encodings/uu_codec.py +++ b/Lib/encodings/uu_codec.py @@ -56,7 +56,7 @@ def uu_decode(input, errors='strict'): break try: data = binascii.a2b_uu(s) - except binascii.Error as v: + except binascii.Error: # Workaround for broken uuencoders by /Fredrik Lundh nbytes = (((s[0]-32) & 63) * 4 + 5) // 3 data = binascii.a2b_uu(s[:nbytes]) diff --git a/Lib/ftplib.py b/Lib/ftplib.py index 50771e8c17c250..640acc64f620cc 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -314,9 +314,9 @@ def makeport(self): port = sock.getsockname()[1] # Get proper port host = self.sock.getsockname()[0] # Get proper host if self.af == socket.AF_INET: - resp = self.sendport(host, port) + self.sendport(host, port) else: - resp = self.sendeprt(host, port) + self.sendeprt(host, port) if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT: sock.settimeout(self.timeout) return sock @@ -455,7 +455,7 @@ def retrlines(self, cmd, callback = None): """ if callback is None: callback = print_line - resp = self.sendcmd('TYPE A') + self.sendcmd('TYPE A') with self.transfercmd(cmd) as conn, \ conn.makefile('r', encoding=self.encoding) as fp: while 1: @@ -951,7 +951,7 @@ def test(): elif file[:2] == '-d': cmd = 'CWD' if file[2:]: cmd = cmd + ' ' + file[2:] - resp = ftp.sendcmd(cmd) + ftp.sendcmd(cmd) elif file == '-p': ftp.set_pasv(not ftp.passiveserver) else: diff --git a/Lib/functools.py b/Lib/functools.py index 8063eb5ffc3304..836eb680ccd4d4 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -687,7 +687,7 @@ def wrapper(*args, **kwds): # still adjusting the links. root = oldroot[NEXT] oldkey = root[KEY] - oldresult = root[RESULT] + oldresult = root[RESULT] # noqa: F841 root[KEY] = root[RESULT] = None # Now update the cache dictionary. diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 1b7c2af1a923d7..b80c8e56c92810 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -498,7 +498,6 @@ def restart_subprocess(self, with_cwd=False, filename=''): self.rpcclt.close() self.terminate_subprocess() console = self.tkconsole - was_executing = console.executing console.executing = False self.spawn_subprocess() try: diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index 23f0f4cb5027ec..0f719a06883ad7 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -129,8 +129,8 @@ def __init__(self, parent, title, contents, modal=True, wrap=WORD, self.title(title) self.viewframe = ViewFrame(self, contents, wrap=wrap) self.protocol("WM_DELETE_WINDOW", self.ok) - self.button_ok = button_ok = Button(self, text='Close', - command=self.ok, takefocus=False) + self.button_ok = Button(self, text='Close', + command=self.ok, takefocus=False) self.viewframe.pack(side='top', expand=True, fill='both') self.is_modal = modal diff --git a/Lib/imaplib.py b/Lib/imaplib.py index c176736548188c..22a0afcd981519 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -808,7 +808,7 @@ def proxyauth(self, user): """ name = 'PROXYAUTH' - return self._simple_command('PROXYAUTH', user) + return self._simple_command(name, user) def rename(self, oldmailbox, newmailbox): @@ -1310,7 +1310,7 @@ def _get_tagged_response(self, tag, expect_bye=False): try: self._get_response() - except self.abort as val: + except self.abort: if __debug__: if self.debug >= 1: self.print_log() @@ -1867,7 +1867,7 @@ def Time2Internaldate(date_time): try: optlist, args = getopt.getopt(sys.argv[1:], 'd:s:') - except getopt.error as val: + except getopt.error: optlist, args = (), () stream_command = None diff --git a/Lib/inspect.py b/Lib/inspect.py index 8e7511b3af015f..ff462750888c88 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2140,7 +2140,6 @@ def _signature_strip_non_python_syntax(signature): current_parameter = 0 OP = token.OP - ERRORTOKEN = token.ERRORTOKEN # token stream always starts with ENCODING token, skip it t = next(token_stream) diff --git a/Lib/modulefinder.py b/Lib/modulefinder.py index b115d99ab30ff1..7fb19a5c5d1805 100644 --- a/Lib/modulefinder.py +++ b/Lib/modulefinder.py @@ -400,7 +400,6 @@ def scan_opcodes(self, co): yield "relative_import", (level, fromlist, name) def scan_code(self, co, m): - code = co.co_code scanner = self.scan_opcodes for what, args in scanner(co): if what == "store": diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py index fc00d2861260a8..64ec53884aeb5d 100644 --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -709,8 +709,7 @@ def accept(self): # written data and then disconnected -- see Issue 14725. else: try: - res = _winapi.WaitForMultipleObjects( - [ov.event], False, INFINITE) + _winapi.WaitForMultipleObjects([ov.event], False, INFINITE) except: ov.cancel() _winapi.CloseHandle(handle) diff --git a/Lib/netrc.py b/Lib/netrc.py index 2f502c1d53364f..750b5071e3c65f 100644 --- a/Lib/netrc.py +++ b/Lib/netrc.py @@ -95,7 +95,7 @@ def _parse(self, file, fp, default_netrc): while 1: # Look for a machine, default, or macdef top-level keyword saved_lineno = lexer.lineno - toplevel = tt = lexer.get_token() + tt = lexer.get_token() if not tt: break elif tt[0] == '#': diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 77d2bf86a5f09d..7d637325240f1c 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -726,7 +726,7 @@ def realpath(path, /, *, strict=False): try: if _getfinalpathname(spath) == path: path = spath - except ValueError as ex: + except ValueError: # Unexpected, as an invalid path should not have gained a prefix # at any point, but we ignore this error just in case. pass diff --git a/Lib/optparse.py b/Lib/optparse.py index 02ff7140882ed6..5ff7f74754f9c1 100644 --- a/Lib/optparse.py +++ b/Lib/optparse.py @@ -1372,7 +1372,7 @@ def parse_args(self, args=None, values=None): self.values = values try: - stop = self._process_args(largs, rargs, values) + self._process_args(largs, rargs, values) except (BadOptionError, OptionValueError) as err: self.error(str(err)) diff --git a/Lib/pickle.py b/Lib/pickle.py index f3025776623d2c..71c12c50f7f035 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -1169,7 +1169,6 @@ def save_frozenset(self, obj): def save_global(self, obj, name=None): write = self.write - memo = self.memo if name is None: name = getattr(obj, '__qualname__', None) @@ -1756,7 +1755,7 @@ def load_binget(self): i = self.read(1)[0] try: self.append(self.memo[i]) - except KeyError as exc: + except KeyError: msg = f'Memo value not found at index {i}' raise UnpicklingError(msg) from None dispatch[BINGET[0]] = load_binget @@ -1765,7 +1764,7 @@ def load_long_binget(self): i, = unpack(' // offsetof() @@ -1951,6 +1952,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) PyErr_SetFromErrno(PyExc_OSError); return NULL; } + _PyAnnotateMemoryMap(m_obj->data, map_size, "cpython:mmap"); m_obj->access = (access_mode)access; return (PyObject *)m_obj; } diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 2b95ebbf8e5ac0..b1f9fa2e692265 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -2,6 +2,7 @@ #include "Python.h" #include "pycore_interp.h" // _PyInterpreterState_HasFeature +#include "pycore_mmap.h" // _PyAnnotateMemoryMap() #include "pycore_object.h" // _PyDebugAllocatorStats() definition #include "pycore_obmalloc.h" #include "pycore_obmalloc_init.h" @@ -467,6 +468,7 @@ _PyMem_ArenaAlloc(void *Py_UNUSED(ctx), size_t size) if (ptr == MAP_FAILED) return NULL; assert(ptr != NULL); + _PyAnnotateMemoryMap(ptr, size, "cpython:pymalloc"); return ptr; #else return malloc(size); diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 85363949c2344f..dcfb75ce162b2f 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -277,6 +277,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 17999690990fb9..247f4b5a784f9c 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -735,6 +735,9 @@ Include\internal + + Include\internal + Include\internal diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 27c0d382281bdb..313982ed28a5dc 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -6,7 +6,7 @@ Quick Start Guide 1a. Optionally install Python 3.10 or later. If not installed, get_externals.bat (via build.bat) will download and use Python via NuGet. -2. Run "build.bat" to build Python in 32-bit Release configuration. +2. Run "build.bat" to build Python in 64-bit Release configuration. 3. (Optional, but recommended) Run the test suite with "rt.bat -q". diff --git a/Python/jit.c b/Python/jit.c index 47d3d7a5d27180..7106db8a99a77a 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -16,6 +16,7 @@ #include "pycore_intrinsics.h" #include "pycore_list.h" #include "pycore_long.h" +#include "pycore_mmap.h" #include "pycore_opcode_metadata.h" #include "pycore_opcode_utils.h" #include "pycore_optimizer.h" @@ -75,6 +76,9 @@ jit_alloc(size_t size) int prot = PROT_READ | PROT_WRITE; unsigned char *memory = mmap(NULL, size, prot, flags, -1, 0); int failed = memory == MAP_FAILED; + if (!failed) { + _PyAnnotateMemoryMap(memory, size, "cpython:jit"); + } #endif if (failed) { jit_error("unable to allocate memory"); diff --git a/Python/perf_jit_trampoline.c b/Python/perf_jit_trampoline.c index 8732be973616d4..af7d8f9f1ec0ae 100644 --- a/Python/perf_jit_trampoline.c +++ b/Python/perf_jit_trampoline.c @@ -61,6 +61,7 @@ #include "pycore_ceval.h" // _PyPerf_Callbacks #include "pycore_frame.h" #include "pycore_interp.h" +#include "pycore_mmap.h" // _PyAnnotateMemoryMap() #include "pycore_runtime.h" // _PyRuntime #ifdef PY_HAVE_PERF_TRAMPOLINE @@ -1085,6 +1086,7 @@ static void* perf_map_jit_init(void) { close(fd); return NULL; // Memory mapping failed } + _PyAnnotateMemoryMap(perf_jit_map_state.mapped_buffer, page_size, "cpython:perf_jit_trampoline"); #endif perf_jit_map_state.mapped_size = page_size; diff --git a/Python/perf_trampoline.c b/Python/perf_trampoline.c index 987e8d2a11a659..669a47ae17377a 100644 --- a/Python/perf_trampoline.c +++ b/Python/perf_trampoline.c @@ -132,6 +132,7 @@ any DWARF information available for them). #include "Python.h" #include "pycore_ceval.h" // _PyPerf_Callbacks #include "pycore_interpframe.h" // _PyFrame_GetCode() +#include "pycore_mmap.h" // _PyAnnotateMemoryMap() #include "pycore_runtime.h" // _PyRuntime @@ -290,6 +291,7 @@ new_code_arena(void) perf_status = PERF_STATUS_FAILED; return -1; } + _PyAnnotateMemoryMap(memory, mem_size, "cpython:perf_trampoline"); void *start = &_Py_trampoline_func_start; void *end = &_Py_trampoline_func_end; size_t code_size = end - start; diff --git a/configure b/configure index 7561fb9c7ad90e..4f9b9b21ca395e 100755 --- a/configure +++ b/configure @@ -23947,6 +23947,25 @@ printf "%s\n" "#define HAVE_UT_NAMESIZE 1" >>confdefs.h fi +ac_fn_check_decl "$LINENO" "PR_SET_VMA_ANON_NAME" "ac_cv_have_decl_PR_SET_VMA_ANON_NAME" "#include + #include +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl_PR_SET_VMA_ANON_NAME" = xyes +then : + ac_have_decl=1 +else case e in #( + e) ac_have_decl=0 ;; +esac +fi +printf "%s\n" "#define HAVE_DECL_PR_SET_VMA_ANON_NAME $ac_have_decl" >>confdefs.h +if test $ac_have_decl = 1 +then : + +printf "%s\n" "#define HAVE_PR_SET_VMA_ANON_NAME 1" >>confdefs.h + +fi + + # check for openpty, login_tty, and forkpty diff --git a/configure.ac b/configure.ac index fa24bc78a2645a..046c046ffb5d10 100644 --- a/configure.ac +++ b/configure.ac @@ -5583,6 +5583,13 @@ AC_CHECK_DECLS([UT_NAMESIZE], [], [@%:@include ]) +AC_CHECK_DECLS([PR_SET_VMA_ANON_NAME], + [AC_DEFINE([HAVE_PR_SET_VMA_ANON_NAME], [1], + [Define if you have the 'PR_SET_VMA_ANON_NAME' constant.])], + [], + [@%:@include + @%:@include ]) + # check for openpty, login_tty, and forkpty AC_CHECK_FUNCS([openpty], [], diff --git a/pyconfig.h.in b/pyconfig.h.in index 8a9f5ca8ec826d..aabf9f0be8da55 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -228,6 +228,10 @@ /* Define to 1 if you have the header file. */ #undef HAVE_DB_H +/* Define to 1 if you have the declaration of 'PR_SET_VMA_ANON_NAME', and to 0 + if you don't. */ +#undef HAVE_DECL_PR_SET_VMA_ANON_NAME + /* Define to 1 if you have the declaration of 'RTLD_DEEPBIND', and to 0 if you don't. */ #undef HAVE_DECL_RTLD_DEEPBIND @@ -996,6 +1000,9 @@ /* Define if your compiler supports function prototype */ #undef HAVE_PROTOTYPES +/* Define if you have the 'PR_SET_VMA_ANON_NAME' constant. */ +#undef HAVE_PR_SET_VMA_ANON_NAME + /* Define to 1 if you have the 'pthread_condattr_setclock' function. */ #undef HAVE_PTHREAD_CONDATTR_SETCLOCK