1"""Test all LLDB macros using LLDB session. 2 3Usages: 4 1. pytest - `xcrun --sdk macosx.internal pytest --disable-warnings -v tools/lldbmacros/tests/integration_smoke/test_lldb_macros [--remote-gdb 127.0.0.1:8000]`. 5 2. module - `xcrun --sdk macosx.internal python tools/lldbmacros/tests/integration_smoke/test_lldb_macros [127.0.0.1:8000]`. 6 3. macros within an existing LLDB session - 7 * `xcrun --sdk macosx.internal lldb [-c coredump file]` (coredumps are supported as well, see lldb command for details. 8 * `command script import command script import tools/lldbmacros/tests/integration_smoke/test_lldb_macros.py` 9 * If you need a `gdb-remote`, do `gdb [ip=127.0.0.1:]<port>` (the port is usually 8000). 10 * `macro_exec [macro1] [macro2] [...]`. 11 12TODO: extend the integration tests to actually validate correctness. 13""" 14import contextlib 15import functools 16import os.path 17import re 18import signal 19import sys 20import threading 21import typing 22 23import pytest 24import lldb 25 26from lldb_session import AtDeskLLDBGdbSession 27 28 29def _get_task(session: AtDeskLLDBGdbSession, name: str) -> str: 30 return session.exec(f"showtask -F {name}").split('\n')[2].split()[0] 31 32 33_get_init_task = functools.partial(_get_task, 'init') 34 35 36def _arbitrary_task(session: AtDeskLLDBGdbSession) -> str: 37 return session.exec("showalltasks").split('\n')[2].split()[0] 38 39 40def _arbitrary_proc(session: AtDeskLLDBGdbSession, pid: int = 0) -> str: 41 return session.exec(f"showpid {pid}").split('\n')[1].split()[5] 42 43 44def _arbitrary_thread(session: AtDeskLLDBGdbSession, task_name: str = 'init') -> str: 45 return session.exec(f"showtaskthreads -F {task_name}").split('\n')[3].strip().split()[0] 46 47 48def _arbitrary_kext(session: AtDeskLLDBGdbSession, kext_name: str = 'com.apple.BootCache') -> str: 49 return re.search(r"(0XFFFFF\w+)", session.exec(f"showkextmacho {kext_name}")).group(1) 50 51 52def _arbitrary_vm_line(session: AtDeskLLDBGdbSession, row: int, col: int) -> str: 53 return session.exec("showallvm").split('\n')[row].strip().split()[col] 54 55 56def _arbitrary_vm_map(session: AtDeskLLDBGdbSession, index: int = 1) -> str: 57 return _arbitrary_vm_line(session, row=index, col=1) 58 59 60def _arbitrary_vm_node(session: AtDeskLLDBGdbSession) -> str: 61 return session.exec("showallvnodes").stdout.readlines(4)[1].strip().split()[0] 62 63 64def __arbitrary_ipc_line(session: AtDeskLLDBGdbSession, index: int) -> str: 65 return session.exec('showallipc').split('\n')[1].strip().split()[index] 66 67 68def _arbitrary_ipc(session: AtDeskLLDBGdbSession) -> str: 69 return __arbitrary_ipc_line(session, index=2) 70 71 72def _arbitrary_mbuf(session: AtDeskLLDBGdbSession) -> str: 73 return session.exec('mbuf_showactive').split('\n')[3].strip().split()[0] 74 75 76def _arbitrary_proc_channel(session: AtDeskLLDBGdbSession, task_name: str = 'apsd') -> str: 77 proc_id = session.exec(f'showtask -F {task_name}').split('\n')[1].split()[6] 78 return session.exec(f'showprocchannels {proc_id}').split('\n')[1].split()[0] 79 80 81MACROS = [ 82 "kgmhelp", 83 ("showraw", "showversion"), # TODO: find a better one. 84 ("xnudebug", "reload memory"), 85 "showversion", 86 "paniclog", 87 "extpaniclog", 88 "showbootargs", 89 "showlldbtypesummaries", 90 ("walkqueue_head", '<struct queue_entry *> "thread *" "task_threads"'), # ? 91 ("walklist_entry", '<struct proc *> "struct proc *" "p_sibling"'), # ? 92 "iotrace", 93 "ttrace", 94 "showsysctls", 95 "showexperiments", 96 "allproc", 97 "zombproc", 98 "zombtasks", 99 "zombstacks", 100 ("showcoalitioninfo", lambda session: session.exec("showallcoalitions").split('\n')[0].split()[0]), 101 "showallcoalitions", 102 "showcurrentforegroundapps", 103 "showallthreadgroups", 104 ("showtaskcoalitions", "-F init"), 105 ("showtask", "-F init"), 106 ("showpid", "0"), 107 ("showproc", _arbitrary_proc), 108 ("showprocinfo",_arbitrary_proc), 109 ("showprocfiles", _arbitrary_proc), 110 ("showtty", lambda session: session.exec("showallttydevs").split('\n')[2].split()[0]), 111 "showallttydevs", 112 "dumpthread_terminate_queue", 113 ("dumpcrashed_thread_queue", None, [pytest.mark.xfail(reason='fails on live-VM coredump - rdar://136215390')]), 114 ("dumpcallqueue", ""), # TODO: ? 115 "showalltasklogicalwrites", 116 "showalltasks", 117 ("taskforpmap", ""), # TODO: ? 118 "showterminatedtasks", 119 ("showtaskstacks", "-F init"), # TODO: does not 120 ("showprocrefs", _arbitrary_task), 121 "showallthreads", 122 "showterminatedthreads", 123 ("showtaskthreads", "-F init"), 124 ("showact", _arbitrary_thread), 125 ("showactstack", _arbitrary_thread), 126 ("switchtoact", _arbitrary_thread), 127 ("switchtoregs", _arbitrary_thread), 128 ("showcallchains", "init"), # TODO: should work without `load-script-from-symbol-file true`. 129 "showallstacks", 130 "showcurrentstacks", 131 "showcurrentthreads", 132 ("fullbt", "0"), 133 ("fullbtall", None, [pytest.mark.xfail(reason='rdar://136033352')]), 134 "symbolicate", 135 "showinitchild", 136 "showproctree", 137 ("showthreadfortid", _arbitrary_thread), 138 ("showtaskledgers", "-F init"), 139 "showalltaskledgers", 140 "showprocuuidpolicytable", 141 "showalltaskpolicy", 142 "showallsuspendedtasks", 143 "showallpte", 144 "showallrefcounts", 145 "showallrunnablethreads", 146 "showallschedusage", 147 "showprocfilessummary", 148 ("workinguserstacks", _get_init_task), 149 ("workingkuserlibraries", _get_init_task), 150 ("showstackaftertask", "-F init"), 151 ("showstackafterthread", _get_init_task), 152 ("showkextmacho", "com.apple.BootCache"), 153 ("showkmodaddr", _arbitrary_kext), 154 "showallkmods", 155 "showallknownkmods", 156 ("addkext", "-N com.apple.BootCache"), 157 ("addkextaddr", _arbitrary_kext), 158 ("showzpcpu", ""), # TODO: ? 159 "memstats", 160 ("whatis", _arbitrary_kext), 161 "showzcache", 162 "zprint", 163 "showkalloctypes", 164 "showzchunks", 165 "showallzchunks", 166 "showbtref", 167 "_showbtlibrary", 168 "showbtlog", 169 "showbtlogrecords", 170 "zstack_showzonesbeinglogged", 171 "zstack", 172 "zstack_inorder", 173 "zstack_findleak", 174 "zstack_findelem", 175 "zstack_findtop", 176 ("showpcpu", ""), # TODO: ? 177 "showioalloc", 178 "showselectmem", 179 ("showtaskvme", _arbitrary_task), 180 "showallvm", 181 ("showtaskvm", functools.partial(_arbitrary_vm_line, row=1, col=0)), 182 "showallvmstats", 183 ("showmap", _arbitrary_vm_map), 184 ("showmapvme", _arbitrary_vm_map), 185 ("showrangevme", "-N 1"), 186 "showvmtagbtlog", 187 ("showmapranges", _arbitrary_vm_map, [pytest.mark.xfail(reason='rdar://136137832')]), 188 ("showmapwired", _arbitrary_vm_map), 189 "showallmounts", 190 "showvnodepath", 191 ("showvnodedev", ""), # TODO: session.exec("showallvnodes").stdout.readlines(4)[1].strip().split()[0], you need to cancel it in the middle. 192 ("showvnodelocks", ""), # TODO: session.exec("showallvnodes").stdout.readlines(4)[1].strip().split()[0], you need to cancel it in the middle. 193 ("showproclocks", ""), # TODO: session.exec("showallvnodes").stdout.readlines(4)[1].strip().split()[0], you need to cancel it in the middle. 194 "showvnode", 195 "showvolvnodes", 196 "showvolbusyvnodes", 197 "print_vnode", 198 "showworkqvnodes", 199 "shownewvnodes", 200 "showprocvnodes", 201 "showallprocvnodes", 202 ("showlock", ""), # TODO: ? 203 ("showthreadrwlck", ""), # TODO: thread? 204 "showallrwlckheld", 205 ("tryfindrwlckholders", _arbitrary_thread), 206 ("getthreadfromctid", ""), # TODO: ctid? 207 ("getturnstilefromctsid", ""), # TODO: ctid? 208 ("showkernapfsreflock", ""), # TODO: kern_apfs_reflock_t? 209 "showbootermemorymap", 210 "show_all_purgeable_objects", 211 "show_all_purgeable_nonvolatile_objects", 212 "show_all_purgeable_volatile_objects", 213 ("showmapcopyvme", _arbitrary_vm_map, [pytest.mark.xfail(reason='rdar://136137832')]), 214 ("showmaptpro", ""), # TODO: vm_map? 215 ("showvmpage", ""), # TODO: vm_page? 216 ("showvmobject", "kernel_object_default"), 217 "showallvmobjects", 218 "showvmtags", 219 ("showtaskloadinfo", _arbitrary_task), 220 ("vmpagelookup", "kernel_object_default 0"), 221 ("vmpage_get_phys_page", "<vm_page_t>"), 222 ("vmpage_from_phys_page", "<ppnum_t>"), 223 "vmpage_unpack_ptr", 224 ("calcvmpagehash", "kernel_object_default 0"), 225 ("showallocatedzoneelement", "<address of zone>"), # TODO: ? 226 ("scan_vm_pages", '-A -N 1'), 227 ("vmobjectwalkpages", "kernel_object_default"), 228 "show_all_apple_protect_pagers", 229 ("show_apple_protect_pager", lambda session: session.exec('show_all_apple_protect_pagers').split('\n')[1].strip().split()[0]), # no protected pager :\ 230 "show_all_shared_region_pagers", 231 ("show_shared_region_pager", lambda session: session.exec('show_all_shared_region_pagers').split('\n')[2].strip().split()[1]), 232 ("show_all_dyld_pagers", None, [pytest.mark.xfail(reason='rdar://139146013')]), 233 ("show_dyld_pager", lambda session: session.exec('show_all_dyld_pagers').split('\n')[2].strip().split()[1]), 234 "show_console_ring", 235 "showjetsamsnapshot", 236 ("showjetsamband", "0"), 237 "showvnodecleanblk", 238 "showvnodedirtyblk", 239 ("vm_page_lookup_in_map", "<map> <vaddr>"), # TODO: ? 240 ("vm_page_lookup_in_object", "<object> <offset>"), # TODO: ? 241 ("vm_page_lookup_in_compressor_pager", "<pager> <offset>"), # TODO: ? 242 ("vm_page_lookup_in_compressor", "<slot>"), # TODO: ? 243 244 # vm_pageout.py 245 "showvmpageoutqueues", 246 "showvmpageoutstats", 247 "showvmpageouthistory", 248 249 # taskinfo.py 250 'showmemorystatus', 251 ('showtasksuspendsources', _arbitrary_task), 252 ('showtasksuspendstats', _arbitrary_task), 253 254 # TODO: understand why it works only on live VMs, in the meantime moved to TOO_LONG. 255 # "show_all_vm_named_entries", 256 # ("show_vm_named_entry", lambda session: session.exec('show_all_vm_named_entries').split('\n')[4].strip().split()[0]), 257 258 ("showmaprb", "<vm_map>"), # TODO: ? 259 "show_all_owned_objects", 260 ("show_task_owned_objects", lambda session: session.exec('show_all_owned_objects').split('\n')[3].split()[0]), 261 "showdeviceinfo", 262 "showdiagmemthresholds", 263 ("showbankaccountstopay", lambda session: session.exec('showallbanktasklist').split('\n')[1].strip().split()[0]), 264 ("showbankaccountstocharge", lambda session: session.exec('showallbanktasklist').split('\n')[1].strip().split()[0]), 265 "showallbanktasklist", 266 "showallbankaccountlist", 267 "showwaitq", 268 "showglobalwaitqs", 269 "showglobalqstats", 270 ("sendcore", "127.0.0.1"), # TODO: reconsider using generic kdumpd server. 271 ("sendsyslog", "127.0.0.1"), # TODO: reconsider using generic kdumpd server. 272 ("sendpaniclog", "127.0.0.1"), # TODO: reconsider using generic kdumpd server. 273 "disablecore", 274 "resume_on", 275 "resume_off", 276 "getdumpinfo", 277 ("kdp-reenter", "0"), 278 "kdp-reboot", 279 ("setdumpinfo", '"" "" "" 0'), # do not change anything. 280 "kdpmode", 281 "showallclasses", 282 ("showobject", "<OSOObject *>"), # TODO: ? 283 ("dumpobject", "<OSOObject *>"), # TODO: same as `showobject`. 284 ("setregistryplane", "0"), 285 ("showregistryentry", lambda session: re.search(r'<object (0x[0-9a-f]+),', session.exec('showregistry').split('\n')[3].strip()).group(1)), 286 "showregistry", 287 ("findregistryentry", "VMAC400AP"), # TODO: reconsider, might change between VM and non-VM. 288 ("findregistryentries", "AppleHWAccess"), 289 ("findregistryprop", lambda session: f"{re.search(r'<object (0x[0-9a-f]+),', session.exec('findregistryentries AppleHWAccess')).group(1)} IOSleepSupported"), 290 ("readioport8", "0"), 291 ("readioport16", "0"), 292 ("readioport32", "0"), 293 ("writeioport8", "0 0"), 294 ("writeioport16", "0 0"), 295 ("writeioport32", "0 0"), 296 ("showioservicepm", "<IOServicePM *>"), # TODO: ? 297 ("showiopmqueues", None, [pytest.mark.xfail(reason='rdar://136151068')]), 298 ("showiopminterest", "<IOService *>"), # TODO: ? 299 "showinterruptvectors", 300 ("showiokitclasshierarchy", "<class?>"), # TODO: ? 301 "showinterruptcounts", 302 "showinterruptstats", 303 "showpreoslog", 304 ("showeventsources", "<IOWorkLoop *>"), # TODO: ? 305 "showcarveouts", 306 ("showipc", _arbitrary_ipc), 307 ("showtaskipc", _arbitrary_task), 308 "showallipc", 309 "showipcsummary", 310 ("showrights", _arbitrary_ipc), 311 ("showtaskrights", functools.partial(__arbitrary_ipc_line, index=0)), 312 ("countallvouchers", None, [pytest.mark.xfail(reason='rdar://136138236')]), 313 ("showtaskrightsbt", functools.partial(__arbitrary_ipc_line, index=0)), 314 ("findportrights", "<ipc_port_t *>"), # TODO: ? 315 "showpipestats", 316 ("showtaskbusyports", _arbitrary_task), 317 ("findkobjectport", "<kobject-addr>"), # TODO: ? 318 ("showtaskbusypsets", _arbitrary_task), 319 "showallbusypsets", 320 "showallpsets", 321 ("showbusyportsummary", None, [pytest.mark.xfail(reason='rdar://136138456')]), 322 ("showport", lambda session: session.exec('showallports').split('\n')[1].split()[0]), 323 ("showpset", lambda session: session.exec('showallpsets').split('\n')[1].split()[0]), 324 ("showkmsg", "<ipc_kmsg_t"), # TODO: ? 325 "showalliits", 326 ("showallimportancetasks", None, [pytest.mark.xfail(reason='fails on MTE enabled machines -> rdar://136151386')]), 327 ("showipcimportance", lambda session: session.exec('showallimportancetasks').split('\n')[1].strip().split()[0]), 328 ("showivac", lambda session: session.exec('showglobalvouchertable').split('\n')[1].strip().split()[2]), 329 "showglobalvouchertable", 330 ("showivacfreelist", lambda session: session.exec('showglobalvouchertable').split('\n')[1].strip().split()[2]), 331 "showallvouchers", 332 ("showvoucher", lambda session: session.exec('showallvouchers').split('\n')[1].strip().split()[0]), 333 ("showtasksuspenders", _arbitrary_task), # TODO: find a way to get a suspend task. 334 ("showmqueue", "<struct ipc_mqueue *>"), # TODO: ? 335 ("readphys", "1 1337"), 336 ("writephys", "1 1337 0"), 337 ("pmap_walk", "<pmap_t> <virtual offset>"), # TODO: ? 338 ("ttep_walk", "<root_ttep> <virtual offset>"), # TODO: ? 339 ("decode_tte", "1 1"), 340 ("pv_walk", ""), # TODO? 341 ("kvtophys", "<kernel virtual address>"), # TODO: ? 342 ("phystokv", "0"), # TODO: ? 343 ("phystofte", "<physical address>"), # TODO: ? 344 ("showpte", "<pte_va>"), # TODO: ? 345 ("pv_check", "<pte>/<physical address>"), # TODO: ? 346 ("pmapsforledger", "0"), 347 ("pmappaindex", "<pai>/<physical address>"), # TODO: ? 348 "mbuf_stat", 349 "mbuf_decode", 350 ("mbuf_dumpdata", _arbitrary_mbuf), 351 ("mbuf_walkpkt", _arbitrary_mbuf), 352 ("mbuf_walk", _arbitrary_mbuf), 353 ("mbuf_buf2slab", _arbitrary_mbuf), 354 ("mbuf_buf2mca", _arbitrary_mbuf), 355 ("mbuf_slabs", lambda session: session.exec('mbuf_slabstbl').split('\n')[3].strip().split()[0]), 356 "mbuf_slabstbl", 357 "mbuf_walk_slabs", 358 ("mbuf_show_m_flags", _arbitrary_mbuf), 359 ("mbuf_showpktcrumbs", _arbitrary_mbuf), 360 "mbuf_showactive", 361 "mbuf_showinactive", 362 "mbuf_show_type_summary", 363 "mbuf_showmca", 364 "mbuf_showall", 365 ("mbuf_countchain", _arbitrary_mbuf), 366 "mbuf_topleak", 367 "mbuf_largefailures", 368 ("mbuf_traceleak", "<mtrace *>"), # TODO: ? 369 ("mcache_walkobj", "<mcache_obj_t *>"), # TODO: ? 370 "mcache_stat", 371 "mcache_showcache", 372 "mbuf_wdlog", 373 ("net_get_always_on_pktap", None, [pytest.mark.xfail(reason='fails on live vm coredump - rdar://136270822')]), 374 "ifconfig_dlil", 375 "showifaddrs", 376 "ifconfig", 377 "showifnets", 378 "showdetachingifnets", 379 "showorderedifnets", 380 "showifmultiaddrs", 381 "showinmultiaddrs", 382 "showin6multiaddrs", 383 "showsocket", 384 "showprocsockets", 385 "showallprocsockets", 386 "show_rt_inet", 387 "show_rt_inet6", 388 "rtentry_showdbg", 389 "inm_showdbg", 390 "ifma_showdbg", 391 "ifpref_showdbg", 392 "ndpr_showdbg", 393 "nddr_showdbg", 394 "imo_showdbg", 395 "im6o_showdbg", 396 "rtentry_trash", 397 ("show_rtentry", "<rtentry *>"), # TODO: ? 398 "inm_trash", 399 "in6m_trash", 400 "ifma_trash", 401 "show_socket_sb_mbuf_usage", 402 ("mbuf_list_usage_summary", "<struct mbuf *>"), # TODO: ? 403 "show_kern_event_pcbinfo", 404 "show_kern_control_pcbinfo", 405 "show_unix_domain_pcbinfo", 406 "show_tcp_pcbinfo", 407 "show_udp_pcbinfo", 408 "show_rip_pcbinfo", 409 "show_mptcp_pcbinfo", 410 "show_domains", 411 ("tcp_count_rxt_segments", "<tcpcb *>"), # TODO: ? 412 ("tcp_walk_rxt_segments", "<tcpcb *>"), # TODO: ? 413 ("showprocchannels", functools.partial(__arbitrary_ipc_line, index=5)), 414 ("showchannelrings", _arbitrary_proc_channel), 415 "showskmemcache", 416 "showskmemslab", 417 "showskmemarena", 418 "showskmemregions", 419 "showskmemregion", 420 "showchannelupphash", 421 "shownetns", 422 "showallnetnstokens", 423 "shownetnstokens", 424 "shownexuschannels", 425 ("showprocnecp", "<proc_t>"), # TODO: ? 426 "shownexuses", 427 "showflowswitches", 428 ("showcuckoohashtable", "<struct cuckoo_hashtable *>"), # TODO: ? 429 "showprotons", 430 ("showthreaduserstack", "<thread_ptr>"), # TODO: ? 431 ("printuserdata", " <task_t> 0 b"), # TODO: ? 432 ("showtaskuserargs", "<task_t>"), # TODO: ? 433 ("showtaskuserstacks", "-F init", [pytest.mark.xfail(reason='fails on MTE enabled machines - rdar://136151909')]), 434 ("showtaskuserlibraries", "<task_t>"), # TODO: ? 435 ("showtaskuserdyldinfo", "<task_t>"), # TODO: ? 436 ("savekcdata", " <kcdata_descriptor_t>"), # TODO: ? 437 ("pci_cfg_read", "<bits=8,16,32> <bus> <device> <function> <offset>"), # TODO: ?? 438 ("pci_cfg_write", "<bits=8,16,32> <bus> <device> <function> <offset> <value>"), # TODO: ?? 439 ("pci_cfg_dump", "0 0 0", [pytest.mark.xfail(reason='fails on MTE enabled machines - rdar://136198241')]), 440 "pci_cfg_scan", 441 "showallprocrunqcount", 442 "showinterrupts", 443 ("showactiveinterrupts", "<AppleInterruptController *>"), # TODO: ? 444 "showirqbyipitimerratio", 445 "showinterruptsourceinfo", 446 "showcurrentabstime", 447 ("showschedclutch", "<processor_set_t>"), # TODO: ? 448 ("showschedclutchroot", "<struct sched_clutch_root *>"), # TODO: ? 449 ("showschedclutchrootbucket", "<struct sched_clutch_root *>"), # TODO: ? 450 ("showschedclutchbucket", "<struct sched_clutch_bucket *>"), # TODO: ? 451 ("abs2nano", "1337"), 452 "showschedhistory", 453 ("showrunq", "<struct run_queue *>"), # TODO: 454 "showscheduler", 455 "showallprocessors", 456 ("showwqthread", lambda session: session.exec(f'showprocworkqueue {_arbitrary_proc(session, pid=1)}').strip().split('\n')[-1].strip().split()[0], 457 pytest.mark.xfail(reason='rdar://136138760')), 458 ("showprocworkqueue", functools.partial(_arbitrary_proc, pid=1)), 459 "showallworkqueues", 460 "showknote", 461 "showkqfile", 462 "showkqworkq", 463 "showkqworkloop", 464 "showkqueue", 465 "showprocworkqkqueue", 466 "showprockqueues", 467 "showprocknotes", 468 "showallkqueues", 469 "showkqueuecounts", 470 ("showcalloutgroup", "threads", [pytest.mark.xfail(reason='fails on MTE enabled machines - rdar://136198396')]), 471 # ("showcalloutgroup", "<struct thread_call_group *>"), # TODO: session.exec('showallcallouts').stdout.readline().strip().split()[-1][1:-1] 472 ("showallcallouts", None, [pytest.mark.xfail(reason='rdar://136033401')]), 473 ("recount", "task -F init"), # TODO: consider `thread`, `coalition` and `processor`. 474 "showmcastate", 475 "longtermtimers", 476 "processortimers", 477 "showcpudata", 478 "showtimerwakeupstats", 479 "showrunningtimers", 480 ("readmsr64", "0"), 481 ("writemsr64", "0 0"), # does not work without kdp. 482 ("q_iterate", "<struct queue_entry *> '<element type>' <field name>"), # TODO: ? 483 "lbrbt", 484 ("lapic_read32", "0"), # TODO: validate on Intel 64-bit architecture. 485 ("lapic_write32", "0 0"), # TODO: validate on Intel 64-bit architecture. 486 "lapic_dump", 487 ("ioapic_read32", "0"), # TODO: validate on Intel 64-bit architecture. 488 ("ioapic_write32", "0 0"), # TODO: validate on Intel 64-bit architecture. 489 "ioapic_dump", 490 ("showstructpacking", "showstructpacking pollfd"), 491 ("showallipcimportance", None, [pytest.mark.xfail(reason='fails on MTE enabled machines -> rdar://136151386')]), 492 ("showturnstile", lambda session: session.exec('showallturnstiles').split('\n')[1].strip().split()[0]), 493 "showturnstilehashtable", 494 "showallturnstiles", 495 "showallbusyturnstiles", 496 "showthreadbaseturnstiles", 497 "showthreadschedturnstiles", 498 "kasan", 499 "showkdebugtypefilter", 500 "showkdebug", 501 "showktrace", 502 "showkdebugtrace", 503 ("savekdebugtrace", "/tmp/dtrace"), 504 ("xi", ""), # TODO: ?? 505 ("newbt", ""), # TODO: ?? 506 "parseLR", 507 ("parseLRfromfile", ""), # TODO: ? 508 "showallulocks", 509 "showallntstat", 510 "zonetriage", 511 "zonetriage_freedelement", 512 "zonetriage_memoryleak", 513 ("decode_sysreg", "esr_el1 0x96000021"), 514 ("showcounter", "<scalable_counter_t>"), # TODO: ? 515 ("showosrefgrp", lambda session: session.exec('showglobaltaskrefgrps').split('\n')[1].strip().split()[0]), 516 ("showosrefgrphierarchy",lambda session: session.exec('showglobaltaskrefgrps').split('\n')[1].strip().split()[0]), 517 "showglobaltaskrefgrps", 518 ("showtaskrefgrps", "<task *>"), # TODO: ? 519 "showallworkloadconfig", # always empty :\ 520 ("showworkloadconfig", lambda session: session.exec('showallworkloadconfig').split('\n')[1].strip().split()[0]), 521 ("showworkloadconfigphases", lambda session: session.exec('showallworkloadconfig').split('\n')[1].strip().split()[0]), 522 "showlogstream", 523 "showlq", 524 ("showmsgbuf", "<struct msgbuf *>"), # TODO: ? 525 "systemlog", 526 "shownvram", 527 "showallconclaves", 528 "showexclavesresourcetable", 529 ("showesynctable", "<ht_t/ht_t *>"), # TODO: ? 530] 531 532TOO_LONG = [ 533 "showallcsblobs", 534 "triagecsblobmemory", 535 "showallvnodes", 536 "showallbusyvnodes", 537 "pci_cfg_dump_all", 538 "check_pmaps", 539 "showallmappings", # may take up to 30 minutes! 540 "showptusage", 541 "show_all_vm_named_entries", 542 "show_vm_named_entry", 543 "showallvme", 544 "vm_scan_all_pages", 545 "showallrights", 546 "showallbusyports", # rdar://136138456 547 "showallports", 548 "countallports", # rdar://136138236 549 "showregistryprops", 550 "showportsendrights", # rdar://136138614 551 "showthreadswaitingforuserserver", # Stuck forever on some devices. 552] 553 554INTERACTIVE = [ 555 "beginusertaskdebugging" 556] 557 558OBSOLETE = [ 559 "showkerneldebugbuffercpu", 560 "showkerneldebugbuffer", 561 "dumprawtracefile" 562] 563IGNORES = TOO_LONG + INTERACTIVE + OBSOLETE 564 565 566 567@pytest.fixture(scope='session') 568def lldb_gdb_session(pytestconfig: pytest.Config) -> AtDeskLLDBGdbSession: 569 if pytestconfig.getoption('--use-existing-debugger'): 570 session = AtDeskLLDBGdbSession(lldb.debugger.GetCommandInterpreter()) 571 session.refresh() 572 yield session 573 return 574 575 with AtDeskLLDBGdbSession.create(pytestconfig.getoption('--gdb-remote')) as session: 576 yield session 577 578 579@pytest.fixture(scope='session') 580def ignores(pytestconfig: pytest.Config) -> set[str]: 581 return set(IGNORES) | set(pytestconfig.getoption('--extra-ignores').split(',')) 582 583 584@pytest.fixture 585def _skip_if_dirty(request: pytest.FixtureRequest) -> None: 586 if request.session.stash.get('dirty', False): 587 pytest.skip('LLDB session is "dirty", skipping.') 588 589 590@pytest.fixture 591def _timed_action(_skip_if_dirty: None, request: pytest.FixtureRequest) -> typing.Callable[[float], typing.ContextManager[None]]: 592 def kill_session(): 593 request.session.stash['dirty'] = True 594 os.kill(os.getpid(), signal.SIGTERM) 595 596 @contextlib.contextmanager 597 def timed_context(timeout: float) -> None: 598 timed_out = False 599 600 def fail(*_): 601 nonlocal timed_out 602 timed_out = True 603 604 signal.signal(signalnum=signal.SIGTERM, handler=fail) 605 606 timer = threading.Timer(interval=timeout, function=kill_session) 607 timer.start() 608 try: 609 yield 610 finally: 611 timer.cancel() 612 signal.signal(signal.SIGTERM, signal.SIG_DFL) # unmask 613 614 if timed_out: 615 pytest.fail('The LLDB session is stuck, self-destructing.') 616 617 return timed_context 618 619 620MACROS = [(i, None, []) if isinstance(i, str) else (i if len(i) == 3 else (*i, [])) for i in MACROS] 621 622 623@pytest.mark.parametrize('macro', [pytest.param(i[:-1], marks=i[-1], id=i[0]) for i in MACROS]) 624def test_macro_exec(_timed_action: typing.Callable[[float], typing.ContextManager[None]], 625 macro: tuple[str, typing.Optional[str], typing.Optional[list[pytest.Mark]]], 626 lldb_gdb_session: AtDeskLLDBGdbSession) -> None: 627 macro, args_or_func = macro 628 if hasattr(args_or_func, '__call__'): 629 try: 630 macro = f'{macro} {args_or_func(lldb_gdb_session)}' 631 except Exception: 632 pytest.skip('Unable to evaluate additional argument(s).') 633 elif args_or_func is not None: 634 if args_or_func == '' or '<' in args_or_func: 635 pytest.skip('The test is not implemented, skipping.') 636 macro = f'{macro} {args_or_func}' 637 638 with _timed_action(timeout=120.): 639 lldb_gdb_session.exec(macro) 640 641 642def test_macro_coverage(lldb_gdb_session: AtDeskLLDBGdbSession, ignores: set[str]) -> None: 643 stdout = lldb_gdb_session.exec('help') 644 _start_after = 'Current user-defined commands:' 645 _end_before = "For more information on any command, type 'help <command-name>'." 646 print(f'{stdout=}') 647 user_defined_macros = stdout[stdout.find(_start_after) + len(_start_after): stdout.find(_end_before)] 648 print(f'{user_defined_macros}') 649 macro_extractor = re.compile(r'(\w+-\w+|\w+)\s+--\s') 650 651 covered = {i[0].split()[0] # All subcommands are aggregated under a single command (see `help` command). 652 for i in MACROS} | ignores 653 macros = set() 654 for line in user_defined_macros.split('\n'): 655 if len(line.strip()) == 0: 656 continue 657 match = macro_extractor.search(line) 658 if match is None: 659 continue 660 macros.add(match.group(1)) 661 print('macros', macros) 662 print('covered', covered) 663 # macros = {i for i in macros if not i.startswith('_')} 664 assert macros == covered, f'not_covered_by_tests=`{macros - covered}`, missing_macros=`{covered - macros}`' 665 # assert not_covered == [] 666 667 668def macro_exec(debugger, command, result, internal_dict) -> None: 669 """Run tests for all LLDB macros (except for IGNORES, see above). 670 671 Usage: 672 (lldb) test_macro_exec 673 -> Test all macros 674 675 (lldb) test_macro_exec [macro_1] [macro 2] ... 676 -> Test specific macro(s). 677 """ 678 no_color = '--no-color' in command 679 if no_color: 680 command = command.replace('--no-color', '').strip() 681 682 abs_file = os.path.abspath(__file__) 683 abs_root = os.path.dirname(abs_file) 684 os.chdir(abs_root) 685 686 result.ret = pytest.main( 687 (['--color', 'no'] if no_color else []) + 688 ['--disable-warnings', '--rootdir', abs_root, 689 f'--durations={len(MACROS)}', # It is the maximal number of test cases. 690 '-s' if len(command.strip()) > 0 else '-vv', 691 '-k', macro_exec.__name__, abs_file, '--use-existing-debugger', 692 ] + [f'{abs_file}::{test_macro_exec.__name__}[{i}]' 693 # Supporting multi-word commands, e.g.: sub-command, options, anything between "" / '' / a single word. 694 for i in re.split('\'(.+?)\'|"(.+?)"|(\\S+)\\s+|(\\S+)$', command) 695 if i and i.strip()]) 696 697 698def macro_coverage(debugger, command, result, internal_dict) -> None: 699 """Run the macro coverage test. 700 701 Usage: 702 (lldb) test_macro_coverage 703 -> Test macro coverage (see also `IGNORES` or --extra-ignores). 704 """ 705 abs_file = os.path.abspath(__file__) 706 abs_root = os.path.dirname(abs_file) 707 os.chdir(abs_root) 708 709 result.ret = pytest.main( 710 (['--color', 'no'] if '--no-color' in command else []) + [ 711 '--disable-warnings', '--rootdir', abs_root, 712 '-v', '-k', test_macro_coverage.__name__, abs_file, 713 '--use-existing-debugger', '--extra-ignores', f'hwtrace,{macro_exec.__name__},{macro_coverage.__name__}' 714 ]) 715 716 717def __lldb_init_module(debugger, internal_dict): 718 debugger.HandleCommand(f'command script add -f {macro_exec.__module__}.{macro_exec.__name__} {macro_exec.__name__}') 719 print(f'The `{macro_exec.__name__}` command has been installed and is ready to use.') 720 721 debugger.HandleCommand(f'command script add -f {macro_coverage.__module__}.{macro_coverage.__name__} {macro_coverage.__name__}') 722 print(f'The `{macro_coverage.__name__}` command has been installed and is ready to use.') 723 724 725if __name__ == '__main__': 726 remote_gdb = sys.argv[1] 727 pytest.main(['--disable-warnings', '-v', __file__, '--gdb-remote', remote_gdb]) 728