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 LOCALE_SUPPORT. If it is not defined, default values for thousands
separator and grouping are used.

Signed-off-by: Irina Tirdea <irina.tir...@intel.com>
---
 tools/perf/Makefile                 |    6 ++
 tools/perf/builtin-stat.c           |  112 ++++++++++++++++++++++++++++++++---
 tools/perf/config/feature-tests.mak |   18 ++++++
 3 files changed, 129 insertions(+), 7 deletions(-)

diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 5149b8a..bc75b2a 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -783,6 +783,12 @@ ifndef NO_BACKTRACE
        endif
 endif
 
+ifndef NO_LOCALE
+       ifeq ($(call try-cc,$(SOURCE_LOCALE),),y)
+               BASIC_CFLAGS += -DLOCALE_SUPPORT
+       endif
+endif
+
 ifdef ASCIIDOC8
        export ASCIIDOC8
 endif
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 93b9011..855e918 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>"
@@ -631,18 +633,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;
+
+#ifndef LOCALE_SUPPORT
+       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",
@@ -651,7 +747,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 3ef5ec9..ec3a6f1 100644
--- a/tools/perf/config/feature-tests.mak
+++ b/tools/perf/config/feature-tests.mak
@@ -222,3 +222,21 @@ int main(void)
        return on_exit(NULL, NULL);
 }
 endef
+
+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