diff mbox series

[4/7] GCOV: add -j argument (human readable format).

Message ID 64ee387ade24b20ee9a6bc2a038abd6664800a61.1509005504.git.mliska@suse.cz
State New
Headers show
Series GCOV: another set of improvements | expand

Commit Message

Martin Liška Oct. 26, 2017, 8:11 a.m. UTC
Human readable format is quite useful in my opinion. There's example:

        -:    1:unsigned
   14.00K:    2:loop (unsigned n, int value)
        -:    3:{
   21.00M:    4:  for (unsigned i = 0; i < n - 1; i++)
        -:    5:  {
   20.99M:    6:    value += i;
        -:    7:  }
        -:    8:
   14.00K:    9:  return value;
        -:   10:}
        -:   11:
        1:   12:int main(int argc)
        -:   13:{
        1:   14:  unsigned sum = 0;
    7.00K:   15:  for (unsigned i = 0; i < 7 * 1000; i++)
        -:   16:  {
    7.00K:   17:    sum += loop (1000, sum);
    7.00K:   18:    sum += loop (2000, sum);
        -:   19:  }
        -:   20:
        1:   21:  return 0;
        -:   22:}

Question is do we want to do it by default, or a new option is fine?
Note that all external tools using gcov should use intermediate format
which is obviously unchanged.

gcc/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* doc/gcov.texi: Document new option.
	* gcov.c (print_usage): Likewise print it.
	(process_args): Support the argument.
	(format_count): New function.
	(format_gcov): Use the function.

gcc/testsuite/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* g++.dg/gcov/loop.C: New test.
	* lib/gcov.exp: Support human readable format for counts.
---
 gcc/doc/gcov.texi                |  5 +++++
 gcc/gcov.c                       | 43 ++++++++++++++++++++++++++++++++++++++--
 gcc/testsuite/g++.dg/gcov/loop.C | 27 +++++++++++++++++++++++++
 gcc/testsuite/lib/gcov.exp       |  2 +-
 4 files changed, 74 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gcov/loop.C

Comments

Nathan Sidwell Oct. 30, 2017, 12:35 p.m. UTC | #1
On 10/26/2017 04:11 AM, marxin wrote:
> Human readable format is quite useful in my opinion. There's example:
> 
>          -:    1:unsigned
>     14.00K:    2:loop (unsigned n, int value)

My first thought is 'why 2 decimal places'?  That seems excessive.  Zero 
surely suffices?

> Question is do we want to do it by default, or a new option is fine?
> Note that all external tools using gcov should use intermediate format
> which is obviously unchanged.

I don't think other tools do it by default.


> +     [@option{-j}|@option{--human-numbers}]

man ls:
       -h, --human-readable
               with -l and/or -s, print human readable sizes (e.g., 1K 
234M 2G)

Sadly '-h' is help (IIRC).  but we could at least copy the long form.


> +  const char *units[] = {"", "K", "M", "G", "Y", "P", "E", "Z"};

Those aren't right  KMGTPEZY
http://www.npl.co.uk/reference/measurement-units/si-prefixes/


> +  for (unsigned i = 0; i < sizeof (units); i++)
> +    {
> +      if (v < 1000.0f)
> +	{
> +	  sprintf (buffer, "%3.2f%s", v, units[i]);
> +	  return buffer;
> +	}
> +
> +      v /= 1000.0f;
> +    }

that's going to fail on certain roundings.  You're doing multiple 
divisions by 1000, which itself will have roundings.  But more 
importantly, numbers after scaling like 999.999 will round to 1000, and 
you probably don't want that.  This kind of formatting is tricky.  My 
inclination is to keep it in the integer domain.  Determine a scaling 
factor.  Divide once by that and then explicitly round to nearest 
even[*] with a check for the 999.9 case.  Then print as an unsigned.

nathan

[*] I presume you're familiar with RNE?  That's probably overkill and 
plain RN would be adequate.
Martin Liška Oct. 31, 2017, 11:49 a.m. UTC | #2
On 10/30/2017 01:35 PM, Nathan Sidwell wrote:
> On 10/26/2017 04:11 AM, marxin wrote:
>> Human readable format is quite useful in my opinion. There's example:
>>
>>          -:    1:unsigned
>>     14.00K:    2:loop (unsigned n, int value)
> 
> My first thought is 'why 2 decimal places'?  That seems excessive.  Zero surely suffices?

Hi.

Agree.

> 
>> Question is do we want to do it by default, or a new option is fine?
>> Note that all external tools using gcov should use intermediate format
>> which is obviously unchanged.
> 
> I don't think other tools do it by default.

Works for me/

> 
> 
>> +     [@option{-j}|@option{--human-numbers}]
> 
> man ls:
>        -h, --human-readable
>                with -l and/or -s, print human readable sizes (e.g., 1K 234M 2G)
> 
> Sadly '-h' is help (IIRC).  but we could at least copy the long form.

Yep, copied.

> 
> 
>> +  const char *units[] = {"", "K", "M", "G", "Y", "P", "E", "Z"};
> 
> Those aren't right  KMGTPEZY
> http://www.npl.co.uk/reference/measurement-units/si-prefixes/

Ha, SI units. Last time I heart about it was at grammar school during Physics classes ;)
Following that.

> 
> 
>> +  for (unsigned i = 0; i < sizeof (units); i++)
>> +    {
>> +      if (v < 1000.0f)
>> +    {
>> +      sprintf (buffer, "%3.2f%s", v, units[i]);
>> +      return buffer;
>> +    }
>> +
>> +      v /= 1000.0f;
>> +    }
> 
> that's going to fail on certain roundings.  You're doing multiple divisions by 1000, which itself will have roundings.  But more importantly, numbers after scaling like 999.999 will round to 1000, and you probably don't want that.  This kind of formatting is tricky.  My inclination is to keep it in the integer domain.  Determine a scaling factor.  Divide once by that and then explicitly round to nearest even[*] with a check for the 999.9 case.  Then print as an unsigned.

Done that.

Is the patch fine to be installed?

Martin

> 
> nathan
> 
> [*] I presume you're familiar with RNE?  That's probably overkill and plain RN would be adequate.
>
From b14bed2b376dc60eb35c07c5521007d16b137c78 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 26 Oct 2017 10:11:16 +0200
Subject: [PATCH 4/8] GCOV: add -j argument (human readable format).

Human readable format is quite useful in my opinion. There's example:

        -:    4:unsigned
      14k:    5:loop (unsigned n, int value)		  /* count(14k) */
        -:    6:{
      21M:    7:  for (unsigned i = 0; i < n - 1; i++)
        -:    8:  {
      21M:    9:    value += i;				  /* count(21M) */
        -:   10:  }
        -:   11:
      14k:   12:  return value;
        -:   13:}
        -:   14:
        1:   15:int main(int argc, char **argv)
        -:   16:{
        1:   17:  unsigned sum = 0;
       7k:   18:  for (unsigned i = 0; i < 7 * 1000; i++)
        -:   19:  {
       7k:   20:    sum += loop (1000, sum);
       7k:   21:    sum += loop (2000, sum);		  /* count(7k) */
        -:   22:  }
        -:   23:
        1:   24:  return 0;				  /* count(1) */
        -:   25:}
        -:   26:

gcc/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* doc/gcov.texi: Document new option.
	* gcov.c (print_usage): Likewise print it.
	(process_args): Support the argument.
	(format_count): New function.
	(format_gcov): Use the function.

gcc/testsuite/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* g++.dg/gcov/loop.C: New test.
	* lib/gcov.exp: Support human readable format for counts.
---
 gcc/doc/gcov.texi                |  5 +++++
 gcc/gcov.c                       | 45 ++++++++++++++++++++++++++++++++++++++--
 gcc/testsuite/g++.dg/gcov/loop.C | 27 ++++++++++++++++++++++++
 gcc/testsuite/lib/gcov.exp       |  2 +-
 4 files changed, 76 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gcov/loop.C

diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index e186ac6e1ea..5c4ba8a51a7 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -125,6 +125,7 @@ gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
      [@option{-d}|@option{--display-progress}]
      [@option{-f}|@option{--function-summaries}]
      [@option{-i}|@option{--intermediate-format}]
+     [@option{-j}|@option{--human-readable}]
      [@option{-k}|@option{--use-colors}]
      [@option{-l}|@option{--long-file-names}]
      [@option{-m}|@option{--demangled-names}]
@@ -186,6 +187,10 @@ be used by @command{lcov} or other tools. The output is a single
 The format of the intermediate @file{.gcov} file is plain text with
 one entry per line
 
+@item -j
+@itemx --human-readable
+Write counts in human readable format (like 24k).
+
 @smallexample
 file:@var{source_file_name}
 function:@var{line_number},@var{execution_count},@var{function_name}
diff --git a/gcc/gcov.c b/gcc/gcov.c
index f9334f96eb3..ff246c104e4 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -44,6 +44,7 @@ along with Gcov; see the file COPYING3.  If not see
 #include "color-macros.h"
 
 #include <getopt.h>
+#include <math.h>
 
 #include "md5.h"
 
@@ -393,6 +394,10 @@ static int flag_use_colors = 0;
 
 static int flag_all_blocks = 0;
 
+/* Output human readable numbers.  */
+
+static int flag_human_readable_numbers = 0;
+
 /* Output summary info for each function.  */
 
 static int flag_function_summary = 0;
@@ -710,6 +715,7 @@ print_usage (int error_p)
   fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
   fnotice (file, "  -h, --help                      Print this help, then exit\n");
   fnotice (file, "  -i, --intermediate-format       Output .gcov file in intermediate text format\n");
+  fnotice (file, "  -j, --human-readable            Output human readable numbers\n");
   fnotice (file, "  -k, --use-colors                Emit colored output\n");
   fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
                                     source files\n");
@@ -752,6 +758,7 @@ static const struct option options[] =
   { "branch-probabilities", no_argument,       NULL, 'b' },
   { "branch-counts",        no_argument,       NULL, 'c' },
   { "intermediate-format",  no_argument,       NULL, 'i' },
+  { "human-readable",	    no_argument,       NULL, 'j' },
   { "no-output",            no_argument,       NULL, 'n' },
   { "long-file-names",      no_argument,       NULL, 'l' },
   { "function-summaries",   no_argument,       NULL, 'f' },
@@ -775,7 +782,7 @@ process_args (int argc, char **argv)
 {
   int opt;
 
-  const char *opts = "abcdfhiklmno:prs:uvwx";
+  const char *opts = "abcdfhijklmno:prs:uvwx";
   while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
     {
       switch (opt)
@@ -798,6 +805,9 @@ process_args (int argc, char **argv)
 	case 'l':
 	  flag_long_names = 1;
 	  break;
+	case 'j':
+	  flag_human_readable_numbers = 1;
+	  break;
 	case 'k':
 	  flag_use_colors = 1;
 	  break;
@@ -1938,6 +1948,37 @@ add_branch_counts (coverage_t *coverage, const arc_t *arc)
     }
 }
 
+/* Format COUNT, if flag_human_readable_numbers is set, return it human
+   readable format.  */
+
+static char const *
+format_count (gcov_type count)
+{
+  static char buffer[64];
+  float v = count;
+  const char *units = " kMGTPEZY";
+
+  if (count < 1000 || !flag_human_readable_numbers)
+    {
+      sprintf (buffer, "%" PRId64, count);
+      return buffer;
+    }
+
+  gcov_type divisor = 1;
+  for (unsigned i = 0; i < strlen (units); i++)
+    {
+      if (v < (1000 * divisor))
+	{
+	  gcov_type r = (gcov_type)roundf (v / divisor);
+	  sprintf (buffer, "%" PRId64 "%c", r, units[i]);
+	  return buffer;
+	}
+      divisor *= 1000;
+    }
+
+  gcc_unreachable ();
+}
+
 /* Format a GCOV_TYPE integer as either a percent ratio, or absolute
    count.  If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
    If DP is zero, no decimal point is printed. Only print 100% when
@@ -1985,7 +2026,7 @@ format_gcov (gcov_type top, gcov_type bottom, int dp)
 	}
     }
   else
-    sprintf (buffer, "%" PRId64, (int64_t)top);
+    return format_count (top);
 
   return buffer;
 }
diff --git a/gcc/testsuite/g++.dg/gcov/loop.C b/gcc/testsuite/g++.dg/gcov/loop.C
new file mode 100644
index 00000000000..7f3be5587af
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gcov/loop.C
@@ -0,0 +1,27 @@
+/* { dg-options "-fprofile-arcs -ftest-coverage" } */
+/* { dg-do run { target native } } */
+
+unsigned
+loop (unsigned n, int value)		  /* count(14k) */
+{
+  for (unsigned i = 0; i < n - 1; i++)
+  {
+    value += i;				  /* count(21M) */
+  }
+
+  return value;
+}
+
+int main(int argc, char **argv)
+{
+  unsigned sum = 0;
+  for (unsigned i = 0; i < 7 * 1000; i++)
+  {
+    sum += loop (1000, sum);
+    sum += loop (2000, sum);		  /* count(7k) */
+  }
+
+  return 0;				  /* count(1) */
+}
+
+/* { dg-final { run-gcov branches { -abj loop.C } } } */
diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp
index 18adc71351a..ede01e70212 100644
--- a/gcc/testsuite/lib/gcov.exp
+++ b/gcc/testsuite/lib/gcov.exp
@@ -59,7 +59,7 @@ proc verify-lines { testname testcase file } {
     while { [gets $fd line] >= 0 } {
         # We want to match both "-" and "#####" as count as well as numbers,
         # since we want to detect lines that shouldn't be marked as covered.
-	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=]+)\\)(.*)" \
+	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=\\.kMGTPEZY]+)\\)(.*)" \
 		"$line" all is n shouldbe rest] {
 	    if [regexp "^ *{(.*)}" $rest all xfailed] {
 		switch [dg-process-target $xfailed] {
Nathan Sidwell Oct. 31, 2017, 12:05 p.m. UTC | #3
On 10/31/2017 07:49 AM, Martin Liška wrote:
> On 10/30/2017 01:35 PM, Nathan Sidwell wrote:

> Is the patch fine to be installed?
not quite ...


+  gcov_type divisor = 1;
+  for (unsigned i = 0; i < strlen (units); i++)
+    {
+      if (v < (1000 * divisor))
+	{
+	  gcov_type r = (gcov_type)roundf (v / divisor);
+	  sprintf (buffer, "%" PRId64 "%c", r, units[i]);
+	  return buffer;
+	}
+      divisor *= 1000;
+    }

This doesn't deal with rounding to 1000, which will look a bit odd. 
It'll also overflow if we ever get 10^24 counts (yeah, like that'll ever 
happen in this universe).  We may as well not use floats either.

Excuse the poor formatting

   unsigned i;
   for (i = 0; units[i+1]; i++, divisor *= 1000) {
     if (count + divisor / 2 < 1000 * divisor)
        break;
   }
   gcov_type r  = (count + divisor / 2) / divisor;
   sprintf (buffer, "%" PRId64 "%c", r, units[i]);
   return buffer;
Martin Liška Oct. 31, 2017, 2:04 p.m. UTC | #4
On 10/31/2017 01:05 PM, Nathan Sidwell wrote:
> On 10/31/2017 07:49 AM, Martin Liška wrote:
>> On 10/30/2017 01:35 PM, Nathan Sidwell wrote:
> 
>> Is the patch fine to be installed?
> not quite ...
> 
> 
> +  gcov_type divisor = 1;
> +  for (unsigned i = 0; i < strlen (units); i++)
> +    {
> +      if (v < (1000 * divisor))
> +    {
> +      gcov_type r = (gcov_type)roundf (v / divisor);
> +      sprintf (buffer, "%" PRId64 "%c", r, units[i]);
> +      return buffer;
> +    }
> +      divisor *= 1000;
> +    }
> 
> This doesn't deal with rounding to 1000, which will look a bit odd. It'll also overflow if we ever get 10^24 counts (yeah, like that'll ever happen in this universe).  We may as well not use floats either.

Hi.

Do you mean numbers a bit smaller than 1000 or bit bigger? Do you have an example that will
be handled in a different way?

> 
> Excuse the poor formatting
> 
>    unsigned i;
>    for (i = 0; units[i+1]; i++, divisor *= 1000) {
>      if (count + divisor / 2 < 1000 * divisor)
>         break;
>    }
>    gcov_type r  = (count + divisor / 2) / divisor;
>    sprintf (buffer, "%" PRId64 "%c", r, units[i]);
>    return buffer;
> 

I've adapted your code snippet.

Martin
From 902208a24eafaf1a6a4b334a2c8ea32afaec63df Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 26 Oct 2017 10:11:16 +0200
Subject: [PATCH 4/8] GCOV: add -j argument (human readable format).

Human readable format is quite useful in my opinion. There's example:

        -:    4:unsigned
      14k:    5:loop (unsigned n, int value)		  /* count(14k) */
        -:    6:{
      21M:    7:  for (unsigned i = 0; i < n - 1; i++)
        -:    8:  {
      21M:    9:    value += i;				  /* count(21M) */
        -:   10:  }
        -:   11:
      14k:   12:  return value;
        -:   13:}
        -:   14:
        1:   15:int main(int argc, char **argv)
        -:   16:{
        1:   17:  unsigned sum = 0;
       7k:   18:  for (unsigned i = 0; i < 7 * 1000; i++)
        -:   19:  {
       7k:   20:    sum += loop (1000, sum);
       7k:   21:    sum += loop (2000, sum);		  /* count(7k) */
        -:   22:  }
        -:   23:
        1:   24:  return 0;				  /* count(1) */
        -:   25:}
        -:   26:

gcc/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* doc/gcov.texi: Document new option.
	* gcov.c (print_usage): Likewise print it.
	(process_args): Support the argument.
	(format_count): New function.
	(format_gcov): Use the function.

gcc/testsuite/ChangeLog:

2017-10-23  Martin Liska  <mliska@suse.cz>

	* g++.dg/gcov/loop.C: New test.
	* lib/gcov.exp: Support human readable format for counts.
---
 gcc/doc/gcov.texi                |  5 +++++
 gcc/gcov.c                       | 41 ++++++++++++++++++++++++++++++++++++++--
 gcc/testsuite/g++.dg/gcov/loop.C | 27 ++++++++++++++++++++++++++
 gcc/testsuite/lib/gcov.exp       |  2 +-
 4 files changed, 72 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gcov/loop.C

diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index e186ac6e1ea..5c4ba8a51a7 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -125,6 +125,7 @@ gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
      [@option{-d}|@option{--display-progress}]
      [@option{-f}|@option{--function-summaries}]
      [@option{-i}|@option{--intermediate-format}]
+     [@option{-j}|@option{--human-readable}]
      [@option{-k}|@option{--use-colors}]
      [@option{-l}|@option{--long-file-names}]
      [@option{-m}|@option{--demangled-names}]
@@ -186,6 +187,10 @@ be used by @command{lcov} or other tools. The output is a single
 The format of the intermediate @file{.gcov} file is plain text with
 one entry per line
 
+@item -j
+@itemx --human-readable
+Write counts in human readable format (like 24k).
+
 @smallexample
 file:@var{source_file_name}
 function:@var{line_number},@var{execution_count},@var{function_name}
diff --git a/gcc/gcov.c b/gcc/gcov.c
index f9334f96eb3..972e567160e 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -44,6 +44,7 @@ along with Gcov; see the file COPYING3.  If not see
 #include "color-macros.h"
 
 #include <getopt.h>
+#include <math.h>
 
 #include "md5.h"
 
@@ -393,6 +394,10 @@ static int flag_use_colors = 0;
 
 static int flag_all_blocks = 0;
 
+/* Output human readable numbers.  */
+
+static int flag_human_readable_numbers = 0;
+
 /* Output summary info for each function.  */
 
 static int flag_function_summary = 0;
@@ -710,6 +715,7 @@ print_usage (int error_p)
   fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
   fnotice (file, "  -h, --help                      Print this help, then exit\n");
   fnotice (file, "  -i, --intermediate-format       Output .gcov file in intermediate text format\n");
+  fnotice (file, "  -j, --human-readable            Output human readable numbers\n");
   fnotice (file, "  -k, --use-colors                Emit colored output\n");
   fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
                                     source files\n");
@@ -752,6 +758,7 @@ static const struct option options[] =
   { "branch-probabilities", no_argument,       NULL, 'b' },
   { "branch-counts",        no_argument,       NULL, 'c' },
   { "intermediate-format",  no_argument,       NULL, 'i' },
+  { "human-readable",	    no_argument,       NULL, 'j' },
   { "no-output",            no_argument,       NULL, 'n' },
   { "long-file-names",      no_argument,       NULL, 'l' },
   { "function-summaries",   no_argument,       NULL, 'f' },
@@ -775,7 +782,7 @@ process_args (int argc, char **argv)
 {
   int opt;
 
-  const char *opts = "abcdfhiklmno:prs:uvwx";
+  const char *opts = "abcdfhijklmno:prs:uvwx";
   while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
     {
       switch (opt)
@@ -798,6 +805,9 @@ process_args (int argc, char **argv)
 	case 'l':
 	  flag_long_names = 1;
 	  break;
+	case 'j':
+	  flag_human_readable_numbers = 1;
+	  break;
 	case 'k':
 	  flag_use_colors = 1;
 	  break;
@@ -1938,6 +1948,33 @@ add_branch_counts (coverage_t *coverage, const arc_t *arc)
     }
 }
 
+/* Format COUNT, if flag_human_readable_numbers is set, return it human
+   readable format.  */
+
+static char const *
+format_count (gcov_type count)
+{
+  static char buffer[64];
+  const char *units = " kMGTPEZY";
+
+  if (count < 1000 || !flag_human_readable_numbers)
+    {
+      sprintf (buffer, "%" PRId64, count);
+      return buffer;
+    }
+
+  unsigned i;
+  gcov_type divisor = 1;
+  for (i = 0; units[i+1]; i++, divisor *= 1000)
+    {
+      if (count + divisor / 2 < 1000 * divisor)
+	break;
+    }
+  gcov_type r  = (count + divisor / 2) / divisor;
+  sprintf (buffer, "%" PRId64 "%c", r, units[i]);
+  return buffer;
+}
+
 /* Format a GCOV_TYPE integer as either a percent ratio, or absolute
    count.  If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
    If DP is zero, no decimal point is printed. Only print 100% when
@@ -1985,7 +2022,7 @@ format_gcov (gcov_type top, gcov_type bottom, int dp)
 	}
     }
   else
-    sprintf (buffer, "%" PRId64, (int64_t)top);
+    return format_count (top);
 
   return buffer;
 }
diff --git a/gcc/testsuite/g++.dg/gcov/loop.C b/gcc/testsuite/g++.dg/gcov/loop.C
new file mode 100644
index 00000000000..7f3be5587af
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gcov/loop.C
@@ -0,0 +1,27 @@
+/* { dg-options "-fprofile-arcs -ftest-coverage" } */
+/* { dg-do run { target native } } */
+
+unsigned
+loop (unsigned n, int value)		  /* count(14k) */
+{
+  for (unsigned i = 0; i < n - 1; i++)
+  {
+    value += i;				  /* count(21M) */
+  }
+
+  return value;
+}
+
+int main(int argc, char **argv)
+{
+  unsigned sum = 0;
+  for (unsigned i = 0; i < 7 * 1000; i++)
+  {
+    sum += loop (1000, sum);
+    sum += loop (2000, sum);		  /* count(7k) */
+  }
+
+  return 0;				  /* count(1) */
+}
+
+/* { dg-final { run-gcov branches { -abj loop.C } } } */
diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp
index 18adc71351a..ede01e70212 100644
--- a/gcc/testsuite/lib/gcov.exp
+++ b/gcc/testsuite/lib/gcov.exp
@@ -59,7 +59,7 @@ proc verify-lines { testname testcase file } {
     while { [gets $fd line] >= 0 } {
         # We want to match both "-" and "#####" as count as well as numbers,
         # since we want to detect lines that shouldn't be marked as covered.
-	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=]+)\\)(.*)" \
+	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=\\.kMGTPEZY]+)\\)(.*)" \
 		"$line" all is n shouldbe rest] {
 	    if [regexp "^ *{(.*)}" $rest all xfailed] {
 		switch [dg-process-target $xfailed] {
Nathan Sidwell Oct. 31, 2017, 2:35 p.m. UTC | #5
On 10/31/2017 10:04 AM, Martin Liška wrote:

> Do you mean numbers a bit smaller than 1000 or bit bigger? Do you have 
> an example that will
> be handled in a different way?

A count like 999500 would have been scaled to 999.5 and then rounded to 
1000.  We'd print 1000k rather than 1M.

> I've adapted your code snippet.

thanks, this is ok.

nathan
Martin Liška Oct. 31, 2017, 3:31 p.m. UTC | #6
On 10/31/2017 03:35 PM, Nathan Sidwell wrote:
> On 10/31/2017 10:04 AM, Martin Liška wrote:
> 
>> Do you mean numbers a bit smaller than 1000 or bit bigger? Do you have an example that will
>> be handled in a different way?
> 
> A count like 999500 would have been scaled to 999.5 and then rounded to 1000.  We'd print 1000k rather than 1M.

I see! Thank you for clarification.

> 
>> I've adapted your code snippet.
> 
> thanks, this is ok.

Has been just installed.

Martin

> 
> nathan
>
diff mbox series

Patch

diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index 4029ccb0a93..9d955827706 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -125,6 +125,7 @@  gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
      [@option{-d}|@option{--display-progress}]
      [@option{-f}|@option{--function-summaries}]
      [@option{-i}|@option{--intermediate-format}]
+     [@option{-j}|@option{--human-numbers}]
      [@option{-k}|@option{--use-colors}]
      [@option{-l}|@option{--long-file-names}]
      [@option{-m}|@option{--demangled-names}]
@@ -186,6 +187,10 @@  be used by @command{lcov} or other tools. The output is a single
 The format of the intermediate @file{.gcov} file is plain text with
 one entry per line
 
+@item -j
+@itemx --human-numbers
+Write counts in human readable format (like 24.64K).
+
 @smallexample
 file:@var{source_file_name}
 function:@var{line_number},@var{execution_count},@var{function_name}
diff --git a/gcc/gcov.c b/gcc/gcov.c
index f9334f96eb3..8ba63f002d8 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -393,6 +393,10 @@  static int flag_use_colors = 0;
 
 static int flag_all_blocks = 0;
 
+/* Output human readable numbers.  */
+
+static int flag_human_readable_numbers = 0;
+
 /* Output summary info for each function.  */
 
 static int flag_function_summary = 0;
@@ -710,6 +714,7 @@  print_usage (int error_p)
   fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
   fnotice (file, "  -h, --help                      Print this help, then exit\n");
   fnotice (file, "  -i, --intermediate-format       Output .gcov file in intermediate text format\n");
+  fnotice (file, "  -j, --human-numbers             Output human readable numbers\n");
   fnotice (file, "  -k, --use-colors                Emit colored output\n");
   fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
                                     source files\n");
@@ -752,6 +757,7 @@  static const struct option options[] =
   { "branch-probabilities", no_argument,       NULL, 'b' },
   { "branch-counts",        no_argument,       NULL, 'c' },
   { "intermediate-format",  no_argument,       NULL, 'i' },
+  { "human-numbers",	    no_argument,       NULL, 'j' },
   { "no-output",            no_argument,       NULL, 'n' },
   { "long-file-names",      no_argument,       NULL, 'l' },
   { "function-summaries",   no_argument,       NULL, 'f' },
@@ -775,7 +781,7 @@  process_args (int argc, char **argv)
 {
   int opt;
 
-  const char *opts = "abcdfhiklmno:prs:uvwx";
+  const char *opts = "abcdfhijklmno:prs:uvwx";
   while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
     {
       switch (opt)
@@ -798,6 +804,9 @@  process_args (int argc, char **argv)
 	case 'l':
 	  flag_long_names = 1;
 	  break;
+	case 'j':
+	  flag_human_readable_numbers = 1;
+	  break;
 	case 'k':
 	  flag_use_colors = 1;
 	  break;
@@ -1938,6 +1947,36 @@  add_branch_counts (coverage_t *coverage, const arc_t *arc)
     }
 }
 
+/* Format COUNT, if flag_human_readable_numbers is set, return it human
+   readable format.  */
+
+static char const *
+format_count (gcov_type count)
+{
+  static char buffer[64];
+  float v = count;
+  const char *units[] = {"", "K", "M", "G", "Y", "P", "E", "Z"};
+
+  if (count < 1000 || !flag_human_readable_numbers)
+    {
+      sprintf (buffer, "%" PRId64, count);
+      return buffer;
+    }
+
+  for (unsigned i = 0; i < sizeof (units); i++)
+    {
+      if (v < 1000.0f)
+	{
+	  sprintf (buffer, "%3.2f%s", v, units[i]);
+	  return buffer;
+	}
+
+      v /= 1000.0f;
+    }
+
+  gcc_unreachable ();
+}
+
 /* Format a GCOV_TYPE integer as either a percent ratio, or absolute
    count.  If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
    If DP is zero, no decimal point is printed. Only print 100% when
@@ -1985,7 +2024,7 @@  format_gcov (gcov_type top, gcov_type bottom, int dp)
 	}
     }
   else
-    sprintf (buffer, "%" PRId64, (int64_t)top);
+    return format_count (top);
 
   return buffer;
 }
diff --git a/gcc/testsuite/g++.dg/gcov/loop.C b/gcc/testsuite/g++.dg/gcov/loop.C
new file mode 100644
index 00000000000..a9d404e7e11
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gcov/loop.C
@@ -0,0 +1,27 @@ 
+/* { dg-options "-fprofile-arcs -ftest-coverage" } */
+/* { dg-do run { target native } } */
+
+unsigned
+loop (unsigned n, int value)		  /* count(14.00K) */
+{
+  for (unsigned i = 0; i < n - 1; i++)
+  {
+    value += i;				  /* count(20.99M) */
+  }
+
+  return value;
+}
+
+int main(int argc, char **argv)
+{
+  unsigned sum = 0;
+  for (unsigned i = 0; i < 7 * 1000; i++)
+  {
+    sum += loop (1000, sum);
+    sum += loop (2000, sum);		  /* count(7.00K) */
+  }
+
+  return 0;				  /* count(1) */
+}
+
+/* { dg-final { run-gcov branches { -abj loop.C } } } */
diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp
index 18adc71351a..38f0db09afc 100644
--- a/gcc/testsuite/lib/gcov.exp
+++ b/gcc/testsuite/lib/gcov.exp
@@ -59,7 +59,7 @@  proc verify-lines { testname testcase file } {
     while { [gets $fd line] >= 0 } {
         # We want to match both "-" and "#####" as count as well as numbers,
         # since we want to detect lines that shouldn't be marked as covered.
-	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=]+)\\)(.*)" \
+	if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=\\.KMGYPEZ]+)\\)(.*)" \
 		"$line" all is n shouldbe rest] {
 	    if [regexp "^ *{(.*)}" $rest all xfailed] {
 		switch [dg-process-target $xfailed] {