~ubuntu-branches/ubuntu/trusty/kcov/trusty

« back to all changes in this revision

Viewing changes to src/kc.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Tautschnig
  • Date: 2010-12-17 10:03:23 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20101217100323-03njos8sfhhuax0y
Tags: 4-1
* New upstream release
  - Fixes FTBFS on non-x86 architectures (closes: #603135).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2010 Simon Kagstrom, Thomas Neumann
 
3
 *
 
4
 * See COPYING for license details
 
5
 */
 
6
#include <sys/types.h>
 
7
#include <sys/time.h>
 
8
#include <sys/types.h>
 
9
#include <sys/stat.h>
 
10
#include <unistd.h>
 
11
#include <sys/fcntl.h>
 
12
#include <stdlib.h>
 
13
#include <stdio.h>
 
14
#include <string.h>
 
15
#include <libgen.h>    /* POSIX basename */
 
16
#include <libelf.h>
 
17
#include <libdwarf.h>
 
18
 
 
19
#include <kc.h>
 
20
#include <utils.h>
 
21
#include <report.h>
 
22
 
 
23
static gint cmp_line(gconstpointer _a, gconstpointer _b)
 
24
{
 
25
        const struct kc_line *a = _a;
 
26
        const struct kc_line *b = _b;
 
27
        int ret;
 
28
 
 
29
        ret = strcmp(a->file->filename, b->file->filename) == 0;
 
30
        if (ret)
 
31
                ret = (a->lineno - b->lineno) == 0;
 
32
 
 
33
        return ret;
 
34
}
 
35
 
 
36
static guint hash_line(gconstpointer _a)
 
37
{
 
38
        const struct kc_line *a = _a;
 
39
 
 
40
        return g_str_hash(a->file->filename) + g_int_hash(&a->lineno);
 
41
}
 
42
 
 
43
 
 
44
void kc_add_addr(struct kc *kc, unsigned long addr_in, int line_nr, const char *filename)
 
45
{
 
46
        struct kc_line *table_line;
 
47
        struct kc_addr *addr;
 
48
        struct kc_line *line;
 
49
        struct kc_file *file;
 
50
 
 
51
        file = g_hash_table_lookup(kc->files, filename);
 
52
        if (!file) {
 
53
                file = kc_file_new(filename);
 
54
                g_hash_table_insert(kc->files, (gpointer*)file->filename, file);
 
55
        }
 
56
 
 
57
        line = kc_line_new(file, line_nr);
 
58
        table_line = g_hash_table_lookup(kc->lines, line);
 
59
 
 
60
        if (table_line) {
 
61
                kc_line_free(line);
 
62
                line = table_line;
 
63
        }
 
64
 
 
65
        addr = kc_addr_new(addr_in);
 
66
        if (!table_line)
 
67
                g_hash_table_insert(kc->lines, line, line);
 
68
        g_hash_table_insert(kc->addrs, (gpointer*)&addr->addr, addr);
 
69
 
 
70
        kc_line_add_addr(line, addr);
 
71
        kc_file_add_line(file, line);
 
72
}
 
73
 
 
74
struct kc_addr *kc_lookup_addr(struct kc *kc, unsigned long addr)
 
75
{
 
76
        return g_hash_table_lookup(kc->addrs, (gpointer *)&addr);
 
77
}
 
78
 
 
79
 
 
80
static void dwarf_error_handler(Dwarf_Error error, Dwarf_Ptr userData)
 
81
{
 
82
        char *msg = dwarf_errmsg(error);
 
83
 
 
84
        printf("Dwarf: %s\n", msg);
 
85
}
 
86
 
 
87
static int lookup_elf_type(struct kc *kc, const char *filename, struct Elf *elf)
 
88
{
 
89
        Elf_Scn *scn = NULL;
 
90
        Elf32_Ehdr *hdr32;
 
91
        Elf64_Ehdr *hdr64;
 
92
        size_t shstrndx;
 
93
        int is_32;
 
94
 
 
95
        kc->type = PTRACE_FILE;
 
96
        if (!elf_getshdrstrndx(elf, &shstrndx) < 0)
 
97
                return -1;
 
98
 
 
99
        hdr32 = elf32_getehdr(elf);
 
100
        hdr64 = elf64_getehdr(elf);
 
101
        if (hdr32 == NULL && hdr64 == NULL) {
 
102
                error("Can't get elf header\n");
 
103
                return -1;
 
104
        }
 
105
        if (hdr32 != NULL && hdr64 != NULL) {
 
106
                error("Both 32- and 64-bit???\n");
 
107
                return -1;
 
108
        }
 
109
 
 
110
        is_32 = hdr32 != NULL;
 
111
        if (is_32)
 
112
                kc->e_machine = (unsigned int)hdr32->e_machine;
 
113
        else
 
114
                kc->e_machine = (unsigned int)hdr64->e_machine;
 
115
 
 
116
        while ( (scn = elf_nextscn(elf, scn)) != NULL ) {
 
117
                const char *name;
 
118
 
 
119
                if (is_32) {
 
120
                        Elf32_Shdr *shdr = elf32_getshdr(scn);
 
121
 
 
122
                        if (!shdr)
 
123
                                continue;
 
124
 
 
125
                        name = elf_strptr(elf, shstrndx, shdr->sh_name);
 
126
                }
 
127
                else {
 
128
                        Elf64_Shdr *shdr = elf64_getshdr(scn);
 
129
 
 
130
                        if (!shdr)
 
131
                                continue;
 
132
 
 
133
                        name = elf_strptr(elf, shstrndx, shdr->sh_name);
 
134
                }
 
135
 
 
136
                /* Linux kernel module? */
 
137
                if (strcmp(name, ".modinfo") == 0) {
 
138
                        char *cpy = xstrdup(filename);
 
139
                        char *bn = xstrdup(basename(cpy));
 
140
                        char *p;
 
141
 
 
142
                        /* Remove .ko and fixup module names */
 
143
                        p = strrchr(bn, '.');
 
144
                        if (p)
 
145
                                *p = '\0';
 
146
                        for (p = bn; *p; p++) {
 
147
                                if (*p == '-')
 
148
                                        *p = '_';
 
149
                        }
 
150
 
 
151
                        free((void *)kc->module_name);
 
152
                        kc->type = KPROBE_COVERAGE;
 
153
                        kc->module_name = bn;
 
154
 
 
155
                        free(cpy);
 
156
                        return 0;
 
157
                }
 
158
                else if (strcmp(name, "__ksymtab") == 0) {
 
159
                        kc->type = KPROBE_COVERAGE;
 
160
                        return 0;
 
161
                }
 
162
        }
 
163
 
 
164
        return 0;
 
165
}
 
166
 
 
167
static const char *lookup_filename_by_pid(pid_t pid)
 
168
{
 
169
        char linkpath[80 + 1];
 
170
        char path[1024];
 
171
        ssize_t err;
 
172
 
 
173
        memset(path, 0, sizeof(path));
 
174
        snprintf(linkpath, sizeof(linkpath) - 1, "/proc/%d/exe", pid);
 
175
        linkpath[sizeof(linkpath) - 1 ] = '\0';
 
176
 
 
177
        err = readlink(linkpath, path, sizeof(path));
 
178
        if (err < 0)
 
179
                return NULL;
 
180
 
 
181
        /* This allocation will be lost, but that's OK - this function
 
182
         * is only called once at startup anyway. */
 
183
        return xstrdup(path);
 
184
}
 
185
 
 
186
struct kc *kc_open_elf(const char *filename, pid_t pid)
 
187
{
 
188
        Dwarf_Unsigned header;
 
189
        struct Elf *elf;
 
190
        Dwarf_Debug dbg;
 
191
        struct kc *kc;
 
192
        int err;
 
193
        int fd;
 
194
 
 
195
        if (pid != 0 && filename == NULL) {
 
196
                filename = lookup_filename_by_pid(pid);
 
197
 
 
198
                if (!filename) {
 
199
                        error("Can't lookup filename of PID %d\n", pid);
 
200
                        return NULL;
 
201
                }
 
202
        }
 
203
 
 
204
        fd = open(filename, O_RDONLY);
 
205
        if (fd < 0)
 
206
                return NULL;
 
207
 
 
208
        /* Initialize libdwarf */
 
209
        err = dwarf_init(fd, DW_DLC_READ, dwarf_error_handler, 0, &dbg,0);
 
210
        if (err == DW_DLV_ERROR) {
 
211
                close(fd);
 
212
                return NULL;
 
213
        }
 
214
 
 
215
        /* Zeroes everything */
 
216
        kc = xmalloc(sizeof(struct kc));
 
217
        kc->module_name = xstrdup("");
 
218
        kc->in_file = filename;
 
219
 
 
220
        kc->addrs = g_hash_table_new(g_int_hash, g_int_equal);
 
221
        kc->lines = g_hash_table_new(hash_line, cmp_line);
 
222
        kc->files = g_hash_table_new(g_str_hash, g_str_equal);
 
223
        kc->sort_type = FILENAME;
 
224
 
 
225
        if (err == DW_DLV_NO_ENTRY)
 
226
                goto out_ok;
 
227
 
 
228
        err = dwarf_get_elf(dbg, &elf, NULL);
 
229
        err = lookup_elf_type(kc, filename, elf);
 
230
        if (err < 0)
 
231
                goto out_err;
 
232
        if (pid != 0)
 
233
                kc->type = PTRACE_PID;
 
234
 
 
235
        /* Iterate over the headers */
 
236
        while (dwarf_next_cu_header(dbg, 0, 0, 0, 0,
 
237
                        &header, 0) == DW_DLV_OK) {
 
238
                Dwarf_Line* line_buffer;
 
239
                Dwarf_Signed line_count;
 
240
                Dwarf_Die die;
 
241
                int i;
 
242
 
 
243
                if (dwarf_siblingof(dbg, 0, &die, 0) != DW_DLV_OK)
 
244
                        goto out_err;
 
245
 
 
246
                /* Get the source lines */
 
247
                if (dwarf_srclines(die, &line_buffer, &line_count, 0) != DW_DLV_OK)
 
248
                        continue;
 
249
 
 
250
                /* Store them */
 
251
                for (i = 0; i < line_count; i++) {
 
252
                        Dwarf_Unsigned line_nr;
 
253
                        char* line_source;
 
254
                        Dwarf_Bool is_code;
 
255
                        Dwarf_Addr addr;
 
256
 
 
257
                        if (dwarf_lineno(line_buffer[i], &line_nr, 0) != DW_DLV_OK)
 
258
                                goto out_err;
 
259
                        if (dwarf_linesrc(line_buffer[i], &line_source, 0) != DW_DLV_OK)
 
260
                                goto out_err;
 
261
                        if (dwarf_linebeginstatement(line_buffer[i], &is_code, 0) != DW_DLV_OK)
 
262
                                goto out_err;
 
263
 
 
264
                        if (dwarf_lineaddr(line_buffer[i], &addr, 0) != DW_DLV_OK)
 
265
                                goto out_err;
 
266
 
 
267
                        if (line_nr && is_code) {
 
268
                                kc_add_addr(kc, addr, line_nr, line_source);
 
269
                        }
 
270
 
 
271
                        dwarf_dealloc(dbg, line_source, DW_DLA_STRING);
 
272
                }
 
273
 
 
274
                /* Release the memory */
 
275
                for (i = 0; i < line_count; i++)
 
276
                        dwarf_dealloc(dbg,line_buffer[i], DW_DLA_LINE);
 
277
                dwarf_dealloc(dbg,line_buffer, DW_DLA_LIST);
 
278
        }
 
279
 
 
280
        /* Shutdown libdwarf */
 
281
        if (dwarf_finish(dbg, 0) != DW_DLV_OK)
 
282
                goto out_err;
 
283
 
 
284
out_ok:
 
285
        close(fd);
 
286
 
 
287
        return kc;
 
288
 
 
289
out_err:
 
290
        close(fd);
 
291
        free(kc);
 
292
 
 
293
        return NULL;
 
294
}
 
295
 
 
296
void kc_close(struct kc *kc)
 
297
{
 
298
        free(kc);
 
299
}
 
300
 
 
301
 
 
302
 
 
303
static uint64_t get_file_checksum(struct kc *kc)
 
304
{
 
305
        struct stat st;
 
306
 
 
307
        if (lstat(kc->in_file, &st) < 0)
 
308
                return 0;
 
309
 
 
310
        return ((uint64_t)st.st_mtim.tv_sec << 32) | ((uint64_t)st.st_mtim.tv_nsec);
 
311
}
 
312
 
 
313
void kc_db_marshal(struct kc_data_db *db)
 
314
{
 
315
}
 
316
 
 
317
void kc_db_unmarshal(struct kc_data_db *db)
 
318
{
 
319
}
 
320
 
 
321
void kc_read_db(struct kc *kc)
 
322
{
 
323
        struct kc_data_db *db;
 
324
        uint64_t checksum;
 
325
        size_t sz;
 
326
        int i;
 
327
 
 
328
        db = read_file(&sz, "%s/%s/kcov.db", kc->out_dir, kc->binary_filename);
 
329
        if (!db)
 
330
                return;
 
331
        kc_db_unmarshal(db);
 
332
        checksum = get_file_checksum(kc);
 
333
        if (db->elf_checksum != checksum)
 
334
                goto out;
 
335
 
 
336
        for (i = 0; i < db->n_addrs; i++) {
 
337
                struct kc_addr *db_addr = &db->addrs[i];
 
338
                struct kc_addr *tgt;
 
339
 
 
340
                kc_addr_unmarshall(db_addr);
 
341
 
 
342
                tgt = kc_lookup_addr(kc, db_addr->addr);
 
343
                if (!tgt)
 
344
                        continue;
 
345
 
 
346
                tgt->hits += db_addr->hits;
 
347
        }
 
348
 
 
349
out:
 
350
        free(db);
 
351
}
 
352
 
 
353
void kc_write_db(struct kc *kc)
 
354
{
 
355
        struct kc_data_db *db;
 
356
        struct kc_addr *val;
 
357
        GHashTableIter iter;
 
358
        unsigned long key;
 
359
        guint sz;
 
360
        int cnt = 0;
 
361
 
 
362
        sz = g_hash_table_size(kc->addrs);
 
363
        db = xmalloc(sizeof(struct kc_data_db) + sizeof(struct kc_addr) * sz);
 
364
        db->n_addrs = sz;
 
365
        db->elf_checksum = get_file_checksum(kc);
 
366
 
 
367
        g_hash_table_iter_init(&iter, kc->addrs);
 
368
        while (g_hash_table_iter_next(&iter, (gpointer*)&key, (gpointer*)&val)) {
 
369
                struct kc_addr *db_addr = &db->addrs[cnt];
 
370
 
 
371
                memcpy(db_addr, val, sizeof(struct kc_addr));
 
372
                kc_addr_marshall(db_addr);
 
373
 
 
374
                cnt++;
 
375
        }
 
376
        kc_db_marshal(db);
 
377
        write_file(db, sizeof(struct kc_data_db) + sz * sizeof(struct kc_addr),
 
378
                        "%s/%s/kcov.db", kc->out_dir, kc->binary_filename);
 
379
 
 
380
        free(db);
 
381
}
 
382
 
 
383
int kc_coverage_db_read(const char *dir, struct kc_coverage_db *dst)
 
384
{
 
385
        struct kc_coverage_db *db;
 
386
        size_t sz;
 
387
 
 
388
        db = read_file(&sz, "%s/kcov_coverage.db", dir);
 
389
        if (!db)
 
390
                return -1;
 
391
        if (sz != sizeof(struct kc_coverage_db)) {
 
392
                warning("Coverage db size wrong: %u vs %u\n",
 
393
                                sz, sizeof(struct kc_coverage_db));
 
394
                free(db);
 
395
                return -1;
 
396
        }
 
397
 
 
398
        *dst = *db;
 
399
        kc_coverage_db_unmarshal(dst);
 
400
        free(db);
 
401
 
 
402
        return 0;
 
403
}
 
404
 
 
405
void kc_coverage_db_write(const char *dir, struct kc_coverage_db *src)
 
406
{
 
407
        struct kc_coverage_db db;
 
408
 
 
409
        db = *src;
 
410
        kc_coverage_db_marshal(&db);
 
411
 
 
412
        write_file(&db, sizeof(struct kc_coverage_db),
 
413
                        "%s/kcov_coverage.db", dir);
 
414
}
 
415
 
 
416
void kc_coverage_db_marshal(struct kc_coverage_db *db)
 
417
{
 
418
}
 
419
 
 
420
void kc_coverage_db_unmarshal(struct kc_coverage_db *db)
 
421
{
 
422
}