1 /*
2  *  This code maintains a list of active profiling data structures.
3  *
4  *    Copyright IBM Corp. 2009
5  *    Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
6  *
7  *    Uses gcc-internal data definitions.
8  *    Based on the gcov-kernel patch by:
9  *       Hubertus Franke <frankeh@us.ibm.com>
10  *       Nigel Hinds <nhinds@us.ibm.com>
11  *       Rajan Ravindran <rajancr@us.ibm.com>
12  *       Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
13  *       Paul Larson
14  *
15  *  Modified for Xen by:
16  *    Wei Liu <wei.liu2@citrix.com>
17  */
18 
19 #include <xen/errno.h>
20 #include <xen/guest_access.h>
21 #include <xen/types.h>
22 
23 #include <public/sysctl.h>
24 
25 #include "gcov.h"
26 
27 /**
28  * gcov_store_uint32 - store 32 bit number in gcov format to buffer
29  * @buffer: target buffer or NULL
30  * @off: offset into the buffer
31  * @v: value to be stored
32  *
33  * Number format defined by gcc: numbers are recorded in the 32 bit
34  * unsigned binary form of the endianness of the machine generating the
35  * file. Returns the number of bytes stored. If @buffer is %NULL, doesn't
36  * store anything.
37  */
gcov_store_uint32(void * buffer,size_t off,uint32_t v)38 size_t gcov_store_uint32(void *buffer, size_t off, uint32_t v)
39 {
40     uint32_t *data;
41 
42     if ( buffer )
43     {
44         data = buffer + off;
45         *data = v;
46     }
47 
48     return sizeof(*data);
49 }
50 
51 /**
52  * gcov_store_uint64 - store 64 bit number in gcov format to buffer
53  * @buffer: target buffer or NULL
54  * @off: offset into the buffer
55  * @v: value to be stored
56  *
57  * Number format defined by gcc: numbers are recorded in the 32 bit
58  * unsigned binary form of the endianness of the machine generating the
59  * file. 64 bit numbers are stored as two 32 bit numbers, the low part
60  * first. Returns the number of bytes stored. If @buffer is %NULL, doesn't store
61  * anything.
62  */
gcov_store_uint64(void * buffer,size_t off,uint64_t v)63 size_t gcov_store_uint64(void *buffer, size_t off, uint64_t v)
64 {
65     uint32_t *data;
66 
67     if ( buffer )
68     {
69         data = buffer + off;
70 
71         data[0] = (v & 0xffffffffUL);
72         data[1] = (v >> 32);
73     }
74 
75     return sizeof(*data) * 2;
76 }
77 
gcov_info_payload_size(const struct gcov_info * info)78 static size_t gcov_info_payload_size(const struct gcov_info *info)
79 {
80     return gcov_info_to_gcda(NULL, info);
81 }
82 
gcov_info_dump_payload(const struct gcov_info * info,XEN_GUEST_HANDLE_PARAM (char)buffer,uint32_t * off)83 static int gcov_info_dump_payload(const struct gcov_info *info,
84                                   XEN_GUEST_HANDLE_PARAM(char) buffer,
85                                   uint32_t *off)
86 {
87     char *buf;
88     uint32_t buf_size;
89     int ret;
90 
91     /*
92      * Allocate a buffer and dump payload there. This helps us to not
93      * have copy_to_guest in other functions and retain their simple
94      * semantics.
95      */
96 
97     buf_size = gcov_info_payload_size(info);
98     buf = xmalloc_array(char, buf_size);
99 
100     if ( !buf )
101     {
102         ret = -ENOMEM;
103         goto out;
104     }
105 
106     gcov_info_to_gcda(buf, info);
107 
108     if ( copy_to_guest_offset(buffer, *off, buf, buf_size) )
109     {
110         ret = -EFAULT;
111         goto out;
112     }
113     *off += buf_size;
114 
115     ret = 0;
116  out:
117     xfree(buf);
118     return ret;
119 
120 }
121 
gcov_get_size(void)122 static uint32_t gcov_get_size(void)
123 {
124     uint32_t total_size = sizeof(uint32_t); /* Magic number XCOV */
125     struct gcov_info *info = NULL;
126 
127     while ( (info = gcov_info_next(info)) )
128     {
129         /* File name length, including trailing \0 */
130         total_size += strlen(gcov_info_filename(info)) + 1;
131 
132         /* Payload size field */
133         total_size += sizeof(uint32_t);
134 
135         /* Payload itself */
136         total_size += gcov_info_payload_size(info);
137     }
138 
139     return total_size;
140 }
141 
gcov_reset_all_counters(void)142 static void gcov_reset_all_counters(void)
143 {
144     struct gcov_info *info = NULL;
145 
146     while ( (info = gcov_info_next(info)) )
147         gcov_info_reset(info);
148 }
149 
gcov_dump_one_record(const struct gcov_info * info,XEN_GUEST_HANDLE_PARAM (char)buffer,uint32_t * off)150 static int gcov_dump_one_record(const struct gcov_info *info,
151                                 XEN_GUEST_HANDLE_PARAM(char) buffer,
152                                 uint32_t *off)
153 {
154     uint32_t payload_size;
155     uint32_t len;
156 
157     /* File name, including trailing \0 */
158     len = strlen(gcov_info_filename(info)) + 1;
159     if ( copy_to_guest_offset(buffer, *off, gcov_info_filename(info), len) )
160         return -EFAULT;
161     *off += len;
162 
163     payload_size = gcov_info_payload_size(info);
164     /* Payload size */
165     if ( copy_to_guest_offset(buffer, *off, (char*)&payload_size,
166                               sizeof(uint32_t)) )
167         return -EFAULT;
168     *off += sizeof(uint32_t);
169 
170     /* Payload itself */
171     return gcov_info_dump_payload(info, buffer, off);
172 }
173 
gcov_dump_all(XEN_GUEST_HANDLE_PARAM (char)buffer,uint32_t * buffer_size)174 static int gcov_dump_all(XEN_GUEST_HANDLE_PARAM(char) buffer,
175                          uint32_t *buffer_size)
176 {
177     uint32_t off;
178     uint32_t magic = XEN_GCOV_FORMAT_MAGIC;
179     struct gcov_info *info = NULL;
180     int ret;
181 
182     if ( *buffer_size < gcov_get_size() )
183     {
184         ret = -ENOBUFS;
185         goto out;
186     }
187 
188     off = 0;
189 
190     /* Magic number */
191     if ( copy_to_guest_offset(buffer, off, (char *)&magic, sizeof(magic)) )
192     {
193         ret = -EFAULT;
194         goto out;
195     }
196     off += sizeof(magic);
197 
198     while ( (info = gcov_info_next(info)) )
199     {
200         ret = gcov_dump_one_record(info, buffer, &off);
201         if ( ret )
202             goto out;
203     }
204 
205     *buffer_size = off;
206 
207     ret = 0;
208  out:
209     return ret;
210 }
211 
sysctl_gcov_op(struct xen_sysctl_gcov_op * op)212 int sysctl_gcov_op(struct xen_sysctl_gcov_op *op)
213 {
214     int ret;
215 
216     switch ( op->cmd )
217     {
218     case XEN_SYSCTL_GCOV_get_size:
219         op->size = gcov_get_size();
220         ret = 0;
221         break;
222 
223     case XEN_SYSCTL_GCOV_read:
224     {
225         XEN_GUEST_HANDLE_PARAM(char) buf;
226         uint32_t size = op->size;
227 
228         buf = guest_handle_cast(op->buffer, char);
229 
230         ret = gcov_dump_all(buf, &size);
231         op->size = size;
232 
233         break;
234     }
235 
236     case XEN_SYSCTL_GCOV_reset:
237         gcov_reset_all_counters();
238         ret = 0;
239         break;
240 
241     default:
242         ret = -EOPNOTSUPP;
243         break;
244     }
245 
246     return ret;
247 }
248 
249 /*
250  * Local variables:
251  * mode: C
252  * c-file-style: "BSD"
253  * c-basic-offset: 4
254  * tab-width: 4
255  * indent-tabs-mode: nil
256  * End:
257  */
258