gpu-info.sh collects GPU and display diagnostics on macOS. Start with the generated gpu-report/report.md in Markdown preview for an overview, diagnostic results, and links to the supporting evidence.
This script gathers:
- System and OS information
- GPU/display details from
system_profiler - Graphics-related I/O Registry signals
- Kernel extension snapshots
- WindowServer state
- OpenGL and Metal visibility checks
- Compact CPU/memory context (
top,vm_stat) - Display-related environment variables (for X11/XQuartz context)
It is designed for quick diagnostics on physical Macs and virtualized macOS environments.
- macOS 13 Ventura (compatibility baseline)
/bin/bash(compatible with macOS's Bash 3.2)- Built-in macOS tools used when available (for example:
system_profiler,ioreg,launchctl,top,vm_stat) - Optional:
glxinfo(for X11 OpenGL probe). If unavailable or incompatible, the script records detailed diagnostics and remediation hints.
From this folder:
chmod +x gpu-info.sh
./gpu-info.shOpen gpu-report/report.md in your editor's Markdown preview. It contains the system/display snapshot, items needing attention, color-coded probe results, a compact performance section, and X11/GLX interpretation. Follow the detail links to HTML reports, or use their plain-text copies. details/summary.txt records the final file-write result.
Each report includes collection start/end timestamps with timezone offset, script version, and Git revision when available. Modified working trees are labeled. Rerun the updated script on the Mac to generate the new layout; updating this repository does not rewrite captured evidence.
Default output directory:
./gpu-report
Use a custom output directory:
./gpu-info.sh /path/to/outputAn invalid or unwritable output directory, or a failed report-file write, produces a nonzero exit status. Missing tools and failed diagnostic probes are recorded in the reports and do not prevent collection of other diagnostics. Exit status 0 means the report files were written; it does not mean the GPU or every probe is healthy.
Every run starts with a fresh output folder. Before collecting diagnostics, the script moves the entire existing output directory into a uniquely named, timestamped sibling archive. This includes old layouts, hidden files, unrelated notes, and any incomplete prior run; nothing is merged into the new report. Symlinks inside the old directory are preserved as links, not followed during archiving.
For example, a rerun produces:
gpu-report/ # new run only
gpu-report.archive-20260909T120000Z.aB3xY7/
gpu-report/ # complete previous folder
The timestamp is UTC and marks when the archive was created, not when the old data was collected. The random suffix prevents collisions, including runs started within the same second. Keeping the previous folder intact preserves its internal relative report links. The archive location is printed in the terminal and recorded in the new details/summary.txt when that file can be written. A first run with no existing output folder needs no archive; an existing empty folder is archived too.
If archiving fails, the script exits nonzero before collection. If creation of the fresh folder fails after archiving, the previous reports remain at the printed archive location. Failed or interrupted collection may leave a partial new report, but never intentionally restores or mixes in the previous run. Check the exit status, timestamps, and details/summary.txt before sharing; an early interruption may prevent that summary from being created.
Use a dedicated report directory, including for custom output paths: every file inside it will move into the archive on rerun. The script rejects output symlinks, files, root/system-container paths, and directories containing your home, working directory, or the script. Run from outside the output folder. New output and archive-container directories use private permissions (umask 077); permissions of archived contents are preserved.
A sibling gpu-report.lock/ directory prevents overlapping runs against the same resolved output path. It is removed on normal exit and when termination signals are handled. After a forced termination or power loss, a stale lock may remain: confirm no collection is running, then remove only the empty lock directory, for example with rmdir ./gpu-report.lock, before rerunning. The script never automatically breaks an existing lock.
Archives are retained indefinitely; review and remove old archives manually when no longer needed. They may contain sensitive data and consume disk space. The default gpu-report.archive-*/ directories and lock are Git-ignored alongside the active report; custom output paths and their archives are not covered. To view an old run, open its archived gpu-report/report.md in Markdown preview.
New output directories have one top-level entry point:
gpu-report/
report.md
details/
summary.txt
display.html
display.txt
...other detail reports (.html and .txt)
raw/
displays.txt
...full probe evidence (.txt)
The script writes the following files into the output directory. Each detail report below has both a styled .html version and a .txt copy (except the final summary):
report.md: main overview with probe results and relative links to evidencedetails/summary.txt: final file-write result, attention items, and this run's file listdetails/system-info.html: selected OS, hardware, and software factsdetails/hardware.html: selected graphics registry properties; large dictionaries are omitteddetails/display.html: GPU/display facts, grouped by device, and display preferencesdetails/kexts.html: loaded graphics drivers first (without addresses/UUIDs), followed by installed files grouped by name familydetails/windowserver.html: separate service-query and process-presence resultsdetails/opengl.html: X11 renderer result, supporting checks, interpretation, and next stepdetails/metal.html: reported Metal fields, with unknown support kept explicitdetails/perf.html: secondtopsample, up to 10 processes ordered by CPU, and selected page countersdetails/env.html: display-related environment variablesraw/*.txt: full output of each command as invoked, with status, exit code, and collection metadata
The display profile is collected once and reused across views. Presentation sections shorten lines beyond 96 characters and limit large lists with explicit notices; the raw evidence is not shortened. Markdown treats dynamic command output as code so embedded markup is displayed literally.
Markdown uses colored status symbols beside the result labels: 🟢 OK, 🔴 FAILED, 🟡 TOOL MISSING, 🔵 REPORTED/UNAVAILABLE, and ⚪ NOT REPORTED. Emoji appearance depends on the viewer; Markdown does not rely on custom CSS. Snapshot code blocks and plain-text/raw files remain uncolored, with no ANSI escape codes.
For full-color detail results, open an HTML report in a browser, for example open gpu-report/details/display.html on the Mac. Pages have light/dark themes, wrapping text, printable styling, labeled status badges, and links to evidence, plain text, and the Markdown overview. Browsers generally show the overview as Markdown source; use your editor's Markdown preview for that file. Some editor previews open HTML links as source too; use the browser command when needed.
HTML is self-contained, works offline, and escapes captured output so device names and diagnostic messages cannot inject markup. No JavaScript or remote assets are used. Color is supplemental, not a health verdict: a successful command returning data does not establish GPU health. Share the complete output folder to preserve relative links, and check the final summary for incomplete writes first.
Installed driver previews are grouped into Intel, AMD/ATI, NVIDIA/GeForce, Apple/AGX, VMware, and shared graphics families. Each family has its own 12-file preview and total count, so a long list from one family cannot hide another. These are filename-based groups, not proof of the active GPU driver. Full installed and loaded lists remain in raw/installed.txt and raw/loaded.txt.
The performance probe takes two samples one second apart and shows the second. Apple documents that the first top sample has invalid per-process CPU percentages. This is a brief CPU/memory snapshot, not a GPU benchmark. Raw top evidence contains both samples with the requested process/column selection; vm_stat retains the reported page size and units.
For an idle baseline, collect another report after startup and background activity have settled; retain a separate output folder when comparing runs.
| Result | Meaning |
|---|---|
| OK | A command returned data; this is not a GPU health verdict. |
| FAILED | A command exited unsuccessfully, or the GLX probe reported an error despite exit code 0. |
| TOOL MISSING | A command was unavailable and could not run. |
| UNAVAILABLE | The optional display-preferences domain is absent for this user/session. The original failure, exit code, and error text remain in raw evidence. |
| NOT REPORTED | The command or selected field supplied no data; this does not establish lack of hardware support. |
| REPORTED | A Metal field is present; read its value for the actual support statement. |
| NOT CHECKED | A derived check, such as GLX-extension visibility, could not be evaluated. |
Installed drivers, loaded drivers, running processes, and successful service queries are distinct observations. The report does not infer the active GPU driver from installation alone or label a failed service query as a stopped process.
The XQuartz process snapshot is taken after both X11/GLX probes and labeled with that timing. A local process can still be unreported while an X11 connection works; the report preserves both observations rather than inferring process presence from the connection.
Only exit code 1 with the exact Domain com.apple.windowserver does not exist response is classified as informational UNAVAILABLE and omitted from Attention. Permission errors, unexpected messages, and other exit codes remain FAILED.
Permission deniedwhen running script:- Run
chmod +x gpu-info.shand retry.
- Run
Tool not available: <name>appears in output:- The script keeps running and records missing tools instead of failing.
- On macOS, most required tools are built in. If
glxinfois missing, OpenGL X11 probing is skipped.
Result: FAILEDappears inopengl.txt:- Inspect the error excerpt and
raw/glx.txt. If exit code 0 accompanied an error, the report explains the discrepancy. - Check the separate X11 connection result. A successful connection and failed renderer probe can occur together; the cause is not established by those observations alone.
- If the connection failed, verify
DISPLAYand the X11 session before repeating the probe.
- Inspect the error excerpt and
FAILED,NOT REPORTED, orTOOL MISSINGappears:- Command errors include the exit status and captured output. Empty results and unavailable tools are identified separately.
- Permissions, session type, and virtualization can affect the available information. An absent graphics registry class or Metal line does not by itself prove that the GPU is faulty.
- On the target Mac, verify the launchd label with
defaults read /System/Library/LaunchDaemons/com.apple.WindowServer.plist Labelif the WindowServer query reports an unknown service. Availability of the preferences domaincom.apple.windowserverdepends on the user/session.
- Report content includes sensitive environment/process details:
- Reports are not automatically anonymized. Review hostnames, serial numbers, UUIDs, user names, process details, and environment values before sharing or committing them. Custom output directories are not covered by the default
gpu-reportignore rule.
- Reports are not automatically anonymized. Review hostnames, serial numbers, UUIDs, user names, process details, and environment values before sharing or committing them. Custom output directories are not covered by the default
Start with the sample report. It is a sanitized copy of the real Mac run collected on September 9, 2026, at 08:33:42–08:33:45 -0500, using script version 2.2.0 (source revision cee9c63). The machine is a MacBook Air (MacBookAir7,1) with Intel HD Graphics 6000 and 8 GB RAM, running macOS 12.7.6. This records the observed machine; the project's macOS 13 compatibility baseline is unchanged.
The sample mirrors the current 39-file layout: report.md, 19 files in details/ (colorized HTML, plain text, and summary), and 19 raw probe files. Collection timestamps, diagnostic statuses, driver versions, and performance measurements are retained. This is a captured example, not a live health report. The previous VMware sample remains available in Git history.
Each sample file is labeled as sanitized. Personal computer/user names, the system serial number, hardware UUID/provisioning UDID, display EDID/preferences keys/GUID, and the launchd display-session token have been replaced with explicit placeholders or sample names. The previous archive location is omitted; that archive is not part of the sample. Driver build UUIDs, process IDs, and diagnostic addresses/counters are retained. Apart from these redactions, sample notices, and LF line-ending normalization, the captured content is preserved; raw/ is therefore sanitized evidence, not a byte-for-byte original. The private source gpu-report is not modified by the sample refresh.
Run bash -n gpu-info.sh and bash tests/test-gpu-info.sh for syntax and mocked regression checks. Tests cover readable widths, retained raw evidence, multiple GPUs, literal markup, second-sample CPU data, collected-once display facts, evidence links, diagnostic states, and partial/write failures. Platform probes are mocked; they do not access the host's GPU. Set GPU_INFO_SHOW_TEST_REPORT=1 when running the tests to print a synthetic Markdown report for inspection.
Regression cases also cover an absent optional preferences domain versus permission errors, a process becoming visible after the X11 probe, and installed-driver lists large enough to require separate family limits.
Presentation tests also cover the details/ layout, colored status labels, HTML escaping, relative navigation/evidence links, and failed HTML writes with plain-text fallback. Lifecycle tests cover whole-folder archival, unchanged old report bytes and hidden files, unique archive names with identical timestamps, custom output paths, startup failures, output-path safety, and lock handling. These tests use temporary fixtures; they do not archive the real gpu-report directory.
On macOS 13, also run ./gpu-info.sh and inspect the reports for your hardware and session. Tests run on another OS validate shell logic only; they do not validate macOS command behavior. The ioreg class and subtree options follow Apple's ioreg documentation; driver classes still vary across Intel, Apple Silicon, and VM configurations.
This project is licensed under the MIT License.
MIT License summary:
- You can use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software.
- You must include the copyright notice and permission notice in copies or substantial portions.
- The software is provided "as is", without warranty of any kind.
See LICENSE for the full MIT license text.