Hello.
As mentioned in the PR, gcda files tend to occupy a large disk space
when each running process streams to its own directory. The test-case
in the PR has very low coverage and so that the data file contain
a lot of zero records.
The patch is attempt to not stream these zero COUNTERS:
a-tramp3d-v4.gcda: 01a10000: 38:COUNTERS arcs 19 counts (all zero)
a-tramp3d-v4.gcda: 0: 0 0 0 0 0 0 0 0
a-tramp3d-v4.gcda: 8: 0 0 0 0 0 0 0 0
a-tramp3d-v4.gcda: 16: 0 0 0
Here we stream -19 as count and any of the 0 is streamed.
Patch can bootstrap on x86_64-linux-gnu and survives regression tests.
The patch survives PGO bootstrap of GCC and it shrinks size of gcda
file from 17MB to 12MB.
One alternative approach could be to use a normal compression (zlib, zstd)
for output file? That can save even more space.
Thoughts?
Thanks,
Martin
gcc/ChangeLog:
PR gcov-profile/95348
* coverage.c (read_counts_file): Read only COUNTERS that are
not all-zero.
* gcov-dump.c (tag_function): Change signature from unsigned to
signed integer.
(tag_blocks): Likewise.
(tag_arcs): Likewise.
(tag_lines): Likewise.
(tag_counters): Likewise.
(tag_summary): Likewise.
* gcov.c (read_count_file): Read all non-zero counters
sensitively.
libgcc/ChangeLog:
PR gcov-profile/95348
* libgcov-driver.c (merge_one_data): Merge only profiles
that are not of non-zero type.
(write_one_data): Write counters only if there's one non-zero
value.
* libgcov-util.c (tag_function): Change signature from unsigned
to int.
(tag_blocks): Likewise.
(tag_arcs): Likewise.
(tag_counters): Likewise.
(tag_summary): Likewise.
(tag_lines): Read only if COUNTERS is non-zero.
(read_gcda_file): Handle negative length for COUNTERS type.
---
gcc/coverage.c | 9 +++++---
gcc/gcov-dump.c | 51 ++++++++++++++++++++++-------------------
gcc/gcov.c | 9 ++++++--
libgcc/libgcov-driver.c | 44 ++++++++++++++++++++++++-----------
libgcc/libgcov-util.c | 41 ++++++++++++++++++---------------
5 files changed, 92 insertions(+), 62 deletions(-)
diff --git a/gcc/coverage.c b/gcc/coverage.c
index 1dcda4353cd..f353c9c5022 100644
--- a/gcc/coverage.c
+++ b/gcc/coverage.c
@@ -245,7 +245,9 @@ read_counts_file (void)
else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
{
counts_entry **slot, *entry, elt;
- unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
+ int read_length = (int)length;
+ length = read_length > 0 ? read_length : 0;
+ unsigned n_counts = GCOV_TAG_COUNTER_NUM (abs (read_length));
unsigned ix;
elt.ident = fn_ident;
@@ -274,8 +276,9 @@ read_counts_file (void)
counts_hash = NULL;
break;
}
- for (ix = 0; ix != n_counts; ix++)
- entry->counts[ix] += gcov_read_counter ();
+ if (read_length > 0)
+ for (ix = 0; ix != n_counts; ix++)
+ entry->counts[ix] = gcov_read_counter ();
}
gcov_sync (offset, length);
if ((is_error = gcov_is_error ()))
diff --git a/gcc/gcov-dump.c b/gcc/gcov-dump.c
index ffb71ca4b68..37d175408ea 100644
--- a/gcc/gcov-dump.c
+++ b/gcc/gcov-dump.c
@@ -32,19 +32,19 @@ static void dump_gcov_file (const char *);
static void print_prefix (const char *, unsigned, gcov_position_t);
static void print_usage (void);
static void print_version (void);
-static void tag_function (const char *, unsigned, unsigned, unsigned);
-static void tag_blocks (const char *, unsigned, unsigned, unsigned);
-static void tag_arcs (const char *, unsigned, unsigned, unsigned);
-static void tag_lines (const char *, unsigned, unsigned, unsigned);
-static void tag_counters (const char *, unsigned, unsigned, unsigned);
-static void tag_summary (const char *, unsigned, unsigned, unsigned);
+static void tag_function (const char *, unsigned, int, unsigned);
+static void tag_blocks (const char *, unsigned, int, unsigned);
+static void tag_arcs (const char *, unsigned, int, unsigned);
+static void tag_lines (const char *, unsigned, int, unsigned);
+static void tag_counters (const char *, unsigned, int, unsigned);
+static void tag_summary (const char *, unsigned, int, unsigned);
extern int main (int, char **);
typedef struct tag_format
{
unsigned tag;
char const *name;
- void (*proc) (const char *, unsigned, unsigned, unsigned);
+ void (*proc) (const char *, unsigned, int, unsigned);
} tag_format_t;
static int flag_dump_contents = 0;
@@ -226,6 +226,7 @@ dump_gcov_file (const char *filename)
while (1)
{
gcov_position_t base, position = gcov_position ();
+ int read_length;
unsigned tag, length;
tag_format_t const *format;
unsigned tag_depth;
@@ -235,7 +236,8 @@ dump_gcov_file (const char *filename)
tag = gcov_read_unsigned ();
if (!tag)
break;
- length = gcov_read_unsigned ();
+ read_length = (int)gcov_read_unsigned ();
+ length = read_length > 0 ? read_length : 0;
base = gcov_position ();
mask = GCOV_TAG_MASK (tag) >> 1;
for (tag_depth = 4; mask; mask >>= 8)
@@ -265,9 +267,9 @@ dump_gcov_file (const char *filename)
}
print_prefix (filename, tag_depth, position);
- printf ("%08x:%4u:%s", tag, length, format->name);
+ printf ("%08x:%4u:%s", tag, abs (read_length), format->name);
if (format->proc)
- (*format->proc) (filename, tag, length, depth);
+ (*format->proc) (filename, tag, read_length, depth);
printf ("\n");
if (flag_dump_contents && format->proc)
@@ -295,10 +297,10 @@ dump_gcov_file (const char *filename)
static void
tag_function (const char *filename ATTRIBUTE_UNUSED,
- unsigned tag ATTRIBUTE_UNUSED, unsigned length,
+ unsigned tag ATTRIBUTE_UNUSED, int length,
unsigned depth ATTRIBUTE_UNUSED)
{
- unsigned long pos = gcov_position ();
+ long pos = gcov_position ();
if (!length)
printf (" placeholder");
@@ -331,7 +333,7 @@ tag_function (const char *filename ATTRIBUTE_UNUSED,
static void
tag_blocks (const char *filename ATTRIBUTE_UNUSED,
- unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED,
+ unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
unsigned depth ATTRIBUTE_UNUSED)
{
printf (" %u blocks", gcov_read_unsigned ());
@@ -339,7 +341,7 @@ tag_blocks (const char *filename ATTRIBUTE_UNUSED,
static void
tag_arcs (const char *filename ATTRIBUTE_UNUSED,
- unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED,
+ unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
unsigned depth)
{
unsigned n_arcs = GCOV_TAG_ARCS_NUM (length);
@@ -381,7 +383,7 @@ tag_arcs (const char *filename ATTRIBUTE_UNUSED,
static void
tag_lines (const char *filename ATTRIBUTE_UNUSED,
- unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED,
+ unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
unsigned depth)
{
if (flag_dump_contents)
@@ -426,7 +428,7 @@ tag_lines (const char *filename ATTRIBUTE_UNUSED,
static void
tag_counters (const char *filename ATTRIBUTE_UNUSED,
- unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED,
+ unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
unsigned depth)
{
#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) NAME,
@@ -434,15 +436,16 @@ tag_counters (const char *filename ATTRIBUTE_UNUSED,
#include "gcov-counter.def"
};
#undef DEF_GCOV_COUNTER
- unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
+ int n_counts = GCOV_TAG_COUNTER_NUM (length);
+ bool has_zeros = n_counts < 0;
+ n_counts = abs (n_counts);
- printf (" %s %u counts",
- counter_names[GCOV_COUNTER_FOR_TAG (tag)], n_counts);
+ printf (" %s %u counts%s",
+ counter_names[GCOV_COUNTER_FOR_TAG (tag)], n_counts,
+ has_zeros ? " (all zero)" : "");
if (flag_dump_contents)
{
- unsigned ix;
-
- for (ix = 0; ix != n_counts; ix++)
+ for (int ix = 0; ix != n_counts; ix++)
{
gcov_type count;
@@ -458,7 +461,7 @@ tag_counters (const char *filename ATTRIBUTE_UNUSED,
printf (VALUE_PADDING_PREFIX VALUE_PREFIX, ix);
}
- count = gcov_read_counter ();
+ count = has_zeros ? 0 : gcov_read_counter ();
printf ("%" PRId64 " ", count);
}
}
@@ -466,7 +469,7 @@ tag_counters (const char *filename ATTRIBUTE_UNUSED,
static void
tag_summary (const char *filename ATTRIBUTE_UNUSED,
- unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED,
+ unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
unsigned depth ATTRIBUTE_UNUSED)
{
gcov_summary summary;
diff --git a/gcc/gcov.c b/gcc/gcov.c
index 488847231c2..cba33f3dd14 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -1968,11 +1968,16 @@ read_count_file (void)
}
else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
{
+ int read_length = (int)length;
+ length = abs (read_length);
if (length != GCOV_TAG_COUNTER_LENGTH (fn->counts.size ()))
goto mismatch;
- for (ix = 0; ix != fn->counts.size (); ix++)
- fn->counts[ix] += gcov_read_counter ();
+ if (read_length > 0)
+ for (ix = 0; ix != fn->counts.size (); ix++)
+ fn->counts[ix] += gcov_read_counter ();
+ else
+ length = 0;
}
gcov_sync (base, length);
if ((error = gcov_is_error ()))
diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c
index cbfcae96d19..ea21fd6e0e8 100644
--- a/libgcc/libgcov-driver.c
+++ b/libgcc/libgcov-driver.c
@@ -302,13 +302,16 @@ merge_one_data (const char *filename,
continue;
tag = gcov_read_unsigned ();
- length = gcov_read_unsigned ();
+ int read_length = (int)gcov_read_unsigned ();
+ length = abs (read_length);
if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
|| (length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num)
&& t_ix != GCOV_COUNTER_V_TOPN
&& t_ix != GCOV_COUNTER_V_INDIR))
goto read_mismatch;
- (*merge) (ci_ptr->values, ci_ptr->num);
+ /* Merging with all zero counters does not make sense. */
+ if (read_length > 0)
+ (*merge) (ci_ptr->values, ci_ptr->num);
ci_ptr++;
}
if ((error = gcov_is_error ()))
@@ -414,27 +417,40 @@ write_one_data (const struct gcov_info *gi_ptr,
ci_ptr = gfi_ptr->ctrs;
for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
{
- gcov_unsigned_t n_counts;
- gcov_type *c_ptr;
+ gcov_position_t n_counts;
- if (!gi_ptr->merge[t_ix])
- continue;
+ if (!gi_ptr->merge[t_ix])
+ continue;
- n_counts = ci_ptr->num;
+ n_counts = ci_ptr->num;
if (gi_ptr->merge[t_ix] == __gcov_merge_topn)
write_top_counters (ci_ptr, t_ix, n_counts);
else
{
- gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
- GCOV_TAG_COUNTER_LENGTH (n_counts));
- c_ptr = ci_ptr->values;
- while (n_counts--)
- gcov_write_counter (*c_ptr++);
+ /* Do not stream when all counters are zero. */
+ int all_zeros = 1;
+ for (unsigned i = 0; i < n_counts; i++)
+ if (ci_ptr->values[i] != 0)
+ {
+ all_zeros = 0;
+ break;
+ }
+
+ if (all_zeros)
+ gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
+ GCOV_TAG_COUNTER_LENGTH (-n_counts));
+ else
+ {
+ gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
+ GCOV_TAG_COUNTER_LENGTH (n_counts));
+ for (unsigned i = 0; i < n_counts; i++)
+ gcov_write_counter (ci_ptr->values[i]);
+ }
}
- ci_ptr++;
- }
+ ci_ptr++;
+ }
if (buffered)
fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
}
diff --git a/libgcc/libgcov-util.c b/libgcc/libgcov-util.c
index 224c190ee63..a8ce6d7f8ae 100644
--- a/libgcc/libgcov-util.c
+++ b/libgcc/libgcov-util.c
@@ -57,12 +57,12 @@ void gcov_set_verbose (void)
#include <ftw.h>
#endif
-static void tag_function (unsigned, unsigned);
-static void tag_blocks (unsigned, unsigned);
-static void tag_arcs (unsigned, unsigned);
-static void tag_lines (unsigned, unsigned);
-static void tag_counters (unsigned, unsigned);
-static void tag_summary (unsigned, unsigned);
+static void tag_function (unsigned, int);
+static void tag_blocks (unsigned, int);
+static void tag_arcs (unsigned, int);
+static void tag_lines (unsigned, int);
+static void tag_counters (unsigned, int);
+static void tag_summary (unsigned, int);
/* The gcov_info for the first module. */
static struct gcov_info *curr_gcov_info;
@@ -119,7 +119,7 @@ typedef struct tag_format
{
unsigned tag;
char const *name;
- void (*proc) (unsigned, unsigned);
+ void (*proc) (unsigned, int);
} tag_format_t;
/* Handler table for various Tags. */
@@ -140,7 +140,7 @@ static const tag_format_t tag_table[] =
/* Handler for reading function tag. */
static void
-tag_function (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+tag_function (unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED)
{
int i;
@@ -173,7 +173,7 @@ tag_function (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
/* Handler for reading block tag. */
static void
-tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+tag_blocks (unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED)
{
/* TBD: gcov-tool currently does not handle gcno files. Assert here. */
gcc_unreachable ();
@@ -182,7 +182,7 @@ tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length
ATTRIBUTE_UNUSED)
/* Handler for reading flow arc tag. */
static void
-tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+tag_arcs (unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED)
{
/* TBD: gcov-tool currently does not handle gcno files. Assert here. */
gcc_unreachable ();
@@ -191,7 +191,7 @@ tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length
ATTRIBUTE_UNUSED)
/* Handler for reading line tag. */
static void
-tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+tag_lines (unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED)
{
/* TBD: gcov-tool currently does not handle gcno files. Assert here. */
gcc_unreachable ();
@@ -200,9 +200,9 @@ tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length
ATTRIBUTE_UNUSED)
/* Handler for reading counters array tag with value as TAG and length of
LENGTH. */
static void
-tag_counters (unsigned tag, unsigned length)
+tag_counters (unsigned tag, int length)
{
- unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
+ unsigned n_counts = GCOV_TAG_COUNTER_NUM (abs (length));
gcov_type *values;
unsigned ix;
unsigned tag_ix;
@@ -213,17 +213,19 @@ tag_counters (unsigned tag, unsigned length)
gcc_assert (k_ctrs[tag_ix].num == 0);
k_ctrs[tag_ix].num = n_counts;
- k_ctrs[tag_ix].values = values = (gcov_type *) xmalloc (n_counts * sizeof (gcov_type));
+ k_ctrs[tag_ix].values = values = (gcov_type *) xcalloc (sizeof (gcov_type),
+ n_counts);
gcc_assert (values);
- for (ix = 0; ix != n_counts; ix++)
- values[ix] = gcov_read_counter ();
+ if (length > 0)
+ for (ix = 0; ix != n_counts; ix++)
+ values[ix] = gcov_read_counter ();
}
/* Handler for reading summary tag. */
static void
-tag_summary (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+tag_summary (unsigned tag ATTRIBUTE_UNUSED, int ATTRIBUTE_UNUSED)
{
curr_object_summary = (gcov_summary *) xcalloc (sizeof (gcov_summary), 1);
gcov_read_summary (curr_object_summary);
@@ -324,7 +326,8 @@ read_gcda_file (const char *filename)
tag = gcov_read_unsigned ();
if (!tag)
break;
- length = gcov_read_unsigned ();
+ int read_length = (int)gcov_read_unsigned ();
+ length = read_length > 0 ? read_length : 0;
base = gcov_position ();
mask = GCOV_TAG_MASK (tag) >> 1;
for (tag_depth = 4; mask; mask >>= 8)
@@ -357,7 +360,7 @@ read_gcda_file (const char *filename)
{
unsigned long actual_length;
- (*format->proc) (tag, length);
+ (*format->proc) (tag, read_length);
actual_length = gcov_position () - base;
if (actual_length > length)
--
2.27.0