xref: /haiku/src/system/kernel/debug/BreakpointManager.cpp (revision 9760dcae2038d47442f4658c2575844c6cf92c40)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "BreakpointManager.h"
7 
8 #include <algorithm>
9 
10 #include <AutoDeleter.h>
11 
12 #include <commpage_defs.h>
13 #include <kernel.h>
14 #include <util/AutoLock.h>
15 #include <vm/vm.h>
16 
17 
18 //#define TRACE_BREAKPOINT_MANAGER
19 #ifdef TRACE_BREAKPOINT_MANAGER
20 #	define TRACE(x...) dprintf(x)
21 #else
22 #	define TRACE(x...) do {} while (false)
23 #endif
24 
25 
26 // soft limit for the number of breakpoints
27 const int32 kMaxBreakpointCount = 10240;
28 
29 
30 BreakpointManager::InstalledBreakpoint::InstalledBreakpoint(addr_t address)
31 	:
32 	breakpoint(NULL),
33 	address(address)
34 {
35 }
36 
37 
38 // #pragma mark -
39 
40 
41 BreakpointManager::BreakpointManager()
42 	:
43 	fBreakpointCount(0),
44 	fWatchpointCount(0)
45 {
46 	rw_lock_init(&fLock, "breakpoint manager");
47 }
48 
49 
50 BreakpointManager::~BreakpointManager()
51 {
52 	WriteLocker locker(fLock);
53 
54 	// delete the installed breakpoint objects
55 	BreakpointTree::Iterator it = fBreakpoints.GetIterator();
56 	while (InstalledBreakpoint* installedBreakpoint = it.Next()) {
57 		it.Remove();
58 
59 		// delete underlying software breakpoint
60 		if (installedBreakpoint->breakpoint->software)
61 			delete installedBreakpoint->breakpoint;
62 
63 		delete installedBreakpoint;
64 	}
65 
66 	// delete the watchpoints
67 	while (InstalledWatchpoint* watchpoint = fWatchpoints.RemoveHead())
68 		delete watchpoint;
69 
70 	// delete the hardware breakpoint objects
71 	while (Breakpoint* breakpoint = fHardwareBreakpoints.RemoveHead())
72 		delete breakpoint;
73 
74 	rw_lock_destroy(&fLock);
75 }
76 
77 
78 status_t
79 BreakpointManager::Init()
80 {
81 	// create objects for the hardware breakpoints
82 	for (int32 i = 0; i < DEBUG_MAX_BREAKPOINTS; i++) {
83 		Breakpoint* breakpoint = new(std::nothrow) Breakpoint;
84 		if (breakpoint == NULL)
85 			return B_NO_MEMORY;
86 
87 		breakpoint->address = 0;
88 		breakpoint->installedBreakpoint = NULL;
89 		breakpoint->used = false;
90 		breakpoint->software = false;
91 
92 		fHardwareBreakpoints.Add(breakpoint);
93 	}
94 
95 	return B_OK;
96 }
97 
98 
99 status_t
100 BreakpointManager::InstallBreakpoint(void* _address)
101 {
102 	const addr_t address = (addr_t)_address;
103 
104 	WriteLocker locker(fLock);
105 
106 	if (fBreakpointCount >= kMaxBreakpointCount)
107 		return B_BUSY;
108 
109 	// check whether there's already a breakpoint at the address
110 	InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
111 	if (installed != NULL)
112 		return B_BAD_VALUE;
113 
114 	// create the breakpoint object
115 	installed = new(std::nothrow) InstalledBreakpoint(address);
116 	if (installed == NULL)
117 		return B_NO_MEMORY;
118 	ObjectDeleter<InstalledBreakpoint> installedDeleter(installed);
119 
120 	// If we still have enough hardware breakpoints left, install a hardware
121 	// breakpoint.
122 	Breakpoint* breakpoint = _GetUnusedHardwareBreakpoint(false);
123 	if (breakpoint != NULL) {
124 		status_t error = _InstallHardwareBreakpoint(breakpoint, address);
125 		if (error != B_OK)
126 			return error;
127 
128 		breakpoint->installedBreakpoint = installed;
129 		installed->breakpoint = breakpoint;
130 	} else {
131 		// install a software breakpoint
132 		status_t error = _InstallSoftwareBreakpoint(installed, address);
133 		if (error != B_OK)
134 			return error;
135 	}
136 
137 	fBreakpoints.Insert(installed);
138 	installedDeleter.Detach();
139 	fBreakpointCount++;
140 
141 	return B_OK;
142 }
143 
144 
145 status_t
146 BreakpointManager::UninstallBreakpoint(void* _address)
147 {
148 	const addr_t address = (addr_t)_address;
149 
150 	WriteLocker locker(fLock);
151 
152 	InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
153 	if (installed == NULL)
154 		return B_BAD_VALUE;
155 
156 	if (installed->breakpoint->software)
157 		_UninstallSoftwareBreakpoint(installed->breakpoint);
158 	else
159 		_UninstallHardwareBreakpoint(installed->breakpoint);
160 
161 	fBreakpoints.Remove(installed);
162 	delete installed;
163 	fBreakpointCount--;
164 
165 	return B_OK;
166 }
167 
168 
169 status_t
170 BreakpointManager::InstallWatchpoint(void* _address, uint32 type, int32 length)
171 {
172 	const addr_t address = (addr_t)_address;
173 
174 	WriteLocker locker(fLock);
175 
176 	InstalledWatchpoint* watchpoint = _FindWatchpoint(address);
177 	if (watchpoint != NULL)
178 		return B_BAD_VALUE;
179 
180 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
181 	// We need at least one hardware breakpoint for our breakpoint management.
182 	if (fWatchpointCount + 1 >= DEBUG_MAX_WATCHPOINTS)
183 		return B_BUSY;
184 #else
185 	if (fWatchpointCount >= DEBUG_MAX_WATCHPOINTS)
186 		return B_BUSY;
187 #endif
188 
189 	watchpoint = new(std::nothrow) InstalledWatchpoint;
190 	if (watchpoint == NULL)
191 		return B_NO_MEMORY;
192 	ObjectDeleter<InstalledWatchpoint> watchpointDeleter(watchpoint);
193 
194 	status_t error = _InstallWatchpoint(watchpoint, address, type, length);
195 	if (error != B_OK)
196 		return error;
197 
198 	fWatchpoints.Add(watchpointDeleter.Detach());
199 	fWatchpointCount++;
200 	return B_OK;
201 }
202 
203 
204 status_t
205 BreakpointManager::UninstallWatchpoint(void* address)
206 {
207 	WriteLocker locker(fLock);
208 
209 	InstalledWatchpoint* watchpoint = _FindWatchpoint((addr_t)address);
210 	if (watchpoint == NULL)
211 		return B_BAD_VALUE;
212 
213 	ObjectDeleter<InstalledWatchpoint> deleter(watchpoint);
214 	fWatchpoints.Remove(watchpoint);
215 	fWatchpointCount--;
216 
217 	return _UninstallWatchpoint(watchpoint);
218 }
219 
220 
221 void
222 BreakpointManager::RemoveAllBreakpoints()
223 {
224 	WriteLocker locker(fLock);
225 
226 	// remove the breakpoints
227 	BreakpointTree::Iterator it = fBreakpoints.GetIterator();
228 	while (InstalledBreakpoint* installedBreakpoint = it.Next()) {
229 		it.Remove();
230 
231 		// uninstall underlying hard/software breakpoint
232 		if (installedBreakpoint->breakpoint->software)
233 			_UninstallSoftwareBreakpoint(installedBreakpoint->breakpoint);
234 		else
235 			_UninstallHardwareBreakpoint(installedBreakpoint->breakpoint);
236 
237 		delete installedBreakpoint;
238 	}
239 
240 	// remove the watchpoints
241 	while (InstalledWatchpoint* watchpoint = fWatchpoints.RemoveHead()) {
242 		_UninstallWatchpoint(watchpoint);
243 		delete watchpoint;
244 	}
245 }
246 
247 
248 /*!	\brief Returns whether the given address can be accessed in principle.
249 	No check whether there's an actually accessible area is performed, though.
250 */
251 /*static*/ bool
252 BreakpointManager::CanAccessAddress(const void* _address, bool write)
253 {
254 	const addr_t address = (addr_t)_address;
255 
256 	// user addresses are always fine
257 	if (IS_USER_ADDRESS(address))
258 		return true;
259 
260 	// a commpage address can at least be read
261 	if (address >= USER_COMMPAGE_ADDR
262 		&& address < USER_COMMPAGE_ADDR + COMMPAGE_SIZE) {
263 		return !write;
264 	}
265 
266 	return false;
267 }
268 
269 
270 /*!	\brief Reads data from user memory.
271 
272 	Tries to read \a size bytes of data from user memory address \a address
273 	into the supplied buffer \a buffer. If only a part could be read the
274 	function won't fail. The number of bytes actually read is return through
275 	\a bytesRead.
276 
277 	\param address The user memory address from which to read.
278 	\param buffer The buffer into which to write.
279 	\param size The number of bytes to read.
280 	\param bytesRead Will be set to the number of bytes actually read.
281 	\return \c B_OK, if reading went fine. Then \a bytesRead will be set to
282 			the amount of data actually read. An error indicates that nothing
283 			has been read.
284 */
285 status_t
286 BreakpointManager::ReadMemory(const void* _address, void* buffer, size_t size,
287 	size_t& bytesRead)
288 {
289 	const addr_t address = (addr_t)_address;
290 
291 	ReadLocker locker(fLock);
292 
293 	status_t error = _ReadMemory(address, buffer, size, bytesRead);
294 	if (error != B_OK)
295 		return error;
296 
297 	// If we have software breakpoints installed, fix the buffer not to contain
298 	// any of them.
299 
300 	// address of the first possibly intersecting software breakpoint
301 	const addr_t startAddress
302 		= std::max(address, (addr_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1)
303 			- (DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1);
304 
305 	BreakpointTree::Iterator it = fBreakpoints.GetIterator(startAddress, true,
306 		true);
307 	while (InstalledBreakpoint* installed = it.Next()) {
308 		Breakpoint* breakpoint = installed->breakpoint;
309 		if (breakpoint->address >= address + size)
310 			break;
311 
312 		if (breakpoint->software) {
313 			// Software breakpoint intersects -- replace the read data with
314 			// the data saved in the breakpoint object.
315 			addr_t minAddress = std::max(breakpoint->address, address);
316 			size_t toCopy = std::min(address + size,
317 					breakpoint->address + DEBUG_SOFTWARE_BREAKPOINT_SIZE)
318 				- minAddress;
319 			memcpy((uint8*)buffer + (minAddress - address),
320 				breakpoint->softwareData + (minAddress - breakpoint->address),
321 				toCopy);
322 		}
323 	}
324 
325 	return B_OK;
326 }
327 
328 
329 status_t
330 BreakpointManager::WriteMemory(void* _address, const void* _buffer, size_t size,
331 	size_t& bytesWritten)
332 {
333 	bytesWritten = 0;
334 
335 	if (size == 0)
336 		return B_OK;
337 
338 	addr_t address = (addr_t)_address;
339 	const uint8* buffer = (uint8*)_buffer;
340 
341 	WriteLocker locker(fLock);
342 
343 	// We don't want to overwrite software breakpoints, so things are a bit more
344 	// complicated. We iterate through the intersecting software breakpoints,
345 	// writing the memory between them normally, but skipping the breakpoints
346 	// itself. We write into their softwareData instead.
347 
348 	// Get the first breakpoint -- if it starts before the address, we'll
349 	// handle it separately to make things in the main loop simpler.
350 	const addr_t startAddress
351 		= std::max(address, (addr_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1)
352 			- (DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1);
353 
354 	BreakpointTree::Iterator it = fBreakpoints.GetIterator(startAddress, true,
355 		true);
356 	InstalledBreakpoint* installed = it.Next();
357 	while (installed != NULL) {
358 		Breakpoint* breakpoint = installed->breakpoint;
359 		if (breakpoint->address >= address)
360 			break;
361 
362 		if (breakpoint->software) {
363 			// We've got a breakpoint that is partially intersecting with the
364 			// beginning of the address range to write.
365 			size_t toCopy = std::min(address + size,
366 					breakpoint->address + DEBUG_SOFTWARE_BREAKPOINT_SIZE)
367 				- address;
368 			memcpy(breakpoint->softwareData + (address - breakpoint->address),
369 				buffer, toCopy);
370 
371 			address += toCopy;
372 			size -= toCopy;
373 			bytesWritten += toCopy;
374 			buffer += toCopy;
375 		}
376 
377 		installed = it.Next();
378 	}
379 
380 	// loop through the breakpoints intersecting with the range
381 	while (installed != NULL) {
382 		Breakpoint* breakpoint = installed->breakpoint;
383 		if (breakpoint->address >= address + size)
384 			break;
385 
386 		if (breakpoint->software) {
387 			// write the data up to the breakpoint (if any)
388 			size_t toCopy = breakpoint->address - address;
389 			if (toCopy > 0) {
390 				size_t chunkWritten;
391 				status_t error = _WriteMemory(address, buffer, toCopy,
392 					chunkWritten);
393 				if (error != B_OK)
394 					return bytesWritten > 0 ? B_OK : error;
395 
396 				address += chunkWritten;
397 				size -= chunkWritten;
398 				bytesWritten += chunkWritten;
399 				buffer += chunkWritten;
400 
401 				if (chunkWritten < toCopy)
402 					return B_OK;
403 			}
404 
405 			// write to the breakpoint data
406 			toCopy = std::min(size, (size_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE);
407 			memcpy(breakpoint->softwareData, buffer, toCopy);
408 
409 			address += toCopy;
410 			size -= toCopy;
411 			bytesWritten += toCopy;
412 			buffer += toCopy;
413 		}
414 
415 		installed = it.Next();
416 	}
417 
418 	// write remaining data
419 	if (size > 0) {
420 		size_t chunkWritten;
421 		status_t error = _WriteMemory(address, buffer, size, chunkWritten);
422 		if (error != B_OK)
423 			return bytesWritten > 0 ? B_OK : error;
424 
425 		bytesWritten += chunkWritten;
426 	}
427 
428 	return B_OK;
429 }
430 
431 
432 void
433 BreakpointManager::PrepareToContinue(void* _address)
434 {
435 	const addr_t address = (addr_t)_address;
436 
437 	WriteLocker locker(fLock);
438 
439 	// Check whether there's a software breakpoint at the continuation address.
440 	InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
441 	if (installed == NULL || !installed->breakpoint->software)
442 		return;
443 
444 	// We need to replace the software breakpoint by a hardware one, or
445 	// we can't continue the thread.
446 	Breakpoint* breakpoint = _GetUnusedHardwareBreakpoint(true);
447 	if (breakpoint == NULL) {
448 		dprintf("Failed to allocate a hardware breakpoint.\n");
449 		return;
450 	}
451 
452 	status_t error = _InstallHardwareBreakpoint(breakpoint, address);
453 	if (error != B_OK)
454 		return;
455 
456 	_UninstallSoftwareBreakpoint(installed->breakpoint);
457 
458 	breakpoint->installedBreakpoint = installed;
459 	installed->breakpoint = breakpoint;
460 }
461 
462 
463 BreakpointManager::Breakpoint*
464 BreakpointManager::_GetUnusedHardwareBreakpoint(bool force)
465 {
466 	// try to find a free one first
467 	for (BreakpointList::Iterator it = fHardwareBreakpoints.GetIterator();
468 			Breakpoint* breakpoint = it.Next();) {
469 		if (!breakpoint->used)
470 			return breakpoint;
471 	}
472 
473 	if (!force)
474 		return NULL;
475 
476 	// replace one by a software breakpoint
477 	for (BreakpointList::Iterator it = fHardwareBreakpoints.GetIterator();
478 			Breakpoint* breakpoint = it.Next();) {
479 		if (breakpoint->installedBreakpoint == NULL)
480 			continue;
481 
482 		status_t error = _InstallSoftwareBreakpoint(
483 			breakpoint->installedBreakpoint, breakpoint->address);
484 		if (error != B_OK)
485 			continue;
486 
487 		if (_UninstallHardwareBreakpoint(breakpoint) == B_OK)
488 			return breakpoint;
489 	}
490 
491 	return NULL;
492 }
493 
494 
495 status_t
496 BreakpointManager::_InstallSoftwareBreakpoint(InstalledBreakpoint* installed,
497 	addr_t address)
498 {
499 	Breakpoint* breakpoint = new(std::nothrow) Breakpoint;
500 	if (breakpoint == NULL)
501 		return B_NO_MEMORY;
502 	ObjectDeleter<Breakpoint> breakpointDeleter(breakpoint);
503 
504 	breakpoint->address = address;
505 	breakpoint->installedBreakpoint = installed;
506 	breakpoint->used = true;
507 	breakpoint->software = true;
508 
509 	// save the memory where the software breakpoint shall be installed
510 	size_t bytesTransferred;
511 	status_t error = _ReadMemory(address, breakpoint->softwareData,
512 		DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesTransferred);
513 	if (error != B_OK)
514 		return error;
515 	if (bytesTransferred != DEBUG_SOFTWARE_BREAKPOINT_SIZE)
516 		return B_BAD_ADDRESS;
517 
518 	// write the breakpoint code
519 	error = _WriteMemory(address, DEBUG_SOFTWARE_BREAKPOINT,
520 		DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesTransferred);
521 	if (error != B_OK)
522 		return error;
523 
524 	if (bytesTransferred < DEBUG_SOFTWARE_BREAKPOINT_SIZE) {
525 		// breakpoint written partially only -- undo the written part
526 		if (bytesTransferred > 0) {
527 			size_t dummy;
528 			_WriteMemory(address, breakpoint->softwareData, bytesTransferred,
529 				dummy);
530 		}
531 		return B_BAD_ADDRESS;
532 	}
533 
534 	installed->breakpoint = breakpoint;
535 	breakpointDeleter.Detach();
536 
537 	TRACE("installed software breakpoint at %#lx\n", address);
538 
539 	return B_OK;
540 }
541 
542 
543 status_t
544 BreakpointManager::_UninstallSoftwareBreakpoint(Breakpoint* breakpoint)
545 {
546 	size_t bytesWritten;
547 	_WriteMemory(breakpoint->address, breakpoint->softwareData,
548 		DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesWritten);
549 
550 	TRACE("uninstalled software breakpoint at %#lx\n", breakpoint->address);
551 
552 	delete breakpoint;
553 	return B_OK;
554 }
555 
556 
557 status_t
558 BreakpointManager::_InstallHardwareBreakpoint(Breakpoint* breakpoint,
559 	addr_t address)
560 {
561 	status_t error = arch_set_breakpoint((void*)address);
562 	if (error != B_OK)
563 		return error;
564 
565 	// move to the tail of the list
566 	fHardwareBreakpoints.Remove(breakpoint);
567 	fHardwareBreakpoints.Add(breakpoint);
568 
569 	TRACE("installed hardware breakpoint at %#lx\n", address);
570 
571 	breakpoint->address = address;
572 	breakpoint->used = true;
573 	return B_OK;
574 }
575 
576 
577 status_t
578 BreakpointManager::_UninstallHardwareBreakpoint(Breakpoint* breakpoint)
579 {
580 	status_t error = arch_clear_breakpoint((void*)breakpoint->address);
581 	if (error != B_OK)
582 		return error;
583 
584 	TRACE("uninstalled hardware breakpoint at %#lx\n", breakpoint->address);
585 
586 	breakpoint->used = false;
587 	breakpoint->installedBreakpoint = NULL;
588 	return B_OK;
589 }
590 
591 
592 BreakpointManager::InstalledWatchpoint*
593 BreakpointManager::_FindWatchpoint(addr_t address) const
594 {
595 	for (InstalledWatchpointList::ConstIterator it = fWatchpoints.GetIterator();
596 		InstalledWatchpoint* watchpoint = it.Next();) {
597 		if (address == watchpoint->address)
598 			return watchpoint;
599 	}
600 
601 	return NULL;
602 }
603 
604 
605 status_t
606 BreakpointManager::_InstallWatchpoint(InstalledWatchpoint* watchpoint,
607 	addr_t address, uint32 type, int32 length)
608 {
609 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
610 	// We need a hardware breakpoint.
611 	watchpoint->breakpoint = _GetUnusedHardwareBreakpoint(true);
612 	if (watchpoint->breakpoint == NULL) {
613 		dprintf("Failed to allocate a hardware breakpoint for watchpoint.\n");
614 		return B_BUSY;
615 	}
616 #endif
617 
618 	status_t error = arch_set_watchpoint((void*)address, type, length);
619 	if (error != B_OK)
620 		return error;
621 
622 	watchpoint->address = address;
623 
624 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
625 	watchpoint->breakpoint->used = true;
626 #endif
627 
628 	return B_OK;
629 }
630 
631 
632 status_t
633 BreakpointManager::_UninstallWatchpoint(InstalledWatchpoint* watchpoint)
634 {
635 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
636 	watchpoint->breakpoint->used = false;
637 #endif
638 
639 	return arch_clear_watchpoint((void*)watchpoint->address);
640 }
641 
642 
643 status_t
644 BreakpointManager::_ReadMemory(const addr_t _address, void* _buffer,
645 	size_t size, size_t& bytesRead)
646 {
647 	const uint8* address = (const uint8*)_address;
648 	uint8* buffer = (uint8*)_buffer;
649 
650 	// check the parameters
651 	if (!CanAccessAddress(address, false))
652 		return B_BAD_ADDRESS;
653 	if (size <= 0)
654 		return B_BAD_VALUE;
655 
656 	// If the region to be read crosses page boundaries, we split it up into
657 	// smaller chunks.
658 	status_t error = B_OK;
659 	bytesRead = 0;
660 	while (size > 0) {
661 		// check whether we're still in user address space
662 		if (!CanAccessAddress(address, false)) {
663 			error = B_BAD_ADDRESS;
664 			break;
665 		}
666 
667 		// don't cross page boundaries in a single read
668 		int32 toRead = size;
669 		int32 maxRead = B_PAGE_SIZE - (addr_t)address % B_PAGE_SIZE;
670 		if (toRead > maxRead)
671 			toRead = maxRead;
672 
673 		error = user_memcpy(buffer, address, toRead);
674 		if (error != B_OK)
675 			break;
676 
677 		bytesRead += toRead;
678 		address += toRead;
679 		buffer += toRead;
680 		size -= toRead;
681 	}
682 
683 	// If reading fails, we only fail, if we haven't read anything yet.
684 	if (error != B_OK) {
685 		if (bytesRead > 0)
686 			return B_OK;
687 		return error;
688 	}
689 
690 	return B_OK;
691 }
692 
693 
694 status_t
695 BreakpointManager::_WriteMemory(addr_t _address, const void* _buffer,
696 	size_t size, size_t& bytesWritten)
697 {
698 	uint8* address = (uint8*)_address;
699 	const uint8* buffer = (const uint8*)_buffer;
700 
701 	// check the parameters
702 	if (!CanAccessAddress(address, true))
703 		return B_BAD_ADDRESS;
704 	if (size <= 0)
705 		return B_BAD_VALUE;
706 
707 	// If the region to be written crosses area boundaries, we split it up into
708 	// smaller chunks.
709 	status_t error = B_OK;
710 	bytesWritten = 0;
711 	while (size > 0) {
712 		// check whether we're still in user address space
713 		if (!CanAccessAddress(address, true)) {
714 			error = B_BAD_ADDRESS;
715 			break;
716 		}
717 
718 		// get the area for the address (we need to use _user_area_for(), since
719 		// we're looking for a user area)
720 		area_id area = _user_area_for(address);
721 		if (area < 0) {
722 			TRACE("BreakpointManager::_WriteMemory(): area not found for "
723 				"address: %p: %lx\n", address, area);
724 			error = area;
725 			break;
726 		}
727 
728 		area_info areaInfo;
729 		status_t error = get_area_info(area, &areaInfo);
730 		if (error != B_OK) {
731 			TRACE("BreakpointManager::_WriteMemory(): failed to get info for "
732 				"area %ld: %lx\n", area, error);
733 			error = B_BAD_ADDRESS;
734 			break;
735 		}
736 
737 		// restrict this round of writing to the found area
738 		int32 toWrite = size;
739 		int32 maxWrite = (uint8*)areaInfo.address + areaInfo.size - address;
740 		if (toWrite > maxWrite)
741 			toWrite = maxWrite;
742 
743 		// if the area is read-only, we temporarily need to make it writable
744 		bool protectionChanged = false;
745 		if (!(areaInfo.protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA))) {
746 			error = set_area_protection(area,
747 				areaInfo.protection | B_WRITE_AREA);
748 			if (error != B_OK) {
749 				TRACE("BreakpointManager::_WriteMemory(): failed to set new "
750 					"protection for area %ld: %lx\n", area, error);
751 				break;
752 			}
753 			protectionChanged = true;
754 		}
755 
756 		// copy the memory
757 		error = user_memcpy(address, buffer, toWrite);
758 
759 		// reset the area protection
760 		if (protectionChanged)
761 			set_area_protection(area, areaInfo.protection);
762 
763 		if (error != B_OK) {
764 			TRACE("BreakpointManager::_WriteMemory(): user_memcpy() failed: "
765 				"%lx\n", error);
766 			break;
767 		}
768 
769 		bytesWritten += toWrite;
770 		address += toWrite;
771 		buffer += toWrite;
772 		size -= toWrite;
773 	}
774 
775 	// If writing fails, we only fail, if we haven't written anything yet.
776 	if (error != B_OK) {
777 		if (bytesWritten > 0)
778 			return B_OK;
779 		return error;
780 	}
781 
782 	return B_OK;
783 }
784