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/