From 2e74d74f4f6d3023650b9525a425f760ea86f007 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 22 Dec 2009 22:00:35 +0000 Subject: [PATCH] * Added method VMCache::TransferAreas() moving areas from one cache to another. The code originates from vm_copy_on_write_area(). We now generate the VM cache tracing entries, though. * count_writable_areas() -> VMCache::CountWritableAreas() * Added debugger command "cache_stack" which is enabled when VM cache tracing is enabled. It prints the source caches of a given cache or area at the time of a specified tracing entry. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34751 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/vm/VMCache.h | 3 + src/system/kernel/vm/VMCache.cpp | 181 ++++++++++++++++++++++++++++ src/system/kernel/vm/vm.cpp | 35 +----- 3 files changed, 189 insertions(+), 30 deletions(-) diff --git a/headers/private/kernel/vm/VMCache.h b/headers/private/kernel/vm/VMCache.h index 15870b9352..6f458e021c 100644 --- a/headers/private/kernel/vm/VMCache.h +++ b/headers/private/kernel/vm/VMCache.h @@ -101,6 +101,8 @@ public: status_t InsertAreaLocked(VMArea* area); status_t RemoveArea(VMArea* area); + void TransferAreas(VMCache* fromCache); + uint32 CountWritableAreas(VMArea* ignoreArea) const; status_t WriteModified(); status_t SetMinimalCommitment(off_t commitment); @@ -204,6 +206,7 @@ extern "C" { #endif status_t vm_cache_init(struct kernel_args *args); +void vm_cache_init_post_heap(); struct VMCache *vm_cache_acquire_locked_page_cache(struct vm_page *page, bool dontWait); diff --git a/src/system/kernel/vm/VMCache.cpp b/src/system/kernel/vm/VMCache.cpp index 7298771d72..48b3a0eaea 100644 --- a/src/system/kernel/vm/VMCache.cpp +++ b/src/system/kernel/vm/VMCache.cpp @@ -79,6 +79,11 @@ class VMCacheTraceEntry : public AbstractTraceEntry { } #endif + VMCache* Cache() const + { + return fCache; + } + protected: VMCache* fCache; #if VM_CACHE_TRACING_STACK_TRACE @@ -182,6 +187,11 @@ class AddConsumer : public VMCacheTraceEntry { fConsumer); } + VMCache* Consumer() const + { + return fConsumer; + } + private: VMCache* fConsumer; }; @@ -245,6 +255,11 @@ class InsertArea : public VMCacheTraceEntry { fArea); } + VMArea* Area() const + { + return fArea; + } + private: VMArea* fArea; }; @@ -333,6 +348,115 @@ class RemovePage : public VMCacheTraceEntry { #endif +// #pragma mark - debugger commands + + +#if VM_CACHE_TRACING + + +static void* +cache_stack_find_area_cache(const TraceEntryIterator& baseIterator, void* area) +{ + using namespace VMCacheTracing; + + // find the previous "insert area" entry for the given area + TraceEntryIterator iterator = baseIterator; + TraceEntry* entry = iterator.Current(); + while (entry != NULL) { + if (InsertArea* insertAreaEntry = dynamic_cast(entry)) { + if (insertAreaEntry->Area() == area) + return insertAreaEntry->Cache(); + } + + entry = iterator.Previous(); + } + + return NULL; +} + + +static void* +cache_stack_find_consumer(const TraceEntryIterator& baseIterator, void* cache) +{ + using namespace VMCacheTracing; + + // find the previous "add consumer" or "create" entry for the given cache + TraceEntryIterator iterator = baseIterator; + TraceEntry* entry = iterator.Current(); + while (entry != NULL) { + if (Create* createEntry = dynamic_cast(entry)) { + if (createEntry->Cache() == cache) + return NULL; + } else if (AddConsumer* addEntry = dynamic_cast(entry)) { + if (addEntry->Consumer() == cache) + return addEntry->Cache(); + } + + entry = iterator.Previous(); + } + + return NULL; +} + + +static int +command_cache_stack(int argc, char** argv) +{ + if (argc < 3 || argc > 4) { + print_debugger_command_usage(argv[0]); + return 0; + } + + bool isArea = false; + + int argi = 1; + if (argc == 4) { + if (strcmp(argv[argi], "area") != 0) { + print_debugger_command_usage(argv[0]); + return 0; + } + + argi++; + isArea = true; + } + + uint64 addressValue; + uint64 debugEntryIndex; + if (!evaluate_debug_expression(argv[argi++], &addressValue, false) + || !evaluate_debug_expression(argv[argi++], &debugEntryIndex, false)) { + return 0; + } + + TraceEntryIterator baseIterator; + if (baseIterator.MoveTo((int32)debugEntryIndex) == NULL) { + kprintf("Invalid tracing entry index %" B_PRIu64 "\n", debugEntryIndex); + return 0; + } + + void* address = (void*)(addr_t)addressValue; + + kprintf("cache stack for %s %p at %" B_PRIu64 ":\n", + isArea ? "area" : "cache", address, debugEntryIndex); + if (isArea) { + address = cache_stack_find_area_cache(baseIterator, address); + if (address == NULL) { + kprintf(" cache not found\n"); + return 0; + } + } + + while (address != NULL) { + kprintf(" %p\n", address); + address = cache_stack_find_consumer(baseIterator, address); + } + + return 0; +} + + +#endif // VM_CACHE_TRACING + + // #pragma mark - @@ -343,6 +467,23 @@ vm_cache_init(kernel_args* args) } +void +vm_cache_init_post_heap() +{ +#if VM_CACHE_TRACING + add_debugger_command_etc("cache_stack", &command_cache_stack, + "List the ancestors (sources) of a VMCache at the time given by " + "tracing entry index", + "[ \"area\" ]
\n" + "All ancestors (sources) of a given VMCache at the time given by the\n" + "tracing entry index are listed. If \"area\" is given the supplied\n" + "address is an area instead of a cache address. The listing will\n" + "start with the area's cache at that point.\n", + 0); +#endif // VM_CACHE_TRACING +} + + VMCache* vm_cache_acquire_locked_page_cache(vm_page* page, bool dontWait) { @@ -750,6 +891,46 @@ VMCache::RemoveArea(VMArea* area) } +/*! Transfers the areas from \a fromCache to this cache. This cache must not + have areas yet. Both caches must be locked. +*/ +void +VMCache::TransferAreas(VMCache* fromCache) +{ + AssertLocked(); + fromCache->AssertLocked(); + ASSERT(areas == NULL); + + areas = fromCache->areas; + fromCache->areas = NULL; + + for (VMArea* area = areas; area != NULL; area = area->cache_next) { + area->cache = this; + AcquireRefLocked(); + fromCache->ReleaseRefLocked(); + + T(RemoveArea(fromCache, area)); + T(InsertArea(this, area)); + } +} + + +uint32 +VMCache::CountWritableAreas(VMArea* ignoreArea) const +{ + uint32 count = 0; + + for (VMArea* area = areas; area != NULL; area = area->cache_next) { + if (area != ignoreArea + && (area->protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA)) != 0) { + count++; + } + } + + return count; +} + + status_t VMCache::WriteModified() { diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index c7265d3f0e..c248cf69a0 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -1638,17 +1638,7 @@ vm_copy_on_write_area(VMCache* lowerCache) // transfer the lower cache areas to the upper cache mutex_lock(&sAreaCacheLock); - - upperCache->areas = lowerCache->areas; - lowerCache->areas = NULL; - - for (VMArea* tempArea = upperCache->areas; tempArea != NULL; - tempArea = tempArea->cache_next) { - tempArea->cache = upperCache; - upperCache->AcquireRefLocked(); - lowerCache->ReleaseRefLocked(); - } - + upperCache->TransferAreas(lowerCache); mutex_unlock(&sAreaCacheLock); lowerCache->AddConsumer(upperCache); @@ -1744,23 +1734,6 @@ vm_copy_area(team_id team, const char* name, void** _address, } -//! You need to hold the cache lock when calling this function -static int32 -count_writable_areas(VMCache* cache, VMArea* ignoreArea) -{ - struct VMArea* area = cache->areas; - uint32 count = 0; - - for (; area != NULL; area = area->cache_next) { - if (area != ignoreArea - && (area->protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA)) != 0) - count++; - } - - return count; -} - - static status_t vm_set_area_protection(team_id team, area_id areaID, uint32 newProtection, bool kernel) @@ -1799,7 +1772,7 @@ vm_set_area_protection(team_id team, area_id areaID, uint32 newProtection, // writable -> !writable if (cache->source != NULL && cache->temporary) { - if (count_writable_areas(cache, area) == 0) { + if (cache->CountWritableAreas(area) == 0) { // Since this cache now lives from the pages in its source cache, // we can change the cache's commitment to take only those pages // into account that really are in this cache. @@ -3325,6 +3298,8 @@ vm_init(kernel_args* args) TRACE(("vm_init: exit\n")); + vm_cache_init_post_heap(); + return err; } @@ -4744,7 +4719,7 @@ transfer_area(area_id id, void** _address, uint32 addressSpec, team_id target, if (info.team != thread_get_current_thread()->team->id) return B_PERMISSION_DENIED; - + area_id clonedArea = vm_clone_area(target, info.name, _address, addressSpec, info.protection, REGION_NO_PRIVATE_MAP, id, kernel); if (clonedArea < 0)