1 | /* |
2 | KSysGuard, the KDE System Guard |
3 | |
4 | Copyright (c) 2000-2001 Chris Schlaeger <cs@kde.org> |
5 | |
6 | This program is free software; you can redistribute it and/or |
7 | modify it under the terms of version 2 of the GNU General Public |
8 | License as published by the Free Software Foundation. |
9 | |
10 | This program is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | GNU General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU General Public License |
16 | along with this program; if not, write to the Free Software |
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | |
19 | */ |
20 | |
21 | #include <sys/types.h> |
22 | #include <sys/stat.h> |
23 | #include <fcntl.h> |
24 | #include <unistd.h> |
25 | #include <stdlib.h> |
26 | #include <stdio.h> |
27 | #include <string.h> |
28 | #include <ctype.h> |
29 | #include <time.h> |
30 | |
31 | #include "Command.h" |
32 | #include "ksysguardd.h" |
33 | |
34 | #include "cpuinfo.h" |
35 | |
36 | static int CpuInfoOK = 0; |
37 | static int numProcessors = 0; /* Total number of physical processors */ |
38 | static int HighNumProcessors = 0; /* Highest # number of physical processors ever seen */ |
39 | static int numCores = 0; /* Total # of cores */ |
40 | static int HighNumCores = 0; /* Highest # of cores ever seen */ |
41 | static float* Clocks = 0; /* Array with one entry per core */ |
42 | |
43 | #define CPUINFOBUFSIZE (32 * 1024) |
44 | static char CpuInfoBuf[ CPUINFOBUFSIZE ]; |
45 | static int Dirty = 0; |
46 | static struct SensorModul *CpuInfoSM; |
47 | |
48 | static void processCpuInfo( void ) |
49 | { |
50 | char format[ 32 ]; |
51 | char tag[ 32 ]; |
52 | char value[ 256 ]; |
53 | char* cibp = CpuInfoBuf; |
54 | |
55 | /* coreUniqueId is not per processor; it is a counter of the number of cores encountered |
56 | * by the parse thus far */ |
57 | int coreUniqueId = 0; |
58 | |
59 | /* Reset global variables */ |
60 | numCores = 0; |
61 | numProcessors = 0; |
62 | |
63 | if ( !CpuInfoOK ) |
64 | return; |
65 | |
66 | sprintf( format, "%%%d[^:]: %%%d[^\n]\n" , (int)sizeof( tag ) - 1, |
67 | (int)sizeof( value ) - 1 ); |
68 | |
69 | while ( sscanf( cibp, format, tag, value ) == 2 ) { |
70 | char* p; |
71 | |
72 | tag[ sizeof( tag ) - 1 ] = '\0'; |
73 | value[ sizeof( value ) - 1 ] = '\0'; |
74 | |
75 | /* remove trailing whitespaces */ |
76 | p = tag + strlen( tag ) - 1; |
77 | /* remove trailing whitespaces */ |
78 | while ( ( *p == ' ' || *p == '\t' ) && p > tag ) |
79 | *p-- = '\0'; |
80 | |
81 | if ( strcmp( tag, "processor" ) == 0 ) { |
82 | if ( sscanf( value, "%d" , &coreUniqueId ) == 1 ) { |
83 | if ( coreUniqueId >= HighNumCores ) { |
84 | /* Found a new processor core. Maybe even a new processor. (We'll check later) */ |
85 | char cmdName[ 24 ]; |
86 | |
87 | /* Each core has a clock speed. Allocate one per core found. */ |
88 | Clocks = (float*) realloc( Clocks, (coreUniqueId+1) * sizeof( float ) ); |
89 | memset(Clocks + HighNumCores, 0, (coreUniqueId +1 - HighNumCores) * sizeof( float )); |
90 | |
91 | HighNumCores = coreUniqueId + 1; |
92 | |
93 | snprintf( cmdName, sizeof( cmdName ) - 1, "cpu/cpu%d/clock" , coreUniqueId ); |
94 | registerMonitor( cmdName, "float" , printCPUxClock, printCPUxClockInfo, |
95 | CpuInfoSM ); |
96 | } |
97 | } |
98 | } else if ( strcmp( tag, "cpu MHz" ) == 0 ) { |
99 | if (HighNumCores > coreUniqueId) { |
100 | /* The if statement above *should* always be true, but there's no harm in being safe. */ |
101 | sscanf( value, "%f" , &Clocks[ coreUniqueId ] ); |
102 | } |
103 | } else if ( strcmp( tag, "core id" ) == 0 ) { |
104 | /* the core id is per processor */ |
105 | int curCore; |
106 | |
107 | if ( (sscanf( value, "%d" , &curCore ) == 1) && curCore == 0 ) { |
108 | /* core id is back at 0. We just found a new processor. */ |
109 | numProcessors++; |
110 | |
111 | if (numProcessors > HighNumProcessors) |
112 | HighNumProcessors = numProcessors; |
113 | } |
114 | } |
115 | /* Move cibp to beginning of next line, if there is one. */ |
116 | cibp = strchr( cibp, '\n' ); |
117 | if ( cibp ) |
118 | cibp++; |
119 | else |
120 | cibp = CpuInfoBuf + strlen( CpuInfoBuf ); |
121 | } |
122 | |
123 | numCores = coreUniqueId + 1; |
124 | |
125 | Dirty = 0; |
126 | } |
127 | |
128 | /* |
129 | ================================ public part ================================= |
130 | */ |
131 | |
132 | void initCpuInfo( struct SensorModul* sm ) |
133 | { |
134 | CpuInfoSM = sm; |
135 | |
136 | if ( updateCpuInfo() < 0 ) |
137 | return; |
138 | |
139 | registerMonitor( "system/processors" , "integer" , printNumCpus, printNumCpusInfo, |
140 | CpuInfoSM ); |
141 | registerMonitor( "system/cores" , "integer" , printNumCores, printNumCoresInfo, |
142 | CpuInfoSM ); |
143 | |
144 | processCpuInfo(); |
145 | |
146 | registerMonitor( "cpu/system/AverageClock" , "float" , printCPUClock, printCPUClockInfo, |
147 | CpuInfoSM ); |
148 | } |
149 | |
150 | void exitCpuInfo( void ) |
151 | { |
152 | CpuInfoOK = -1; |
153 | |
154 | free( Clocks ); |
155 | } |
156 | |
157 | int updateCpuInfo( void ) |
158 | { |
159 | size_t n; |
160 | int fd; |
161 | |
162 | if ( CpuInfoOK < 0 ) |
163 | return -1; |
164 | |
165 | if ( ( fd = open( "/proc/cpuinfo" , O_RDONLY ) ) < 0 ) { |
166 | if ( CpuInfoOK != 0 ) |
167 | print_error( "Cannot open file \'/proc/cpuinfo\'!\n" |
168 | "The kernel needs to be compiled with support\n" |
169 | "for /proc file system enabled!\n" ); |
170 | CpuInfoOK = -1; |
171 | return -1; |
172 | } |
173 | |
174 | n = 0; |
175 | for(;;) { |
176 | ssize_t len = read( fd, CpuInfoBuf + n, CPUINFOBUFSIZE - 1 - n ); |
177 | if( len < 0 ) { |
178 | print_error( "Failed to read file \'/proc/cpuinfo\'!\n" ); |
179 | CpuInfoOK = -1; |
180 | close( fd ); |
181 | return -1; |
182 | } |
183 | n += len; |
184 | if( len == 0 ) /* reading finished */ |
185 | break; |
186 | if( n == CPUINFOBUFSIZE - 1 ) { |
187 | log_error( "Internal buffer too small to read \'/proc/cpuinfo\'" ); |
188 | CpuInfoOK = 0; |
189 | close( fd ); |
190 | return -1; |
191 | } |
192 | } |
193 | |
194 | close( fd ); |
195 | CpuInfoOK = 1; |
196 | CpuInfoBuf[ n ] = '\0'; |
197 | Dirty = 1; |
198 | |
199 | return 0; |
200 | } |
201 | |
202 | void printCPUxClock( const char* cmd ) |
203 | { |
204 | int id; |
205 | |
206 | if ( Dirty ) |
207 | processCpuInfo(); |
208 | |
209 | sscanf( cmd + 7, "%d" , &id ); |
210 | output( "%f\n" , Clocks[ id ] ); |
211 | } |
212 | |
213 | void printCPUClock( const char* cmd ) |
214 | { |
215 | int id; |
216 | float clock = 0; |
217 | cmd = cmd; /*Silence warning*/ |
218 | |
219 | if ( Dirty ) { |
220 | processCpuInfo(); |
221 | } |
222 | |
223 | for ( id = 0; id < HighNumCores; id++ ) { |
224 | clock += Clocks[ id ]; |
225 | } |
226 | clock /= HighNumCores; |
227 | output( "%f\n" , clock ); |
228 | } |
229 | |
230 | void printCPUxClockInfo( const char* cmd ) |
231 | { |
232 | int id; |
233 | |
234 | sscanf( cmd + 7, "%d" , &id ); |
235 | output( "CPU%d Clock Frequency\t0\t0\tMHz\n" , id ); |
236 | } |
237 | |
238 | void printCPUClockInfo( const char* cmd ) |
239 | { |
240 | cmd = cmd; /*Silence warning*/ |
241 | output( "CPU Clock Frequency\t0\t0\tMHz\n" ); |
242 | } |
243 | |
244 | void printNumCpus( const char* cmd ) |
245 | { |
246 | (void) cmd; |
247 | |
248 | if ( Dirty ) |
249 | processCpuInfo(); |
250 | |
251 | output( "%d\n" , numProcessors ); |
252 | } |
253 | |
254 | void printNumCpusInfo( const char* cmd ) |
255 | { |
256 | (void) cmd; |
257 | |
258 | output( "Number of physical CPUs\t0\t0\t\n" ); |
259 | } |
260 | |
261 | void printNumCores( const char* cmd ) |
262 | { |
263 | (void) cmd; |
264 | |
265 | if ( Dirty ) |
266 | processCpuInfo(); |
267 | |
268 | output( "%d\n" , numCores ); |
269 | } |
270 | |
271 | void printNumCoresInfo( const char* cmd ) |
272 | { |
273 | (void) cmd; |
274 | |
275 | output( "Total number of processor cores\t0\t0\t\n" ); |
276 | } |
277 | |