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