1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
| // https://github.com/prometheus-community/windows_exporter/pull/727
package collector
import (
"bytes"
"fmt"
"strings"
"syscall"
"unsafe"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
"golang.org/x/sys/windows"
)
// technical documentation for the shared memory structure: https://www.alcpu.com/CoreTemp/developers.html
var (
kernel32 = syscall.NewLazyDLL("KERNEL32.dll")
msvcrt = syscall.NewLazyDLL("msvcrt.dll")
createMutexW = kernel32.NewProc("CreateMutexW")
releaseMutex = kernel32.NewProc("ReleaseMutex")
openFileMappingW = kernel32.NewProc("OpenFileMappingW")
closeHandle = kernel32.NewProc("CloseHandle")
mapViewOfFile = kernel32.NewProc("MapViewOfFile")
unmapViewOfFile = kernel32.NewProc("UnmapViewOfFile")
memcpy_s = msvcrt.NewProc("memcpy_s")
)
func init() {
registerCollector("coretemp", NewCoreTempCollector)
}
// A coreTempCollector is a Prometheus collector for CoreTemp shared data metrics
type coreTempCollector struct {
Temperature *prometheus.Desc
Load *prometheus.Desc
}
// NewCoreTempCollector ...
func NewCoreTempCollector() (Collector, error) {
const subsystem = "coretemp"
return &coreTempCollector{
Temperature: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "temperature_celsius"),
"(Temperature)",
[]string{"name", "core"},
nil,
),
Load: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "load"),
"(Load)",
[]string{"name", "core"},
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *coreTempCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting coretemp metrics:", desc, err)
return err
}
return nil
}
func (c *coreTempCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
s, err := readCoreTempSharedData()
if err != nil {
return nil, err
}
cpuName := s.GetCPUName()
for i := uint32(0); i < s.CoreCount; i++ {
// convert to celsius if internal unit is fahrenheit
temp := float64(s.Temp[i])
if s.IsFahrenheit {
temp = (temp - 32) * 5 / 9
}
ch <- prometheus.MustNewConstMetric(
c.Temperature,
prometheus.GaugeValue,
temp,
cpuName,
fmt.Sprintf("%d", i),
)
ch <- prometheus.MustNewConstMetric(
c.Load,
prometheus.GaugeValue,
float64(s.Load[i]),
cpuName,
fmt.Sprintf("%d", i),
)
}
return nil, nil
}
// read memory from CoreTemp to fetch cpu data
func readCoreTempSharedData() (*coreTempSharedData, error) {
mutexName, _ := windows.UTF16PtrFromString("CoreTempMutexObject")
mutexObject, _, err := createMutexW.Call(0, 0, uintptr(unsafe.Pointer(mutexName)))
if err == nil {
return nil, fmt.Errorf("CoreTempMutexObject not found. make sure Core Temp is running")
}
defer releaseMutex.Call(mutexObject)
mappingName, _ := windows.UTF16PtrFromString("CoreTempMappingObject")
mappingObject, _, err := CoreTempMappingObject.Call(4, 1, uintptr(unsafe.Pointer(mappingName)))
if mappingObject == uintptr(0) {
return nil, err
}
defer closeHandle.Call(mappingObject)
mapView, _, err := mapViewOfFile.Call(mappingObject, 4, 0, 0, 0)
if mapView == uintptr(0) {
return nil, err
}
defer unmapViewOfFile.Call(mapView)
data := coreTempSharedData{}
_, _, _ = memcpy_s.Call(uintptr(unsafe.Pointer(&data)), 0xa80, mapView, 0xa80)
return &data, nil
}
type coreTempSharedData struct {
Load [256]uint32
TjMax [128]uint32
CoreCount uint32
CPUCount uint32
Temp [256]float32
VID float32
CPUSpeed float32
FSBSpeed float32
Multipier float32
CPUName [100]byte
IsFahrenheit bool // if true, true, the temperature is reported in Fahrenheit
IsDeltaToTjMax bool // if true, the temperature reported represents the distance from TjMax
}
func (s *coreTempSharedData) GetCPUName() string {
n := bytes.IndexByte(s.CPUName[:], 0)
return strings.TrimSpace(string(s.CPUName[:n]))
}
|