(ctx context.Context, nodeID int)
| 21 | ) |
| 22 | |
| 23 | func CachesForNode(ctx context.Context, nodeID int) ([]*Cache, error) { |
| 24 | // The /sys/devices/node/nodeX directory contains a subdirectory called |
| 25 | // 'cpuX' for each logical processor assigned to the node. Each of those |
| 26 | // subdirectories containers a 'cache' subdirectory which contains a number |
| 27 | // of subdirectories beginning with 'index' and ending in the cache's |
| 28 | // internal 0-based identifier. Those subdirectories contain a number of |
| 29 | // files, including 'shared_cpu_list', 'size', and 'type' which we use to |
| 30 | // determine cache characteristics. |
| 31 | paths := linuxpath.New(ctx) |
| 32 | path := filepath.Join( |
| 33 | paths.SysDevicesSystemNode, |
| 34 | fmt.Sprintf("node%d", nodeID), |
| 35 | ) |
| 36 | caches := make(map[string]*Cache) |
| 37 | |
| 38 | files, err := os.ReadDir(path) |
| 39 | if err != nil { |
| 40 | return nil, err |
| 41 | } |
| 42 | for _, file := range files { |
| 43 | filename := file.Name() |
| 44 | if !strings.HasPrefix(filename, "cpu") { |
| 45 | continue |
| 46 | } |
| 47 | if filename == "cpumap" || filename == "cpulist" { |
| 48 | // There are two files in the node directory that start with 'cpu' |
| 49 | // but are not subdirectories ('cpulist' and 'cpumap'). Ignore |
| 50 | // these files. |
| 51 | continue |
| 52 | } |
| 53 | // Grab the logical processor ID by cutting the integer from the |
| 54 | // /sys/devices/system/node/nodeX/cpuX filename |
| 55 | cpuPath := filepath.Join(path, filename) |
| 56 | lpID, _ := strconv.Atoi(filename[3:]) |
| 57 | |
| 58 | // Inspect the caches for each logical processor. There will be a |
| 59 | // /sys/devices/system/node/nodeX/cpuX/cache directory containing a |
| 60 | // number of directories beginning with the prefix "index" followed by |
| 61 | // a number. The number indicates the level of the cache, which |
| 62 | // indicates the "distance" from the processor. Each of these |
| 63 | // directories contains information about the size of that level of |
| 64 | // cache and the processors mapped to it. |
| 65 | cachePath := filepath.Join(cpuPath, "cache") |
| 66 | if _, err = os.Stat(cachePath); errors.Is(err, os.ErrNotExist) { |
| 67 | continue |
| 68 | } |
| 69 | cacheDirFiles, err := os.ReadDir(cachePath) |
| 70 | if err != nil { |
| 71 | return nil, err |
| 72 | } |
| 73 | for _, cacheDirFile := range cacheDirFiles { |
| 74 | cacheDirFileName := cacheDirFile.Name() |
| 75 | if !strings.HasPrefix(cacheDirFileName, "index") { |
| 76 | continue |
| 77 | } |
| 78 | cacheIndex, _ := strconv.Atoi(cacheDirFileName[5:]) |
| 79 | |
| 80 | // The cache information is repeated for each node, so here, we |
no test coverage detected
searching dependent graphs…