From 86d904588e8c84c7fccb8faf84b343f03461970d Mon Sep 17 00:00:00 2001 From: kaushal trivedi <155625932+Kaushalt2004@users.noreply.github.com> Date: Thu, 25 Dec 2025 14:43:39 +0530 Subject: [PATCH 1/8] gh-143004: Fix possible use-after-free in collections.Counter.update() (GH-143044) This happened when the Counter was mutated when incrementing the value for an existing key. --- Lib/test/test_collections.py | 13 +++++++++++++ ...5-12-22-00-00-00.gh-issue-143004.uaf-counter.rst | 2 ++ Modules/_collectionsmodule.c | 5 +++++ 3 files changed, 20 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-12-22-00-00-00.gh-issue-143004.uaf-counter.rst diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 22595239252814..fad639b20a1801 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -2135,6 +2135,19 @@ def test_basics(self): self.assertEqual(c.setdefault('e', 5), 5) self.assertEqual(c['e'], 5) + def test_update_reentrant_add_clears_counter(self): + c = Counter() + key = object() + + class Evil(int): + def __add__(self, other): + c.clear() + return NotImplemented + + c[key] = Evil() + c.update([key]) + self.assertEqual(c[key], 1) + def test_init(self): self.assertEqual(list(Counter(self=42).items()), [('self', 42)]) self.assertEqual(list(Counter(iterable=42).items()), [('iterable', 42)]) diff --git a/Misc/NEWS.d/next/Library/2025-12-22-00-00-00.gh-issue-143004.uaf-counter.rst b/Misc/NEWS.d/next/Library/2025-12-22-00-00-00.gh-issue-143004.uaf-counter.rst new file mode 100644 index 00000000000000..278066e9b706bf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-22-00-00-00.gh-issue-143004.uaf-counter.rst @@ -0,0 +1,2 @@ +Fix a potential use-after-free in :meth:`collections.Counter.update` when user code +mutates the Counter during an update. diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 3b14a21fa8428e..45ca63e6d7c77f 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -2577,7 +2577,12 @@ _collections__count_elements_impl(PyObject *module, PyObject *mapping, if (_PyDict_SetItem_KnownHash(mapping, key, one, hash) < 0) goto done; } else { + /* oldval is a borrowed reference. Keep it alive across + PyNumber_Add(), which can execute arbitrary user code and + mutate (or even clear) the underlying dict. */ + Py_INCREF(oldval); newval = PyNumber_Add(oldval, one); + Py_DECREF(oldval); if (newval == NULL) goto done; if (_PyDict_SetItem_KnownHash(mapping, key, newval, hash) < 0) From 8d46f961c30b795357df9a8cde479742562d8fc5 Mon Sep 17 00:00:00 2001 From: Hauke D Date: Thu, 25 Dec 2025 12:34:44 +0100 Subject: [PATCH 2/8] gh-143103: Added pad parameter to base64.z85encode() (GH-143106) This makes it analogous to a85encode() and b85encode() and allows the user to more easily meet the Z85 specification, which requires input lengths to be a multiple of 4. --- Doc/library/base64.rst | 8 +++++++- Lib/base64.py | 4 ++-- Lib/test/test_base64.py | 16 ++++++++++++++++ Misc/ACKS | 1 + ...025-12-23-17-07-22.gh-issue-143103.LRjXEW.rst | 1 + 5 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-23-17-07-22.gh-issue-143103.LRjXEW.rst diff --git a/Doc/library/base64.rst b/Doc/library/base64.rst index 529a7242443820..2d901824335145 100644 --- a/Doc/library/base64.rst +++ b/Doc/library/base64.rst @@ -267,14 +267,20 @@ Refer to the documentation of the individual functions for more information. .. versionadded:: 3.4 -.. function:: z85encode(s) +.. function:: z85encode(s, pad=False) Encode the :term:`bytes-like object` *s* using Z85 (as used in ZeroMQ) and return the encoded :class:`bytes`. See `Z85 specification `_ for more information. + If *pad* is true, the input is padded with ``b'\0'`` so its length is a + multiple of 4 bytes before encoding. + .. versionadded:: 3.13 + .. versionchanged:: next + The *pad* parameter was added. + .. function:: z85decode(s) diff --git a/Lib/base64.py b/Lib/base64.py index 341bf8eaf1891e..c2fdee8eab9690 100644 --- a/Lib/base64.py +++ b/Lib/base64.py @@ -508,9 +508,9 @@ def b85decode(b): ) _z85_encode_translation = bytes.maketrans(_b85alphabet, _z85alphabet) -def z85encode(s): +def z85encode(s, pad=False): """Encode bytes-like object b in z85 format and return a bytes object.""" - return b85encode(s).translate(_z85_encode_translation) + return b85encode(s, pad).translate(_z85_encode_translation) def z85decode(s): """Decode the z85-encoded bytes-like object or ASCII string b diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py index ac3f09405457df..288caf663e8321 100644 --- a/Lib/test/test_base64.py +++ b/Lib/test/test_base64.py @@ -665,6 +665,7 @@ def test_z85encode(self): tests = { b'': b'', + b'\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B': b'HelloWorld', b'www.python.org': b'CxXl-AcVLsz/dgCA+t', bytes(range(255)): b"""009c61o!#m2NH?C3>iWS5d]J*6CRx17-skh9337x""" b"""ar.{NbQB=+c[cR@eg&FcfFLssg=mfIi5%2YjuU>)kTv.7l}6Nnnj=AD""" @@ -840,6 +841,21 @@ def test_b85_padding(self): eq(base64.b85decode(b'czAet'), b"xxxx") eq(base64.b85decode(b'czAetcmMzZ'), b"xxxxx\x00\x00\x00") + def test_z85_padding(self): + eq = self.assertEqual + + eq(base64.z85encode(b"x", pad=True), b'CMmZz') + eq(base64.z85encode(b"xx", pad=True), b'CZ6h*') + eq(base64.z85encode(b"xxx", pad=True), b'CZaDk') + eq(base64.z85encode(b"xxxx", pad=True), b'CZaET') + eq(base64.z85encode(b"xxxxx", pad=True), b'CZaETCMmZz') + + eq(base64.z85decode(b'CMmZz'), b"x\x00\x00\x00") + eq(base64.z85decode(b'CZ6h*'), b"xx\x00\x00") + eq(base64.z85decode(b'CZaDk'), b"xxx\x00") + eq(base64.z85decode(b'CZaET'), b"xxxx") + eq(base64.z85decode(b'CZaETCMmZz'), b"xxxxx\x00\x00\x00") + def test_a85decode_errors(self): illegal = (set(range(32)) | set(range(118, 256))) - set(b' \t\n\r\v') for c in illegal: diff --git a/Misc/ACKS b/Misc/ACKS index a14089a39cce82..bb6b6bde822a4e 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -418,6 +418,7 @@ Lisandro Dalcin Darren Dale Andrew Dalke Lars Damerow +Hauke Dämpfling Evan Dandrea Eric Daniel Scott David Daniels diff --git a/Misc/NEWS.d/next/Library/2025-12-23-17-07-22.gh-issue-143103.LRjXEW.rst b/Misc/NEWS.d/next/Library/2025-12-23-17-07-22.gh-issue-143103.LRjXEW.rst new file mode 100644 index 00000000000000..b00c03707ca352 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-23-17-07-22.gh-issue-143103.LRjXEW.rst @@ -0,0 +1 @@ +Add padding support to :func:`base64.z85encode` via the ``pad`` parameter. From 579c5b496b467a2b175cb30caa4f6873cb13c9a1 Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Thu, 25 Dec 2025 22:24:25 +0800 Subject: [PATCH 3/8] gh-143145: Fix possible reference leak in ctypes _build_result() (GH-143131) The result tuple was leaked if __ctypes_from_outparam__() failed for any item. Signed-off-by: Yongtao Huang --- .../next/Library/2025-12-24-14-18-52.gh-issue-143145.eXLw8D.rst | 1 + Modules/_ctypes/_ctypes.c | 1 + 2 files changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-12-24-14-18-52.gh-issue-143145.eXLw8D.rst diff --git a/Misc/NEWS.d/next/Library/2025-12-24-14-18-52.gh-issue-143145.eXLw8D.rst b/Misc/NEWS.d/next/Library/2025-12-24-14-18-52.gh-issue-143145.eXLw8D.rst new file mode 100644 index 00000000000000..2aff1090b1812f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-24-14-18-52.gh-issue-143145.eXLw8D.rst @@ -0,0 +1 @@ +Fixed a possible reference leak in ctypes when constructing results with multiple output parameters on error. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 774ac71ce9ec56..563e95a762599b 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -4557,6 +4557,7 @@ _build_result(PyObject *result, PyObject *callargs, v = PyTuple_GET_ITEM(callargs, i); v = PyObject_CallMethodNoArgs(v, &_Py_ID(__ctypes_from_outparam__)); if (v == NULL || numretvals == 1) { + Py_XDECREF(tup); Py_DECREF(callargs); return v; } From 8611f74e089d9ac9de84dd42be9d251db27889aa Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 25 Dec 2025 11:31:41 -0500 Subject: [PATCH 4/8] gh-142975: During GC, mark frozen objects with a merged zero refcount for destruction (GH-143156) --- Lib/test/test_free_threading/test_gc.py | 32 +++++++++++++++++++ ...-12-24-13-44-24.gh-issue-142975.8C4vIP.rst | 2 ++ Python/gc_free_threading.c | 6 +++- 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-24-13-44-24.gh-issue-142975.8C4vIP.rst diff --git a/Lib/test/test_free_threading/test_gc.py b/Lib/test/test_free_threading/test_gc.py index 3b83e0431efa6b..8b45b6e2150c28 100644 --- a/Lib/test/test_free_threading/test_gc.py +++ b/Lib/test/test_free_threading/test_gc.py @@ -62,6 +62,38 @@ def mutator_thread(): with threading_helper.start_threads(gcs + mutators): pass + def test_freeze_object_in_brc_queue(self): + # GH-142975: Freezing objects in the BRC queue could result in some + # objects having a zero refcount without being deallocated. + + class Weird: + # We need a destructor to trigger the check for object resurrection + def __del__(self): + pass + + # This is owned by the main thread, so the subthread will have to increment + # this object's reference count. + weird = Weird() + + def evil(): + gc.freeze() + + # Decrement the reference count from this thread, which will trigger the + # slow path during resurrection and add our weird object to the BRC queue. + nonlocal weird + del weird + + # Collection will merge the object's reference count and make it zero. + gc.collect() + + # Unfreeze the object, making it visible to the GC. + gc.unfreeze() + gc.collect() + + thread = Thread(target=evil) + thread.start() + thread.join() + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-24-13-44-24.gh-issue-142975.8C4vIP.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-24-13-44-24.gh-issue-142975.8C4vIP.rst new file mode 100644 index 00000000000000..9d7f57ee60aa47 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-24-13-44-24.gh-issue-142975.8C4vIP.rst @@ -0,0 +1,2 @@ +Fix crash after unfreezing all objects tracked by the garbage collector on +the :term:`free threaded ` build. diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 04b9b8f3f85603..51261cea0cfe2c 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -906,7 +906,11 @@ gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, gc_mark_args_t *ar static void queue_untracked_obj_decref(PyObject *op, struct collection_state *state) { - if (!_PyObject_GC_IS_TRACKED(op)) { + assert(Py_REFCNT(op) == 0); + // gh-142975: We have to treat frozen objects as untracked in this function + // or else they might be picked up in a future collection, which breaks the + // assumption that all incoming objects have a non-zero reference count. + if (!_PyObject_GC_IS_TRACKED(op) || gc_is_frozen(op)) { // GC objects with zero refcount are handled subsequently by the // GC as if they were cyclic trash, but we have to handle dead // non-GC objects here. Add one to the refcount so that we can From b9a48064306229287d7211e9510f578065e457fc Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Fri, 26 Dec 2025 01:08:43 +0800 Subject: [PATCH 5/8] gh-143164: Fix incorrect error message for ctypes bitfield overflow (GH-143165) Signed-off-by: Yongtao Huang --- Lib/test/test_ctypes/test_struct_fields.py | 15 +++++++++++++++ ...2025-12-25-08-58-55.gh-issue-142164.XrFztf.rst | 1 + Modules/_ctypes/cfield.c | 4 ++-- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-25-08-58-55.gh-issue-142164.XrFztf.rst diff --git a/Lib/test/test_ctypes/test_struct_fields.py b/Lib/test/test_ctypes/test_struct_fields.py index b50bbcbb65c423..dc26e26d8a9fb1 100644 --- a/Lib/test/test_ctypes/test_struct_fields.py +++ b/Lib/test/test_ctypes/test_struct_fields.py @@ -130,6 +130,21 @@ class S(Structure): self.check_struct(S) self.assertEqual(S.largeField.bit_size, size * 8) + def test_bitfield_overflow_error_message(self): + with self.assertRaisesRegex( + ValueError, + r"bit field 'x' overflows its type \(2 \+ 7 > 8\)", + ): + CField( + name="x", + type=c_byte, + byte_size=1, + byte_offset=0, + index=0, + _internal_use=True, + bit_size=7, + bit_offset=2, + ) # __set__ and __get__ should raise a TypeError in case their self # argument is not a ctype instance. diff --git a/Misc/NEWS.d/next/Library/2025-12-25-08-58-55.gh-issue-142164.XrFztf.rst b/Misc/NEWS.d/next/Library/2025-12-25-08-58-55.gh-issue-142164.XrFztf.rst new file mode 100644 index 00000000000000..e75270b9e94c03 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-25-08-58-55.gh-issue-142164.XrFztf.rst @@ -0,0 +1 @@ +Fix the ctypes bitfield overflow error message to report the correct offset and size calculation. diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 547e2471a1cbc0..4ebca0e0b3db0a 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -160,8 +160,8 @@ PyCField_new_impl(PyTypeObject *type, PyObject *name, PyObject *proto, if ((bitfield_size + bit_offset) > byte_size * 8) { PyErr_Format( PyExc_ValueError, - "bit field %R overflows its type (%zd + %zd >= %zd)", - name, bit_offset, byte_size*8); + "bit field %R overflows its type (%zd + %zd > %zd)", + name, bit_offset, bitfield_size, byte_size * 8); goto error; } } From 59ede34c8c4b45d8ede5416e34d88db172fbea75 Mon Sep 17 00:00:00 2001 From: ivonastojanovic <80911834+ivonastojanovic@users.noreply.github.com> Date: Thu, 25 Dec 2025 19:22:54 +0100 Subject: [PATCH 6/8] gh-138122: Convert GIL/GC/exception stats from tiles to progress bars (#143177) --- .../_flamegraph_assets/flamegraph.css | 97 ++++++++----------- .../sampling/_flamegraph_assets/flamegraph.js | 24 ++++- .../flamegraph_template.html | 79 +++++++++------ 3 files changed, 110 insertions(+), 90 deletions(-) diff --git a/Lib/profiling/sampling/_flamegraph_assets/flamegraph.css b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.css index 03eb2274d23e68..e8fda417428104 100644 --- a/Lib/profiling/sampling/_flamegraph_assets/flamegraph.css +++ b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.css @@ -405,21 +405,18 @@ body.resizing-sidebar { text-overflow: ellipsis; } -/* Efficiency Bar */ -.efficiency-section { - margin-top: 10px; - padding-top: 10px; - border-top: 1px solid var(--border); -} +/* -------------------------------------------------------------------------- + Progress Bars + -------------------------------------------------------------------------- */ -.efficiency-header { +.bar-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px; } -.efficiency-label { +.bar-label { font-size: 9px; font-weight: 600; color: var(--text-secondary); @@ -427,21 +424,21 @@ body.resizing-sidebar { letter-spacing: 0.2px; } -.efficiency-value { +.bar-value { font-family: var(--font-mono); font-size: 11px; font-weight: 700; color: var(--accent); } -.efficiency-bar { +.bar { height: 6px; background: var(--bg-tertiary); border-radius: 3px; overflow: hidden; } -.efficiency-fill { +.bar-fill { height: 100%; background: linear-gradient(90deg, #28a745 0%, #20c997 50%, #17a2b8 100%); border-radius: 3px; @@ -450,7 +447,7 @@ body.resizing-sidebar { overflow: hidden; } -.efficiency-fill::after { +.bar-fill::after { content: ''; position: absolute; top: 0; @@ -467,68 +464,56 @@ body.resizing-sidebar { } /* -------------------------------------------------------------------------- - Thread Stats Grid (in Sidebar) + Efficiency Section Container + -------------------------------------------------------------------------- */ + +.efficiency-section { + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid var(--border); +} + +/* -------------------------------------------------------------------------- + Thread Stats Progress Bars (in Sidebar) -------------------------------------------------------------------------- */ .thread-stats-section { display: block; } -.stats-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 8px; +.stats-container { + display: flex; + flex-direction: column; + gap: 10px; } -.stat-tile { - background: var(--bg-primary); - border-radius: 8px; - padding: 10px; - text-align: center; - border: 2px solid var(--border); - transition: all var(--transition-fast); +.stat-item { animation: fadeIn 0.4s ease-out backwards; animation-delay: calc(var(--i, 0) * 0.05s); } -.stat-tile:nth-child(1) { --i: 0; } -.stat-tile:nth-child(2) { --i: 1; } -.stat-tile:nth-child(3) { --i: 2; } -.stat-tile:nth-child(4) { --i: 3; } +.stat-item:nth-child(1) { --i: 0; } +.stat-item:nth-child(2) { --i: 1; } +.stat-item:nth-child(3) { --i: 2; } +.stat-item:nth-child(4) { --i: 3; } +.stat-item:nth-child(5) { --i: 4; } -.stat-tile:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-sm); +/* Color variants for bar-fill */ +.bar-fill--green { + background: linear-gradient(90deg, #28a745 0%, #20c997 100%); } -.stat-tile-value { - font-family: var(--font-mono); - font-size: 16px; - font-weight: 700; - color: var(--text-primary); - line-height: 1.2; +.bar-fill--yellow { + background: linear-gradient(90deg, #ffc107 0%, #ffdb4d 100%); } -.stat-tile-label { - font-size: 9px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.3px; - color: var(--text-muted); - margin-top: 2px; +.bar-fill--purple { + background: linear-gradient(90deg, #6f42c1 0%, #9b6dd6 100%); } -/* Stat tile color variants */ -.stat-tile--green { --tile-color: 40, 167, 69; --tile-text: #28a745; } -.stat-tile--red { --tile-color: 220, 53, 69; --tile-text: #dc3545; } -.stat-tile--yellow { --tile-color: 255, 193, 7; --tile-text: #d39e00; } -.stat-tile--purple { --tile-color: 111, 66, 193; --tile-text: #6f42c1; } - -.stat-tile[class*="--"] { - border-color: rgba(var(--tile-color), 0.4); - background: linear-gradient(135deg, rgba(var(--tile-color), 0.08) 0%, var(--bg-primary) 100%); +.bar-fill--red { + background: linear-gradient(90deg, #dc3545 0%, #ff6b7a 100%); } -.stat-tile[class*="--"] .stat-tile-value { color: var(--tile-text); } /* -------------------------------------------------------------------------- Hotspot Cards @@ -985,10 +970,6 @@ body.resizing-sidebar { .brand-info { display: none; } - - .stats-grid { - grid-template-columns: 1fr; - } } /* -------------------------------------------------------------------------- diff --git a/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js index 17fd95af859587..1a51802ffefac7 100644 --- a/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js +++ b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js @@ -742,24 +742,38 @@ function populateThreadStats(data, selectedThreadId = null) { if (gilReleasedStat) gilReleasedStat.style.display = 'block'; if (gilWaitingStat) gilWaitingStat.style.display = 'block'; + const gilHeldPct = threadStats.has_gil_pct || 0; const gilHeldPctElem = document.getElementById('gil-held-pct'); - if (gilHeldPctElem) gilHeldPctElem.textContent = `${(threadStats.has_gil_pct || 0).toFixed(1)}%`; + if (gilHeldPctElem) gilHeldPctElem.textContent = `${gilHeldPct.toFixed(1)}%`; + const gilHeldFill = document.getElementById('gil-held-fill'); + if (gilHeldFill) gilHeldFill.style.width = `${gilHeldPct}%`; - const gilReleasedPctElem = document.getElementById('gil-released-pct'); // GIL Released = not holding GIL and not waiting for it const gilReleasedPct = Math.max(0, 100 - (threadStats.has_gil_pct || 0) - (threadStats.gil_requested_pct || 0)); + const gilReleasedPctElem = document.getElementById('gil-released-pct'); if (gilReleasedPctElem) gilReleasedPctElem.textContent = `${gilReleasedPct.toFixed(1)}%`; + const gilReleasedFill = document.getElementById('gil-released-fill'); + if (gilReleasedFill) gilReleasedFill.style.width = `${gilReleasedPct}%`; + const gilWaitingPct = threadStats.gil_requested_pct || 0; const gilWaitingPctElem = document.getElementById('gil-waiting-pct'); - if (gilWaitingPctElem) gilWaitingPctElem.textContent = `${(threadStats.gil_requested_pct || 0).toFixed(1)}%`; + if (gilWaitingPctElem) gilWaitingPctElem.textContent = `${gilWaitingPct.toFixed(1)}%`; + const gilWaitingFill = document.getElementById('gil-waiting-fill'); + if (gilWaitingFill) gilWaitingFill.style.width = `${gilWaitingPct}%`; } + const gcPct = threadStats.gc_pct || 0; const gcPctElem = document.getElementById('gc-pct'); - if (gcPctElem) gcPctElem.textContent = `${(threadStats.gc_pct || 0).toFixed(1)}%`; + if (gcPctElem) gcPctElem.textContent = `${gcPct.toFixed(1)}%`; + const gcFill = document.getElementById('gc-fill'); + if (gcFill) gcFill.style.width = `${gcPct}%`; // Exception stats + const excPct = threadStats.has_exception_pct || 0; const excPctElem = document.getElementById('exc-pct'); - if (excPctElem) excPctElem.textContent = `${(threadStats.has_exception_pct || 0).toFixed(1)}%`; + if (excPctElem) excPctElem.textContent = `${excPct.toFixed(1)}%`; + const excFill = document.getElementById('exc-fill'); + if (excFill) excFill.style.width = `${excPct}%`; } // ============================================================================ diff --git a/Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html b/Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html index 936c9adfc8c519..195a555d68e98b 100644 --- a/Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html +++ b/Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html @@ -159,21 +159,21 @@

Profile Summary