/* ** Zabbix ** Copyright (C) 2001-2023 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ package cpu import ( "errors" "unsafe" "git.zabbix.com/ap/plugin-support/plugin" "git.zabbix.com/ap/plugin-support/zbxerr" "zabbix.com/pkg/win32" ) // Plugin - type Plugin struct { plugin.Base cpus []*cpuUnit collector *pdhCollector } const ( modeParam = 2 cpuParam = 1 noParam = 0 defaultIndex = 60 ) func numCPUOnline() int { return numCPU() } func numCPUConf() int { // unsupported on Windows return 0 } func numCPU() (numCpu int) { size, err := win32.GetLogicalProcessorInformationEx(win32.RelationProcessorCore, nil) if err != nil { return } b := make([]byte, size) size, err = win32.GetLogicalProcessorInformationEx(win32.RelationProcessorCore, b) if err != nil { return } var sinfo *win32.SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX for i := uint32(0); i < size; i += sinfo.Size { sinfo = (*win32.SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)(unsafe.Pointer(&b[i])) pinfo := (*win32.PROCESSOR_RELATIONSHIP)(unsafe.Pointer(&sinfo.Data[0])) groups := (*win32.RGGROUP_AFFINITY)(unsafe.Pointer(&pinfo.GroupMask[0]))[:pinfo.GroupCount:pinfo.GroupCount] for _, group := range groups { for mask := group.Mask; mask != 0; mask >>= 1 { numCpu += int(mask & 1) } } } return } func (p *Plugin) getCpuLoad(params []string) (result interface{}, err error) { split := 1 period := historyIndex(defaultIndex) switch len(params) { case modeParam: // mode parameter if period = periodByMode(params[1]); period < 0 { return nil, errors.New("Invalid first parameter.") } fallthrough case cpuParam: // all, cpu number or per cpu switch params[0] { case "", "all": case "percpu": split = numCPUOnline() default: return nil, errors.New("Invalid second parameter.") } case noParam: default: return nil, zbxerr.ErrorTooManyParameters } return p.cpus[0].counterAverage(counterLoad, period, split), nil } func (p *Plugin) Collect() (err error) { ok, err := p.collector.collect() if err != nil || !ok { return } for i, cpu := range p.cpus { slot := &cpu.history[cpu.tail] cpu.status = cpuStatusOnline if i == 0 { // gather cpu load into 'total' slot slot.load += p.collector.cpuLoad() } slot.util += p.collector.cpuUtil(i) if cpu.tail = cpu.tail.inc(); cpu.tail == cpu.head { cpu.head = cpu.head.inc() } // write the current value into next slot so next time the new value // can be added to it resulting in incrementing counter nextSlot := &cpu.history[cpu.tail] *nextSlot = *slot } return } func (p *Plugin) Start() { numCpus := numCPU() numGroups := getNumaNodeCount() if numCpus == 0 || numGroups == 0 { p.Warningf("cannot calculate the number of CPUs per group, only total values will be available") } p.cpus = p.newCpus(numCpus) p.collector.open(numCpus, numGroups) } func (p *Plugin) Stop() { p.collector.close() p.cpus = nil } func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) { if p.cpus == nil || p.cpus[0].head == p.cpus[0].tail { // no data gathered yet return } switch key { case "system.cpu.discovery": return p.getCpuDiscovery(params) case "system.cpu.load": return p.getCpuLoad(params) case "system.cpu.num": if len(params) > 0 && params[0] == "max" { return nil, errors.New("Invalid first parameter.") } return p.getCpuNum(params) case "system.cpu.util": return p.getCpuUtil(params) default: return nil, plugin.UnsupportedMetricError } } func init() { impl.collector = newPdhCollector(&impl) plugin.RegisterMetrics(&impl, pluginName, "system.cpu.discovery", "List of detected CPUs/CPU cores, used for low-level discovery.", "system.cpu.load", "CPU load.", "system.cpu.num", "Number of CPUs.", "system.cpu.util", "CPU utilization percentage.") }