mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-15 16:51:03 +00:00
report the approximate hardware specs (CPU speed, cores, memory) of the server in the /info endpoint. This may be useful when benchmarking. We do some workarounds because gopsutil's core count output is confusingly different between Linux and Darwin, and the MHz output is usually wrong on Linux. Intel's app notes say to just parse the model string. Whyyyyyyy.
243 lines
6.1 KiB
Go
243 lines
6.1 KiB
Go
// Copyright 2017 Pilosa Corp.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package gopsutil
|
|
|
|
import (
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/pilosa/pilosa"
|
|
"github.com/shirou/gopsutil/cpu"
|
|
"github.com/shirou/gopsutil/host"
|
|
"github.com/shirou/gopsutil/mem"
|
|
)
|
|
|
|
var _ pilosa.SystemInfo = NewSystemInfo()
|
|
|
|
// systemInfo is an implementation of pilosa.systemInfo that uses gopsutil to collect information about the host OS.
|
|
type systemInfo struct {
|
|
platform string
|
|
family string
|
|
osVersion string
|
|
cpuModel string
|
|
cpuPhysicalCores int
|
|
cpuLogicalCores int
|
|
cpuMHz int
|
|
}
|
|
|
|
// Uptime returns the system uptime in seconds.
|
|
func (s *systemInfo) Uptime() (uptime uint64, err error) {
|
|
hostInfo, err := host.Info()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return hostInfo.Uptime, nil
|
|
}
|
|
|
|
// cpuFrequencyMultipliers is a lookup table for prefixes on clock speeds.
|
|
// This is probably overkill.
|
|
var cpuFrequencyMultipliers = [256]int{
|
|
'M': 1,
|
|
'G': 1000,
|
|
'T': 1000 * 1000,
|
|
}
|
|
|
|
// computeHz determines the official rated speed of a CPU from its brand
|
|
// string. This insanity is *actually the official documented way to do
|
|
// this according to Intel*. There is also a cpuid leaf for the frequency,
|
|
// but I am not sure how supported it is, so.
|
|
func computeMHz(brandString string) int {
|
|
hz := strings.LastIndex(brandString, "Hz")
|
|
// ' 1MHz'
|
|
if hz < 3 {
|
|
return -1
|
|
}
|
|
multiplier := cpuFrequencyMultipliers[brandString[hz-1]]
|
|
if multiplier == 0 {
|
|
return -1
|
|
}
|
|
freq := 0
|
|
divisor := 0
|
|
decimalShift := 1
|
|
var i int
|
|
for i = hz - 2; i >= 0 && brandString[i] != ' '; i-- {
|
|
if brandString[i] >= '0' && brandString[i] <= '9' {
|
|
freq += int(brandString[i]-'0') * decimalShift
|
|
decimalShift *= 10
|
|
} else if brandString[i] == '.' {
|
|
if divisor != 0 {
|
|
return -1
|
|
}
|
|
divisor = decimalShift
|
|
} else {
|
|
return -1
|
|
}
|
|
}
|
|
// we didn't find a space
|
|
if i < 0 {
|
|
return -1
|
|
}
|
|
if divisor != 0 {
|
|
return (freq * multiplier) / divisor
|
|
}
|
|
return freq * multiplier
|
|
}
|
|
|
|
// collectPlatformInfo fetches and caches system platform information.
|
|
func (s *systemInfo) collectPlatformInfo() error {
|
|
var err error
|
|
if s.platform == "" {
|
|
s.platform, s.family, s.osVersion, err = host.PlatformInformation()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if s.cpuModel == "" {
|
|
infos, err := cpu.Info()
|
|
if err != nil || len(infos) == 0 {
|
|
s.cpuModel = "unknown"
|
|
// if err is nil, but we got no infos, we don't
|
|
// have a meaningful error to return.
|
|
return err
|
|
}
|
|
s.cpuModel = infos[0].ModelName
|
|
s.cpuMHz = computeMHz(s.cpuModel)
|
|
|
|
// gopsutil reports core and clock speed info inconsistently
|
|
// by OS
|
|
switch runtime.GOOS {
|
|
case "linux":
|
|
// Each reported "CPU" is a logical core. Some cores may
|
|
// have the same Core ID, which is a strictly numeric
|
|
// value which gopsutil returned as a string, which
|
|
// indicates that they're hyperthreading or similar things
|
|
// on the same physical core.
|
|
uniqueCores := make(map[string]struct{}, len(infos))
|
|
totalCores := 0
|
|
for _, info := range infos {
|
|
uniqueCores[info.CoreID] = struct{}{}
|
|
totalCores += int(info.Cores)
|
|
}
|
|
s.cpuPhysicalCores = len(uniqueCores)
|
|
s.cpuLogicalCores = totalCores
|
|
case "darwin":
|
|
fallthrough
|
|
default: // let's hope other systems give useful core info?
|
|
s.cpuPhysicalCores = int(infos[0].Cores)
|
|
// we have no way to know, let's try runtime
|
|
s.cpuLogicalCores = runtime.NumCPU()
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Platform returns the system platform.
|
|
func (s *systemInfo) Platform() (string, error) {
|
|
err := s.collectPlatformInfo()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return s.platform, nil
|
|
}
|
|
|
|
// Family returns the system family.
|
|
func (s *systemInfo) Family() (string, error) {
|
|
err := s.collectPlatformInfo()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return s.family, err
|
|
}
|
|
|
|
// OSVersion returns the OS Version.
|
|
func (s *systemInfo) OSVersion() (string, error) {
|
|
err := s.collectPlatformInfo()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return s.osVersion, err
|
|
}
|
|
|
|
// MemFree returns the amount of free memory in bytes.
|
|
func (s *systemInfo) MemFree() (uint64, error) {
|
|
memInfo, err := mem.VirtualMemory()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return memInfo.Free, err
|
|
}
|
|
|
|
// MemTotal returns the amount of total memory in bytes.
|
|
func (s *systemInfo) MemTotal() (uint64, error) {
|
|
memInfo, err := mem.VirtualMemory()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return memInfo.Total, err
|
|
}
|
|
|
|
// MemUsed returns the amount of used memory in bytes.
|
|
func (s *systemInfo) MemUsed() (uint64, error) {
|
|
memInfo, err := mem.VirtualMemory()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return memInfo.Used, err
|
|
}
|
|
|
|
// KernelVersion returns the kernel version as a string.
|
|
func (s *systemInfo) KernelVersion() (string, error) {
|
|
return host.KernelVersion()
|
|
}
|
|
|
|
// CPUArch returns the CPU architecture, such as amd64
|
|
func (s *systemInfo) CPUArch() string {
|
|
return runtime.GOARCH
|
|
}
|
|
|
|
// CPUModel returns the CPU model string
|
|
func (s *systemInfo) CPUModel() string {
|
|
err := s.collectPlatformInfo()
|
|
if err != nil {
|
|
return "unknown"
|
|
}
|
|
return s.cpuModel
|
|
}
|
|
|
|
// CPUMhz returns the CPU clock speed
|
|
func (s *systemInfo) CPUMHz() (int, error) {
|
|
err := s.collectPlatformInfo()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return s.cpuMHz, nil
|
|
}
|
|
|
|
// CPUCores returns the number of (physical or logical) CPU cores
|
|
func (s *systemInfo) CPUCores() (physical, logical int, err error) {
|
|
err = s.collectPlatformInfo()
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return s.cpuPhysicalCores, s.cpuLogicalCores, nil
|
|
}
|
|
|
|
// NewSystemInfo is a constructor for the gopsutil implementation of SystemInfo.
|
|
func NewSystemInfo() *systemInfo {
|
|
return &systemInfo{}
|
|
}
|