| /* |
| * Copyright (c) 2001, 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 sun.jvm.hotspot.debugger.cdbg.basic; |
| |
| import java.util.*; |
| import sun.jvm.hotspot.debugger.*; |
| import sun.jvm.hotspot.debugger.cdbg.*; |
| import sun.jvm.hotspot.utilities.AddressOps; |
| import sun.jvm.hotspot.utilities.Assert; |
| |
| public class BasicCDebugInfoDataBase implements CDebugInfoDataBase { |
| private static final int INITIALIZED_STATE = 0; |
| private static final int CONSTRUCTION_STATE = 1; |
| private static final int RESOLVED_STATE = 2; |
| private static final int COMPLETE_STATE = 3; |
| |
| private int state = INITIALIZED_STATE; |
| |
| /////////// |
| // Types // |
| /////////// |
| |
| // Used only during construction |
| private Map lazyTypeMap; |
| |
| // Used during construction and at run time for iteration |
| private List types; |
| |
| // Used only during runtime |
| private Map nameToTypeMap; |
| |
| ///////////// |
| // Symbols // |
| ///////////// |
| |
| // Used only during construction |
| private Map lazySymMap; |
| |
| // List of blocks in increasing order by starting address. These can |
| // then be binary searched. |
| private List blocks; |
| |
| // Name-to-global symbol table |
| private Map nameToSymMap; |
| |
| ////////////////// |
| // Line numbers // |
| ////////////////// |
| |
| private BasicLineNumberMapping lineNumbers; |
| |
| /** Supports lazy instantiation and references between types and |
| symbols via insertion using arbitrary Object keys that are |
| wrapped by LazyTypes. Once the database has been fully |
| constructed and all types are present, one should call |
| resolveTypes(), which will resolve all LazyTypes down to |
| concrete types (and signal an error if some lazy types were |
| unresolved). */ |
| public void beginConstruction() { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(state == INITIALIZED_STATE, "wrong state"); |
| } |
| state = CONSTRUCTION_STATE; |
| |
| // Types |
| lazyTypeMap = new HashMap(); |
| types = new ArrayList(); |
| |
| // Symbols |
| lazySymMap = new HashMap(); |
| blocks = new ArrayList(); |
| nameToSymMap = new HashMap(); |
| |
| // Line numbers |
| lineNumbers = new BasicLineNumberMapping(); |
| } |
| |
| /** Add a type which may later in construction be referred to via a |
| LazyType with this key. lazyKey may be null. */ |
| public void addType(Object lazyKey, Type type) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(state == CONSTRUCTION_STATE, "wrong state"); |
| } |
| if (lazyKey != null) { |
| if (lazyTypeMap.put(lazyKey, type) != null) { |
| throw new RuntimeException("Type redefined for lazy key " + lazyKey); |
| } |
| } else { |
| types.add(type); |
| } |
| } |
| |
| public void resolve(ResolveListener listener) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(state == CONSTRUCTION_STATE, "wrong state"); |
| } |
| // Go through all types in lazyTypeMap and types. |
| // Resolve all LazyTypes. |
| resolveLazyMap(listener); |
| for (ListIterator iter = types.listIterator(); iter.hasNext(); ) { |
| BasicType t = (BasicType) iter.next(); |
| BasicType t2 = (BasicType) t.resolveTypes(this, listener); |
| if (t != t2) { |
| iter.set(t2); |
| } |
| } |
| // Go through all symbols and resolve references to types and |
| // references to other symbols |
| for (Iterator iter = blocks.iterator(); iter.hasNext(); ) { |
| ((BasicSym) iter.next()).resolve(this, listener); |
| } |
| for (Iterator iter = nameToSymMap.values().iterator(); iter.hasNext(); ) { |
| ((BasicSym) iter.next()).resolve(this, listener); |
| } |
| |
| // Sort blocks in ascending order of starting address (but do not |
| // change ordering among blocks with the same starting address) |
| Collections.sort(blocks, new Comparator() { |
| public int compare(Object o1, Object o2) { |
| BlockSym b1 = (BlockSym) o1; |
| BlockSym b2 = (BlockSym) o2; |
| Address a1 = b1.getAddress(); |
| Address a2 = b2.getAddress(); |
| if (AddressOps.lt(a1, a2)) { return -1; } |
| if (AddressOps.gt(a1, a2)) { return 1; } |
| return 0; |
| } |
| }); |
| |
| state = RESOLVED_STATE; |
| } |
| |
| public void endConstruction() { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(state == RESOLVED_STATE, "wrong state"); |
| } |
| // Move all types to type list |
| for (Iterator iter = lazyTypeMap.values().iterator(); iter.hasNext(); ) { |
| types.add(iter.next()); |
| } |
| // Build name-to-type map |
| nameToTypeMap = new HashMap(); |
| for (Iterator iter = types.iterator(); iter.hasNext(); ) { |
| Type t = (Type) iter.next(); |
| if (!t.isConst() && !t.isVolatile()) { |
| nameToTypeMap.put(t.getName(), t); |
| } |
| } |
| // Lose lazy maps |
| lazyTypeMap = null; |
| lazySymMap = null; |
| // Sort and finish line number information |
| lineNumbers.sort(); |
| // FIXME: on some platforms it might not be necessary to call |
| // recomputeEndPCs(). Will have to see what stabs information |
| // looks like. Should make configurable whether we make this call |
| // or not. |
| lineNumbers.recomputeEndPCs(); |
| |
| state = COMPLETE_STATE; |
| } |
| |
| public Type lookupType(String name) { |
| return lookupType(name, 0); |
| } |
| |
| public Type lookupType(String name, int cvAttributes) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(state == COMPLETE_STATE, "wrong state"); |
| } |
| BasicType t = (BasicType) nameToTypeMap.get(name); |
| if (t != null) { |
| if (cvAttributes != 0) { |
| t = (BasicType) t.getCVVariant(cvAttributes); |
| } |
| } |
| return t; |
| } |
| |
| public void iterate(TypeVisitor v) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(state == COMPLETE_STATE, "wrong state"); |
| } |
| for (Iterator iter = types.iterator(); iter.hasNext(); ) { |
| BasicType t = (BasicType) iter.next(); |
| t.visit(v); |
| } |
| } |
| |
| /** Add a BlockSym to the debug information database. The given |
| BlockSym may be referred to by a LazyBlockSym wrapping the given |
| Object key, which must be non-null. Any references to other |
| blocks (for example, the parent scope) should be made with |
| LazyBlockSyms. These references will be resolved after the |
| database is built. */ |
| public void addBlock(Object key, BlockSym block) { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(key != null, "key must be non-null"); |
| } |
| lazySymMap.put(key, block); |
| blocks.add(block); |
| } |
| |
| public void addGlobalSym(GlobalSym sym) { |
| nameToSymMap.put(sym.getName(), sym); |
| } |
| |
| public BlockSym debugInfoForPC(Address pc) { |
| return searchBlocks(pc, 0, blocks.size() - 1); |
| } |
| |
| public GlobalSym lookupSym(String name) { |
| return (GlobalSym) nameToSymMap.get(name); |
| } |
| |
| public void addLineNumberInfo(BasicLineNumberInfo info) { |
| lineNumbers.addLineNumberInfo(info); |
| } |
| |
| public LineNumberInfo lineNumberForPC(Address pc) throws DebuggerException { |
| return lineNumbers.lineNumberForPC(pc); |
| } |
| |
| public void iterate(LineNumberVisitor v) { |
| lineNumbers.iterate(v); |
| } |
| |
| //---------------------------------------------------------------------- |
| // Internals only below this point |
| // |
| |
| /** Intended only to be used by the BasicType implementation. */ |
| public Type resolveType(Type containingType, Type targetType, ResolveListener listener, String detail) { |
| BasicType basicTargetType = (BasicType) targetType; |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(state == CONSTRUCTION_STATE, "wrong state"); |
| } |
| if (basicTargetType.isLazy()) { |
| BasicType resolved = (BasicType) lazyTypeMap.get(((LazyType) targetType).getKey()); |
| // FIXME: would like to have an assert here that the target is |
| // non-null, but apparently have bugs here with forward |
| // references of pointer types |
| if (resolved == null) { |
| listener.resolveFailed(containingType, (LazyType) targetType, detail + " because target type was not found"); |
| return targetType; |
| } |
| if (resolved.isLazy()) { |
| // Might happen for const/var variants for forward references |
| if (resolved.isConst() || resolved.isVolatile()) { |
| resolved = (BasicType) resolved.resolveTypes(this, listener); |
| } |
| if (resolved.isLazy()) { |
| listener.resolveFailed(containingType, (LazyType) targetType, |
| detail + " because target type (with key " + |
| ((Integer) ((LazyType) resolved).getKey()).intValue() + |
| (resolved.isConst() ? ", const" : ", not const") + |
| (resolved.isVolatile() ? ", volatile" : ", not volatile") + |
| ") was lazy"); |
| } |
| } |
| return resolved; |
| } |
| return targetType; |
| } |
| |
| /** Intended only to be usd by the BasicSym implementation. */ |
| public Type resolveType(Sym containingSymbol, Type targetType, ResolveListener listener, String detail) { |
| BasicType basicTargetType = (BasicType) targetType; |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(state == CONSTRUCTION_STATE, "wrong state"); |
| } |
| if (basicTargetType.isLazy()) { |
| BasicType resolved = (BasicType) lazyTypeMap.get(((LazyType) targetType).getKey()); |
| // FIXME: would like to have an assert here that the target is |
| // non-null, but apparently have bugs here |
| if (resolved == null) { |
| listener.resolveFailed(containingSymbol, (LazyType) targetType, detail); |
| return targetType; |
| } |
| if (resolved.isLazy()) { |
| // Might happen for const/var variants for forward references |
| if (resolved.isConst() || resolved.isVolatile()) { |
| resolved = (BasicType) resolved.resolveTypes(this, listener); |
| } |
| if (resolved.isLazy()) { |
| listener.resolveFailed(containingSymbol, (LazyType) targetType, detail); |
| } |
| } |
| return resolved; |
| } |
| return targetType; |
| } |
| |
| /** Intended only to be usd by the BasicSym implementation. */ |
| public Sym resolveSym(Sym containingSymbol, Sym targetSym, ResolveListener listener, String detail) { |
| if (targetSym == null) return null; |
| BasicSym basicTargetSym = (BasicSym) targetSym; |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(state == CONSTRUCTION_STATE, "wrong state"); |
| } |
| if (basicTargetSym.isLazy()) { |
| BasicSym resolved = (BasicSym) lazySymMap.get(((LazyBlockSym) targetSym).getKey()); |
| // FIXME: would like to have an assert here that the target is |
| // non-null, but apparently have bugs here |
| if (resolved == null) { |
| listener.resolveFailed(containingSymbol, (LazyBlockSym) targetSym, detail); |
| return targetSym; |
| } |
| if (resolved.isLazy()) { |
| listener.resolveFailed(containingSymbol, (LazyBlockSym) targetSym, detail); |
| } |
| return resolved; |
| } |
| return targetSym; |
| } |
| |
| private void resolveLazyMap(ResolveListener listener) { |
| for (Iterator iter = lazyTypeMap.entrySet().iterator(); iter.hasNext(); ) { |
| Map.Entry entry = (Map.Entry) iter.next(); |
| BasicType t = (BasicType) entry.getValue(); |
| BasicType t2 = (BasicType) t.resolveTypes(this, listener); |
| if (t2 != t) { |
| entry.setValue(t2); |
| } |
| } |
| } |
| |
| /** Find the block whose starting address is closest to but less |
| than the given address. */ |
| private BlockSym searchBlocks(Address addr, int lowIdx, int highIdx) { |
| if (highIdx < lowIdx) return null; |
| if ((lowIdx == highIdx) || (lowIdx == highIdx - 1)) { |
| // Base case: start with highIdx and walk backward. See whether |
| // addr is greater than any of the blocks' starting addresses, |
| // and if so, return that block. |
| Address lastAddr = null; |
| BlockSym ret = null; |
| for (int i = highIdx; i >= 0; --i) { |
| BlockSym block = (BlockSym) blocks.get(i); |
| if (AddressOps.lte(block.getAddress(), addr)) { |
| if ((lastAddr == null) || (AddressOps.equal(block.getAddress(), lastAddr))) { |
| lastAddr = block.getAddress(); |
| ret = block; |
| } else { |
| break; |
| } |
| } |
| } |
| return ret; |
| } |
| int midIdx = (lowIdx + highIdx) >> 1; |
| BlockSym block = (BlockSym) blocks.get(midIdx); |
| // See address relationship |
| if (AddressOps.lte(block.getAddress(), addr)) { |
| // Always move search up |
| return searchBlocks(addr, midIdx, highIdx); |
| } else { |
| // Always move search down |
| return searchBlocks(addr, lowIdx, midIdx); |
| } |
| } |
| } |