aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaojie Sun <shaojie.sun@linaro.com>2013-09-24 18:52:49 +0800
committerShaojie Sun <shaojie.sun@linaro.com>2013-10-18 16:11:24 +0800
commit7f2e3b9bf2bf43cf99723561287b91b70d104cce (patch)
treee078e13e4f2f9367cc35af9c02f4e6d59f5f1151
parent5b9c18bca578cb61cee54cc1e127281b8dc7a799 (diff)
downloadidlestat-7f2e3b9bf2bf43cf99723561287b91b70d104cce.tar.gz
Find source of wakeup irq
First enable irq ftrace event and capture irq events. Then get the first irq info after cpu entered idle. And count the irq times which waked up cpu. cpuX : enter idle cpuX : softirq_raise | irq_handler_entry (count first irq) cpuX : exit idle Then we will show wakeup irq info as next. cpuX/hardirq id 32, name timer, wakeup count 227 Signed-off-by: Shaojie Sun <shaojie.sun@linaro.com>
-rw-r--r--idlestat.c112
-rw-r--r--idlestat.h21
-rw-r--r--trace.c6
-rw-r--r--trace.h1
4 files changed, 129 insertions, 11 deletions
diff --git a/idlestat.c b/idlestat.c
index 88150a9..8058f7b 100644
--- a/idlestat.c
+++ b/idlestat.c
@@ -62,6 +62,8 @@ static int display_cstates(struct cpuidle_cstates *cstates, int state,
{
int j;
struct cpuidle_cstate *cstate;
+ struct wakeup_info *wakeinfo;
+ struct wakeup_irq *irqinfo;
for (j = 0; j < cstates->cstate_max + 1; j++) {
@@ -78,6 +80,17 @@ static int display_cstates(struct cpuidle_cstates *cstates, int state,
cstate->max_time);
}
+ if (strstr(str, IRQ_WAKEUP_UNIT_NAME)) {
+ wakeinfo = &cstates->wakeinfo;
+ irqinfo = wakeinfo->irqinfo;
+ for (j = 0; j < wakeinfo->nrdata; j++, irqinfo++) {
+ printf("\t%s", str);
+ printf("/%sirq id %d, name %s, wakeup count %d\n",
+ (irqinfo->irq_type == 0) ? "hard" : "soft",
+ irqinfo->id, irqinfo->name, irqinfo->count);
+ }
+ }
+
return 0;
}
@@ -237,7 +250,7 @@ static int store_data(double time, int state, int cpu,
cstate->duration += data->duration;
cstate->nrdata++;
-
+
return 0;
}
@@ -250,13 +263,87 @@ static int store_data(double time, int state, int cpu,
cstates->cstate[state].data = data;
cstates->cstate_max = MAX(cstates->cstate_max, state);
cstates->last_cstate = state;
+ cstates->wakeirq = NULL;
return 0;
}
+static struct wakeup_irq *find_irqinfo(struct wakeup_info *wakeinfo, int irqid)
+{
+ struct wakeup_irq *irqinfo;
+ int i;
+
+ for (i = 0; i < wakeinfo->nrdata; i++) {
+ irqinfo = &wakeinfo->irqinfo[i];
+ if (irqinfo->id == irqid)
+ return irqinfo;
+ }
+
+ return NULL;
+}
+
+static int store_irq(int cpu, int irqid, char *irqname,
+ struct cpuidle_datas *datas, int count, int irq_type)
+{
+ struct cpuidle_cstates *cstates = &datas->cstates[cpu];
+ struct wakeup_irq *irqinfo;
+ struct wakeup_info *wakeinfo = &cstates->wakeinfo;
+
+ if (cstates->wakeirq != NULL)
+ return 0;
+
+ irqinfo = find_irqinfo(wakeinfo, irqid);
+ if (NULL == irqinfo) {
+ irqinfo = realloc(wakeinfo->irqinfo,
+ sizeof(*irqinfo) * (wakeinfo->nrdata + 1));
+ if (!irqinfo)
+ return error("realloc irqinfo");
+
+ wakeinfo->irqinfo = irqinfo;
+
+ irqinfo = &wakeinfo->irqinfo[wakeinfo->nrdata++];
+ irqinfo->id = irqid;
+ strcpy(irqinfo->name, irqname);
+ irqinfo->irq_type = irq_type;
+ irqinfo->count = 0;
+ }
+
+ irqinfo->count++;
+
+ cstates->wakeirq = irqinfo;
+
+ return 0;
+}
+
+#define TRACE_IRQ_FORMAT "%*[^[][%d] %*[^=]=%d%*[^=]=%16s"
+#define TRACE_SOFTIRQ_FORMAT "%*[^[][%d] %*[^=]=%d%*[^=]=%16[^]]s"
+
#define TRACE_CMD_FORMAT "%*[^]]] %lf:%*[^=]=%u%*[^=]=%d"
#define TRACE_FORMAT "%*[^]]] %*s %lf:%*[^=]=%u%*[^=]=%d"
+static int get_wakeup_irq(struct cpuidle_datas *datas, char *buffer, int count)
+{
+ int cpu, irqid;
+ char irqname[NAMELEN+1];
+
+ if (strstr(buffer, "irq_handler_entry")) {
+ sscanf(buffer, TRACE_IRQ_FORMAT, &cpu, &irqid, irqname);
+
+ store_irq(cpu, irqid, irqname, datas, count, 0);
+ return 0;
+ }
+
+ if (strstr(buffer, "softirq_raise")) {
+ sscanf(buffer, TRACE_SOFTIRQ_FORMAT, &cpu, &irqid, irqname);
+
+ store_irq(cpu, irqid, irqname, datas, count, 1);
+ return 0;
+ }
+
+ return -1;
+}
+
+
static struct cpuidle_datas *idlestat_load(const char *path)
{
FILE *f;
@@ -264,6 +351,7 @@ static struct cpuidle_datas *idlestat_load(const char *path)
double time, begin = 0, end = 0;
size_t count, start = 1;
struct cpuidle_datas *datas;
+ int ret;
f = fopen(path, "r");
if (!f)
@@ -293,19 +381,23 @@ static struct cpuidle_datas *idlestat_load(const char *path)
read_cpu_topo_info(f, buffer);
do {
- if (!strstr(buffer, "cpu_idle"))
- continue;
+ if (strstr(buffer, "cpu_idle")) {
+ sscanf(buffer, TRACE_FORMAT, &time, &state, &cpu);
- sscanf(buffer, TRACE_FORMAT, &time, &state, &cpu);
+ if (start) {
+ begin = time;
+ start = 0;
+ }
+ end = time;
- if (start) {
- begin = time;
- start = 0;
+ store_data(time, state, cpu, datas, count);
+ count++;
+ continue;
}
- end = time;
- store_data(time, state, cpu, datas, count);
- count++;
+ ret = get_wakeup_irq(datas, buffer, count);
+ count += (0 == ret) ? 1 : 0;
+
} while (fgets(buffer, BUFSIZE, f));
fclose(f);
diff --git a/idlestat.h b/idlestat.h
index 954020f..7a20516 100644
--- a/idlestat.h
+++ b/idlestat.h
@@ -3,11 +3,14 @@
#define __IDLESTAT_H
#define BUFSIZE 256
+#define NAMELEN 16
#define MAXCSTATE 8
#define MAX(A, B) (A > B ? A : B)
#define MIN(A, B) (A < B ? A : B)
#define AVG(A, B, I) ((A) + ((B - A) / (I)))
+#define IRQ_WAKEUP_UNIT_NAME "cpu"
+
struct cpuidle_data {
double begin;
double end;
@@ -23,10 +26,28 @@ struct cpuidle_cstate {
double duration;
};
+/* irq type:
+ * 0: hardware irq;
+ * 1: software irq;
+ */
+struct wakeup_irq {
+ int id;
+ int irq_type;
+ char name[NAMELEN+1];
+ int count;
+};
+
+struct wakeup_info {
+ struct wakeup_irq *irqinfo;
+ int nrdata;
+};
+
struct cpuidle_cstates {
struct cpuidle_cstate cstate[MAXCSTATE];
+ struct wakeup_info wakeinfo;
int last_cstate;
int cstate_max;
+ struct wakeup_irq *wakeirq;
};
struct cpuidle_datas {
diff --git a/trace.c b/trace.c
index 56afe73..4749abe 100644
--- a/trace.c
+++ b/trace.c
@@ -43,9 +43,13 @@ int idlestat_init_trace(unsigned int duration)
if (write_int(TRACE_EVENT_PATH, 0))
return -1;
- /* Enable only cpu_idle traces */
+ /* Enable cpu_idle traces */
if (write_int(TRACE_CPUIDLE_EVENT_PATH, 1))
return -1;
+ /* Enable irq traces */
+ if (write_int(TRACE_IRQ_EVENT_PATH, 1))
+ return -1;
+
return 0;
}
diff --git a/trace.h b/trace.h
index 49198f7..fb98351 100644
--- a/trace.h
+++ b/trace.h
@@ -7,6 +7,7 @@
#define TRACE_BUFFER_SIZE_PATH TRACE_PATH "/buffer_size_kb"
#define TRACE_BUFFER_TOTAL_PATH TRACE_PATH "/buffer_total_size_kb"
#define TRACE_CPUIDLE_EVENT_PATH TRACE_PATH "/events/power/cpu_idle/enable"
+#define TRACE_IRQ_EVENT_PATH TRACE_PATH "/events/irq/enable"
#define TRACE_EVENT_PATH TRACE_PATH "/events/enable"
#define TRACE_FREE TRACE_PATH "/free_buffer"
#define TRACE_FILE TRACE_PATH "/trace"