summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag
diff options
context:
space:
mode:
Diffstat (limited to 'tools/guest-diag')
-rw-r--r--tools/guest-diag/egl_retry.c124
-rw-r--r--tools/guest-diag/xorg-vgpu.conf31
-rw-r--r--tools/guest-diag/xshm_pub.c213
3 files changed, 368 insertions, 0 deletions
diff --git a/tools/guest-diag/egl_retry.c b/tools/guest-diag/egl_retry.c
new file mode 100644
index 0000000..ce45d45
--- /dev/null
+++ b/tools/guest-diag/egl_retry.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * egl_retry.c - EGL/GLES2 offscreen probe with an explicit eglMakeCurrent
+ * retry loop. The r581-era trace showed the glsi engine-init returning
+ * 0xee00001 ("busy") exactly once and setting the device state bit 0x20;
+ * eglcore does not retry. This probe retries the make-current (and the whole
+ * display/context creation) up to N times, printing the precise EGL error of
+ * every attempt, so we can tell "one-shot busy, retry succeeds" from
+ * "permanent failure".
+ *
+ * Build (guest): gcc -O2 -o egl_retry egl_retry.c -lEGL -lGLESv2
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+
+static EGLDisplay get_device_display(void)
+{
+ PFNEGLGETPLATFORMDISPLAYEXTPROC gp =
+ (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress(
+ "eglGetPlatformDisplayEXT");
+ PFNEGLQUERYDEVICESEXTPROC qd =
+ (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress(
+ "eglQueryDevicesEXT");
+ EGLDeviceEXT devs[8];
+ EGLint n = 0;
+
+ if (!gp || !qd || !qd(8, devs, &n) || n <= 0)
+ return EGL_NO_DISPLAY;
+ printf("EGL_DEVICES n=%d\n", n);
+ return gp(EGL_PLATFORM_DEVICE_EXT, devs[0], NULL);
+}
+
+int main(int argc, char **argv)
+{
+ int tries = argc > 1 ? atoi(argv[1]) : 10;
+ EGLDisplay dpy;
+ EGLint major = 0, minor = 0;
+ EGLConfig cfg;
+ EGLint ncfg = 0;
+ EGLSurface surf;
+ EGLContext ctx = EGL_NO_CONTEXT;
+ EGLint attrs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8, EGL_NONE
+ };
+ EGLint pb[] = { EGL_WIDTH, 64, EGL_HEIGHT, 64, EGL_NONE };
+ EGLint cattr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+ int i;
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ dpy = get_device_display();
+ if (dpy == EGL_NO_DISPLAY) {
+ printf("NO_DEVICE_DISPLAY\n");
+ return 1;
+ }
+ if (!eglInitialize(dpy, &major, &minor)) {
+ printf("EGL_INIT_FAIL 0x%x\n", eglGetError());
+ return 1;
+ }
+ printf("EGL_INIT ok %d.%d vendor=%s\n", major, minor,
+ eglQueryString(dpy, EGL_VENDOR));
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ printf("BIND_API_FAIL 0x%x\n", eglGetError());
+ return 1;
+ }
+ if (!eglChooseConfig(dpy, attrs, &cfg, 1, &ncfg) || ncfg < 1) {
+ printf("CHOOSE_CONFIG_FAIL 0x%x n=%d\n", eglGetError(), ncfg);
+ return 1;
+ }
+ for (i = 0; i < tries; i++) {
+ surf = eglCreatePbufferSurface(dpy, cfg, pb);
+ if (surf == EGL_NO_SURFACE) {
+ printf("TRY %d CREATE_SURFACE_FAIL 0x%x\n", i + 1,
+ eglGetError());
+ usleep(200000);
+ continue;
+ }
+ if (ctx == EGL_NO_CONTEXT) {
+ ctx = eglCreateContext(dpy, cfg, EGL_NO_CONTEXT,
+ cattr);
+ if (ctx == EGL_NO_CONTEXT) {
+ printf("TRY %d CREATE_CTX_FAIL 0x%x\n", i + 1,
+ eglGetError());
+ eglDestroySurface(dpy, surf);
+ usleep(200000);
+ continue;
+ }
+ }
+ if (eglMakeCurrent(dpy, surf, surf, ctx)) {
+ const char *rend = (const char *)glGetString(
+ GL_RENDERER);
+ const char *ver = (const char *)glGetString(
+ GL_VERSION);
+ printf("TRY %d CURRENT OK renderer=%s version=%s\n",
+ i + 1, rend ? rend : "?", ver ? ver : "?");
+ /* draw one clear + readback so the pipeline runs */
+ glClearColor(0.1f, 0.4f, 0.9f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ {
+ unsigned char px[4];
+ glReadPixels(0, 0, 1, 1, GL_RGBA,
+ GL_UNSIGNED_BYTE, px);
+ printf("PIXEL %02x%02x%02x%02x\n", px[0],
+ px[1], px[2], px[3]);
+ }
+ printf("EGL_RETRY_OK on try %d\n", i + 1);
+ return 0;
+ }
+ printf("TRY %d MAKE_CURRENT_FAIL 0x%x\n", i + 1,
+ eglGetError());
+ eglDestroySurface(dpy, surf);
+ usleep(200000);
+ }
+ printf("EGL_RETRY_EXHAUSTED tries=%d\n", tries);
+ return 1;
+} \ No newline at end of file
diff --git a/tools/guest-diag/xorg-vgpu.conf b/tools/guest-diag/xorg-vgpu.conf
new file mode 100644
index 0000000..4d4c07d
--- /dev/null
+++ b/tools/guest-diag/xorg-vgpu.conf
@@ -0,0 +1,31 @@
+# Minimal Xorg config for the emulated vGPU (nvidia driver), headless.
+# AllowEmptyInitialConfiguration lets the X server start with no connected
+# display; UseDisplayDevice "None" keeps the server from demanding one.
+Section "ServerLayout"
+ Identifier "Layout0"
+ Screen 0 "Screen0"
+EndSection
+
+Section "Device"
+ Identifier "Device0"
+ Driver "nvidia"
+ BusID "PCI:1:0:0"
+ Option "AllowEmptyInitialConfiguration" "True"
+ Option "UseDisplayDevice" "None"
+ Option "ConnectToAcpid" "False"
+EndSection
+
+Section "Screen"
+ Identifier "Screen0"
+ Device "Device0"
+ DefaultDepth 24
+ SubSection "Display"
+ Depth 24
+ Virtual 1280 720
+ EndSubSection
+EndSection
+
+Section "ServerFlags"
+ Option "AutoAddDevices" "False"
+ Option "AutoEnableDevices" "False"
+EndSection \ No newline at end of file
diff --git a/tools/guest-diag/xshm_pub.c b/tools/guest-diag/xshm_pub.c
new file mode 100644
index 0000000..98fb539
--- /dev/null
+++ b/tools/guest-diag/xshm_pub.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * xshm_pub.c - capture an X11 screen and publish frames into the kvmfr
+ * ivshmem BAR so the host can display them (the Looking-Glass-style path:
+ * the guest renders with its real graphics stack - X on the passed GPU /
+ * GLX / Vulkan surface - and this tool copies the framebuffer into the
+ * shared BAR; the host reader decodes it).
+ *
+ * Layout in the BAR (same as tools/cuda-ab/frame_producer.cu):
+ * offset 0x100000: struct frame_hdr { magic, version, width, height,
+ * stride, format, seq } then width*height*4 RGBA8 bytes.
+ *
+ * Usage (guest, with DISPLAY set):
+ * ./xshm_pub [frames] [fps] [x] [y] [w] [h]
+ * Prints FRAME lines (first 5 and every 30th) then FRAME_DONE.
+ * Build (host, same glibc): gcc -O2 -o xshm_pub xshm_pub.c -lX11 -lXext
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#define FRAME_OFFSET 0x100000UL
+#define HDR_MAGIC 0x4b564d46u /* "FMVK" */
+
+struct frame_hdr {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uint32_t format; /* 0 = RGBA8 */
+ uint32_t seq;
+ uint32_t reserved;
+ uint64_t timestamp_ns;
+};
+
+static uint64_t now_ns(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (uint64_t)ts.tv_sec * 1000000000ull + ts.tv_nsec;
+}
+
+int main(int argc, char **argv)
+{
+ int frames = argc > 1 ? atoi(argv[1]) : 120;
+ int fps = argc > 2 ? atoi(argv[2]) : 10;
+ int gx = argc > 3 ? atoi(argv[3]) : 0;
+ int gy = argc > 4 ? atoi(argv[4]) : 0;
+ int gw = argc > 5 ? atoi(argv[5]) : 0;
+ int gh = argc > 6 ? atoi(argv[6]) : 0;
+ const char *bar_path =
+ "/sys/bus/pci/devices/0000:00:03.0/resource2";
+ Display *dpy;
+ Window root;
+ XImage *img = NULL;
+ XShmSegmentInfo shm;
+ int use_shm = 1, fd, i, w, h;
+ unsigned char *bar;
+ struct frame_hdr *hdr;
+ volatile uint32_t *seqp;
+ uint64_t t_first = 0;
+ uint64_t t_last = 0;
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ dpy = XOpenDisplay(NULL);
+ if (!dpy) {
+ printf("XOPEN_FAIL DISPLAY=%s\n",
+ getenv("DISPLAY") ? getenv("DISPLAY") : "(unset)");
+ return 1;
+ }
+ root = DefaultRootWindow(dpy);
+ {
+ XWindowAttributes wa;
+
+ XGetWindowAttributes(dpy, root, &wa);
+ w = gw > 0 ? gw : wa.width;
+ h = gh > 0 ? gh : wa.height;
+ printf("XSCREEN %dx%d root=%dx%d\n", w, h, wa.width,
+ wa.height);
+ }
+ fd = open(bar_path, O_RDWR | O_SYNC);
+ if (fd < 0) {
+ printf("OPEN_FAIL %s\n", bar_path);
+ return 1;
+ }
+ bar = (unsigned char *)mmap(NULL, 128UL << 20, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (bar == MAP_FAILED) {
+ printf("MMAP_FAIL\n");
+ return 1;
+ }
+ hdr = (struct frame_hdr *)(bar + FRAME_OFFSET);
+ seqp = &hdr->seq;
+
+ memset(&shm, 0, sizeof(shm));
+ if (use_shm) {
+ img = XShmCreateImage(dpy, DefaultVisual(dpy,
+ DefaultScreen(dpy)),
+ DefaultDepth(dpy, DefaultScreen(dpy)),
+ ZPixmap, NULL, &shm, w, h);
+ if (!img) {
+ printf("XSHM_CREATE_FAIL\n");
+ return 1;
+ }
+ shm.shmid = shmget(IPC_PRIVATE,
+ (size_t)img->bytes_per_line * h,
+ IPC_CREAT | 0600);
+ if (shm.shmid < 0) {
+ printf("SHMGET_FAIL\n");
+ return 1;
+ }
+ shm.shmaddr = img->data = shmat(shm.shmid, NULL, 0);
+ if (shm.shmaddr == (char *)-1) {
+ printf("SHMAT_FAIL\n");
+ return 1;
+ }
+ shm.readOnly = False;
+ if (!XShmAttach(dpy, &shm)) {
+ printf("XSHM_ATTACH_FAIL (falling back)\n");
+ XDestroyImage(img);
+ img = NULL;
+ } else {
+ XSync(dpy, False);
+ printf("XSHM OK %dx%d bpl=%d\n", w, h,
+ img->bytes_per_line);
+ }
+ }
+ if (!img) {
+ img = XGetImage(dpy, root, gx, gy, w, h, AllPlanes, ZPixmap);
+ if (!img) {
+ printf("XGETIMAGE_FAIL\n");
+ return 1;
+ }
+ printf("XGETIMAGE OK %dx%d bpl=%d\n", w, h,
+ img->bytes_per_line);
+ }
+
+ hdr->magic = HDR_MAGIC;
+ hdr->version = 1;
+ hdr->width = (uint32_t)w;
+ hdr->height = (uint32_t)h;
+ hdr->stride = (uint32_t)w * 4;
+ hdr->format = 0;
+
+ for (i = 0; i < frames; i++) {
+ uint64_t t0 = now_ns();
+ unsigned char *dst = bar + FRAME_OFFSET +
+ sizeof(struct frame_hdr);
+ int x, y;
+
+ if (shm.shmaddr) {
+ XShmGetImage(dpy, root, img, gx, gy, AllPlanes);
+ } else {
+ XImage *n = XGetImage(dpy, root, gx, gy, w, h,
+ AllPlanes, ZPixmap);
+
+ if (!n)
+ break;
+ XDestroyImage(img);
+ img = n;
+ }
+ /* X ZPixmap: 0x00RRGGBB little-endian -> RGBA bytes */
+ for (y = 0; y < h; y++) {
+ unsigned char *s = (unsigned char *)img->data +
+ (size_t)y * img->bytes_per_line;
+ unsigned char *d = dst + (size_t)y * w * 4;
+
+ for (x = 0; x < w; x++) {
+ uint32_t p;
+
+ memcpy(&p, s + 4 * x, 4);
+ d[4 * x + 0] = (unsigned char)(p >> 16);
+ d[4 * x + 1] = (unsigned char)(p >> 8);
+ d[4 * x + 2] = (unsigned char)(p);
+ d[4 * x + 3] = 0xff;
+ }
+ }
+ hdr->timestamp_ns = now_ns();
+ if (!t_first)
+ t_first = t0;
+ t_last = t0;
+ __sync_synchronize();
+ *seqp = (uint32_t)(i + 1);
+ __sync_synchronize();
+ if (i < 5 || (i % 30) == 29)
+ printf("FRAME seq=%d ms=%.1f\n", i + 1,
+ (double)(now_ns() - t0) / 1e6);
+ if (fps > 0)
+ usleep((useconds_t)(1000000 / fps));
+ }
+ if (t_first && t_last > t_first)
+ printf("XPUB_RATE fps=%.2f frames=%d\n",
+ (double)(i - 1) * 1e9 /
+ (double)(t_last - t_first), i);
+ printf("FRAME_DONE frames=%d %dx%d\n", i, w, h);
+ if (shm.shmaddr)
+ XShmDetach(dpy, &shm);
+ XCloseDisplay(dpy);
+ return 0;
+} \ No newline at end of file