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