diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/guest-diag/guest_pin_stream.py | 3 | ||||
| -rw-r--r-- | tools/guest-diag/pin_gr_stream.py | 29 | ||||
| -rwxr-xr-x | tools/guest-diag/pin_supervisor.sh | 9 | ||||
| -rw-r--r-- | tools/guest-diag/xfocus.c | 69 | ||||
| -rw-r--r-- | tools/guest-diag/xgrab.c | 26 | ||||
| -rw-r--r-- | tools/guest-diag/xtest_move.c | 171 |
6 files changed, 216 insertions, 91 deletions
diff --git a/tools/guest-diag/guest_pin_stream.py b/tools/guest-diag/guest_pin_stream.py index 8768f2f..3db61ba 100644 --- a/tools/guest-diag/guest_pin_stream.py +++ b/tools/guest-diag/guest_pin_stream.py @@ -1428,7 +1428,8 @@ def pid_line(tag, pid, start): def cdt_pid(): prefixes = tuple(p for p in os.environ.get("CLIENT_PREFIXES", - "cdt,va,victory").split(",") if p) + "cdt,va,victory,Xorg,X,glxgears," + "gzdoom").split(",") if p) # Processes that hold a /dev/nvidia* fd but are not CUDA clients; the # scanner must never commit to them (a transient nvidia-smi at boot was # picked once and its exited pagemap read back EOF forever). diff --git a/tools/guest-diag/pin_gr_stream.py b/tools/guest-diag/pin_gr_stream.py index ff7e299..de2e706 100644 --- a/tools/guest-diag/pin_gr_stream.py +++ b/tools/guest-diag/pin_gr_stream.py @@ -136,7 +136,8 @@ def _start_cmd(): b64 = subprocess.run(["base64", "-w0"], stdin=f, capture_output=True, text=True, timeout=10).stdout - prefixes = os.environ.get("CLIENT_PREFIXES", "cdt,va,victory") + prefixes = os.environ.get("CLIENT_PREFIXES", + "cdt,va,victory,Xorg,X,glxgears,gzdoom") vma_every = os.environ.get("STREAM_VMA_EVERY", "1") # Kill EVERY existing scanner, not just the pid-file one: a retry after # a partially-failed first attempt (pid file not written yet) used to @@ -219,6 +220,14 @@ def _demand_loop(): that was not yet written when first asked is picked up on a retry. This closes the stall class where the one-shot #sem kprobe event was lost and the chain resolver never learns the client. + + r908: the GR ring VA itself ('GR ring h=0x... ... (va=0x...)', the + path-B "RAM content scan found no candidate" and "not at fb" rows) is + harvested too - the ring page is a kernel allocation the userspace + pagemap cannot cover, so the guest's content resolver is the only + source, and the pump needs the ring readable to decode its entries. + Same bounded/deduped tracking (distinct VAs, 20 s resend, 300 s + expiry) as the stalled-VA class. """ track = {} # va -> [first_seen, last_sent] while True: @@ -229,9 +238,21 @@ def _demand_loop(): except Exception: continue now = time.time() - for m in re.finditer(r"(?:stalled VA #\d+[:=]\s*|pending release starved va=)(0x[0-9a-f]+)", - (r.stdout or "")[-200000:]): - va = int(m.group(1), 16) + for m in re.finditer( + r"(?:stalled VA #\d+[:=]\s*" + r"|pending release starved va=" + r"|GR ring h=0x[0-9a-f]+ rank=\d+[^\n]*?\(va=)" + r"(0x[0-9a-f]+)" + # r914: an unreadable GR push names its pushbuffer VA - + # the GL client's push page is a kernel allocation the + # userspace pagemap cannot cover, so the guest's content + # resolver is the only source (same class as the ring VA + # demand above). Harvest it so the guest resolves and + # injects the page. + r"|sema-pushdec: h=0x[0-9a-f]+ va=(0x[0-9a-f]+)" + r"[^\n]*unreadable=1", + (r.stdout or "")[-200000:]): + va = int(m.group(1) or m.group(2), 16) track.setdefault(va, [now, 0.0]) for va in list(track): first, last = track[va] diff --git a/tools/guest-diag/pin_supervisor.sh b/tools/guest-diag/pin_supervisor.sh index b87d5c1..72f588f 100755 --- a/tools/guest-diag/pin_supervisor.sh +++ b/tools/guest-diag/pin_supervisor.sh @@ -70,7 +70,14 @@ kill_tool() { start_pin() { if tool_alive "$PIN"; then return 0; fi touch "$PIN_HB" 2>/dev/null || true - ( setsid nohup env SEMFIX_COUNTER_RETRY=10 \ + # CLIENT_PREFIXES threads through pin_gr_stream.py into the guest + # scanner launch (its _start_cmd). GL clients (glxgears, gzdoom) and + # the X server must be covered or the GL client's ring/mappings are + # never pinned (SESSION 149: the GR ring scan then finds no candidate + # for h=0xbfef0002, va=0x1203cfff0). + ( setsid nohup env \ + CLIENT_PREFIXES="${CLIENT_PREFIXES:-cdt,va,victory,Xorg,X,glxgears,gzdoom}" \ + SEMFIX_COUNTER_RETRY=10 \ python3 "$PIN" >> "$PIN_LOG" 2>&1 & ) pin_started=$(date +%s) log "START pin_gr_stream" diff --git a/tools/guest-diag/xfocus.c b/tools/guest-diag/xfocus.c new file mode 100644 index 0000000..e0ec05d --- /dev/null +++ b/tools/guest-diag/xfocus.c @@ -0,0 +1,69 @@ +// xfocus.c - print the current X input focus and the gzdoom window, and try +// to focus the gzdoom window; then report again. Diagnostic for WM-less X. +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <stdio.h> +#include <string.h> + +static Window find_named(Display *d, Window root, const char *sub, + char *title, size_t tl) +{ + Window r, parent, *kids = NULL; + unsigned int nk = 0; + + title[0] = 0; + if (!XQueryTree(d, root, &r, &parent, &kids, &nk)) + return 0; + for (unsigned int i = 0; i < nk; i++) { + char *name = NULL; + + if (XFetchName(d, kids[i], &name) && name) { + if (strstr(name, sub)) { + snprintf(title, tl, "%s", name); + Window w = kids[i]; + + XFree(name); + if (kids) + XFree(kids); + return w; + } + XFree(name); + } + } + if (kids) + XFree(kids); + return 0; +} + +int main(void) +{ + Display *d = XOpenDisplay(NULL); + Window root, focus, w, croot; + int rx, ry, wx, wy; + unsigned int mask; + char title[256]; + + if (!d) { + printf("no display\n"); + return 1; + } + root = DefaultRootWindow(d); + w = find_named(d, root, "Freedoom", title, sizeof(title)); + XGetInputFocus(d, &focus, &rx); + printf("gzdoom window: 0x%lx '%s'\n", w, title); + printf("input focus: 0x%lx\n", focus); + printf("pointer window: "); + if (XQueryPointer(d, root, &croot, &rx /*reuse*/, &wx, &wy, &rx, &ry, + &mask)) + printf("0x%lx at %d,%d mask=0x%x\n", rx, wx, wy, mask); + else + printf("query failed\n"); + if (w) { + XSetInputFocus(d, w, RevertToParent, CurrentTime); + XFlush(d); + XGetInputFocus(d, &focus, &rx); + printf("after set: 0x%lx\n", focus); + } + XCloseDisplay(d); + return 0; +} diff --git a/tools/guest-diag/xgrab.c b/tools/guest-diag/xgrab.c index 3f4ec76..eb605d6 100644 --- a/tools/guest-diag/xgrab.c +++ b/tools/guest-diag/xgrab.c @@ -22,6 +22,10 @@ int main(int argc, char **argv) const char *out = argc > 2 ? argv[2] : "/dev/shm/gzframes.bin"; long maxf = argc > 3 ? atol(argv[3]) : 0; long pace = argc > 4 ? atol(argv[4]) : 0; + int r_x = argc > 5 ? atoi(argv[5]) : -1; + int r_y = argc > 6 ? atoi(argv[6]) : 0; + int r_w = argc > 7 ? atoi(argv[7]) : 0; + int r_h = argc > 8 ? atoi(argv[8]) : 0; Display *d; Window root; XWindowAttributes wa; @@ -39,8 +43,12 @@ int main(int argc, char **argv) fprintf(stderr, "xgrab: no root attrs\n"); return 1; } - fprintf(stderr, "xgrab: root %dx%d depth=%d\n", wa.width, wa.height, - wa.depth); + if (r_x < 0 || r_w <= 0 || r_h <= 0 || r_x + r_w > wa.width || + r_y + r_h > wa.height) { + r_x = 0; r_y = 0; r_w = wa.width; r_h = wa.height; + } + fprintf(stderr, "xgrab: root %dx%d depth=%d region %d,%d %dx%d\n", + wa.width, wa.height, wa.depth, r_x, r_y, r_w, r_h); f = fopen(out, "wb"); if (!f) { perror("xgrab: open out"); @@ -52,8 +60,8 @@ int main(int argc, char **argv) fflush(f); while (!maxf || (long)seq < maxf) { - XImage *im = XGetImage(d, root, 0, 0, wa.width, wa.height, - AllPlanes, ZPixmap); + XImage *im = XGetImage(d, root, r_x, r_y, r_w, r_h, AllPlanes, + ZPixmap); struct timespec ts; uint64_t tns; uint32_t rh[4]; @@ -72,17 +80,17 @@ int main(int argc, char **argv) } clock_gettime(CLOCK_MONOTONIC, &ts); tns = (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec; - rh[0] = (uint32_t)wa.width; - rh[1] = (uint32_t)wa.height; - rh[2] = (uint32_t)wa.width * 4; + rh[0] = (uint32_t)r_w; + rh[1] = (uint32_t)r_h; + rh[2] = (uint32_t)r_w * 4; rh[3] = 32; fwrite(m, 1, 4, f); fwrite(rh, 4, 4, f); fwrite(&tns, 8, 1, f); fwrite(&seq, 8, 1, f); - for (y = 0; y < wa.height; y++) + for (y = 0; y < r_h; y++) fwrite(im->data + (size_t)y * im->bytes_per_line, 1, - (size_t)wa.width * 4, f); + (size_t)r_w * 4, f); fflush(f); seq++; XDestroyImage(im); diff --git a/tools/guest-diag/xtest_move.c b/tools/guest-diag/xtest_move.c index a156bf8..8331487 100644 --- a/tools/guest-diag/xtest_move.c +++ b/tools/guest-diag/xtest_move.c @@ -1,106 +1,125 @@ // SPDX-License-Identifier: BSD-3-Clause -// xtest_move.c - drive the game with XTEST: hold forward + fire and tap -// left/right periodically so the captured footage has constant motion. +// xtest_move.c - drive the game with XSendEvent-synthetic keys (SDL2 on this +// Xorg ignores XTEST-faked keys; it accepts send_event KeyPress/KeyRelease). +// Finds the game window by name, then holds forward+fire and alternates +// turning left/right so the captured footage has constant motion. // Usage: xtest_move <display> <seconds> #include <X11/Xlib.h> #include <X11/keysym.h> +#include <X11/Xutil.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <string.h> +#include <unistd.h> + +static Display *d; +static Window game; + +static Window find_named(const char *sub) +{ + Window root = DefaultRootWindow(d), r, parent, *kids = NULL; + unsigned int nk = 0; + + if (!XQueryTree(d, root, &r, &parent, &kids, &nk)) + return 0; + for (unsigned int i = 0; i < nk; i++) { + char *name = NULL; -extern int XTestFakeKeyEvent(Display *, unsigned int, int, unsigned long); + if (XFetchName(d, kids[i], &name) && name) { + if (strstr(name, sub)) { + Window w = kids[i]; + + XFree(name); + if (kids) + XFree(kids); + return w; + } + XFree(name); + } + } + if (kids) + XFree(kids); + return 0; +} + +static void send_key(KeySym ks, int down) +{ + XEvent e; + Window root = DefaultRootWindow(d); + int rx, ry, wx, wy; + unsigned int mask; + Window cr, pr; + + if (!game) + return; + memset(&e, 0, sizeof(e)); + e.xkey.type = down ? KeyPress : KeyRelease; + e.xkey.display = d; + e.xkey.window = game; + e.xkey.root = root; + e.xkey.subwindow = None; + e.xkey.time = CurrentTime; + XQueryPointer(d, root, &cr, &pr, &rx, &ry, &wx, &wy, &mask); + e.xkey.x = wx; + e.xkey.y = wy; + e.xkey.x_root = rx; + e.xkey.y_root = ry; + e.xkey.same_screen = True; + e.xkey.keycode = XKeysymToKeycode(d, ks); + e.xkey.state = 0; + XSendEvent(d, game, True, down ? KeyPressMask : KeyReleaseMask, &e); + XFlush(d); +} int main(int argc, char **argv) { - Display *d = XOpenDisplay(argc > 1 ? argv[1] : ":1"); int secs = argc > 2 ? atoi(argv[2]) : 12; - KeyCode kFwd, kFire, kLeft, kRight; int t; + d = XOpenDisplay(argc > 1 ? argv[1] : ":1"); if (!d) { fprintf(stderr, "xtest_move: no display\n"); return 1; } - /* No WM on Xvfb: give the game window input focus ourselves. */ - { - Window r, parent, *kids = NULL; - unsigned int nk = 0; - - if (XQueryTree(d, DefaultRootWindow(d), &r, &parent, &kids, &nk)) { - unsigned int i; - - for (i = 0; i < nk; i++) { - char *nm = NULL; - - if (XFetchName(d, kids[i], &nm) && nm) { - int hit = strstr(nm, "Freedoom") || strstr(nm, "Doom") || - strstr(nm, "gzdoom") || strstr(nm, "Outer"); - - XFree(nm); - if (hit) { - XSetInputFocus(d, kids[i], RevertToParent, CurrentTime); - fprintf(stderr, "xtest_move: focused 0x%lx\n", - (unsigned long)kids[i]); - break; - } - } - } - if (kids) - XFree(kids); - } - } - kFwd = XKeysymToKeycode(d, XK_Up); - kFire = XKeysymToKeycode(d, XK_space); - kLeft = XKeysymToKeycode(d, XK_Left); - kRight = XKeysymToKeycode(d, XK_Right); - fprintf(stderr, "xtest_move: fwd=%d fire=%d left=%d right=%d\n", - kFwd, kFire, kLeft, kRight); - XTestFakeKeyEvent(d, kFwd, 1, 0); - XTestFakeKeyEvent(d, kFire, 1, 0); + game = find_named("Freedoom"); + if (!game) + game = find_named("Outer Prison"); + if (!game) + game = find_named("Doom"); + fprintf(stderr, "xtest_move: game window 0x%lx\n", game); + if (!game) + return 1; + XSetInputFocus(d, game, RevertToParent, CurrentTime); XFlush(d); - /* Continuous motion: rotate steadily (hold a turn key ~0.8 s, then - * the other) while forward+fire stay held - every frame changes. */ - for (t = 0; t < secs * 10; t++) { - KeyCode turn = (t & 8) ? kLeft : kRight; - Window wr, wpar, *wk = NULL; - unsigned int wnk = 0; - /* Re-assert focus every tick: Xvfb has no WM, and any new X client - * (the capture tool) can leave the game unfocused - GZDoom pauses - * the game the moment SDL reports focus loss. */ - if (XQueryTree(d, DefaultRootWindow(d), &wr, &wpar, &wk, &wnk)) { - unsigned int i; + /* Hold forward only (no fire: fire pulls enemies and the player can + * die mid-capture; with +noclip from the command line this is a smooth + * fly-through). */ + send_key(XK_Up, 1); - for (i = 0; i < wnk; i++) { - char *nm = NULL; + for (t = 0; t < secs * 10; t++) { + static int dir; /* 0 = left held, 1 = right held */ - if (XFetchName(d, wk[i], &nm) && nm) { - int hit = strstr(nm, "Freedoom") || strstr(nm, "Outer") || - strstr(nm, "Doom"); + XSetInputFocus(d, game, RevertToParent, CurrentTime); /* keep focus */ + /* Re-assert the held forward key: SDL's held state drops between + * synthetic events, so forward motion stalls unless re-sent. */ + if ((t % 4) == 0) + send_key(XK_Up, 1); + if ((t % 9) == 8) { + KeySym off = dir ? XK_Right : XK_Left; + KeySym on = dir ? XK_Left : XK_Right; - XFree(nm); - if (hit) { - XSetInputFocus(d, wk[i], RevertToParent, - CurrentTime); - break; - } - } - } - if (wk) - XFree(wk); + send_key(off, 0); + usleep(30000); + send_key(on, 1); + dir = !dir; } - XTestFakeKeyEvent(d, kLeft, 0, 0); - XTestFakeKeyEvent(d, kRight, 0, 0); - XTestFakeKeyEvent(d, turn, 1, 0); XFlush(d); usleep(100000); } - XTestFakeKeyEvent(d, kLeft, 0, 0); - XTestFakeKeyEvent(d, kRight, 0, 0); - XTestFakeKeyEvent(d, kFwd, 0, 0); - XTestFakeKeyEvent(d, kFire, 0, 0); - XFlush(d); + send_key(XK_Left, 0); + send_key(XK_Right, 0); + send_key(XK_Up, 0); XCloseDisplay(d); fprintf(stderr, "xtest_move: done\n"); return 0; |
