xref: /xnu-12377.1.9/tools/lldbmacros/vm_pageout.py (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
1"""
2Macros relating to VM Pageout Scan.
3"""
4from core.cvalue import addressof, cast, sizeof, unsigned, value
5import json
6from memory import PrettyPrintDictionary
7from collections import namedtuple
8from process import GetTaskSummary
9import re
10from scheduler import GetRecentTimestamp
11from typing import Iterable, Optional
12from xnu import header, lldb_command, lldb_type_summary, kern, xnudebug_test
13
14
15class VmPageoutStats(Iterable):
16    def __init__(self, num_samples: Optional[int] = None):
17        self.num_samples = num_samples
18        self.stats = kern.globals.vm_pageout_stats
19        self.num_stats = sizeof(self.stats) // sizeof(self.stats[0])
20        self.now = kern.globals.vm_pageout_stat_now
21
22    def __iter__(self):
23        self.samples_iterated = 0
24        self.index = self.now - 1 if self.now > 0 else self.num_stats - 1
25        return self
26
27    ## Iterate stats in reverse chronological order
28    def __next__(self):
29        if self.index == self.now:
30            raise StopIteration
31
32        if (self.num_samples is not None and
33            self.samples_iterated == self.num_samples):
34            raise StopIteration
35
36        self.samples_iterated += 1
37
38        if self.index == 0:
39            self.index = self.num_stats - 1
40        else:
41            self.index -= 1
42
43        return self
44
45    def page_counts(self) -> str:
46       stat = self.stats[self.index]
47       return (f'{stat.vm_page_active_count:12d} '
48              f'{stat.vm_page_inactive_count:12d} '
49              f'{stat.vm_page_speculative_count:12d} '
50              f'{stat.vm_page_anonymous_count:12d} '
51              f'{stat.vm_page_free_count:12d} '
52              f'{stat.vm_page_wire_count:12d} '
53              f'{stat.vm_page_compressor_count:12d} '
54              f'{stat.vm_page_pages_compressed:12d} '
55              f'{stat.vm_page_pageable_internal_count:12d} '
56              f'{stat.vm_page_pageable_external_count:12d} '
57              f'{stat.vm_page_realtime_count:12d} '
58              f'{stat.vm_page_xpmapped_external_count:12d}')
59
60    def page_stats(self) -> str:
61        stat = self.stats[self.index]
62        return (f'{stat.considered:12d} '
63              f'{stat.pages_grabbed:12d} '
64              f'{stat.pages_freed:12d} '
65              f'{stat.pages_compressed:12d} '
66              f'{stat.pages_evicted:12d} '
67              f'{stat.pages_purged:12d} '
68              f'{stat.skipped_external:12d} '
69              f'{stat.skipped_internal:12d} '
70              f'{stat.freed_speculative:12d} '
71              f'{stat.freed_internal:12d} '
72              f'{stat.freed_external:12d} '
73              f'{stat.freed_cleaned:12d} '
74              f'{stat.freed_internal:12d} '
75              f'{stat.freed_external:12d} '
76              f'{stat.inactive_referenced:12d} '
77              f'{stat.inactive_nolock:12d} '
78              f'{stat.reactivation_limit_exceeded:12d} '
79              f'{stat.throttled_internal_q:12d} '
80              f'{stat.throttled_external_q:12d} '
81              f'{stat.forcereclaimed_sharedcache:12d} '
82              f'{stat.forcereclaimed_realtime:12d} '
83              f'{stat.protected_sharedcache:12d} '
84              f'{stat.protected_realtime:12d}')
85
86# Macro: showvmpagehistory
87
88@header(f"{'active':>12s} "
89        f"{'inactive':>12s} "
90        f"{'speculative':>12s} "
91        f"{'anonymous':>12s} "
92        f"{'free':>12s} "
93        f"{'wired':>12s} "
94        f"{'compressor':>12s} "
95        f"{'compressed':>12s} "
96        f"{'pageable_int':>12s} "
97        f"{'pageable_ext':>12s} "
98        f"{'realtime':>12s} "
99        f"{'xpmapped_ext':>12s}")
100@lldb_command('showvmpageouthistory', 'S:')
101def ShowVMPageoutHistory(cmd_args=None, cmd_options={}):
102    '''
103    Dump a recent history of VM page dispostions in reverse chronological order.
104
105    usage: showvmpagehistory [-S samples]
106
107        -S n    Show only `n` most recent samples (samples are collect at 1 Hz)
108    '''
109    num_samples = int(cmd_options['-S']) if '-S' in cmd_options else None
110
111    print(ShowVMPageoutHistory.header)
112    for stat in VmPageoutStats(num_samples):
113        print(stat.page_counts())
114
115# EndMacro: showvmpageouthistory
116
117# Macro: showvmpageoutstats
118
119@header(f"{'considered':>12s} "
120        f"{'grabbed':>12s} "
121        f"{'freed':>12s} "
122        f"{'compressed':>12s} "
123        f"{'evicted':>12s} "
124        f"{'purged':>12s} "
125        f"{'skipped_ext':>12s} "
126        f"{'skipped_int':>12s} "
127        f"{'freed_spec':>12s} "
128        f"{'freed_int':>12s} "
129        f"{'freed_ext':>12s} "
130        f"{'cleaned':>12s} "
131        f"{'cleaned_ext':>12s} "
132        f"{'cleaned_int':>12s} "
133        f"{'inact_ref':>12s} "
134        f"{'inact_lck':>12s} "
135        f"{'react_lim':>12s} "
136        f"{'thrtl_int':>12s} "
137        f"{'thrtl_ext':>12s} "
138        f"{'forced_sc':>12s} "
139        f"{'forced_rt':>12s} "
140        f"{'prot_sc':>12s} "
141        f"{'prot_rt':>12s}")
142@lldb_command('showvmpageoutstats', 'S:')
143def ShowVMPageoutStats(cmd_args=None, cmd_options={}):
144    '''
145    Dump a recent history of VM pageout statistics in reverse chronological order.
146
147    usage: showvmpageoutstats [-S samples]
148
149        -S n    Show only `n` most recent samples (samples are collect at 1 Hz)
150    '''
151    num_samples = int(cmd_options['-S']) if '-S' in cmd_options else None
152    print(ShowVMPageoutStats.header)
153    for stat in VmPageoutStats(num_samples):
154        print(stat.page_stats())
155
156# EndMacro: showvmpageoutstats
157
158# Macro: showvmpageoutqueues
159
160PageoutQueueSummary = namedtuple('VMPageoutQueueSummary', [
161        'name', 'queue', 'laundry', 'max_laundry', 'busy', 'throttled',
162        'low_pri', 'draining', 'initialized'])
163PageoutQueueSummaryNames = PageoutQueueSummary(*PageoutQueueSummary._fields)
164PageoutQueueSummaryFormat = ('{summary.queue:<18s} {summary.name:<12s} '
165                             '{summary.laundry:>7s} {summary.max_laundry:>11s} '
166                             '{summary.busy:>4s} {summary.throttled:>9s} '
167                             '{summary.low_pri:>7s} {summary.draining:>8s} '
168                             '{summary.initialized:>12s}')
169
170@lldb_type_summary(['struct vm_pageout_queue'])
171@header(PageoutQueueSummaryFormat.format(summary=PageoutQueueSummaryNames))
172def GetVMPageoutQueueSummary(pageout_queue):
173    ''' Dump a summary of the given pageout queue
174    '''
175    if addressof(pageout_queue) == kern.GetLoadAddressForSymbol('vm_pageout_queue_internal'):
176        name = 'internal'
177    elif addressof(pageout_queue) == kern.GetLoadAddressForSymbol('vm_pageout_queue_external'):
178        name = 'external'
179    elif ('vm_pageout_queue_benchmark' in kern.globals and
180          addressof(pageout_queue) == kern.GetLoadAddressForSymbol('vm_pageout_queue_benchmark')):
181        name = 'benchmark'
182    else:
183        name = 'unknown'
184    summary = PageoutQueueSummary(name=name,
185                                  queue=f'{addressof(pageout_queue.pgo_pending):<#018x}',
186                                  laundry=str(unsigned(pageout_queue.pgo_laundry)),
187                                  max_laundry=str(unsigned(pageout_queue.pgo_maxlaundry)),
188                                  busy=("Y" if bool(pageout_queue.pgo_busy) else "N"),
189                                  throttled=("Y" if bool(pageout_queue.pgo_throttled) else "N"),
190                                  low_pri=("Y" if bool(pageout_queue.pgo_lowpriority) else "N"),
191                                  draining=("Y" if bool(pageout_queue.pgo_draining) else "N"),
192                                  initialized=("Y" if bool(pageout_queue.pgo_inited) else "N"))
193    return PageoutQueueSummaryFormat.format(summary=summary)
194
195@lldb_command('showvmpageoutqueues', '')
196def ShowVMPageoutQueues(cmd_args=None, cmd_options={}):
197    '''
198    Print information about the various pageout queues.
199
200    usage: showvmpageoutqueues
201    '''
202
203    internal_queue = kern.globals.vm_pageout_queue_internal
204    external_queue = kern.globals.vm_pageout_queue_external
205    print(GetVMPageoutQueueSummary.header)
206    print(GetVMPageoutQueueSummary(internal_queue))
207    print(GetVMPageoutQueueSummary(external_queue))
208    try:
209        benchmark_queue = kern.globals.vm_pageout_queue_benchmark
210        print(GetVMPageoutQueueSummary(benchmark_queue))
211    except:
212        pass
213
214# EndMacro: showvmpageoutqueues
215