aboutsummaryrefslogtreecommitdiffstats
path: root/xen/common/gcov/gcov.c
blob: b5717b9c4aa3a9d3fa3d80398fa8d904ec9aa386 (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
 *  This code maintains a list of active profiling data structures.
 *
 *    Copyright IBM Corp. 2009
 *    Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
 *
 *    Uses gcc-internal data definitions.
 *    Based on the gcov-kernel patch by:
 *       Hubertus Franke <frankeh@us.ibm.com>
 *       Nigel Hinds <nhinds@us.ibm.com>
 *       Rajan Ravindran <rajancr@us.ibm.com>
 *       Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
 *       Paul Larson
 */

#include <xen/config.h>
#include <xen/init.h>
#include <xen/lib.h>
#include <xen/hypercall.h>
#include <xen/gcov.h>
#include <xen/errno.h>
#include <xen/guest_access.h>
#include <public/xen.h>
#include <public/gcov.h>

static struct gcov_info *info_list;

/*
 * __gcov_init is called by gcc-generated constructor code for each object
 * file compiled with -fprofile-arcs.
 *
 * Although this function is called only during initialization is called from
 * a .text section which is still present after initialization so not declare
 * as __init.
 */
void __gcov_init(struct gcov_info *info)
{
    /* add new profiling data structure to list */
    info->next = info_list;
    info_list = info;
}

/*
 * These functions may be referenced by gcc-generated profiling code but serve
 * no function for Xen.
 */
void __gcov_flush(void)
{
    /* Unused. */
}

void __gcov_merge_add(gcov_type *counters, unsigned int n_counters)
{
    /* Unused. */
}

void __gcov_merge_single(gcov_type *counters, unsigned int n_counters)
{
    /* Unused. */
}

void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters)
{
    /* Unused. */
}

static inline int counter_active(const struct gcov_info *info, unsigned int type)
{
    return (1 << type) & info->ctr_mask;
}

typedef struct write_iter_t
{
    XEN_GUEST_HANDLE(uint8) ptr;
    int real;
    uint32_t write_offset;
} write_iter_t;

static int write_raw(struct write_iter_t *iter, const void *data,
                     size_t data_len)
{
    if ( iter->real &&
        copy_to_guest_offset(iter->ptr, iter->write_offset,
                             (const unsigned char *) data, data_len) )
        return -EFAULT;

    iter->write_offset += data_len;
    return 0;
}

#define chk(v) do { ret=(v); if ( ret ) return ret; } while(0)

static inline int write32(write_iter_t *iter, uint32_t val)
{
    return write_raw(iter, &val, sizeof(val));
}

static int write_string(write_iter_t *iter, const char *s)
{
    int ret;
    size_t len = strlen(s);

    chk(write32(iter, len));
    return write_raw(iter, s, len);
}

static inline int next_type(const struct gcov_info *info, int *type)
{
    while ( ++*type < XENCOV_COUNTERS && !counter_active(info, *type) )
        continue;
    return *type;
}

static inline void align_iter(write_iter_t *iter)
{
    iter->write_offset =
        (iter->write_offset + sizeof(uint64_t) - 1) & -sizeof(uint64_t);
}

static int write_gcov(write_iter_t *iter)
{
    struct gcov_info *info;
    int ret;

    /* reset offset */
    iter->write_offset = 0;

    /* dump all files */
    for ( info = info_list ; info; info = info->next )
    {
        const struct gcov_ctr_info *ctr;
        int type;
        size_t size_fn = sizeof(struct gcov_fn_info);

        align_iter(iter);
        chk(write32(iter, XENCOV_TAG_FILE));
        chk(write32(iter, info->version));
        chk(write32(iter, info->stamp));
        chk(write_string(iter, info->filename));

        /* dump counters */
        ctr = info->counts;
        for ( type = -1; next_type(info, &type) < XENCOV_COUNTERS; ++ctr )
        {
            align_iter(iter);
            chk(write32(iter, XENCOV_TAG_COUNTER(type)));
            chk(write32(iter, ctr->num));
            chk(write_raw(iter, ctr->values,
                          ctr->num * sizeof(ctr->values[0])));

            size_fn += sizeof(unsigned);
        }

        /* dump all functions together */
        align_iter(iter);
        chk(write32(iter, XENCOV_TAG_FUNC));
        chk(write32(iter, info->n_functions));
        chk(write_raw(iter, info->functions, info->n_functions * size_fn));
    }

    /* stop tag */
    align_iter(iter);
    chk(write32(iter, XENCOV_TAG_END));
    return 0;
}

static int reset_counters(void)
{
    struct gcov_info *info;

    for ( info = info_list ; info; info = info->next )
    {
        const struct gcov_ctr_info *ctr;
        int type;

        /* reset counters */
        ctr = info->counts;
        for ( type = -1; next_type(info, &type) < XENCOV_COUNTERS; ++ctr )
            memset(ctr->values, 0, ctr->num * sizeof(ctr->values[0]));
    }

    return 0;
}

int sysctl_coverage_op(xen_sysctl_coverage_op_t *op)
{
    int ret = -EINVAL;
    write_iter_t iter;

    switch ( op->cmd )
    {
    case XEN_SYSCTL_COVERAGE_get_total_size:
        iter.real = 0;

        write_gcov(&iter);
        op->u.total_size = iter.write_offset;
        ret = 0;
        break;

    case XEN_SYSCTL_COVERAGE_read_and_reset:
    case XEN_SYSCTL_COVERAGE_read:
        iter.ptr = op->u.raw_info;
        iter.real = 1;

        ret = write_gcov(&iter);
        if ( ret || op->cmd != XEN_SYSCTL_COVERAGE_read_and_reset )
            break;

        /* fall through */
    case XEN_SYSCTL_COVERAGE_reset:
        ret = reset_counters();
        break;
    }
    return ret;
}

/*
 * Local variables:
 * mode: C
 * c-file-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */