xref: /haiku/src/system/kernel/vm/vm_debug.cpp (revision 592a3b6921f10ad84e44ad74e066c23466ab85cb)
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, &copyAddress,
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