* Added support for debugging through exec*().
* Added initialization function. Currently only need to init the single step hack for qemu. * Fixed a deadlock when the nub thread destroyed the debug info. It was waiting for itself. * Moved the filling in the origin info of the debug messages into thread_hit_debug_event_internal(). No need for code duplication. * Writing to user memory can now be partial. We also change the area protection, if it wasn't writable. Necessary for setting software breakpoints. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@11996 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
99a4d67e7b
commit
3eae1fea40
|
@ -139,6 +139,10 @@ void clear_thread_debug_info(struct thread_debug_info *info,
|
|||
bool dying);
|
||||
void destroy_thread_debug_info(struct thread_debug_info *info);
|
||||
|
||||
void user_debug_prepare_for_exec();
|
||||
void user_debug_finish_after_exec();
|
||||
|
||||
void init_user_debug();
|
||||
|
||||
// debug event callbacks
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <thread.h>
|
||||
#include <thread_types.h>
|
||||
#include <user_debugger.h>
|
||||
#include <vm.h>
|
||||
#include <vm_types.h>
|
||||
#include <arch/user_debugger.h>
|
||||
|
||||
//#define TRACE_USER_DEBUGGER
|
||||
|
@ -160,8 +162,11 @@ destroy_team_debug_info(struct team_debug_info *info)
|
|||
|
||||
// wait for the nub thread
|
||||
if (info->nub_thread >= 0) {
|
||||
int32 result;
|
||||
wait_for_thread(info->nub_thread, &result);
|
||||
if (info->nub_thread != thread_get_current_thread()->id) {
|
||||
int32 result;
|
||||
wait_for_thread(info->nub_thread, &result);
|
||||
}
|
||||
|
||||
info->nub_thread = -1;
|
||||
}
|
||||
|
||||
|
@ -205,6 +210,74 @@ destroy_thread_debug_info(struct thread_debug_info *info)
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
user_debug_prepare_for_exec()
|
||||
{
|
||||
struct thread *thread = thread_get_current_thread();
|
||||
struct team *team = thread->team;
|
||||
|
||||
// If a debugger is installed for the team and the thread debug stuff
|
||||
// initialized, changed the ownership of the debug port for the thread
|
||||
// to the kernel team, since exec_team() deletes all ports owned by this
|
||||
// team. We change the ownership back later.
|
||||
if (atomic_get(&team->debug_info.flags) & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
|
||||
// get the port
|
||||
port_id debugPort = -1;
|
||||
|
||||
cpu_status state = disable_interrupts();
|
||||
GRAB_THREAD_LOCK();
|
||||
|
||||
if (thread->debug_info.flags & B_THREAD_DEBUG_INITIALIZED)
|
||||
debugPort = thread->debug_info.debug_port;
|
||||
|
||||
RELEASE_THREAD_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
// set the new port ownership
|
||||
if (debugPort >= 0)
|
||||
set_port_owner(debugPort, team_get_kernel_team_id());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
user_debug_finish_after_exec()
|
||||
{
|
||||
struct thread *thread = thread_get_current_thread();
|
||||
struct team *team = thread->team;
|
||||
|
||||
// If a debugger is installed for the team and the thread debug stuff
|
||||
// initialized for this thread, change the ownership of its debug port
|
||||
// back to this team.
|
||||
if (atomic_get(&team->debug_info.flags) & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
|
||||
// get the port
|
||||
port_id debugPort = -1;
|
||||
|
||||
cpu_status state = disable_interrupts();
|
||||
GRAB_THREAD_LOCK();
|
||||
|
||||
if (thread->debug_info.flags & B_THREAD_DEBUG_INITIALIZED)
|
||||
debugPort = thread->debug_info.debug_port;
|
||||
|
||||
RELEASE_THREAD_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
// set the new port ownership
|
||||
if (debugPort >= 0)
|
||||
set_port_owner(debugPort, team->id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
init_user_debug()
|
||||
{
|
||||
#ifdef ARCH_INIT_USER_DEBUG
|
||||
ARCH_INIT_USER_DEBUG();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
get_team_debug_info(team_debug_info &teamDebugInfo)
|
||||
{
|
||||
|
@ -313,8 +386,15 @@ thread_hit_debug_event_internal(debug_debugger_message event,
|
|||
|
||||
// send a message to the debugger port
|
||||
if (debuggerInstalled) {
|
||||
// update the message's origin info first
|
||||
debug_origin *origin = (debug_origin *)message;
|
||||
origin->thread = thread->id;
|
||||
origin->team = thread->team->id;
|
||||
origin->nub_port = nubPort;
|
||||
|
||||
TRACE(("thread_hit_debug_event(): thread: %ld, sending message to "
|
||||
"debugger port %ld\n", thread->id, debuggerPort));
|
||||
|
||||
error = debugger_write(debuggerPort, event, message, size, false);
|
||||
}
|
||||
|
||||
|
@ -481,9 +561,6 @@ user_debug_pre_syscall(uint32 syscall, void *args)
|
|||
|
||||
// prepare the message
|
||||
debug_pre_syscall message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = thread->team->debug_info.nub_port;
|
||||
message.syscall = syscall;
|
||||
|
||||
// copy the syscall args
|
||||
|
@ -516,9 +593,6 @@ user_debug_post_syscall(uint32 syscall, void *args, uint64 returnValue,
|
|||
|
||||
// prepare the message
|
||||
debug_post_syscall message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = thread->team->debug_info.nub_port;
|
||||
message.start_time = startTime;
|
||||
message.end_time = system_time();
|
||||
message.return_value = returnValue;
|
||||
|
@ -558,9 +632,6 @@ user_debug_exception_occurred(debug_exception_type exception, int signal)
|
|||
|
||||
// prepare the message
|
||||
debug_exception_occurred message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = nubPort;
|
||||
message.exception = exception;
|
||||
message.signal = signal;
|
||||
|
||||
|
@ -583,9 +654,6 @@ user_debug_handle_signal(int signal, struct sigaction *handler, bool deadly)
|
|||
|
||||
// prepare the message
|
||||
debug_signal_received message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = thread->team->debug_info.nub_port;
|
||||
message.signal = signal;
|
||||
message.handler = *handler;
|
||||
message.deadly = deadly;
|
||||
|
@ -611,9 +679,6 @@ user_debug_stop_thread()
|
|||
|
||||
// prepare the message
|
||||
debug_thread_debugged message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = nubPort;
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_THREAD_DEBUGGED, &message,
|
||||
sizeof(message), true);
|
||||
|
@ -634,9 +699,6 @@ user_debug_team_created(team_id teamID)
|
|||
|
||||
// prepare the message
|
||||
debug_team_created message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = thread->team->debug_info.nub_port;
|
||||
message.new_team = teamID;
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_TEAM_CREATED, &message,
|
||||
|
@ -648,6 +710,9 @@ void
|
|||
user_debug_team_deleted(team_id teamID, port_id debuggerPort)
|
||||
{
|
||||
if (debuggerPort >= 0) {
|
||||
TRACE(("user_debug_team_deleted(team: %ld, debugger port: %ld)\n",
|
||||
teamID, debuggerPort));
|
||||
|
||||
debug_team_deleted message;
|
||||
message.origin.thread = -1;
|
||||
message.origin.team = teamID;
|
||||
|
@ -672,9 +737,6 @@ user_debug_thread_created(thread_id threadID)
|
|||
|
||||
// prepare the message
|
||||
debug_thread_created message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = thread->team->debug_info.nub_port;
|
||||
message.new_thread = threadID;
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_THREAD_CREATED, &message,
|
||||
|
@ -737,9 +799,6 @@ user_debug_image_created(const image_info *imageInfo)
|
|||
|
||||
// prepare the message
|
||||
debug_image_created message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = thread->team->debug_info.nub_port;
|
||||
memcpy(&message.info, imageInfo, sizeof(image_info));
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_IMAGE_CREATED, &message,
|
||||
|
@ -760,9 +819,6 @@ user_debug_image_deleted(const image_info *imageInfo)
|
|||
|
||||
// prepare the message
|
||||
debug_image_deleted message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = thread->team->debug_info.nub_port;
|
||||
memcpy(&message.info, imageInfo, sizeof(image_info));
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_IMAGE_CREATED, &message,
|
||||
|
@ -785,9 +841,6 @@ user_debug_breakpoint_hit(bool software)
|
|||
|
||||
// prepare the message
|
||||
debug_breakpoint_hit message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = nubPort;
|
||||
message.software = software;
|
||||
arch_get_debug_cpu_state(&message.cpu_state);
|
||||
|
||||
|
@ -811,9 +864,6 @@ user_debug_watchpoint_hit()
|
|||
|
||||
// prepare the message
|
||||
debug_watchpoint_hit message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = nubPort;
|
||||
arch_get_debug_cpu_state(&message.cpu_state);
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_WATCHPOINT_HIT, &message,
|
||||
|
@ -836,9 +886,6 @@ user_debug_single_stepped()
|
|||
|
||||
// prepare the message
|
||||
debug_single_step message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = nubPort;
|
||||
arch_get_debug_cpu_state(&message.cpu_state);
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_SINGLE_STEP, &message,
|
||||
|
@ -891,6 +938,9 @@ broadcast_debugged_thread_message(struct thread *nubThread, int32 code,
|
|||
static void
|
||||
nub_thread_cleanup(struct thread *nubThread)
|
||||
{
|
||||
TRACE(("nub_thread_cleanup(%ld): debugger port: %ld\n", nubThread->id,
|
||||
nubThread->team->debug_info.debugger_port));
|
||||
|
||||
team_debug_info teamDebugInfo;
|
||||
bool destroyDebugInfo = false;
|
||||
|
||||
|
@ -974,6 +1024,94 @@ read_user_memory(const void *_address, void *_buffer, int32 size,
|
|||
}
|
||||
|
||||
|
||||
static status_t
|
||||
write_user_memory(void *_address, const void *_buffer, int32 size,
|
||||
int32 &bytesWritten)
|
||||
{
|
||||
char *address = (char*)_address;
|
||||
const char *buffer = (const char*)_buffer;
|
||||
|
||||
// check the parameters
|
||||
if (!IS_USER_ADDRESS(address))
|
||||
return B_BAD_ADDRESS;
|
||||
if (size <= 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// If the region to be written crosses area boundaries, we split it up into
|
||||
// smaller chunks.
|
||||
status_t error = B_OK;
|
||||
bytesWritten = 0;
|
||||
while (size > 0) {
|
||||
int32 toWrite = size;
|
||||
|
||||
// get the area for the address (we need to use _user_area_for(), since
|
||||
// we're looking for a user area)
|
||||
area_id area = _user_area_for((void*)address);
|
||||
if (area < 0) {
|
||||
TRACE(("write_user_memory(): area not found for address: %p: "
|
||||
"%lx\n", address, area));
|
||||
error = area;
|
||||
break;
|
||||
}
|
||||
|
||||
area_info areaInfo;
|
||||
status_t error = get_area_info(area, &areaInfo);
|
||||
if (error != B_OK) {
|
||||
TRACE(("write_user_memory(): failed to get info for area %ld: "
|
||||
"%lx\n", area, error));
|
||||
error = B_BAD_ADDRESS;
|
||||
break;
|
||||
}
|
||||
|
||||
// restrict this round of writing to the found area
|
||||
if (address + toWrite > (char*)areaInfo.address + areaInfo.size)
|
||||
toWrite = (char*)areaInfo.address + areaInfo.size - address;
|
||||
|
||||
// if the area is read-only, we temporarily need to make it writable
|
||||
bool protectionChanged = false;
|
||||
if (!(areaInfo.protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA))) {
|
||||
error = set_area_protection(area,
|
||||
// areaInfo.protection | B_KERNEL_WRITE_AREA);
|
||||
(areaInfo.protection & ~B_EXECUTE_AREA) | B_KERNEL_WRITE_AREA);
|
||||
// TODO: Not being writable when being executable is currently a requirement
|
||||
// of vm_set_area_protection().
|
||||
if (error != B_OK) {
|
||||
TRACE(("write_user_memory(): failed to set new protection for "
|
||||
"area %ld: %lx\n", area, error));
|
||||
break;
|
||||
}
|
||||
protectionChanged = true;
|
||||
}
|
||||
|
||||
// copy the memory
|
||||
error = user_memcpy(address, buffer, toWrite);
|
||||
|
||||
// reset the area protection
|
||||
if (protectionChanged)
|
||||
set_area_protection(area, areaInfo.protection);
|
||||
|
||||
if (error != B_OK) {
|
||||
TRACE(("write_user_memory(): user_memcpy() failed: %lx\n", error));
|
||||
break;
|
||||
}
|
||||
|
||||
bytesWritten += toWrite;
|
||||
address += toWrite;
|
||||
buffer += toWrite;
|
||||
size -= toWrite;
|
||||
}
|
||||
|
||||
// If writing fails, we only fail, if we haven't written anything yet.
|
||||
if (error != B_OK) {
|
||||
if (bytesWritten > 0)
|
||||
return B_OK;
|
||||
return error;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/** \brief Debug nub thread helper function that returns the debug port of
|
||||
* a thread of the same team.
|
||||
*/
|
||||
|
@ -1092,8 +1230,9 @@ debug_nub_thread(void *)
|
|||
reply.read_memory.error = result;
|
||||
|
||||
TRACE(("nub thread %ld: B_DEBUG_MESSAGE_READ_MEMORY: "
|
||||
"reply port: %ld, address: %p, size: %ld, result: %lx\n",
|
||||
nubThread->id, replyPort, address, size, result));
|
||||
"reply port: %ld, address: %p, size: %ld, result: %lx, "
|
||||
"read: %ld\n", nubThread->id, replyPort, address, size,
|
||||
result, bytesRead));
|
||||
|
||||
// send only as much data as necessary
|
||||
reply.read_memory.size = bytesRead;
|
||||
|
@ -1112,10 +1251,6 @@ debug_nub_thread(void *)
|
|||
int32 realSize = (char*)&message + messageSize - data;
|
||||
status_t result = B_OK;
|
||||
|
||||
TRACE(("nub thread %ld: B_DEBUG_MESSAGE_WRITE_MEMORY: "
|
||||
"reply port: %ld, address: %p, size: %ld\n", nubThread->id,
|
||||
replyPort, address, size));
|
||||
|
||||
// check the parameters
|
||||
if (!IS_USER_ADDRESS(address))
|
||||
result = B_BAD_ADDRESS;
|
||||
|
@ -1123,10 +1258,19 @@ debug_nub_thread(void *)
|
|||
result = B_BAD_VALUE;
|
||||
|
||||
// write the memory
|
||||
if (result == B_OK)
|
||||
result = user_memcpy(address, data, size);
|
||||
int32 bytesWritten = 0;
|
||||
if (result == B_OK) {
|
||||
result = write_user_memory(address, data, size,
|
||||
bytesWritten);
|
||||
}
|
||||
reply.write_memory.error = result;
|
||||
|
||||
TRACE(("nub thread %ld: B_DEBUG_MESSAGE_WRITE_MEMORY: "
|
||||
"reply port: %ld, address: %p, size: %ld, result: %lx, "
|
||||
"written: %ld\n", nubThread->id, replyPort, address, size,
|
||||
result, bytesWritten));
|
||||
|
||||
reply.write_memory.size = bytesWritten;
|
||||
sendReply = true;
|
||||
replySize = sizeof(debug_nub_write_memory_reply);
|
||||
break;
|
||||
|
@ -1878,9 +2022,6 @@ _user_debugger(const char *userMessage)
|
|||
|
||||
// prepare the message
|
||||
debug_debugger_call message;
|
||||
message.origin.thread = thread->id;
|
||||
message.origin.team = thread->team->id;
|
||||
message.origin.nub_port = nubPort;
|
||||
message.message = (void*)userMessage;
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_DEBUGGER_CALL, &message,
|
||||
|
@ -2024,6 +2165,9 @@ _user_debug_thread(thread_id threadID)
|
|||
void
|
||||
_user_wait_for_debugger(void)
|
||||
{
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_THREAD_DEBUGGED, NULL, 0, false);
|
||||
debug_thread_debugged message;
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_THREAD_DEBUGGED, &message,
|
||||
sizeof(message), false);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue