Skip to content

Commit 028cc24

Browse files
authored
Merge branch 'main' into fix-142252
2 parents 41d272c + 9609574 commit 028cc24

File tree

18 files changed

+204
-56
lines changed

18 files changed

+204
-56
lines changed

Doc/library/profiling.sampling.rst

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -878,9 +878,9 @@ interesting functions that highlights:
878878

879879
Use :option:`--no-summary` to suppress both the legend and summary sections.
880880

881-
To save pstats output to a file instead of stdout::
881+
To save pstats output to a binary file instead of stdout::
882882

883-
python -m profiling.sampling run -o profile.txt script.py
883+
python -m profiling.sampling run -o profile.pstats script.py
884884

885885
The pstats format supports several options for controlling the display.
886886
The :option:`--sort` option determines the column used for ordering results::
@@ -1455,7 +1455,9 @@ Output options
14551455

14561456
.. option:: --pstats
14571457

1458-
Generate text statistics output. This is the default.
1458+
Generate pstats statistics. This is the default.
1459+
When written to stdout, the output is a text table; with :option:`-o`,
1460+
it is a binary pstats file.
14591461

14601462
.. option:: --collapsed
14611463

@@ -1486,8 +1488,9 @@ Output options
14861488
.. option:: -o <path>, --output <path>
14871489

14881490
Output file or directory path. Default behavior varies by format:
1489-
:option:`--pstats` writes to stdout, while other formats generate a file
1490-
named ``<format>_<PID>.<ext>`` (for example, ``flamegraph_12345.html``).
1491+
:option:`--pstats` prints a text table to stdout, while ``-o`` writes a
1492+
binary pstats file. Other formats generate a file named
1493+
``<format>_<PID>.<ext>`` (for example, ``flamegraph_12345.html``).
14911494
:option:`--heatmap` creates a directory named ``heatmap_<PID>``.
14921495

14931496
.. option:: --browser

Include/internal/pycore_backoff.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ extern "C" {
1212
#include <assert.h>
1313
#include <stdbool.h>
1414
#include "pycore_structs.h" // _Py_BackoffCounter
15+
#include "pycore_tstate.h" // _PyPolicy
1516

1617
/* 16-bit countdown counters using exponential backoff.
1718
@@ -127,10 +128,11 @@ trigger_backoff_counter(void)
127128
#define JUMP_BACKWARD_INITIAL_VALUE 4000
128129
#define JUMP_BACKWARD_INITIAL_BACKOFF 6
129130
static inline _Py_BackoffCounter
130-
initial_jump_backoff_counter(void)
131+
initial_jump_backoff_counter(_PyPolicy *policy)
131132
{
132-
return make_backoff_counter(JUMP_BACKWARD_INITIAL_VALUE,
133-
JUMP_BACKWARD_INITIAL_BACKOFF);
133+
return make_backoff_counter(
134+
policy->interp.jump_backward_initial_value,
135+
policy->interp.jump_backward_initial_backoff);
134136
}
135137

136138
/* Initial exit temperature.
@@ -141,10 +143,11 @@ initial_jump_backoff_counter(void)
141143
#define SIDE_EXIT_INITIAL_BACKOFF 6
142144

143145
static inline _Py_BackoffCounter
144-
initial_temperature_backoff_counter(void)
146+
initial_temperature_backoff_counter(_PyPolicy *policy)
145147
{
146-
return make_backoff_counter(SIDE_EXIT_INITIAL_VALUE,
147-
SIDE_EXIT_INITIAL_BACKOFF);
148+
return make_backoff_counter(
149+
policy->jit.side_exit_initial_value,
150+
policy->jit.side_exit_initial_backoff);
148151
}
149152

150153
/* Unreachable backoff counter. */

Include/internal/pycore_tstate.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,24 @@ typedef struct _PyJitTracerState {
5252
_PyJitTracerInitialState initial_state;
5353
_PyJitTracerPreviousState prev_state;
5454
} _PyJitTracerState;
55+
5556
#endif
5657

58+
typedef struct _PyJitPolicy {
59+
uint16_t side_exit_initial_value;
60+
uint16_t side_exit_initial_backoff;
61+
} _PyJitPolicy;
62+
63+
typedef struct _PyInterpreterPolicy {
64+
uint16_t jump_backward_initial_value;
65+
uint16_t jump_backward_initial_backoff;
66+
} _PyInterpreterPolicy;
67+
68+
typedef struct _PyPolicy {
69+
_PyJitPolicy jit;
70+
_PyInterpreterPolicy interp;
71+
} _PyPolicy;
72+
5773
// Every PyThreadState is actually allocated as a _PyThreadStateImpl. The
5874
// PyThreadState fields are exposed as part of the C API, although most fields
5975
// are intended to be private. The _PyThreadStateImpl fields not exposed.
@@ -132,6 +148,7 @@ typedef struct _PyThreadStateImpl {
132148
#if _Py_TIER2
133149
_PyJitTracerState jit_tracer_state;
134150
#endif
151+
_PyPolicy policy;
135152
} _PyThreadStateImpl;
136153

137154
#ifdef __cplusplus

Lib/profiling/sampling/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,8 @@ def _add_format_options(parser, include_compression=True, include_binary=True):
489489
"-o",
490490
"--output",
491491
dest="outfile",
492-
help="Output path (default: stdout for pstats, auto-generated for others). "
493-
"For heatmap: directory name (default: heatmap_PID)",
492+
help="Output path (default: stdout for pstats text; with -o, pstats is binary). "
493+
"Auto-generated for other formats. For heatmap: directory name (default: heatmap_PID)",
494494
)
495495
output_group.add_argument(
496496
"--browser",

Lib/test/pickletester.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4261,6 +4261,35 @@ def check_array(arr):
42614261
# 2-D, non-contiguous
42624262
check_array(arr[::2])
42634263

4264+
def test_concurrent_mutation_in_buffer_with_bytearray(self):
4265+
def factory():
4266+
s = b"a" * 16
4267+
return bytearray(s), s
4268+
self.do_test_concurrent_mutation_in_buffer_callback(factory)
4269+
4270+
def test_concurrent_mutation_in_buffer_with_memoryview(self):
4271+
def factory():
4272+
obj = memoryview(b"a" * 32)[10:26]
4273+
sub = b"a" * len(obj)
4274+
return obj, sub
4275+
self.do_test_concurrent_mutation_in_buffer_callback(factory)
4276+
4277+
def do_test_concurrent_mutation_in_buffer_callback(self, factory):
4278+
# See: https://github.com/python/cpython/issues/143308.
4279+
class R:
4280+
def __bool__(self):
4281+
buf.release()
4282+
return True
4283+
4284+
for proto in range(5, pickle.HIGHEST_PROTOCOL + 1):
4285+
obj, sub = factory()
4286+
buf = pickle.PickleBuffer(obj)
4287+
buffer_callback = lambda _: R()
4288+
4289+
with self.subTest(proto=proto, obj=obj, sub=sub):
4290+
res = self.dumps(buf, proto, buffer_callback=buffer_callback)
4291+
self.assertIn(sub, res)
4292+
42644293
def test_evil_class_mutating_dict(self):
42654294
# https://github.com/python/cpython/issues/92930
42664295
from random import getrandbits

Lib/test/test_os/test_os.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2624,6 +2624,40 @@ def test_execve_invalid_env(self):
26242624
with self.assertRaises(ValueError):
26252625
os.execve(args[0], args, newenv)
26262626

2627+
# See https://github.com/python/cpython/issues/137934 and the other
2628+
# related issues for the reason why we cannot test this on Windows.
2629+
@unittest.skipIf(os.name == "nt", "POSIX-specific test")
2630+
def test_execve_env_concurrent_mutation_with_fspath_posix(self):
2631+
# Prevent crash when mutating environment during parsing.
2632+
# Regression test for https://github.com/python/cpython/issues/143309.
2633+
2634+
message = "hello from execve"
2635+
code = """
2636+
import os, sys
2637+
2638+
class MyPath:
2639+
def __fspath__(self):
2640+
mutated.clear()
2641+
return b"pwn"
2642+
2643+
mutated = KEYS = VALUES = [MyPath()]
2644+
2645+
class MyEnv:
2646+
def __getitem__(self): raise RuntimeError("must not be called")
2647+
def __len__(self): return 1
2648+
def keys(self): return KEYS
2649+
def values(self): return VALUES
2650+
2651+
args = [sys.executable, '-c', "print({message!r})"]
2652+
os.execve(args[0], args, MyEnv())
2653+
""".format(message=message)
2654+
2655+
# Use '__cleanenv' to signal to assert_python_ok() not
2656+
# to do a copy of os.environ on its own.
2657+
rc, out, _ = assert_python_ok('-c', code, __cleanenv=True)
2658+
self.assertEqual(rc, 0)
2659+
self.assertIn(bytes(message, "ascii"), out)
2660+
26272661
@unittest.skipUnless(sys.platform == "win32", "Win32-specific test")
26282662
def test_execve_with_empty_path(self):
26292663
# bpo-32890: Check GetLastError() misuse

Lib/test/test_threading.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ def f(mutex):
323323

324324
# PyThreadState_SetAsyncExc() is a CPython-only gimmick, not (currently)
325325
# exposed at the Python level. This test relies on ctypes to get at it.
326+
@cpython_only
326327
def test_PyThreadState_SetAsyncExc(self):
327328
ctypes = import_module("ctypes")
328329

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Factor out tracing and optimization heuristics into a single object.
2+
Patch by Donghee Na.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`pickle`: fix use-after-free crashes when a :class:`~pickle.PickleBuffer`
2+
is concurrently mutated by a custom buffer callback during pickling.
3+
Patch by Bénédikt Tran and Aaron Wieczorek.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash in :func:`os.execve` on non-Windows platforms when
2+
given a custom environment mapping which is then mutated during
3+
parsing. Patch by Bénédikt Tran.

0 commit comments

Comments
 (0)