summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 e4fd369..2f422be 100644
--- a/android/android.make
+++ b/android/android.make
@@ -7,13 +7,19 @@
# $Id$
#
+# this is a glibc compatibility hack to provide a get_nprocs() replacement.
+# The NDK ships cpu-features.c which has a compatible function android_getCpuCount()
+CPUFEAT = /home/kugel/share/android-ndk-r6/sources/android/cpufeatures
+INCLUDES += -I$(CPUFEAT)
+OTHER_SRC += $(CPUFEAT)/cpu-features.c
+$(BUILDDIR)/cpu-features.o: $(CPUFEAT)/cpu-features.c
+ $(call PRINTS,CC $(subst $(ANDROID_NDK_PATH)/,,$<))$(CC) -o $@ -c $^ \
+ $(GCCOPTS) -Wno-unused
+
.SECONDEXPANSION: # $$(JAVA_OBJ) is not populated until after this
.SECONDEXPANSION: # $$(OBJ) is not populated until after this
.PHONY: apk classes clean dex dirs libs jar
-$(BUILDDIR)/$(BINARY): $$(OBJ) $(VOICESPEEXLIB) $(FIRMLIB) $(SKINLIB)
- $(call PRINTS,LD $(BINARY))$(CC) -o $@ $^ $(LDOPTS) $(GLOBAL_LDOPTS)
-
PACKAGE=org.rockbox
PACKAGE_PATH=org/rockbox
ANDROID_DIR=$(ROOTDIR)/android
@@ -103,6 +109,10 @@ dex: $(DEX)
classes: $(R_OBJ) $(JAVA_OBJ)
+
+$(BUILDDIR)/$(BINARY): $$(OBJ) $(VOICESPEEXLIB) $(FIRMLIB) $(SKINLIB) $(BUILDDIR)/cpu-features.o
+ $(call PRINTS,LD $(BINARY))$(CC) -o $@ $^ $(LDOPTS) $(GLOBAL_LDOPTS)
+
$(BINLIB_DIR)/$(BINARY): $(BUILDDIR)/$(BINARY)
$(call PRINTS,CP $(BINARY))cp $^ $@
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index e034357..5773374 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -232,6 +232,108 @@ static bool dbg_os(void)
return simplelist_show_list(&info);
}
+#ifdef __linux__
+#include "cpuinfo-linux.h"
+
+#define MAX_STATES 16
+static struct time_state states[MAX_STATES];
+
+static const char* get_cpuinfo(int selected_item, void *data,
+ char *buffer, size_t buffer_len)
+{
+ (void)data;(void)buffer_len;
+ const char* text;
+ long time, diff;
+ struct cpuusage us;
+ static struct cpuusage last_us;
+ int state_count = *(int*)data;
+
+ if (cpuusage_linux(&us) != 0)
+ return NULL;
+
+ switch(selected_item)
+ {
+ case 0:
+ diff = abs(last_us.usage - us.usage);
+ sprintf(buffer, "Usage: %ld.%02ld%% (%c %ld.%02ld)",
+ us.usage/100, us.usage%100,
+ (us.usage >= last_us.usage) ? '+':'-',
+ diff/100, diff%100);
+ last_us.usage = us.usage;
+ return buffer;
+ case 1:
+ text = "User";
+ time = us.utime;
+ diff = us.utime - last_us.utime;
+ last_us.utime = us.utime;
+ break;
+ case 2:
+ text = "Sys";
+ time = us.stime;
+ diff = us.stime - last_us.stime;
+ last_us.stime = us.stime;
+ break;
+ case 3:
+ text = "Real";
+ time = us.rtime;
+ diff = us.rtime - last_us.rtime;
+ last_us.rtime = us.rtime;
+ break;
+ case 4:
+ return "*** Per CPU freq stats ***";
+ default:
+ {
+ int cpu = (selected_item - 5) / (state_count + 1);
+ int cpu_line = (selected_item - 5) % (state_count + 1);
+ int freq1 = cpufrequency_linux(cpu);
+ int freq2 = scalingfrequency_linux(cpu);
+ if (cpu_line == 0)
+ {
+ sprintf(buffer, " CPU%d: Cur/Scal freq: %d/%d MHz", cpu,
+ freq1 > 0 ? freq1/1000 : -1,
+ freq2 > 0 ? freq2/1000 : -1);
+ }
+ else
+ {
+ cpustatetimes_linux(cpu, states, ARRAYLEN(states));
+ snprintf(buffer, buffer_len, " %ld %ld",
+ states[cpu_line-1].frequency,
+ states[cpu_line-1].time);
+ }
+ return buffer;
+ }
+ }
+ sprintf(buffer, "%s: %ld.%02lds (+ %ld.%02ld)", text,
+ time / us.hz, time % us.hz,
+ diff / us.hz, diff % us.hz);
+ return buffer;
+}
+
+static int cpuinfo_cb(int action, struct gui_synclist *lists)
+{
+ (void)lists;
+ if (action == ACTION_NONE)
+ action = ACTION_REDRAW;
+ return action;
+}
+
+static bool dbg_cpuinfo(void)
+{
+ struct simplelist_info info;
+ int cpu_count = MAX(cpucount_linux(), 1);
+ int state_count = cpustatetimes_linux(0, states, ARRAYLEN(states));
+ printf("%s(): %d %d\n", __func__, cpu_count, state_count);
+ simplelist_info_init(&info, "CPU info:", 5 + cpu_count*(state_count+1), &state_count);
+ info.get_name = get_cpuinfo;
+ info.action_callback = cpuinfo_cb;
+ info.timeout = HZ;
+ info.hide_selection = true;
+ info.scroll_all = true;
+ return simplelist_show_list(&info);
+}
+
+#endif
+
#ifdef HAVE_LCD_BITMAP
#if CONFIG_CODEC != SWCODEC
#ifndef SIMULATOR
@@ -2062,6 +2164,9 @@ static const struct the_menu_item menuitems[] = {
{ "Catch mem accesses", dbg_set_memory_guard },
#endif
{ "View OS stacks", dbg_os },
+#ifdef __linux__
+ { "View CPU stats", dbg_cpuinfo },
+#endif
#ifdef HAVE_LCD_BITMAP
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
{ "View battery", view_battery },
@@ -2187,4 +2292,3 @@ bool run_debug_screen(char* screen)
}
return false;
}
-
diff --git a/firmware/SOURCES b/firmware/SOURCES
index b4e9c5c..7053358 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -7,6 +7,12 @@ general.c
load_code.c
powermgmt.c
#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
+
+#ifdef __linux__
+#undef linux
+target/hosted/cpuinfo-linux.c
+#endif
+
target/hosted/powermgmt.c
target/hosted/rtc.c
#endif
diff --git a/firmware/target/hosted/cpuinfo-linux.c b/firmware/target/hosted/cpuinfo-linux.c
new file mode 100644
index 0000000..373d1c7
--- /dev/null
+++ b/firmware/target/hosted/cpuinfo-linux.c
@@ -0,0 +1,198 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2011 Thomas Martitz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <sys/times.h>
+#include <sys/sysinfo.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "kernel.h"
+#include "thread.h"
+#include "cpuinfo-linux.h"
+#include "gcc_extensions.h"
+
+#undef open /* want the *real* open here, not sim_open or the like */
+#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
+#include "cpu-features.h"
+#define get_nprocs android_getCpuCount
+#endif
+
+#define NUM_SAMPLES 64
+#define NUM_SAMPLES_MASK (NUM_SAMPLES-1)
+#define SAMPLE_RATE 4
+
+struct cputime_sample {
+ struct tms sample;
+ time_t time;
+};
+
+static struct cputime_sample samples[NUM_SAMPLES];
+static int current_sample;
+static int samples_taken;
+static clock_t initial_time, hz;
+
+
+static char cputime_thread_stack[DEFAULT_STACK_SIZE + 0x100];
+static char cputime_thread_name[] = "cputime";
+static int cputime_threadid;
+static void cputime_thread(void);
+
+/* times() can return -1 early after boot */
+static clock_t times_wrapper(struct tms *t)
+{
+ clock_t res = times(t);
+ if (res == (clock_t)-1)
+ res = time(NULL) * hz;
+
+ return res;
+}
+
+static inline void thread_func(void)
+{
+ struct cputime_sample *s = &samples[current_sample++];
+ s->time = times_wrapper(&s->sample);
+
+ current_sample &= NUM_SAMPLES_MASK;
+ if (samples_taken < NUM_SAMPLES) samples_taken++;
+
+ sleep(HZ/SAMPLE_RATE);
+}
+
+static void NORETURN_ATTR cputime_thread(void)
+{
+ while(1)
+ thread_func();
+}
+
+static void __attribute__((constructor)) get_initial_time(void)
+{
+ struct tms ign;
+ hz = sysconf(_SC_CLK_TCK);
+ initial_time = times_wrapper(&ign); /* dont pass NULL */;
+}
+
+int cpuusage_linux(struct cpuusage* u)
+{
+ if (UNLIKELY(!cputime_threadid))
+ {
+ /* collect some initial data and start the thread */
+ thread_func();
+ cputime_threadid = create_thread(cputime_thread, cputime_thread_stack,
+ sizeof(cputime_thread_stack), 0, cputime_thread_name
+ IF_PRIO(,PRIORITY_BACKGROUND) IF_COP(, CPU));
+ }
+ if (!u)
+ return -1;
+
+ clock_t total_cputime;
+ clock_t diff_utime, diff_stime;
+ time_t diff_rtime;
+ int latest_sample = ((current_sample == 0) ? NUM_SAMPLES : current_sample) - 1;
+ int oldest_sample = (samples_taken < NUM_SAMPLES) ? 0 : current_sample;
+
+ diff_utime = samples[latest_sample].sample.tms_utime - samples[oldest_sample].sample.tms_utime;
+ diff_stime = samples[latest_sample].sample.tms_stime - samples[oldest_sample].sample.tms_stime;
+ diff_rtime = samples[latest_sample].time - samples[oldest_sample].time;
+ if (UNLIKELY(!diff_rtime))
+ diff_rtime = 1;
+ u->hz = hz;
+
+ u->utime = samples[latest_sample].sample.tms_utime;
+ u->stime = samples[latest_sample].sample.tms_stime;
+ u->rtime = samples[latest_sample].time - initial_time;
+
+ total_cputime = diff_utime + diff_stime;
+ total_cputime *= 100; /* pump up by 100 for hundredth */
+ u->usage = total_cputime * 100 / diff_rtime;
+
+ return 0;
+}
+
+int cpucount_linux(void)
+{
+ return get_nprocs();
+}
+
+int cpufrequency_linux(int cpu)
+{
+ char path[64];
+ char temp[10];
+ int cpu_dev, ret;
+ snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq", cpu);
+ cpu_dev = open(path, O_RDONLY);
+ if (cpu_dev < 0)
+ return -1;
+ read(cpu_dev, temp, 10);
+ ret = atoi(temp);
+ close(cpu_dev);
+ return ret;
+}
+
+int scalingfrequency_linux(int cpu)
+{
+ char path[64];
+ char temp[10];
+ int cpu_dev, ret;
+ snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpu);
+ cpu_dev = open(path, O_RDONLY);
+ if (cpu_dev < 0)
+ return -1;
+ read(cpu_dev, temp, 10);
+ ret = atoi(temp);
+ close(cpu_dev);
+ return ret;
+}
+
+int cpustatetimes_linux(int cpu, struct time_state* data, int max_elements)
+{
+ int elements_left = max_elements, cpu_dev;
+ char buf[256], path[64], *p;
+ ssize_t read_size;
+ snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpu);
+ cpu_dev = open(path, O_RDONLY);
+ if (cpu_dev < 0)
+ return -1;
+ read_size = read(cpu_dev, buf, sizeof(buf));
+
+ close(cpu_dev);
+
+ p = buf;
+ while(elements_left > 0 && (p-buf) < read_size)
+ {
+ data->frequency = atol(p);
+ /* this loop breaks when it seems the space or line-feed,
+ * so buf points to a number aftwards */
+ while(isdigit(*p++));
+ data->time = atol(p);
+ /* now skip over to the next line */
+ while(isdigit(*p++));
+ data++;
+ elements_left--;
+ }
+
+ return (max_elements - elements_left) ?: -1;
+}
diff --git a/firmware/target/hosted/cpuinfo-linux.h b/firmware/target/hosted/cpuinfo-linux.h
new file mode 100644
index 0000000..d9ba376
--- /dev/null
+++ b/firmware/target/hosted/cpuinfo-linux.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2011 Thomas Martitz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef __CPUINFO_LINUX_H__
+#define __CPUINFO_LINUX_H__
+
+struct cpuusage {
+ long usage; /* in hundredth percent */
+ long utime; /* in clock ticks */
+ long stime; /* in clock ticks */
+ long rtime; /* in clock ticks */
+ long hz; /* how clock ticks per second */
+};
+
+struct time_state {
+ long frequency;
+ long time;
+};
+
+int cpuusage_linux(struct cpuusage* u);
+int cpufrequency_linux(int cpu);
+int scalingfrequency_linux(int cpu);
+int cpustatetimes_linux(int cpu, struct time_state* data, int max_elements);
+int cpucount_linux(void);
+
+#endif /* __CPUINFO_LINUX_H__ */