breakpoints.c revision 5bfb061c4c7cfac6e1882a586cf36c849d95fcea
1#if HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include "ltrace.h"
6#include "options.h"
7#include "output.h"
8
9#include <stdlib.h>
10#include <assert.h>
11
12#ifdef __powerpc__
13#include <sys/ptrace.h>
14#endif
15
16/*****************************************************************************/
17
18/*
19  Dictionary code done by Morten Eriksen <mortene@sim.no>.
20
21  FIXME: should we merge with dictionary code in demangle.c? 19990704 mortene.
22*/
23
24struct dict_entry {
25	struct process * proc;
26	struct breakpoint brk; /* addr field of struct is the hash key. */
27	struct dict_entry * next;
28};
29
30#define DICTTABLESIZE 997 /* Semi-randomly selected prime number. */
31static struct dict_entry * dict_buckets[DICTTABLESIZE];
32static int dict_initialized = 0;
33
34static void dict_init(void);
35static void dict_clear(void);
36static struct breakpoint * dict_enter(struct process * proc, void * brkaddr);
37struct breakpoint * dict_find_entry(struct process * proc, void * brkaddr);
38static void dict_apply_to_all(void (* func)(struct process *, struct breakpoint *, void * data), void * data);
39
40
41static void
42dict_init(void) {
43	int i;
44	/* FIXME: is this necessary? Check with ANSI C spec. 19990702 mortene. */
45	for (i = 0; i < DICTTABLESIZE; i++) dict_buckets[i] = NULL;
46	dict_initialized = 1;
47}
48
49static void
50dict_clear(void) {
51	int i;
52	struct dict_entry * entry, * nextentry;
53
54	for (i = 0; i < DICTTABLESIZE; i++) {
55		for (entry = dict_buckets[i]; entry != NULL; entry = nextentry) {
56			nextentry = entry->next;
57			free(entry);
58		}
59		dict_buckets[i] = NULL;
60	}
61}
62
63static struct breakpoint *
64dict_enter(struct process * proc, void * brkaddr) {
65	struct dict_entry * entry, * newentry;
66	unsigned int bucketpos = ((unsigned long int)brkaddr) % DICTTABLESIZE;
67
68	newentry = malloc(sizeof(struct dict_entry));
69	if (!newentry) {
70		perror("malloc");
71		return NULL;
72	}
73
74	newentry->proc = proc;
75	newentry->brk.addr = brkaddr;
76	newentry->brk.enabled = 0;
77	newentry->next = NULL;
78
79	entry = dict_buckets[bucketpos];
80	while (entry && entry->next) entry = entry->next;
81
82	if (entry) entry->next = newentry;
83	else dict_buckets[bucketpos] = newentry;
84
85	if (opt_d > 2)
86		output_line(0, "new brk dict entry at %p\n", brkaddr);
87
88	return &(newentry->brk);
89}
90
91struct breakpoint *
92dict_find_entry(struct process * proc, void * brkaddr) {
93	unsigned int bucketpos = ((unsigned long int)brkaddr) % DICTTABLESIZE;
94	struct dict_entry * entry = dict_buckets[bucketpos];
95	while (entry) {
96		if ((entry->brk.addr == brkaddr) && (entry->proc == proc)) break;
97		entry = entry->next;
98	}
99	return entry ? &(entry->brk) : NULL;
100}
101
102static void
103dict_apply_to_all(void (* func)(struct process *, struct breakpoint *, void * data), void * data) {
104	int i;
105
106	for (i = 0; i < DICTTABLESIZE; i++) {
107		struct dict_entry * entry = dict_buckets[i];
108		while (entry) {
109			func(entry->proc, &(entry->brk), data);
110			entry = entry->next;
111		}
112	}
113}
114
115#undef DICTTABLESIZE
116
117/*****************************************************************************/
118
119struct breakpoint *
120address2bpstruct(struct process * proc, void * addr) {
121	return dict_find_entry(proc, addr);
122}
123
124void
125insert_breakpoint(struct process * proc, void * addr) {
126	struct breakpoint * sbp;
127
128	if (!dict_initialized) {
129		dict_init();
130		atexit(dict_clear);
131	}
132
133	sbp = dict_find_entry(proc, addr);
134	if (!sbp) sbp = dict_enter(proc, addr);
135	if (!sbp) return;
136
137	sbp->enabled++;
138	if (sbp->enabled==1 && proc->pid) enable_breakpoint(proc->pid, sbp);
139}
140
141void
142delete_breakpoint(struct process * proc, void * addr) {
143	struct breakpoint * sbp = dict_find_entry(proc, addr);
144	assert(sbp); /* FIXME: remove after debugging has been done. */
145	/* This should only happen on out-of-memory conditions. */
146	if (sbp == NULL) return;
147
148	sbp->enabled--;
149	if (sbp->enabled == 0) disable_breakpoint(proc->pid, sbp);
150	assert(sbp->enabled >= 0);
151}
152
153static void
154enable_bp_cb(struct process * proc, struct breakpoint * sbp, void * data) {
155	struct process * myproc = (struct process *)data;
156	if (myproc == proc && sbp->enabled) enable_breakpoint(proc->pid, sbp);
157}
158
159void
160enable_all_breakpoints(struct process * proc) {
161	if (proc->breakpoints_enabled <= 0) {
162#ifdef __powerpc__
163		unsigned long a;
164
165		/*
166		 * PPC HACK! (XXX FIXME TODO)
167		 * If the dynamic linker hasn't populated the PLT then
168		 * dont enable the breakpoints
169		 */
170		a = ptrace(PTRACE_PEEKTEXT, proc->pid, proc->list_of_symbols->enter_addr, 0);
171		if (a == 0x0)
172			return;
173#endif
174
175		if (opt_d>0) {
176			output_line(0, "Enabling breakpoints for pid %u...", proc->pid);
177		}
178		dict_apply_to_all(enable_bp_cb, proc);
179	}
180	proc->breakpoints_enabled = 1;
181}
182
183static void
184disable_bp_cb(struct process * proc, struct breakpoint * sbp, void * data) {
185	struct process * myproc = (struct process *)data;
186	if (myproc == proc && sbp->enabled) disable_breakpoint(proc->pid, sbp);
187}
188
189void
190disable_all_breakpoints(struct process * proc) {
191	if (proc->breakpoints_enabled) {
192		if (opt_d>0) {
193			output_line(0, "Disabling breakpoints for pid %u...", proc->pid);
194		}
195		dict_apply_to_all(disable_bp_cb, proc);
196	}
197	proc->breakpoints_enabled = 0;
198}
199