xref: /xnu-11215.41.3/tools/lldbmacros/vm_pageout.py (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
1"""
2Macros relating to VM Pageout Scan.
3"""
4from core.cvalue import sizeof, value
5import json
6from memory import PrettyPrintDictionary
7from process import GetTaskSummary
8import re
9from scheduler import GetRecentTimestamp
10from typing import Iterable, Optional
11from xnu import header, lldb_command, kern, xnudebug_test
12
13
14class VmPageoutStats(Iterable):
15    def __init__(self, num_samples: Optional[int] = None):
16        self.num_samples = num_samples
17        self.stats = kern.globals.vm_pageout_stats
18        self.num_stats = sizeof(self.stats) // sizeof(self.stats[0])
19        self.now = kern.globals.vm_pageout_stat_now
20
21    def __iter__(self):
22        self.samples_iterated = 0
23        self.index = self.now - 1 if self.now > 0 else self.num_stats - 1
24        return self
25
26    ## Iterate stats in reverse chronological order
27    def __next__(self):
28        if self.index == self.now:
29            raise StopIteration
30
31        if (self.num_samples is not None and
32            self.samples_iterated == self.num_samples):
33            raise StopIteration
34
35        self.samples_iterated += 1
36
37        if self.index == 0:
38            self.index = self.num_stats - 1
39        else:
40            self.index -= 1
41
42        return self
43
44    def page_counts(self) -> str:
45       stat = self.stats[self.index]
46       return (f'{stat.vm_page_active_count:12d} '
47              f'{stat.vm_page_inactive_count:12d} '
48              f'{stat.vm_page_speculative_count:12d} '
49              f'{stat.vm_page_anonymous_count:12d} '
50              f'{stat.vm_page_free_count:12d} '
51              f'{stat.vm_page_wire_count:12d} '
52              f'{stat.vm_page_compressor_count:12d} '
53              f'{stat.vm_page_pages_compressed:12d} '
54              f'{stat.vm_page_pageable_internal_count:12d} '
55              f'{stat.vm_page_pageable_external_count:12d} '
56              f'{stat.vm_page_realtime_count:12d} '
57              f'{stat.vm_page_xpmapped_external_count:12d}')
58
59    def page_stats(self) -> str:
60        stat = self.stats[self.index]
61        return (f'{stat.considered:12d} '
62              f'{stat.pages_grabbed:12d} '
63              f'{stat.pages_freed:12d} '
64              f'{stat.pages_compressed:12d} '
65              f'{stat.pages_evicted:12d} '
66              f'{stat.pages_purged:12d} '
67              f'{stat.skipped_external:12d} '
68              f'{stat.skipped_internal:12d} '
69              f'{stat.freed_speculative:12d} '
70              f'{stat.freed_internal:12d} '
71              f'{stat.freed_external:12d} '
72              f'{stat.freed_cleaned:12d} '
73              f'{stat.freed_internal:12d} '
74              f'{stat.freed_external:12d} '
75              f'{stat.inactive_referenced:12d} '
76              f'{stat.inactive_nolock:12d} '
77              f'{stat.reactivation_limit_exceeded:12d} '
78              f'{stat.throttled_internal_q:12d} '
79              f'{stat.throttled_external_q:12d} '
80              f'{stat.forcereclaimed_sharedcache:12d} '
81              f'{stat.forcereclaimed_realtime:12d} '
82              f'{stat.protected_sharedcache:12d} '
83              f'{stat.protected_realtime:12d}')
84
85# Macro: showvmpagehistory
86
87@header(f"{'active':>12s} "
88        f"{'inactive':>12s} "
89        f"{'speculative':>12s} "
90        f"{'anonymous':>12s} "
91        f"{'free':>12s} "
92        f"{'wired':>12s} "
93        f"{'compressor':>12s} "
94        f"{'compressed':>12s} "
95        f"{'pageable_int':>12s} "
96        f"{'pageable_ext':>12s} "
97        f"{'realtime':>12s} "
98        f"{'xpmapped_ext':>12s}")
99@lldb_command('showvmpageouthistory', 'S:')
100def ShowVMPageoutHistory(cmd_args=None, cmd_options={}):
101    '''
102    Dump a recent history of VM page dispostions in reverse chronological order.
103
104    usage: showvmpagehistory [-S samples]
105
106        -S n    Show only `n` most recent samples (samples are collect at 1 Hz)
107    '''
108    num_samples = int(cmd_options['-S']) if '-S' in cmd_options else None
109
110    print(ShowVMPageoutHistory.header)
111    for stat in VmPageoutStats(num_samples):
112        print(stat.page_counts())
113
114@xnudebug_test('test_vmpageouthistory')
115def TestMemstats(kernel_target, config, lldb_obj, isConnected ):
116    """ Test the functionality of showvmpageouthistory command
117        returns
118         - False on failure
119         - True on success
120    """
121    if not isConnected:
122        print("Target is not connected. Cannot test memstats")
123        return False
124    res = lldb.SBCommandReturnObject()
125    lldb_obj.debugger.GetCommandInterpreter().HandleCommand("showvmpageouthistory", res)
126    result = res.GetOutput()
127    if len(result.splitlines()) < 2:
128        print("Result has fewer than two lines")
129        return False
130    for line in result.splitlines():
131        matches = re.findall(r'(\d+|[\w_]+)', line)
132        if len(matches) < 12:
133            print("Line has fewer than 12 elements!")
134            print(line)
135            return False
136    return True
137
138# EndMacro: showvmpageouthistory
139
140# Macro: showvmpageoutstats
141
142@header(f"{'considered':>12s} "
143        f"{'grabbed':>12s} "
144        f"{'freed':>12s} "
145        f"{'compressed':>12s} "
146        f"{'evicted':>12s} "
147        f"{'purged':>12s} "
148        f"{'skipped_ext':>12s} "
149        f"{'skipped_int':>12s} "
150        f"{'freed_spec':>12s} "
151        f"{'freed_int':>12s} "
152        f"{'freed_ext':>12s} "
153        f"{'cleaned':>12s} "
154        f"{'cleaned_ext':>12s} "
155        f"{'cleaned_int':>12s} "
156        f"{'inact_ref':>12s} "
157        f"{'inact_lck':>12s} "
158        f"{'react_lim':>12s} "
159        f"{'thrtl_int':>12s} "
160        f"{'thrtl_ext':>12s} "
161        f"{'forced_sc':>12s} "
162        f"{'forced_rt':>12s} "
163        f"{'prot_sc':>12s} "
164        f"{'prot_rt':>12s}")
165@lldb_command('showvmpageoutstats', 'S:')
166def ShowVMPageoutStats(cmd_args=None, cmd_options={}):
167    '''
168    Dump a recent history of VM pageout statistics in reverse chronological order.
169
170    usage: showvmpageoutstats [-S samples]
171
172        -S n    Show only `n` most recent samples (samples are collect at 1 Hz)
173    '''
174    num_samples = int(cmd_options['-S']) if '-S' in cmd_options else None
175    print(ShowVMPageoutStats.header)
176    for stat in VmPageoutStats(num_samples):
177        print(stat.page_stats())
178
179@xnudebug_test('test_vmpageoutstats')
180def TestMemstats(kernel_target, config, lldb_obj, isConnected ):
181    """ Test the functionality of showvmpageoutstats command
182        returns
183         - False on failure
184         - True on success
185    """
186    if not isConnected:
187        print("Target is not connected. Cannot test showmvmpageoutstats")
188        return False
189    res = lldb.SBCommandReturnObject()
190    lldb_obj.debugger.GetCommandInterpreter().HandleCommand("showvmpageoutstats", res)
191    result = res.GetOutput()
192    if len(result.splitlines()) < 2:
193        print("Result has fewer than two lines")
194        return False
195    for line in result.splitlines():
196        matches = re.findall(r'(\d|[\w_]+)', line)
197        if len(matches) < 23:
198            print("Line has fewer than 23 elements!")
199            print(line)
200            return False
201    return True
202
203# EndMacro: showvmpageoutstats
204
205