Before, power:cpu_idle events were very specific X86 Intel mwait events.
This got fixed with previous patches and cpu_idle events are now thrown by
all cpuidle drivers and can be mapped to the corresponding cpuidle state
in /sys.

This patch reads out the corresponding cpuidle name of a cpu_idle event
and uses it in the title line of the chart (c-states Cx in x86, omap2
- DDR self refresh states for various arm archs).

It also reads out the corresponding abbr(eviation) and uses the string
to draw the cpu idle occurences. This needs a short (3 letter) string
to keep the overview in the chart.

Signed-off-by: Thomas Renninger <tr...@suse.de>
CC: l...@kernel.org
CC: linux-a...@vger.kernel.org
CC: linux...@lists.linux-foundation.org
CC: Arjan van de Ven <ar...@linux.intel.com>
CC: Ingo Molnar <mi...@elte.hu>
CC: linux-ker...@vger.kernel.org
CC: linux-perf-us...@vger.kernel.org
CC: linux-omap@vger.kernel.org
---
 tools/perf/builtin-timechart.c |   26 ++++++-
 tools/perf/util/svghelper.c    |  156 +++++++++++++++++++++++++++++++++++-----
 2 files changed, 163 insertions(+), 19 deletions(-)

diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 148dc5e..391e475 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -31,6 +31,7 @@
 #include "util/event.h"
 #include "util/session.h"
 #include "util/svghelper.h"
+#include "util/debug.h"
 
 #define SUPPORT_OLD_POWER_EVENTS 1
 #define PWR_EVENT_EXIT -1
@@ -379,6 +380,10 @@ static void c_state_end(int cpu, u64 timestamp)
        pwr->next = power_events;
 
        power_events = pwr;
+       if (verbose)
+               printf("CPU: %d - start_time: %llu - end_time: %llu\n",
+                      power_events->cpu, power_events->start_time,
+                      power_events->end_time);
 }
 
 static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
@@ -657,8 +662,25 @@ static void draw_c_p_states(void)
         * two pass drawing so that the P state bars are on top of the C state 
blocks
         */
        while (pwr) {
-               if (pwr->type == CSTATE)
-                       svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, 
pwr->state);
+               if (pwr->type == CSTATE) {
+                       if (verbose) {
+                               printf("CPU: %d, Start: %llu, End: %llu, type:"
+                                      " %d\n", pwr->cpu, pwr->start_time,
+                                      pwr->end_time, pwr->state);
+                       }
+                       /* trenn: Looks like there is a dummy c-state entry of
+                        * type==0, start_time==0, end_time==highest_timestamp
+                        * for each CPU at the end of the list, this one should
+                        * better be filtered out before  -> ignore it here.
+                       */
+                       if ((pwr->state == 0 && pwr->start_time == 0)) {
+                               pwr = pwr->next;
+                               continue;
+                       }
+
+                       svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time,
+                                  pwr->state);
+               }
                pwr = pwr->next;
        }
 
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c
index b3637db..20c02fc 100644
--- a/tools/perf/util/svghelper.c
+++ b/tools/perf/util/svghelper.c
@@ -16,8 +16,13 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <linux/cpuidle.h>
 
 #include "svghelper.h"
+#include "debug.h"
 
 static u64 first_time, last_time;
 static u64 turbo_frequency, max_freq;
@@ -43,11 +48,11 @@ static double cpu2y(int cpu)
        return cpu2slot(cpu) * SLOT_MULT;
 }
 
-static double time2pixels(u64 time)
+static double time2pixels(u64 __time)
 {
        double X;
 
-       X = 1.0 * svg_page_width * (time - first_time) / (last_time - 
first_time);
+       X = 1.0 * svg_page_width * (__time - first_time) / (last_time - 
first_time);
        return X;
 }
 
@@ -107,12 +112,14 @@ void open_svg(const char *filename, int cpus, int rows, 
u64 start, u64 end)
        fprintf(svgfile, "      rect.WAITING  { fill:rgb(255,214, 48); 
fill-opacity:0.6; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n");
        fprintf(svgfile, "      rect.cpu      { fill:rgb(192,192,192); 
fill-opacity:0.2; stroke-width:0.5; stroke:rgb(128,128,128); } \n");
        fprintf(svgfile, "      rect.pstate   { fill:rgb(128,128,128); 
fill-opacity:0.8; stroke-width:0; } \n");
-       fprintf(svgfile, "      rect.c1       { fill:rgb(255,214,214); 
fill-opacity:0.5; stroke-width:0; } \n");
-       fprintf(svgfile, "      rect.c2       { fill:rgb(255,172,172); 
fill-opacity:0.5; stroke-width:0; } \n");
-       fprintf(svgfile, "      rect.c3       { fill:rgb(255,130,130); 
fill-opacity:0.5; stroke-width:0; } \n");
-       fprintf(svgfile, "      rect.c4       { fill:rgb(255, 88, 88); 
fill-opacity:0.5; stroke-width:0; } \n");
-       fprintf(svgfile, "      rect.c5       { fill:rgb(255, 44, 44); 
fill-opacity:0.5; stroke-width:0; } \n");
-       fprintf(svgfile, "      rect.c6       { fill:rgb(255,  0,  0); 
fill-opacity:0.5; stroke-width:0; } \n");
+       fprintf(svgfile, "      rect.c0       { fill:rgb(102,255,102); 
fill-opacity:0.5; stroke-width:0; } \n");
+       fprintf(svgfile, "      rect.c1       { fill:rgb(102,255,  0); 
fill-opacity:0.5; stroke-width:0; } \n");
+       fprintf(svgfile, "      rect.c2       { fill:rgb(  0,255,102); 
fill-opacity:0.5; stroke-width:0; } \n");
+       fprintf(svgfile, "      rect.c3       { fill:rgb( 51,255, 51); 
fill-opacity:0.5; stroke-width:0; } \n");
+       fprintf(svgfile, "      rect.c4       { fill:rgb( 51,255,  0); 
fill-opacity:0.5; stroke-width:0; } \n");
+       fprintf(svgfile, "      rect.c5       { fill:rgb(  0,255, 51); 
fill-opacity:0.5; stroke-width:0; } \n");
+       fprintf(svgfile, "      rect.c6       { fill:rgb(  0,204,  0); 
fill-opacity:0.5; stroke-width:0; } \n");
+       fprintf(svgfile, "      rect.c7       { fill:rgb(  0,153,  0); 
fill-opacity:0.5; stroke-width:0; } \n");
        fprintf(svgfile, "      line.pstate   { stroke:rgb(255,255,  0); 
stroke-opacity:0.8; stroke-width:2; } \n");
 
        fprintf(svgfile, "    ]]>\n   </style>\n</defs>\n");
@@ -199,6 +206,81 @@ void svg_waiting(int Yslot, u64 start, u64 end)
        fprintf(svgfile, "</g>\n");
 }
 
+/* Cpuidle info from sysfs ***************************/
+struct cpuidle_state cpuidle_states[CPUIDLE_STATE_MAX];
+unsigned int cpuidle_info_max;
+
+static void debug_dump_cpuidle_states(void)
+{
+       unsigned int state;
+
+       if (cpuidle_info_max == 0) {
+               printf("No cpuidle info retrieved from /sys\n");
+               return;
+       }
+       printf("cpuidle_info_max: %u\n", cpuidle_info_max);
+       for (state = 0; state < cpuidle_info_max; state++) {
+               printf("CPUIDLE[%u]:\n", state);
+               printf("Name: %s\n", cpuidle_states[state].name);
+               printf("Abbr: %s\n", cpuidle_states[state].abbr);
+       }
+}
+static int get_sysfs_string(const char *path, char *string,
+                            int max_string_size)
+{
+       int fd;
+       size_t numread, i;
+
+       fd = open(path, O_RDONLY);
+       if (fd == -1)
+               return -1;
+
+       numread = read(fd, string, max_string_size-1);
+       if (numread < 1) {
+               close(fd);
+               return -1;
+       }
+       for (i = 0; i < numread; i++)
+               if (string[i] == '\n')
+                       string[i] = '\0';
+       string[numread] = '\0';
+       close(fd);
+       return 0;
+}
+
+#define PERF_CPUIDLE_SYS_PATH "/sys/devices/system/cpu/cpu0/cpuidle/state%u/"
+#define PERF_SYSFS_PATH_MAX 255
+
+/*
+ * Fills up cpuidle_states[CPUIDLE_STATE_MAX] info from
+ * /sys/devices/system/cpu/cpu0/cpuidle/stateX/ and sets cpuidle_info_max
+ * to found states
+ */
+static int retrieve_cpuidle_info(void)
+{
+       char path[PERF_SYSFS_PATH_MAX];
+       int state, ret;
+
+       for (state = 0; state < CPUIDLE_STATE_MAX; state++) {
+               snprintf(path, sizeof(path), PERF_CPUIDLE_SYS_PATH "name",
+                        state);
+               ret = get_sysfs_string(path, cpuidle_states[state].name,
+                                      CPUIDLE_NAME_LEN + 1);
+               if (ret)
+                       break;
+
+               snprintf(path, sizeof(path), PERF_CPUIDLE_SYS_PATH "abbr",
+                        state);
+               ret = get_sysfs_string(path, cpuidle_states[state].abbr,
+                                      CPUIDLE_ABBR_LEN + 1);
+               if (ret)
+                       break;
+       }
+       cpuidle_info_max = state;
+       return state;
+}
+/* Cpuidle info from sysfs ***************************/
+
 static char *cpu_model(void)
 {
        static char cpu_m[255];
@@ -278,17 +360,33 @@ void svg_process(int cpu, u64 start, u64 end, const char 
*type, const char *name
        fprintf(svgfile, "</g>\n");
 }
 
+/*
+ * Svg util and kernel supported max cpuidle states may differ.
+ * Cmp. with tools/perf/utils/include/linux/cpuidle.h
+ * and include/linux/cpuidle.h
+ * Currently cpuidle kernel interface and this svg tool, both support 8 states
+ */
+#define PERF_SVG_CPUIDLE_STATE_MAX 8
+
 void svg_cstate(int cpu, u64 start, u64 end, int type)
 {
        double width;
        char style[128];
+       static bool max_states_exceed_msg;
 
        if (!svgfile)
                return;
 
+       if (type > PERF_SVG_CPUIDLE_STATE_MAX) {
+               if (verbose || max_states_exceed_msg == false) {
+                       max_states_exceed_msg = true;
+                       printf("cpuidle state (%d) exceeding max supported "
+                              "states (%d).. ignoring\n",
+                              type, PERF_SVG_CPUIDLE_STATE_MAX);
+                       return;
+               }
+       }
 
-       if (type > 6)
-               type = 6;
        sprintf(style, "c%i", type);
 
        fprintf(svgfile, "<rect class=\"%s\" x=\"%4.8f\" width=\"%4.8f\" 
y=\"%4.1f\" height=\"%4.1f\"/>\n",
@@ -451,16 +549,40 @@ static void svg_legenda_box(int X, const char *text, 
const char *style)
 
 void svg_legenda(void)
 {
+       unsigned int cstate, offset = 500;
+       char class[3];
+
+       retrieve_cpuidle_info();
+       /* perf timechart does not implement a verbose (-v) param yet
+          remove below comment if you want to be more verbose. */
+       /* verbose = 1; */
+       if (verbose)
+               debug_dump_cpuidle_states();
+
        if (!svgfile)
                return;
 
-       svg_legenda_box(0,      "Running", "sample");
-       svg_legenda_box(100,    "Idle","rect.c1");
-       svg_legenda_box(200,    "Deeper Idle", "rect.c3");
-       svg_legenda_box(350,    "Deepest Idle", "rect.c6");
-       svg_legenda_box(550,    "Sleeping", "process2");
-       svg_legenda_box(650,    "Waiting for cpu", "waiting");
-       svg_legenda_box(800,    "Blocked on IO", "blocked");
+               svg_legenda_box(0,      "Running", "sample");
+               svg_legenda_box(100,    "Sleeping", "process2");
+               svg_legenda_box(200,    "Waiting for cpu", "waiting");
+               svg_legenda_box(350,    "Blocked on IO", "blocked");
+       /* trenn: Arch specific events. Only C1 exists on x86 if
+          no cpuidle driver registered. Deeper and Deepest can get
+          removed. Also C1 events may only get fired through cpuidle
+          driver at some time. */
+       if (cpuidle_info_max == 0) {
+               svg_legenda_box(500,    "Idle", "c1");
+       } else {
+               for (cstate = 0; cstate < cpuidle_info_max; cstate++) {
+                       sprintf(class, "c%u", cstate);
+                       svg_legenda_box(offset, cpuidle_states[cstate].name,
+                                       class);
+                       /* The box */
+                       offset += 20;
+                       /* The text */
+                       offset += (strlen(cpuidle_states[cstate].name) * 10);
+               }
+       }
 }
 
 void svg_time_grid(void)
-- 
1.6.0.2

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to