summaryrefslogtreecommitdiff
path: root/minix/commands/gcov-pull/gcov-pull.c
blob: 11b6263901145fe0c4e73483d9a896efcc45923f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
 * gcov-pull - Request gcov data from server and write it to gcda files
 * Author: Anton Kuijsten
*/

#include <fcntl.h>
#include <stdio.h>
#include <lib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <minix/gcov.h>

#define BUFF_SZ (4 * 1024 * 1024)	/* 4MB */

int read_int(void);

char *buff_p;

/* helper function to read int from the buffer */
int read_int(void)
{
	int res;
	memcpy(&res, buff_p, sizeof(int));
	buff_p += sizeof(int);
	return res;
}

int main(int argc, char *argv[])
{
  FILE *fd = NULL;
  int server_nr, command, size, result;
  static char buff[BUFF_SZ]; /* Buffer for all the metadata and file data */

  if (argc != 2) {
	fprintf(stderr, "Usage: %s <label>\n", argv[0]);
	return 1;
  }

  /*
    When making a GCOV call to a server, the gcov library linked into
    the server will try to write gcov data to disk. This  writing is
    normally done with calls to the vfs,  using stdio library calls.
    This is not correct behaviour for servers, especially vfs itself.
    Therefore, the server catches those attempts.  The messages used for
    this communication are stored in a buffer. When the gcov operation
    is  done, the buffer is copied from the server to this user space,
    from where the calls are finally made to the vfs. GCOV calls to the
    various servers are all routed trough vfs. For more information, see
    the <minix/gcov.h> header file.
  */
  
  /* Fault in the complete buffer, so vm won't have to
     manage the pages while flushing
   */ 
  memset(buff, '\0', sizeof(buff));

  buff_p = buff;

  result = gcov_flush_svr(argv[1], buff_p, BUFF_SZ);

  if(result >= BUFF_SZ) {
    fprintf(stderr, "Too much data to hold in buffer: %d\n", result);
    fprintf(stderr, "Maximum: %d\n", BUFF_SZ);
    return 1;
  }

  if(result < 0) {
    fprintf(stderr, "Call failed\n");
    return 1;
  }

  /* At least GCOVOP_END opcode expected. */
  if(result < sizeof(int)) {
    fprintf(stderr, "Invalid gcov data from pid %d\n", server_nr);
    return 1;
  }

  /* Only GCOVOP_END is valid but empty. */
  if(result == sizeof(int)) {
    fprintf(stderr, "no gcov data.\n");
    return 0;
  }

  /* Iterate through the system calls contained in the buffer,
   * and execute them
   */
  while((command=read_int()) != GCOVOP_END) {
  	char *fn;
	switch(command) {
		case GCOVOP_OPEN:
			size = read_int();
			fn = buff_p;
			if(strchr(fn, '/')) {
				fn = strrchr(fn, '/');
				assert(fn);
				fn++;
			}
			assert(fn);
			if(!(fd = fopen(fn, "w+"))) {
				perror(buff_p);
				exit(1);
			}
			buff_p += size;
			break;
		case GCOVOP_CLOSE:
			if(!fd) {
				fprintf(stderr, "bogus close\n");
				exit(1);
			}
			fclose(fd);
			fd = NULL;
			break;
		case GCOVOP_WRITE:
			size = read_int();
			fwrite(buff_p, size, 1, fd);
			buff_p += size;
			break;
		default:
			fprintf(stderr, "bogus command %d in buffer.\n",
				command);
			exit(1);
	}
  }

  return 0;
}