summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-12-19 15:42:04 +0000
committerThomas Martitz <kugel@rockbox.org>2011-12-19 15:42:04 +0000
commit287454de2e076eafcd393dec6cf0e2674ca25586 (patch)
treeab66ced6589ae8188dbcd110fc5572797b12155f
parentf53c04b766f470eb58c919b7d38f8bfec654c797 (diff)
downloadrockbox-287454de2e076eafcd393dec6cf0e2674ca25586.tar.gz
rockbox-287454de2e076eafcd393dec6cf0e2674ca25586.zip
Hosted/linux: Add process/cpu info screen to the debug menu.
The new menu is very helpful on RaaA, but also shown in the sim. It shows the process cpu usage, process' time stats (user,sys,real) and the cpu frequency stats. It uses a thread to sample the data, however the thread is not created until the menu is visited for the first time. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31364 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--android/android.make16
-rw-r--r--apps/debug_menu.c106
-rw-r--r--firmware/SOURCES6
-rw-r--r--firmware/target/hosted/cpuinfo-linux.c198
-rw-r--r--firmware/target/hosted/cpuinfo-linux.h45
5 files changed, 367 insertions, 4 deletions
diff --git a/android/android.make b/android/android.make
index e4fd3693de..2f422be736 100644
--- a/android/android.make
+++ b/android/android.make
@@ -7,13 +7,19 @@
7# $Id$ 7# $Id$
8# 8#
9 9
10# this is a glibc compatibility hack to provide a get_nprocs() replacement.
11# The NDK ships cpu-features.c which has a compatible function android_getCpuCount()
12CPUFEAT = /home/kugel/share/android-ndk-r6/sources/android/cpufeatures
13INCLUDES += -I$(CPUFEAT)
14OTHER_SRC += $(CPUFEAT)/cpu-features.c
15$(BUILDDIR)/cpu-features.o: $(CPUFEAT)/cpu-features.c
16 $(call PRINTS,CC $(subst $(ANDROID_NDK_PATH)/,,$<))$(CC) -o $@ -c $^ \
17 $(GCCOPTS) -Wno-unused
18
10.SECONDEXPANSION: # $$(JAVA_OBJ) is not populated until after this 19.SECONDEXPANSION: # $$(JAVA_OBJ) is not populated until after this
11.SECONDEXPANSION: # $$(OBJ) is not populated until after this 20.SECONDEXPANSION: # $$(OBJ) is not populated until after this
12.PHONY: apk classes clean dex dirs libs jar 21.PHONY: apk classes clean dex dirs libs jar
13 22
14$(BUILDDIR)/$(BINARY): $$(OBJ) $(VOICESPEEXLIB) $(FIRMLIB) $(SKINLIB)
15 $(call PRINTS,LD $(BINARY))$(CC) -o $@ $^ $(LDOPTS) $(GLOBAL_LDOPTS)
16
17PACKAGE=org.rockbox 23PACKAGE=org.rockbox
18PACKAGE_PATH=org/rockbox 24PACKAGE_PATH=org/rockbox
19ANDROID_DIR=$(ROOTDIR)/android 25ANDROID_DIR=$(ROOTDIR)/android
@@ -103,6 +109,10 @@ dex: $(DEX)
103 109
104classes: $(R_OBJ) $(JAVA_OBJ) 110classes: $(R_OBJ) $(JAVA_OBJ)
105 111
112
113$(BUILDDIR)/$(BINARY): $$(OBJ) $(VOICESPEEXLIB) $(FIRMLIB) $(SKINLIB) $(BUILDDIR)/cpu-features.o
114 $(call PRINTS,LD $(BINARY))$(CC) -o $@ $^ $(LDOPTS) $(GLOBAL_LDOPTS)
115
106$(BINLIB_DIR)/$(BINARY): $(BUILDDIR)/$(BINARY) 116$(BINLIB_DIR)/$(BINARY): $(BUILDDIR)/$(BINARY)
107 $(call PRINTS,CP $(BINARY))cp $^ $@ 117 $(call PRINTS,CP $(BINARY))cp $^ $@
108 118
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index e0343572b7..5773374d36 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -232,6 +232,108 @@ static bool dbg_os(void)
232 return simplelist_show_list(&info); 232 return simplelist_show_list(&info);
233} 233}
234 234
235#ifdef __linux__
236#include "cpuinfo-linux.h"
237
238#define MAX_STATES 16
239static struct time_state states[MAX_STATES];
240
241static const char* get_cpuinfo(int selected_item, void *data,
242 char *buffer, size_t buffer_len)
243{
244 (void)data;(void)buffer_len;
245 const char* text;
246 long time, diff;
247 struct cpuusage us;
248 static struct cpuusage last_us;
249 int state_count = *(int*)data;
250
251 if (cpuusage_linux(&us) != 0)
252 return NULL;
253
254 switch(selected_item)
255 {
256 case 0:
257 diff = abs(last_us.usage - us.usage);
258 sprintf(buffer, "Usage: %ld.%02ld%% (%c %ld.%02ld)",
259 us.usage/100, us.usage%100,
260 (us.usage >= last_us.usage) ? '+':'-',
261 diff/100, diff%100);
262 last_us.usage = us.usage;
263 return buffer;
264 case 1:
265 text = "User";
266 time = us.utime;
267 diff = us.utime - last_us.utime;
268 last_us.utime = us.utime;
269 break;
270 case 2:
271 text = "Sys";
272 time = us.stime;
273 diff = us.stime - last_us.stime;
274 last_us.stime = us.stime;
275 break;
276 case 3:
277 text = "Real";
278 time = us.rtime;
279 diff = us.rtime - last_us.rtime;
280 last_us.rtime = us.rtime;
281 break;
282 case 4:
283 return "*** Per CPU freq stats ***";
284 default:
285 {
286 int cpu = (selected_item - 5) / (state_count + 1);
287 int cpu_line = (selected_item - 5) % (state_count + 1);
288 int freq1 = cpufrequency_linux(cpu);
289 int freq2 = scalingfrequency_linux(cpu);
290 if (cpu_line == 0)
291 {
292 sprintf(buffer, " CPU%d: Cur/Scal freq: %d/%d MHz", cpu,
293 freq1 > 0 ? freq1/1000 : -1,
294 freq2 > 0 ? freq2/1000 : -1);
295 }
296 else
297 {
298 cpustatetimes_linux(cpu, states, ARRAYLEN(states));
299 snprintf(buffer, buffer_len, " %ld %ld",
300 states[cpu_line-1].frequency,
301 states[cpu_line-1].time);
302 }
303 return buffer;
304 }
305 }
306 sprintf(buffer, "%s: %ld.%02lds (+ %ld.%02ld)", text,
307 time / us.hz, time % us.hz,
308 diff / us.hz, diff % us.hz);
309 return buffer;
310}
311
312static int cpuinfo_cb(int action, struct gui_synclist *lists)
313{
314 (void)lists;
315 if (action == ACTION_NONE)
316 action = ACTION_REDRAW;
317 return action;
318}
319
320static bool dbg_cpuinfo(void)
321{
322 struct simplelist_info info;
323 int cpu_count = MAX(cpucount_linux(), 1);
324 int state_count = cpustatetimes_linux(0, states, ARRAYLEN(states));
325 printf("%s(): %d %d\n", __func__, cpu_count, state_count);
326 simplelist_info_init(&info, "CPU info:", 5 + cpu_count*(state_count+1), &state_count);
327 info.get_name = get_cpuinfo;
328 info.action_callback = cpuinfo_cb;
329 info.timeout = HZ;
330 info.hide_selection = true;
331 info.scroll_all = true;
332 return simplelist_show_list(&info);
333}
334
335#endif
336
235#ifdef HAVE_LCD_BITMAP 337#ifdef HAVE_LCD_BITMAP
236#if CONFIG_CODEC != SWCODEC 338#if CONFIG_CODEC != SWCODEC
237#ifndef SIMULATOR 339#ifndef SIMULATOR
@@ -2062,6 +2164,9 @@ static const struct the_menu_item menuitems[] = {
2062 { "Catch mem accesses", dbg_set_memory_guard }, 2164 { "Catch mem accesses", dbg_set_memory_guard },
2063#endif 2165#endif
2064 { "View OS stacks", dbg_os }, 2166 { "View OS stacks", dbg_os },
2167#ifdef __linux__
2168 { "View CPU stats", dbg_cpuinfo },
2169#endif
2065#ifdef HAVE_LCD_BITMAP 2170#ifdef HAVE_LCD_BITMAP
2066#if (CONFIG_PLATFORM & PLATFORM_NATIVE) 2171#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
2067 { "View battery", view_battery }, 2172 { "View battery", view_battery },
@@ -2187,4 +2292,3 @@ bool run_debug_screen(char* screen)
2187 } 2292 }
2188 return false; 2293 return false;
2189} 2294}
2190
diff --git a/firmware/SOURCES b/firmware/SOURCES
index b4e9c5ca2b..7053358bee 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -7,6 +7,12 @@ general.c
7load_code.c 7load_code.c
8powermgmt.c 8powermgmt.c
9#if (CONFIG_PLATFORM & PLATFORM_HOSTED) 9#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
10
11#ifdef __linux__
12#undef linux
13target/hosted/cpuinfo-linux.c
14#endif
15
10target/hosted/powermgmt.c 16target/hosted/powermgmt.c
11target/hosted/rtc.c 17target/hosted/rtc.c
12#endif 18#endif
diff --git a/firmware/target/hosted/cpuinfo-linux.c b/firmware/target/hosted/cpuinfo-linux.c
new file mode 100644
index 0000000000..373d1c742e
--- /dev/null
+++ b/firmware/target/hosted/cpuinfo-linux.c
@@ -0,0 +1,198 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 Thomas Martitz
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22
23#include <sys/times.h>
24#include <sys/sysinfo.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <time.h>
29#include <unistd.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <ctype.h>
33#include "kernel.h"
34#include "thread.h"
35#include "cpuinfo-linux.h"
36#include "gcc_extensions.h"
37
38#undef open /* want the *real* open here, not sim_open or the like */
39#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
40#include "cpu-features.h"
41#define get_nprocs android_getCpuCount
42#endif
43
44#define NUM_SAMPLES 64
45#define NUM_SAMPLES_MASK (NUM_SAMPLES-1)
46#define SAMPLE_RATE 4
47
48struct cputime_sample {
49 struct tms sample;
50 time_t time;
51};
52
53static struct cputime_sample samples[NUM_SAMPLES];
54static int current_sample;
55static int samples_taken;
56static clock_t initial_time, hz;
57
58
59static char cputime_thread_stack[DEFAULT_STACK_SIZE + 0x100];
60static char cputime_thread_name[] = "cputime";
61static int cputime_threadid;
62static void cputime_thread(void);
63
64/* times() can return -1 early after boot */
65static clock_t times_wrapper(struct tms *t)
66{
67 clock_t res = times(t);
68 if (res == (clock_t)-1)
69 res = time(NULL) * hz;
70
71 return res;
72}
73
74static inline void thread_func(void)
75{
76 struct cputime_sample *s = &samples[current_sample++];
77 s->time = times_wrapper(&s->sample);
78
79 current_sample &= NUM_SAMPLES_MASK;
80 if (samples_taken < NUM_SAMPLES) samples_taken++;
81
82 sleep(HZ/SAMPLE_RATE);
83}
84
85static void NORETURN_ATTR cputime_thread(void)
86{
87 while(1)
88 thread_func();
89}
90
91static void __attribute__((constructor)) get_initial_time(void)
92{
93 struct tms ign;
94 hz = sysconf(_SC_CLK_TCK);
95 initial_time = times_wrapper(&ign); /* dont pass NULL */;
96}
97
98int cpuusage_linux(struct cpuusage* u)
99{
100 if (UNLIKELY(!cputime_threadid))
101 {
102 /* collect some initial data and start the thread */
103 thread_func();
104 cputime_threadid = create_thread(cputime_thread, cputime_thread_stack,
105 sizeof(cputime_thread_stack), 0, cputime_thread_name
106 IF_PRIO(,PRIORITY_BACKGROUND) IF_COP(, CPU));
107 }
108 if (!u)
109 return -1;
110
111 clock_t total_cputime;
112 clock_t diff_utime, diff_stime;
113 time_t diff_rtime;
114 int latest_sample = ((current_sample == 0) ? NUM_SAMPLES : current_sample) - 1;
115 int oldest_sample = (samples_taken < NUM_SAMPLES) ? 0 : current_sample;
116
117 diff_utime = samples[latest_sample].sample.tms_utime - samples[oldest_sample].sample.tms_utime;
118 diff_stime = samples[latest_sample].sample.tms_stime - samples[oldest_sample].sample.tms_stime;
119 diff_rtime = samples[latest_sample].time - samples[oldest_sample].time;
120 if (UNLIKELY(!diff_rtime))
121 diff_rtime = 1;
122 u->hz = hz;
123
124 u->utime = samples[latest_sample].sample.tms_utime;
125 u->stime = samples[latest_sample].sample.tms_stime;
126 u->rtime = samples[latest_sample].time - initial_time;
127
128 total_cputime = diff_utime + diff_stime;
129 total_cputime *= 100; /* pump up by 100 for hundredth */
130 u->usage = total_cputime * 100 / diff_rtime;
131
132 return 0;
133}
134
135int cpucount_linux(void)
136{
137 return get_nprocs();
138}
139
140int cpufrequency_linux(int cpu)
141{
142 char path[64];
143 char temp[10];
144 int cpu_dev, ret;
145 snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq", cpu);
146 cpu_dev = open(path, O_RDONLY);
147 if (cpu_dev < 0)
148 return -1;
149 read(cpu_dev, temp, 10);
150 ret = atoi(temp);
151 close(cpu_dev);
152 return ret;
153}
154
155int scalingfrequency_linux(int cpu)
156{
157 char path[64];
158 char temp[10];
159 int cpu_dev, ret;
160 snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpu);
161 cpu_dev = open(path, O_RDONLY);
162 if (cpu_dev < 0)
163 return -1;
164 read(cpu_dev, temp, 10);
165 ret = atoi(temp);
166 close(cpu_dev);
167 return ret;
168}
169
170int cpustatetimes_linux(int cpu, struct time_state* data, int max_elements)
171{
172 int elements_left = max_elements, cpu_dev;
173 char buf[256], path[64], *p;
174 ssize_t read_size;
175 snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpu);
176 cpu_dev = open(path, O_RDONLY);
177 if (cpu_dev < 0)
178 return -1;
179 read_size = read(cpu_dev, buf, sizeof(buf));
180
181 close(cpu_dev);
182
183 p = buf;
184 while(elements_left > 0 && (p-buf) < read_size)
185 {
186 data->frequency = atol(p);
187 /* this loop breaks when it seems the space or line-feed,
188 * so buf points to a number aftwards */
189 while(isdigit(*p++));
190 data->time = atol(p);
191 /* now skip over to the next line */
192 while(isdigit(*p++));
193 data++;
194 elements_left--;
195 }
196
197 return (max_elements - elements_left) ?: -1;
198}
diff --git a/firmware/target/hosted/cpuinfo-linux.h b/firmware/target/hosted/cpuinfo-linux.h
new file mode 100644
index 0000000000..d9ba376f49
--- /dev/null
+++ b/firmware/target/hosted/cpuinfo-linux.h
@@ -0,0 +1,45 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 Thomas Martitz
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22
23#ifndef __CPUINFO_LINUX_H__
24#define __CPUINFO_LINUX_H__
25
26struct cpuusage {
27 long usage; /* in hundredth percent */
28 long utime; /* in clock ticks */
29 long stime; /* in clock ticks */
30 long rtime; /* in clock ticks */
31 long hz; /* how clock ticks per second */
32};
33
34struct time_state {
35 long frequency;
36 long time;
37};
38
39int cpuusage_linux(struct cpuusage* u);
40int cpufrequency_linux(int cpu);
41int scalingfrequency_linux(int cpu);
42int cpustatetimes_linux(int cpu, struct time_state* data, int max_elements);
43int cpucount_linux(void);
44
45#endif /* __CPUINFO_LINUX_H__ */