1 /* 2 * Copyright 2024, Haiku, Inc. All rights reserved. 3 * Copyright 2010-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 4 * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. 5 * Distributed under the terms of the MIT License. 6 * 7 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 8 * Distributed under the terms of the NewOS License. 9 */ 10 11 12 #include <ctype.h> 13 14 #include <team.h> 15 16 #include <vm/vm_page.h> 17 #include <vm/vm.h> 18 #include <vm/vm_priv.h> 19 #include <vm/VMAddressSpace.h> 20 #include <vm/VMArea.h> 21 #include <vm/VMCache.h> 22 23 24 #if DEBUG_CACHE_LIST 25 26 struct cache_info { 27 VMCache* cache; 28 addr_t page_count; 29 addr_t committed; 30 }; 31 32 static const uint32 kCacheInfoTableCount = 100 * 1024; 33 static cache_info* sCacheInfoTable; 34 35 #endif // DEBUG_CACHE_LIST 36 37 38 static int 39 display_mem(int argc, char** argv) 40 { 41 bool physical = false; 42 addr_t copyAddress; 43 int32 displayWidth; 44 int32 itemSize; 45 int32 num = -1; 46 addr_t address; 47 int i = 1, j; 48 49 if (argc > 1 && argv[1][0] == '-') { 50 if (!strcmp(argv[1], "-p") || !strcmp(argv[1], "--physical")) { 51 physical = true; 52 i++; 53 } else 54 i = 99; 55 } 56 57 if (argc < i + 1 || argc > i + 2) { 58 kprintf("usage: dl/dw/ds/db/string [-p|--physical] <address> [num]\n" 59 "\tdl - 8 bytes\n" 60 "\tdw - 4 bytes\n" 61 "\tds - 2 bytes\n" 62 "\tdb - 1 byte\n" 63 "\tstring - a whole string\n" 64 " -p or --physical only allows memory from a single page to be " 65 "displayed.\n"); 66 return 0; 67 } 68 69 address = parse_expression(argv[i]); 70 71 if (argc > i + 1) 72 num = parse_expression(argv[i + 1]); 73 74 // build the format string 75 if (strcmp(argv[0], "db") == 0) { 76 itemSize = 1; 77 displayWidth = 16; 78 } else if (strcmp(argv[0], "ds") == 0) { 79 itemSize = 2; 80 displayWidth = 8; 81 } else if (strcmp(argv[0], "dw") == 0) { 82 itemSize = 4; 83 displayWidth = 4; 84 } else if (strcmp(argv[0], "dl") == 0) { 85 itemSize = 8; 86 displayWidth = 2; 87 } else if (strcmp(argv[0], "string") == 0) { 88 itemSize = 1; 89 displayWidth = -1; 90 } else { 91 kprintf("display_mem called in an invalid way!\n"); 92 return 0; 93 } 94 95 if (num <= 0) 96 num = displayWidth; 97 98 void* physicalPageHandle = NULL; 99 100 if (physical) { 101 int32 offset = address & (B_PAGE_SIZE - 1); 102 if (num * itemSize + offset > B_PAGE_SIZE) { 103 num = (B_PAGE_SIZE - offset) / itemSize; 104 kprintf("NOTE: number of bytes has been cut to page size\n"); 105 } 106 107 address = ROUNDDOWN(address, B_PAGE_SIZE); 108 109 if (vm_get_physical_page_debug(address, ©Address, 110 &physicalPageHandle) != B_OK) { 111 kprintf("getting the hardware page failed."); 112 return 0; 113 } 114 115 address += offset; 116 copyAddress += offset; 117 } else 118 copyAddress = address; 119 120 if (!strcmp(argv[0], "string")) { 121 kprintf("%p \"", (char*)copyAddress); 122 123 // string mode 124 for (i = 0; true; i++) { 125 char c; 126 if (debug_memcpy(B_CURRENT_TEAM, &c, (char*)copyAddress + i, 1) 127 != B_OK 128 || c == '\0') { 129 break; 130 } 131 132 if (c == '\n') 133 kprintf("\\n"); 134 else if (c == '\t') 135 kprintf("\\t"); 136 else { 137 if (!isprint(c)) 138 c = '.'; 139 140 kprintf("%c", c); 141 } 142 } 143 144 kprintf("\"\n"); 145 } else { 146 // number mode 147 for (i = 0; i < num; i++) { 148 uint64 value; 149 150 if ((i % displayWidth) == 0) { 151 int32 displayed = min_c(displayWidth, (num-i)) * itemSize; 152 if (i != 0) 153 kprintf("\n"); 154 155 kprintf("[0x%lx] ", address + i * itemSize); 156 157 for (j = 0; j < displayed; j++) { 158 char c; 159 if (debug_memcpy(B_CURRENT_TEAM, &c, 160 (char*)copyAddress + i * itemSize + j, 1) != B_OK) { 161 displayed = j; 162 break; 163 } 164 if (!isprint(c)) 165 c = '.'; 166 167 kprintf("%c", c); 168 } 169 if (num > displayWidth) { 170 // make sure the spacing in the last line is correct 171 for (j = displayed; j < displayWidth * itemSize; j++) 172 kprintf(" "); 173 } 174 kprintf(" "); 175 } 176 177 if (debug_memcpy(B_CURRENT_TEAM, &value, 178 (uint8*)copyAddress + i * itemSize, itemSize) != B_OK) { 179 kprintf("read fault"); 180 break; 181 } 182 183 switch (itemSize) { 184 case 1: 185 kprintf(" %02" B_PRIx8, *(uint8*)&value); 186 break; 187 case 2: 188 kprintf(" %04" B_PRIx16, *(uint16*)&value); 189 break; 190 case 4: 191 kprintf(" %08" B_PRIx32, *(uint32*)&value); 192 break; 193 case 8: 194 kprintf(" %016" B_PRIx64, *(uint64*)&value); 195 break; 196 } 197 } 198 199 kprintf("\n"); 200 } 201 202 if (physical) { 203 copyAddress = ROUNDDOWN(copyAddress, B_PAGE_SIZE); 204 vm_put_physical_page_debug(copyAddress, physicalPageHandle); 205 } 206 return 0; 207 } 208 209 210 static void 211 dump_cache_tree_recursively(VMCache* cache, int level, 212 VMCache* highlightCache) 213 { 214 // print this cache 215 for (int i = 0; i < level; i++) 216 kprintf(" "); 217 if (cache == highlightCache) 218 kprintf("%p <--\n", cache); 219 else 220 kprintf("%p\n", cache); 221 222 // recursively print its consumers 223 for (VMCache::ConsumerList::Iterator it = cache->consumers.GetIterator(); 224 VMCache* consumer = it.Next();) { 225 dump_cache_tree_recursively(consumer, level + 1, highlightCache); 226 } 227 } 228 229 230 static int 231 dump_cache_tree(int argc, char** argv) 232 { 233 if (argc != 2 || !strcmp(argv[1], "--help")) { 234 kprintf("usage: %s <address>\n", argv[0]); 235 return 0; 236 } 237 238 addr_t address = parse_expression(argv[1]); 239 if (address == 0) 240 return 0; 241 242 VMCache* cache = (VMCache*)address; 243 VMCache* root = cache; 244 245 // find the root cache (the transitive source) 246 while (root->source != NULL) 247 root = root->source; 248 249 dump_cache_tree_recursively(root, 0, cache); 250 251 return 0; 252 } 253 254 255 const char* 256 vm_cache_type_to_string(int32 type) 257 { 258 switch (type) { 259 case CACHE_TYPE_RAM: 260 return "RAM"; 261 case CACHE_TYPE_DEVICE: 262 return "device"; 263 case CACHE_TYPE_VNODE: 264 return "vnode"; 265 case CACHE_TYPE_NULL: 266 return "null"; 267 268 default: 269 return "unknown"; 270 } 271 } 272 273 274 #if DEBUG_CACHE_LIST 275 276 static void 277 update_cache_info_recursively(VMCache* cache, cache_info& info) 278 { 279 info.page_count += cache->page_count; 280 if (cache->type == CACHE_TYPE_RAM) 281 info.committed += cache->committed_size; 282 283 // recurse 284 for (VMCache::ConsumerList::Iterator it = cache->consumers.GetIterator(); 285 VMCache* consumer = it.Next();) { 286 update_cache_info_recursively(consumer, info); 287 } 288 } 289 290 291 static int 292 cache_info_compare_page_count(const void* _a, const void* _b) 293 { 294 const cache_info* a = (const cache_info*)_a; 295 const cache_info* b = (const cache_info*)_b; 296 if (a->page_count == b->page_count) 297 return 0; 298 return a->page_count < b->page_count ? 1 : -1; 299 } 300 301 302 static int 303 cache_info_compare_committed(const void* _a, const void* _b) 304 { 305 const cache_info* a = (const cache_info*)_a; 306 const cache_info* b = (const cache_info*)_b; 307 if (a->committed == b->committed) 308 return 0; 309 return a->committed < b->committed ? 1 : -1; 310 } 311 312 313 static void 314 dump_caches_recursively(VMCache* cache, cache_info& info, int level) 315 { 316 for (int i = 0; i < level; i++) 317 kprintf(" "); 318 319 kprintf("%p: type: %s, base: %" B_PRIdOFF ", size: %" B_PRIdOFF ", " 320 "pages: %" B_PRIu32, cache, vm_cache_type_to_string(cache->type), 321 cache->virtual_base, cache->virtual_end, cache->page_count); 322 323 if (level == 0) 324 kprintf("/%lu", info.page_count); 325 326 if (cache->type == CACHE_TYPE_RAM || (level == 0 && info.committed > 0)) { 327 kprintf(", committed: %" B_PRIdOFF, cache->committed_size); 328 329 if (level == 0) 330 kprintf("/%lu", info.committed); 331 } 332 333 // areas 334 if (cache->areas != NULL) { 335 VMArea* area = cache->areas; 336 kprintf(", areas: %" B_PRId32 " (%s, team: %" B_PRId32 ")", area->id, 337 area->name, area->address_space->ID()); 338 339 while (area->cache_next != NULL) { 340 area = area->cache_next; 341 kprintf(", %" B_PRId32, area->id); 342 } 343 } 344 345 kputs("\n"); 346 347 // recurse 348 for (VMCache::ConsumerList::Iterator it = cache->consumers.GetIterator(); 349 VMCache* consumer = it.Next();) { 350 dump_caches_recursively(consumer, info, level + 1); 351 } 352 } 353 354 355 static int 356 dump_caches(int argc, char** argv) 357 { 358 if (sCacheInfoTable == NULL) { 359 kprintf("No cache info table!\n"); 360 return 0; 361 } 362 363 bool sortByPageCount = true; 364 365 for (int32 i = 1; i < argc; i++) { 366 if (strcmp(argv[i], "-c") == 0) { 367 sortByPageCount = false; 368 } else { 369 print_debugger_command_usage(argv[0]); 370 return 0; 371 } 372 } 373 374 uint32 totalCount = 0; 375 uint32 rootCount = 0; 376 off_t totalCommitted = 0; 377 page_num_t totalPages = 0; 378 379 VMCache* cache = gDebugCacheList; 380 while (cache) { 381 totalCount++; 382 if (cache->source == NULL) { 383 cache_info stackInfo; 384 cache_info& info = rootCount < kCacheInfoTableCount 385 ? sCacheInfoTable[rootCount] : stackInfo; 386 rootCount++; 387 info.cache = cache; 388 info.page_count = 0; 389 info.committed = 0; 390 update_cache_info_recursively(cache, info); 391 totalCommitted += info.committed; 392 totalPages += info.page_count; 393 } 394 395 cache = cache->debug_next; 396 } 397 398 kprintf("total committed memory: %" B_PRIdOFF ", total used pages: %" 399 B_PRIuPHYSADDR "\n", totalCommitted, totalPages); 400 kprintf("%" B_PRIu32 " caches (%" B_PRIu32 " root caches), sorted by %s " 401 "per cache tree...\n\n", totalCount, rootCount, sortByPageCount ? 402 "page count" : "committed size"); 403 404 if (rootCount > kCacheInfoTableCount) { 405 kprintf("Cache info table too small! Can't sort and print caches!\n"); 406 return 0; 407 } 408 409 qsort(sCacheInfoTable, rootCount, sizeof(cache_info), 410 sortByPageCount 411 ? &cache_info_compare_page_count 412 : &cache_info_compare_committed); 413 414 for (uint32 i = 0; i < rootCount; i++) { 415 cache_info& info = sCacheInfoTable[i]; 416 dump_caches_recursively(info.cache, info, 0); 417 } 418 419 return 0; 420 } 421 422 #endif // DEBUG_CACHE_LIST 423 424 425 static int 426 dump_cache(int argc, char** argv) 427 { 428 VMCache* cache; 429 bool showPages = false; 430 int i = 1; 431 432 if (argc < 2 || !strcmp(argv[1], "--help")) { 433 kprintf("usage: %s [-ps] <address>\n" 434 " if -p is specified, all pages are shown, if -s is used\n" 435 " only the cache info is shown respectively.\n", argv[0]); 436 return 0; 437 } 438 while (argv[i][0] == '-') { 439 char* arg = argv[i] + 1; 440 while (arg[0]) { 441 if (arg[0] == 'p') 442 showPages = true; 443 arg++; 444 } 445 i++; 446 } 447 if (argv[i] == NULL) { 448 kprintf("%s: invalid argument, pass address\n", argv[0]); 449 return 0; 450 } 451 452 addr_t address = parse_expression(argv[i]); 453 if (address == 0) 454 return 0; 455 456 cache = (VMCache*)address; 457 458 cache->Dump(showPages); 459 460 set_debug_variable("_sourceCache", (addr_t)cache->source); 461 462 return 0; 463 } 464 465 466 static void 467 dump_area_struct(VMArea* area, bool mappings) 468 { 469 kprintf("AREA: %p\n", area); 470 kprintf("name:\t\t'%s'\n", area->name); 471 kprintf("owner:\t\t0x%" B_PRIx32 "\n", area->address_space->ID()); 472 kprintf("id:\t\t0x%" B_PRIx32 "\n", area->id); 473 kprintf("base:\t\t0x%lx\n", area->Base()); 474 kprintf("size:\t\t0x%lx\n", area->Size()); 475 kprintf("protection:\t0x%" B_PRIx32 "\n", area->protection); 476 kprintf("page_protection:%p\n", area->page_protections); 477 kprintf("wiring:\t\t0x%x\n", area->wiring); 478 kprintf("memory_type:\t%#" B_PRIx32 "\n", area->MemoryType()); 479 kprintf("cache:\t\t%p\n", area->cache); 480 kprintf("cache_type:\t%s\n", vm_cache_type_to_string(area->cache_type)); 481 kprintf("cache_offset:\t0x%" B_PRIx64 "\n", area->cache_offset); 482 kprintf("cache_next:\t%p\n", area->cache_next); 483 kprintf("cache_prev:\t%p\n", area->cache_prev); 484 485 VMAreaMappings::Iterator iterator = area->mappings.GetIterator(); 486 if (mappings) { 487 kprintf("page mappings:\n"); 488 while (iterator.HasNext()) { 489 vm_page_mapping* mapping = iterator.Next(); 490 kprintf(" %p", mapping->page); 491 } 492 kprintf("\n"); 493 } else { 494 uint32 count = 0; 495 while (iterator.Next() != NULL) { 496 count++; 497 } 498 kprintf("page mappings:\t%" B_PRIu32 "\n", count); 499 } 500 } 501 502 503 static int 504 dump_area(int argc, char** argv) 505 { 506 bool mappings = false; 507 bool found = false; 508 int32 index = 1; 509 VMArea* area; 510 addr_t num; 511 512 if (argc < 2 || !strcmp(argv[1], "--help")) { 513 kprintf("usage: area [-m] [id|contains|address|name] <id|address|name>\n" 514 "All areas matching either id/address/name are listed. You can\n" 515 "force to check only a specific item by prefixing the specifier\n" 516 "with the id/contains/address/name keywords.\n" 517 "-m shows the area's mappings as well.\n"); 518 return 0; 519 } 520 521 if (!strcmp(argv[1], "-m")) { 522 mappings = true; 523 index++; 524 } 525 526 int32 mode = 0xf; 527 if (!strcmp(argv[index], "id")) 528 mode = 1; 529 else if (!strcmp(argv[index], "contains")) 530 mode = 2; 531 else if (!strcmp(argv[index], "name")) 532 mode = 4; 533 else if (!strcmp(argv[index], "address")) 534 mode = 0; 535 if (mode != 0xf) 536 index++; 537 538 if (index >= argc) { 539 kprintf("No area specifier given.\n"); 540 return 0; 541 } 542 543 num = parse_expression(argv[index]); 544 545 if (mode == 0) { 546 dump_area_struct((struct VMArea*)num, mappings); 547 } else { 548 // walk through the area list, looking for the arguments as a name 549 550 VMAreasTree::Iterator it = VMAreas::GetIterator(); 551 while ((area = it.Next()) != NULL) { 552 if (((mode & 4) != 0 553 && !strcmp(argv[index], area->name)) 554 || (num != 0 && (((mode & 1) != 0 && (addr_t)area->id == num) 555 || (((mode & 2) != 0 && area->Base() <= num 556 && area->Base() + area->Size() > num))))) { 557 dump_area_struct(area, mappings); 558 found = true; 559 } 560 } 561 562 if (!found) 563 kprintf("could not find area %s (%ld)\n", argv[index], num); 564 } 565 566 return 0; 567 } 568 569 570 static int 571 dump_area_list(int argc, char** argv) 572 { 573 VMArea* area; 574 const char* name = NULL; 575 int32 id = 0; 576 577 if (argc > 1) { 578 id = parse_expression(argv[1]); 579 if (id == 0) 580 name = argv[1]; 581 } 582 583 kprintf("%-*s id %-*s %-*sprotect lock name\n", 584 B_PRINTF_POINTER_WIDTH, "addr", B_PRINTF_POINTER_WIDTH, "base", 585 B_PRINTF_POINTER_WIDTH, "size"); 586 587 VMAreasTree::Iterator it = VMAreas::GetIterator(); 588 while ((area = it.Next()) != NULL) { 589 if ((id != 0 && area->address_space->ID() != id) 590 || (name != NULL && strstr(area->name, name) == NULL)) 591 continue; 592 593 kprintf("%p %5" B_PRIx32 " %p %p %4" B_PRIx32 " %4d %s\n", area, 594 area->id, (void*)area->Base(), (void*)area->Size(), 595 area->protection, area->wiring, area->name); 596 } 597 return 0; 598 } 599 600 601 static int 602 dump_available_memory(int argc, char** argv) 603 { 604 kprintf("Available memory: %" B_PRIdOFF "/%" B_PRIuPHYSADDR " bytes\n", 605 vm_available_memory_debug(), (phys_addr_t)vm_page_num_pages() * B_PAGE_SIZE); 606 return 0; 607 } 608 609 610 static int 611 dump_mapping_info(int argc, char** argv) 612 { 613 bool reverseLookup = false; 614 bool pageLookup = false; 615 616 int argi = 1; 617 for (; argi < argc && argv[argi][0] == '-'; argi++) { 618 const char* arg = argv[argi]; 619 if (strcmp(arg, "-r") == 0) { 620 reverseLookup = true; 621 } else if (strcmp(arg, "-p") == 0) { 622 reverseLookup = true; 623 pageLookup = true; 624 } else { 625 print_debugger_command_usage(argv[0]); 626 return 0; 627 } 628 } 629 630 // We need at least one argument, the address. Optionally a thread ID can be 631 // specified. 632 if (argi >= argc || argi + 2 < argc) { 633 print_debugger_command_usage(argv[0]); 634 return 0; 635 } 636 637 uint64 addressValue; 638 if (!evaluate_debug_expression(argv[argi++], &addressValue, false)) 639 return 0; 640 641 Team* team = NULL; 642 if (argi < argc) { 643 uint64 threadID; 644 if (!evaluate_debug_expression(argv[argi++], &threadID, false)) 645 return 0; 646 647 Thread* thread = Thread::GetDebug(threadID); 648 if (thread == NULL) { 649 kprintf("Invalid thread/team ID \"%s\"\n", argv[argi - 1]); 650 return 0; 651 } 652 653 team = thread->team; 654 } 655 656 if (reverseLookup) { 657 phys_addr_t physicalAddress; 658 if (pageLookup) { 659 vm_page* page = (vm_page*)(addr_t)addressValue; 660 physicalAddress = page->physical_page_number * B_PAGE_SIZE; 661 } else { 662 physicalAddress = (phys_addr_t)addressValue; 663 physicalAddress -= physicalAddress % B_PAGE_SIZE; 664 } 665 666 kprintf(" Team Virtual Address Area\n"); 667 kprintf("--------------------------------------\n"); 668 669 struct Callback : VMTranslationMap::ReverseMappingInfoCallback { 670 Callback() 671 : 672 fAddressSpace(NULL) 673 { 674 } 675 676 void SetAddressSpace(VMAddressSpace* addressSpace) 677 { 678 fAddressSpace = addressSpace; 679 } 680 681 virtual bool HandleVirtualAddress(addr_t virtualAddress) 682 { 683 kprintf("%8" B_PRId32 " %#18" B_PRIxADDR, fAddressSpace->ID(), 684 virtualAddress); 685 if (VMArea* area = fAddressSpace->LookupArea(virtualAddress)) 686 kprintf(" %8" B_PRId32 " %s\n", area->id, area->name); 687 else 688 kprintf("\n"); 689 return false; 690 } 691 692 private: 693 VMAddressSpace* fAddressSpace; 694 } callback; 695 696 if (team != NULL) { 697 // team specified -- get its address space 698 VMAddressSpace* addressSpace = team->address_space; 699 if (addressSpace == NULL) { 700 kprintf("Failed to get address space!\n"); 701 return 0; 702 } 703 704 callback.SetAddressSpace(addressSpace); 705 addressSpace->TranslationMap()->DebugGetReverseMappingInfo( 706 physicalAddress, callback); 707 } else { 708 // no team specified -- iterate through all address spaces 709 for (VMAddressSpace* addressSpace = VMAddressSpace::DebugFirst(); 710 addressSpace != NULL; 711 addressSpace = VMAddressSpace::DebugNext(addressSpace)) { 712 callback.SetAddressSpace(addressSpace); 713 addressSpace->TranslationMap()->DebugGetReverseMappingInfo( 714 physicalAddress, callback); 715 } 716 } 717 } else { 718 // get the address space 719 addr_t virtualAddress = (addr_t)addressValue; 720 virtualAddress -= virtualAddress % B_PAGE_SIZE; 721 VMAddressSpace* addressSpace; 722 if (IS_KERNEL_ADDRESS(virtualAddress)) { 723 addressSpace = VMAddressSpace::Kernel(); 724 } else if (team != NULL) { 725 addressSpace = team->address_space; 726 } else { 727 Thread* thread = debug_get_debugged_thread(); 728 if (thread == NULL || thread->team == NULL) { 729 kprintf("Failed to get team!\n"); 730 return 0; 731 } 732 733 addressSpace = thread->team->address_space; 734 } 735 736 if (addressSpace == NULL) { 737 kprintf("Failed to get address space!\n"); 738 return 0; 739 } 740 741 // let the translation map implementation do the job 742 addressSpace->TranslationMap()->DebugPrintMappingInfo(virtualAddress); 743 } 744 745 return 0; 746 } 747 748 749 /*! Copies a range of memory directly from/to a page that might not be mapped 750 at the moment. 751 752 For \a unsafeMemory the current mapping (if any is ignored). The function 753 walks through the respective area's cache chain to find the physical page 754 and copies from/to it directly. 755 The memory range starting at \a unsafeMemory with a length of \a size bytes 756 must not cross a page boundary. 757 758 \param teamID The team ID identifying the address space \a unsafeMemory is 759 to be interpreted in. Ignored, if \a unsafeMemory is a kernel address 760 (the kernel address space is assumed in this case). If \c B_CURRENT_TEAM 761 is passed, the address space of the thread returned by 762 debug_get_debugged_thread() is used. 763 \param unsafeMemory The start of the unsafe memory range to be copied 764 from/to. 765 \param buffer A safely accessible kernel buffer to be copied from/to. 766 \param size The number of bytes to be copied. 767 \param copyToUnsafe If \c true, memory is copied from \a buffer to 768 \a unsafeMemory, the other way around otherwise. 769 */ 770 status_t 771 vm_debug_copy_page_memory(team_id teamID, void* unsafeMemory, void* buffer, 772 size_t size, bool copyToUnsafe) 773 { 774 if (size > B_PAGE_SIZE || ROUNDDOWN((addr_t)unsafeMemory, B_PAGE_SIZE) 775 != ROUNDDOWN((addr_t)unsafeMemory + size - 1, B_PAGE_SIZE)) { 776 return B_BAD_VALUE; 777 } 778 779 // get the address space for the debugged thread 780 VMAddressSpace* addressSpace; 781 if (IS_KERNEL_ADDRESS(unsafeMemory)) { 782 addressSpace = VMAddressSpace::Kernel(); 783 } else if (teamID == B_CURRENT_TEAM) { 784 Thread* thread = debug_get_debugged_thread(); 785 if (thread == NULL || thread->team == NULL) 786 return B_BAD_ADDRESS; 787 788 addressSpace = thread->team->address_space; 789 } else 790 addressSpace = VMAddressSpace::DebugGet(teamID); 791 792 if (addressSpace == NULL) 793 return B_BAD_ADDRESS; 794 795 // get the area 796 VMArea* area = addressSpace->LookupArea((addr_t)unsafeMemory); 797 if (area == NULL) 798 return B_BAD_ADDRESS; 799 800 // search the page 801 off_t cacheOffset = (addr_t)unsafeMemory - area->Base() 802 + area->cache_offset; 803 VMCache* cache = area->cache; 804 vm_page* page = NULL; 805 while (cache != NULL) { 806 page = cache->DebugLookupPage(cacheOffset); 807 if (page != NULL) 808 break; 809 810 // Page not found in this cache -- if it is paged out, we must not try 811 // to get it from lower caches. 812 if (cache->DebugHasPage(cacheOffset)) 813 break; 814 815 cache = cache->source; 816 } 817 818 if (page == NULL) 819 return B_UNSUPPORTED; 820 821 // copy from/to physical memory 822 phys_addr_t physicalAddress = page->physical_page_number * B_PAGE_SIZE 823 + (addr_t)unsafeMemory % B_PAGE_SIZE; 824 825 if (copyToUnsafe) { 826 if (page->Cache() != area->cache) 827 return B_UNSUPPORTED; 828 829 return vm_memcpy_to_physical(physicalAddress, buffer, size, false); 830 } 831 832 return vm_memcpy_from_physical(buffer, physicalAddress, size, false); 833 } 834 835 836 void 837 vm_debug_init() 838 { 839 #if DEBUG_CACHE_LIST 840 if (vm_page_num_free_pages() >= 200 * 1024 * 1024 / B_PAGE_SIZE) { 841 virtual_address_restrictions virtualRestrictions = {}; 842 virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS; 843 physical_address_restrictions physicalRestrictions = {}; 844 create_area_etc(VMAddressSpace::KernelID(), "cache info table", 845 ROUNDUP(kCacheInfoTableCount * sizeof(cache_info), B_PAGE_SIZE), 846 B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 847 CREATE_AREA_DONT_WAIT, 0, &virtualRestrictions, 848 &physicalRestrictions, (void**)&sCacheInfoTable); 849 } 850 #endif // DEBUG_CACHE_LIST 851 852 // add some debugger commands 853 add_debugger_command("areas", &dump_area_list, "Dump a list of all areas"); 854 add_debugger_command("area", &dump_area, 855 "Dump info about a particular area"); 856 add_debugger_command("cache", &dump_cache, "Dump VMCache"); 857 add_debugger_command("cache_tree", &dump_cache_tree, "Dump VMCache tree"); 858 #if DEBUG_CACHE_LIST 859 if (sCacheInfoTable != NULL) { 860 add_debugger_command_etc("caches", &dump_caches, 861 "List all VMCache trees", 862 "[ \"-c\" ]\n" 863 "All cache trees are listed sorted in decreasing order by number " 864 "of\n" 865 "used pages or, if \"-c\" is specified, by size of committed " 866 "memory.\n", 867 0); 868 } 869 #endif 870 add_debugger_command("avail", &dump_available_memory, 871 "Dump available memory"); 872 add_debugger_command("dl", &display_mem, "dump memory long words (64-bit)"); 873 add_debugger_command("dw", &display_mem, "dump memory words (32-bit)"); 874 add_debugger_command("ds", &display_mem, "dump memory shorts (16-bit)"); 875 add_debugger_command("db", &display_mem, "dump memory bytes (8-bit)"); 876 add_debugger_command("string", &display_mem, "dump strings"); 877 878 add_debugger_command_etc("mapping", &dump_mapping_info, 879 "Print address mapping information", 880 "[ \"-r\" | \"-p\" ] <address> [ <thread ID> ]\n" 881 "Prints low-level page mapping information for a given address. If\n" 882 "neither \"-r\" nor \"-p\" are specified, <address> is a virtual\n" 883 "address that is looked up in the translation map of the current\n" 884 "team, respectively the team specified by thread ID <thread ID>. If\n" 885 "\"-r\" is specified, <address> is a physical address that is\n" 886 "searched in the translation map of all teams, respectively the team\n" 887 "specified by thread ID <thread ID>. If \"-p\" is specified,\n" 888 "<address> is the address of a vm_page structure. The behavior is\n" 889 "equivalent to specifying \"-r\" with the physical address of that\n" 890 "page.\n", 891 0); 892 } 893