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 "os/os_time.h" 33 #include "util/u_memory.h" 34 #include <stdio.h> 35 #include <inttypes.h> 36 #ifdef PIPE_OS_WINDOWS 37 #include <windows.h> 38 #endif 39 40 41 #ifdef PIPE_OS_WINDOWS 42 43 static inline uint64_t 44 filetime_to_scalar(FILETIME ft) 45 { 46 ULARGE_INTEGER uli; 47 uli.LowPart = ft.dwLowDateTime; 48 uli.HighPart = ft.dwHighDateTime; 49 return uli.QuadPart; 50 } 51 52 static boolean 53 get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time) 54 { 55 SYSTEM_INFO sysInfo; 56 FILETIME ftNow, ftCreation, ftExit, ftKernel, ftUser; 57 58 GetSystemInfo(&sysInfo); 59 assert(sysInfo.dwNumberOfProcessors >= 1); 60 if (cpu_index != ALL_CPUS && cpu_index >= sysInfo.dwNumberOfProcessors) { 61 /* Tell hud_get_num_cpus there are only this many CPUs. */ 62 return FALSE; 63 } 64 65 /* Get accumulated user and sys time for all threads */ 66 if (!GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit, 67 &ftKernel, &ftUser)) 68 return FALSE; 69 70 GetSystemTimeAsFileTime(&ftNow); 71 72 *busy_time = filetime_to_scalar(ftUser) + filetime_to_scalar(ftKernel); 73 *total_time = filetime_to_scalar(ftNow) - filetime_to_scalar(ftCreation); 74 75 /* busy_time already has the time accross all cpus. 76 * XXX: if we want 100% to mean one CPU, 200% two cpus, eliminate the 77 * following line. 78 */ 79 *total_time *= sysInfo.dwNumberOfProcessors; 80 81 /* XXX: we ignore cpu_index, i.e, we assume that the individual CPU usage 82 * and the system usage are one and the same. 83 */ 84 return TRUE; 85 } 86 87 #else 88 89 static boolean 90 get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time) 91 { 92 char cpuname[32]; 93 char line[1024]; 94 FILE *f; 95 96 if (cpu_index == ALL_CPUS) 97 strcpy(cpuname, "cpu"); 98 else 99 sprintf(cpuname, "cpu%u", cpu_index); 100 101 f = fopen("/proc/stat", "r"); 102 if (!f) 103 return FALSE; 104 105 while (!feof(f) && fgets(line, sizeof(line), f)) { 106 if (strstr(line, cpuname) == line) { 107 uint64_t v[12]; 108 int i, num; 109 110 num = sscanf(line, 111 "%s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64 112 " %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64 113 " %"PRIu64" %"PRIu64"", 114 cpuname, &v[0], &v[1], &v[2], &v[3], &v[4], &v[5], 115 &v[6], &v[7], &v[8], &v[9], &v[10], &v[11]); 116 if (num < 5) { 117 fclose(f); 118 return FALSE; 119 } 120 121 /* user + nice + system */ 122 *busy_time = v[0] + v[1] + v[2]; 123 *total_time = *busy_time; 124 125 /* ... + idle + iowait + irq + softirq + ... */ 126 for (i = 3; i < num-1; i++) { 127 *total_time += v[i]; 128 } 129 fclose(f); 130 return TRUE; 131 } 132 } 133 fclose(f); 134 return FALSE; 135 } 136 #endif 137 138 139 struct cpu_info { 140 unsigned cpu_index; 141 uint64_t last_cpu_busy, last_cpu_total, last_time; 142 }; 143 144 static void 145 query_cpu_load(struct hud_graph *gr) 146 { 147 struct cpu_info *info = gr->query_data; 148 uint64_t now = os_time_get(); 149 150 if (info->last_time) { 151 if (info->last_time + gr->pane->period <= now) { 152 uint64_t cpu_busy, cpu_total, cpu_load; 153 154 get_cpu_stats(info->cpu_index, &cpu_busy, &cpu_total); 155 156 cpu_load = (cpu_busy - info->last_cpu_busy) * 100 / 157 (double)(cpu_total - info->last_cpu_total); 158 hud_graph_add_value(gr, cpu_load); 159 160 info->last_cpu_busy = cpu_busy; 161 info->last_cpu_total = cpu_total; 162 info->last_time = now; 163 } 164 } 165 else { 166 /* initialize */ 167 info->last_time = now; 168 get_cpu_stats(info->cpu_index, &info->last_cpu_busy, 169 &info->last_cpu_total); 170 } 171 } 172 173 static void 174 free_query_data(void *p) 175 { 176 FREE(p); 177 } 178 179 void 180 hud_cpu_graph_install(struct hud_pane *pane, unsigned cpu_index) 181 { 182 struct hud_graph *gr; 183 struct cpu_info *info; 184 uint64_t busy, total; 185 186 /* see if the cpu exists */ 187 if (cpu_index != ALL_CPUS && !get_cpu_stats(cpu_index, &busy, &total)) { 188 return; 189 } 190 191 gr = CALLOC_STRUCT(hud_graph); 192 if (!gr) 193 return; 194 195 if (cpu_index == ALL_CPUS) 196 strcpy(gr->name, "cpu"); 197 else 198 sprintf(gr->name, "cpu%u", cpu_index); 199 200 gr->query_data = CALLOC_STRUCT(cpu_info); 201 if (!gr->query_data) { 202 FREE(gr); 203 return; 204 } 205 206 gr->query_new_value = query_cpu_load; 207 208 /* Don't use free() as our callback as that messes up Gallium's 209 * memory debugger. Use simple free_query_data() wrapper. 210 */ 211 gr->free_query_data = free_query_data; 212 213 info = gr->query_data; 214 info->cpu_index = cpu_index; 215 216 hud_graph_set_dump_file(gr); 217 218 hud_pane_add_graph(pane, gr); 219 hud_pane_set_max_value(pane, 100); 220 } 221 222 int 223 hud_get_num_cpus(void) 224 { 225 uint64_t busy, total; 226 int i = 0; 227 228 while (get_cpu_stats(i, &busy, &total)) 229 i++; 230 231 return i; 232 } 233