1 /************************************************************************** 2 * 3 * Copyright 2013 Marek Olk <maraeo (at) gmail.com> 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21 * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR 22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 28 /* This file contains code for reading CPU load for displaying on the HUD. 29 */ 30 31 #include "hud/hud_private.h" 32 #include "util/os_time.h" 33 #include "os/os_thread.h" 34 #include "util/u_memory.h" 35 #include "util/u_queue.h" 36 #include <stdio.h> 37 #include <inttypes.h> 38 #ifdef PIPE_OS_WINDOWS 39 #include <windows.h> 40 #endif 41 42 43 #ifdef PIPE_OS_WINDOWS 44 45 static inline uint64_t 46 filetime_to_scalar(FILETIME ft) 47 { 48 ULARGE_INTEGER uli; 49 uli.LowPart = ft.dwLowDateTime; 50 uli.HighPart = ft.dwHighDateTime; 51 return uli.QuadPart; 52 } 53 54 static boolean 55 get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time) 56 { 57 SYSTEM_INFO sysInfo; 58 FILETIME ftNow, ftCreation, ftExit, ftKernel, ftUser; 59 60 GetSystemInfo(&sysInfo); 61 assert(sysInfo.dwNumberOfProcessors >= 1); 62 if (cpu_index != ALL_CPUS && cpu_index >= sysInfo.dwNumberOfProcessors) { 63 /* Tell hud_get_num_cpus there are only this many CPUs. */ 64 return FALSE; 65 } 66 67 /* Get accumulated user and sys time for all threads */ 68 if (!GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit, 69 &ftKernel, &ftUser)) 70 return FALSE; 71 72 GetSystemTimeAsFileTime(&ftNow); 73 74 *busy_time = filetime_to_scalar(ftUser) + filetime_to_scalar(ftKernel); 75 *total_time = filetime_to_scalar(ftNow) - filetime_to_scalar(ftCreation); 76 77 /* busy_time already has the time accross all cpus. 78 * XXX: if we want 100% to mean one CPU, 200% two cpus, eliminate the 79 * following line. 80 */ 81 *total_time *= sysInfo.dwNumberOfProcessors; 82 83 /* XXX: we ignore cpu_index, i.e, we assume that the individual CPU usage 84 * and the system usage are one and the same. 85 */ 86 return TRUE; 87 } 88 89 #else 90 91 static boolean 92 get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time) 93 { 94 char cpuname[32]; 95 char line[1024]; 96 FILE *f; 97 98 if (cpu_index == ALL_CPUS) 99 strcpy(cpuname, "cpu"); 100 else 101 sprintf(cpuname, "cpu%u", cpu_index); 102 103 f = fopen("/proc/stat", "r"); 104 if (!f) 105 return FALSE; 106 107 while (!feof(f) && fgets(line, sizeof(line), f)) { 108 if (strstr(line, cpuname) == line) { 109 uint64_t v[12]; 110 int i, num; 111 112 num = sscanf(line, 113 "%s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64 114 " %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64 115 " %"PRIu64" %"PRIu64"", 116 cpuname, &v[0], &v[1], &v[2], &v[3], &v[4], &v[5], 117 &v[6], &v[7], &v[8], &v[9], &v[10], &v[11]); 118 if (num < 5) { 119 fclose(f); 120 return FALSE; 121 } 122 123 /* user + nice + system */ 124 *busy_time = v[0] + v[1] + v[2]; 125 *total_time = *busy_time; 126 127 /* ... + idle + iowait + irq + softirq + ... */ 128 for (i = 3; i < num-1; i++) { 129 *total_time += v[i]; 130 } 131 fclose(f); 132 return TRUE; 133 } 134 } 135 fclose(f); 136 return FALSE; 137 } 138 #endif 139 140 141 struct cpu_info { 142 unsigned cpu_index; 143 uint64_t last_cpu_busy, last_cpu_total, last_time; 144 }; 145 146 static void 147 query_cpu_load(struct hud_graph *gr, struct pipe_context *pipe) 148 { 149 struct cpu_info *info = gr->query_data; 150 uint64_t now = os_time_get(); 151 152 if (info->last_time) { 153 if (info->last_time + gr->pane->period <= now) { 154 uint64_t cpu_busy, cpu_total; 155 double cpu_load; 156 157 get_cpu_stats(info->cpu_index, &cpu_busy, &cpu_total); 158 159 cpu_load = (cpu_busy - info->last_cpu_busy) * 100 / 160 (double)(cpu_total - info->last_cpu_total); 161 hud_graph_add_value(gr, cpu_load); 162 163 info->last_cpu_busy = cpu_busy; 164 info->last_cpu_total = cpu_total; 165 info->last_time = now; 166 } 167 } 168 else { 169 /* initialize */ 170 info->last_time = now; 171 get_cpu_stats(info->cpu_index, &info->last_cpu_busy, 172 &info->last_cpu_total); 173 } 174 } 175 176 static void 177 free_query_data(void *p, struct pipe_context *pipe) 178 { 179 FREE(p); 180 } 181 182 void 183 hud_cpu_graph_install(struct hud_pane *pane, unsigned cpu_index) 184 { 185 struct hud_graph *gr; 186 struct cpu_info *info; 187 uint64_t busy, total; 188 189 /* see if the cpu exists */ 190 if (cpu_index != ALL_CPUS && !get_cpu_stats(cpu_index, &busy, &total)) { 191 return; 192 } 193 194 gr = CALLOC_STRUCT(hud_graph); 195 if (!gr) 196 return; 197 198 if (cpu_index == ALL_CPUS) 199 strcpy(gr->name, "cpu"); 200 else 201 sprintf(gr->name, "cpu%u", cpu_index); 202 203 gr->query_data = CALLOC_STRUCT(cpu_info); 204 if (!gr->query_data) { 205 FREE(gr); 206 return; 207 } 208 209 gr->query_new_value = query_cpu_load; 210 211 /* Don't use free() as our callback as that messes up Gallium's 212 * memory debugger. Use simple free_query_data() wrapper. 213 */ 214 gr->free_query_data = free_query_data; 215 216 info = gr->query_data; 217 info->cpu_index = cpu_index; 218 219 hud_pane_add_graph(pane, gr); 220 hud_pane_set_max_value(pane, 100); 221 } 222 223 int 224 hud_get_num_cpus(void) 225 { 226 uint64_t busy, total; 227 int i = 0; 228 229 while (get_cpu_stats(i, &busy, &total)) 230 i++; 231 232 return i; 233 } 234 235 struct thread_info { 236 bool main_thread; 237 int64_t last_time; 238 int64_t last_thread_time; 239 }; 240 241 static void 242 query_api_thread_busy_status(struct hud_graph *gr, struct pipe_context *pipe) 243 { 244 struct thread_info *info = gr->query_data; 245 int64_t now = os_time_get_nano(); 246 247 if (info->last_time) { 248 if (info->last_time + gr->pane->period*1000 <= now) { 249 int64_t thread_now; 250 251 if (info->main_thread) { 252 thread_now = pipe_current_thread_get_time_nano(); 253 } else { 254 struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue; 255 256 if (mon && mon->queue) 257 thread_now = util_queue_get_thread_time_nano(mon->queue, 0); 258 else 259 thread_now = 0; 260 } 261 262 double percent = (thread_now - info->last_thread_time) * 100.0 / 263 (now - info->last_time); 264 265 /* Check if the context changed a thread, so that we don't show 266 * a random value. When a thread is changed, the new thread clock 267 * is different, which can result in "percent" being very high. 268 */ 269 if (percent > 100.0) 270 percent = 0.0; 271 hud_graph_add_value(gr, percent); 272 273 info->last_thread_time = thread_now; 274 info->last_time = now; 275 } 276 } else { 277 /* initialize */ 278 info->last_time = now; 279 info->last_thread_time = pipe_current_thread_get_time_nano(); 280 } 281 } 282 283 void 284 hud_thread_busy_install(struct hud_pane *pane, const char *name, bool main) 285 { 286 struct hud_graph *gr; 287 288 gr = CALLOC_STRUCT(hud_graph); 289 if (!gr) 290 return; 291 292 strcpy(gr->name, name); 293 294 gr->query_data = CALLOC_STRUCT(thread_info); 295 if (!gr->query_data) { 296 FREE(gr); 297 return; 298 } 299 300 ((struct thread_info*)gr->query_data)->main_thread = main; 301 gr->query_new_value = query_api_thread_busy_status; 302 303 /* Don't use free() as our callback as that messes up Gallium's 304 * memory debugger. Use simple free_query_data() wrapper. 305 */ 306 gr->free_query_data = free_query_data; 307 308 hud_pane_add_graph(pane, gr); 309 hud_pane_set_max_value(pane, 100); 310 } 311 312 struct counter_info { 313 enum hud_counter counter; 314 unsigned last_value; 315 int64_t last_time; 316 }; 317 318 static unsigned get_counter(struct hud_graph *gr, enum hud_counter counter) 319 { 320 struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue; 321 322 if (!mon || !mon->queue) 323 return 0; 324 325 switch (counter) { 326 case HUD_COUNTER_OFFLOADED: 327 return mon->num_offloaded_items; 328 case HUD_COUNTER_DIRECT: 329 return mon->num_direct_items; 330 case HUD_COUNTER_SYNCS: 331 return mon->num_syncs; 332 default: 333 assert(0); 334 return 0; 335 } 336 } 337 338 static void 339 query_thread_counter(struct hud_graph *gr, struct pipe_context *pipe) 340 { 341 struct counter_info *info = gr->query_data; 342 int64_t now = os_time_get_nano(); 343 344 if (info->last_time) { 345 if (info->last_time + gr->pane->period*1000 <= now) { 346 unsigned current_value = get_counter(gr, info->counter); 347 348 hud_graph_add_value(gr, current_value - info->last_value); 349 info->last_value = current_value; 350 info->last_time = now; 351 } 352 } else { 353 /* initialize */ 354 info->last_value = get_counter(gr, info->counter); 355 info->last_time = now; 356 } 357 } 358 359 void hud_thread_counter_install(struct hud_pane *pane, const char *name, 360 enum hud_counter counter) 361 { 362 struct hud_graph *gr = CALLOC_STRUCT(hud_graph); 363 if (!gr) 364 return; 365 366 strcpy(gr->name, name); 367 368 gr->query_data = CALLOC_STRUCT(counter_info); 369 if (!gr->query_data) { 370 FREE(gr); 371 return; 372 } 373 374 ((struct counter_info*)gr->query_data)->counter = counter; 375 gr->query_new_value = query_thread_counter; 376 377 /* Don't use free() as our callback as that messes up Gallium's 378 * memory debugger. Use simple free_query_data() wrapper. 379 */ 380 gr->free_query_data = free_query_data; 381 382 hud_pane_add_graph(pane, gr); 383 hud_pane_set_max_value(pane, 100); 384 } 385