AutoDump.cpp   [plain text]


/*
 * Copyright (c) 2009 Apple Inc. All rights reserved.
 *
 * @APPLE_APACHE_LICENSE_HEADER_START@
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * @APPLE_APACHE_LICENSE_HEADER_END@
 */
/*
    AutoDump.cpp
    Copyright (c) 2009 Apple Inc. All rights reserved.
 */
 
#include "AutoAdmin.h"
#include "AutoBitmap.h"
#include "AutoBlockIterator.h"
#include "AutoCollector.h"
#include "AutoConfiguration.h"
#include "AutoDefs.h"
#include "AutoEnvironment.h"
#include "AutoLarge.h"
#include "AutoLock.h"
#include "AutoRange.h"
#include "AutoRegion.h"
#include "AutoStatistics.h"
#include "AutoSubzone.h"
#include "AutoMemoryScanner.h"
#include "AutoThread.h"
#include "AutoWriteBarrierIterator.h"
#include "AutoThreadLocalCollector.h"
#include "AutoZone.h"

#include "auto_weak.h"
#include "auto_trace.h"
 
namespace Auto {

    struct dump_all_blocks_visitor {
        void (^node_dump)(const void *address, unsigned long size, unsigned int layout, unsigned long refcount);
        
        // Constructor
        dump_all_blocks_visitor(void) {}
        
        // visitor function for subzone
        inline bool visit(Zone *zone, Subzone *subzone, usword_t q) {
            // send single block information
            node_dump(subzone->quantum_address(q), subzone->size(q), subzone->layout(q), subzone->refcount(q));
            // always continue
            return true;
        }
        
        // visitor function for large
        inline bool visit(Zone *zone, Large *large) {
            // send single block information
            node_dump(large->address(), large->size(), large->layout(), large->refcount());
            // always continue
            return true;
        }
    };

    void Zone::dump(
            auto_zone_stack_dump stack_dump,
            auto_zone_register_dump register_dump,
            auto_zone_node_dump thread_local_node_dump,  // unused
            auto_zone_root_dump root_dump,
            auto_zone_node_dump global_node_dump,
            auto_zone_weak_dump weak_dump_entry)
    {
        // Lock out new threads and suspend all others.
        // We don't take many locks nor are we dependent on anything (much) that requires a lock.
        // We don't take, for example, the large lock and are willing to miss a brand new one.
        // We don't take, for example, the regions lock, and are willing to miss a new empty region.
        // We don't take, for example, the refcounts lock, and are willing to provide an inexact refcount.
        // We don't take, for example, the admin locks, and will miss a not-quite-born object.
        
        // XXX need associative refs, too
        
        // XXX_PCB:  grab the thread list mutex, so newly registered threads can't interfere.
        // This can deadlock if called from gdb with other threads suspended
        Mutex lock(&_registered_threads_mutex);
        
        // suspend all threads...
        Thread *thread = threads();
        while (thread != NULL) {
            if (!thread->is_current_thread() && thread->is_bound()) {
                thread->suspend();
            }
            thread = thread->next();
        }
        
        // for all nodes
        dump_all_blocks_visitor visitor;
        visitor.node_dump = global_node_dump;
        visitAllocatedBlocks(this, visitor);

        thread = threads();
        while (thread != NULL) {
            thread->dump(stack_dump, register_dump, thread_local_node_dump);
            thread = thread->next();
        }
        
        // for all roots
        if (root_dump) {
            SpinLock lock(&_roots_lock);
            PtrHashSet::iterator i = _roots.begin();
            while (i != _roots.end()) {
                root_dump((const void **)*i);
                i++;
            }
        }
        
        
        // for all weak
        if (weak_dump_entry) {
            SpinLock lock(&weak_refs_table_lock);
            weak_dump_table(this, weak_dump_entry);
        }
        
        // resume threads

        thread = threads();
        while (thread != NULL) {
            if (!thread->is_current_thread() && thread->is_bound()) thread->resume();
            thread = thread->next();
        }
    }
};