3636 pwd = None
3737
3838from . import _common
39+ from . import _ntuples as _ntp
3940from ._common import AIX
4041from ._common import BSD
4142from ._common import CONN_CLOSE
203204AF_LINK = _psplatform .AF_LINK
204205
205206__author__ = "Giampaolo Rodola'"
206- __version__ = "7.1.3 "
207+ __version__ = "7.2.0 "
207208version_info = tuple (int (num ) for num in __version__ .split ('.' ))
208209
209210_timer = getattr (time , 'monotonic' , time .time )
@@ -1162,7 +1163,7 @@ def memory_percent(self, memtype="rss"):
11621163 >>> psutil.Process().memory_info()._fields
11631164 ('rss', 'vms', 'shared', 'text', 'lib', 'data', 'dirty', 'uss', 'pss')
11641165 """
1165- valid_types = list (_psplatform .pfullmem ._fields )
1166+ valid_types = list (_ntp .pfullmem ._fields )
11661167 if memtype not in valid_types :
11671168 msg = (
11681169 f"invalid memtype { memtype !r} ; valid types are"
@@ -1171,7 +1172,7 @@ def memory_percent(self, memtype="rss"):
11711172 raise ValueError (msg )
11721173 fun = (
11731174 self .memory_info
1174- if memtype in _psplatform .pmem ._fields
1175+ if memtype in _ntp .pmem ._fields
11751176 else self .memory_full_info
11761177 )
11771178 metrics = fun ()
@@ -1211,11 +1212,9 @@ def memory_maps(self, grouped=True):
12111212 d [path ] = list (map (lambda x , y : x + y , d [path ], nums ))
12121213 except KeyError :
12131214 d [path ] = nums
1214- nt = _psplatform .pmmap_grouped
1215- return [nt (path , * d [path ]) for path in d ]
1215+ return [_ntp .pmmap_grouped (path , * d [path ]) for path in d ]
12161216 else :
1217- nt = _psplatform .pmmap_ext
1218- return [nt (* x ) for x in it ]
1217+ return [_ntp .pmmap_ext (* x ) for x in it ]
12191218
12201219 def open_files (self ):
12211220 """Return files opened by process as a list of
@@ -1751,7 +1750,7 @@ def _cpu_busy_time(times):
17511750def _cpu_times_deltas (t1 , t2 ):
17521751 assert t1 ._fields == t2 ._fields , (t1 , t2 )
17531752 field_deltas = []
1754- for field in _psplatform .scputimes ._fields :
1753+ for field in _ntp .scputimes ._fields :
17551754 field_delta = getattr (t2 , field ) - getattr (t1 , field )
17561755 # CPU times are always supposed to increase over time
17571756 # or at least remain the same and that's because time
@@ -1766,7 +1765,7 @@ def _cpu_times_deltas(t1, t2):
17661765 # https://gitlab.com/procps-ng/procps/blob/v3.3.12/top/top.c#L5063
17671766 field_delta = max (0 , field_delta )
17681767 field_deltas .append (field_delta )
1769- return _psplatform .scputimes (* field_deltas )
1768+ return _ntp .scputimes (* field_deltas )
17701769
17711770
17721771def cpu_percent (interval = None , percpu = False ):
@@ -1885,7 +1884,7 @@ def calculate(t1, t2):
18851884 # make sure we don't return negative values or values over 100%
18861885 field_perc = min (max (0.0 , field_perc ), 100.0 )
18871886 nums .append (field_perc )
1888- return _psplatform .scputimes (* nums )
1887+ return _ntp .scputimes (* nums )
18891888
18901889 # system-wide usage
18911890 if not percpu :
@@ -1955,7 +1954,7 @@ def cpu_freq(percpu=False):
19551954 min_ = mins / num_cpus
19561955 max_ = maxs / num_cpus
19571956
1958- return _common .scpufreq (current , min_ , max_ )
1957+ return _ntp .scpufreq (current , min_ , max_ )
19591958
19601959 __all__ .append ("cpu_freq" )
19611960
@@ -2114,13 +2113,12 @@ def disk_io_counters(perdisk=False, nowrap=True):
21142113 return {} if perdisk else None
21152114 if nowrap :
21162115 rawdict = _wrap_numbers (rawdict , 'psutil.disk_io_counters' )
2117- nt = getattr (_psplatform , "sdiskio" , _common .sdiskio )
21182116 if perdisk :
21192117 for disk , fields in rawdict .items ():
2120- rawdict [disk ] = nt (* fields )
2118+ rawdict [disk ] = _ntp . sdiskio (* fields )
21212119 return rawdict
21222120 else :
2123- return nt (* (sum (x ) for x in zip (* rawdict .values ())))
2121+ return _ntp . sdiskio (* (sum (x ) for x in zip (* rawdict .values ())))
21242122
21252123
21262124disk_io_counters .cache_clear = functools .partial (
@@ -2167,10 +2165,10 @@ def net_io_counters(pernic=False, nowrap=True):
21672165 rawdict = _wrap_numbers (rawdict , 'psutil.net_io_counters' )
21682166 if pernic :
21692167 for nic , fields in rawdict .items ():
2170- rawdict [nic ] = _common .snetio (* fields )
2168+ rawdict [nic ] = _ntp .snetio (* fields )
21712169 return rawdict
21722170 else :
2173- return _common .snetio (* [sum (x ) for x in zip (* rawdict .values ())])
2171+ return _ntp .snetio (* [sum (x ) for x in zip (* rawdict .values ())])
21742172
21752173
21762174net_io_counters .cache_clear = functools .partial (
@@ -2252,7 +2250,7 @@ def net_if_addrs():
22522250 while addr .count (separator ) < 5 :
22532251 addr += f"{ separator } 00"
22542252
2255- nt = _common .snicaddr (fam , addr , mask , broadcast , ptp )
2253+ nt = _ntp .snicaddr (fam , addr , mask , broadcast , ptp )
22562254
22572255 # On Windows broadcast is None, so we determine it via
22582256 # ipaddress module.
@@ -2321,9 +2319,7 @@ def convert(n):
23212319 elif critical and not high :
23222320 high = critical
23232321
2324- ret [name ].append (
2325- _common .shwtemp (label , current , high , critical )
2326- )
2322+ ret [name ].append (_ntp .shwtemp (label , current , high , critical ))
23272323
23282324 return dict (ret )
23292325
@@ -2409,6 +2405,52 @@ def win_service_get(name):
24092405 return _psplatform .win_service_get (name )
24102406
24112407
2408+ # =====================================================================
2409+ # --- malloc / heap
2410+ # =====================================================================
2411+
2412+
2413+ # Linux + glibc, Windows, macOS, FreeBSD, NetBSD
2414+ if hasattr (_psplatform , "heap_info" ):
2415+
2416+ def heap_info ():
2417+ """Return low-level heap statistics from the C heap allocator
2418+ (glibc).
2419+
2420+ - `heap_used`: the total number of bytes allocated via
2421+ malloc/free. These are typically allocations smaller than
2422+ MMAP_THRESHOLD.
2423+
2424+ - `mmap_used`: the total number of bytes allocated via `mmap()`
2425+ or via large ``malloc()`` allocations.
2426+
2427+ - `heap_count` (Windows only): number of private heaps created
2428+ via `HeapCreate()`.
2429+ """
2430+ return _ntp .pheap (* _psplatform .heap_info ())
2431+
2432+ def heap_trim ():
2433+ """Request that the underlying allocator free any unused memory
2434+ it's holding in the heap (typically small `malloc()`
2435+ allocations).
2436+
2437+ In practice, modern allocators rarely comply, so this is not a
2438+ general-purpose memory-reduction tool and won't meaningfully
2439+ shrink RSS in real programs. Its primary value is in **leak
2440+ detection tools**.
2441+
2442+ Calling `heap_trim()` before taking measurements helps reduce
2443+ allocator noise, giving you a cleaner baseline so that changes
2444+ in `heap_used` come from the code you're testing, not from
2445+ internal allocator caching or fragmentation. Its effectiveness
2446+ depends on allocator behavior and fragmentation patterns.
2447+ """
2448+ _psplatform .heap_trim ()
2449+
2450+ __all__ .append ("heap_info" )
2451+ __all__ .append ("heap_trim" )
2452+
2453+
24122454# =====================================================================
24132455
24142456
0 commit comments