-
Notifications
You must be signed in to change notification settings - Fork 1
/
watchpoint.c
196 lines (166 loc) · 4.86 KB
/
watchpoint.c
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
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#include <asm/unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/hw_breakpoint.h>
#include <linux/perf_event.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <ucontext.h>
#include <unistd.h>
#define NUM_VALUES 4
struct recordValues {
char names[NUM_VALUES];
unsigned long addresses[NUM_VALUES];
unsigned long values[NUM_VALUES];
};
struct recordValues wp;
long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) {
return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
}
pid_t gettid() {
return syscall(__NR_gettid);
}
// The iterator variable
volatile int i = 0;
volatile int j = 0;
volatile int k = 0;
volatile int m = 0;
volatile int n = 0;
int last_i = -1;
void initialize() {
wp.addresses[0]= (unsigned long)&i;
wp.values[0]= i;
wp.names[0]= 'i';
wp.addresses[1]= (unsigned long)&j;
wp.values[1]= j;
wp.names[1]= 'j';
wp.addresses[2]= (unsigned long)&k;
wp.values[2]= k;
wp.names[2]= 'k';
wp.addresses[3]= (unsigned long)&m;
wp.values[3]= m;
wp.names[3]= 'm';
//wp.addresses[4]= (unsigned long)&n;
//wp.values[4]= n;
//wp.names[4]= 'n';
}
void first_handler(int signum, siginfo_t* info, void* p) {
#if 0
if(i % 3 == 0) {
print_header();
fprintf(stderr, "first ");
}
#endif
// Check which value has been changed.
int in;
for(in = 0; in < NUM_VALUES; in++) {
if(wp.values[in] != *((unsigned long *)wp.addresses[in])) {
wp.values[in] = *((unsigned long *)wp.addresses[in]);
fprintf(stderr, " Now watchpoint %d: change %c value %d\n", in, wp.names[in], wp.values[in]);
}
}
last_i = i;
}
int create_watchpoint(uintptr_t address, int sig, int group) {
// Perf event settings
struct perf_event_attr pe = {
.type = PERF_TYPE_BREAKPOINT,
.size = sizeof(struct perf_event_attr),
.bp_type = HW_BREAKPOINT_W,
.bp_len = HW_BREAKPOINT_LEN_4,
.bp_addr = (uintptr_t)address,
.disabled = 1,
.sample_period = 1,
};
/*
int perf_event_open(struct perf_event_attr *attr,
pid_t pid, int cpu, int group_fd,
unsigned long flags);
*/
// Create the perf_event for this thread on all CPUs with no event group
int perf_fd = perf_event_open(&pe, 0, -1, group, 0);
if(perf_fd == -1) {
fprintf(stderr, "Failed to open perf event file: %s\n", strerror(errno));
abort();
}
// Set the perf_event file to async mode
if(fcntl(perf_fd, F_SETFL, fcntl(perf_fd, F_GETFL, 0) | O_ASYNC) == -1) {
fprintf(stderr, "Failed to set perf event file to ASYNC mode: %s\n", strerror(errno));
abort();
}
// Tell the file to send a SIGUSR1 when an event occurs
if(fcntl(perf_fd, F_SETSIG, sig) == -1) {
fprintf(stderr, "Failed to set perf event file's async signal: %s\n", strerror(errno));
abort();
}
// Deliver the signal to this thread
if(fcntl(perf_fd, F_SETOWN, gettid()) == -1) {
fprintf(stderr, "Failed to set the owner of the perf event file: %s\n", strerror(errno));
abort();
}
return perf_fd;
}
// Enable a setof watch points now
void enable_watchpoints(int fd) {
// Start the event
if(ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) == -1) {
fprintf(stderr, "Failed to enable perf event: %s\n", strerror(errno));
abort();
}
}
void disable_watchpoint(int fd) {
// Start the event
if(ioctl(fd, PERF_EVENT_IOC_DISABLE, 0) == -1) {
fprintf(stderr, "Failed to disable perf event: %s\n", strerror(errno));
abort();
}
}
size_t get_watchpoint_count(int fd) {
uint64_t count;
read(fd, &count, sizeof(uint64_t));
return count;
}
int main(int argc, char** argv) {
initialize();
// Create watchpoints
int first_watchpoint = create_watchpoint((uintptr_t)&i, SIGTRAP, -1);
create_watchpoint((uintptr_t)&j, SIGTRAP, first_watchpoint);
create_watchpoint((uintptr_t)&k, SIGTRAP, first_watchpoint);
create_watchpoint((uintptr_t)&m, SIGTRAP, first_watchpoint);
//create_watchpoint((uintptr_t)&n, SIGTRAP, first_watchpoint);
//int buzz_watchpoint = create_watchpoint((uintptr_t)&i, SIGUSR2);
// Set a signal handler for SIGUSR1
struct sigaction sa1 = {
.sa_sigaction = first_handler,
.sa_flags = SA_SIGINFO
};
if(sigaction(SIGTRAP, &sa1, NULL) == -1) {
fprintf(stderr, "Failed to set SIGTRAP handler: %s\n", strerror(errno));
abort();
}
// Start watchpoints
enable_watchpoints(first_watchpoint);
// Shortest firstbuzz implementation ever:
for(i=0; i<15; i++)
{
j++;
k++;
m++;
n++;
}
// Disable watchpoints
disable_watchpoint(first_watchpoint);
// Add a final newline
fprintf(stderr, "\n");
// Read out the count of events
fprintf(stderr, "Watchpoints tripped %lu times\n", get_watchpoint_count(first_watchpoint));
return 0;
}