This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 3648e5db3fc3deae22b08bffa5401ef832fc47e6
Author: wangmingrong1 <wangmingro...@xiaomi.com>
AuthorDate: Tue Mar 11 14:29:21 2025 +0800

    gcov: Refactoring the implementation framework of gcov
    
    All implementations of gcov are sunk to the kernel implementation
    1. Support three dump modes: serial port output, single file output, 
standard output
    
    Signed-off-by: wangmingrong1 <wangmingro...@xiaomi.com>
---
 include/gcov.h                | 114 -----------
 libs/libbuiltin/libgcc/gcov.c | 435 ++++++++++++++++++++++++------------------
 tools/gcov.py                 | 271 ++++++++++++++------------
 3 files changed, 399 insertions(+), 421 deletions(-)

diff --git a/include/gcov.h b/include/gcov.h
index 3c6251b82c..5dc38d07dd 100644
--- a/include/gcov.h
+++ b/include/gcov.h
@@ -29,63 +29,6 @@
 
 #include <sys/types.h>
 
-/****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
-
-/* The GCOV 12 gcno/gcda format has slight change,
- * Please refer to gcov-io.h in the GCC 12 for
- * more details.
- */
-
-#if __GNUC__ >= 12
-#  define GCOV_12_FORMAT
-#endif
-
-#if __GNUC__ >= 14
-#  define GCOV_COUNTERS 9u
-#elif __GNUC__ >= 10
-#  define GCOV_COUNTERS 8u
-#elif __GNUC__ >= 8
-#  define GCOV_COUNTERS 9u
-#else
-#  define GCOV_COUNTERS 10u
-#endif
-
-/****************************************************************************
- * Public Types
- ****************************************************************************/
-
-struct gcov_fn_info;
-typedef uint64_t gcov_type;
-
-/** Profiling data per object file
- *
- * This data is generated by gcc during compilation and doesn't change
- * at run-time with the exception of the next pointer.
- */
-
-struct gcov_info
-{
-  unsigned int version;                /* Gcov version (same as GCC version) */
-  FAR struct gcov_info *next;          /* List head for a singly-linked list */
-  unsigned int stamp;                  /* Uniquifying time stamp */
-#ifdef GCOV_12_FORMAT
-  unsigned int checksum;               /* unique object checksum */
-#endif
-  FAR const char *filename;            /* Name of the associated gcda data 
file */
-  void (*merge[GCOV_COUNTERS])(FAR gcov_type *, unsigned int);
-  unsigned int n_functions;            /* number of instrumented functions */
-  FAR struct gcov_fn_info **functions; /* function information */
-};
-
-/****************************************************************************
- * Public Data
- ****************************************************************************/
-
-extern FAR struct gcov_info *__gcov_info_start;
-extern FAR struct gcov_info *__gcov_info_end;
-
 /****************************************************************************
  * Public Function Prototypes
  ****************************************************************************/
@@ -118,63 +61,6 @@ extern void __gcov_reset(void);
 
 extern void __gcov_dump(void);
 
-/****************************************************************************
- * Name: __gcov_info_to_gcda
- *
- * Description:
- *   Convert the gcov information referenced by INFO to a gcda data stream.
- *
- * Parameters:
- *   info - Pointer to the gcov information.
- *   filename - Callback function to get the filename.
- *   dump - Callback function to write the gcda data.
- *   allocate - Callback function to allocate memory.
- *   arg - User-provided argument.
- *
- ****************************************************************************/
-
-extern void __gcov_info_to_gcda(FAR const struct gcov_info *info,
-                                FAR void (*filename)(FAR const char *,
-                                                     FAR void *),
-                                FAR void (*dump)(FAR const void *,
-                                                 size_t, FAR void *),
-                                FAR void *(*allocate)(unsigned int,
-                                                      FAR void *),
-                                FAR void *arg);
-
-/****************************************************************************
- * Name: __gcov_filename_to_gcfn
- *
- * Description:
- *   Convert the filename to a gcfn data stream.
- *
- * Parameters:
- *   filename - Pointer to the filename.
- *   dump - Callback function to write the gcfn data.
- *   arg - User-provided argument.
- *
- ****************************************************************************/
-
-extern void __gcov_filename_to_gcfn(FAR const char *filename,
-                                    FAR void (*dump)(FAR const void *,
-                                                     unsigned int,
-                                                     FAR void *),
-                                     FAR void *arg);
-
-/****************************************************************************
- * Name: __gcov_dump_to_memory
- *
- * Description:
- *   Dump gcov data directly to memory
- *
- * Parameters:
- *   ptr - Memory Address
- *   size - Memory block size
- *
- ****************************************************************************/
-
-size_t __gcov_dump_to_memory(FAR void *ptr, size_t size);
-
 #undef EXTERN
 #ifdef __cplusplus
 }
diff --git a/libs/libbuiltin/libgcc/gcov.c b/libs/libbuiltin/libgcc/gcov.c
index e9571ce97f..6d6680382c 100644
--- a/libs/libbuiltin/libgcc/gcov.c
+++ b/libs/libbuiltin/libgcc/gcov.c
@@ -27,25 +27,30 @@
 #include <errno.h>
 #include <string.h>
 #include <syslog.h>
-#include <unistd.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 
-#include <nuttx/lib/lib.h>
+#include <nuttx/streams.h>
 #include <nuttx/reboot_notifier.h>
 
 /****************************************************************************
  * Pre-processor Definitions
  ****************************************************************************/
 
-#define GCOV_DATA_MAGIC       (0x67636461)
-#define GCOV_NOTE_MAGIC       (0x67636e6f)
-#define GCOV_FILENAME_MAGIC   (0x6763666e)
+/* The GCOV 12 gcno/gcda format has slight change,
+ * Please refer to gcov-io.h in the GCC 12 for
+ * more details.
+ */
 
-#define GCOV_TAG_FUNCTION     (0x01000000)
-#define GCOV_TAG_COUNTER_BASE (0x01a10000)
+#if __GNUC__ >= 12
+#  define GCOV_12_FORMAT
+#endif
 
-#define GCOV_TAG_FOR_COUNTER(count) \
-  (GCOV_TAG_COUNTER_BASE + ((uint32_t)(count) << 17))
+#ifdef GCOV_12_FORMAT
+#  define GCOV_UNIT_SIZE      4
+#else
+#  define GCOV_UNIT_SIZE      1
+#endif
 
 #ifdef GCOV_12_FORMAT
 #  define GCOV_TAG_FUNCTION_LENGTH 12
@@ -53,18 +58,55 @@
 #  define GCOV_TAG_FUNCTION_LENGTH 3
 #endif
 
-#ifdef GCOV_12_FORMAT
-#  define GCOV_UNIT_SIZE      4
+#if __GNUC__ >= 14
+#  define GCOV_COUNTERS 9u
+#elif __GNUC__ >= 10
+#  define GCOV_COUNTERS 8u
+#elif __GNUC__ >= 8
+#  define GCOV_COUNTERS 9u
 #else
-#  define GCOV_UNIT_SIZE      1
+#  define GCOV_COUNTERS 10u
 #endif
 
+#define GCOV_DATA_MAGIC       (0x67636461)
+#define GCOV_NOTE_MAGIC       (0x67636e6f)
+#define GCOV_FILENAME_MAGIC   (0x6763666e)
+
+#define GCOV_TAG_FUNCTION     (0x01000000)
+#define GCOV_TAG_COUNTER_BASE (0x01a10000)
+
+#define GCOV_TAG_FOR_COUNTER(count) \
+  (GCOV_TAG_COUNTER_BASE + ((uint32_t)(count) << 17))
+
+#define GCOV_PATH_MAX_TOKENS 64
+
 /****************************************************************************
  * Private Types
  ****************************************************************************/
 
+typedef uint64_t gcov_type;
 typedef unsigned int gcov_unsigned_t;
 
+/** Profiling data per object file
+ *
+ * This data is generated by gcc during compilation and doesn't change
+ * at run-time with the exception of the next pointer.
+ */
+
+struct gcov_info
+{
+  unsigned int version;                /* Gcov version (same as GCC version) */
+  FAR struct gcov_info *next;          /* List head for a singly-linked list */
+  unsigned int stamp;                  /* Uniquifying time stamp */
+#ifdef GCOV_12_FORMAT
+  unsigned int checksum;               /* unique object checksum */
+#endif
+  FAR const char *filename;            /* Name of the associated gcda data 
file */
+  void (*merge[GCOV_COUNTERS])(FAR gcov_type *, unsigned int);
+  unsigned int n_functions;            /* number of instrumented functions */
+  FAR struct gcov_fn_info **functions; /* function information */
+};
+
 /* Information about counters for a single function
  *
  * This data is generated by gcc during compilation and doesn't change
@@ -92,6 +134,20 @@ struct gcov_fn_info
   struct gcov_ctr_info ctrs[1];    /* Instrumented counters */
 };
 
+struct dump_args
+{
+  FAR CODE int  (*mkdir)(FAR const char *);
+  FAR struct lib_outstream_s *stream;
+  FAR char *prefix;
+  FAR char *tokens[GCOV_PATH_MAX_TOKENS];
+  size_t    token_cnt;
+  FAR char *path_buffer;
+  int       strip;
+};
+
+typedef CODE int (*dump_fn_t)(FAR struct dump_args *,
+                              FAR char *, FAR uint8_t *, size_t);
+
 /****************************************************************************
  * Public Data
  ****************************************************************************/
@@ -123,6 +179,28 @@ static void dump_unsigned(FAR void *buffer, FAR size_t 
*off, uint32_t v)
   *off += sizeof(uint32_t);
 }
 
+static int get_path_tokens(FAR char *path,
+                           FAR char **tokens, size_t max_tokens)
+{
+  size_t token_count = 0;
+  FAR char *token;
+
+  token = strtok(path, "/");
+  while (token != NULL)
+    {
+      if (token_count >= max_tokens)
+        {
+          return -ENAMETOOLONG;
+        }
+
+      tokens[token_count++] = token;
+      token = strtok(NULL, "/");
+    }
+
+  tokens[token_count] = NULL;
+  return token_count;
+}
+
 static size_t gcov_convert(FAR uint8_t *buffer,
                            FAR const struct gcov_info *info)
 {
@@ -182,130 +260,150 @@ static size_t gcov_convert(FAR uint8_t *buffer,
   return pos;
 }
 
-static int gcov_process_path(FAR char *prefix, int strip,
-                             FAR char *path, FAR char *new_path,
-                             size_t len)
+static int gcov_mkdir(FAR const char *path)
 {
-  FAR char *tokens[64];
-  FAR char *filename;
-  FAR char *token;
-  int token_count = 0;
-  int prefix_count;
-  int level = 0;
-  int ret;
-  int i;
-
-  token = strtok(prefix, "/");
-  while (token != NULL)
+  if (access(path, F_OK) != 0)
     {
-      tokens[token_count++] = token;
-      token = strtok(NULL, "/");
+      return mkdir(path, 0777);
     }
 
-  /* Split the path into directories and filename */
+  return OK;
+}
 
-  prefix_count = token_count;
-  token = strtok(path, "/");
-  if (token == NULL)
+#ifdef CONFIG_COVERAGE_GCOV_DUMP_REBOOT
+static int gcov_reboot_notify(FAR struct notifier_block *nb,
+                              unsigned long action, FAR void *data)
+{
+  __gcov_dump();
+  return OK;
+}
+#endif
+
+static int gcov_standard_dump(FAR struct dump_args *args,
+                              FAR char *path, FAR uint8_t *data, size_t size)
+{
+  int written;
+  int fd;
+
+  fd = _NX_OPEN(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+  if (fd < 0)
     {
-      return -EINVAL;
+      return -errno;
     }
 
-  while (token != NULL)
+  written = _NX_WRITE(fd, data, size);
+  if (written != size)
     {
-      filename = token;
-      if (level++ >= strip)
-        {
-          /* Skip the specified number of leading directories */
+      return -errno;
+    }
 
-          if (token_count >= sizeof(tokens) / sizeof(tokens[0]))
-            {
-              return -ENAMETOOLONG;
-            }
+  _NX_CLOSE(fd);
+  return OK;
+}
 
-          tokens[token_count++] = token;
-        }
+static int gcov_onefile_dump(FAR struct dump_args *args,
+                             FAR char *path,
+                             FAR uint8_t *data, size_t size)
+{
+  FAR struct lib_outstream_s *stream = args->stream;
+  struct lib_hexdumpstream_s hexstream;
+  uint16_t checksum = 0;
+  int i;
 
-      token = strtok(NULL, "/");
+  lib_hexdumpstream(&hexstream, stream);
+  for (i = 0; i < size; i++)
+    {
+      checksum += ((FAR const uint8_t *)data)[i];
     }
 
-  /* Add the filename */
+  lib_sprintf(stream,
+              "gcov start filename:%s size: %zuByte\n",
+              path, size);
 
-  if (prefix_count == token_count)
-    {
-      tokens[token_count++] = filename;
-    }
+  lib_stream_puts(&hexstream, data, size);
+  lib_stream_flush(&hexstream);
 
-  new_path[0] = '\0';
-  tokens[token_count] = NULL;
+  lib_sprintf(stream,
+              "gcov end filename:%s checksum: %#0x\n",
+              path, checksum);
+  lib_stream_flush(stream);
+
+  return OK;
+}
 
-  /* Check and create directories */
+static int gcov_dump_foreach(dump_fn_t handler, FAR struct dump_args *args)
+{
+  FAR struct gcov_info *info;
+  FAR char *filename;
+  FAR uint8_t *data;
+  int token_count;
+  int size;
+  int i;
 
-  for (i = 0; i < token_count - 1; i++)
+  for (info = __gcov_info_start; info; info = info->next)
     {
-      strcat(new_path, "/");
-      strcat(new_path, tokens[i]);
-      if (access(new_path, F_OK) != 0)
+      memset(args->path_buffer, 0x00, PATH_MAX);
+
+      size = gcov_convert(NULL, info);
+      data = lib_malloc(size);
+      if (data == NULL)
+        {
+          syslog(LOG_ERR, "gcov alloc failed!");
+          return -ERROR;
+        }
+
+      gcov_convert(data, info);
+
+      filename = strdup(info->filename);
+      if (filename == NULL)
+        {
+          syslog(LOG_ERR, "gcov alloc failed! skip %s", info->filename);
+          lib_free(data);
+          return -ERROR;
+        }
+
+      token_count = args->token_cnt +
+                    get_path_tokens(filename,
+                                    &args->tokens[args->token_cnt],
+                                    GCOV_PATH_MAX_TOKENS);
+      if (token_count < args->token_cnt)
+        {
+          syslog(LOG_ERR, "gcov get path tokens failed! skip %s",
+                          info->filename);
+          goto exit;
+        }
+
+      for (i = 0; i < token_count - 1; i++)
         {
-          ret = mkdir(new_path, 0777);
-          if (ret != 0)
+          strcat(args->path_buffer, "/");
+          strcat(args->path_buffer, args->tokens[i]);
+          if (args->mkdir)
             {
-              return -errno;
+              args->mkdir(args->path_buffer);
             }
         }
-    }
 
-  strcat(new_path, "/");
-  strcat(new_path, filename);
-  return 0;
-}
-
-static int gcov_write_file(FAR const char *filename,
-                           FAR const struct gcov_info *info)
-{
-  FAR uint8_t *buffer;
-  size_t written;
-  int ret = OK;
-  size_t size;
-  int fd;
+      strcat(args->path_buffer, "/");
+      strcat(args->path_buffer, args->tokens[token_count - 1]);
 
-  fd = _NX_OPEN(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-  if (fd < 0)
-    {
-      syslog(LOG_ERR, "open %s failed!", filename);
-      return -errno;
-    }
+      if (handler(args, args->path_buffer, data, size) < 0)
+        {
+          syslog(LOG_ERR, "gcov dump %s failed!\n", args->path_buffer);
+          goto exit;
+        }
 
-  size = gcov_convert(NULL, info);
-  buffer = lib_malloc(size);
-  if (buffer == NULL)
-    {
-      syslog(LOG_ERR, "gcov alloc failed!");
-      _NX_CLOSE(fd);
-      return -errno;
+      lib_free(filename);
+      lib_free(data);
     }
 
-  gcov_convert(buffer, info);
-  written = _NX_WRITE(fd, buffer, size);
-  if (written != size)
-    {
-      syslog(LOG_ERR, "gcov write file failed!");
-      ret = -errno;
-    }
+  return OK;
 
-  _NX_CLOSE(fd);
-  lib_free(buffer);
-  return ret;
-}
+exit:
+  lib_free(filename);
+  lib_free(data);
 
-#ifdef CONFIG_COVERAGE_GCOV_DUMP_REBOOT
-static int gcov_reboot_notify(FAR struct notifier_block *nb,
-                              unsigned long action, FAR void *data)
-{
-  __gcov_dump();
-  return 0;
+  return -ERROR;
 }
-#endif
 
 /****************************************************************************
  * Public Functions
@@ -332,6 +430,7 @@ void __gcov_init(FAR struct gcov_info *info)
                tm_info);
 
       setenv("GCOV_PREFIX_STRIP", CONFIG_COVERAGE_DEFAULT_PREFIX_STRIP, 1);
+      setenv("GCOV_DUMP_ONEFILE", "1", 1);
       setenv("GCOV_PREFIX", path, 1);
 
 #ifdef CONFIG_COVERAGE_GCOV_DUMP_REBOOT
@@ -373,66 +472,68 @@ pid_t __gcov_fork(void)
 
 void __gcov_dump(void)
 {
-  FAR struct gcov_info *info;
-  FAR const char *strip = getenv("GCOV_PREFIX_STRIP");
-  FAR const char *prefix = getenv("GCOV_PREFIX");
-  FAR char *new_path;
-  FAR char *prefix2;
-  int ret;
-
+  int ret = -1;
+  bool onefile;
+  FAR char *prefix;
+  struct dump_args args =
+  {
+    0
+  };
+
+  prefix = getenv("GCOV_PREFIX");
   if (prefix == NULL)
     {
       syslog(LOG_ERR, "No path prefix specified");
-      return;
     }
 
-  prefix2 = strdup(prefix);
-  if (prefix2 == NULL)
+  args.path_buffer = lib_get_tempbuffer(PATH_MAX);
+  if (args.path_buffer == NULL)
     {
       syslog(LOG_ERR, "gcov alloc failed!");
       return;
     }
 
-  new_path = lib_get_tempbuffer(PATH_MAX);
-  for (info = __gcov_info_start; info; info = info->next)
+  onefile = strcmp(getenv("GCOV_DUMP_ONEFILE"), "1") == 0;
+  if (onefile)
     {
-      FAR char *filename;
-
-      filename = strdup(info->filename);
-      if (filename == NULL)
+      struct lib_rawoutstream_s stream;
+      int fd = _NX_OPEN(prefix, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+      if (fd < 0)
         {
-          syslog(LOG_ERR, "gcov alloc failed! skip %s", info->filename);
-          continue;
+          syslog(LOG_ERR, "open %s failed! ret: %d\n", prefix, ret);
+          goto exit;
         }
 
-      /* Process the path, add the prefix and strip the leading directories */
+      lib_rawoutstream(&stream, fd);
+      args.stream = &stream.common;
 
-      strcpy(prefix2, prefix);
-      ret = gcov_process_path(prefix2, atoi(strip), filename,
-                              new_path, PATH_MAX);
-      if (ret != 0)
-        {
-          syslog(LOG_ERR, "gcov process path failed! skip %s",
-                 new_path);
-          lib_free(filename);
-          continue;
-        }
+      ret = gcov_dump_foreach(gcov_onefile_dump, &args);
 
-      /* Convert the data and write to the file */
+      _NX_CLOSE(fd);
+    }
+  else
+    {
+      args.mkdir = gcov_mkdir;
+      args.strip = atoi(getenv("GCOV_PREFIX_STRIP"));
+      args.token_cnt = get_path_tokens(prefix,
+                                       args.tokens,
+                                       GCOV_PATH_MAX_TOKENS);
 
-      ret = gcov_write_file(new_path, info);
-      if (ret != 0)
-        {
-          syslog(LOG_ERR, "gcov write file failed! skip %s", new_path);
-          lib_free(filename);
-          continue;
-        }
+      ret = gcov_dump_foreach(gcov_standard_dump, &args);
+    }
 
-      lib_free(filename);
+exit:
+
+  if (ret < 0)
+    {
+      syslog(LOG_INFO, "Gcov dump failed\n");
+    }
+  else
+    {
+      syslog(LOG_INFO, "Gcov dump complete\n");
     }
 
-  lib_put_tempbuffer(new_path);
-  lib_free(prefix2);
+  lib_put_tempbuffer(args.path_buffer);
 }
 
 void __gcov_reset(void)
@@ -461,47 +562,3 @@ void __gcov_reset(void)
         }
     }
 }
-
-void __gcov_filename_to_gcfn(FAR const char *filename,
-                             FAR void (*dump_fn)(FAR const void *,
-                                                 unsigned, FAR void *),
-                             FAR void *arg)
-{
-  if (dump_fn)
-    {
-      size_t len = strlen(filename);
-      dump_fn(filename, len, arg);
-    }
-}
-
-void __gcov_info_to_gcda(FAR const struct gcov_info *info,
-                         FAR void (*filename_fn)(FAR const char *,
-                                                 FAR void *),
-                         FAR void (*dump_fn)(FAR const void *, size_t,
-                                             FAR void *),
-                         FAR void *(*allocate_fn)(unsigned int, FAR void *),
-                         FAR void *arg)
-{
-  FAR const char *name = info->filename;
-  FAR uint8_t *buffer;
-  size_t size;
-
-  filename_fn(name, arg);
-
-  /* Get the size of the buffer */
-
-  size = gcov_convert(NULL, info);
-
-  buffer = lib_malloc(size);
-  if (!buffer)
-    {
-      syslog(LOG_ERR, "gcov alloc failed!");
-      return;
-    }
-
-  /* Convert the data */
-
-  gcov_convert(buffer, info);
-  dump_fn(buffer, size, arg);
-  lib_free(buffer);
-}
diff --git a/tools/gcov.py b/tools/gcov.py
index 946b73d6b0..9be55e2b37 100755
--- a/tools/gcov.py
+++ b/tools/gcov.py
@@ -30,6 +30,9 @@ def parse_gcda_data(path):
     with open(path, "r") as file:
         lines = file.read().strip().splitlines()
 
+    gcda_path = path + "_covert"
+    os.makedirs(gcda_path, exist_ok=True)
+
     started = False
     filename = ""
     output = ""
@@ -47,70 +50,147 @@ def parse_gcda_data(path):
         if not started:
             continue
 
-        if line.startswith("gcov end"):
-            started = False
-            if size != len(output) // 2:
-                print(
-                    f"Size mismatch for {filename}: expected {size} bytes, got 
{len(output) // 2} bytes"
-                )
-
-            match = re.search(r"checksum:\s*(0x[0-9a-fA-F]+)", line)
-            if match:
-                checksum = int(match.group(1), 16)
-                output = bytearray.fromhex(output)
-                expected = sum(output) % 65536
-                if checksum != expected:
-                    print(
-                        f"Checksum mismatch for {filename}: expected 
{checksum}, got {expected}"
+        try:
+            if line.startswith("gcov end"):
+                started = False
+                if size != len(output) // 2:
+                    raise ValueError(
+                        f"Size mismatch for {filename}: expected {size} bytes, 
got {len(output) // 2} bytes"
                     )
-                    continue
-
-                with open(filename, "wb") as fp:
-                    fp.write(output)
-                    print(f"write {filename} success")
-            output = ""
-        else:
-            output += line.strip()
-
 
-def correct_content_path(file, newpath):
+                match = re.search(r"checksum:\s*(0x[0-9a-fA-F]+)", line)
+                if match:
+                    checksum = int(match.group(1), 16)
+                    output = bytearray.fromhex(output)
+                    expected = sum(output) % 65536
+                    if checksum != expected:
+                        raise ValueError(
+                            f"Checksum mismatch for {filename}: expected 
{checksum}, got {expected}"
+                        )
+
+                    outfile = os.path.join(gcda_path, "./" + filename)
+                    os.makedirs(os.path.dirname(outfile), exist_ok=True)
+
+                    with open(outfile, "wb") as fp:
+                        fp.write(output)
+                        print(f"write {outfile} success")
+                output = ""
+            else:
+                output += line.strip()
+        except Exception as e:
+            print(f"Error processing {path}: {e}")
+            print(f"gcov start filename:{filename} size:{size}")
+            print(output)
+            print(f"gcov end filename:{filename} checksum:{checksum}")
+
+    return gcda_path
+
+
+def correct_content_path(file, shield: list, newpath):
     with open(file, "r", encoding="utf-8") as f:
         content = f.read()
 
-    pattern = r"SF:([^\s]*?)/nuttx/include/nuttx"
-    matches = re.findall(pattern, content)
+    for i in shield:
+        content = content.replace(i, "")
 
-    if matches:
-        new_content = content.replace(matches[0], newpath)
+    new_content = content
+    if newpath is not None:
+        pattern = r"SF:([^\s]*?)/nuttx/include/nuttx"
+        matches = re.findall(pattern, content)
 
-        with open(file, "w", encoding="utf-8") as f:
-            f.write(new_content)
+        if matches:
+            new_content = content.replace(matches[0], newpath)
 
+    with open(file, "w", encoding="utf-8") as f:
+        f.write(new_content)
 
-def copy_file_endswith(endswith, source_dir, target_dir, skip_dir):
-    print(f"Collect {endswith} files {source_dir} -> {target_dir}")
 
-    if not os.path.exists(target_dir):
-        os.makedirs(target_dir)
-
-    for root, _, files in os.walk(source_dir):
-        if skip_dir in root:
+def copy_file_endswith(endswith, source, target):
+    for root, dirs, files in os.walk(source, topdown=True):
+        if target in root:
             continue
 
         for file in files:
             if file.endswith(endswith):
-                source_file = os.path.join(root, file)
-                target_file = os.path.join(target_dir, file)
-                shutil.copy2(source_file, target_file)
+                src_file = os.path.join(root, file)
+                dst_file = os.path.join(target, os.path.relpath(src_file, 
source))
+                os.makedirs(os.path.dirname(dst_file), exist_ok=True)
+                shutil.copy2(src_file, dst_file)
+
+
+def run_lcov(data_dir, gcov_tool):
+    output = data_dir + ".info"
+    # lcov collect coverage data to coverage.info
+    command = [
+        "lcov",
+        "-c",
+        "-o",
+        output,
+        "--rc",
+        "lcov_branch_coverage=1",
+        "--gcov-tool",
+        gcov_tool,
+        "--ignore-errors",
+        "gcov",
+        "--directory",
+        data_dir,
+    ]
+    print(command)
+    subprocess.run(
+        command,
+        check=True,
+        stdout=sys.stdout,
+        stderr=sys.stdout,
+    )
+
+    return output
+
+
+def run_genhtml(info, report):
+    cmd = [
+        "genhtml",
+        "--branch-coverage",
+        "-o",
+        report,
+        "--ignore-errors",
+        "source",
+        info,
+    ]
+    print(cmd)
+    subprocess.run(
+        cmd,
+        check=True,
+        stdout=sys.stdout,
+        stderr=sys.stdout,
+    )
+
+
+def run_merge(gcda_dir1, gcda_dir2, output, merge_tool):
+    command = [
+        merge_tool,
+        "merge",
+        gcda_dir1,
+        gcda_dir2,
+        "-o",
+        output,
+    ]
+    print(command)
+    subprocess.run(
+        command,
+        check=True,
+        stdout=sys.stdout,
+        stderr=sys.stdout,
+    )
 
 
 def arg_parser():
     parser = argparse.ArgumentParser(
         description="Code coverage generation tool.", add_help=False
     )
-    parser.add_argument("-i", "--input", help="Input dump data")
     parser.add_argument("-t", dest="gcov_tool", help="Path to gcov tool")
     parser.add_argument("-b", dest="base_dir", help="Compile base directory")
+    parser.add_argument("--debug", action="store_true", help="Enable debug 
mode")
+    parser.add_argument("--delete", action="store_true", help="Delete gcda 
files")
     parser.add_argument(
         "-s",
         dest="gcno_dir",
@@ -124,7 +204,6 @@ def arg_parser():
         nargs="+",
         help="Directory containing gcda files",
     )
-    parser.add_argument("--debug", action="store_true", help="Enable debug 
mode")
     parser.add_argument(
         "-x",
         dest="only_copy",
@@ -133,7 +212,7 @@ def arg_parser():
     )
     parser.add_argument(
         "-o",
-        dest="gcov_dir",
+        dest="result_dir",
         default="gcov",
         help="Directory to store gcov data and report",
     )
@@ -145,41 +224,19 @@ def main():
     args = arg_parser()
 
     root_dir = os.getcwd()
-    gcov_dir = os.path.abspath(args.gcov_dir)
     gcno_dir = os.path.abspath(args.gcno_dir)
+    result_dir = os.path.abspath(args.result_dir)
 
-    os.makedirs(gcov_dir, exist_ok=True)
-
-    gcda_dir = []
-    for i in args.gcda_dir:
-        gcda_dir.append(os.path.abspath(i))
-
-    coverage_file = os.path.join(gcov_dir, "coverage.info")
-    result_dir = os.path.join(gcov_dir, "result")
+    os.makedirs(result_dir, exist_ok=True)
+    merge_tool = args.gcov_tool + "-tool"
+    data_dir = os.path.join(result_dir, "data")
+    report_dir = os.path.join(result_dir, "report")
+    coverage_file = os.path.join(result_dir, "coverage.info")
 
     if args.debug:
-        debug_file = os.path.join(gcov_dir, "debug.log")
+        debug_file = os.path.join(result_dir, "debug.log")
         sys.stdout = open(debug_file, "w+")
 
-    if args.input:
-        parse_gcda_data(os.path.join(root_dir, args.input))
-
-    gcov_data_dir = []
-
-    # Collect gcno, gcda files
-    for i in gcda_dir:
-
-        dir = os.path.join(gcov_dir + "/data", os.path.basename(i))
-        gcov_data_dir.append(dir)
-        os.makedirs(dir)
-
-        copy_file_endswith(".gcno", gcno_dir, dir, gcov_dir)
-        copy_file_endswith(".gcda", i, dir, gcov_dir)
-
-    # Only copy files
-    if args.only_copy:
-        sys.exit(0)
-
     # lcov tool is required
     if shutil.which("lcov") is None:
         print(
@@ -187,57 +244,35 @@ def main():
         )
         sys.exit(1)
 
-    try:
+    gcda_dirs = []
+    for i in args.gcda_dir:
+        if os.path.isfile(i):
+            gcda_dirs.append(parse_gcda_data(os.path.join(root_dir, i)))
+            if args.delete:
+                os.remove(i)
+        else:
+            gcda_dirs.append(os.path.abspath(i))
 
-        # lcov collect coverage data to coverage_file
-        command = [
-            "lcov",
-            "-c",
-            "-o",
-            coverage_file,
-            "--rc",
-            "lcov_branch_coverage=1",
-            "--gcov-tool",
-            args.gcov_tool,
-            "--ignore-errors",
-            "gcov",
-        ]
-        for i in gcov_data_dir:
-            command.append("-d")
-            command.append(i)
-
-        print(command)
-
-        subprocess.run(
-            command,
-            check=True,
-            stdout=sys.stdout,
-            stderr=sys.stdout,
-        )
+    # Merge all gcda files
+    shutil.copytree(gcda_dirs[0], data_dir)
+    for gcda_dir in gcda_dirs[1:]:
+        run_merge(data_dir, gcda_dir, data_dir, merge_tool)
 
-        if args.base_dir:
-            correct_content_path(coverage_file, args.base_dir)
-
-        # genhtml generate coverage report
-        subprocess.run(
-            [
-                "genhtml",
-                "--branch-coverage",
-                "-o",
-                result_dir,
-                coverage_file,
-                "--ignore-errors",
-                "source",
-            ],
-            check=True,
-            stdout=sys.stdout,
-            stderr=sys.stdout,
-        )
+    # Copy gcno files and run lcov generate coverage info file
+    copy_file_endswith(".gcno", gcno_dir, data_dir)
+    coverage_file = run_lcov(data_dir, args.gcov_tool)
+
+    # Only copy files
+    if args.only_copy:
+        sys.exit(0)
+
+    try:
+        run_genhtml(coverage_file, report_dir)
 
         print(
             "Copy the following link and open it in the browser to view the 
coverage report:"
         )
-        print(f"file://{os.path.join(result_dir, 'index.html')}")
+        print(f"file://{os.path.join(report_dir, 'index.html')}")
 
     except subprocess.CalledProcessError:
         print("Failed to generate coverage file.")

Reply via email to