/ src / lib / gcov-glue.c
gcov-glue.c
  1  /* SPDX-License-Identifier: GPL-2.0-only */
  2  
  3  #include <stdint.h>
  4  #include <bootstate.h>
  5  #include <cbmem.h>
  6  
  7  typedef struct file {
  8  	uint32_t magic;
  9  	struct file *next;
 10  	char *filename;
 11  	char *data;
 12  	int offset;
 13  	int len;
 14  } FILE;
 15  
 16  #define SEEK_SET       0       /* Seek from beginning of file.  */
 17  
 18  #define DIR_SEPARATOR '/'
 19  #define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
 20  #define HAS_DRIVE_SPEC(f) (0)
 21  
 22  #define COVERAGE_SIZE (32*1024)
 23  
 24  #define COVERAGE_MAGIC 0x584d4153
 25  
 26  static FILE *current_file = NULL;
 27  static FILE *previous_file = NULL;
 28  
 29  static FILE *fopen(const char *path, const char *mode)
 30  {
 31  #if CONFIG(DEBUG_COVERAGE)
 32  	printk(BIOS_DEBUG, "%s %s with mode %s\n", __func__, path, mode);
 33  #endif
 34  	if (!current_file) {
 35  		current_file = cbmem_add(CBMEM_ID_COVERAGE, 32*1024);
 36  	} else {
 37  		previous_file = current_file;
 38  		current_file =
 39  			(FILE *)(ALIGN_UP(((unsigned long)previous_file->data
 40  			+ previous_file->len), 16));
 41  	}
 42  
 43  	// TODO check if we're at the end of the CBMEM region (ENOMEM)
 44  	if (current_file) {
 45  		current_file->magic = COVERAGE_MAGIC;
 46  		current_file->next = NULL;
 47  		if (previous_file)
 48  			previous_file->next = current_file;
 49  		current_file->filename = (char *)&current_file[1];
 50  		strcpy(current_file->filename, path);
 51  		current_file->data =
 52  			(char *)ALIGN_UP(((unsigned long)current_file->filename
 53  			+ strlen(path) + 1), 16);
 54  		current_file->offset = 0;
 55  		current_file->len = 0;
 56  	}
 57  
 58  	return current_file;
 59  }
 60  
 61  static int fclose(FILE *stream)
 62  {
 63  #if CONFIG(DEBUG_COVERAGE)
 64  	printk(BIOS_DEBUG, "%s %s\n", __func__, stream->filename);
 65  #endif
 66  	return 0;
 67  }
 68  
 69  static int fseek(FILE *stream, long offset, int whence)
 70  {
 71  	/* fseek should only be called with offset==0 and whence==SEEK_SET
 72  	 * to a freshly opened file. */
 73  	gcc_assert(offset == 0 && whence == SEEK_SET);
 74  #if CONFIG(DEBUG_COVERAGE)
 75  	printk(BIOS_DEBUG, "%s %s offset=%ld whence=%d\n",
 76  		__func__, stream->filename, offset, whence);
 77  #endif
 78  	return 0;
 79  }
 80  
 81  static long ftell(FILE *stream)
 82  {
 83  	/* ftell should currently not be called */
 84  	BUG();
 85  #if CONFIG(DEBUG_COVERAGE)
 86  	printk(BIOS_DEBUG, "%s %s\n", __func__, stream->filename);
 87  #endif
 88  	return 0;
 89  }
 90  
 91  static size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
 92  {
 93  #if CONFIG(DEBUG_COVERAGE)
 94  	printk(BIOS_DEBUG, "%s: ptr=%p size=%zd nmemb=%zd FILE*=%p\n",
 95  		__func__, ptr, size, nmemb, stream);
 96  #endif
 97  	return 0;
 98  }
 99  
100  static size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
101  {
102  #if CONFIG(DEBUG_COVERAGE)
103  	printk(BIOS_DEBUG, "%s: %zd * %zd bytes to file %s\n",
104  		__func__, nmemb, size, stream->filename);
105  #endif
106  	// TODO check if file is last opened file and fail otherwise.
107  
108  	memcpy(stream->data + stream->offset, ptr, size * nmemb);
109  	stream->len += (nmemb * size) - (stream->len - stream->offset);
110  	stream->offset += nmemb * size;
111  	return nmemb;
112  }
113  
114  static void setbuf(FILE *stream, char *buf)
115  {
116  	gcc_assert(buf == 0);
117  }
118  
119  static void coverage_init(void *unused)
120  {
121  	extern long __CTOR_LIST__;
122  	typedef void (*func_ptr)(void);
123  	func_ptr *ctor = (func_ptr *) &__CTOR_LIST__;
124  	if (ctor == NULL)
125  		return;
126  
127  	for (; *ctor != (func_ptr) 0; ctor++)
128  		(*ctor)();
129  }
130  
131  void __gcov_flush(void);
132  static void coverage_exit(void *unused)
133  {
134  #if CONFIG(DEBUG_COVERAGE)
135  	printk(BIOS_DEBUG, "Syncing coverage data.\n");
136  #endif
137  	__gcov_flush();
138  }
139  
140  BOOT_STATE_INIT_ENTRY(BS_PRE_DEVICE, BS_ON_ENTRY, coverage_init, NULL);
141  BOOT_STATE_INIT_ENTRY(BS_OS_RESUME, BS_ON_ENTRY, coverage_exit, NULL);
142  BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_EXIT, coverage_exit, NULL);