1 | //
|
---|
2 | // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo
|
---|
3 | //
|
---|
4 | // The contents of this file are covered under the licence agreement in the
|
---|
5 | // file "LICENCE" distributed with Cforall.
|
---|
6 | //
|
---|
7 | // cpu.cfa -- checks that libcfa correctly counts the number of cpus.
|
---|
8 | //
|
---|
9 | // Author : Thierry Delisle
|
---|
10 | // Created On : Mon Jun 14 13:59:01 2021
|
---|
11 | // Last Modified By :
|
---|
12 | // Last Modified On :
|
---|
13 | // Update Count :
|
---|
14 | //
|
---|
15 |
|
---|
16 |
|
---|
17 | #include <device/cpu.hfa>
|
---|
18 | #include <limits.hfa>
|
---|
19 | #include <fstream.hfa>
|
---|
20 | #include <stdlib.hfa>
|
---|
21 |
|
---|
22 | #include <errno.h>
|
---|
23 | #include <stdio.h>
|
---|
24 | #include <string.h>
|
---|
25 | #include <unistd.h>
|
---|
26 |
|
---|
27 | extern "C" {
|
---|
28 | #include <dirent.h>
|
---|
29 | #include <sys/types.h>
|
---|
30 | #include <sys/stat.h>
|
---|
31 | #include <sys/sysinfo.h>
|
---|
32 | #include <fcntl.h>
|
---|
33 | }
|
---|
34 |
|
---|
35 | // go through a directory calling fn on each file
|
---|
36 | static int iterate_dir( const char * path, void (*fn)(struct dirent * ent) ) {
|
---|
37 | // open the directory
|
---|
38 | DIR *dir = opendir(path);
|
---|
39 | if(dir == 0p) { return ENOTDIR; }
|
---|
40 |
|
---|
41 | // call fn for each
|
---|
42 | struct dirent * ent;
|
---|
43 | while ((ent = readdir(dir)) != 0p) {
|
---|
44 | fn( ent );
|
---|
45 | }
|
---|
46 |
|
---|
47 | // no longer need this
|
---|
48 | closedir(dir);
|
---|
49 | return 0;
|
---|
50 | }
|
---|
51 |
|
---|
52 | // count the number of directories with the specified prefix
|
---|
53 | // the directories counted have the form '[prefix]N' where prefix is the parameter
|
---|
54 | // and N is an base 10 integer.
|
---|
55 | static int count_prefix_dirs(const char * path, const char * prefix) {
|
---|
56 | // read the directory and find the cpu count
|
---|
57 | // and make sure everything is as expected
|
---|
58 | int max = -1;
|
---|
59 | int count = 0;
|
---|
60 | void lambda(struct dirent * ent) {
|
---|
61 | // were are looking for prefixX, where X is a number
|
---|
62 | // check that it starts with 'cpu
|
---|
63 | char * s = strstr(ent->d_name, prefix);
|
---|
64 | if(s == 0p) { return; }
|
---|
65 | if(s != ent->d_name) { return; }
|
---|
66 |
|
---|
67 | // check that the next part is a number
|
---|
68 | s += strlen(prefix);
|
---|
69 | char * end;
|
---|
70 | long int val = strtol(s, &end, 10);
|
---|
71 | if(*end != '\0' || val < 0) { return; }
|
---|
72 |
|
---|
73 | // check that it's a directory
|
---|
74 | if(ent->d_type != DT_DIR) { return; }
|
---|
75 |
|
---|
76 | // it's a match!
|
---|
77 | max = max(val, max);
|
---|
78 | count++;
|
---|
79 | }
|
---|
80 | iterate_dir(path, lambda);
|
---|
81 |
|
---|
82 | /* paranoid */ verifyf(count == max + 1, "Inconsistent %s count, counted %d, but max %s was %d", prefix, count, prefix, (int)max);
|
---|
83 |
|
---|
84 | return count;
|
---|
85 | }
|
---|
86 |
|
---|
87 | // Count number of cache *indexes* in the system
|
---|
88 | // cache indexes are distinct from cache level as Data or Instruction cache
|
---|
89 | // can share a level but not an index
|
---|
90 | // PITFALL: assumes all cpus have the same indexes as cpu0
|
---|
91 | static int count_cache_indexes(void) {
|
---|
92 | return count_prefix_dirs("/sys/devices/system/cpu/cpu0/cache", "index");
|
---|
93 | }
|
---|
94 |
|
---|
95 | // read information about a spcficic cache index/cpu file into the output buffer
|
---|
96 | static size_t read_cpuidxinfo_into(unsigned cpu, unsigned idx, const char * file, char * out, size_t out_len) {
|
---|
97 | // Pick the file we want and read it
|
---|
98 | char buf[128];
|
---|
99 | /* paranoid */ __attribute__((unused)) int len =
|
---|
100 | snprintf(buf, 128, "/sys/devices/system/cpu/cpu%u/cache/index%u/%s", cpu, idx, file);
|
---|
101 | /* paranoid */ verifyf(len > 0, "Could not generate '%s' filename for cpu %u, index %u", file, cpu, idx);
|
---|
102 |
|
---|
103 | int fd = open(buf, 0, O_RDONLY);
|
---|
104 | /* paranoid */ verifyf(fd > 0, "Could not open file '%s'", buf);
|
---|
105 |
|
---|
106 | ssize_t r = read(fd, out, out_len);
|
---|
107 | /* paranoid */ verifyf(r > 0, "Could not read file '%s'", buf);
|
---|
108 |
|
---|
109 | /* paranoid */ __attribute__((unused)) int ret =
|
---|
110 | close(fd);
|
---|
111 | /* paranoid */ verifyf(ret == 0, "Could not close file '%s'", buf);
|
---|
112 |
|
---|
113 | out[r-1] = '\0';
|
---|
114 | return r-1;
|
---|
115 | }
|
---|
116 |
|
---|
117 | unsigned find_idx() {
|
---|
118 | int idxs = count_cache_indexes();
|
---|
119 | if( 0 == idxs ) return 0;
|
---|
120 |
|
---|
121 | unsigned found_level = 0;
|
---|
122 | unsigned found = MAX;
|
---|
123 | for(i; idxs) {
|
---|
124 | unsigned idx = idxs - 1 - i;
|
---|
125 | char buf[32];
|
---|
126 |
|
---|
127 | // Level is the cache level: higher means bigger and slower
|
---|
128 | read_cpuidxinfo_into(0, idx, "level", buf, 32);
|
---|
129 | char * end;
|
---|
130 | unsigned long level = strtoul(buf, &end, 10);
|
---|
131 | /* paranoid */ verifyf(level <= 250, "Cpu %u has more than 250 levels of cache, that doesn't sound right", 0);
|
---|
132 | /* paranoid */ verify(*end == '\0');
|
---|
133 |
|
---|
134 | if(found_level < level) {
|
---|
135 | found_level = level;
|
---|
136 | found = idx;
|
---|
137 | }
|
---|
138 | }
|
---|
139 |
|
---|
140 | /* paranoid */ verify(found != MAX);
|
---|
141 | return found;
|
---|
142 | }
|
---|
143 |
|
---|
144 | int main() {
|
---|
145 | //-----------------------------------------------------------------------
|
---|
146 | int ret1 = get_nprocs();
|
---|
147 | int ret2 = cpu_info.hthrd_count;
|
---|
148 | if(ret1 != ret2) {
|
---|
149 | sout | "No match! libcfa says" | ret2 | "hardware threads but linux counts" | ret1 | "processors";
|
---|
150 | }
|
---|
151 | else {
|
---|
152 | sout | "Match!";
|
---|
153 | }
|
---|
154 |
|
---|
155 | //-----------------------------------------------------------------------
|
---|
156 | // Make sure no one has the same self
|
---|
157 | for(ime; cpu_info.hthrd_count) {
|
---|
158 | unsigned me = cpu_info.llc_map[ime].self;
|
---|
159 | {
|
---|
160 | unsigned s = cpu_info.llc_map[ime].start;
|
---|
161 | unsigned e = s + cpu_info.llc_map[ime].count;
|
---|
162 | if(me < s || me >= e) {
|
---|
163 | sout | "CPU" | ime | "outside of it's own map: " | s | "<=" | me | "<" | e;
|
---|
164 | }
|
---|
165 | }
|
---|
166 |
|
---|
167 |
|
---|
168 | for(ithem; cpu_info.hthrd_count) {
|
---|
169 | if(ime == ithem) continue;
|
---|
170 |
|
---|
171 | unsigned them = cpu_info.llc_map[ithem].self;
|
---|
172 | if(me == them) {
|
---|
173 | sout | "CPU" | ime | "has conflicting self id with" | ithem | "(" | me | ")";
|
---|
174 | }
|
---|
175 | }
|
---|
176 | }
|
---|
177 |
|
---|
178 |
|
---|
179 | //-----------------------------------------------------------------------
|
---|
180 | unsigned idx = find_idx();
|
---|
181 | // For all procs check mapping is consistent
|
---|
182 | if( idx > 0 ) for(cpu_me; cpu_info.hthrd_count) {
|
---|
183 | char buf_me[32];
|
---|
184 | size_t len_me = read_cpuidxinfo_into(cpu_me, idx, "shared_cpu_list", buf_me, 32);
|
---|
185 | for(cpu_them; cpu_info.hthrd_count) {
|
---|
186 | if(cpu_me == cpu_them) continue;
|
---|
187 | char buf_them[32];
|
---|
188 | size_t len_them = read_cpuidxinfo_into(cpu_them, idx, "shared_cpu_list", buf_them, 32);
|
---|
189 |
|
---|
190 | bool match_file = len_them == len_me && 0 == strncmp(buf_them, buf_me, len_me);
|
---|
191 | bool match_info = cpu_info.llc_map[cpu_me].start == cpu_info.llc_map[cpu_them].start && cpu_info.llc_map[cpu_me].count == cpu_info.llc_map[cpu_them].count;
|
---|
192 |
|
---|
193 | if(match_file != match_info) {
|
---|
194 | sout | "CPU" | cpu_me | "and" | cpu_them | "have inconsitent file and cpu_info";
|
---|
195 | sout | cpu_me | ": <" | cpu_info.llc_map[cpu_me ].start | "," | cpu_info.llc_map[cpu_me ].count | "> '" | buf_me | "'";
|
---|
196 | sout | cpu_me | ": <" | cpu_info.llc_map[cpu_them].start | "," | cpu_info.llc_map[cpu_them].count | "> '" | buf_them | "'";
|
---|
197 | }
|
---|
198 | }
|
---|
199 | }
|
---|
200 | }
|
---|