/*
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2020, Red Hat Inc. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.oracle.objectfile.debugentry;
import com.oracle.objectfile.debuginfo.DebugInfoProvider;
import org.graalvm.compiler.debug.DebugContext;
import java.nio.ByteOrder;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
An abstract class which indexes the information presented by the DebugInfoProvider in an
organization suitable for use by subclasses targeting a specific binary format.
/**
* An abstract class which indexes the information presented by the DebugInfoProvider in an
* organization suitable for use by subclasses targeting a specific binary format.
*/
public abstract class DebugInfoBase {
protected ByteOrder byteOrder;
A table listing all known strings, some of which may be marked for insertion into the
debug_str section.
/**
* A table listing all known strings, some of which may be marked for insertion into the
* debug_str section.
*/
private StringTable stringTable = new StringTable();
Index of all dirs in which files are found to reside either as part of substrate/compiler or
user code.
/**
* Index of all dirs in which files are found to reside either as part of substrate/compiler or
* user code.
*/
private Map<Path, DirEntry> dirsIndex = new HashMap<>();
/*
* The obvious traversal structure for debug records is:
*
* 1) by top level compiled method (primary Range) ordered by ascending address
*
* 2) by inlined method (sub range) within top level method ordered by ascending address
*
* These can be used to ensure that all debug records are generated in increasing address order
*
* An alternative traversal option is
*
* 1) by top level class (String id)
*
* 2) by top level compiled method (primary Range) within a class ordered by ascending address
*
* 3) by inlined method (sub range) within top level method ordered by ascending address
*
* This relies on the (current) fact that methods of a given class always appear in a single
* continuous address range with no intervening code from other methods or data values. this
* means we can treat each class as a compilation unit, allowing data common to all methods of
* the class to be shared.
*
* A third option appears to be to traverse via files, then top level class within file etc.
* Unfortunately, files cannot be treated as the compilation unit. A file F may contain multiple
* classes, say C1 and C2. There is no guarantee that methods for some other class C' in file F'
* will not be compiled into the address space interleaved between methods of C1 and C2. That is
* a shame because generating debug info records one file at a time would allow more sharing
* e.g. enabling all classes in a file to share a single copy of the file and dir tables.
*/
List of class entries detailing class info for primary ranges.
/**
* List of class entries detailing class info for primary ranges.
*/
private LinkedList<ClassEntry> primaryClasses = new LinkedList<>();
index of already seen classes.
/**
* index of already seen classes.
*/
private Map<String, ClassEntry> primaryClassesIndex = new HashMap<>();
Index of files which contain primary or secondary ranges.
/**
* Index of files which contain primary or secondary ranges.
*/
private Map<Path, FileEntry> filesIndex = new HashMap<>();
List of of files which contain primary or secondary ranges.
/**
* List of of files which contain primary or secondary ranges.
*/
private LinkedList<FileEntry> files = new LinkedList<>();
public DebugInfoBase(ByteOrder byteOrder) {
this.byteOrder = byteOrder;
}
Entry point allowing ELFObjectFile to pass on information about types, code and heap data.
Params: - debugInfoProvider – provider instance passed by ObjectFile client.
/**
* Entry point allowing ELFObjectFile to pass on information about types, code and heap data.
*
* @param debugInfoProvider provider instance passed by ObjectFile client.
*/
@SuppressWarnings("try")
public void installDebugInfo(DebugInfoProvider debugInfoProvider) {
/*
* This will be needed once we add support for type info:
*
* DebugTypeInfoProvider typeInfoProvider = debugInfoProvider.typeInfoProvider(); for
* (DebugTypeInfo debugTypeInfo : typeInfoProvider) { install types }
*/
/*
* Ensure we have a null string in the string section.
*/
stringTable.uniqueDebugString("");
debugInfoProvider.codeInfoProvider().forEach(debugCodeInfo -> debugCodeInfo.debugContext((debugContext) -> {
/*
* primary file name and full method name need to be written to the debug_str section
*/
String fileName = debugCodeInfo.fileName();
Path filePath = debugCodeInfo.filePath();
Path cachePath = debugCodeInfo.cachePath();
// switch '$' in class names for '.'
String className = debugCodeInfo.className().replaceAll("\\$", ".");
String methodName = debugCodeInfo.methodName();
String symbolName = debugCodeInfo.symbolNameForMethod();
String paramNames = debugCodeInfo.paramNames();
String returnTypeName = debugCodeInfo.returnTypeName();
int lo = debugCodeInfo.addressLo();
int hi = debugCodeInfo.addressHi();
int primaryLine = debugCodeInfo.line();
boolean isDeoptTarget = debugCodeInfo.isDeoptTarget();
Range primaryRange = new Range(fileName, filePath, cachePath, className, methodName, symbolName, paramNames, returnTypeName, stringTable, lo, hi, primaryLine, isDeoptTarget);
debugContext.log(DebugContext.INFO_LEVEL, "PrimaryRange %s.%s %s %s:%d [0x%x, 0x%x]", className, methodName, filePath, fileName, primaryLine, lo, hi);
addRange(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize());
debugCodeInfo.lineInfoProvider().forEach(debugLineInfo -> {
String fileNameAtLine = debugLineInfo.fileName();
Path filePathAtLine = debugLineInfo.filePath();
// Switch '$' in class names for '.'
String classNameAtLine = debugLineInfo.className().replaceAll("\\$", ".");
String methodNameAtLine = debugLineInfo.methodName();
String symbolNameAtLine = debugLineInfo.symbolNameForMethod();
int loAtLine = lo + debugLineInfo.addressLo();
int hiAtLine = lo + debugLineInfo.addressHi();
int line = debugLineInfo.line();
Path cachePathAtLine = debugLineInfo.cachePath();
/*
* Record all subranges even if they have no line or file so we at least get a
* symbol for them.
*/
Range subRange = new Range(fileNameAtLine, filePathAtLine, cachePathAtLine, classNameAtLine, methodNameAtLine, symbolNameAtLine, "", "", stringTable, loAtLine, hiAtLine, line,
primaryRange);
addSubRange(primaryRange, subRange);
try (DebugContext.Scope s = debugContext.scope("Subranges")) {
debugContext.log(DebugContext.VERBOSE_LEVEL, "SubRange %s.%s %s %s:%d 0x%x, 0x%x]", classNameAtLine, methodNameAtLine, filePathAtLine, fileNameAtLine, line, loAtLine, hiAtLine);
}
});
}));
/*
* This will be needed once we add support for data info:
*
* DebugDataInfoProvider dataInfoProvider = debugInfoProvider.dataInfoProvider(); for
* (DebugDataInfo debugDataInfo : dataInfoProvider) { install details of heap elements
* String name = debugDataInfo.toString(); }
*/
}
private ClassEntry ensureClassEntry(Range range) {
String className = range.getClassName();
/*
* See if we already have an entry.
*/
ClassEntry classEntry = primaryClassesIndex.get(className);
if (classEntry == null) {
/*
* Create and index the entry associating it with the right file.
*/
FileEntry fileEntry = ensureFileEntry(range);
classEntry = new ClassEntry(className, fileEntry);
primaryClasses.add(classEntry);
primaryClassesIndex.put(className, classEntry);
}
assert classEntry.getClassName().equals(className);
return classEntry;
}
private FileEntry ensureFileEntry(Range range) {
String fileName = range.getFileName();
if (fileName == null) {
return null;
}
Path filePath = range.getFilePath();
Path fileAsPath = range.getFileAsPath();
/*
* Ensure we have an entry.
*/
FileEntry fileEntry = filesIndex.get(fileAsPath);
if (fileEntry == null) {
DirEntry dirEntry = ensureDirEntry(filePath);
fileEntry = new FileEntry(fileName, dirEntry, range.getCachePath());
files.add(fileEntry);
/*
* Index the file entry by file path.
*/
filesIndex.put(fileAsPath, fileEntry);
if (!range.isPrimary()) {
/* Check we have a file for the corresponding primary range. */
Range primaryRange = range.getPrimary();
FileEntry primaryFileEntry = filesIndex.get(primaryRange.getFileAsPath());
assert primaryFileEntry != null;
}
}
return fileEntry;
}
private void addRange(Range primaryRange, List<DebugInfoProvider.DebugFrameSizeChange> frameSizeInfos, int frameSize) {
assert primaryRange.isPrimary();
ClassEntry classEntry = ensureClassEntry(primaryRange);
classEntry.addPrimary(primaryRange, frameSizeInfos, frameSize);
}
private void addSubRange(Range primaryRange, Range subrange) {
assert primaryRange.isPrimary();
assert !subrange.isPrimary();
String className = primaryRange.getClassName();
ClassEntry classEntry = primaryClassesIndex.get(className);
FileEntry subrangeFileEntry = ensureFileEntry(subrange);
/*
* The primary range should already have been seen and associated with a primary class
* entry.
*/
assert classEntry.primaryIndexFor(primaryRange) != null;
if (subrangeFileEntry != null) {
classEntry.addSubRange(subrange, subrangeFileEntry);
}
}
private DirEntry ensureDirEntry(Path filePath) {
if (filePath == null) {
return null;
}
DirEntry dirEntry = dirsIndex.get(filePath);
if (dirEntry == null) {
dirEntry = new DirEntry(filePath);
dirsIndex.put(filePath, dirEntry);
}
return dirEntry;
}
/* Accessors to query the debug info model. */
public ByteOrder getByteOrder() {
return byteOrder;
}
public LinkedList<ClassEntry> getPrimaryClasses() {
return primaryClasses;
}
@SuppressWarnings("unused")
public LinkedList<FileEntry> getFiles() {
return files;
}
@SuppressWarnings("unused")
public FileEntry findFile(Path fullFileName) {
return filesIndex.get(fullFileName);
}
public StringTable getStringTable() {
return stringTable;
}
Indirects this call to the string table.
Params: - string – the string whose index is required.
Returns: the offset of the string in the .debug_str section.
/**
* Indirects this call to the string table.
*
* @param string the string whose index is required.
*
* @return the offset of the string in the .debug_str section.
*/
public int debugStringIndex(String string) {
return stringTable.debugStringIndex(string);
}
}