1 /*
2  *  This code provides functions to handle gcc's profiling data format
3  *  introduced with gcc 4.7.
4  *
5  *  This file is based heavily on gcc_3_4.c file.
6  *
7  *  For a better understanding, refer to gcc source:
8  *  gcc/gcov-io.h
9  *  libgcc/libgcov.c
10  *
11  *  Uses gcc-internal data definitions.
12  *
13  *  Imported from Linux and modified for Xen by
14  *    Wei Liu <wei.liu2@citrix.com>
15  */
16 
17 #include <xen/string.h>
18 
19 #include "gcov.h"
20 
21 /*
22  * GCOV_COUNTERS will be defined if this file is included by other
23  * source files.
24  */
25 #ifndef GCOV_COUNTERS
26 # if !(GCC_VERSION >= 40700 && GCC_VERSION < 40900)
27 #  error "Wrong version of GCC used to compile gcov"
28 # endif
29 #define GCOV_COUNTERS 8
30 #endif
31 
32 #define GCOV_TAG_FUNCTION_LENGTH        3
33 
34 static struct gcov_info *gcov_info_head;
35 
36 /**
37  * struct gcov_ctr_info - information about counters for a single function
38  * @num: number of counter values for this type
39  * @values: array of counter values for this type
40  *
41  * This data is generated by gcc during compilation and doesn't change
42  * at run-time with the exception of the values array.
43  */
44 struct gcov_ctr_info {
45     unsigned int num;
46     gcov_type *values;
47 };
48 
49 /**
50  * struct gcov_fn_info - profiling meta data per function
51  * @key: comdat key
52  * @ident: unique ident of function
53  * @lineno_checksum: function lineo_checksum
54  * @cfg_checksum: function cfg checksum
55  * @ctrs: instrumented counters
56  *
57  * This data is generated by gcc during compilation and doesn't change
58  * at run-time.
59  *
60  * Information about a single function.  This uses the trailing array
61  * idiom. The number of counters is determined from the merge pointer
62  * array in gcov_info.  The key is used to detect which of a set of
63  * comdat functions was selected -- it points to the gcov_info object
64  * of the object file containing the selected comdat function.
65  */
66 struct gcov_fn_info {
67     const struct gcov_info *key;
68     unsigned int ident;
69     unsigned int lineno_checksum;
70     unsigned int cfg_checksum;
71     struct gcov_ctr_info ctrs[0];
72 };
73 
74 /**
75  * struct gcov_info - profiling data per object file
76  * @version: gcov version magic indicating the gcc version used for compilation
77  * @next: list head for a singly-linked list
78  * @stamp: uniquifying time stamp
79  * @filename: name of the associated gcov data file
80  * @merge: merge functions (null for unused counter type)
81  * @n_functions: number of instrumented functions
82  * @functions: pointer to pointers to function information
83  *
84  * This data is generated by gcc during compilation and doesn't change
85  * at run-time with the exception of the next pointer.
86  */
87 struct gcov_info {
88     unsigned int version;
89     struct gcov_info *next;
90     unsigned int stamp;
91     const char *filename;
92     void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
93     unsigned int n_functions;
94     struct gcov_fn_info **functions;
95 };
96 
counter_active(const struct gcov_info * info,unsigned int type)97 static int counter_active(const struct gcov_info *info, unsigned int type)
98 {
99     return info->merge[type] ? 1 : 0;
100 }
101 
gcov_info_link(struct gcov_info * info)102 void gcov_info_link(struct gcov_info *info)
103 {
104     info->next = gcov_info_head;
105     gcov_info_head = info;
106 }
107 
gcov_info_next(const struct gcov_info * info)108 struct gcov_info *gcov_info_next(const struct gcov_info *info)
109 {
110     if ( !info )
111         return gcov_info_head;
112     return info->next;
113 }
114 
gcov_info_reset(struct gcov_info * info)115 void gcov_info_reset(struct gcov_info *info)
116 {
117     struct gcov_ctr_info *ci_ptr;
118     unsigned int fi_idx;
119     unsigned int ct_idx;
120 
121     for ( fi_idx = 0; fi_idx < info->n_functions; fi_idx++ )
122     {
123         ci_ptr = info->functions[fi_idx]->ctrs;
124 
125         for ( ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++ )
126         {
127             if ( !counter_active(info, ct_idx) )
128                 continue;
129 
130             memset(ci_ptr->values, 0, sizeof(gcov_type) * ci_ptr->num);
131             ci_ptr++;
132         }
133     }
134 }
135 
gcov_info_filename(const struct gcov_info * info)136 const char *gcov_info_filename(const struct gcov_info *info)
137 {
138     return info->filename;
139 }
140 
141 
142 /**
143  * gcov_info_to_gcda - convert profiling data set to gcda file format
144  * @buffer: the buffer to store file data or %NULL if no data should be stored
145  * @info: profiling data set to be converted
146  *
147  * Returns the number of bytes that were/would have been stored into the buffer.
148  */
gcov_info_to_gcda(char * buffer,const struct gcov_info * info)149 size_t gcov_info_to_gcda(char *buffer, const struct gcov_info *info)
150 {
151     struct gcov_fn_info *fi_ptr;
152     struct gcov_ctr_info *ci_ptr;
153     unsigned int fi_idx;
154     unsigned int ct_idx;
155     unsigned int cv_idx;
156     size_t pos = 0;
157 
158     /* File header. */
159     pos += gcov_store_uint32(buffer, pos, GCOV_DATA_MAGIC);
160     pos += gcov_store_uint32(buffer, pos, info->version);
161     pos += gcov_store_uint32(buffer, pos, info->stamp);
162 
163     for ( fi_idx = 0; fi_idx < info->n_functions; fi_idx++ )
164     {
165         fi_ptr = info->functions[fi_idx];
166 
167         /* Function record. */
168         pos += gcov_store_uint32(buffer, pos, GCOV_TAG_FUNCTION);
169         pos += gcov_store_uint32(buffer, pos, GCOV_TAG_FUNCTION_LENGTH);
170         pos += gcov_store_uint32(buffer, pos, fi_ptr->ident);
171         pos += gcov_store_uint32(buffer, pos, fi_ptr->lineno_checksum);
172         pos += gcov_store_uint32(buffer, pos, fi_ptr->cfg_checksum);
173 
174         ci_ptr = fi_ptr->ctrs;
175 
176         for ( ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++ )
177         {
178             if (! counter_active(info, ct_idx) )
179                 continue;
180 
181             /* Counter record. */
182             pos += gcov_store_uint32(buffer, pos,
183                                      GCOV_TAG_FOR_COUNTER(ct_idx));
184             pos += gcov_store_uint32(buffer, pos, ci_ptr->num * 2);
185 
186             for ( cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++ )
187                 pos += gcov_store_uint64(buffer, pos, ci_ptr->values[cv_idx]);
188 
189             ci_ptr++;
190         }
191     }
192 
193     return pos;
194 }
195 
196 /*
197  * Local variables:
198  * mode: C
199  * c-file-style: "BSD"
200  * c-basic-offset: 4
201  * tab-width: 4
202  * indent-tabs-mode: nil
203  * End:
204  */
205