xref: /haiku/src/kits/debug/debug_support.cpp (revision 89755088d790ff4fe36f8aa77dacb2bd15507108)
1 /*
2  * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <new>
7 
8 #include <string.h>
9 
10 #include <AutoDeleter.h>
11 #include <debug_support.h>
12 
13 #include "arch_debug_support.h"
14 #include "SymbolLookup.h"
15 
16 using std::nothrow;
17 
18 struct debug_symbol_lookup_context {
19 	debug_context	context;
20 	SymbolLookup	*lookup;
21 };
22 
23 
24 // init_debug_context
25 status_t
26 init_debug_context(debug_context *context, team_id team, port_id nubPort)
27 {
28 	if (!context || team < 0 || nubPort < 0)
29 		return B_BAD_VALUE;
30 
31 	context->team = team;
32 	context->nub_port = nubPort;
33 
34 	// create the reply port
35 	context->reply_port = create_port(1, "debug reply port");
36 	if (context->reply_port < 0)
37 		return context->reply_port;
38 
39 	return B_OK;
40 }
41 
42 // destroy_debug_context
43 void
44 destroy_debug_context(debug_context *context)
45 {
46 	if (context) {
47 		if (context->reply_port >= 0)
48 			delete_port(context->reply_port);
49 
50 		context->team = -1;
51 		context->nub_port = -1;
52 		context->reply_port = -1;
53 	}
54 }
55 
56 // send_debug_message
57 status_t
58 send_debug_message(debug_context *context, int32 messageCode,
59 	const void *message, int32 messageSize, void *reply, int32 replySize)
60 {
61 	if (!context)
62 		return B_BAD_VALUE;
63 
64 	// send message
65 	while (true) {
66 		status_t result = write_port(context->nub_port, messageCode, message,
67 			messageSize);
68 		if (result == B_OK)
69 			break;
70 		if (result != B_INTERRUPTED)
71 			return result;
72 	}
73 
74 	if (!reply)
75 		return B_OK;
76 
77 	// read reply
78 	while (true) {
79 		int32 code;
80 		ssize_t bytesRead = read_port(context->reply_port, &code, reply,
81 			replySize);
82 		if (bytesRead > 0)
83 			return B_OK;
84 		if (bytesRead != B_INTERRUPTED)
85 			return bytesRead;
86 	}
87 }
88 
89 // debug_read_memory_partial
90 ssize_t
91 debug_read_memory_partial(debug_context *context, const void *address,
92 	void *buffer, size_t size)
93 {
94 	if (!context)
95 		return B_BAD_VALUE;
96 
97 	if (size == 0)
98 		return 0;
99 	if (size > B_MAX_READ_WRITE_MEMORY_SIZE)
100 		size = B_MAX_READ_WRITE_MEMORY_SIZE;
101 
102 	// prepare the message
103 	debug_nub_read_memory message;
104 	message.reply_port = context->reply_port;
105 	message.address = (void*)address;
106 	message.size = size;
107 
108 	// send the message
109 	debug_nub_read_memory_reply reply;
110 	status_t error = send_debug_message(context, B_DEBUG_MESSAGE_READ_MEMORY,
111 		&message, sizeof(message), &reply, sizeof(reply));
112 
113 	if (error != B_OK)
114 		return error;
115 	if (reply.error != B_OK)
116 		return reply.error;
117 
118 	// copy the read data
119 	memcpy(buffer, reply.data, reply.size);
120 	return reply.size;
121 }
122 
123 // debug_read_memory
124 ssize_t
125 debug_read_memory(debug_context *context, const void *_address, void *_buffer,
126 	size_t size)
127 {
128 	const char *address = (const char *)_address;
129 	char *buffer = (char*)_buffer;
130 
131 	// check parameters
132 	if (!context || !address || !buffer)
133 		return B_BAD_VALUE;
134 	if (size == 0)
135 		return 0;
136 
137 	// read as long as we can read data
138 	ssize_t sumRead = 0;
139 	while (size > 0) {
140 		ssize_t bytesRead = debug_read_memory_partial(context, address, buffer,
141 			size);
142 		if (bytesRead < 0) {
143 			if (sumRead > 0)
144 				return sumRead;
145 			return bytesRead;
146 		}
147 
148 		address += bytesRead;
149 		buffer += bytesRead;
150 		sumRead += bytesRead;
151 		size -= bytesRead;
152 	}
153 
154 	return sumRead;
155 }
156 
157 // debug_read_string
158 ssize_t
159 debug_read_string(debug_context *context, const void *_address, char *buffer,
160 	size_t size)
161 {
162 	const char *address = (const char *)_address;
163 
164 	// check parameters
165 	if (!context || !address || !buffer || size == 0)
166 		return B_BAD_VALUE;
167 
168 	// read as long as we can read data
169 	ssize_t sumRead = 0;
170 	while (size > 0) {
171 		ssize_t bytesRead = debug_read_memory_partial(context, address, buffer,
172 			size);
173 		if (bytesRead < 0) {
174 			// always null-terminate what we have (even, if it is an empty
175 			// string) and be done
176 			*buffer = '\0';
177 			return (sumRead > 0 ? sumRead : bytesRead);
178 		}
179 
180 		int chunkSize = strnlen(buffer, bytesRead);
181 		if (chunkSize < bytesRead) {
182 			// we found a terminating null
183 			sumRead += chunkSize;
184 			return sumRead;
185 		}
186 
187 		address += bytesRead;
188 		buffer += bytesRead;
189 		sumRead += bytesRead;
190 		size -= bytesRead;
191 	}
192 
193 	// We filled the complete buffer without encountering a terminating null
194 	// replace the last char. But nevertheless return the full size to indicate
195 	// that the buffer was too small.
196 	buffer[-1] = '\0';
197 
198 	return sumRead;
199 }
200 
201 // debug_get_cpu_state
202 status_t
203 debug_get_cpu_state(debug_context *context, thread_id thread,
204 	debug_debugger_message *messageCode, debug_cpu_state *cpuState)
205 {
206 	if (!context || !cpuState)
207 		return B_BAD_VALUE;
208 
209 	// prepare message
210 	debug_nub_get_cpu_state message;
211 	message.reply_port = context->reply_port;
212 	message.thread = thread;
213 
214 	// send message
215 	debug_nub_get_cpu_state_reply reply;
216 	status_t error = send_debug_message(context, B_DEBUG_MESSAGE_GET_CPU_STATE,
217 		&message, sizeof(message), &reply, sizeof(reply));
218 	if (error == B_OK)
219 		error = reply.error;
220 
221 	// get state
222 	if (error == B_OK) {
223 		*cpuState = reply.cpu_state;
224 		if (messageCode)
225 			*messageCode = reply.message;
226 	}
227 
228 	return error;
229 }
230 
231 
232 // #pragma mark -
233 
234 // debug_get_instruction_pointer
235 status_t
236 debug_get_instruction_pointer(debug_context *context, thread_id thread,
237 	void **ip, void **stackFrameAddress)
238 {
239 	if (!context || !ip || !stackFrameAddress)
240 		return B_BAD_VALUE;
241 
242 	return arch_debug_get_instruction_pointer(context, thread, ip,
243 		stackFrameAddress);
244 }
245 
246 // debug_get_stack_frame
247 status_t
248 debug_get_stack_frame(debug_context *context, void *stackFrameAddress,
249 	debug_stack_frame_info *stackFrameInfo)
250 {
251 	if (!context || !stackFrameAddress || !stackFrameInfo)
252 		return B_BAD_VALUE;
253 
254 	return arch_debug_get_stack_frame(context, stackFrameAddress,
255 		stackFrameInfo);
256 }
257 
258 
259 // #pragma mark -
260 
261 // debug_create_symbol_lookup_context
262 status_t
263 debug_create_symbol_lookup_context(debug_context *debugContext,
264 	debug_symbol_lookup_context **_lookupContext)
265 {
266 	if (!debugContext || !_lookupContext)
267 		return B_BAD_VALUE;
268 
269 	// create the lookup context
270 	debug_symbol_lookup_context *lookupContext
271 		= new(nothrow) debug_symbol_lookup_context;
272 	lookupContext->context = *debugContext;
273 	ObjectDeleter<debug_symbol_lookup_context> contextDeleter(lookupContext);
274 
275 	// create and init symbol lookup
276 	SymbolLookup *lookup = new(nothrow) SymbolLookup(debugContext->team);
277 	if (!lookup)
278 		return B_NO_MEMORY;
279 
280 	status_t error = lookup->Init();
281 	if (error != B_OK) {
282 		delete lookup;
283 		return error;
284 	}
285 
286 	// everything went fine: return the result
287 	lookupContext->lookup = lookup;
288 	*_lookupContext = lookupContext;
289 	contextDeleter.Detach();
290 
291 	return B_OK;
292 }
293 
294 // debug_delete_symbol_lookup_context
295 void
296 debug_delete_symbol_lookup_context(debug_symbol_lookup_context *lookupContext)
297 {
298 	if (lookupContext) {
299 		delete lookupContext->lookup;
300 		delete lookupContext;
301 	}
302 }
303 
304 // debug_lookup_symbol_address
305 status_t
306 debug_lookup_symbol_address(debug_symbol_lookup_context *lookupContext,
307 	const void *address, void **baseAddress, char *symbolName,
308 	int32 symbolNameSize, char *imageName, int32 imageNameSize,
309 	bool *exactMatch)
310 {
311 	if (!lookupContext || !lookupContext->lookup)
312 		return B_BAD_VALUE;
313 	SymbolLookup *lookup = lookupContext->lookup;
314 
315 	// find the symbol
316 	addr_t _baseAddress;
317 	const char *_symbolName;
318 	const char *_imageName;
319 	try {
320 		status_t error = lookup->LookupSymbolAddress((addr_t)address, &_baseAddress,
321 			&_symbolName, &_imageName, exactMatch);
322 		if (error != B_OK)
323 			return error;
324 	} catch (BPrivate::Exception exception) {
325 		return exception.Error();
326 	}
327 
328 	// translate/copy the results
329 	if (baseAddress)
330 		*baseAddress = (void*)_baseAddress;
331 
332 	if (symbolName && symbolNameSize > 0) {
333 		// _symbolName is a remote address: We read the string from the
334 		// remote memory. The reason for not using the cloned area is that
335 		// we don't trust that the data therein is valid (i.e. null-terminated)
336 		// and thus strlcpy() could segfault when hitting the cloned area end.
337 		if (_symbolName) {
338 			ssize_t sizeRead = debug_read_string(&lookupContext->context,
339 				_symbolName, symbolName, symbolNameSize);
340 			if (sizeRead < 0)
341 				return sizeRead;
342 		} else
343 			symbolName[0] = '\0';
344 	}
345 
346 	if (imageName) {
347 		if (imageNameSize > B_PATH_NAME_LENGTH)
348 			imageNameSize = B_PATH_NAME_LENGTH;
349 		strlcpy(imageName, _imageName, imageNameSize);
350 	}
351 
352 	return B_OK;
353 }
354