USB controller, port, hub and device inspector for macOS
Descriptor detail, power budgets, connector negotiation and port fault counters, read from the IORegistry and presented as a navigable tree.
macOS provides three ways to inspect USB, each with a significant gap:
- System Information is shallow and read-only, and its USB pane has lost detail across recent releases.
system_profiler SPUSBDataTypereturns an empty array on macOS 26. The key was replaced bySPUSBHostDataType, and a good deal of existing tooling still queries the old one.ioregexposes everything, but with no structure or interpretation.
The IORegistry also carries data that none of the three surfaces: per-port overcurrent and enumeration-failure counters, link error counts, USB-C transport negotiation, and per-power-state dwell times. USBview reads and presents all of it.
The intended audience is developers and IT staff diagnosing unreliable hardware, though the interface does not assume prior knowledge of USB descriptors.
| Accurate topology | Controllers, physical ports, hubs and devices, correctly nested. A USB-C connector appears as one port rather than the two registry nodes, USB 2 and USB 3, that macOS reports. |
| Link speed diagnosis | When a device advertises SuperSpeed in its BOS descriptor but negotiates a slower link, USBview reports the discrepancy. The cause is almost always a USB 2 cable, an intermediate hub, or a port wired for USB 2 only. No other macOS tool reports this. |
| Port health | Connect count, overcurrent events, enumeration and address failures, link errors, and time spent in each power state. Non-zero fault counters are flagged. |
| USB-C connector state | (Apple Silicon) What the connector supports against what it has negotiated: USB 2, USB 3, Thunderbolt or DisplayPort, with cable type and connector-level overcurrent. |
| Storage and network | Volumes, capacity and free space for USB drives. IP addresses, MAC address, link rate and traffic counters for USB network adapters. Both update live. |
| Accurate power reporting | Requested power and port budget, each labelled for what it is. macOS exposes no measured draw, so USBview reports none. See Limitations. |
| Internal device filtering | (Intel) The T2 places the camera, keyboard, Touch Bar, headset and ambient light sensor on a virtual USB bus. On affected Macs these permanently attached devices outnumber the physical ones. Hidden by default, with one setting to restore them. |
| Headless operation | The enumeration core runs without a window through make dump, --watch and --descriptors. Output is JSON or plain text, suitable for piping, diffing, or attaching to a bug report. |
| Offline by default | Vendor and product names come from a bundled usb.ids. A single opt-in setting can access the network, and it ships disabled. |
Device detail. Vendor, product and serial strings, VID/PID, USB specification version, negotiated link speed, device class, address, location ID, configurations, interfaces, alternate settings, endpoints, SuperSpeed burst sizes, class-specific descriptors and driver binding. Devices that enumerated but were never configured are flagged, which normally indicates that no driver matched.
Power. Requested power (bMaxPower), the port's current budget awake and asleep, and bus- against self-powered status from the configuration descriptor's bmAttributes.
Connector (Apple Silicon). Separates a slow device from a saturated connector. A connector running DisplayPort is using the lanes SuperSpeed requires, which is a different problem from a bad cable, and the two are reported distinctly. A port with a display attached is never reported as empty.
Internal devices (Intel). A Mac with a T2 attaches its own camera, keyboard and trackpad, Touch Bar display and backlight, headset and ambient light sensor to a virtual USB bus, alongside the T2 itself. On a MacBookPro15,2 that accounts for seven of the machine's thirteen USB devices, all permanently attached and unreachable by cable. They are hidden by default, and Settings → Sidebar → Show internal devices restores them.
Their ports are labelled Virtual rather than assigned a USB generation, because the registry node for a virtual port carries no protocol flags at all. The devices on it do negotiate 480 Mb/s, but that is the device's speed rather than the port's capability, and USBview does not blur that distinction elsewhere. Apple Silicon has no equivalent bus, since its built-in peripherals are not on USB, so the setting is disabled there with that explanation rather than left active and inert.
Storage. Disks, partitions, volumes, capacity, free space, filesystem and mount points, updating live as volumes mount and unmount. Figures match Disk Utility.
Network. Interface name, MAC address, Ethernet link rate and state, IPv4 and IPv6 addresses with prefix lengths, router, MTU and traffic counters. The Ethernet link rate is kept distinct from the USB one: a 2.5G adapter on a 5 Gb/s USB connection is doing 2.5 Gb/s of Ethernet, and conflating the two misattributes the bottleneck.
Diagnostic log. Attach and detach events, enumeration failures and action results, collapsed at the foot of the detail pane until expanded.
Native throughout. SwiftUI, following the system appearance, fully keyboard navigable with ⌘F to search and arrow keys to move, and labelled for VoiceOver.
- macOS 15 Sequoia or later
- Xcode 16 or later (macOS 15 SDK)
- XcodeGen, installed with
brew install xcodegen
git clone https://github.com/fuckmaz/USBview.git
cd USBview
make runThis generates the Xcode project, builds it, and launches the app. No .xcodeproj is checked in; it is generated from project.yml, so every build starts from the same specification.
make build
make test| Command | Purpose |
|---|---|
make build |
Build the app |
make run |
Build and launch |
make test |
Run the unit tests |
make dump |
Print the USB topology as JSON, without a window |
make tree |
Print the USB topology as an indented tree, without a window |
make generate |
Regenerate USBview.xcodeproj from project.yml |
make clean |
Remove build artefacts |
make help |
List every target |
Core/ never imports SwiftUI, so everything the app knows is reachable from a terminal:
| Command | Output |
|---|---|
USBview --dump-json |
The full topology as JSON |
USBview --dump-tree |
The topology as an indented tree |
USBview --watch |
A live stream of attach and detach events |
USBview --descriptors |
Full descriptors for every device, comparable to lsusb -v |
USBview --storage |
USB disks and their volumes |
USBview --network |
USB network adapters and their addresses |
USBview --dump-registry |
The raw IORegistry subtree, for bug reports |
None of these are filtered by the app's display settings. Hiding internal devices changes the window and never a capture, since a capture missing a third of the bus would mislead whoever reads it.
make tree prints the topology as a tree:
Mac17,2 · Version 26.6.2 (Build 25G83)
3 controllers · 3 ports · 1 device
USB 3.1 Bus · AppleT8142USBXHCI · 0x01000000
└── Port 2 — USB 2.0 + USB 3.x · 3000 mA budget
└── USB 10/100/1G/2.5G LAN · 0BDA:8156 · 5 Gb/s · Composite · Realtek
--watch prints the topology and then narrates every attach, detach and re-enumeration, with timings:
17:44:12.927 Found 3 controllers, 3 ports, 1 device
Enumerated in 5.3 ms
17:44:19.104 + Attached: SanDisk Ultra on Port 1
ID 0781:5583 · SuperSpeed · 5 Gb/s
This is intended for intermittent faults: the stream can be left running and reviewed afterwards.
USBview --dump-registry > capture.json writes the raw IORegistry subtree. Attaching one to an issue makes the problem reproducible on hardware the maintainer has no access to, and the test suite runs directly against these captures.
Prebuilt disk images are on the releases page:
| Download | Target |
|---|---|
USBview-<version>-Universal.dmg |
Any Mac. Recommended. |
USBview-<version>-AppleSilicon.dmg |
Apple Silicon only (M1 and later) |
USBview-<version>-Intel.dmg |
Intel only |
The universal image runs everywhere and is about a megabyte larger. The single-architecture builds exist for anyone wanting the smaller file, or needing to pin an architecture while diagnosing something.
USBview is not code signed, as there is no Developer ID behind it, so macOS quarantines a downloaded build and refuses to open it the first time. Clearing the attribute once resolves this:
xattr -dr com.apple.quarantine /Applications/USBview.appApply that only to a binary built locally or otherwise trusted. Building from source avoids the step entirely, and every release publishes SHA-256 checksums for verifying a download instead.
The Intel build is cross-compiled on Apple Silicon and smoke-tested under Rosetta rather than on Intel hardware. The enumeration core is covered by tests against a captured Intel-style xHCI registry, and a bug report with USBview --dump-registry attached is the fastest route to fixing anything that slips through.
USBview reports what macOS exposes, and documents what it does not. Several capabilities that a USB inspector might reasonably be expected to offer have no public API at any privilege level:
| Reason | |
|---|---|
| No live current draw | macOS reports no measured per-device current anywhere in the IORegistry. USBview shows the device's requested power (bMaxPower) and the port's budget (kUSBWakePortCurrentLimit), each labelled as such. It is not an ammeter and does not present itself as one. |
| No port disable or enable | No public API exists. |
| No USB controller restart | No public API exists, and SIP blocks kext-level workarounds even as root. |
| Device reset works, and is destructive | Testing confirmed that IOUSBHostDevice.reset() succeeds even on a device with a matched kernel driver, so this is a genuine re-enumeration rather than a no-op. On a drive with a mounted volume it is equivalent to unplugging the cable, so USBview asks for confirmation and says so. Refusals are reported rather than swallowed. |
The first three still appear in the interface, dimmed and marked with an information symbol that explains the underlying restriction when clicked. An explanation is more useful than an unexplained absence, particularly for the capabilities people most often open a USB inspector to find.
A privileged helper daemon was considered and rejected. SIP prevents it from unlocking any of the above, so it would add signing and lifecycle complexity for no gain in capability.
┌──────────────────────────────────────────────────────────┐
│ Sources/UI Sidebar · detail pane · settings │ ← SwiftUI
├──────────────────────────────────────────────────────────┤
│ Sources/App Entry point · headless CLI · app state │ ← bridge
├──────────────────────────────────────────────────────────┤
│ Sources/Core IOKit · descriptors · storage · network │ ← no SwiftUI
│ actions · metadata · settings │
└──────────────────────────────────────────────────────────┘
| Path | Role |
|---|---|
project.yml |
XcodeGen specification, and the source of truth for the build |
Makefile |
generate, build, run, test, dump |
Sources/Resources/ |
Assets, app icon, bundled usb.ids |
Tests/CoreTests/ |
Fixture-driven, requiring no hardware |
The architectural constraint behind that split: Core/ never imports SwiftUI, and everything it produces must be reachable through make dump. That is what keeps the enumeration core testable without the interface, and what makes the headless commands possible at all.
USBview operates entirely offline. Vendor and product names come from a bundled copy of usb.ids.
One opt-in, off-by-default setting allows USBview to access the network, for downloading vendor logos. While enabled, USBview sends the manufacturer's company name, resolved locally from the bundled usb.ids, to wikidata.org and commons.wikimedia.org. No device identifier, product ID, serial number or machine information is transmitted.
While disabled, no network client is ever constructed. The gate is structural rather than a flag checked before sending, and the test suite asserts that zero clients are built rather than merely zero requests. lsof -i against the running app under default settings shows no connections at all.
Results are cached on disk for a month, including negative results, and the Privacy tab lists what is stored alongside a button to clear it.
Issues and pull requests are welcome. Run make test before opening a pull request. CI runs the same build, test and headless-dump steps on every push.
- Written by maz, github.com/fuckmaz
- USB vendor and product names from
usb.ids, maintained by Stephen J. Gowdy. The upstream project distributes it under the GNU General Public Licence v2 or later, or the 3-clause BSD licence. A copy is bundled atSources/Resources/usb.ids, and the About panel reports which version. - No vendor logos are bundled. Company logos are trademarks, so USBview draws vendor initials instead.
Sources/Resources/VendorLogos/README.mddocuments how to add logos to a local build. - Built with SwiftUI, IOKit, DiskArbitration and SystemConfiguration.
MIT. See LICENSE.


