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

« back to all changes in this revision

Viewing changes to kc_ptrace.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
 
 * Taken from bcov:Debugger.cpp:
7
 
 *
8
 
 * Copyright (C) 2007 Thomas Neumann
9
 
 */
10
 
#include <sys/ptrace.h>
11
 
#include <sys/user.h>
12
 
#include <sys/wait.h>
13
 
#include <sys/errno.h>
14
 
 
15
 
#include <kc.h>
16
 
#include <utils.h>
17
 
 
18
 
static pid_t active_child, child;
19
 
 
20
 
static unsigned char peek_byte(const void *ptr)
21
 
{
22
 
        unsigned long addr = (unsigned long)ptr;
23
 
        unsigned long aligned = (addr / sizeof(long)) * sizeof(long);
24
 
        union { long val; unsigned char data[sizeof(long)]; } data;
25
 
 
26
 
        data.val = ptrace(PTRACE_PEEKTEXT, active_child, aligned, 0);
27
 
 
28
 
        return data.data[addr - aligned];
29
 
}
30
 
 
31
 
static void poke_byte(unsigned long addr, unsigned char c)
32
 
{
33
 
        unsigned long aligned = (addr / sizeof(long)) * sizeof(long);
34
 
        union { long val; unsigned char data[sizeof(long)]; } data;
35
 
 
36
 
        data.val = ptrace(PTRACE_PEEKTEXT, active_child, aligned, 0);
37
 
        data.data[addr - aligned] = c;
38
 
 
39
 
        ptrace(PTRACE_POKETEXT, active_child, aligned, data.val);
40
 
}
41
 
 
42
 
static void *ptrace_get_ip(void)
43
 
{
44
 
   struct user_regs_struct regs;
45
 
 
46
 
   memset(&regs, 0, sizeof(regs));
47
 
   ptrace(PTRACE_GETREGS, active_child, 0, &regs);
48
 
 
49
 
#if defined(__x86_64__)
50
 
   return (void*)(regs.rip);
51
 
#elif defined(__i386__)
52
 
   return (void*)(regs.eip);
53
 
#else
54
 
   #error specify how to read the IP
55
 
#endif
56
 
}
57
 
 
58
 
void* ptrace_get_ip_before_trap(void)
59
 
{
60
 
#if defined(__x86_64__) || defined(__i386__)
61
 
   return (char*)(ptrace_get_ip()) - 1;
62
 
#else
63
 
   #error specify how to adjust the IP after a breakpoint
64
 
#endif
65
 
}
66
 
 
67
 
static void ptrace_setup_breakpoints(struct kc *kc)
68
 
{
69
 
        GHashTableIter iter;
70
 
        unsigned long key;
71
 
        struct kc_addr *addr;
72
 
 
73
 
        g_hash_table_iter_init(&iter, kc->addrs);
74
 
        while (g_hash_table_iter_next(&iter,
75
 
                        (gpointer*)&key, (gpointer*)&addr)) {
76
 
                uint8_t old_byte = peek_byte((void *)addr->addr);
77
 
 
78
 
                addr->saved_code = old_byte;
79
 
#if defined(__x86_64__)||defined(__i386__)
80
 
      poke_byte(addr->addr, 0xCC);
81
 
#else
82
 
   #error specify how to set a breakpoint
83
 
#endif
84
 
        }
85
 
}
86
 
 
87
 
void ptrace_eliminate_breakpoint(struct kc_addr *addr)
88
 
{
89
 
   struct user_regs_struct regs;
90
 
   memset(&regs, 0, sizeof(regs));
91
 
   unsigned long ptr;
92
 
 
93
 
   ptrace(PTRACE_GETREGS, active_child, 0, &regs);
94
 
#if defined(__x86_64__)
95
 
   ptr = (unsigned long)(--regs.rip);
96
 
#elif defined(__i386__)
97
 
   ptr = (unsigned long)(--regs.eip);
98
 
#else
99
 
   #error specify how to adjust the IP after a breakpoint
100
 
#endif
101
 
   ptrace(PTRACE_SETREGS, active_child, 0, &regs);
102
 
 
103
 
   poke_byte(ptr, addr->saved_code);
104
 
   kc_addr_register_hit(addr);
105
 
}
106
 
 
107
 
enum {
108
 
        PT_CODE_ERROR,
109
 
        PT_CODE_TRAP,
110
 
        PT_CODE_EXIT,
111
 
};
112
 
 
113
 
static int do_ptrace_run(struct kc *kc)
114
 
{
115
 
        // Continue the stopped child
116
 
        ptrace(PTRACE_CONT, active_child, 0, 0);
117
 
 
118
 
        while (1) {
119
 
                // Wait for a child
120
 
                int status;
121
 
 
122
 
                pid_t r = waitpid(-1, &status, __WALL);
123
 
 
124
 
                // Got no one? Child probably died
125
 
                if (r == -1)
126
 
                        return PT_CODE_EXIT;
127
 
                active_child = r;
128
 
 
129
 
                // A signal?
130
 
                if (WIFSTOPPED(status)) {
131
 
                        // A trap?
132
 
                        if (WSTOPSIG(status) == SIGTRAP)
133
 
                                return PT_CODE_TRAP;
134
 
                        // No, deliver it directly
135
 
                        ptrace(PTRACE_CONT, active_child, 0, WSTOPSIG(status));
136
 
                        continue;
137
 
                }
138
 
                // Thread died?
139
 
                if (WIFSIGNALED(status) || WIFEXITED(status)) {
140
 
                        if (active_child == child)
141
 
                                return PT_CODE_EXIT;
142
 
                        continue;
143
 
                }
144
 
                // Stopped?
145
 
                if (WIFSTOPPED(status)) {
146
 
                        // A new clone? Ignore the stop event
147
 
                        if ((status >> 8) == PTRACE_EVENT_CLONE) {
148
 
                                ptrace(PTRACE_CONT, active_child, 0, 0);
149
 
                                continue;
150
 
                        }
151
 
                        // Hm, why did we stop? Ignore the event and continue
152
 
                        ptrace(PTRACE_CONT, active_child, 0, 0);
153
 
                        continue;
154
 
                }
155
 
 
156
 
                // Unknown event
157
 
                return PT_CODE_ERROR;
158
 
        }
159
 
}
160
 
 
161
 
static void ptrace_run_debugger(struct kc *kc)
162
 
{
163
 
        while (1) {
164
 
                int err = do_ptrace_run(kc);
165
 
 
166
 
                switch (err) {
167
 
                case PT_CODE_ERROR:
168
 
                        fprintf(stderr, "Error while tracing\n");
169
 
                case PT_CODE_EXIT:
170
 
                        return;
171
 
                case PT_CODE_TRAP:
172
 
                {
173
 
                        void *where = ptrace_get_ip_before_trap();
174
 
                        struct kc_addr *addr = kc_lookup_addr(kc, (unsigned long)where);
175
 
 
176
 
                        if (addr)
177
 
                                ptrace_eliminate_breakpoint(addr);
178
 
 
179
 
                } break;
180
 
      }
181
 
   }
182
 
}
183
 
 
184
 
static pid_t fork_child(const char *executable, char *const argv[])
185
 
{
186
 
        /* Basic check first */
187
 
        if (access(executable, X_OK) != 0)
188
 
                return -1;
189
 
 
190
 
        /* Executable exists, try to launch it */
191
 
        if ((child = fork()) == 0) {
192
 
 
193
 
                /* And launch the process */
194
 
                ptrace(PTRACE_TRACEME, 0, 0, 0);
195
 
                execv(executable, argv);
196
 
 
197
 
                /* Exec failed */
198
 
                return -1;
199
 
        }
200
 
 
201
 
        /* Fork error? */
202
 
        if (child < 0)
203
 
                return -1;
204
 
 
205
 
        /* Wait for the initial stop */
206
 
        int status;
207
 
        if ((waitpid(child, &status, 0) == -1) || (!WIFSTOPPED(status)))
208
 
                return -1;
209
 
        ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK);
210
 
 
211
 
        return child;
212
 
}
213
 
 
214
 
 
215
 
 
216
 
int ptrace_run(struct kc *kc, char *const argv[])
217
 
{
218
 
        active_child = fork_child(argv[0], &argv[0]);
219
 
 
220
 
        if (active_child < 0) {
221
 
                fprintf(stderr, "Can't fork child!\n");
222
 
                return -1;
223
 
        }
224
 
 
225
 
        ptrace_setup_breakpoints(kc);
226
 
 
227
 
        ptrace_run_debugger(kc);
228
 
 
229
 
        return 0;
230
 
}
231
 
 
232
 
int ptrace_pid_run(struct kc *kc, pid_t pid)
233
 
{
234
 
        active_child = child = pid;
235
 
 
236
 
        errno = 0;
237
 
        ptrace(PTRACE_ATTACH, active_child, 0, 0);
238
 
        if (errno) {
239
 
                const char *err = strerror(errno);
240
 
 
241
 
                fprintf(stderr, "Can't attach to %d. Error %s\n", pid, err);
242
 
                return -1;
243
 
        }
244
 
        ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK);
245
 
 
246
 
        ptrace_setup_breakpoints(kc);
247
 
 
248
 
        ptrace_run_debugger(kc);
249
 
 
250
 
        return 0;
251
 
}
252
 
 
253
 
int ptrace_detach(struct kc *kc)
254
 
{
255
 
        GHashTableIter iter;
256
 
        struct kc_addr *val;
257
 
        unsigned long key;
258
 
 
259
 
        /* Eliminate all unhit breakpoints */
260
 
        g_hash_table_iter_init(&iter, kc->addrs);
261
 
        while (g_hash_table_iter_next(&iter, (gpointer*)&key, (gpointer*)&val)) {
262
 
                if (!val->hits)
263
 
                        ptrace_eliminate_breakpoint(val);
264
 
        }
265
 
 
266
 
        ptrace(PTRACE_DETACH, active_child, 0, 0);
267
 
 
268
 
        return 0;
269
 
}