From: Irina Tirdea <irina.tir...@intel.com>

In glibc, printf supports ' to group numbers with thousands' grouping
characters. Bionic does not support ' for printf.

Implement thousands's grouping for numbers according to locale.
The implementation uses the algorithm from glibc
(http://www.gnu.org/software/libc/).

Bionic does not implement locales, so we need to add a configuration
option NO_LOCALE. If NO_LOCALE is defined, default values for thousands
separator and grouping are used.

Signed-off-by: Irina Tirdea <irina.tir...@intel.com>
---

Changes in v2:
This is a rewrite of http://lkml.org/lkml/2012/9/13/574
Instead of disabling big num for Android, I added the implementation in perf
(as Ingo suggested).

 tools/perf/Makefile                 |    8 +++
 tools/perf/builtin-stat.c           |  112 ++++++++++++++++++++++++++++++++---
 tools/perf/config/feature-tests.mak |   18 ++++++
 3 files changed, 131 insertions(+), 7 deletions(-)

diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 5077f8e..74e21cf 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -769,6 +769,14 @@ else
        endif
 endif
 
+ifdef NO_LOCALE
+       BASIC_CFLAGS += -DNO_LOCALE
+else
+       ifneq ($(call try-cc,$(SOURCE_LOCALE),),y)
+               BASIC_CFLAGS += -DNO_LOCALE
+       endif
+endif
+
 ifdef ASCIIDOC8
        export ASCIIDOC8
 endif
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index e0f65fe..cb8b399 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -60,6 +60,8 @@
 #include <sys/prctl.h>
 #include <locale.h>
 
+/* max double number have E+308 + \0 + sign */
+#define MAX_NR_STR 310
 #define DEFAULT_SEPARATOR      " "
 #define CNTR_NOT_SUPPORTED     "<not supported>"
 #define CNTR_NOT_COUNTED       "<not counted>"
@@ -744,18 +746,112 @@ static void print_ll_cache_misses(int cpu,
        fprintf(output, " of all LL-cache hits   ");
 }
 
+/* Group the digits according to the grouping rules of the current locale.
+   The interpretation of GROUPING is as in `struct lconv' from <locale.h>.  */
+static int group_number_locale(char *number, char **gnumber)
+{
+       const char *thousands_sep = NULL, *grouping = NULL;
+       int glen, tlen, dest_alloc_size, src_size, ret = 0, cnt;
+       char *dest_alloc_ptr, *dest_end, *src_start, *src_end;
+
+#ifdef NO_LOCALE
+       thousands_sep = ",";
+       grouping = "\x3";
+#else
+       struct lconv *lc = localeconv();
+       if (lc != NULL) {
+               thousands_sep = lc->thousands_sep;
+               grouping = lc->grouping;
+       }
+#endif
+
+       *gnumber = NULL;
+       /* No grouping */
+       if (thousands_sep == NULL || grouping == NULL ||
+           *thousands_sep == '\0' || *grouping == CHAR_MAX || *grouping <= 0) {
+               *gnumber = strdup(number);
+               if (*gnumber == NULL)
+                       ret = -ENOMEM;
+               goto out;
+       }
+
+       glen = *grouping++;
+       tlen = strlen(thousands_sep);
+
+       src_size = strlen(number);
+       /* Worst case scenario we have 1-character grouping */
+       dest_alloc_size = (src_size + src_size * tlen) * sizeof(char);
+       dest_alloc_ptr = zalloc(dest_alloc_size);
+       if (dest_alloc_ptr == NULL) {
+               ret = -ENOMEM;
+               goto out;
+       }
+       /* -1 for '\0' */
+       dest_end = dest_alloc_ptr + dest_alloc_size - 1;
+
+       src_start = number;
+       src_end = number + src_size;
+
+       while (src_end > src_start) {
+               *--dest_end = *--src_end;
+               if (--glen == 0 && src_end > src_start) {
+                       /* A new group */
+                       cnt = tlen;
+                       do
+                               *--dest_end = thousands_sep[--cnt];
+                       while (cnt > 0);
+
+                       if (*grouping == CHAR_MAX || *grouping < 0) {
+                               /* No further grouping to be done.
+                                  Copy the rest of the number. */
+                               do
+                                       *--dest_end = *--src_end;
+                               while (src_end > src_start);
+                               break;
+                       } else if (*grouping != '\0') {
+                               glen = *grouping++;
+                       } else {
+                               /* The previous grouping repeats ad infinitum */
+                               glen = grouping[-1];
+                       }
+               }
+       }
+
+       /* Make a copy with the exact needed size of the grouped number */
+       *gnumber = strdup(dest_end);
+       if (*gnumber == NULL) {
+               ret = -ENOMEM;
+               goto out_free_dest;
+       }
+
+       /* fall through */
+out_free_dest:
+       free(dest_alloc_ptr);
+out:
+       return ret;
+}
+
 static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
 {
        double total, ratio = 0.0;
        char cpustr[16] = { '\0', };
        const char *fmt;
+       char avgstr[MAX_NR_STR], *pavgstr;
+       int ret;
 
-       if (csv_output)
-               fmt = "%s%.0f%s%s";
-       else if (big_num)
-               fmt = "%s%'18.0f%s%-25s";
-       else
-               fmt = "%s%18.0f%s%-25s";
+       sprintf(avgstr, "%.0f", avg);
+       pavgstr = avgstr;
+
+       if (csv_output) {
+               fmt = "%s%s%s%s";
+       } else {
+               fmt = "%s%18s%s%-25s";
+               if (big_num) {
+                       ret = group_number_locale(avgstr, &pavgstr);
+                       if (ret < 0)
+                               pavgstr = avgstr;
+               }
+       }
 
        if (no_aggr)
                sprintf(cpustr, "CPU%*d%s",
@@ -764,7 +860,9 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, 
double avg)
        else
                cpu = 0;
 
-       fprintf(output, fmt, cpustr, avg, csv_sep, perf_evsel__name(evsel));
+       fprintf(output, fmt, cpustr, pavgstr, csv_sep, perf_evsel__name(evsel));
+       if (pavgstr != avgstr)
+               free(pavgstr);
 
        if (evsel->cgrp)
                fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
diff --git a/tools/perf/config/feature-tests.mak 
b/tools/perf/config/feature-tests.mak
index 116690a..7e5c005 100644
--- a/tools/perf/config/feature-tests.mak
+++ b/tools/perf/config/feature-tests.mak
@@ -193,3 +193,21 @@ int main(void)
 }
 endef
 endif
+
+ifndef NO_LOCALE
+define SOURCE_LOCALE
+#include <locale.h>
+
+int main(void)
+{
+       char *thousands_sep, *grouping;
+
+       struct lconv *lc = localeconv();
+       if (lc != NULL) {
+               thousands_sep = lc->thousands_sep;
+               grouping = lc->grouping;
+       }
+       return 0;
+}
+endef
+endif
-- 
1.7.9.5

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

Reply via email to