forked from jaypipes/ghw
-
Notifications
You must be signed in to change notification settings - Fork 0
/
memory_cache_linux.go
204 lines (188 loc) · 5.88 KB
/
memory_cache_linux.go
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//
package ghw
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
)
func cachesForNode(nodeID int) ([]*MemoryCache, error) {
// The /sys/devices/node/nodeX directory contains a subdirectory called
// 'cpuX' for each logical processor assigned to the node. Each of those
// subdirectories containers a 'cache' subdirectory which contains a number
// of subdirectories beginning with 'index' and ending in the cache's
// internal 0-based identifier. Those subdirectories contain a number of
// files, including 'shared_cpu_list', 'size', and 'type' which we use to
// determine cache characteristics.
path := filepath.Join(
pathSysDevicesSystemNode(),
fmt.Sprintf("node%d", nodeID),
)
caches := make(map[string]*MemoryCache)
files, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
for _, file := range files {
filename := file.Name()
if !strings.HasPrefix(filename, "cpu") {
continue
}
if filename == "cpumap" || filename == "cpulist" {
// There are two files in the node directory that start with 'cpu'
// but are not subdirectories ('cpulist' and 'cpumap'). Ignore
// these files.
continue
}
// Grab the logical processor ID by cutting the integer from the
// /sys/devices/system/node/nodeX/cpuX filename
cpuPath := filepath.Join(path, filename)
lpID, _ := strconv.Atoi(filename[3:])
// Inspect the caches for each logical processor. There will be a
// /sys/devices/system/node/nodeX/cpuX/cache directory containing a
// number of directories beginning with the prefix "index" followed by
// a number. The number indicates the level of the cache, which
// indicates the "distance" from the processor. Each of these
// directories contains information about the size of that level of
// cache and the processors mapped to it.
cachePath := filepath.Join(cpuPath, "cache")
if _, err = os.Stat(cachePath); os.IsNotExist(err) {
continue
}
cacheDirFiles, err := ioutil.ReadDir(cachePath)
if err != nil {
return nil, err
}
for _, cacheDirFile := range cacheDirFiles {
cacheDirFileName := cacheDirFile.Name()
if !strings.HasPrefix(cacheDirFileName, "index") {
continue
}
cacheIndex, _ := strconv.Atoi(cacheDirFileName[5:])
// The cache information is repeated for each node, so here, we
// just ensure that we only have a one MemoryCache object for each
// unique combination of level, type and processor map
level := memoryCacheLevel(nodeID, lpID, cacheIndex)
cacheType := memoryCacheType(nodeID, lpID, cacheIndex)
sharedCpuMap := memoryCacheSharedCPUMap(nodeID, lpID, cacheIndex)
cacheKey := fmt.Sprintf("%d-%d-%s", level, cacheType, sharedCpuMap)
cache, exists := caches[cacheKey]
if !exists {
size := memoryCacheSize(nodeID, lpID, level)
cache = &MemoryCache{
Level: uint8(level),
Type: cacheType,
SizeBytes: uint64(size) * uint64(KB),
LogicalProcessors: make([]uint32, 0),
}
caches[cacheKey] = cache
}
cache.LogicalProcessors = append(
cache.LogicalProcessors,
uint32(lpID),
)
}
}
cacheVals := make([]*MemoryCache, len(caches))
x := 0
for _, c := range caches {
// ensure the cache's processor set is sorted by logical process ID
sort.Sort(SortByLogicalProcessorId(c.LogicalProcessors))
cacheVals[x] = c
x++
}
return cacheVals, nil
}
func pathNodeCPU(nodeID int, lpID int) string {
return filepath.Join(
pathSysDevicesSystemNode(),
fmt.Sprintf("node%d", nodeID),
fmt.Sprintf("cpu%d", lpID),
)
}
func pathNodeCPUCache(nodeID int, lpID int) string {
return filepath.Join(
pathNodeCPU(nodeID, lpID),
"cache",
)
}
func pathNodeCPUCacheIndex(nodeID int, lpID int, cacheIndex int) string {
return filepath.Join(
pathNodeCPUCache(nodeID, lpID),
fmt.Sprintf("index%d", cacheIndex),
)
}
func memoryCacheLevel(nodeID int, lpID int, cacheIndex int) int {
levelPath := filepath.Join(
pathNodeCPUCacheIndex(nodeID, lpID, cacheIndex),
"level",
)
levelContents, err := ioutil.ReadFile(levelPath)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%s\n", err)
return -1
}
// levelContents is now a []byte with the last byte being a newline
// character. Trim that off and convert the contents to an integer.
level, err := strconv.Atoi(string(levelContents[:len(levelContents)-1]))
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Unable to parse int from %s\n", levelContents)
return -1
}
return level
}
func memoryCacheSize(nodeID int, lpID int, cacheIndex int) int {
sizePath := filepath.Join(
pathNodeCPUCacheIndex(nodeID, lpID, cacheIndex),
"size",
)
sizeContents, err := ioutil.ReadFile(sizePath)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%s\n", err)
return -1
}
// size comes as XK\n, so we trim off the K and the newline.
size, err := strconv.Atoi(string(sizeContents[:len(sizeContents)-2]))
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Unable to parse int from %s\n", sizeContents)
return -1
}
return size
}
func memoryCacheType(nodeID int, lpID int, cacheIndex int) MemoryCacheType {
typePath := filepath.Join(
pathNodeCPUCacheIndex(nodeID, lpID, cacheIndex),
"type",
)
cacheTypeContents, err := ioutil.ReadFile(typePath)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%s\n", err)
return UNIFIED
}
switch string(cacheTypeContents[:len(cacheTypeContents)-1]) {
case "Data":
return DATA
case "Instruction":
return INSTRUCTION
default:
return UNIFIED
}
}
func memoryCacheSharedCPUMap(nodeID int, lpID int, cacheIndex int) string {
scpuPath := filepath.Join(
pathNodeCPUCacheIndex(nodeID, lpID, cacheIndex),
"shared_cpu_map",
)
sharedCpuMap, err := ioutil.ReadFile(scpuPath)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%s\n", err)
return ""
}
return string(sharedCpuMap[:len(sharedCpuMap)-1])
}