#!/usr/bin/env python3 """Why does the roster-record count differ between runs (116 vs 42)? Samples the counts repeatedly WITHIN one run, from the instant flight is detected, and also reports what the scan is actually looking at: * roster records -- vtable 0x820AF030 hits * craft -- unit-definition-pointer sites in the entity heap * definitions -- vtable 0x820AF844 hits * extents/bytes -- what gmem.extents() enumerates, to test whether the scan region itself differs between runs A count that climbs = a load race. A count flat at a run-specific value = not a race, and the cause is in the guest, not the probe. """ import os, sys, time, struct, collections sys.path.insert(0, __file__.rsplit('/', 1)[0]) import gmem, gworld, entities2 ROSTER_VT = struct.pack('>I', 0x820AF030) DEF_VT = struct.pack('>I', 0x820AF844) DELTA = 0x130 def count_vt(fd, size, vt, want_vas=False): """Raw aligned hits, and optionally how many DISTINCT primary VAs they map to. wave5 printed the latter and census the former; if an offset can alias to a VA another offset also claims, the two are different numbers and that alone would explain 42 vs 116.""" n = 0; vas = set() for a, b in gmem.extents(fd, size): pos = a while pos < b: m = min(1 << 24, b - pos) blob = os.pread(fd, m, pos) i = blob.find(vt) while i != -1: if (pos + i) % 4 == 0: n += 1 if want_vas: v = gmem.primary_va(pos + i) if v is not None: vas.add(v) i = blob.find(vt, i + 1) pos += m return (n, len(vas)) if want_vas else n def extent_stats(fd, size): n = tot = 0 for a, b in gmem.extents(fd, size): n += 1; tot += b - a return n, tot def craft_count(fd, defs): lo, hi = gmem.va_to_off(entities2.ENT_VA_LO), gmem.va_to_off(entities2.ENT_VA_HI) n, pos = 0, lo while pos < hi: m = min(1 << 24, hi - pos) blob = os.pread(fd, m, pos) for k in range(0, len(blob) - 3, 4): if blob[k:k+4] in defs: n += 1 pos += m return n def main(): secs = int(sys.argv[1]) if len(sys.argv) > 1 else 180 every = int(sys.argv[2]) if len(sys.argv) > 2 else 12 w = gworld.World(); fd = w.fd; size = w.size t0 = time.time() print('%5s %8s %8s %7s %6s %8s %10s' % ('t', 'roster', 'distVA', 'craft', 'defs', 'extents', 'MB')) while time.time() - t0 < secs: el = round(time.time() - t0) defs = entities2.definitions(w) r, rvas = count_vt(fd, size, ROSTER_VT, want_vas=True) d = count_vt(fd, size, DEF_VT) c = craft_count(fd, defs) if defs else 0 en, eb = extent_stats(fd, size) print('%5d %8d %8d %7d %6d %8d %10.1f' % (el, r, rvas, c, d, en, eb / 2**20), flush=True) time.sleep(max(0, every - (time.time() - t0 - el))) return 0 if __name__ == '__main__': sys.exit(main())