diff --git a/headers/private/kernel/vm/vm.h b/headers/private/kernel/vm/vm.h index ac78c7a25d..009eb6a343 100644 --- a/headers/private/kernel/vm/vm.h +++ b/headers/private/kernel/vm/vm.h @@ -144,6 +144,7 @@ status_t vm_put_physical_page_debug(addr_t vaddr, void* handle); void vm_get_info(system_info *info); uint32 vm_num_page_faults(void); off_t vm_available_memory(void); +off_t vm_available_memory_debug(void); off_t vm_available_not_needed_memory(void); off_t vm_available_not_needed_memory_debug(void); size_t vm_kernel_address_space_left(void); diff --git a/headers/private/kernel/vm/vm_priv.h b/headers/private/kernel/vm/vm_priv.h index 9091c60c81..0858a14073 100644 --- a/headers/private/kernel/vm/vm_priv.h +++ b/headers/private/kernel/vm/vm_priv.h @@ -31,6 +31,8 @@ status_t vm_page_fault(addr_t address, addr_t faultAddress, bool isWrite, bool isExecute, bool isUser, addr_t *newip); void vm_unreserve_memory(size_t bytes); status_t vm_try_reserve_memory(size_t bytes, int priority, bigtime_t timeout); + +void vm_debug_init(); status_t vm_daemon_init(void); const char *page_state_to_string(int state); diff --git a/src/system/kernel/vm/Jamfile b/src/system/kernel/vm/Jamfile index a646e719c4..004fb8fc89 100644 --- a/src/system/kernel/vm/Jamfile +++ b/src/system/kernel/vm/Jamfile @@ -9,6 +9,7 @@ UsePrivateHeaders [ FDirName kernel util ] ; KernelMergeObject kernel_vm.o : PageCacheLocker.cpp vm.cpp + vm_debug.cpp vm_page.cpp VMAddressSpace.cpp VMAddressSpaceLocking.cpp diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index cd445258a3..55b3e76ff0 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -256,19 +256,6 @@ static uint32 sPageFaults; static VMPhysicalPageMapper* sPhysicalPageMapper; -#if DEBUG_CACHE_LIST - -struct cache_info { - VMCache* cache; - addr_t page_count; - addr_t committed; -}; - -static const uint32 kCacheInfoTableCount = 100 * 1024; -static cache_info* sCacheInfoTable; - -#endif // DEBUG_CACHE_LIST - // function declarations static void delete_area(VMAddressSpace* addressSpace, VMArea* area, @@ -3292,717 +3279,6 @@ vm_remove_all_page_mappings_if_unaccessed(struct vm_page *page) } -static int -display_mem(int argc, char** argv) -{ - bool physical = false; - addr_t copyAddress; - int32 displayWidth; - int32 itemSize; - int32 num = -1; - addr_t address; - int i = 1, j; - - if (argc > 1 && argv[1][0] == '-') { - if (!strcmp(argv[1], "-p") || !strcmp(argv[1], "--physical")) { - physical = true; - i++; - } else - i = 99; - } - - if (argc < i + 1 || argc > i + 2) { - kprintf("usage: dl/dw/ds/db/string [-p|--physical]
[num]\n" - "\tdl - 8 bytes\n" - "\tdw - 4 bytes\n" - "\tds - 2 bytes\n" - "\tdb - 1 byte\n" - "\tstring - a whole string\n" - " -p or --physical only allows memory from a single page to be " - "displayed.\n"); - return 0; - } - - address = parse_expression(argv[i]); - - if (argc > i + 1) - num = parse_expression(argv[i + 1]); - - // build the format string - if (strcmp(argv[0], "db") == 0) { - itemSize = 1; - displayWidth = 16; - } else if (strcmp(argv[0], "ds") == 0) { - itemSize = 2; - displayWidth = 8; - } else if (strcmp(argv[0], "dw") == 0) { - itemSize = 4; - displayWidth = 4; - } else if (strcmp(argv[0], "dl") == 0) { - itemSize = 8; - displayWidth = 2; - } else if (strcmp(argv[0], "string") == 0) { - itemSize = 1; - displayWidth = -1; - } else { - kprintf("display_mem called in an invalid way!\n"); - return 0; - } - - if (num <= 0) - num = displayWidth; - - void* physicalPageHandle = NULL; - - if (physical) { - int32 offset = address & (B_PAGE_SIZE - 1); - if (num * itemSize + offset > B_PAGE_SIZE) { - num = (B_PAGE_SIZE - offset) / itemSize; - kprintf("NOTE: number of bytes has been cut to page size\n"); - } - - address = ROUNDDOWN(address, B_PAGE_SIZE); - - if (vm_get_physical_page_debug(address, ©Address, - &physicalPageHandle) != B_OK) { - kprintf("getting the hardware page failed."); - return 0; - } - - address += offset; - copyAddress += offset; - } else - copyAddress = address; - - if (!strcmp(argv[0], "string")) { - kprintf("%p \"", (char*)copyAddress); - - // string mode - for (i = 0; true; i++) { - char c; - if (debug_memcpy(B_CURRENT_TEAM, &c, (char*)copyAddress + i, 1) - != B_OK - || c == '\0') { - break; - } - - if (c == '\n') - kprintf("\\n"); - else if (c == '\t') - kprintf("\\t"); - else { - if (!isprint(c)) - c = '.'; - - kprintf("%c", c); - } - } - - kprintf("\"\n"); - } else { - // number mode - for (i = 0; i < num; i++) { - uint64 value; - - if ((i % displayWidth) == 0) { - int32 displayed = min_c(displayWidth, (num-i)) * itemSize; - if (i != 0) - kprintf("\n"); - - kprintf("[0x%lx] ", address + i * itemSize); - - for (j = 0; j < displayed; j++) { - char c; - if (debug_memcpy(B_CURRENT_TEAM, &c, - (char*)copyAddress + i * itemSize + j, 1) != B_OK) { - displayed = j; - break; - } - if (!isprint(c)) - c = '.'; - - kprintf("%c", c); - } - if (num > displayWidth) { - // make sure the spacing in the last line is correct - for (j = displayed; j < displayWidth * itemSize; j++) - kprintf(" "); - } - kprintf(" "); - } - - if (debug_memcpy(B_CURRENT_TEAM, &value, - (uint8*)copyAddress + i * itemSize, itemSize) != B_OK) { - kprintf("read fault"); - break; - } - - switch (itemSize) { - case 1: - kprintf(" %02" B_PRIx8, *(uint8*)&value); - break; - case 2: - kprintf(" %04" B_PRIx16, *(uint16*)&value); - break; - case 4: - kprintf(" %08" B_PRIx32, *(uint32*)&value); - break; - case 8: - kprintf(" %016" B_PRIx64, *(uint64*)&value); - break; - } - } - - kprintf("\n"); - } - - if (physical) { - copyAddress = ROUNDDOWN(copyAddress, B_PAGE_SIZE); - vm_put_physical_page_debug(copyAddress, physicalPageHandle); - } - return 0; -} - - -static void -dump_cache_tree_recursively(VMCache* cache, int level, - VMCache* highlightCache) -{ - // print this cache - for (int i = 0; i < level; i++) - kprintf(" "); - if (cache == highlightCache) - kprintf("%p <--\n", cache); - else - kprintf("%p\n", cache); - - // recursively print its consumers - for (VMCache::ConsumerList::Iterator it = cache->consumers.GetIterator(); - VMCache* consumer = it.Next();) { - dump_cache_tree_recursively(consumer, level + 1, highlightCache); - } -} - - -static int -dump_cache_tree(int argc, char** argv) -{ - if (argc != 2 || !strcmp(argv[1], "--help")) { - kprintf("usage: %s
\n", argv[0]); - return 0; - } - - addr_t address = parse_expression(argv[1]); - if (address == 0) - return 0; - - VMCache* cache = (VMCache*)address; - VMCache* root = cache; - - // find the root cache (the transitive source) - while (root->source != NULL) - root = root->source; - - dump_cache_tree_recursively(root, 0, cache); - - return 0; -} - - -const char* -vm_cache_type_to_string(int32 type) -{ - switch (type) { - case CACHE_TYPE_RAM: - return "RAM"; - case CACHE_TYPE_DEVICE: - return "device"; - case CACHE_TYPE_VNODE: - return "vnode"; - case CACHE_TYPE_NULL: - return "null"; - - default: - return "unknown"; - } -} - - -#if DEBUG_CACHE_LIST - -static void -update_cache_info_recursively(VMCache* cache, cache_info& info) -{ - info.page_count += cache->page_count; - if (cache->type == CACHE_TYPE_RAM) - info.committed += cache->committed_size; - - // recurse - for (VMCache::ConsumerList::Iterator it = cache->consumers.GetIterator(); - VMCache* consumer = it.Next();) { - update_cache_info_recursively(consumer, info); - } -} - - -static int -cache_info_compare_page_count(const void* _a, const void* _b) -{ - const cache_info* a = (const cache_info*)_a; - const cache_info* b = (const cache_info*)_b; - if (a->page_count == b->page_count) - return 0; - return a->page_count < b->page_count ? 1 : -1; -} - - -static int -cache_info_compare_committed(const void* _a, const void* _b) -{ - const cache_info* a = (const cache_info*)_a; - const cache_info* b = (const cache_info*)_b; - if (a->committed == b->committed) - return 0; - return a->committed < b->committed ? 1 : -1; -} - - -static void -dump_caches_recursively(VMCache* cache, cache_info& info, int level) -{ - for (int i = 0; i < level; i++) - kprintf(" "); - - kprintf("%p: type: %s, base: %" B_PRIdOFF ", size: %" B_PRIdOFF ", " - "pages: %" B_PRIu32, cache, vm_cache_type_to_string(cache->type), - cache->virtual_base, cache->virtual_end, cache->page_count); - - if (level == 0) - kprintf("/%lu", info.page_count); - - if (cache->type == CACHE_TYPE_RAM || (level == 0 && info.committed > 0)) { - kprintf(", committed: %" B_PRIdOFF, cache->committed_size); - - if (level == 0) - kprintf("/%lu", info.committed); - } - - // areas - if (cache->areas != NULL) { - VMArea* area = cache->areas; - kprintf(", areas: %" B_PRId32 " (%s, team: %" B_PRId32 ")", area->id, - area->name, area->address_space->ID()); - - while (area->cache_next != NULL) { - area = area->cache_next; - kprintf(", %" B_PRId32, area->id); - } - } - - kputs("\n"); - - // recurse - for (VMCache::ConsumerList::Iterator it = cache->consumers.GetIterator(); - VMCache* consumer = it.Next();) { - dump_caches_recursively(consumer, info, level + 1); - } -} - - -static int -dump_caches(int argc, char** argv) -{ - if (sCacheInfoTable == NULL) { - kprintf("No cache info table!\n"); - return 0; - } - - bool sortByPageCount = true; - - for (int32 i = 1; i < argc; i++) { - if (strcmp(argv[i], "-c") == 0) { - sortByPageCount = false; - } else { - print_debugger_command_usage(argv[0]); - return 0; - } - } - - uint32 totalCount = 0; - uint32 rootCount = 0; - off_t totalCommitted = 0; - page_num_t totalPages = 0; - - VMCache* cache = gDebugCacheList; - while (cache) { - totalCount++; - if (cache->source == NULL) { - cache_info stackInfo; - cache_info& info = rootCount < kCacheInfoTableCount - ? sCacheInfoTable[rootCount] : stackInfo; - rootCount++; - info.cache = cache; - info.page_count = 0; - info.committed = 0; - update_cache_info_recursively(cache, info); - totalCommitted += info.committed; - totalPages += info.page_count; - } - - cache = cache->debug_next; - } - - kprintf("total committed memory: %" B_PRIdOFF ", total used pages: %" - B_PRIuPHYSADDR "\n", totalCommitted, totalPages); - kprintf("%" B_PRIu32 " caches (%" B_PRIu32 " root caches), sorted by %s " - "per cache tree...\n\n", totalCount, rootCount, sortByPageCount ? - "page count" : "committed size"); - - if (rootCount > kCacheInfoTableCount) { - kprintf("Cache info table too small! Can't sort and print caches!\n"); - return 0; - } - - qsort(sCacheInfoTable, rootCount, sizeof(cache_info), - sortByPageCount - ? &cache_info_compare_page_count - : &cache_info_compare_committed); - - for (uint32 i = 0; i < rootCount; i++) { - cache_info& info = sCacheInfoTable[i]; - dump_caches_recursively(info.cache, info, 0); - } - - return 0; -} - -#endif // DEBUG_CACHE_LIST - - -static int -dump_cache(int argc, char** argv) -{ - VMCache* cache; - bool showPages = false; - int i = 1; - - if (argc < 2 || !strcmp(argv[1], "--help")) { - kprintf("usage: %s [-ps]
\n" - " if -p is specified, all pages are shown, if -s is used\n" - " only the cache info is shown respectively.\n", argv[0]); - return 0; - } - while (argv[i][0] == '-') { - char* arg = argv[i] + 1; - while (arg[0]) { - if (arg[0] == 'p') - showPages = true; - arg++; - } - i++; - } - if (argv[i] == NULL) { - kprintf("%s: invalid argument, pass address\n", argv[0]); - return 0; - } - - addr_t address = parse_expression(argv[i]); - if (address == 0) - return 0; - - cache = (VMCache*)address; - - cache->Dump(showPages); - - set_debug_variable("_sourceCache", (addr_t)cache->source); - - return 0; -} - - -static void -dump_area_struct(VMArea* area, bool mappings) -{ - kprintf("AREA: %p\n", area); - kprintf("name:\t\t'%s'\n", area->name); - kprintf("owner:\t\t0x%" B_PRIx32 "\n", area->address_space->ID()); - kprintf("id:\t\t0x%" B_PRIx32 "\n", area->id); - kprintf("base:\t\t0x%lx\n", area->Base()); - kprintf("size:\t\t0x%lx\n", area->Size()); - kprintf("protection:\t0x%" B_PRIx32 "\n", area->protection); - kprintf("page_protection:%p\n", area->page_protections); - kprintf("wiring:\t\t0x%x\n", area->wiring); - kprintf("memory_type:\t%#" B_PRIx32 "\n", area->MemoryType()); - kprintf("cache:\t\t%p\n", area->cache); - kprintf("cache_type:\t%s\n", vm_cache_type_to_string(area->cache_type)); - kprintf("cache_offset:\t0x%" B_PRIx64 "\n", area->cache_offset); - kprintf("cache_next:\t%p\n", area->cache_next); - kprintf("cache_prev:\t%p\n", area->cache_prev); - - VMAreaMappings::Iterator iterator = area->mappings.GetIterator(); - if (mappings) { - kprintf("page mappings:\n"); - while (iterator.HasNext()) { - vm_page_mapping* mapping = iterator.Next(); - kprintf(" %p", mapping->page); - } - kprintf("\n"); - } else { - uint32 count = 0; - while (iterator.Next() != NULL) { - count++; - } - kprintf("page mappings:\t%" B_PRIu32 "\n", count); - } -} - - -static int -dump_area(int argc, char** argv) -{ - bool mappings = false; - bool found = false; - int32 index = 1; - VMArea* area; - addr_t num; - - if (argc < 2 || !strcmp(argv[1], "--help")) { - kprintf("usage: area [-m] [id|contains|address|name] \n" - "All areas matching either id/address/name are listed. You can\n" - "force to check only a specific item by prefixing the specifier\n" - "with the id/contains/address/name keywords.\n" - "-m shows the area's mappings as well.\n"); - return 0; - } - - if (!strcmp(argv[1], "-m")) { - mappings = true; - index++; - } - - int32 mode = 0xf; - if (!strcmp(argv[index], "id")) - mode = 1; - else if (!strcmp(argv[index], "contains")) - mode = 2; - else if (!strcmp(argv[index], "name")) - mode = 4; - else if (!strcmp(argv[index], "address")) - mode = 0; - if (mode != 0xf) - index++; - - if (index >= argc) { - kprintf("No area specifier given.\n"); - return 0; - } - - num = parse_expression(argv[index]); - - if (mode == 0) { - dump_area_struct((struct VMArea*)num, mappings); - } else { - // walk through the area list, looking for the arguments as a name - - VMAreasTree::Iterator it = VMAreas::GetIterator(); - while ((area = it.Next()) != NULL) { - if (((mode & 4) != 0 - && !strcmp(argv[index], area->name)) - || (num != 0 && (((mode & 1) != 0 && (addr_t)area->id == num) - || (((mode & 2) != 0 && area->Base() <= num - && area->Base() + area->Size() > num))))) { - dump_area_struct(area, mappings); - found = true; - } - } - - if (!found) - kprintf("could not find area %s (%ld)\n", argv[index], num); - } - - return 0; -} - - -static int -dump_area_list(int argc, char** argv) -{ - VMArea* area; - const char* name = NULL; - int32 id = 0; - - if (argc > 1) { - id = parse_expression(argv[1]); - if (id == 0) - name = argv[1]; - } - - kprintf("%-*s id %-*s %-*sprotect lock name\n", - B_PRINTF_POINTER_WIDTH, "addr", B_PRINTF_POINTER_WIDTH, "base", - B_PRINTF_POINTER_WIDTH, "size"); - - VMAreasTree::Iterator it = VMAreas::GetIterator(); - while ((area = it.Next()) != NULL) { - if ((id != 0 && area->address_space->ID() != id) - || (name != NULL && strstr(area->name, name) == NULL)) - continue; - - kprintf("%p %5" B_PRIx32 " %p %p %4" B_PRIx32 " %4d %s\n", area, - area->id, (void*)area->Base(), (void*)area->Size(), - area->protection, area->wiring, area->name); - } - return 0; -} - - -static int -dump_available_memory(int argc, char** argv) -{ - kprintf("Available memory: %" B_PRIdOFF "/%" B_PRIuPHYSADDR " bytes\n", - sAvailableMemory, (phys_addr_t)vm_page_num_pages() * B_PAGE_SIZE); - return 0; -} - - -static int -dump_mapping_info(int argc, char** argv) -{ - bool reverseLookup = false; - bool pageLookup = false; - - int argi = 1; - for (; argi < argc && argv[argi][0] == '-'; argi++) { - const char* arg = argv[argi]; - if (strcmp(arg, "-r") == 0) { - reverseLookup = true; - } else if (strcmp(arg, "-p") == 0) { - reverseLookup = true; - pageLookup = true; - } else { - print_debugger_command_usage(argv[0]); - return 0; - } - } - - // We need at least one argument, the address. Optionally a thread ID can be - // specified. - if (argi >= argc || argi + 2 < argc) { - print_debugger_command_usage(argv[0]); - return 0; - } - - uint64 addressValue; - if (!evaluate_debug_expression(argv[argi++], &addressValue, false)) - return 0; - - Team* team = NULL; - if (argi < argc) { - uint64 threadID; - if (!evaluate_debug_expression(argv[argi++], &threadID, false)) - return 0; - - Thread* thread = Thread::GetDebug(threadID); - if (thread == NULL) { - kprintf("Invalid thread/team ID \"%s\"\n", argv[argi - 1]); - return 0; - } - - team = thread->team; - } - - if (reverseLookup) { - phys_addr_t physicalAddress; - if (pageLookup) { - vm_page* page = (vm_page*)(addr_t)addressValue; - physicalAddress = page->physical_page_number * B_PAGE_SIZE; - } else { - physicalAddress = (phys_addr_t)addressValue; - physicalAddress -= physicalAddress % B_PAGE_SIZE; - } - - kprintf(" Team Virtual Address Area\n"); - kprintf("--------------------------------------\n"); - - struct Callback : VMTranslationMap::ReverseMappingInfoCallback { - Callback() - : - fAddressSpace(NULL) - { - } - - void SetAddressSpace(VMAddressSpace* addressSpace) - { - fAddressSpace = addressSpace; - } - - virtual bool HandleVirtualAddress(addr_t virtualAddress) - { - kprintf("%8" B_PRId32 " %#18" B_PRIxADDR, fAddressSpace->ID(), - virtualAddress); - if (VMArea* area = fAddressSpace->LookupArea(virtualAddress)) - kprintf(" %8" B_PRId32 " %s\n", area->id, area->name); - else - kprintf("\n"); - return false; - } - - private: - VMAddressSpace* fAddressSpace; - } callback; - - if (team != NULL) { - // team specified -- get its address space - VMAddressSpace* addressSpace = team->address_space; - if (addressSpace == NULL) { - kprintf("Failed to get address space!\n"); - return 0; - } - - callback.SetAddressSpace(addressSpace); - addressSpace->TranslationMap()->DebugGetReverseMappingInfo( - physicalAddress, callback); - } else { - // no team specified -- iterate through all address spaces - for (VMAddressSpace* addressSpace = VMAddressSpace::DebugFirst(); - addressSpace != NULL; - addressSpace = VMAddressSpace::DebugNext(addressSpace)) { - callback.SetAddressSpace(addressSpace); - addressSpace->TranslationMap()->DebugGetReverseMappingInfo( - physicalAddress, callback); - } - } - } else { - // get the address space - addr_t virtualAddress = (addr_t)addressValue; - virtualAddress -= virtualAddress % B_PAGE_SIZE; - VMAddressSpace* addressSpace; - if (IS_KERNEL_ADDRESS(virtualAddress)) { - addressSpace = VMAddressSpace::Kernel(); - } else if (team != NULL) { - addressSpace = team->address_space; - } else { - Thread* thread = debug_get_debugged_thread(); - if (thread == NULL || thread->team == NULL) { - kprintf("Failed to get team!\n"); - return 0; - } - - addressSpace = thread->team->address_space; - } - - if (addressSpace == NULL) { - kprintf("Failed to get address space!\n"); - return 0; - } - - // let the translation map implementation do the job - addressSpace->TranslationMap()->DebugPrintMappingInfo(virtualAddress); - } - - return 0; -} - - /*! Deletes all areas and reserved regions in the given address space. The caller must ensure that none of the areas has any wired ranges. @@ -4620,59 +3896,7 @@ vm_init(kernel_args* args) create_page_mappings_object_caches(); -#if DEBUG_CACHE_LIST - if (vm_page_num_free_pages() >= 200 * 1024 * 1024 / B_PAGE_SIZE) { - virtual_address_restrictions virtualRestrictions = {}; - virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS; - physical_address_restrictions physicalRestrictions = {}; - create_area_etc(VMAddressSpace::KernelID(), "cache info table", - ROUNDUP(kCacheInfoTableCount * sizeof(cache_info), B_PAGE_SIZE), - B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, - CREATE_AREA_DONT_WAIT, 0, &virtualRestrictions, - &physicalRestrictions, (void**)&sCacheInfoTable); - } -#endif // DEBUG_CACHE_LIST - - // add some debugger commands - add_debugger_command("areas", &dump_area_list, "Dump a list of all areas"); - add_debugger_command("area", &dump_area, - "Dump info about a particular area"); - add_debugger_command("cache", &dump_cache, "Dump VMCache"); - add_debugger_command("cache_tree", &dump_cache_tree, "Dump VMCache tree"); -#if DEBUG_CACHE_LIST - if (sCacheInfoTable != NULL) { - add_debugger_command_etc("caches", &dump_caches, - "List all VMCache trees", - "[ \"-c\" ]\n" - "All cache trees are listed sorted in decreasing order by number " - "of\n" - "used pages or, if \"-c\" is specified, by size of committed " - "memory.\n", - 0); - } -#endif - add_debugger_command("avail", &dump_available_memory, - "Dump available memory"); - add_debugger_command("dl", &display_mem, "dump memory long words (64-bit)"); - add_debugger_command("dw", &display_mem, "dump memory words (32-bit)"); - add_debugger_command("ds", &display_mem, "dump memory shorts (16-bit)"); - add_debugger_command("db", &display_mem, "dump memory bytes (8-bit)"); - add_debugger_command("string", &display_mem, "dump strings"); - - add_debugger_command_etc("mapping", &dump_mapping_info, - "Print address mapping information", - "[ \"-r\" | \"-p\" ]
[ ]\n" - "Prints low-level page mapping information for a given address. If\n" - "neither \"-r\" nor \"-p\" are specified,
is a virtual\n" - "address that is looked up in the translation map of the current\n" - "team, respectively the team specified by thread ID . If\n" - "\"-r\" is specified,
is a physical address that is\n" - "searched in the translation map of all teams, respectively the team\n" - "specified by thread ID . If \"-p\" is specified,\n" - "
is the address of a vm_page structure. The behavior is\n" - "equivalent to specifying \"-r\" with the physical address of that\n" - "page.\n", - 0); + vm_debug_init(); TRACE(("vm_init: exit\n")); @@ -5354,6 +4578,16 @@ vm_available_memory(void) } +/*! Like vm_available_memory(), but only for use in the kernel + debugger. +*/ +off_t +vm_available_memory_debug(void) +{ + return sAvailableMemory; +} + + off_t vm_available_not_needed_memory(void) { @@ -5710,93 +4944,6 @@ vm_memcpy_physical_page(phys_addr_t to, phys_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 || ROUNDDOWN((addr_t)unsafeMemory, B_PAGE_SIZE) - != ROUNDDOWN((addr_t)unsafeMemory + size - 1, B_PAGE_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) { - 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 - phys_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); -} - - /** Validate that a memory range is either fully in kernel space, or fully in * userspace */ static inline bool diff --git a/src/system/kernel/vm/vm_debug.cpp b/src/system/kernel/vm/vm_debug.cpp new file mode 100644 index 0000000000..6d8f19cabc --- /dev/null +++ b/src/system/kernel/vm/vm_debug.cpp @@ -0,0 +1,890 @@ +/* + * Copyright 2024, Haiku, Inc. All rights reserved. + * Copyright 2010-2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + * + * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ + + +#include + +#include +#include +#include +#include +#include +#include + + +#if DEBUG_CACHE_LIST + +struct cache_info { + VMCache* cache; + addr_t page_count; + addr_t committed; +}; + +static const uint32 kCacheInfoTableCount = 100 * 1024; +static cache_info* sCacheInfoTable; + +#endif // DEBUG_CACHE_LIST + + +static int +display_mem(int argc, char** argv) +{ + bool physical = false; + addr_t copyAddress; + int32 displayWidth; + int32 itemSize; + int32 num = -1; + addr_t address; + int i = 1, j; + + if (argc > 1 && argv[1][0] == '-') { + if (!strcmp(argv[1], "-p") || !strcmp(argv[1], "--physical")) { + physical = true; + i++; + } else + i = 99; + } + + if (argc < i + 1 || argc > i + 2) { + kprintf("usage: dl/dw/ds/db/string [-p|--physical]
[num]\n" + "\tdl - 8 bytes\n" + "\tdw - 4 bytes\n" + "\tds - 2 bytes\n" + "\tdb - 1 byte\n" + "\tstring - a whole string\n" + " -p or --physical only allows memory from a single page to be " + "displayed.\n"); + return 0; + } + + address = parse_expression(argv[i]); + + if (argc > i + 1) + num = parse_expression(argv[i + 1]); + + // build the format string + if (strcmp(argv[0], "db") == 0) { + itemSize = 1; + displayWidth = 16; + } else if (strcmp(argv[0], "ds") == 0) { + itemSize = 2; + displayWidth = 8; + } else if (strcmp(argv[0], "dw") == 0) { + itemSize = 4; + displayWidth = 4; + } else if (strcmp(argv[0], "dl") == 0) { + itemSize = 8; + displayWidth = 2; + } else if (strcmp(argv[0], "string") == 0) { + itemSize = 1; + displayWidth = -1; + } else { + kprintf("display_mem called in an invalid way!\n"); + return 0; + } + + if (num <= 0) + num = displayWidth; + + void* physicalPageHandle = NULL; + + if (physical) { + int32 offset = address & (B_PAGE_SIZE - 1); + if (num * itemSize + offset > B_PAGE_SIZE) { + num = (B_PAGE_SIZE - offset) / itemSize; + kprintf("NOTE: number of bytes has been cut to page size\n"); + } + + address = ROUNDDOWN(address, B_PAGE_SIZE); + + if (vm_get_physical_page_debug(address, ©Address, + &physicalPageHandle) != B_OK) { + kprintf("getting the hardware page failed."); + return 0; + } + + address += offset; + copyAddress += offset; + } else + copyAddress = address; + + if (!strcmp(argv[0], "string")) { + kprintf("%p \"", (char*)copyAddress); + + // string mode + for (i = 0; true; i++) { + char c; + if (debug_memcpy(B_CURRENT_TEAM, &c, (char*)copyAddress + i, 1) + != B_OK + || c == '\0') { + break; + } + + if (c == '\n') + kprintf("\\n"); + else if (c == '\t') + kprintf("\\t"); + else { + if (!isprint(c)) + c = '.'; + + kprintf("%c", c); + } + } + + kprintf("\"\n"); + } else { + // number mode + for (i = 0; i < num; i++) { + uint64 value; + + if ((i % displayWidth) == 0) { + int32 displayed = min_c(displayWidth, (num-i)) * itemSize; + if (i != 0) + kprintf("\n"); + + kprintf("[0x%lx] ", address + i * itemSize); + + for (j = 0; j < displayed; j++) { + char c; + if (debug_memcpy(B_CURRENT_TEAM, &c, + (char*)copyAddress + i * itemSize + j, 1) != B_OK) { + displayed = j; + break; + } + if (!isprint(c)) + c = '.'; + + kprintf("%c", c); + } + if (num > displayWidth) { + // make sure the spacing in the last line is correct + for (j = displayed; j < displayWidth * itemSize; j++) + kprintf(" "); + } + kprintf(" "); + } + + if (debug_memcpy(B_CURRENT_TEAM, &value, + (uint8*)copyAddress + i * itemSize, itemSize) != B_OK) { + kprintf("read fault"); + break; + } + + switch (itemSize) { + case 1: + kprintf(" %02" B_PRIx8, *(uint8*)&value); + break; + case 2: + kprintf(" %04" B_PRIx16, *(uint16*)&value); + break; + case 4: + kprintf(" %08" B_PRIx32, *(uint32*)&value); + break; + case 8: + kprintf(" %016" B_PRIx64, *(uint64*)&value); + break; + } + } + + kprintf("\n"); + } + + if (physical) { + copyAddress = ROUNDDOWN(copyAddress, B_PAGE_SIZE); + vm_put_physical_page_debug(copyAddress, physicalPageHandle); + } + return 0; +} + + +static void +dump_cache_tree_recursively(VMCache* cache, int level, + VMCache* highlightCache) +{ + // print this cache + for (int i = 0; i < level; i++) + kprintf(" "); + if (cache == highlightCache) + kprintf("%p <--\n", cache); + else + kprintf("%p\n", cache); + + // recursively print its consumers + for (VMCache::ConsumerList::Iterator it = cache->consumers.GetIterator(); + VMCache* consumer = it.Next();) { + dump_cache_tree_recursively(consumer, level + 1, highlightCache); + } +} + + +static int +dump_cache_tree(int argc, char** argv) +{ + if (argc != 2 || !strcmp(argv[1], "--help")) { + kprintf("usage: %s
\n", argv[0]); + return 0; + } + + addr_t address = parse_expression(argv[1]); + if (address == 0) + return 0; + + VMCache* cache = (VMCache*)address; + VMCache* root = cache; + + // find the root cache (the transitive source) + while (root->source != NULL) + root = root->source; + + dump_cache_tree_recursively(root, 0, cache); + + return 0; +} + + +const char* +vm_cache_type_to_string(int32 type) +{ + switch (type) { + case CACHE_TYPE_RAM: + return "RAM"; + case CACHE_TYPE_DEVICE: + return "device"; + case CACHE_TYPE_VNODE: + return "vnode"; + case CACHE_TYPE_NULL: + return "null"; + + default: + return "unknown"; + } +} + + +#if DEBUG_CACHE_LIST + +static void +update_cache_info_recursively(VMCache* cache, cache_info& info) +{ + info.page_count += cache->page_count; + if (cache->type == CACHE_TYPE_RAM) + info.committed += cache->committed_size; + + // recurse + for (VMCache::ConsumerList::Iterator it = cache->consumers.GetIterator(); + VMCache* consumer = it.Next();) { + update_cache_info_recursively(consumer, info); + } +} + + +static int +cache_info_compare_page_count(const void* _a, const void* _b) +{ + const cache_info* a = (const cache_info*)_a; + const cache_info* b = (const cache_info*)_b; + if (a->page_count == b->page_count) + return 0; + return a->page_count < b->page_count ? 1 : -1; +} + + +static int +cache_info_compare_committed(const void* _a, const void* _b) +{ + const cache_info* a = (const cache_info*)_a; + const cache_info* b = (const cache_info*)_b; + if (a->committed == b->committed) + return 0; + return a->committed < b->committed ? 1 : -1; +} + + +static void +dump_caches_recursively(VMCache* cache, cache_info& info, int level) +{ + for (int i = 0; i < level; i++) + kprintf(" "); + + kprintf("%p: type: %s, base: %" B_PRIdOFF ", size: %" B_PRIdOFF ", " + "pages: %" B_PRIu32, cache, vm_cache_type_to_string(cache->type), + cache->virtual_base, cache->virtual_end, cache->page_count); + + if (level == 0) + kprintf("/%lu", info.page_count); + + if (cache->type == CACHE_TYPE_RAM || (level == 0 && info.committed > 0)) { + kprintf(", committed: %" B_PRIdOFF, cache->committed_size); + + if (level == 0) + kprintf("/%lu", info.committed); + } + + // areas + if (cache->areas != NULL) { + VMArea* area = cache->areas; + kprintf(", areas: %" B_PRId32 " (%s, team: %" B_PRId32 ")", area->id, + area->name, area->address_space->ID()); + + while (area->cache_next != NULL) { + area = area->cache_next; + kprintf(", %" B_PRId32, area->id); + } + } + + kputs("\n"); + + // recurse + for (VMCache::ConsumerList::Iterator it = cache->consumers.GetIterator(); + VMCache* consumer = it.Next();) { + dump_caches_recursively(consumer, info, level + 1); + } +} + + +static int +dump_caches(int argc, char** argv) +{ + if (sCacheInfoTable == NULL) { + kprintf("No cache info table!\n"); + return 0; + } + + bool sortByPageCount = true; + + for (int32 i = 1; i < argc; i++) { + if (strcmp(argv[i], "-c") == 0) { + sortByPageCount = false; + } else { + print_debugger_command_usage(argv[0]); + return 0; + } + } + + uint32 totalCount = 0; + uint32 rootCount = 0; + off_t totalCommitted = 0; + page_num_t totalPages = 0; + + VMCache* cache = gDebugCacheList; + while (cache) { + totalCount++; + if (cache->source == NULL) { + cache_info stackInfo; + cache_info& info = rootCount < kCacheInfoTableCount + ? sCacheInfoTable[rootCount] : stackInfo; + rootCount++; + info.cache = cache; + info.page_count = 0; + info.committed = 0; + update_cache_info_recursively(cache, info); + totalCommitted += info.committed; + totalPages += info.page_count; + } + + cache = cache->debug_next; + } + + kprintf("total committed memory: %" B_PRIdOFF ", total used pages: %" + B_PRIuPHYSADDR "\n", totalCommitted, totalPages); + kprintf("%" B_PRIu32 " caches (%" B_PRIu32 " root caches), sorted by %s " + "per cache tree...\n\n", totalCount, rootCount, sortByPageCount ? + "page count" : "committed size"); + + if (rootCount > kCacheInfoTableCount) { + kprintf("Cache info table too small! Can't sort and print caches!\n"); + return 0; + } + + qsort(sCacheInfoTable, rootCount, sizeof(cache_info), + sortByPageCount + ? &cache_info_compare_page_count + : &cache_info_compare_committed); + + for (uint32 i = 0; i < rootCount; i++) { + cache_info& info = sCacheInfoTable[i]; + dump_caches_recursively(info.cache, info, 0); + } + + return 0; +} + +#endif // DEBUG_CACHE_LIST + + +static int +dump_cache(int argc, char** argv) +{ + VMCache* cache; + bool showPages = false; + int i = 1; + + if (argc < 2 || !strcmp(argv[1], "--help")) { + kprintf("usage: %s [-ps]
\n" + " if -p is specified, all pages are shown, if -s is used\n" + " only the cache info is shown respectively.\n", argv[0]); + return 0; + } + while (argv[i][0] == '-') { + char* arg = argv[i] + 1; + while (arg[0]) { + if (arg[0] == 'p') + showPages = true; + arg++; + } + i++; + } + if (argv[i] == NULL) { + kprintf("%s: invalid argument, pass address\n", argv[0]); + return 0; + } + + addr_t address = parse_expression(argv[i]); + if (address == 0) + return 0; + + cache = (VMCache*)address; + + cache->Dump(showPages); + + set_debug_variable("_sourceCache", (addr_t)cache->source); + + return 0; +} + + +static void +dump_area_struct(VMArea* area, bool mappings) +{ + kprintf("AREA: %p\n", area); + kprintf("name:\t\t'%s'\n", area->name); + kprintf("owner:\t\t0x%" B_PRIx32 "\n", area->address_space->ID()); + kprintf("id:\t\t0x%" B_PRIx32 "\n", area->id); + kprintf("base:\t\t0x%lx\n", area->Base()); + kprintf("size:\t\t0x%lx\n", area->Size()); + kprintf("protection:\t0x%" B_PRIx32 "\n", area->protection); + kprintf("page_protection:%p\n", area->page_protections); + kprintf("wiring:\t\t0x%x\n", area->wiring); + kprintf("memory_type:\t%#" B_PRIx32 "\n", area->MemoryType()); + kprintf("cache:\t\t%p\n", area->cache); + kprintf("cache_type:\t%s\n", vm_cache_type_to_string(area->cache_type)); + kprintf("cache_offset:\t0x%" B_PRIx64 "\n", area->cache_offset); + kprintf("cache_next:\t%p\n", area->cache_next); + kprintf("cache_prev:\t%p\n", area->cache_prev); + + VMAreaMappings::Iterator iterator = area->mappings.GetIterator(); + if (mappings) { + kprintf("page mappings:\n"); + while (iterator.HasNext()) { + vm_page_mapping* mapping = iterator.Next(); + kprintf(" %p", mapping->page); + } + kprintf("\n"); + } else { + uint32 count = 0; + while (iterator.Next() != NULL) { + count++; + } + kprintf("page mappings:\t%" B_PRIu32 "\n", count); + } +} + + +static int +dump_area(int argc, char** argv) +{ + bool mappings = false; + bool found = false; + int32 index = 1; + VMArea* area; + addr_t num; + + if (argc < 2 || !strcmp(argv[1], "--help")) { + kprintf("usage: area [-m] [id|contains|address|name] \n" + "All areas matching either id/address/name are listed. You can\n" + "force to check only a specific item by prefixing the specifier\n" + "with the id/contains/address/name keywords.\n" + "-m shows the area's mappings as well.\n"); + return 0; + } + + if (!strcmp(argv[1], "-m")) { + mappings = true; + index++; + } + + int32 mode = 0xf; + if (!strcmp(argv[index], "id")) + mode = 1; + else if (!strcmp(argv[index], "contains")) + mode = 2; + else if (!strcmp(argv[index], "name")) + mode = 4; + else if (!strcmp(argv[index], "address")) + mode = 0; + if (mode != 0xf) + index++; + + if (index >= argc) { + kprintf("No area specifier given.\n"); + return 0; + } + + num = parse_expression(argv[index]); + + if (mode == 0) { + dump_area_struct((struct VMArea*)num, mappings); + } else { + // walk through the area list, looking for the arguments as a name + + VMAreasTree::Iterator it = VMAreas::GetIterator(); + while ((area = it.Next()) != NULL) { + if (((mode & 4) != 0 + && !strcmp(argv[index], area->name)) + || (num != 0 && (((mode & 1) != 0 && (addr_t)area->id == num) + || (((mode & 2) != 0 && area->Base() <= num + && area->Base() + area->Size() > num))))) { + dump_area_struct(area, mappings); + found = true; + } + } + + if (!found) + kprintf("could not find area %s (%ld)\n", argv[index], num); + } + + return 0; +} + + +static int +dump_area_list(int argc, char** argv) +{ + VMArea* area; + const char* name = NULL; + int32 id = 0; + + if (argc > 1) { + id = parse_expression(argv[1]); + if (id == 0) + name = argv[1]; + } + + kprintf("%-*s id %-*s %-*sprotect lock name\n", + B_PRINTF_POINTER_WIDTH, "addr", B_PRINTF_POINTER_WIDTH, "base", + B_PRINTF_POINTER_WIDTH, "size"); + + VMAreasTree::Iterator it = VMAreas::GetIterator(); + while ((area = it.Next()) != NULL) { + if ((id != 0 && area->address_space->ID() != id) + || (name != NULL && strstr(area->name, name) == NULL)) + continue; + + kprintf("%p %5" B_PRIx32 " %p %p %4" B_PRIx32 " %4d %s\n", area, + area->id, (void*)area->Base(), (void*)area->Size(), + area->protection, area->wiring, area->name); + } + return 0; +} + + +static int +dump_available_memory(int argc, char** argv) +{ + kprintf("Available memory: %" B_PRIdOFF "/%" B_PRIuPHYSADDR " bytes\n", + vm_available_memory_debug(), (phys_addr_t)vm_page_num_pages() * B_PAGE_SIZE); + return 0; +} + + +static int +dump_mapping_info(int argc, char** argv) +{ + bool reverseLookup = false; + bool pageLookup = false; + + int argi = 1; + for (; argi < argc && argv[argi][0] == '-'; argi++) { + const char* arg = argv[argi]; + if (strcmp(arg, "-r") == 0) { + reverseLookup = true; + } else if (strcmp(arg, "-p") == 0) { + reverseLookup = true; + pageLookup = true; + } else { + print_debugger_command_usage(argv[0]); + return 0; + } + } + + // We need at least one argument, the address. Optionally a thread ID can be + // specified. + if (argi >= argc || argi + 2 < argc) { + print_debugger_command_usage(argv[0]); + return 0; + } + + uint64 addressValue; + if (!evaluate_debug_expression(argv[argi++], &addressValue, false)) + return 0; + + Team* team = NULL; + if (argi < argc) { + uint64 threadID; + if (!evaluate_debug_expression(argv[argi++], &threadID, false)) + return 0; + + Thread* thread = Thread::GetDebug(threadID); + if (thread == NULL) { + kprintf("Invalid thread/team ID \"%s\"\n", argv[argi - 1]); + return 0; + } + + team = thread->team; + } + + if (reverseLookup) { + phys_addr_t physicalAddress; + if (pageLookup) { + vm_page* page = (vm_page*)(addr_t)addressValue; + physicalAddress = page->physical_page_number * B_PAGE_SIZE; + } else { + physicalAddress = (phys_addr_t)addressValue; + physicalAddress -= physicalAddress % B_PAGE_SIZE; + } + + kprintf(" Team Virtual Address Area\n"); + kprintf("--------------------------------------\n"); + + struct Callback : VMTranslationMap::ReverseMappingInfoCallback { + Callback() + : + fAddressSpace(NULL) + { + } + + void SetAddressSpace(VMAddressSpace* addressSpace) + { + fAddressSpace = addressSpace; + } + + virtual bool HandleVirtualAddress(addr_t virtualAddress) + { + kprintf("%8" B_PRId32 " %#18" B_PRIxADDR, fAddressSpace->ID(), + virtualAddress); + if (VMArea* area = fAddressSpace->LookupArea(virtualAddress)) + kprintf(" %8" B_PRId32 " %s\n", area->id, area->name); + else + kprintf("\n"); + return false; + } + + private: + VMAddressSpace* fAddressSpace; + } callback; + + if (team != NULL) { + // team specified -- get its address space + VMAddressSpace* addressSpace = team->address_space; + if (addressSpace == NULL) { + kprintf("Failed to get address space!\n"); + return 0; + } + + callback.SetAddressSpace(addressSpace); + addressSpace->TranslationMap()->DebugGetReverseMappingInfo( + physicalAddress, callback); + } else { + // no team specified -- iterate through all address spaces + for (VMAddressSpace* addressSpace = VMAddressSpace::DebugFirst(); + addressSpace != NULL; + addressSpace = VMAddressSpace::DebugNext(addressSpace)) { + callback.SetAddressSpace(addressSpace); + addressSpace->TranslationMap()->DebugGetReverseMappingInfo( + physicalAddress, callback); + } + } + } else { + // get the address space + addr_t virtualAddress = (addr_t)addressValue; + virtualAddress -= virtualAddress % B_PAGE_SIZE; + VMAddressSpace* addressSpace; + if (IS_KERNEL_ADDRESS(virtualAddress)) { + addressSpace = VMAddressSpace::Kernel(); + } else if (team != NULL) { + addressSpace = team->address_space; + } else { + Thread* thread = debug_get_debugged_thread(); + if (thread == NULL || thread->team == NULL) { + kprintf("Failed to get team!\n"); + return 0; + } + + addressSpace = thread->team->address_space; + } + + if (addressSpace == NULL) { + kprintf("Failed to get address space!\n"); + return 0; + } + + // let the translation map implementation do the job + addressSpace->TranslationMap()->DebugPrintMappingInfo(virtualAddress); + } + + return 0; +} + + +/*! 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 || ROUNDDOWN((addr_t)unsafeMemory, B_PAGE_SIZE) + != ROUNDDOWN((addr_t)unsafeMemory + size - 1, B_PAGE_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) { + 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 + phys_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); +} + + +void +vm_debug_init() +{ +#if DEBUG_CACHE_LIST + if (vm_page_num_free_pages() >= 200 * 1024 * 1024 / B_PAGE_SIZE) { + virtual_address_restrictions virtualRestrictions = {}; + virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS; + physical_address_restrictions physicalRestrictions = {}; + create_area_etc(VMAddressSpace::KernelID(), "cache info table", + ROUNDUP(kCacheInfoTableCount * sizeof(cache_info), B_PAGE_SIZE), + B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, + CREATE_AREA_DONT_WAIT, 0, &virtualRestrictions, + &physicalRestrictions, (void**)&sCacheInfoTable); + } +#endif // DEBUG_CACHE_LIST + + // add some debugger commands + add_debugger_command("areas", &dump_area_list, "Dump a list of all areas"); + add_debugger_command("area", &dump_area, + "Dump info about a particular area"); + add_debugger_command("cache", &dump_cache, "Dump VMCache"); + add_debugger_command("cache_tree", &dump_cache_tree, "Dump VMCache tree"); +#if DEBUG_CACHE_LIST + if (sCacheInfoTable != NULL) { + add_debugger_command_etc("caches", &dump_caches, + "List all VMCache trees", + "[ \"-c\" ]\n" + "All cache trees are listed sorted in decreasing order by number " + "of\n" + "used pages or, if \"-c\" is specified, by size of committed " + "memory.\n", + 0); + } +#endif + add_debugger_command("avail", &dump_available_memory, + "Dump available memory"); + add_debugger_command("dl", &display_mem, "dump memory long words (64-bit)"); + add_debugger_command("dw", &display_mem, "dump memory words (32-bit)"); + add_debugger_command("ds", &display_mem, "dump memory shorts (16-bit)"); + add_debugger_command("db", &display_mem, "dump memory bytes (8-bit)"); + add_debugger_command("string", &display_mem, "dump strings"); + + add_debugger_command_etc("mapping", &dump_mapping_info, + "Print address mapping information", + "[ \"-r\" | \"-p\" ]
[ ]\n" + "Prints low-level page mapping information for a given address. If\n" + "neither \"-r\" nor \"-p\" are specified,
is a virtual\n" + "address that is looked up in the translation map of the current\n" + "team, respectively the team specified by thread ID . If\n" + "\"-r\" is specified,
is a physical address that is\n" + "searched in the translation map of all teams, respectively the team\n" + "specified by thread ID . If \"-p\" is specified,\n" + "
is the address of a vm_page structure. The behavior is\n" + "equivalent to specifying \"-r\" with the physical address of that\n" + "page.\n", + 0); +}