/* * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.tele.debug; import java.util.*; import com.sun.max.platform.*; import com.sun.max.program.*; import com.sun.max.tele.*; import com.sun.max.tele.memory.*; import com.sun.max.tele.object.*; import com.sun.max.tele.util.*; import com.sun.max.unsafe.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.runtime.SafepointPoll.State; import com.sun.max.vm.thread.*; /** * Access to a block of thread local storage. * * @see VmThreadLocal */ public final class TeleThreadLocalsBlock extends AbstractVmHolder implements TeleVMCache, MaxThreadLocalsBlock { private static final int TRACE_VALUE = 2; private final TimedTrace updateTracer; private long lastUpdateEpoch = -1L; /** * Description of the memory region occupied by a {@linkplain MaxThreadLocalsBlock thread locals block} in the VM. * <br> * This region has no parent; it is allocated from the OS. * <br> * This region's children are * the {@linkplain MaxThreadLocalsArea thread locals areas} it contains. */ private final class ThreadLocalsBlockMemoryRegion extends TeleFixedMemoryRegion implements MaxEntityMemoryRegion<MaxThreadLocalsBlock> { private final TeleThreadLocalsBlock teleThreadLocalsBlock; private ThreadLocalsBlockMemoryRegion(MaxVM vm, TeleThreadLocalsBlock owner, String regionName, Address start, long nBytes) { super(vm, regionName, start, nBytes); this.teleThreadLocalsBlock = owner; } public MaxEntityMemoryRegion<? extends MaxEntity> parent() { // Thread local memory blocks are allocated from the OS, not part of any other region return null; } public List<MaxEntityMemoryRegion<? extends MaxEntity>> children() { if (threadLocalsBlockMemoryRegion == null) { return new ArrayList<MaxEntityMemoryRegion<? extends MaxEntity>>(0); } final List<MaxEntityMemoryRegion<? extends MaxEntity>> regions = new ArrayList<MaxEntityMemoryRegion<? extends MaxEntity>>(areas.size()); for (TeleThreadLocalsArea teleThreadLocalsArea : areas.values()) { if (teleThreadLocalsArea != null) { regions.add(teleThreadLocalsArea.memoryRegion()); } } return regions; } public MaxThreadLocalsBlock owner() { return teleThreadLocalsBlock; } } private final String entityName; private final String entityDescription; private final TeleNativeThread teleNativeThread; /** * The region of VM memory occupied by this block, null if this is a dummy * for which there are no locals (as for a native thread). * <p> * Don't null this field out when the thread is known to have died; * we'll need to keep track of it to update update information about memory allocations. */ private final ThreadLocalsBlockMemoryRegion threadLocalsBlockMemoryRegion; /** * The thread locals areas for each state; null if no actual thread locals allocated. */ private final Map<SafepointPoll.State, TeleThreadLocalsArea> areas; private final int offsetToTTLA; /** * Control to prevent infinite recursion due to cycle in call path. */ private boolean updatingCache = false; /** * The VM thread object pointed to by the most recently read value of a particular thread local variable. */ private TeleVmThread teleVmThread = null; /** * Creates an accessor for thread local information in the ordinary case. * * @param teleNativeThread the thread owning the thread local information * @param regionName descriptive name for this thread locals block in the VM * @param start starting location of the memory associated with this entity in the VM. * @param nBytes length of the memory associated with this entity in the VM. */ public TeleThreadLocalsBlock(TeleNativeThread teleNativeThread, String regionName, Address start, long nBytes) { super(teleNativeThread.vm()); final TimedTrace tracer = new TimedTrace(TRACE_VALUE, tracePrefix() + " creating"); tracer.begin(); this.teleNativeThread = teleNativeThread; this.entityName = regionName; this.threadLocalsBlockMemoryRegion = new ThreadLocalsBlockMemoryRegion(teleNativeThread.vm(), this, regionName, start, nBytes); teleNativeThread.vm().addressSpace().add(threadLocalsBlockMemoryRegion); this.areas = new EnumMap<SafepointPoll.State, TeleThreadLocalsArea>(SafepointPoll.State.class); this.offsetToTTLA = Platform.platform().pageSize - Word.size(); this.entityDescription = "VM thread-local variables owned by thread " + teleNativeThread.entityName(); this.updateTracer = new TimedTrace(TRACE_VALUE, tracePrefix() + " updating"); tracer.end(null); } /** * Creates an accessor for thread local information in the special case where there is actually no thread local storage * identified. This might happen if the thread is non-Java, or isn't far enough along in its creation sequence for * the storage to be known. * * @param teleNativeThread the thread owning the thread local information * @param name a descriptive name for the area, in the absence of one associated with a memory region */ public TeleThreadLocalsBlock(TeleNativeThread teleNativeThread, String name) { super(teleNativeThread.vm()); final TimedTrace tracer = new TimedTrace(TRACE_VALUE, tracePrefix() + " creating"); tracer.begin(); this.teleNativeThread = teleNativeThread; this.entityName = name; this.threadLocalsBlockMemoryRegion = null; this.areas = null; this.offsetToTTLA = Platform.platform().pageSize - Word.size(); this.entityDescription = "VM thread-local variables owned by thread " + teleNativeThread.entityName(); this.updateTracer = new TimedTrace(TRACE_VALUE, tracePrefix() + " updating"); tracer.end(null); } public void updateCache(long epoch) { if (threadLocalsBlockMemoryRegion != null) { // This gets called redundantly from several places; be sure it only gets done once per epoch. if (epoch > lastUpdateEpoch) { assert vm().lockHeldByCurrentThread(); if (updatingCache) { return; } updatingCache = true; updateTracer.begin(); for (TeleThreadLocalsArea teleThreadLocalsArea : areas.values()) { if (teleThreadLocalsArea != null) { teleThreadLocalsArea.updateCache(epoch); } } final TeleThreadLocalsArea enabledThreadLocalsArea = areas.get(SafepointPoll.State.ENABLED); if (enabledThreadLocalsArea != null) { final Word threadLocalValue = enabledThreadLocalsArea.getWord(VmThreadLocal.VM_THREAD); if (threadLocalValue.isNotZero()) { teleVmThread = (TeleVmThread) objects().findObjectAt(threadLocalValue.asAddress()); } } updatingCache = false; lastUpdateEpoch = epoch; updateTracer.end(null); } else { Trace.line(TRACE_VALUE, tracePrefix() + "redundant update epoch=" + epoch + ": " + this); } } } public String entityName() { return entityName; } public String entityDescription() { return entityDescription; } public MaxEntityMemoryRegion<MaxThreadLocalsBlock> memoryRegion() { return threadLocalsBlockMemoryRegion; } public boolean contains(Address address) { return threadLocalsBlockMemoryRegion.contains(address); } public TeleObject representation() { // No distinguished object in VM runtime represents this. return null; } public TeleNativeThread thread() { return teleNativeThread; } public TeleThreadLocalsArea tlaFor(State state) { if (threadLocalsBlockMemoryRegion != null) { updateCache(vm().teleProcess().epoch()); return areas.get(state); } return null; } public MaxThreadLocalsArea findTLA(Address address) { if (threadLocalsBlockMemoryRegion != null) { for (SafepointPoll.State state : SafepointPoll.State.CONSTANTS) { final TeleThreadLocalsArea tla = tlaFor(state); if (tla.memoryRegion().contains(address)) { return tla; } } } return null; } /** * Gets the value of the thread local variable holding a reference to the VM thread corresponding to the native * thread. * * @return access to the VM thread corresponding to this thread, if any */ TeleVmThread teleVmThread() { return teleVmThread; } /** * Update any state related to this thread locals area, based on possibly more information having been acquired. * * @param threadLocalsRegion the memory region containing the thread locals block * @param tlaSize the size in bytes of each Thread Locals Area in the region. */ void updateAfterGather(TeleFixedMemoryRegion threadLocalsRegion, int tlaSize) { if (threadLocalsRegion != null) { for (SafepointPoll.State safepointState : SafepointPoll.State.CONSTANTS) { final Pointer tlaStartPointer = getThreadLocalsAreaStart(threadLocalsRegion, tlaSize, safepointState); // Only create a new TeleThreadLocalsArea if the start address has changed which // should only happen once going from 0 to a non-zero value. final TeleThreadLocalsArea area = areas.get(safepointState); if (area == null || !area.memoryRegion().start().equals(tlaStartPointer)) { areas.put(safepointState, new TeleThreadLocalsArea(vm(), thread(), safepointState, tlaStartPointer)); } } updateCache(vm().teleProcess().epoch()); } } /** * Removes any state associated with the thread, typically because the thread has died. */ void clear() { if (threadLocalsBlockMemoryRegion != null) { vm().addressSpace().remove(threadLocalsBlockMemoryRegion); areas.clear(); teleVmThread = null; lastUpdateEpoch = vm().teleProcess().epoch(); } } /** * Gets the address of one of the three thread locals areas inside a given thread locals region. * * @param threadLocalsRegion the VM memory region containing the thread locals block * @param tlaSize the size of a thread locals area within the region * @param safepointState denotes which of the three thread locals areas is being requested * @return the address of the thread locals areas in {@code threadLocalsRegion} corresponding to {@code state} * @see VmThreadLocal */ private Pointer getThreadLocalsAreaStart(TeleFixedMemoryRegion threadLocalsRegion, int tlaSize, SafepointPoll.State safepointState) { if (threadLocalsRegion != null) { return threadLocalsRegion.start().plus(offsetToTTLA).plus(tlaSize * safepointState.ordinal()).asPointer(); } return null; } }