ghw
Golang hardware discovery/inspection library
About ghw
This large README is shown as a plain-text preview. Read the full README at the source
# `ghw` - Go HardWare discovery/inspection library
[](https://pkg.go.dev/github.com/jaypipes/ghw)
[](https://github.com/jaypipes/ghw/actions)
[](CODE_OF_CONDUCT.md)

`ghw` is a Go library providing hardware inspection and discovery for Linux and
Windows. There currently exists partial support for MacOSX.
## Design Principles
* No root privileges needed for discovery
`ghw` goes the extra mile to be useful without root privileges. We query for
host hardware information as directly as possible without relying on shellouts
to programs like `dmidecode` that require root privileges to execute.
Elevated privileges are indeed required to query for some information, but
`ghw` will never error out if blocked from reading that information. Instead,
`ghw` will print a warning message about the information that could not be
retrieved. You may disable these warning messages with the
`GHW_DISABLE_WARNINGS` environment variable.
* Well-documented code and plenty of example code
The code itself should be well-documented with lots of usage examples.
* Interfaces should be consistent across modules
Each module in the library should be structured in a consistent fashion, and
the structs returned by various library functions should have consistent
attribute and method names.
## Inspecting != Monitoring
`ghw` is a tool for gathering information about your hardware's **capacity**
and **capabilities**.
It is important to point out that `ghw` does **NOT** report information that is
temporary or variable. It is **NOT** a system monitor nor is it an appropriate
tool for gathering data points for metrics that change over time. If you are
looking for a system that tracks **usage** of CPU, memory, network I/O or disk
I/O, there are plenty of great open source tools that do this! Check out the
[Prometheus project](https://prometheus.io/) for a great example.
## Usage
`ghw` has functions that return an `Info` object about a particular hardware
domain (e.g. CPU, Memory, Block storage, etc).
Use the following functions in `ghw` to inspect information about the host
hardware:
* [`ghw.CPU()`](#cpu)
* [`ghw.Memory()`](#memory)
* [`ghw.Block()`](#block-storage) (block storage)
* [`ghw.Topology()`](#topology) (processor architecture, NUMA topology and
memory cache hierarchy)
* [`ghw.Network()`](#network)
* [`ghw.PCI()`](#pci)
* [`ghw.GPU()`](#gpu) (graphical processing unit)
* [`ghw.Accelerator()`](#accelerator) (processing accelerators, AI)
* [`ghw.Chassis()`](#chassis)
* [`ghw.BIOS()`](#bios)
* [`ghw.Baseboard()`](#baseboard)
* [`ghw.Product()`](#product)
### CPU
The `ghw.CPU()` function returns a `ghw.CPUInfo` struct that contains
information about the CPUs on the host system.
`ghw.CPUInfo` contains the following fields:
* `ghw.CPUInfo.TotalCores` has the total number of physical cores the host
system contains
* `ghw.CPUInfo.TotalHardwareThreads` has the total number of hardware threads
the host system contains
* `ghw.CPUInfo.Processors` is an array of `ghw.Processor` structs, one for each
physical processor package contained in the host
Each `ghw.Processor` struct contains a number of fields:
* `ghw.Processor.ID` is the physical processor `uint32` ID according to the
system
* `ghw.Processor.TotalCores` is the number of physical cores in the processor
package
* `ghw.Processor.TotalHardwareThreads` is the number of hardware threads in the
processor package
* `ghw.Processor.Vendor` is a string containing the vendor name
* `ghw.Processor.Model` is a string containing the vendor's model name
* `ghw.Processor.Capabilities` (Linux only) is an array of strings indicating
the features the processor has enabled
* `ghw.Processor.Cores` (Linux only) is an array of `ghw.ProcessorCore` structs
that are packed onto this physical processor
A `ghw.ProcessorCore` has the following fields:
* `ghw.ProcessorCore.ID` is the `uint32` identifier that the host gave this
core. Note that this does *not* necessarily equate to a zero-based index of
the core within a physical package. For example, the core IDs for an Intel Core
i7 are 0, 1, 2, 8, 9, and 10
* `ghw.ProcessorCore.TotalHardwareThreads` is the number of hardware threads
associated with the core
* `ghw.ProcessorCore.LogicalProcessors` is an array of ints representing the
logical processor IDs assigned to any processing unit for the core. These are
sometimes called the "thread siblings". Logical processor IDs are the
*zero-based* index of the processor on the host and are *not* related to the
core ID.
```go
package main
import (
"fmt"
"math"
"strings"
"github.com/jaypipes/ghw"
)
func main() {
cpu, err := ghw.CPU()
if err != nil {
fmt.Printf("Error getting CPU info: %v", err)
}
fmt.Printf("%v\n", cpu)
for _, proc := range cpu.Processors {
fmt.Printf(" %v\n", proc)
for _, core := range proc.Cores {
fmt.Printf(" %v\n", core)
}
if len(proc.Capabilities) > 0 {
// pretty-print the (large) block of capability strings into rows
// of 6 capability strings
rows := int(math.Ceil(float64(len(proc.Capabilities)) / float64(6)))
for row := 1; row < rows; row = row + 1 {
rowStart := (row * 6) - 1
rowEnd := int(math.Min(float64(rowStart+6), float64(len(proc.Capabilities))))
rowElems := proc.Capabilities[rowStart:rowEnd]
capStr := strings.Join(rowElems, " ")
if row == 1 {
fmt.Printf(" capabilities: [%s\n", capStr)
} else if rowEnd < len(proc.Capabilities) {
fmt.Printf(" %s\n", capStr)
} else {
fmt.Printf(" %s]\n", capStr)
}
}
}
}
}
```
Example output from my personal workstation:
```
cpu (1 physical package, 6 cores, 12 hardware threads)
physical package #0 (6 cores, 12 hardware threads)
processor core #0 (2 threads), logical processors [0 6]
processor core #1 (2 threads), logical processors [1 7]
processor core #2 (2 threads), logical processors [2 8]
processor core #3 (2 threads), logical processors [3 9]
processor core #4 (2 threads), logical processors [4 10]
processor core #5 (2 threads), logical processors [5 11]
capabilities: [msr pae mce cx8 apic sep
mtrr pge mca cmov pat pse36
clflush dts acpi mmx fxsr sse
sse2 ss ht tm pbe syscall
nx pdpe1gb rdtscp lm constant_tsc arch_perfmon
pebs bts rep_good nopl xtopology nonstop_tsc
cpuid aperfmperf pni pclmulqdq dtes64 monitor
ds_cpl vmx est tm2 ssse3 cx16
xtpr pdcm pcid sse4_1 sse4_2 popcnt
aes lahf_lm pti retpoline tpr_shadow vnmi
flexpriority ept vpid dtherm ida arat]
```
### Memory
The `ghw.Memory()` function returns a `ghw.MemoryInfo` struct that contains
information about the RAM on the host system.
`ghw.MemoryInfo` contains the following fields:
* `ghw.MemoryInfo.TotalPhysicalBytes` contains the amount of physical memory on
the host
* `ghw.MemoryInfo.TotalUsableBytes` contains the amount of memory the
system can actually use. Usable memory accounts for things like the kernel's
resident memory size and some reserved system bits. Please note this value is
**NOT** the amount of memory currently in use by processes in the system. See
[the discussion][#physical-versus-usage-memory] about the difference.
* `ghw.MemoryInfo.SupportedPageSizes` is an array of integers representing the
size, in bytes, of memory pages the system supports
* `ghw.MemoryInfo.Modules` is an array of pointers to `ghw.MemoryModule`
structs, one for each physical [DIMM](https://en.wikipedia.org/wiki/DIMM).
Currently, this information is only included on Windows, with Linux support
[planned](https://github.com/jaypipes/ghw/pull/171#issuecomment-597082409).
```go
package main
import (
"fmt"
"github.com/jaypipes/ghw"
)
func main() {
memory, err := ghw.Memory()
if err != nil {
fmt.Printf("Error getting memory info: %v", err)
}
fmt.Println(memory.String())
}
```
Example output from my personal workstation:
```
memory (24GB physical, 24GB usable)
```
#### Physical versus Usable Memory
There has been [some](https://github.com/jaypipes/ghw/pull/171)
[confusion](https://github.com/jaypipes/ghw/issues/183) regarding the
difference between the total physical bytes versus total usable bytes of
memory.
Some of this confusion has been due to a misunderstanding of the term "usable".
As mentioned [above](#inspection!=monitoring), `ghw` does inspection of the
system's capacity.
A host computer has two capacities when it comes to RAM. The first capacity is
the amount of RAM that is contained in all memory banks (DIMMs) that are
attached to the motherboard. `ghw.MemoryInfo.TotalPhysicalBytes` refers to this
first capacity.
There is a (usually small) amount of RAM that is consumed by the bootloader
before the operating system is started (booted). Once the bootloader has booted
the operating system, the amount of RAM that may be used by the operating
system and its applications is fixed. `ghw.MemoryInfo.TotalUsableBytes` refers
to this second capacity.
You can determine the amount of RAM that the bootloader used (that is not made
available to the operating system) by subtracting
`ghw.MemoryInfo.TotalUsableBytes` from `ghw.MemoryInfo.TotalPhysicalBytes`:
```go
package main
import (
"fmt"
"github.com/jaypipes/ghw"
)
func main() {
memory, err := ghw.Memory()
if err != nil {
fmt.Printf("Error getting memory info: %v", err)
}
phys := memory.TotalPhysicalBytes
usable := memory.TotalUsableBytes
fmt.Printf("The bootloader consumes %d bytes of RAM\n", phys - usable)
}
```
Example output from my personal workstation booted into a Windows10 operating
system with a Linux GRUB bootloader:
```
The bootloader consumes 3832720 bytes of RAM
```
### Block storage
The `ghw.Block()` function returns a `ghw.BlockInfo` struct that contains
information about the block storage on the host system.
`ghw.BlockInfo` contains the following fields:
* `ghw.BlockInfo.TotalSizeBytes` contains the amount of physical block storage
on the host.
* `ghw.BlockInfo.Disks` is an array of pointers to `ghw.Disk` structs, one for
each disk found by the system
Each `ghw.Disk` struct contains the following fields:
* `ghw.Disk.Name` contains a string with the short name of the disk, e.g. "sda"
* `ghw.Disk.SizeBytes` contains the amount of storage the disk provides
* `ghw.Disk.PhysicalBlockSizeBytes` contains the size of the physical blocks
used on the disk, in bytes. This is typically the minimum amount of data that
will be written in a single write operation for the disk.
* `ghw.Disk.LogicalBlockSizeBytes` (Linux only) contains the size of the
logical blocks used on the disk, in bytes. This is the disk's addressable
unit size, which may be smaller than `PhysicalBlockSizeBytes`, e.g. for a
512e drive that reports a 512-byte logical block size over 4096-byte
physical sectors.
* `ghw.Disk.IsRemovable` contains a boolean indicating if the disk drive is
removable
* `ghw.Disk.DriveType` is the type of drive. It is of type `ghw.DriveType`
which has a `ghw.DriveType.String()` method that can be called to return a
string representation of the bus. This string will be `HDD`, `FDD`, `ODD`,
or `SSD`, which correspond to a hard disk drive (rotational), floppy drive,
optical (CD/DVD) drive and solid-state drive.
* `ghw.Disk.StorageController` is the type of storage controller. It is of type
`ghw.StorageController` which has a `ghw.StorageController.String()` method
that can be called to return a string representation of the bus. This string
will be `SCSI`, `IDE`, `virtio`, `MMC`, or `NVMe`
* `ghw.Disk.BusPath` (Linux, Darwin only) is the filepath to the bus used by
the disk.
* `ghw.Disk.NUMANodeID` (Linux only) is the numeric index of the NUMA node this
disk is local to, or -1 if the host system is not a NUMA system or is not
Linux.
* `ghw.Disk.Vendor` contains a string with the name of the hardware vendor for
the disk
* `ghw.Disk.Model` contains a string with the vendor-assigned disk model name
* `ghw.Disk.SerialNumber` contains a string with the disk's serial number
* `ghw.Disk.WWN` contains a string with the disk's
[World Wide Name](https://en.wikipedia.org/wiki/World_Wide_Name)
* `ghw.Disk.Partitions` contains an array of pointers to `ghw.Partition`
structs, one for each partition on the disk
Each `ghw.Partition` struct contains these fields:
* `ghw.Partition.Name` contains a string with the short name of the partition,
e.g. `sda1`
* `ghw.Partition.Label` contains the label for the partition itself. On Linux
systems, this is derived from the `ID_PART_ENTRY_NAME` [udev][udev] entry for
the partition.
* `ghw.Partition.FilesystemLabel` contains the label for the filesystem housed
on the partition. On Linux systems, this is derived from the `ID_FS_NAME`
[udev][udev] entry for the partition.
* `ghw.Partition.SizeBytes` contains the amount of storage the partition
provides
* `ghw.Partition.MountPoint` contains a string with the partition's mount
point, or `""` if no mount point was discovered
* `ghw.Partition.Type` contains a string indicated the filesystem type for the
partition, or `""` if the system could not determine the type
* `ghw.Partition.IsReadOnly` is a bool indicating the partition is read-only
* `ghw.Partition.Disk` is a pointer to the `ghw.Disk` object associated with
the partition.
* `ghw.Partition.UUID` is a string containing the partition UUID on Linux and MacOS,
and the `VolumeSerialNumber` on Windows (e.g. "A8C3D032"). On Linux systems, this is
derived from the `ID_PART_ENTRY_UUID` [udev][udev] entry for the partition.
[udev]: https://en.wikipedia.org/wiki/Udev
```go
package main
import (
"fmt"
"github.com/jaypipes/ghw"
)
func main() {
block, err := ghw.Block()
if err != nil {
fmt.Printf("Error getting block storage info: %v", err)
}
fmt.Printf("%v\n", block)
for _, disk := range block.Disks {
fmt.Printf(" %v\n", disk)
for _, part := range disk.Partitions {
fmt.Printf(" %v\n", part)
}
}
}
```
Example output from my personal workstation:
```
block storage (1 disk, 2TB physical storage)
sda HDD (2TB) SCSI [@pci-0000:04:00.0-scsi-0:1:0:0 (node #0)] vendor=LSI model=Logical_Volume serial=600508e000000000f8253aac9a1abd0c WWN=0x600508e000000000f8253aac9a1abd0c
/dev/sda1 (100MB)
/dev/sda2 (187GB)
/dev/sda3 (449MB)
/dev/sda4 (1KB)
/dev/sda5 (15GB)
/dev/sda6 (2TB) [ext4] mounted@/
```
> **NOTE**: `ghw` looks in the udev runtime database for some information. If
> you are using `ghw` in a container, remember to bind mount `/dev/disk` and
> `/run` into your container, otherwise `ghw` won't be able to query the udev
> DB or sysfs paths for information.
### Topology
> **NOTE**: Topology support is currently Linux-only. Windows support is
> [planned](https://github.com/jaypipes/ghw/issues/166).
The `ghw.Topology()` function returns a `ghw.TopologyInfo` struct that contains
information about the host computer's architecture (NUMA vs. SMP), the host's
NUMA node layout and processor-specific memory caches.
The `ghw.TopologyInfo` struct contains two fields:
* `ghw.TopologyInfo.Architecture` contains an enum with the value `ghw.NUMA` or
`ghw.SMP` depending on what the topology of the system is
* `ghw.TopologyInfo.Nodes` is an array of pointers to `ghw.TopologyNode`
structs, one for each topology node (typically physical processor package)
found by the system
Each `ghw.TopologyNode` struct contains the following fields:
* `ghw.TopologyNode.ID` is the system's `uint32` identifier for the node
* `ghw.TopologyNode.Memory` is a `ghw.MemoryArea` struct describing the memory
attached to this node.
* `ghw.TopologyNode.Cores` is an array of pointers to `ghw.ProcessorCore` structs that
are contained in this node
* `ghw.TopologyNode.Caches` is an array of pointers to `ghw.MemoryCache` structs that
represent the low-level caches associated with processors and cores on the
system
* `ghw.TopologyNode.Distance` is an array of distances between NUMA nodes as reported
by the system.
`ghw.MemoryArea` describes a collection of *physical* RAM on the host.
In the simplest and most common case, all system memory fits in a single memory
area. In more complex host systems, like [NUMA systems][numa], many memory
areas may be present in the host system (e.g. one for each NUMA cell).
[numa]: https://en.wikipedia.org/wiki/Non-uniform_memory_access
The `ghw.MemoryArea` struct contains the following fields:
* `ghw.MemoryArea.TotalPhysicalBytes` contains the amount of physical memory
associated with this memory area.
* `ghw.MemoryArea.TotalUsableBytes` contains the amount of memory of this
memory area the system can actually use. Usable memory accounts for things
like the kernel's resident memory size and some reserved system bits. Please
note this value is **NOT** the amount of memory currently in use by processes
in the system. See [the discussion][#physical-versus-usage-memory] about
the difference.
See above in the [CPU](#cpu) section for information about the
`ghw.ProcessorCore` struct and how to use and query it.
Each `ghw.MemoryCache` struct contains the following fields:
* `ghw.MemoryCache.Type` is an enum that contains one of `ghw.DATA`,
`ghw.INSTRUCTION` or `ghw.UNIFIED` depending on whether the cache stores CPU
instructions, program data, or both
* `ghw.MemoryCache.Level` is a positive integer indicating how close the cache
is to the processor. The lower the number, the closer the cache is to the
processor and the faster the processor can access its contents
* `ghw.MemoryCache.SizeBytes` is an integer containing the number of bytes the
cache can contain
* `ghw.MemoryCache.LogicalProcessors` is an array of integers representing the
logical processors that use the cache
```go
package main
import (
"fmt"
"github.com/jaypipes/ghw"
)
func main() {
topology, err := ghw.Topology()
if err != nil {
fmt.Printf("Error getting topology info: %v", err)
}
fmt.Printf("%v\n", topology)
for _, node := range topology.Nodes {
fmt.Printf(" %v\n", node)
for _, cache := range node.Caches {
fmt.Printf(" %v\n", cache)
}
}
}
```
Example output from my personal workstation:
```
topology SMP (1 nodes)
node #0 (6 cores)
L1i cache (32 KB) shared with logical processors: 3,9
L1i cache (32 KB) shared with logical processors: 2,8
L1i cache (32 KB) shared with logical processors: 11,5
L1i cache (32 KB) shared with logical processors: 10,4
L1i cache (32 KB) shared with logical processors: 0,6
L1i cache (32 KB) shared with logical processors: 1,7
L1d cache (32 KB) shared with logical processors: 11,5
L1d cache (32 KB) shared with logical processors: 10,4
L1d cache (32 KB) shared with logical processors: 3,9
L1d cache (32 KB) shared with logical processors: 1,7
L1d cache (32 KB) shared with logical processors: 0,6
L1d cache (32 KB) shared with logical processors: 2,8
L2 cache (256 KB) shared with logical processors: 2,8
L2 cache (256 KB) shared with logical processors: 3,9
L2 cache (256 KB) shared with logical processors: 0,6
L2 cache (256 KB) shared with logical processors: 10,4
L2 cache (256 KB) shared with logical processors: 1,7
L2 cache (256 KB) shared with logical processors: 11,5
L3 cache (12288 KB) shared with logical processors: 0,1,10,11,2,3,4,5,6,7,8,9
```
### Network
The `ghw.Network()` function returns a `ghw.NetworkInfo` struct that contains
information about the host computer's networking hardware.
Frequently Asked Questions
What is ghw?
ghw is a Hardware library for the Go programming language. Golang hardware discovery/inspection library
How many GitHub stars does ghw have?
ghw has 1,879 GitHub stars in the directory's latest synchronization.
How do I install ghw?
Install ghw with the Go module system using `go get jaypipes/ghw`. Check the repository for the current installation instructions.
What category does ghw belong to?
ghw is listed under Hardware, specifically Hardware.