* Added vm_debug_copy_page_memory() which copies memory from a potentially not
mapped page.
* debug_{mem,strl}cpy():
- Added "team" parameter for specifying the address space the address are
to be interpreted in.
- When the standard memcpy() (with fault handler) fails, fall back to
vm_debug_copy_page_memory().
* Added debug_is_debugged_team(): Predicate returning true, if the supplied
team_id refers to the same team debug_get_debugged_thread() belongs to.
* Added DebuggedThreadSetter class for scope-based debug_set_debugged_thread().
Made use of it in several debugger functions.
* print_demangled_call() (x86): Fixed unsafe memory access.
Allows KDL stack traces to work correctly again, even if the page daemon has
already unmapped the concerned pages.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36230 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -132,8 +132,10 @@ extern bool debug_is_kernel_memory_accessible(addr_t address, size_t size,
|
||||
uint32 protection);
|
||||
extern int debug_call_with_fault_handler(jmp_buf jumpBuffer,
|
||||
void (*function)(void*), void* parameter);
|
||||
extern status_t debug_memcpy(void* to, const void* from, size_t size);
|
||||
extern ssize_t debug_strlcpy(char* to, const char* from, size_t size);
|
||||
extern status_t debug_memcpy(team_id teamID, void* to, const void* from,
|
||||
size_t size);
|
||||
extern ssize_t debug_strlcpy(team_id teamID, char* to, const char* from,
|
||||
size_t size);
|
||||
|
||||
extern char kgetc(void);
|
||||
extern void kputs(const char *string);
|
||||
@@ -172,8 +174,9 @@ extern status_t debug_get_next_demangled_argument(uint32* _cookie,
|
||||
|
||||
extern struct thread* debug_set_debugged_thread(struct thread* thread);
|
||||
extern struct thread* debug_get_debugged_thread();
|
||||
extern struct arch_debug_registers* debug_get_debug_registers(int32 cpu);
|
||||
extern bool debug_is_debugged_team(team_id teamID);
|
||||
|
||||
extern struct arch_debug_registers* debug_get_debug_registers(int32 cpu);
|
||||
|
||||
extern status_t _user_kernel_debugger(const char *message);
|
||||
extern void _user_debug_output(const char *userString);
|
||||
@@ -182,4 +185,27 @@ extern void _user_debug_output(const char *userString);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
struct DebuggedThreadSetter {
|
||||
DebuggedThreadSetter(struct thread* thread)
|
||||
:
|
||||
fPreviousThread(debug_set_debugged_thread(thread))
|
||||
{
|
||||
}
|
||||
|
||||
~DebuggedThreadSetter()
|
||||
{
|
||||
debug_set_debugged_thread(fPreviousThread);
|
||||
}
|
||||
|
||||
private:
|
||||
struct thread* fPreviousThread;
|
||||
};
|
||||
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
|
||||
#endif /* _KERNEL_DEBUG_H */
|
||||
|
||||
@@ -140,6 +140,9 @@ status_t vm_memcpy_to_physical(addr_t to, const void* from, size_t length,
|
||||
bool user);
|
||||
void vm_memcpy_physical_page(addr_t to, addr_t from);
|
||||
|
||||
status_t vm_debug_copy_page_memory(team_id teamID, void* unsafeMemory,
|
||||
void* buffer, size_t size, bool copyToUnsafe);
|
||||
|
||||
// user syscalls
|
||||
area_id _user_create_area(const char *name, void **address, uint32 addressSpec,
|
||||
size_t size, uint32 lock, uint32 protection);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2009, Haiku Inc. All rights reserved.
|
||||
* Copyright 2003-2010, Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
@@ -66,8 +66,10 @@ static status_t
|
||||
get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
|
||||
{
|
||||
stack_frame frame;
|
||||
if (debug_memcpy(&frame, (void*)framePointer, sizeof(frame)) != B_OK)
|
||||
if (debug_memcpy(B_CURRENT_TEAM, &frame, (void*)framePointer, sizeof(frame))
|
||||
!= B_OK) {
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
*ip = frame.return_address;
|
||||
*next = (addr_t)frame.previous;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2009, Haiku Inc. All rights reserved.
|
||||
* Copyright 2003-2010, Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
@@ -65,8 +65,10 @@ static status_t
|
||||
get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
|
||||
{
|
||||
stack_frame frame;
|
||||
if (debug_memcpy(&frame, (void*)framePointer, sizeof(frame)) != B_OK)
|
||||
if (debug_memcpy(B_CURRENT_TEAM, &frame, (void*)framePointer, sizeof(frame))
|
||||
!= B_OK) {
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
*ip = frame.return_address;
|
||||
*next = (addr_t)frame.previous;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Copyright 2009-2010, Ingo Weinhold, [email protected].
|
||||
* Copyright 2002-2008, Axel Dörfler, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
@@ -81,7 +81,7 @@ static status_t
|
||||
get_next_frame_debugger(addr_t ebp, addr_t *_next, addr_t *_eip)
|
||||
{
|
||||
stack_frame frame;
|
||||
if (debug_memcpy(&frame, (void*)ebp, sizeof(frame)) != B_OK)
|
||||
if (debug_memcpy(B_CURRENT_TEAM, &frame, (void*)ebp, sizeof(frame)) != B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
*_eip = frame.return_address;
|
||||
@@ -126,6 +126,21 @@ set_debug_argument_variable(int32 index, uint64 value)
|
||||
}
|
||||
|
||||
|
||||
template<typename Type>
|
||||
static Type
|
||||
read_function_argument_value(void* argument, bool& _valueKnown)
|
||||
{
|
||||
Type value;
|
||||
if (debug_memcpy(B_CURRENT_TEAM, &value, argument, sizeof(Type)) == B_OK) {
|
||||
_valueKnown = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
_valueKnown = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
print_demangled_call(const char* image, const char* symbol, addr_t args,
|
||||
bool noObjectMethod, bool addDebugVariables)
|
||||
@@ -151,10 +166,15 @@ print_demangled_call(const char* image, const char* symbol, addr_t args,
|
||||
const char* lastName = strrchr(name, ':') - 1;
|
||||
int namespaceLength = lastName - name;
|
||||
|
||||
kprintf("<%s> %.*s<\33[32m%p\33[0m>%s", image, namespaceLength, name,
|
||||
*(uint32 **)arg, lastName);
|
||||
uint32 argValue = 0;
|
||||
if (debug_memcpy(B_CURRENT_TEAM, &argValue, arg, 4) == B_OK) {
|
||||
kprintf("<%s> %.*s<\33[32m%#" B_PRIx32 "\33[0m>%s", image,
|
||||
namespaceLength, name, argValue, lastName);
|
||||
} else
|
||||
kprintf("<%s> %.*s<???>%s", image, namespaceLength, name, lastName);
|
||||
|
||||
if (addDebugVariables)
|
||||
set_debug_variable("_this", *(uint32 *)arg);
|
||||
set_debug_variable("_this", argValue);
|
||||
arg++;
|
||||
} else
|
||||
kprintf("<%s> %s", image, name);
|
||||
@@ -172,78 +192,103 @@ print_demangled_call(const char* image, const char* symbol, addr_t args,
|
||||
// retrieve value and type identifier
|
||||
|
||||
uint64 value;
|
||||
bool valueKnown = false;
|
||||
|
||||
switch (type) {
|
||||
case B_INT64_TYPE:
|
||||
value = *(int64*)arg;
|
||||
kprintf("int64: \33[34m%Ld\33[0m", value);
|
||||
value = read_function_argument_value<int64>(arg, valueKnown);
|
||||
if (valueKnown)
|
||||
kprintf("int64: \33[34m%Ld\33[0m", value);
|
||||
break;
|
||||
case B_INT32_TYPE:
|
||||
value = *(int32*)arg;
|
||||
kprintf("int32: \33[34m%ld\33[0m", (int32)value);
|
||||
value = read_function_argument_value<int32>(arg, valueKnown);
|
||||
if (valueKnown)
|
||||
kprintf("int32: \33[34m%ld\33[0m", (int32)value);
|
||||
break;
|
||||
case B_INT16_TYPE:
|
||||
value = *(int16*)arg;
|
||||
kprintf("int16: \33[34m%d\33[0m", (int16)value);
|
||||
value = read_function_argument_value<int16>(arg, valueKnown);
|
||||
if (valueKnown)
|
||||
kprintf("int16: \33[34m%d\33[0m", (int16)value);
|
||||
break;
|
||||
case B_INT8_TYPE:
|
||||
value = *(int8*)arg;
|
||||
kprintf("int8: \33[34m%d\33[0m", (int8)value);
|
||||
value = read_function_argument_value<int8>(arg, valueKnown);
|
||||
if (valueKnown)
|
||||
kprintf("int8: \33[34m%d\33[0m", (int8)value);
|
||||
break;
|
||||
case B_UINT64_TYPE:
|
||||
value = *(uint64*)arg;
|
||||
kprintf("uint64: \33[34m%#Lx\33[0m", value);
|
||||
if (value < 0x100000)
|
||||
kprintf(" (\33[34m%Lu\33[0m)", value);
|
||||
value = read_function_argument_value<uint64>(arg, valueKnown);
|
||||
if (valueKnown) {
|
||||
kprintf("uint64: \33[34m%#Lx\33[0m", value);
|
||||
if (value < 0x100000)
|
||||
kprintf(" (\33[34m%Lu\33[0m)", value);
|
||||
}
|
||||
break;
|
||||
case B_UINT32_TYPE:
|
||||
value = *(uint32*)arg;
|
||||
kprintf("uint32: \33[34m%#lx\33[0m", (uint32)value);
|
||||
if (value < 0x100000)
|
||||
kprintf(" (\33[34m%lu\33[0m)", (uint32)value);
|
||||
value = read_function_argument_value<uint32>(arg, valueKnown);
|
||||
if (valueKnown) {
|
||||
kprintf("uint32: \33[34m%#lx\33[0m", (uint32)value);
|
||||
if (value < 0x100000)
|
||||
kprintf(" (\33[34m%lu\33[0m)", (uint32)value);
|
||||
}
|
||||
break;
|
||||
case B_UINT16_TYPE:
|
||||
value = *(uint16*)arg;
|
||||
kprintf("uint16: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
|
||||
(uint16)value, (uint16)value);
|
||||
value = read_function_argument_value<uint16>(arg, valueKnown);
|
||||
if (valueKnown) {
|
||||
kprintf("uint16: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
|
||||
(uint16)value, (uint16)value);
|
||||
}
|
||||
break;
|
||||
case B_UINT8_TYPE:
|
||||
value = *(uint8*)arg;
|
||||
kprintf("uint8: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
|
||||
(uint8)value, (uint8)value);
|
||||
value = read_function_argument_value<uint8>(arg, valueKnown);
|
||||
if (valueKnown) {
|
||||
kprintf("uint8: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
|
||||
(uint8)value, (uint8)value);
|
||||
}
|
||||
break;
|
||||
case B_BOOL_TYPE:
|
||||
value = *(uint8*)arg;
|
||||
kprintf("\33[34m%s\33[0m", value ? "true" : "false");
|
||||
value = read_function_argument_value<uint8>(arg, valueKnown);
|
||||
if (valueKnown)
|
||||
kprintf("\33[34m%s\33[0m", value ? "true" : "false");
|
||||
break;
|
||||
default:
|
||||
if (buffer[0])
|
||||
kprintf("%s: ", buffer);
|
||||
|
||||
if (length == 4) {
|
||||
value = *(uint32*)arg;
|
||||
if (value == 0
|
||||
&& (type == B_POINTER_TYPE || type == B_REF_TYPE))
|
||||
kprintf("NULL");
|
||||
else
|
||||
kprintf("\33[34m%#lx\33[0m", (uint32)value);
|
||||
value = read_function_argument_value<uint32>(arg,
|
||||
valueKnown);
|
||||
if (valueKnown) {
|
||||
if (value == 0
|
||||
&& (type == B_POINTER_TYPE || type == B_REF_TYPE))
|
||||
kprintf("NULL");
|
||||
else
|
||||
kprintf("\33[34m%#lx\33[0m", (uint32)value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (length == 8)
|
||||
value = *(uint64*)arg;
|
||||
else
|
||||
|
||||
if (length == 8) {
|
||||
value = read_function_argument_value<uint64>(arg,
|
||||
valueKnown);
|
||||
} else
|
||||
value = (uint64)arg;
|
||||
kprintf("\33[34m%#Lx\33[0m", value);
|
||||
|
||||
if (valueKnown)
|
||||
kprintf("\33[34m%#Lx\33[0m", value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == B_STRING_TYPE) {
|
||||
if (!valueKnown)
|
||||
kprintf("???");
|
||||
|
||||
if (valueKnown && type == B_STRING_TYPE) {
|
||||
if (value == 0)
|
||||
kprintf(" \33[31m\"<NULL>\"\33[0m");
|
||||
else if (debug_strlcpy(buffer, (char*)value, kBufferSize) < B_OK)
|
||||
else if (debug_strlcpy(B_CURRENT_TEAM, buffer, (char*)value,
|
||||
kBufferSize) < B_OK) {
|
||||
kprintf(" \33[31m\"<???>\"\33[0m");
|
||||
else
|
||||
} else
|
||||
kprintf(" \33[36m\"%s\"\33[0m", buffer);
|
||||
}
|
||||
|
||||
@@ -564,6 +609,8 @@ stack_trace(int argc, char **argv)
|
||||
&thread, &ebp, &oldPageDirectory))
|
||||
return 0;
|
||||
|
||||
DebuggedThreadSetter threadSetter(thread);
|
||||
|
||||
if (thread != NULL) {
|
||||
kprintf("stack trace for thread %ld \"%s\"\n", thread->id,
|
||||
thread->name);
|
||||
@@ -736,6 +783,8 @@ show_call(int argc, char **argv)
|
||||
&oldPageDirectory))
|
||||
return 0;
|
||||
|
||||
DebuggedThreadSetter threadSetter(thread);
|
||||
|
||||
int32 callIndex = strtoul(argv[argc == 3 ? 2 : 1], NULL, 0);
|
||||
|
||||
if (thread != NULL)
|
||||
@@ -816,6 +865,8 @@ dump_iframes(int argc, char **argv)
|
||||
if (thread != NULL)
|
||||
kprintf("iframes for thread %ld \"%s\"\n", thread->id, thread->name);
|
||||
|
||||
DebuggedThreadSetter threadSetter(thread);
|
||||
|
||||
struct iframe* frame = find_previous_iframe(thread, x86_read_ebp());
|
||||
while (frame != NULL) {
|
||||
print_iframe(frame);
|
||||
@@ -889,12 +940,11 @@ cmd_in_context(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
|
||||
struct thread* previousThread = debug_set_debugged_thread(thread);
|
||||
|
||||
// execute the command
|
||||
evaluate_debug_command(commandLine);
|
||||
|
||||
debug_set_debugged_thread(previousThread);
|
||||
{
|
||||
DebuggedThreadSetter threadSetter(thread);
|
||||
evaluate_debug_command(commandLine);
|
||||
}
|
||||
|
||||
// reset the page directory
|
||||
if (oldPageDirectory)
|
||||
@@ -927,6 +977,8 @@ bool
|
||||
arch_debug_contains_call(struct thread *thread, const char *symbol,
|
||||
addr_t start, addr_t end)
|
||||
{
|
||||
DebuggedThreadSetter threadSetter(thread);
|
||||
|
||||
addr_t ebp;
|
||||
if (thread == thread_get_current_thread())
|
||||
ebp = x86_read_ebp();
|
||||
|
||||
@@ -1852,33 +1852,69 @@ debug_call_with_fault_handler(jmp_buf jumpBuffer, void (*function)(void*),
|
||||
|
||||
/*! Similar to user_memcpy(), but can only be invoked from within the kernel
|
||||
debugger (and must not be used outside).
|
||||
The supplied \a teamID specifies the address space in which to interpret
|
||||
the addresses. It can be \c B_CURRENT_TEAM for debug_get_debugged_thread(),
|
||||
or any valid team ID. If the addresses are both kernel addresses, the
|
||||
argument is ignored and the current address space is used.
|
||||
*/
|
||||
status_t
|
||||
debug_memcpy(void* to, const void* from, size_t size)
|
||||
debug_memcpy(team_id teamID, void* to, const void* from, size_t size)
|
||||
{
|
||||
// don't allow address overflows
|
||||
if ((addr_t)from + size < (addr_t)from || (addr_t)to + size < (addr_t)to)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
debug_memcpy_parameters parameters = {to, from, size};
|
||||
// Try standard memcpy() with fault handler, if the addresses can be
|
||||
// interpreted in the current address space.
|
||||
if ((IS_KERNEL_ADDRESS(from) && IS_KERNEL_ADDRESS(to))
|
||||
|| debug_is_debugged_team(teamID)) {
|
||||
debug_memcpy_parameters parameters = {to, from, size};
|
||||
|
||||
if (debug_call_with_fault_handler(gCPU[sDebuggerOnCPU].fault_jump_buffer,
|
||||
&debug_memcpy_trampoline, ¶meters) != 0) {
|
||||
return B_BAD_ADDRESS;
|
||||
if (debug_call_with_fault_handler(gCPU[sDebuggerOnCPU].fault_jump_buffer,
|
||||
&debug_memcpy_trampoline, ¶meters) == 0) {
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Try harder. The pages of the respective memory could be unmapped but
|
||||
// still exist in a cache (the page daemon does that with inactive pages).
|
||||
while (size > 0) {
|
||||
uint8 buffer[32];
|
||||
size_t toCopy = std::min(size, sizeof(buffer));
|
||||
|
||||
// restrict the size so we don't cross page boundaries
|
||||
if (((addr_t)from + toCopy) % B_PAGE_SIZE < toCopy)
|
||||
toCopy -= ((addr_t)from + toCopy) % B_PAGE_SIZE;
|
||||
if (((addr_t)to + toCopy) % B_PAGE_SIZE < toCopy)
|
||||
toCopy -= ((addr_t)to + toCopy) % B_PAGE_SIZE;
|
||||
|
||||
if (vm_debug_copy_page_memory(teamID, (void*)from, buffer, toCopy,
|
||||
false) != B_OK
|
||||
|| vm_debug_copy_page_memory(teamID, to, buffer, toCopy, true)
|
||||
!= B_OK) {
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
from = (const uint8*)from + toCopy;
|
||||
to = (uint8*)to + toCopy;
|
||||
size -= toCopy;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! Similar to user_strlcpy(), but can only be invoked from within the kernel
|
||||
debugger (and must not be used outside).
|
||||
The supplied \a teamID specifies the address space in which to interpret
|
||||
the addresses. It can be \c B_CURRENT_TEAM for debug_get_debugged_thread(),
|
||||
or any valid team ID. If the addresses are both kernel addresses, the
|
||||
argument is ignored and the current address space is used.
|
||||
*/
|
||||
ssize_t
|
||||
debug_strlcpy(char* to, const char* from, size_t size)
|
||||
debug_strlcpy(team_id teamID, char* to, const char* from, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return 0;
|
||||
if (from == NULL || to == NULL)
|
||||
if (from == NULL || (to == NULL && size > 0))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
// limit size to avoid address overflows
|
||||
@@ -1887,18 +1923,80 @@ debug_strlcpy(char* to, const char* from, size_t size)
|
||||
// NOTE: Since strlcpy() determines the length of \a from, the source
|
||||
// address might still overflow.
|
||||
|
||||
debug_strlcpy_parameters parameters = {to, from, maxSize};
|
||||
// Try standard strlcpy() with fault handler, if the addresses can be
|
||||
// interpreted in the current address space.
|
||||
if ((IS_KERNEL_ADDRESS(from) && IS_KERNEL_ADDRESS(to))
|
||||
|| debug_is_debugged_team(teamID)) {
|
||||
debug_strlcpy_parameters parameters = {to, from, maxSize};
|
||||
|
||||
if (debug_call_with_fault_handler(gCPU[sDebuggerOnCPU].fault_jump_buffer,
|
||||
&debug_strlcpy_trampoline, ¶meters) != 0) {
|
||||
return B_BAD_ADDRESS;
|
||||
if (debug_call_with_fault_handler(
|
||||
gCPU[sDebuggerOnCPU].fault_jump_buffer,
|
||||
&debug_strlcpy_trampoline, ¶meters) == 0) {
|
||||
// If we hit the address overflow boundary, fail.
|
||||
if (parameters.result >= maxSize && maxSize < size)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
return parameters.result;
|
||||
}
|
||||
}
|
||||
|
||||
// If we hit the address overflow boundary, fail.
|
||||
if (parameters.result >= maxSize && maxSize < size)
|
||||
return B_BAD_ADDRESS;
|
||||
// Try harder. The pages of the respective memory could be unmapped but
|
||||
// still exist in a cache (the page daemon does that with inactive pages).
|
||||
size_t totalLength = 0;
|
||||
while (maxSize > 0) {
|
||||
char buffer[32];
|
||||
size_t toCopy = std::min(maxSize, sizeof(buffer));
|
||||
|
||||
return parameters.result;
|
||||
// restrict the size so we don't cross page boundaries
|
||||
if (((addr_t)from + toCopy) % B_PAGE_SIZE < toCopy)
|
||||
toCopy -= ((addr_t)from + toCopy) % B_PAGE_SIZE;
|
||||
if (((addr_t)to + toCopy) % B_PAGE_SIZE < toCopy)
|
||||
toCopy -= ((addr_t)to + toCopy) % B_PAGE_SIZE;
|
||||
|
||||
// copy the next part of the string from the source
|
||||
if (vm_debug_copy_page_memory(teamID, (void*)from, buffer, toCopy,
|
||||
false) != B_OK) {
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
// determine the length of the part and whether we've reached the end
|
||||
// of the string
|
||||
size_t length = strnlen(buffer, toCopy);
|
||||
bool endOfString = length < toCopy;
|
||||
|
||||
from = (const char*)from + toCopy;
|
||||
totalLength += length;
|
||||
maxSize -= length;
|
||||
|
||||
if (endOfString) {
|
||||
// only copy the actual string, including the terminating null
|
||||
toCopy = length + 1;
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
// We still have space left in the target buffer.
|
||||
if (size <= length) {
|
||||
// Not enough space for the complete part. Null-terminate it and
|
||||
// copy what we can.
|
||||
buffer[size - 1] = '\0';
|
||||
totalLength += length - size;
|
||||
toCopy = size;
|
||||
}
|
||||
|
||||
if (vm_debug_copy_page_memory(teamID, to, buffer, toCopy, true)
|
||||
!= B_OK) {
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
to = (char*)to + toCopy;
|
||||
size -= toCopy;
|
||||
}
|
||||
|
||||
if (endOfString)
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
|
||||
@@ -2060,6 +2158,22 @@ debug_get_debugged_thread()
|
||||
}
|
||||
|
||||
|
||||
/*! Returns whether the supplied team ID refers to the same team the currently
|
||||
debugged thread (debug_get_debugged_thread()) belongs to.
|
||||
Always returns \c true, if \c B_CURRENT_TEAM is given.
|
||||
*/
|
||||
bool
|
||||
debug_is_debugged_team(team_id teamID)
|
||||
{
|
||||
if (teamID == B_CURRENT_TEAM)
|
||||
return true;
|
||||
|
||||
struct thread* thread = debug_get_debugged_thread();
|
||||
return thread != NULL && thread->team != NULL
|
||||
&& thread->team->id == teamID;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
// userland syscalls
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
* Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
* Copyright 2006, Stephan Aßmus, superstippi@gmx.de
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
@@ -723,7 +723,7 @@ ExpressionParser::_ParseExpression(bool expectAssignment)
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug_memcpy(address, &buffer, size) != B_OK) {
|
||||
if (debug_memcpy(B_CURRENT_TEAM, address, &buffer, size) != B_OK) {
|
||||
snprintf(sTempBuffer, sizeof(sTempBuffer),
|
||||
"failed to write to address %p", address);
|
||||
parse_exception(sTempBuffer, position);
|
||||
@@ -1062,7 +1062,7 @@ ExpressionParser::_ParseDereference(void** _address, uint32* _size)
|
||||
|
||||
// read bytes from address into a tempory buffer
|
||||
uint64 buffer;
|
||||
if (debug_memcpy(&buffer, address, size) != B_OK) {
|
||||
if (debug_memcpy(B_CURRENT_TEAM, &buffer, address, size) != B_OK) {
|
||||
snprintf(sTempBuffer, sizeof(sTempBuffer),
|
||||
"failed to dereference address %p", address);
|
||||
parse_exception(sTempBuffer, starPosition);
|
||||
|
||||
@@ -296,9 +296,10 @@ gdb_parse_command(void)
|
||||
// for gdb may be trying to access an stray pointer
|
||||
// We copy the memory to a safe buffer using
|
||||
// the bulletproof debug_memcpy().
|
||||
if (debug_memcpy(sSafeMemory, (char*)address, len) < 0)
|
||||
if (debug_memcpy(B_CURRENT_TEAM, sSafeMemory, (char*)address, len)
|
||||
< 0) {
|
||||
gdb_reply("E02");
|
||||
else
|
||||
} else
|
||||
gdb_memreply(sSafeMemory, len);
|
||||
|
||||
break;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
@@ -1467,8 +1467,10 @@ public:
|
||||
if (!IS_USER_ADDRESS(address))
|
||||
return false;
|
||||
|
||||
if (debug_debugger_running())
|
||||
return debug_strlcpy(buffer, address, bufferSize) >= 0;
|
||||
if (debug_debugger_running()) {
|
||||
return debug_strlcpy(B_CURRENT_TEAM, buffer, address, bufferSize)
|
||||
>= 0;
|
||||
}
|
||||
return user_strlcpy(buffer, address, bufferSize) >= 0;
|
||||
}
|
||||
|
||||
@@ -1491,7 +1493,7 @@ UserSymbolLookup::_Read(const T* address, T& data)
|
||||
return false;
|
||||
|
||||
if (debug_debugger_running())
|
||||
return debug_memcpy(&data, address, sizeof(T)) == B_OK;
|
||||
return debug_memcpy(B_CURRENT_TEAM, &data, address, sizeof(T)) == B_OK;
|
||||
return user_memcpy(&data, address, sizeof(T)) == B_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -2640,9 +2640,11 @@ display_mem(int argc, char** argv)
|
||||
// string mode
|
||||
for (i = 0; true; i++) {
|
||||
char c;
|
||||
if (debug_memcpy(&c, (char*)copyAddress + i, 1) != B_OK
|
||||
|| c == '\0')
|
||||
if (debug_memcpy(B_CURRENT_TEAM, &c, (char*)copyAddress + i, 1)
|
||||
!= B_OK
|
||||
|| c == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
kprintf("\\n");
|
||||
@@ -2671,8 +2673,8 @@ display_mem(int argc, char** argv)
|
||||
|
||||
for (j = 0; j < displayed; j++) {
|
||||
char c;
|
||||
if (debug_memcpy(&c, (char*)copyAddress + i * itemSize + j,
|
||||
1) != B_OK) {
|
||||
if (debug_memcpy(B_CURRENT_TEAM, &c,
|
||||
(char*)copyAddress + i * itemSize + j, 1) != B_OK) {
|
||||
displayed = j;
|
||||
break;
|
||||
}
|
||||
@@ -2689,8 +2691,8 @@ display_mem(int argc, char** argv)
|
||||
kprintf(" ");
|
||||
}
|
||||
|
||||
if (debug_memcpy(&value, (uint8*)copyAddress + i * itemSize,
|
||||
itemSize) != B_OK) {
|
||||
if (debug_memcpy(B_CURRENT_TEAM, &value,
|
||||
(uint8*)copyAddress + i * itemSize, itemSize) != B_OK) {
|
||||
kprintf("read fault");
|
||||
break;
|
||||
}
|
||||
@@ -4658,6 +4660,93 @@ vm_memcpy_physical_page(addr_t to, addr_t from)
|
||||
}
|
||||
|
||||
|
||||
/*! Copies a range of memory directly from/to a page that might not be mapped
|
||||
at the moment.
|
||||
|
||||
For \a unsafeMemory the current mapping (if any is ignored). The function
|
||||
walks through the respective area's cache chain to find the physical page
|
||||
and copies from/to it directly.
|
||||
The memory range starting at \a unsafeMemory with a length of \a size bytes
|
||||
must not cross a page boundary.
|
||||
|
||||
\param teamID The team ID identifying the address space \a unsafeMemory is
|
||||
to be interpreted in. Ignored, if \a unsafeMemory is a kernel address
|
||||
(the kernel address space is assumed in this case). If \c B_CURRENT_TEAM
|
||||
is passed, the address space of the thread returned by
|
||||
debug_get_debugged_thread() is used.
|
||||
\param unsafeMemory The start of the unsafe memory range to be copied
|
||||
from/to.
|
||||
\param buffer A safely accessible kernel buffer to be copied from/to.
|
||||
\param size The number of bytes to be copied.
|
||||
\param copyToUnsafe If \c true, memory is copied from \a buffer to
|
||||
\a unsafeMemory, the other way around otherwise.
|
||||
*/
|
||||
status_t
|
||||
vm_debug_copy_page_memory(team_id teamID, void* unsafeMemory, void* buffer,
|
||||
size_t size, bool copyToUnsafe)
|
||||
{
|
||||
if (size > B_PAGE_SIZE
|
||||
|| ((addr_t)unsafeMemory + size) % B_PAGE_SIZE < size) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
// get the address space for the debugged thread
|
||||
VMAddressSpace* addressSpace;
|
||||
if (IS_KERNEL_ADDRESS(unsafeMemory)) {
|
||||
addressSpace = VMAddressSpace::Kernel();
|
||||
} else if (teamID == B_CURRENT_TEAM) {
|
||||
struct thread* thread = debug_get_debugged_thread();
|
||||
if (thread == NULL || thread->team == NULL)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
addressSpace = thread->team->address_space;
|
||||
} else
|
||||
addressSpace = VMAddressSpace::DebugGet(teamID);
|
||||
|
||||
if (addressSpace == NULL)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
// get the area
|
||||
VMArea* area = addressSpace->LookupArea((addr_t)unsafeMemory);
|
||||
if (area == NULL)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
// search the page
|
||||
off_t cacheOffset = (addr_t)unsafeMemory - area->Base()
|
||||
+ area->cache_offset;
|
||||
VMCache* cache = area->cache;
|
||||
vm_page* page = NULL;
|
||||
while (cache != NULL) {
|
||||
page = cache->DebugLookupPage(cacheOffset);
|
||||
if (page != NULL)
|
||||
break;
|
||||
|
||||
// Page not found in this cache -- if it is paged out, we must not try
|
||||
// to get it from lower caches.
|
||||
if (cache->DebugHasPage(cacheOffset))
|
||||
break;
|
||||
|
||||
cache = cache->source;
|
||||
}
|
||||
|
||||
if (page == NULL)
|
||||
return B_UNSUPPORTED;
|
||||
|
||||
// copy from/to physical memory
|
||||
addr_t physicalAddress = page->physical_page_number * B_PAGE_SIZE
|
||||
+ (addr_t)unsafeMemory % B_PAGE_SIZE;
|
||||
|
||||
if (copyToUnsafe) {
|
||||
if (page->Cache() != area->cache)
|
||||
return B_UNSUPPORTED;
|
||||
|
||||
return vm_memcpy_to_physical(physicalAddress, buffer, size, false);
|
||||
}
|
||||
|
||||
return vm_memcpy_from_physical(buffer, physicalAddress, size, false);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - kernel public API
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user