From ab1cb427a768786f0e6a70ae58e907861d8969de Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 21 Jan 2020 17:11:44 +0000 Subject: [PATCH 1/9] Implement basic debuginfo generation for ELF objects --- substratevm/DEBUGINFO.md | 127 + .../src/com/oracle/objectfile/ObjectFile.java | 11 + .../debuginfo/DebugInfoProvider.java | 134 + .../oracle/objectfile/elf/ELFObjectFile.java | 38 + .../objectfile/elf/ELFRelocationSection.java | 10 + .../objectfile/elf/dwarf/ClassEntry.java | 230 ++ .../oracle/objectfile/elf/dwarf/DirEntry.java | 43 + .../objectfile/elf/dwarf/DwarfSections.java | 2336 +++++++++++++++++ .../objectfile/elf/dwarf/FileEntry.java | 57 + .../objectfile/elf/dwarf/PrimaryEntry.java | 172 ++ .../oracle/objectfile/elf/dwarf/Range.java | 132 + .../objectfile/elf/dwarf/StringEntry.java | 92 + .../objectfile/elf/dwarf/StringTable.java | 106 + .../svm/hosted/image/NativeBootImage.java | 316 +++ substratevm/write_gdbsourcepath | 193 ++ 15 files changed, 3997 insertions(+) create mode 100644 substratevm/DEBUGINFO.md create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/PrimaryEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java create mode 100644 substratevm/write_gdbsourcepath diff --git a/substratevm/DEBUGINFO.md b/substratevm/DEBUGINFO.md new file mode 100644 index 000000000000..5b859cd3cd7a --- /dev/null +++ b/substratevm/DEBUGINFO.md @@ -0,0 +1,127 @@ +Using the ptototype debug info feature +-------------------------------------- + +To add debug info to a generated native image add flag +-H:+TrackNodeSourcePosition to the native image command line. + + mx native-image -H:+TrackNodeSourcePosition Hello.java + +The resulting image should contain code (method) debug symbols. + +What is currently implemented +----------------------------- + +The currently implemented features include: + + - break points configured by file and line or by method name + - single stepping by line including both into and over function calls + - stack backtraces (not including frames detailing inlined code) + +Note that single stepping within a compiled method includes file and +line number info for inlined code, including inlined Graal methods. +So, gdb may switch files even though you are still in the same +compiled method. + +Identifying the location of source code +--------------------------------------- + +In order for gdb to be able to locate the source files for your app +methods, Graal methods and JDK runtime methods you need to provide gdb +with a list of source root dirs a 'set directories' command: + + (gdb) set directories /home/adinn/hello/src:/home/adinn/graal/sdk/src/org/graalvm.word/src:/home/adinn/graal/sdk/src/org.graalvm.options/src:... + +The argument is a comma separated list of source roots. It needs to +identify: + + - sources for your app + - sources under the Graal sdk, compiler, substratevm and truffle trees + - sources in the JDK src.zip file + +Needless to say the list for Graal is long and complex. Also, the JDK +sources are in a zip file and gdb does not understand zip sources. So +you need to extract the JDK sources as a preparatory step, + +You can use shell script write_gdbsourcepath (added to Graal dir +substratevm) to auto-generate settings for the GRaal and JDK sources. + + $ bash write_gdbsourcepath + +It creates a local file .gdbsourcepath which sets the relevant +directories. Before running it you can set two env vars to tell it +where to locate the source trees it needs to include: + + - GRAAL_JAVA_SRC_ROOT should point to the dir into which you have + unzipped the src.zip from your Graal JDK release + - GRAAL_SRC_ROOT should point to the dir in which your Graal git + tree checkout is located + +Note that the script ignores test source dirs and jdk dirs that do not +match the release level of the JDK (i.e. if you use jdk8 it will ony +include jdk8 src dirs). The script runs the java command available via +JAVA_HOME to idenitfy which JDK is in use). + +If you run the script from the substratevm dir of your Graal git repo +checkout the script will default GRAAL_SRC_ROOT to the parent dir. + +If JAVA_HOME is set the script will default GRAAL_JAVA_SRC_ROOT to +$JAVA_HOME/src + +Checking debug info +------------------- + +The objdump command can be used to display the dbeug info embedded +into a native image. The following commands (which all assume the +target binary is called hello) can be used to display all currentyl +generated content: + + $ objdump --dwarf=info hello > info + $ objdump --dwarf=abbrev hello > abbrev + $ objdump --dwarf=ranges hello > ranges + $ objdump --dwarf=decodedline hello > decodedline + $ objdump --dwarf=rawline hello > rawline + $ objdump --dwarf=str hello > str + $ objdump --dwarf=frames hello > frames + +The *info* section includes details of all compiled Java methods. + +The *abbrev* sectio defines the layout of records in the info section +that describe Java files (compilation units) and methods. + +The *ranges* section details the start and end addresses of method +code segments + +The *decodedline* section maps subsegments of method code range +segments to files and line numbers. This mapping includes entries +for files and line numbers for inlined methods. + +The *rawline* segment provides deatails of how the line table is +generated using DWARF state machine instuctions that encode file, +line and address transitions. + +The *str* section provides a lookup table for strings referenced +from records in the info section + +The *frames* section lists transition points in compiled methods +where a (fixed size) stack frame is pushed or popped, allowing +the debugger to identify each frame's current and previous stack +pointers and it's return address. + +Note that some of the content embedded in the debug records is +generated by the C compiler and belongs to code that is either in +libraries or the C lib bootstrap code that is bundled in with the +Java method code. + +Currently supported targets +--------------------------- + +The prototype is currently implemented only for gdb on Linux. + + - Linux/x86_64 suppoort has been tested and should work + correctly. + + - Linux/AArch64 support is present but has not yet been fully + verified (break points should work ok but stack backtraces + may be incorrect). + +Windows support is still under development. diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java index cbf13d644688..518b171882d4 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java @@ -45,6 +45,7 @@ import java.util.TreeSet; import java.util.stream.StreamSupport; +import com.oracle.objectfile.debuginfo.DebugInfoProvider; import com.oracle.objectfile.elf.ELFObjectFile; import com.oracle.objectfile.macho.MachOObjectFile; import com.oracle.objectfile.pecoff.PECoffObjectFile; @@ -1086,6 +1087,16 @@ protected boolean elementsCanSharePage(Element s1, Element s2, int offset1, int // flag compatibility } + /** + * API method provided to allow a native image generator to provide details of + * types, code and heap data inserted into a native image. + * @param debugInfoProvider an implementation of the provider interface that + * communicates details of the relevant types, code and heap data. + */ + public void installDebugInfo(@SuppressWarnings("unused") DebugInfoProvider debugInfoProvider) { + // do nothing by default + } + protected static Iterable allDecisions(final Map decisions) { return () -> StreamSupport.stream(decisions.values().spliterator(), false) .flatMap(layoutDecisionMap -> StreamSupport.stream(layoutDecisionMap.spliterator(), false)).iterator(); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java new file mode 100644 index 000000000000..13e8cb73dab3 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.debuginfo; + +import java.util.List; + +/** + * interfaces used to allow a native image to communicate + * details of types, code and data to the underlying + * object file so that the latter can insert appropriate + * debug info. + */ +public interface DebugInfoProvider { + /** + * access details of a specific type. + */ + interface DebugTypeInfo { + } + + /** + * access details of a specific compiled method. + */ + interface DebugCodeInfo { + String fileName(); + + String className(); + + String methodName(); + + int addressLo(); + + int addressHi(); + + int line(); + + DebugLineInfoProvider lineInfoProvider(); + + String paramNames(); + + String returnTypeName(); + + int getFrameSize(); + + List getFrameSizeChanges(); + } + + /** + * access details of a specific heap object. + */ + interface DebugDataInfo { + } + + /** + * access details of a specific outer or inlined method at a given line number. + */ + interface DebugLineInfo { + String fileName(); + + String className(); + + String methodName(); + + int addressLo(); + + int addressHi(); + + int line(); + } + + interface DebugFrameSizeChange { + enum Type { + EXTEND, + CONTRACT + } + + int getOffset(); + + DebugFrameSizeChange.Type getType(); + } + + /** + * convenience interface defining iterator type. + */ + interface DebugTypeInfoProvider extends Iterable { + } + + /** + * convenience interface defining iterator type. + */ + interface DebugCodeInfoProvider extends Iterable { + } + + /** + * convenience interface defining iterator type. + */ + interface DebugLineInfoProvider extends Iterable { + } + + /** + * convenience interface defining iterator type. + */ + interface DebugDataInfoProvider extends Iterable { + } + + DebugTypeInfoProvider typeInfoProvider(); + + DebugCodeInfoProvider codeInfoProvider(); + + DebugDataInfoProvider dataInfoProvider(); +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java index 6a2f99a778ba..be10b81edd80 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java @@ -42,6 +42,8 @@ import com.oracle.objectfile.ObjectFile; import com.oracle.objectfile.StringTable; import com.oracle.objectfile.SymbolTable; +import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import com.oracle.objectfile.elf.dwarf.DwarfSections; import com.oracle.objectfile.io.AssemblyBuffer; import com.oracle.objectfile.io.OutputAssembler; @@ -1156,4 +1158,40 @@ public SymbolTable getSymbolTable() { protected int getMinimumFileSize() { return 0; } + + @Override + public void installDebugInfo(DebugInfoProvider debugInfoProvider) { + DwarfSections dwarfSections = new DwarfSections(getMachine()); + // we need an implementation for each section + DwarfSections.DwarfStrSectionImpl elfStrSectionImpl = dwarfSections.getStrSectionImpl(); + DwarfSections.DwarfAbbrevSectionImpl elfAbbrevSectionImpl = dwarfSections.getAbbrevSectionImpl(); + DwarfSections.DwarfFrameSectionImpl frameSectionImpl = dwarfSections.getFrameSectionImpl(); + DwarfSections.DwarfInfoSectionImpl elfInfoSectionImpl = dwarfSections.getInfoSectionImpl(); + DwarfSections.DwarfARangesSectionImpl elfARangesSectionImpl = dwarfSections.getARangesSectionImpl(); + DwarfSections.DwarfLineSectionImpl elfLineSectionImpl = dwarfSections.getLineSectionImpl(); + // now we can create the section elements with empty content + newUserDefinedSection(elfStrSectionImpl.getSectionName(), elfStrSectionImpl); + newUserDefinedSection(elfAbbrevSectionImpl.getSectionName(), elfAbbrevSectionImpl); + newUserDefinedSection(frameSectionImpl.getSectionName(), frameSectionImpl); + newUserDefinedSection(elfInfoSectionImpl.getSectionName(), elfInfoSectionImpl); + newUserDefinedSection(elfARangesSectionImpl.getSectionName(), elfARangesSectionImpl); + newUserDefinedSection(elfLineSectionImpl.getSectionName(), elfLineSectionImpl); + // the byte[] for each implementation's content are created and + // written under getOrDecideContent. doing that ensures that all + // dependent sections are filled in and then sized according to the + // declared dependencies. however, if we leave it at that then + // associated reloc sections only get created when the first reloc + // is inserted during content write that's too late for them to have + // layout constraints included in the layout decision set and causes + // an NPE during reloc section write. so we need to create the relevant + // reloc sections here in advance + elfStrSectionImpl.getOrCreateRelocationElement(false); + elfAbbrevSectionImpl.getOrCreateRelocationElement(false); + frameSectionImpl.getOrCreateRelocationElement(false); + elfInfoSectionImpl.getOrCreateRelocationElement(false); + elfARangesSectionImpl.getOrCreateRelocationElement(false); + elfLineSectionImpl.getOrCreateRelocationElement(false); + // ok now we can populate the implementations + dwarfSections.installDebugInfo(debugInfoProvider); + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFRelocationSection.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFRelocationSection.java index 1209c6662099..7826a6a786ba 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFRelocationSection.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFRelocationSection.java @@ -236,6 +236,16 @@ public Iterable getDependencies(Map // decisions.get(syms).getDecision(LayoutProperty.Kind.CONTENT); // deps.add(BuildDependency.createOrGet(ourContent, symtabContent)); /* If we're dynamic, it also depends on the vaddr of all referenced sections. */ + + // our content depends on the content of the section being relocated + // (because entries only get registered during generation) + if (relocated != null) { + LayoutDecision relocatedSectionContent = + decisions.get(relocated).getDecision(LayoutDecision.Kind.CONTENT); + deps.add(BuildDependency.createOrGet(ourContent, + relocatedSectionContent)); + } + if (isDynamic()) { Set referenced = new HashSet<>(); for (Entry ent : entries.keySet()) { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java new file mode 100644 index 000000000000..9e429a565e49 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * track debug info associated with a Java class. + */ +public class ClassEntry { + /** + * the name of the associated class. + */ + private String className; + /** + * details of the associated file. + */ + FileEntry fileEntry; + /** + * a list recording details of all primary + * ranges included in this class sorted by + * ascending address range. + */ + private LinkedList primaryEntries; + /** + * an index identifying primary ranges which + * have already been encountered. + */ + private Map primaryIndex; + /** + * an index of all primary and secondary files + * referenced from this class's compilation unit. + */ + private Map localFilesIndex; + /** + * a list of the same files. + */ + private LinkedList localFiles; + /** + * an index of all primary and secondary dirs + * referenced from this class's compilation unit. + */ + private HashMap localDirsIndex; + /** + * a list of the same dirs. + */ + private LinkedList localDirs; + /** + * index of debug_info section compilation unit + * for this class. + */ + private int cuIndex; + /** + * index into debug_line section for associated + * compilation unit. + */ + private int lineIndex; + /** + * size of line number info prologue region for + * associated compilation unit. + */ + private int linePrologueSize; + /** + * total size of line number info region for + * associated compilation unit. + */ + private int totalSize; + + public ClassEntry(String className, FileEntry fileEntry) { + this.className = className; + this.fileEntry = fileEntry; + this.primaryEntries = new LinkedList<>(); + this.primaryIndex = new HashMap<>(); + this.localFiles = new LinkedList<>(); + this.localFilesIndex = new HashMap<>(); + this.localDirs = new LinkedList<>(); + this.localDirsIndex = new HashMap<>(); + localFiles.add(fileEntry); + localFilesIndex.put(fileEntry, localFiles.size()); + DirEntry dirEntry = fileEntry.dirEntry; + if (dirEntry != null) { + localDirs.add(dirEntry); + localDirsIndex.put(dirEntry, localDirs.size()); + } + this.cuIndex = -1; + this.lineIndex = -1; + this.linePrologueSize = -1; + this.totalSize = -1; + } + + PrimaryEntry addPrimary(Range primary, List frameSizeInfos, int frameSize) { + if (primaryIndex.get(primary) == null) { + PrimaryEntry primaryEntry = new PrimaryEntry(primary, frameSizeInfos, frameSize, this); + primaryEntries.add(primaryEntry); + primaryIndex.put(primary, primaryEntry); + return primaryEntry; + } + return null; + } + + void addSubRange(Range subrange, FileEntry subFileEntry) { + Range primary = subrange.getPrimary(); + // the subrange should belong to a primary range + assert primary != null; + PrimaryEntry primaryEntry = primaryIndex.get(primary); + // we should already have seen the primary range + assert primaryEntry != null; + assert primaryEntry.getClassEntry() == this; + primaryEntry.addSubRange(subrange, subFileEntry); + if (localFilesIndex.get(subFileEntry) == null) { + localFiles.add(subFileEntry); + localFilesIndex.put(subFileEntry, localFiles.size()); + } + DirEntry dirEntry = subFileEntry.dirEntry; + if (dirEntry != null && localDirsIndex.get(dirEntry) == null) { + localDirs.add(dirEntry); + localDirsIndex.put(dirEntry, localDirs.size()); + } + } + + public int localDirsIdx(DirEntry dirEntry) { + if (dirEntry != null) { + return localDirsIndex.get(dirEntry); + } else { + return 0; + } + } + + public int localFilesIdx(@SuppressWarnings("hiding") FileEntry fileEntry) { + return localFilesIndex.get(fileEntry); + } + + String getFileName() { + return fileEntry.getFileName(); + } + + String getDirName() { + return fileEntry.getDirName(); + } + + void setCUIndex(int cuIndex) { + // should only get set once to a non-negative value + assert cuIndex >= 0; + assert this.cuIndex == -1; + this.cuIndex = cuIndex; + } + + int getCUIndex() { + // should have been set before being read + assert cuIndex >= 0; + return cuIndex; + } + + int getLineIndex() { + return lineIndex; + } + + void setLineIndex(int lineIndex) { + this.lineIndex = lineIndex; + } + + public void setLinePrologueSize(int linePrologueSize) { + this.linePrologueSize = linePrologueSize; + } + + public int getLinePrologueSize() { + return linePrologueSize; + } + + public int getTotalSize() { + return totalSize; + } + + public void setTotalSize(int totalSize) { + this.totalSize = totalSize; + } + + public FileEntry getFileEntry() { + return fileEntry; + } + + public String getClassName() { + return className; + } + + public LinkedList getPrimaryEntries() { + return primaryEntries; + } + + public Object primaryIndexFor(Range primaryRange) { + return primaryIndex.get(primaryRange); + } + + public LinkedList getLocalDirs() { + return localDirs; + } + + public LinkedList getLocalFiles() { + return localFiles; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java new file mode 100644 index 000000000000..5caac5a20997 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +/** + * track the directory associated with one or + * more source files. + */ +public class DirEntry { + private String path; + + public DirEntry(String path) { + this.path = path; + } + + public String getPath() { + return path; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java new file mode 100644 index 000000000000..474e74d20735 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java @@ -0,0 +1,2336 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +import com.oracle.objectfile.BasicProgbitsSectionImpl; +import com.oracle.objectfile.BuildDependency; +import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.LayoutDecisionMap; +import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfoProvider; +// import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugDataInfoProvider; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLineInfo; +// import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfoProvider; +import com.oracle.objectfile.ObjectFile.Element; +import com.oracle.objectfile.elf.ELFMachine; +import com.oracle.objectfile.elf.ELFObjectFile.ELFSection; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * an outer class that models the debug info in an + * organization that facilitates generation of the + * required DWARF sections. It groups common data and + * behaviours for use by the various subclasses of + * inner class DwarfSectionImpl that take responsibility + * for generating content for a specific section type. + */ +public class DwarfSections { + // names of the different ELF sections we create or reference + // in reverse dependency order + public static final String TEXT_SECTION_NAME = ".text"; + public static final String DW_STR_SECTION_NAME = ".debug_str"; + public static final String DW_LINE_SECTION_NAME = ".debug_line"; + public static final String DW_FRAME_SECTION_NAME = ".debug_frame"; + public static final String DW_ABBREV_SECTION_NAME = ".debug_abbrev"; + public static final String DW_INFO_SECTION_NAME = ".debug_info"; + public static final String DW_ARANGES_SECTION_NAME = ".debug_aranges"; + + /** + * currently generated debug info relies on DWARF spec vesion 2. + */ + private static final short DW_VERSION_2 = 2; + + // define all the abbrev section codes we need for our DIEs + // private static final int DW_ABBREV_CODE_null = 0; + private static final int DW_ABBREV_CODE_compile_unit = 1; + private static final int DW_ABBREV_CODE_subprogram = 2; + + // define all the Dwarf tags we need for our DIEs + private static final int DW_TAG_compile_unit = 0x11; + private static final int DW_TAG_subprogram = 0x2e; + // define all the Dwarf attributes we need for our DIEs + private static final int DW_AT_null = 0x0; + private static final int DW_AT_name = 0x3; + // private static final int DW_AT_comp_dir = 0x1b; + private static final int DW_AT_stmt_list = 0x10; + private static final int DW_AT_low_pc = 0x11; + private static final int DW_AT_hi_pc = 0x12; + private static final int DW_AT_language = 0x13; + private static final int DW_AT_external = 0x3f; + // private static final int DW_AT_return_addr = 0x2a; + // private static final int DW_AT_frame_base = 0x40; + // define all the Dwarf attribute forms we need for our DIEs + private static final int DW_FORM_null = 0x0; + // private static final int DW_FORM_string = 0x8; + private static final int DW_FORM_strp = 0xe; // not currently used + private static final int DW_FORM_addr = 0x1; + private static final int DW_FORM_data1 = 0x0b; // use flag instead + private static final int DW_FORM_data4 = 0x6; + // private static final int DW_FORM_data8 = 0x7; + // private static final int DW_FORM_block1 = 0x0a; + private static final int DW_FORM_flag = 0xc; + + // define specific attribute values for given attribute or form types + // DIE header has_children attribute values + private static final byte DW_CHILDREN_no = 0; + private static final byte DW_CHILDREN_yes = 1; + // DW_FORM_flag attribute values + // private static final byte DW_FLAG_false = 0; + private static final byte DW_FLAG_true = 1; + // value for DW_AT_language attribute with form DATA1 + private static final byte DW_LANG_Java = 0xb; + // access not needed until we make functions members + // DW_AT_Accessibility attribute values + // private static final byte DW_ACCESS_public = 1; + // private static final byte DW_ACCESS_protected = 2; + // private static final byte DW_ACCESS_private = 3; + + // not yet needed + // private static final int DW_AT_type = 0; // only present for non-void functions + // private static final int DW_AT_accessibility = 0; + + // CIE and FDE entries + + private static final int DW_CFA_CIE_id = -1; + // private static final int DW_CFA_FDE_id = 0; + + private static final byte DW_CFA_CIE_version = 1; + + // values for high 2 bits + private static final byte DW_CFA_advance_loc = 0x1; + private static final byte DW_CFA_offset = 0x2; + // private static final byte DW_CFA_restore = 0x3; + + // values for low 6 bits + private static final byte DW_CFA_nop = 0x0; + // private static final byte DW_CFA_set_loc1 = 0x1; + private static final byte DW_CFA_advance_loc1 = 0x2; + private static final byte DW_CFA_advance_loc2 = 0x3; + private static final byte DW_CFA_advance_loc4 = 0x4; + // private static final byte DW_CFA_offset_extended = 0x5; + // private static final byte DW_CFA_restore_extended = 0x6; + // private static final byte DW_CFA_undefined = 0x7; + // private static final byte DW_CFA_same_value = 0x8; + private static final byte DW_CFA_register = 0x9; + private static final byte DW_CFA_def_cfa = 0xc; + // private static final byte DW_CFA_def_cfa_register = 0xd; + private static final byte DW_CFA_def_cfa_offset = 0xe; + + private ELFMachine elfMachine; + private DwarfStrSectionImpl dwarfStrSection; + private DwarfAbbrevSectionImpl dwarfAbbrevSection; + private DwarfInfoSectionImpl dwarfInfoSection; + private DwarfARangesSectionImpl dwarfARangesSection; + private DwarfLineSectionImpl dwarfLineSection; + private DwarfFrameSectionImpl dwarfFameSection; + + public DwarfSections(ELFMachine elfMachine) { + this.elfMachine = elfMachine; + dwarfStrSection = new DwarfStrSectionImpl(); + dwarfAbbrevSection = new DwarfAbbrevSectionImpl(); + dwarfInfoSection = new DwarfInfoSectionImpl(); + dwarfARangesSection = new DwarfARangesSectionImpl(); + dwarfLineSection = new DwarfLineSectionImpl(); + dwarfFameSection = (elfMachine == ELFMachine.AArch64 + ? new DwarfFrameSectionImplAArch64() + : new DwarfFrameSectionImplX86_64()); + } + + public DwarfStrSectionImpl getStrSectionImpl() { + return dwarfStrSection; + } + + public DwarfAbbrevSectionImpl getAbbrevSectionImpl() { + return dwarfAbbrevSection; + } + + public DwarfFrameSectionImpl getFrameSectionImpl() { + return dwarfFameSection; + } + + public DwarfInfoSectionImpl getInfoSectionImpl() { + return dwarfInfoSection; + } + + public DwarfARangesSectionImpl getARangesSectionImpl() { + return dwarfARangesSection; + } + + public DwarfLineSectionImpl getLineSectionImpl() { + return dwarfLineSection; + } + + public ELFMachine getElfMachine() { + return elfMachine; + } + + /** + * a scratch buffer used during computation of a section's size. + */ + protected static final byte[] scratch = new byte[10]; + + /** + * a table listing all known strings, some of + * which may be marked for insertion into the + * debug_str section. + */ + private StringTable stringTable = new StringTable(); + + /** + * list detailing all dirs in which files are found to reside + * either as part of substrate/compiler or user code. + */ + private LinkedList dirs = new LinkedList<>(); + /** + * index of already seen dirs. + */ + private Map 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. + */ + private LinkedList primaryClasses = new LinkedList<>(); + /** + * index of already seen classes. + */ + private Map primaryClassesIndex = new HashMap<>(); + + /** + * list of files which contain primary ranges. + */ + private LinkedList primaryFiles = new LinkedList<>(); + /** + * List of files which contain primary or secondary ranges. + */ + private LinkedList files = new LinkedList<>(); + /** + * index of already seen files. + */ + private Map filesIndex = new HashMap<>(); + + /** + * indirects this call to the string table. + * @param string the string to be inserted + * @return a unique equivalent String + */ + public String uniqueString(String string) { + return stringTable.uniqueString(string); + } + + /** + * indirects this call to the string table, ensuring + * the table entry is marked for inclusion in the + * debug_str section. + * @param string the string to be inserted and + * marked for inclusion in the debug_str section + * @return a unique equivalent String + */ + public String uniqueDebugString(String string) { + return stringTable.uniqueDebugString(string); + } + + /** + * 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 + */ + private int debugStringIndex(String string) { + return stringTable.debugStringIndex(string); + } + + /** + * entry point allowing ELFObjectFile to pass on information + * about types, code and heap data. + * @param debugInfoProvider provider instance passed by + * ObjectFile client + */ + public void installDebugInfo(DebugInfoProvider debugInfoProvider) { + // DebugTypeInfoProvider typeInfoProvider = debugInfoProvider.typeInfoProvider(); + // for (DebugTypeInfo debugTypeInfo : typeInfoProvider) { + // install types + // } + + // ensure we have a null string in the string section + uniqueDebugString(""); + + DebugCodeInfoProvider codeInfoProvider = debugInfoProvider.codeInfoProvider(); + for (DebugCodeInfo debugCodeInfo : codeInfoProvider) { + // primary file name and full method name need to be written to the debug_str section + String fileName = debugCodeInfo.fileName(); + String className = debugCodeInfo.className(); + String methodName = debugCodeInfo.methodName(); + String paramNames = debugCodeInfo.paramNames(); + String returnTypeName = debugCodeInfo.returnTypeName(); + int lo = debugCodeInfo.addressLo(); + int hi = debugCodeInfo.addressHi(); + int primaryLine = debugCodeInfo.line(); + Range primaryRange = new Range(fileName, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, primaryLine); + // System.out.format("arange: [0x%08x,0x%08x) %s %s::%s(%s) %s\n", lo, hi, + // returnTypeName, className, methodName, paramNames, fileName); + // create an infoSection entry for the method + addRange(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); + for (DebugLineInfo debugLineInfo : debugCodeInfo.lineInfoProvider()) { + String fileNameAtLine = debugLineInfo.fileName(); + String classNameAtLine = debugLineInfo.className(); + String methodNameAtLine = debugLineInfo.methodName(); + int loAtLine = lo + debugLineInfo.addressLo(); + int hiAtLine = lo + debugLineInfo.addressHi(); + int line = debugLineInfo.line(); + // 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, classNameAtLine, methodNameAtLine, "", "", stringTable, loAtLine, hiAtLine, line, primaryRange); + addSubRange(primaryRange, subRange); + } + } + // DebugDataInfoProvider dataInfoProvider = debugInfoProvider.dataInfoProvider(); + // for (DebugDataInfo debugDataInfo : dataInfoProvider) { + // install details of heap elements + // String name = debugDataInfo.toString(); + // } + } + + public 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; + } + + public FileEntry ensureFileEntry(Range range) { + String fileName = range.getFileName(); + // ensure we have an entry + FileEntry fileEntry = filesIndex.get(fileName); + if (fileEntry == null) { + DirEntry dirEntry = ensureDirEntry(fileName); + String baseName = (dirEntry == null ? fileName : fileName.substring(dirEntry.getPath().length() + 1)); + fileEntry = new FileEntry(stringTable.uniqueDebugString(fileName), + stringTable.uniqueString(baseName), + dirEntry); + files.add(fileEntry); + filesIndex.put(fileName, fileEntry); + // if this is a primary entry then add it to the primary list + if (range.isPrimary()) { + primaryFiles.add(fileEntry); + } else { + Range primaryRange = range.getPrimary(); + FileEntry primaryEntry = filesIndex.get(primaryRange.getFileName()); + assert primaryEntry != null; + } + } + return fileEntry; + } + + public void addRange(Range primaryRange, List frameSizeInfos, int frameSize) { + assert primaryRange.isPrimary(); + ClassEntry classEntry = ensureClassEntry(primaryRange); + PrimaryEntry entry = classEntry.addPrimary(primaryRange, frameSizeInfos, frameSize); + } + + public void addSubRange(Range primaryRange, Range subrange) { + assert primaryRange.isPrimary(); + assert !subrange.isPrimary(); + String className = primaryRange.getClassName(); + ClassEntry classEntry = primaryClassesIndex.get(className); + FileEntry subrangeEntry = ensureFileEntry(subrange); + // the primary range should already have been seen + // and associated with a primary class entry + assert classEntry.primaryIndexFor(primaryRange) != null; + classEntry.addSubRange(subrange, subrangeEntry); + } + + private DirEntry ensureDirEntry(String file) { + int pathLength = file.lastIndexOf('/'); + if (pathLength < 0) { + // no path/package means use dir entry 0 + return null; + } + String filePath = file.substring(0, pathLength); + DirEntry dirEntry = dirsIndex.get(filePath); + if (dirEntry == null) { + dirEntry = new DirEntry(stringTable.uniqueString(filePath)); + dirsIndex.put(filePath, dirEntry); + dirs.add(dirEntry); + } + return dirEntry; + } + + /** + * class from which all DWARF debug section + * inherit providing common behaviours. + */ + // shared implementation methods to manage content creation + public abstract class DwarfSectionImpl extends BasicProgbitsSectionImpl { + public boolean debug = false; + public long debugTextBase = 0; + public long debugAddress = 0; + public int debugBase = 0; + + public DwarfSectionImpl() { + } + + /** + * creates the target byte[] array used to define the section + * contents. + * + * the main task of this method is to precompute the + * size of the debug section. given the complexity of the + * data layouts that invariably requires performing a dummy + * write of the contents, inserting bytes into a small, + * scratch buffer only when absolutely necessary. subclasses + * may also cache some information for use when writing the + * contents. + */ + public abstract void createContent(); + + /** + * populates the byte[] array used to contain the section + * contents. + * + * in most cases this task reruns the operations performed + * under createContent but this time actually writing data + * to the target byte[]. + */ + public abstract void writeContent(); + + @Override + public boolean isLoadable() { + // even though we're a progbits section impl we're not actually loadable + return false; + } + + public void checkDebug(int pos) { + // if the env var relevant to this element + // type is set then switch on debugging + String name = getSectionName(); + String envVarName = "DWARF_" + name.substring(1).toUpperCase(); + if (System.getenv(envVarName) != null) { + debug = true; + debugBase = pos; + debugAddress = debugTextBase; + } + } + + protected void debug(String format, Object... args) { + if (debug) { + System.out.format(format, args); + } + } + + // base level put methods that assume a non-null buffer + + public int putByte(byte b, byte[] buffer, int p) { + int pos = p; + buffer[pos++] = b; + return pos; + } + + public int putShort(short s, byte[] buffer, int p) { + int pos = p; + buffer[pos++] = (byte) (s & 0xff); + buffer[pos++] = (byte) ((s >> 8) & 0xff); + return pos; + } + + public int putInt(int i, byte[] buffer, int p) { + int pos = p; + buffer[pos++] = (byte) (i & 0xff); + buffer[pos++] = (byte) ((i >> 8) & 0xff); + buffer[pos++] = (byte) ((i >> 16) & 0xff); + buffer[pos++] = (byte) ((i >> 24) & 0xff); + return pos; + } + + public int putLong(long l, byte[] buffer, int p) { + int pos = p; + buffer[pos++] = (byte) (l & 0xff); + buffer[pos++] = (byte) ((l >> 8) & 0xff); + buffer[pos++] = (byte) ((l >> 16) & 0xff); + buffer[pos++] = (byte) ((l >> 24) & 0xff); + buffer[pos++] = (byte) ((l >> 32) & 0xff); + buffer[pos++] = (byte) ((l >> 40) & 0xff); + buffer[pos++] = (byte) ((l >> 48) & 0xff); + buffer[pos++] = (byte) ((l >> 56) & 0xff); + return pos; + } + + public int putRelocatableCodeOffset(long l, byte[] buffer, int p) { + int pos = p; + // mark address so it is relocated relative to the start of the text segment + markRelocationSite(pos, 8, ObjectFile.RelocationKind.DIRECT, TEXT_SECTION_NAME, false, Long.valueOf(l)); + pos = putLong(0, buffer, pos); + return pos; + } + + public int putULEB(long val, byte[] buffer, int p) { + long l = val; + int pos = p; + for (int i = 0; i < 9; i++) { + byte b = (byte) (l & 0x7f); + l = l >>> 7; + boolean done = (l == 0); + if (!done) { + b = (byte) (b | 0x80); + } + pos = putByte(b, buffer, pos); + if (done) { + break; + } + } + return pos; + } + + public int putSLEB(long val, byte[] buffer, int p) { + long l = val; + int pos = p; + for (int i = 0; i < 9; i++) { + byte b = (byte) (l & 0x7f); + l = l >> 7; + boolean bIsSigned = (b & 0x40) != 0; + boolean done = ((bIsSigned && l == -1) || (!bIsSigned && l == 0)); + if (!done) { + b = (byte) (b | 0x80); + } + pos = putByte(b, buffer, pos); + if (done) { + break; + } + } + return pos; + } + + public int putAsciiStringBytes(String s, byte[] buffer, int pos) { + return putAsciiStringBytes(s, 0, buffer, pos); + } + + public int putAsciiStringBytes(String s, int startChar, byte[] buffer, int p) { + int pos = p; + for (int l = startChar; l < s.length(); l++) { + char c = s.charAt(l); + if (c > 127) { + throw new RuntimeException("oops : expected ASCII string! " + s); + } + buffer[pos++] = (byte) c; + } + buffer[pos++] = '\0'; + return pos; + } + + // common write methods that check for a null buffer + + public void patchLength(int lengthPos, byte[] buffer, int pos) { + if (buffer != null) { + int length = pos - (lengthPos + 4); + putInt(length, buffer, lengthPos); + } + } + + public int writeAbbrevCode(long code, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putSLEB(code, scratch, 0); + } else { + return putSLEB(code, buffer, pos); + } + } + + public int writeTag(long code, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putSLEB(code, scratch, 0); + } else { + return putSLEB(code, buffer, pos); + } + } + + public int writeFlag(byte flag, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putByte(flag, scratch, 0); + } else { + return putByte(flag, buffer, pos); + } + } + + public int writeAttrAddress(long address, byte[] buffer, int pos) { + if (buffer == null) { + return pos + 8; + } else { + return putRelocatableCodeOffset(address, buffer, pos); + } + } + + public int writeAttrData8(long value, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putLong(value, scratch, 0); + } else { + return putLong(value, buffer, pos); + } + } + + public int writeAttrData4(int value, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putInt(value, scratch, 0); + } else { + return putInt(value, buffer, pos); + } + } + + public int writeAttrData1(byte value, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putByte(value, scratch, 0); + } else { + return putByte(value, buffer, pos); + } + } + + public int writeAttrNull(byte[] buffer, int pos) { + if (buffer == null) { + return pos + putSLEB(0, scratch, 0); + } else { + return putSLEB(0, buffer, pos); + } + } + + /** + * identify the section after which this debug section + * needs to be ordered when sizing and creating content. + * @return the name of the preceding section + */ + public abstract String targetSectionName(); + + /** + * identify the layout properties of the target section + * which need to have been decided before the contents + * of this section can be created. + * @return an array of the relevant decision kinds + */ + public abstract LayoutDecision.Kind[] targetSectionKinds(); + + /** + * identify this debug section by name. + * @return the name of the debug section + */ + public abstract String getSectionName(); + + @Override + public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { + // ensure content byte[] has been created before calling super method + createContent(); + + // ensure content byte[] has been written before calling super method + writeContent(); + + return super.getOrDecideContent(alreadyDecided, contentHint); + } + + @Override + public Set getDependencies(Map decisions) { + Set deps = super.getDependencies(decisions); + String targetName = targetSectionName(); + ELFSection targetSection = (ELFSection) getElement().getOwner().elementForName(targetName); + LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT); + LayoutDecision ourSize = decisions.get(getElement()).getDecision(LayoutDecision.Kind.SIZE); + LayoutDecision.Kind[] targetKinds = targetSectionKinds(); + // make our content depend on the size and content of the target + for (LayoutDecision.Kind targetKind : targetKinds) { + LayoutDecision targetDecision = decisions.get(targetSection).getDecision(targetKind); + deps.add(BuildDependency.createOrGet(ourContent, targetDecision)); + } + // make our size depend on our content + deps.add(BuildDependency.createOrGet(ourSize, ourContent)); + + return deps; + } + } + + /** + * generator for debug_str section. + */ + public class DwarfStrSectionImpl extends DwarfSectionImpl { + public DwarfStrSectionImpl() { + super(); + } + + @Override + public String getSectionName() { + return DW_STR_SECTION_NAME; + } + + @Override + public void createContent() { + int pos = 0; + for (StringEntry stringEntry : stringTable) { + if (stringEntry.isAddToStrSection()) { + stringEntry.setOffset(pos); + String string = stringEntry.getString(); + pos += string.length() + 1; + } + } + byte[] buffer = new byte[pos]; + super.setContent(buffer); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + int size = buffer.length; + int pos = 0; + + checkDebug(pos); + + for (StringEntry stringEntry : stringTable) { + if (stringEntry.isAddToStrSection()) { + assert stringEntry.getOffset() == pos; + String string = stringEntry.getString(); + pos = putAsciiStringBytes(string, buffer, pos); + } + } + assert pos == size; + } + + @Override + protected void debug(String format, Object... args) { + super.debug(format, args); + } + + /** + * debug_str section content depends on text section content and offset. + */ + public static final String TARGET_SECTION_NAME = TEXT_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + /** + * debug_str section content depends on text section content and offset. + */ + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET, + LayoutDecision.Kind.VADDR, // add this so we can use the base address + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } + } + + /** + * generator for debug_abbrev section. + */ + public class DwarfAbbrevSectionImpl extends DwarfSectionImpl { + + public DwarfAbbrevSectionImpl() { + super(); + } + + @Override + public String getSectionName() { + return DW_ABBREV_SECTION_NAME; + } + + @Override + public void createContent() { + int pos = 0; + // an abbrev table contains abbrev entries for one or + // more CUs. the table includes a sequence of abbrev + // entries each of which defines a specific DIE layout + // employed to describe some DIE in a CU. a table is + // terminated by a null entry + // + // a null entry has consists of just a 0 abbrev code + // LEB128 abbrev_code; ...... == 0 + // + // non-null entries have the following format + // LEB128 abbrev_code; ...... unique noncode for this layout != 0 + // LEB128 tag; .............. defines the type of the DIE (class, subprogram, var etc) + // uint8 has_chldren; ....... is the DIE followed by child DIEs or a sibling DIE + // * ........ zero or more attributes + // .... terminator + // + // An attribute_spec consists of an attribute name and form + // LEB128 attr_name; ........ 0 for the null attribute name + // LEB128 attr_form; ........ 0 for the null attribute form + // + // For the moment we only use one abbrev table for all CUs. + // It contains two DIEs, the first to describe the compilation + // unit itself and the second to describe each method within + // that compilation unit. + // + // The DIE layouts are as follows: + // + // abbrev_code == 1, tag == DW_TAG_compilation_unit, has_children + // DW_AT_language : ... DW_FORM_data1 + // DW_AT_name : ....... DW_FORM_strp + // DW_AT_low_pc : ..... DW_FORM_address + // DW_AT_hi_pc : ...... DW_FORM_address + // DW_AT_stmt_list : .. DW_FORM_data4 + // + // abbrev_code == 2, tag == DW_TAG_subprogram, no_children + // DW_AT_name : ....... DW_FORM_strp + // DW_AT_low_pc : ..... DW_FORM_addr + // DW_AT_hi_pc : ...... DW_FORM_addr + // DW_AT_external : ... DW_FORM_flag + + pos = writeAbbrev1(null, pos); + pos = writeAbbrev2(null, pos); + + byte[] buffer = new byte[pos]; + super.setContent(buffer); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + int size = buffer.length; + int pos = 0; + + checkDebug(pos); + + pos = writeAbbrev1(buffer, pos); + pos = writeAbbrev2(buffer, pos); + assert pos == size; + } + + public int writeAttrType(long code, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putSLEB(code, scratch, 0); + } else { + return putSLEB(code, buffer, pos); + } + } + + public int writeAttrForm(long code, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putSLEB(code, scratch, 0); + } else { + return putSLEB(code, buffer, pos); + } + } + + public int writeAbbrev1(byte[] buffer, int p) { + int pos = p; + // abbrev 1 compile unit + pos = writeAbbrevCode(DW_ABBREV_CODE_compile_unit, buffer, pos); + pos = writeTag(DW_TAG_compile_unit, buffer, pos); + pos = writeFlag(DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DW_AT_language, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_low_pc, buffer, pos); + pos = writeAttrForm(DW_FORM_addr, buffer, pos); + pos = writeAttrType(DW_AT_hi_pc, buffer, pos); + pos = writeAttrForm(DW_FORM_addr, buffer, pos); + pos = writeAttrType(DW_AT_stmt_list, buffer, pos); + pos = writeAttrForm(DW_FORM_data4, buffer, pos); + // now terminate + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + public int writeAbbrev2(byte[] buffer, int p) { + int pos = p; + // abbrev 2 compile unit + pos = writeAbbrevCode(DW_ABBREV_CODE_subprogram, buffer, pos); + pos = writeTag(DW_TAG_subprogram, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_low_pc, buffer, pos); + pos = writeAttrForm(DW_FORM_addr, buffer, pos); + pos = writeAttrType(DW_AT_hi_pc, buffer, pos); + pos = writeAttrForm(DW_FORM_addr, buffer, pos); + pos = writeAttrType(DW_AT_external, buffer, pos); + pos = writeAttrForm(DW_FORM_flag, buffer, pos); + // now terminate + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + @Override + protected void debug(String format, Object... args) { + super.debug(format, args); + } + + /** + * debug_abbrev section content depends on debug_frame section content and offset. + */ + public static final String TARGET_SECTION_NAME = DW_FRAME_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } + } + + /** + * generic generator for debug_frame section. + */ + public abstract class DwarfFrameSectionImpl extends DwarfSectionImpl { + + public DwarfFrameSectionImpl() { + super(); + } + + @Override + public String getSectionName() { + return DW_FRAME_SECTION_NAME; + } + + @Override + public void createContent() { + int pos = 0; + + // the frame section contains one CIE at offset 0 + // followed by an FIE for each method + pos = writeCIE(null, pos); + pos = writeMethodFrames(null, pos); + + byte[] buffer = new byte[pos]; + super.setContent(buffer); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + int size = buffer.length; + int pos = 0; + + checkDebug(pos); + + // there are entries for the prologue region where the + // stack is being built, the method body region(s) where + // the code executes with a fixed size frame and the + // epilogue region(s) where the stack is torn down + pos = writeCIE(buffer, pos); + pos = writeMethodFrames(buffer, pos); + + if (pos != size) { + System.out.format("pos = 0x%x size = 0x%x", pos, size); + } + assert pos == size; + } + + public int writeCIE(byte[] buffer, int p) { + // we only need a vanilla CIE with default fields + // because we have to have at least one + // the layout is + // + // uint32 : length ............... length of remaining fields in this CIE + // uint32 : CIE_id ................ unique id for CIE == 0xffffff + // uint8 : version ................ == 1 + // uint8[] : augmentation ......... == "" so always 1 byte + // ULEB : code_alignment_factor ... == 1 (could use 4 for Aarch64) + // ULEB : data_alignment_factor ... == -8 + // byte : ret_addr reg id ......... x86_64 => 16 AArch64 => 32 + // byte[] : initial_instructions .. includes pad to 8-byte boundary + int pos = p; + if (buffer == null) { + pos += putInt(0, scratch, 0); // don't care about length + pos += putInt(DW_CFA_CIE_id, scratch, 0); + pos += putByte(DW_CFA_CIE_version, scratch, 0); + pos += putAsciiStringBytes("", scratch, 0); + pos += putULEB(1, scratch, 0); + pos += putSLEB(-8, scratch, 0); + pos += putByte((byte) getPCIdx(), scratch, 0); + // write insns to set up empty frame + pos = writeInitialInstructions(buffer, pos); + // pad to word alignment + pos = writePaddingNops(8, buffer, pos); + // no need to write length + return pos; + } else { + int lengthPos = pos; + pos = putInt(0, buffer, pos); + pos = putInt(DW_CFA_CIE_id, buffer, pos); + pos = putByte(DW_CFA_CIE_version, buffer, pos); + pos = putAsciiStringBytes("", buffer, pos); + pos = putULEB(1, buffer, pos); + pos = putSLEB(-8, buffer, pos); + pos = putByte((byte) getPCIdx(), buffer, pos); + // write insns to set up empty frame + pos = writeInitialInstructions(buffer, pos); + // pad to word alignment + pos = writePaddingNops(8, buffer, pos); + patchLength(lengthPos, buffer, pos); + return pos; + } + } + + public int writeMethodFrames(byte[] buffer, int p) { + int pos = p; + for (ClassEntry classEntry : primaryClasses) { + for (PrimaryEntry primaryEntry : classEntry.getPrimaryEntries()) { + long lo = primaryEntry.getPrimary().getLo(); + long hi = primaryEntry.getPrimary().getHi(); + int frameSize = primaryEntry.getFrameSize(); + int currentOffset = 0; + int lengthPos = pos; + pos = writeFDEHeader((int) lo, (int) hi, buffer, pos); + for (DebugFrameSizeChange debugFrameSizeInfo : primaryEntry.getFrameSizeInfos()) { + int advance = debugFrameSizeInfo.getOffset() - currentOffset; + currentOffset += advance; + pos = writeAdvanceLoc(advance, buffer, pos); + if (debugFrameSizeInfo.getType() == DebugFrameSizeChange.Type.EXTEND) { + // SP has been extended so rebase CFA using full frame + pos = writeDefCFAOffset(frameSize, buffer, pos); + } else { + // SP has been contracted so rebase CFA using empty frame + pos = writeDefCFAOffset(8, buffer, pos); + } + } + pos = writePaddingNops(8, buffer, pos); + patchLength(lengthPos, buffer, pos); + } + } + return pos; + } + + public int writeFDEHeader(int lo, int hi, byte[] buffer, int p) { + // we only need a vanilla FDE header with default fields + // the layout is + // + // uint32 : length ........... length of remaining fields in this FDE + // uint32 : CIE_offset ........ always 0 i.e. identifies our only CIE header + // uint64 : initial_location .. i.e. method lo address + // uint64 : address_range ..... i.e. method hi - lo + // byte[] : instructions ...... includes pad to 8-byte boundary + + int pos = p; + if (buffer == null) { + pos += putInt(0, scratch, 0); // dummy length + pos += putInt(0, scratch, 0); // CIE_offset + pos += putLong(lo, scratch, 0); // initial address + return pos + putLong(hi - lo, scratch, 0); // address range + } else { + pos = putInt(0, buffer, pos); // dummy length + pos = putInt(0, buffer, pos); // CIE_offset + pos = putRelocatableCodeOffset(lo, buffer, pos); // initial address + return putLong(hi - lo, buffer, pos); // address range + } + } + + public int writePaddingNops(int alignment, byte[] buffer, int p) { + int pos = p; + assert (alignment & (alignment - 1)) == 0; + while ((pos & (alignment - 1)) != 0) { + if (buffer == null) { + pos++; + } else { + pos = putByte(DW_CFA_nop, buffer, pos); + } + } + return pos; + } + + public int writeDefCFA(int register, int offset, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + pos += putByte(DW_CFA_def_cfa, scratch, 0); + pos += putSLEB(register, scratch, 0); + return pos + putULEB(offset, scratch, 0); + } else { + pos = putByte(DW_CFA_def_cfa, buffer, pos); + pos = putULEB(register, buffer, pos); + return putULEB(offset, buffer, pos); + } + } + + public int writeDefCFAOffset(int offset, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + pos += putByte(DW_CFA_def_cfa_offset, scratch, 0); + return pos + putULEB(offset, scratch, 0); + } else { + pos = putByte(DW_CFA_def_cfa_offset, buffer, pos); + return putULEB(offset, buffer, pos); + } + } + + public int writeAdvanceLoc(int offset, byte[] buffer, int pos) { + if (offset <= 0x3f) { + return writeAdvanceLoc0((byte) offset, buffer, pos); + } else if (offset <= 0xff) { + return writeAdvanceLoc1((byte) offset, buffer, pos); + } else if (offset <= 0xffff) { + return writeAdvanceLoc2((short) offset, buffer, pos); + } else { + return writeAdvanceLoc4(offset, buffer, pos); + } + } + + public int writeAdvanceLoc0(byte offset, byte[] buffer, int pos) { + byte op = advanceLoc0Op(offset); + if (buffer == null) { + return pos + putByte(op, scratch, 0); + } else { + return putByte(op, buffer, pos); + } + } + + public int writeAdvanceLoc1(byte offset, byte[] buffer, int p) { + int pos = p; + byte op = DW_CFA_advance_loc1; + if (buffer == null) { + pos += putByte(op, scratch, 0); + return pos + putByte(offset, scratch, 0); + } else { + pos = putByte(op, buffer, pos); + return putByte(offset, buffer, pos); + } + } + + public int writeAdvanceLoc2(short offset, byte[] buffer, int p) { + byte op = DW_CFA_advance_loc2; + int pos = p; + if (buffer == null) { + pos += putByte(op, scratch, 0); + return pos + putShort(offset, scratch, 0); + } else { + pos = putByte(op, buffer, pos); + return putShort(offset, buffer, pos); + } + } + + public int writeAdvanceLoc4(int offset, byte[] buffer, int p) { + byte op = DW_CFA_advance_loc4; + int pos = p; + if (buffer == null) { + pos += putByte(op, scratch, 0); + return pos + putInt(offset, scratch, 0); + } else { + pos = putByte(op, buffer, pos); + return putInt(offset, buffer, pos); + } + } + + public int writeOffset(int register, int offset, byte[] buffer, int p) { + byte op = offsetOp(register); + int pos = p; + if (buffer == null) { + pos += putByte(op, scratch, 0); + return pos + putULEB(offset, scratch, 0); + } else { + pos = putByte(op, buffer, pos); + return putULEB(offset, buffer, pos); + } + } + + public int writeRegister(int savedReg, int savedToReg, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + pos += putByte(DW_CFA_register, scratch, 0); + pos += putULEB(savedReg, scratch, 0); + return pos + putULEB(savedToReg, scratch, 0); + } else { + pos = putByte(DW_CFA_register, buffer, pos); + pos = putULEB(savedReg, buffer, pos); + return putULEB(savedToReg, buffer, pos); + } + } + + public abstract int getPCIdx(); + + public abstract int getSPIdx(); + + public abstract int writeInitialInstructions(byte[] buffer, int pos); + + @Override + protected void debug(String format, Object... args) { + super.debug(format, args); + } + + /** + * debug_frame section content depends on debug_line section content and offset. + */ + public static final String TARGET_SECTION_NAME = DW_LINE_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } + + private byte offsetOp(int register) { + assert (register >> 6) == 0; + return (byte) ((DW_CFA_offset << 6) | register); + } + + private byte advanceLoc0Op(int offset) { + assert (offset >= 0 && offset <= 0x3f); + return (byte) ((DW_CFA_advance_loc << 6) | offset); + } + } + + /** + * x86_64-specific generator for debug_frame section + * that knows details of x86_64 registers and frame layout. + */ + public class DwarfFrameSectionImplX86_64 extends DwarfFrameSectionImpl { + public static final int DW_CFA_RSP_IDX = 7; + public static final int DW_CFA_RIP_IDX = 16; + + public DwarfFrameSectionImplX86_64() { + super(); + } + + @Override + public int getPCIdx() { + return DW_CFA_RIP_IDX; + } + + @Override + public int getSPIdx() { + return DW_CFA_RSP_IDX; + } + + @Override + public int writeInitialInstructions(byte[] buffer, int p) { + int pos = p; + // rsp points at the word containing the saved rip + // so the frame base (cfa) is at rsp + 8 (why not - ???) + // def_cfa r7 (sp) offset 8 + pos = writeDefCFA(DW_CFA_RSP_IDX, 8, buffer, pos); + // and rip is saved at offset 8 (coded as 1 which gets scaled by dataAlignment) from cfa + // (why not -1 ???) + // offset r16 (rip) cfa - 8 + pos = writeOffset(DW_CFA_RIP_IDX, 1, buffer, pos); + return pos; + } + } + + /** + * AArch64-specific generator for debug_frame section + * that knows details of AArch64 registers and frame layout. + */ + public class DwarfFrameSectionImplAArch64 extends DwarfFrameSectionImpl { + public static final int DW_CFA_FP_IDX = 29; + public static final int DW_CFA_LR_IDX = 30; + public static final int DW_CFA_SP_IDX = 31; + public static final int DW_CFA_PC_IDX = 32; + + public DwarfFrameSectionImplAArch64() { + super(); + } + + @Override + public int getPCIdx() { + return DW_CFA_PC_IDX; + } + + @Override + public int getSPIdx() { + return DW_CFA_SP_IDX; + } + + @Override + public int writeInitialInstructions(byte[] buffer, int p) { + int pos = p; + // rsp has not been updated + // caller pc is in lr + // register r32 (rpc), r30 (lr) + pos = writeRegister(DW_CFA_PC_IDX, DW_CFA_LR_IDX, buffer, pos); + return pos; + } + } + + /** + * generator for debug_info section. + */ + public class DwarfInfoSectionImpl extends DwarfSectionImpl { + /** + * an info header section always contains a fixed number of bytes. + */ + private static final int DW_DIE_HEADER_SIZE = 11; + + public DwarfInfoSectionImpl() { + super(); + } + + @Override + public String getSectionName() { + return DW_INFO_SECTION_NAME; + } + + @Override + public void createContent() { + // we need a single level 0 DIE for each compilation unit (CU) + // Each CU's Level 0 DIE is preceded by a fixed header: + // and terminated by a null DIE + // uint32 length ......... excluding this length field + // uint16 dwarf_version .. always 2 ?? + // uint32 abbrev offset .. always 0 ?? + // uint8 address_size .... always 8 + // * ................ sequence of top-level and nested child entries + // ............ == 0 + // + // a DIE is a recursively defined structure + // it starts with a code for the associated + // abbrev entry followed by a series of attribute + // values as determined by the entry terminated by + // a null value and followed by zero or more child + // DIEs (zero iff has_children == no_children) + // + // LEB128 abbrev_code != 0 .. non-zero value indexes tag + attr layout of DIE + // * ....... value sequence as determined by abbrev entry + // * ................... sequence of child DIEs (if appropriate) + // ............. == 0 + // + // note that a null_DIE looks like + // LEB128 abbrev_code ....... == 0 + // i.e. it also looks like a null_value + + byte[] buffer = null; + int pos = 0; + + for (ClassEntry classEntry : primaryClasses) { + int lengthPos = pos; + pos = writeCUHeader(buffer, pos); + assert pos == lengthPos + DW_DIE_HEADER_SIZE; + pos = writeCU(classEntry, buffer, pos); + // no need to backpatch length at lengthPos + } + buffer = new byte[pos]; + super.setContent(buffer); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + int size = buffer.length; + int pos = 0; + + checkDebug(pos); + + debug(" [0x%08x] DEBUG_INFO\n", pos); + debug(" [0x%08x] size = 0x%08x\n", pos, size); + for (ClassEntry classEntry : primaryClasses) { + // save the offset of this file's CU so it can + // be used when writing the aranges section + classEntry.setCUIndex(pos); + int lengthPos = pos; + pos = writeCUHeader(buffer, pos); + debug(" [0x%08x] Compilation Unit\n", pos, size); + assert pos == lengthPos + DW_DIE_HEADER_SIZE; + pos = writeCU(classEntry, buffer, pos); + // backpatch length at lengthPos (excluding length field) + patchLength(lengthPos, buffer, pos); + } + assert pos == size; + } + + public int writeCUHeader(byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + pos += putInt(0, scratch, 0); // CU length + pos += putShort(DW_VERSION_2, scratch, 0); // dwarf version + pos += putInt(0, scratch, 0); // abbrev offset + return pos + putByte((byte) 8, scratch, 0); // address size + } else { + pos = putInt(0, buffer, pos); // CU length + pos = putShort(DW_VERSION_2, buffer, pos); // dwarf version + pos = putInt(0, buffer, pos); // abbrev offset + return putByte((byte) 8, buffer, pos); // address size + } + } + + public int writeCU(ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + debug(" [0x%08x] <0> Abbrev Number %d\n", pos, DW_ABBREV_CODE_compile_unit); + pos = writeAbbrevCode(DW_ABBREV_CODE_compile_unit, buffer, pos); + debug(" [0x%08x] language %s\n", pos, "DW_LANG_Java"); + pos = writeAttrData1(DW_LANG_Java, buffer, pos); + debug(" [0x%08x] name 0x%x (%s)\n", pos, debugStringIndex(classEntry.getFileName()), classEntry.getFileName()); + pos = writeAttrStrp(classEntry.getFileName(), buffer, pos); + debug(" [0x%08x] low_pc 0x%08x\n", pos, classPrimaryEntries.getFirst().getPrimary().getLo()); + pos = writeAttrAddress(classPrimaryEntries.getFirst().getPrimary().getLo(), buffer, pos); + debug(" [0x%08x] hi_pc 0x%08x\n", pos, classPrimaryEntries.getLast().getPrimary().getHi()); + pos = writeAttrAddress(classPrimaryEntries.getLast().getPrimary().getHi(), buffer, pos); + debug(" [0x%08x] stmt_list 0x%08x\n", pos, classEntry.getLineIndex()); + pos = writeAttrData4(classEntry.getLineIndex(), buffer, pos); + for (PrimaryEntry primaryEntry : classPrimaryEntries) { + pos = writePrimary(primaryEntry, buffer, pos); + } + // write a terminating null attribute for the the level 2 primaries + return writeAttrNull(buffer, pos); + + } + + public int writePrimary(PrimaryEntry primaryEntry, byte[] buffer, int p) { + int pos = p; + Range primary = primaryEntry.getPrimary(); + debug(" [0x%08x] <1> Abbrev Number %d\n", pos, DW_ABBREV_CODE_subprogram); + pos = writeAbbrevCode(DW_ABBREV_CODE_subprogram, buffer, pos); + debug(" [0x%08x] name 0x%X (%s)\n", pos, debugStringIndex(primary.getFullMethodName()), primary.getFullMethodName()); + pos = writeAttrStrp(primary.getFullMethodName(), buffer, pos); + debug(" [0x%08x] low_pc 0x%08x\n", pos, primary.getLo()); + pos = writeAttrAddress(primary.getLo(), buffer, pos); + debug(" [0x%08x] high_pc 0x%08x\n", pos, primary.getHi()); + pos = writeAttrAddress(primary.getHi(), buffer, pos); + // need to pass true only if method is public + debug(" [0x%08x] external true\n", pos); + return writeFlag(DW_FLAG_true, buffer, pos); + } + + public int writeAttrStrp(String value, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + return pos + putInt(0, scratch, 0); + } else { + int idx = debugStringIndex(value); + return putInt(idx, buffer, pos); + } + } + + public int writeAttrString(String value, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + return pos + value.length() + 1; + } else { + return putAsciiStringBytes(value, buffer, pos); + } + } + + @Override + protected void debug(String format, Object... args) { + if (((int) args[0] - debugBase) < 0x100000) { + super.debug(format, args); + } else if (format.startsWith(" [0x%08x] primary file")) { + super.debug(format, args); + } + } + + /** + * debug_info section content depends on abbrev section content and offset. + */ + public static final String TARGET_SECTION_NAME = DW_ABBREV_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } + } + + /** + * generator for debug_aranges section. + */ + public class DwarfARangesSectionImpl extends DwarfSectionImpl { + private static final int DW_AR_HEADER_SIZE = 12; + private static final int DW_AR_HEADER_PAD_SIZE = 4; // align up to 2 * address size + + public DwarfARangesSectionImpl() { + super(); + } + + @Override + public String getSectionName() { + return DW_ARANGES_SECTION_NAME; + } + + @Override + public void createContent() { + int pos = 0; + // we need an entry for each compilation unit + // + // uint32 length ............ in bytes (not counting these 4 bytes) + // uint16 dwarf_version ..... always 2 + // uint32 info_offset ....... offset of compilation unit on debug_info + // uint8 address_size ....... always 8 + // uint8 segment_desc_size .. ??? + // + // i.e. 12 bytes followed by padding + // aligning up to 2 * address size + // + // uint8 pad[4] + // + // followed by N + 1 times + // + // uint64 lo ................ lo address of range + // uint64 length ............ number of bytes in range + // + // where N is the number of ranges belonging to the compilation unit + // and the last range contains two zeroes + + for (ClassEntry classEntry : primaryClasses) { + pos += DW_AR_HEADER_SIZE; + // align to 2 * address size + pos += DW_AR_HEADER_PAD_SIZE; + pos += classEntry.getPrimaryEntries().size() * 2 * 8; + pos += 2 * 8; + } + byte[] buffer = new byte[pos]; + super.setContent(buffer); + } + + @Override + public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { + Element textElement = getElement().getOwner().elementForName(".text"); + LayoutDecisionMap decisionMap = alreadyDecided.get(textElement); + if (decisionMap != null) { + Object valueObj = decisionMap.getDecidedValue(LayoutDecision.Kind.VADDR); + if (valueObj != null && valueObj instanceof Number) { + // this may not be the final vaddr for the text segment + // but it will be close enough to make debug easier + // i.e. to within a 4k page or two + debugTextBase = ((Number) valueObj).longValue(); + } + } + return super.getOrDecideContent(alreadyDecided, contentHint); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + int size = buffer.length; + int pos = 0; + + checkDebug(pos); + + debug(" [0x%08x] DEBUG_ARANGES\n", pos); + for (ClassEntry classEntry : primaryClasses) { + int lastpos = pos; + int length = DW_AR_HEADER_SIZE + DW_AR_HEADER_PAD_SIZE - 4; + int cuIndex = classEntry.getCUIndex(); + LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + // add room for each entry into length count + length += classPrimaryEntries.size() * 2 * 8; + length += 2 * 8; + debug(" [0x%08x] %s CU %d length 0x%x\n", pos, classEntry.getFileName(), cuIndex, length); + pos = putInt(length, buffer, pos); + pos = putShort(DW_VERSION_2, buffer, pos); // dwarf version is always 2 + pos = putInt(cuIndex, buffer, pos); + pos = putByte((byte) 8, buffer, pos); // address size is always 8 + pos = putByte((byte) 0, buffer, pos); // segment size is always 0 + assert (pos - lastpos) == DW_AR_HEADER_SIZE; + // align to 2 * address size + for (int i = 0; i < DW_AR_HEADER_PAD_SIZE; i++) { + pos = putByte((byte) 0, buffer, pos); + } + debug(" [0x%08x] Address Length Name\n", pos); + for (PrimaryEntry classPrimaryEntry : classPrimaryEntries) { + Range primary = classPrimaryEntry.getPrimary(); + debug(" [0x%08x] %016x %016x %s\n", pos, debugTextBase + primary.getLo(), primary.getHi() - primary.getLo(), primary.getFullMethodName()); + pos = putRelocatableCodeOffset(primary.getLo(), buffer, pos); + pos = putLong(primary.getHi() - primary.getLo(), buffer, pos); + } + pos = putLong(0, buffer, pos); + pos = putLong(0, buffer, pos); + } + + assert pos == size; + } + + @Override + protected void debug(String format, Object... args) { + super.debug(format, args); + } + + // .debug_aranges section content depends on .debug_info section content and offset + public static final String TARGET_SECTION_NAME = DW_INFO_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } + } + + /** + * generator for debug_line section. + */ + public class DwarfLineSectionImpl extends DwarfSectionImpl { + /** + * line header section always contains fixed number of bytes. + */ + private static final int DW_LN_HEADER_SIZE = 27; + /** + * current generator follows C++ with line base -5. + */ + private static final int DW_LN_LINE_BASE = -5; + /** + * current generator follows C++ with line range 14 + * giving full range -5 to 8. + */ + private static final int DW_LN_LINE_RANGE = 14; + /** + * current generator uses opcode base of 13 + * which must equal DW_LNS_define_file + 1. + */ + private static final int DW_LN_OPCODE_BASE = 13; + + /* + * standard opcodes defined by Dwarf 2 + */ + private static final byte DW_LNS_undefined = 0; // 0 can be returned to indicate an + // invalid opcode + private static final byte DW_LNS_extended_prefix = 0; // 0 can be inserted as a prefix for + // extended opcodes + private static final byte DW_LNS_copy = 1; // append current state as matrix row + // 0 args + private static final byte DW_LNS_advance_pc = 2; // increment address 1 uleb arg + private static final byte DW_LNS_advance_line = 3; // increment line 1 sleb arg + private static final byte DW_LNS_set_file = 4; // set file 1 uleb arg + private static final byte DW_LNS_set_column = 5; // set column 1 uleb arg + private static final byte DW_LNS_negate_stmt = 6; // flip is_stmt 0 args + private static final byte DW_LNS_set_basic_block = 7; // set end sequence and copy row + private static final byte DW_LNS_const_add_pc = 8; // increment address as per opcode + // 255 0 args + private static final byte DW_LNS_fixed_advance_pc = 9; // increment address 1 ushort arg + + /* + * extended opcodes defined by Dwarf 2 + */ + // private static final byte DW_LNE_undefined = 0; // there is no extended opcode 0 + private static final byte DW_LNE_end_sequence = 1; // end sequence of addresses + private static final byte DW_LNE_set_address = 2; // there is no extended opcode 0 + private static final byte DW_LNE_define_file = 3; // there is no extended opcode 0 + + DwarfLineSectionImpl() { + super(); + } + + @Override + public String getSectionName() { + return DW_LINE_SECTION_NAME; + } + + @Override + public void createContent() { + // we need to create a header, dir table, file table and line + // number table encoding for each CU + + // write entries for each file listed in the primary list + int pos = 0; + for (ClassEntry classEntry : primaryClasses) { + int startPos = pos; + classEntry.setLineIndex(startPos); + int headerSize = headerSize(); + int dirTableSize = computeDirTableSize(classEntry); + int fileTableSize = computeFileTableSize(classEntry); + int prologueSize = headerSize + dirTableSize + fileTableSize; + classEntry.setLinePrologueSize(prologueSize); + int lineNumberTableSize = computeLineNUmberTableSize(classEntry); + int totalSize = prologueSize + lineNumberTableSize; + classEntry.setTotalSize(totalSize); + pos += totalSize; + } + byte[] buffer = new byte[pos]; + super.setContent(buffer); + } + + public int headerSize() { + // header size is standard 31 bytes + // uint32 total_length + // uint16 version + // uint32 prologue_length + // uint8 min_insn_length + // uint8 default_is_stmt + // int8 line_base + // uint8 line_range + // uint8 opcode_base + // uint8 li_opcode_base + // uint8[opcode_base-1] standard_opcode_lengths + + return DW_LN_HEADER_SIZE; + } + + public int computeDirTableSize(ClassEntry classEntry) { + // table contains a sequence of 'nul'-terminated + // dir name bytes followed by an extra 'nul' + // and then a sequence of 'nul'-terminated + // file name bytes followed by an extra 'nul' + + // for now we assume dir and file names are ASCII + // byte strings + int dirSize = 0; + for (DirEntry dir : classEntry.getLocalDirs()) { + dirSize += dir.getPath().length() + 1; + } + // allow for separator nul + dirSize++; + return dirSize; + } + + public int computeFileTableSize(ClassEntry classEntry) { + // table contains a sequence of 'nul'-terminated + // dir name bytes followed by an extra 'nul' + // and then a sequence of 'nul'-terminated + // file name bytes followed by an extra 'nul' + + // for now we assume dir and file names are ASCII + // byte strings + int fileSize = 0; + for (FileEntry localEntry : classEntry.getLocalFiles()) { + // we want the file base name excluding path + String baseName = localEntry.getBaseName(); + int length = baseName.length(); + fileSize += length + 1; + DirEntry dirEntry = localEntry.dirEntry; + int idx = classEntry.localDirsIdx(dirEntry); + fileSize += putULEB(idx, scratch, 0); + // the two zero timestamps require 1 byte each + fileSize += 2; + } + // allow for terminator nul + fileSize++; + return fileSize; + } + + public int computeLineNUmberTableSize(ClassEntry classEntry) { + // sigh -- we have to do this by generating the + // content even though we cannot write it into a byte[] + return writeLineNumberTable(classEntry, null, 0); + } + + @Override + public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { + Element textElement = getElement().getOwner().elementForName(".text"); + LayoutDecisionMap decisionMap = alreadyDecided.get(textElement); + if (decisionMap != null) { + Object valueObj = decisionMap.getDecidedValue(LayoutDecision.Kind.VADDR); + if (valueObj != null && valueObj instanceof Number) { + // this may not be the final vaddr for the text segment + // but it will be close enough to make debug easier + // i.e. to within a 4k page or two + debugTextBase = ((Number) valueObj).longValue(); + } + } + return super.getOrDecideContent(alreadyDecided, contentHint); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + + int pos = 0; + checkDebug(pos); + debug(" [0x%08x] DEBUG_LINE\n", pos); + + for (ClassEntry classEntry : primaryClasses) { + int startPos = pos; + assert classEntry.getLineIndex() == startPos; + debug(" [0x%08x] Compile Unit for %s\n", pos, classEntry.getFileName()); + pos = writeHeader(classEntry, buffer, pos); + debug(" [0x%08x] headerSize = 0x%08x\n", pos, pos - startPos); + int dirTablePos = pos; + pos = writeDirTable(classEntry, buffer, pos); + debug(" [0x%08x] dirTableSize = 0x%08x\n", pos, pos - dirTablePos); + int fileTablePos = pos; + pos = writeFileTable(classEntry, buffer, pos); + debug(" [0x%08x] fileTableSize = 0x%08x\n", pos, pos - fileTablePos); + int lineNumberTablePos = pos; + pos = writeLineNumberTable(classEntry, buffer, pos); + debug(" [0x%08x] lineNumberTableSize = 0x%x\n", pos, pos - lineNumberTablePos); + debug(" [0x%08x] size = 0x%x\n", pos, pos - startPos); + } + assert pos == buffer.length; + } + + public int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + // 4 ubyte length field + pos = putInt(classEntry.getTotalSize() - 4, buffer, pos); + // 2 ubyte version is always 2 + pos = putShort(DW_VERSION_2, buffer, pos); + // 4 ubyte prologue length includes rest of header and + // dir + file table section + int prologueSize = classEntry.getLinePrologueSize() - 6; + pos = putInt(prologueSize, buffer, pos); + // 1 ubyte min instruction length is always 1 + pos = putByte((byte) 1, buffer, pos); + // 1 byte default is_stmt is always 1 + pos = putByte((byte) 1, buffer, pos); + // 1 byte line base is always -5 + pos = putByte((byte) DW_LN_LINE_BASE, buffer, pos); + // 1 ubyte line range is always 14 giving range -5 to 8 + pos = putByte((byte) DW_LN_LINE_RANGE, buffer, pos); + // 1 ubyte opcode base is always 13 + pos = putByte((byte) DW_LN_OPCODE_BASE, buffer, pos); + // specify opcode arg sizes for the standard opcodes + putByte((byte) 0, buffer, pos); // DW_LNS_copy + putByte((byte) 1, buffer, pos + 1); // DW_LNS_advance_pc + putByte((byte) 1, buffer, pos + 2); // DW_LNS_advance_line + putByte((byte) 1, buffer, pos + 3); // DW_LNS_set_file + putByte((byte) 1, buffer, pos + 4); // DW_LNS_set_column + putByte((byte) 0, buffer, pos + 5); // DW_LNS_negate_stmt + putByte((byte) 0, buffer, pos + 6); // DW_LNS_set_basic_block + putByte((byte) 0, buffer, pos + 7); // DW_LNS_const_add_pc + putByte((byte) 1, buffer, pos + 8); // DW_LNS_fixed_advance_pc + putByte((byte) 0, buffer, pos + 9); // DW_LNS_end_sequence + putByte((byte) 0, buffer, pos + 10); // DW_LNS_set_address + pos = putByte((byte) 1, buffer, pos + 11); // DW_LNS_define_file + return pos; + } + + public int writeDirTable(ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + debug(" [0x%08x] Dir Name\n", pos); + // write out the list of dirs referenced form this file entry + int dirIdx = 1; + for (DirEntry dir : classEntry.getLocalDirs()) { + // write nul terminated string text. + debug(" [0x%08x] %-4d %s\n", pos, dirIdx, dir.getPath()); + pos = putAsciiStringBytes(dir.getPath(), buffer, pos); + dirIdx++; + } + // separate dirs from files with a nul + pos = putByte((byte) 0, buffer, pos); + return pos; + } + + public int writeFileTable(ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + int fileIdx = 1; + debug(" [0x%08x] Entry Dir Name\n", pos); + for (FileEntry localEntry : classEntry.getLocalFiles()) { + // we need the file name minus path, the associated dir index, and 0 for time stamps + String baseName = localEntry.getBaseName(); + DirEntry dirEntry = localEntry.dirEntry; + int dirIdx = classEntry.localDirsIdx(dirEntry); + debug(" [0x%08x] %-5d %-5d %s\n", pos, fileIdx, dirIdx, baseName); + pos = putAsciiStringBytes(baseName, buffer, pos); + pos = putULEB(dirIdx, buffer, pos); + pos = putULEB(0, buffer, pos); + pos = putULEB(0, buffer, pos); + fileIdx++; + } + // terminate files with a nul + pos = putByte((byte) 0, buffer, pos); + return pos; + } + + public int debugLine = 1; + public int debugCopyCount = 0; + + public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + // the primary file entry should always be first in the local files list + assert classEntry.localFilesIdx(classEntry.getFileEntry()) == 1; + String primaryClassName = classEntry.getClassName(); + String primaryFileName = classEntry.getFileName(); + String file = primaryFileName; + int fileIdx = 1; + debug(" [0x%08x] primary class %s\n", pos, primaryClassName); + debug(" [0x%08x] primary file %s\n", pos, primaryFileName); + for (PrimaryEntry primaryEntry : classEntry.getPrimaryEntries()) { + Range primaryRange = primaryEntry.getPrimary(); + assert primaryRange.getFileName().equals(primaryFileName); + // each primary represents a method i.e. a contiguous + // sequence of subranges. we assume the default state + // at the start of each sequence because we always post an + // end_sequence when we finish all the subranges in the method + long line = primaryRange.getLine(); + if (line < 0 && primaryEntry.getSubranges().size() > 0) { + line = primaryEntry.getSubranges().get(0).getLine(); + } + if (line < 0) { + line = 0; + } + long address = primaryRange.getLo(); + + // set state for primary + debug(" [0x%08x] primary range [0x%08x, 0x%08x] %s:%d\n", pos, debugTextBase + primaryRange.getLo(), debugTextBase + primaryRange.getHi(), primaryRange.getFullMethodName(), + primaryRange.getLine()); + + // initialize and write a row for the start of the primary method + pos = putSetFile(file, fileIdx, buffer, pos); + pos = putSetBasicBlock(buffer, pos); + // address is currently 0 + pos = putSetAddress(address, buffer, pos); + // state machine value of line is currently 1 + // increment to desired line + if (line != 1) { + pos = putAdvanceLine(line - 1, buffer, pos); + } + pos = putCopy(buffer, pos); + + // now write a row for each subrange lo and hi + for (Range subrange : primaryEntry.getSubranges()) { + assert subrange.getLo() >= primaryRange.getLo(); + assert subrange.getHi() <= primaryRange.getHi(); + FileEntry subFileEntry = primaryEntry.getSubrangeFileEntry(subrange); + String subfile = subFileEntry.getFileName(); + int subFileIdx = classEntry.localFilesIdx(subFileEntry); + long subLine = subrange.getLine(); + long subAddressLo = subrange.getLo(); + long subAddressHi = subrange.getHi(); + debug(" [0x%08x] sub range [0x%08x, 0x%08x] %s:%d\n", pos, debugTextBase + subAddressLo, debugTextBase + subAddressHi, subrange.getFullMethodName(), subLine); + if (subLine < 0) { + // no line info so stay at previous file:line + subLine = line; + subfile = file; + subFileIdx = fileIdx; + debug(" [0x%08x] missing line info - staying put at %s:%d\n", pos, file, line); + } + // there is a temptation to append end sequence at here + // when the hiAddress lies strictly between the current + // address and the start of the next subrange because, + // ostensibly, we have void space between the end of + // the current subrange and the start of the next one. + // however, debug works better if we treat all the insns up + // to the next range start as belonging to the current line + // + // if we have to update to a new file then do so + if (subFileIdx != fileIdx) { + // update the current file + pos = putSetFile(subfile, subFileIdx, buffer, pos); + file = subfile; + fileIdx = subFileIdx; + } + // check if we can advance line and/or address in + // one byte with a special opcode + long lineDelta = subLine - line; + long addressDelta = subAddressLo - address; + byte opcode = isSpecialOpcode(addressDelta, lineDelta); + if (opcode != DW_LNS_undefined) { + // ignore pointless write when addressDelta == lineDelta == 0 + if (addressDelta != 0 || lineDelta != 0) { + pos = putSpecialOpcode(opcode, buffer, pos); + } + } else { + // does it help to divide and conquer using + // a fixed address increment + int remainder = isConstAddPC(addressDelta); + if (remainder > 0) { + pos = putConstAddPC(buffer, pos); + // the remaining address can be handled with a + // special opcode but what about the line delta + opcode = isSpecialOpcode(remainder, lineDelta); + if (opcode != DW_LNS_undefined) { + // address remainder and line now fit + pos = putSpecialOpcode(opcode, buffer, pos); + } else { + // ok, bump the line separately then use a + // special opcode for the address remainder + opcode = isSpecialOpcode(remainder, 0); + assert opcode != DW_LNS_undefined; + pos = putAdvanceLine(lineDelta, buffer, pos); + pos = putSpecialOpcode(opcode, buffer, pos); + } + } else { + // increment line and pc separately + if (lineDelta != 0) { + pos = putAdvanceLine(lineDelta, buffer, pos); + } + // n.b. we might just have had an out of range line increment + // with a zero address increment + if (addressDelta > 0) { + // see if we can use a ushort for the increment + if (isFixedAdvancePC(addressDelta)) { + pos = putFixedAdvancePC((short) addressDelta, buffer, pos); + } else { + pos = putAdvancePC(addressDelta, buffer, pos); + } + } + pos = putCopy(buffer, pos); + } + } + // move line and address range on + line += lineDelta; + address += addressDelta; + } + // append a final end sequence just below the next primary range + if (address < primaryRange.getHi()) { + long addressDelta = primaryRange.getHi() - address; + // increment address before we write the end sequence + pos = putAdvancePC(addressDelta, buffer, pos); + } + pos = putEndSequence(buffer, pos); + } + debug(" [0x%08x] primary file processed %s\n", pos, primaryFileName); + + return pos; + } + + @Override + protected void debug(String format, Object... args) { + if (((int) args[0] - debugBase) < 0x100000) { + super.debug(format, args); + } else if (format.startsWith(" [0x%08x] primary file")) { + super.debug(format, args); + } + } + + public int putCopy(byte[] buffer, int p) { + byte opcode = DW_LNS_copy; + int pos = p; + if (buffer == null) { + return pos + putByte(opcode, scratch, 0); + } else { + debugCopyCount++; + debug(" [0x%08x] Copy %d\n", pos, debugCopyCount); + return putByte(opcode, buffer, pos); + } + } + + public int putAdvancePC(long uleb, byte[] buffer, int p) { + byte opcode = DW_LNS_advance_pc; + int pos = p; + if (buffer == null) { + pos = pos + putByte(opcode, scratch, 0); + return pos + putULEB(uleb, scratch, 0); + } else { + debugAddress += uleb; + debug(" [0x%08x] Advance PC by %d to 0x%08x\n", pos, uleb, debugAddress); + pos = putByte(opcode, buffer, pos); + return putULEB(uleb, buffer, pos); + } + } + + public int putAdvanceLine(long sleb, byte[] buffer, int p) { + byte opcode = DW_LNS_advance_line; + int pos = p; + if (buffer == null) { + pos = pos + putByte(opcode, scratch, 0); + return pos + putSLEB(sleb, scratch, 0); + } else { + debugLine += sleb; + debug(" [0x%08x] Advance Line by %d to %d\n", pos, sleb, debugLine); + pos = putByte(opcode, buffer, pos); + return putSLEB(sleb, buffer, pos); + } + } + + public int putSetFile(String file, long uleb, byte[] buffer, int p) { + byte opcode = DW_LNS_set_file; + int pos = p; + if (buffer == null) { + pos = pos + putByte(opcode, scratch, 0); + return pos + putULEB(uleb, scratch, 0); + } else { + debug(" [0x%08x] Set File Name to entry %d in the File Name Table (%s)\n", pos, uleb, file); + pos = putByte(opcode, buffer, pos); + return putULEB(uleb, buffer, pos); + } + } + + public int putSetColumn(long uleb, byte[] buffer, int p) { + byte opcode = DW_LNS_set_column; + int pos = p; + if (buffer == null) { + pos = pos + putByte(opcode, scratch, 0); + return pos + putULEB(uleb, scratch, 0); + } else { + pos = putByte(opcode, buffer, pos); + return putULEB(uleb, buffer, pos); + } + } + + public int putNegateStmt(byte[] buffer, int p) { + byte opcode = DW_LNS_negate_stmt; + int pos = p; + if (buffer == null) { + return pos + putByte(opcode, scratch, 0); + } else { + return putByte(opcode, buffer, pos); + } + } + + public int putSetBasicBlock(byte[] buffer, int p) { + byte opcode = DW_LNS_set_basic_block; + int pos = p; + if (buffer == null) { + return pos + putByte(opcode, scratch, 0); + } else { + debug(" [0x%08x] Set basic block\n", pos); + return putByte(opcode, buffer, pos); + } + } + + public int putConstAddPC(byte[] buffer, int p) { + byte opcode = DW_LNS_const_add_pc; + int pos = p; + if (buffer == null) { + return pos + putByte(opcode, scratch, 0); + } else { + int advance = opcodeAddress((byte) 255); + debugAddress += advance; + debug(" [0x%08x] Advance PC by constant %d to 0x%08x\n", pos, advance, debugAddress); + return putByte(opcode, buffer, pos); + } + } + + public int putFixedAdvancePC(short arg, byte[] buffer, int p) { + byte opcode = DW_LNS_fixed_advance_pc; + int pos = p; + if (buffer == null) { + pos = pos + putByte(opcode, scratch, 0); + return pos + putShort(arg, scratch, 0); + } else { + debugAddress += arg; + debug(" [0x%08x] Fixed advance Address by %d to 0x%08x\n", pos, arg, debugAddress); + pos = putByte(opcode, buffer, pos); + return putShort(arg, buffer, pos); + } + } + + public int putEndSequence(byte[] buffer, int p) { + byte opcode = DW_LNE_end_sequence; + int pos = p; + if (buffer == null) { + pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); + // insert extended insn byte count as ULEB + pos = pos + putULEB(1, scratch, 0); + return pos + putByte(opcode, scratch, 0); + } else { + debug(" [0x%08x] Extended opcode 1: End sequence\n", pos); + debugAddress = debugTextBase; + debugLine = 1; + debugCopyCount = 0; + pos = putByte(DW_LNS_extended_prefix, buffer, pos); + // insert extended insn byte count as ULEB + pos = putULEB(1, buffer, pos); + return putByte(opcode, buffer, pos); + } + } + + public int putSetAddress(long arg, byte[] buffer, int p) { + byte opcode = DW_LNE_set_address; + int pos = p; + if (buffer == null) { + pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); + // insert extended insn byte count as ULEB + pos = pos + putULEB(9, scratch, 0); + pos = pos + putByte(opcode, scratch, 0); + return pos + putLong(arg, scratch, 0); + } else { + debugAddress = debugTextBase + (int) arg; + debug(" [0x%08x] Extended opcode 2: Set Address to 0x%08x\n", pos, debugAddress); + pos = putByte(DW_LNS_extended_prefix, buffer, pos); + // insert extended insn byte count as ULEB + pos = putULEB(9, buffer, pos); + pos = putByte(opcode, buffer, pos); + return putRelocatableCodeOffset(arg, buffer, pos); + } + } + + public int putDefineFile(String file, long uleb1, long uleb2, long uleb3, byte[] buffer, int p) { + byte opcode = DW_LNE_define_file; + int pos = p; + // calculate bytes needed for opcode + args + int fileBytes = file.length() + 1; + long insnBytes = 1; + insnBytes += fileBytes; + insnBytes += putULEB(uleb1, scratch, 0); + insnBytes += putULEB(uleb2, scratch, 0); + insnBytes += putULEB(uleb3, scratch, 0); + if (buffer == null) { + pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); + // write insnBytes as a ULEB + pos += putULEB(insnBytes, scratch, 0); + return pos + (int) insnBytes; + } else { + debug(" [0x%08x] Extended opcode 3: Define File %s idx %d ts1 %d ts2 %d\n", pos, file, uleb1, uleb2, uleb3); + pos = putByte(DW_LNS_extended_prefix, buffer, pos); + // insert insn length as uleb + pos = putULEB(insnBytes, buffer, pos); + // insert opcode and args + pos = putByte(opcode, buffer, pos); + pos = putAsciiStringBytes(file, buffer, pos); + pos = putULEB(uleb1, buffer, pos); + pos = putULEB(uleb2, buffer, pos); + return putULEB(uleb3, buffer, pos); + } + } + + public int opcodeId(byte opcode) { + int iopcode = opcode & 0xff; + return iopcode - DW_LN_OPCODE_BASE; + } + + public int opcodeAddress(byte opcode) { + int iopcode = opcode & 0xff; + return (iopcode - DW_LN_OPCODE_BASE) / DW_LN_LINE_RANGE; + } + + public int opcodeLine(byte opcode) { + int iopcode = opcode & 0xff; + return ((iopcode - DW_LN_OPCODE_BASE) % DW_LN_LINE_RANGE) + DW_LN_LINE_BASE; + } + + public int putSpecialOpcode(byte opcode, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + return pos + putByte(opcode, scratch, 0); + } else { + if (debug && opcode == 0) { + debug(" [0x%08x] ERROR Special Opcode %d: Address 0x%08x Line %d\n", debugAddress, debugLine); + } + debugAddress += opcodeAddress(opcode); + debugLine += opcodeLine(opcode); + debug(" [0x%08x] Special Opcode %d: advance Address by %d to 0x%08x and Line by %d to %d\n", + pos, opcodeId(opcode), opcodeAddress(opcode), debugAddress, opcodeLine(opcode), debugLine); + return putByte(opcode, buffer, pos); + } + } + + private static final int MAX_ADDRESS_ONLY_DELTA = (0xff - DW_LN_OPCODE_BASE) / DW_LN_LINE_RANGE; + private static final int MAX_ADDPC_DELTA = MAX_ADDRESS_ONLY_DELTA + (MAX_ADDRESS_ONLY_DELTA - 1); + + public byte isSpecialOpcode(long addressDelta, long lineDelta) { + if (addressDelta < 0) { + return DW_LNS_undefined; + } + if (lineDelta >= DW_LN_LINE_BASE) { + long offsetLineDelta = lineDelta - DW_LN_LINE_BASE; + if (offsetLineDelta < DW_LN_LINE_RANGE) { + // line_delta can be encoded + // check if address is ok + if (addressDelta <= MAX_ADDRESS_ONLY_DELTA) { + long opcode = DW_LN_OPCODE_BASE + (addressDelta * DW_LN_LINE_RANGE) + offsetLineDelta; + if (opcode <= 255) { + return (byte) opcode; + } + } + } + } + + // return invalid opcode + return DW_LNS_undefined; + } + + public int isConstAddPC(long addressDelta) { + if (addressDelta < MAX_ADDRESS_ONLY_DELTA) { + return 0; + } + if (addressDelta <= MAX_ADDPC_DELTA) { + return (int) (addressDelta - MAX_ADDRESS_ONLY_DELTA); + } else { + return 0; + } + } + + public boolean isFixedAdvancePC(long addressDiff) { + return addressDiff >= 0 && addressDiff < 0xffff; + } + + /** + * debug_line section content depends on debug_str section content and offset. + */ + public static final String TARGET_SECTION_NAME = DW_STR_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET, + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java new file mode 100644 index 000000000000..cd530c68de04 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +/** + * track debug info associated with a Java source file. + */ +public class FileEntry { + // the name of the associated file including path + private String fileName; + // the name of the associated file excluding path + private String baseName; + // the directory entry associated with this file entry + DirEntry dirEntry; + + public FileEntry(String fileName, String baseName, DirEntry dirEntry) { + this.fileName = fileName; + this.baseName = baseName; + this.dirEntry = dirEntry; + } + + public String getFileName() { + return fileName; + } + + public String getBaseName() { + return baseName; + } + + String getDirName() { + return (dirEntry != null ? dirEntry.getPath() : ""); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/PrimaryEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/PrimaryEntry.java new file mode 100644 index 000000000000..6720703cf921 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/PrimaryEntry.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +/** + * track debug info associated with a primary method. + * i.e. a top level compiled method + */ +public class PrimaryEntry { + /** + * the primary range detailed by this object. + */ + Range primary; + /** + * details of the class owning this range. + */ + ClassEntry classEntry; + /** + * a list of subranges associated with the primary range. + */ + List subranges; + /** + * a mapping from subranges to their associated file entry. + */ + HashMap subrangeIndex; + /** + * details of of compiled method frame size changes. + */ + private List frameSizeInfos; + /** + * size of compiled method frame. + */ + private int frameSize; + /** + * index of debug_info section compilation unit for this file. + */ + private int cuIndex; + /** + * index into debug_line section for associated compilation unit. + */ + private int lineIndex; + /** + * size of line number info prologue region for associated compilation unit. + */ + private int linePrologueSize; + /** + * total size of line number info region for associated compilation unit. + */ + private int totalSize; + + public PrimaryEntry(Range primary, List frameSizeInfos, int frameSize, ClassEntry classEntry) { + this.primary = primary; + this.classEntry = classEntry; + this.subranges = new LinkedList<>(); + this.subrangeIndex = new HashMap<>(); + this.frameSizeInfos = frameSizeInfos; + this.frameSize = frameSize; + // initialize indices into other sections to illegal values + this.cuIndex = -1; + this.lineIndex = -1; + } + + public void addSubRange(Range subrange, FileEntry subFileEntry) { + // we should not see a subrange more than once + assert !subranges.contains(subrange); + assert subrangeIndex.get(subrange) == null; + // we need to generate a file table entry + // for all ranges + subranges.add(subrange); + subrangeIndex.put(subrange, subFileEntry); + } + + public Range getPrimary() { + return primary; + } + + public ClassEntry getClassEntry() { + return classEntry; + } + + public FileEntry getFileEntry() { + return classEntry.getFileEntry(); + } + + public List getSubranges() { + return subranges; + } + + public FileEntry getSubrangeFileEntry(Range subrange) { + return subrangeIndex.get(subrange); + } + + List getFrameSizeInfos() { + return frameSizeInfos; + } + + int getFrameSize() { + return frameSize; + } + + void setCUIndex(int cuIndex) { + // should only get set once to a non-negative value + assert cuIndex >= 0; + assert this.cuIndex == -1; + this.cuIndex = cuIndex; + } + + int getCUIndex() { + // should have been set before being read + assert cuIndex >= 0; + return cuIndex; + } + + int getLineIndex() { + // should have been set before being read + assert lineIndex >= 0; + return lineIndex; + } + + void setLineIndex(int lineIndex) { + // should only get set once to a non-negative value + assert lineIndex >= 0; + assert this.lineIndex == -1; + this.lineIndex = lineIndex; + } + + public int getLinePrologueSize() { + return linePrologueSize; + } + + public void setLinePrologueSize(int linePrologueSize) { + this.linePrologueSize = linePrologueSize; + } + + public int getTotalSize() { + return totalSize; + } + + public void setTotalSize(int totalSize) { + this.totalSize = totalSize; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java new file mode 100644 index 000000000000..758825272a7e --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +/** + * details of a specific address range in a compiled method + * either a primary range identifying a whole method + * or a sub-range identifying a sequence of + * instructions that belong to an inlined method + */ + +public class Range { + private String fileName; + private String className; + private String methodName; + private String paramNames; + private String returnTypeName; + private String fullMethodName; + private int lo; + private int hi; + private int line; + // this is null for a primary range + private Range primary; + + // create a primary range + Range(String fileName, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line) { + this(fileName, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, line, null); + } + + // create a primary or secondary range + Range(String fileName, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line, Range primary) { + // currently file name and full method name need to go into the debug_str section + // other strings just need to be deduplicated to save space + this.fileName = stringTable.uniqueDebugString(fileName); + this.className = stringTable.uniqueString(className); + this.methodName = stringTable.uniqueString(methodName); + this.paramNames = stringTable.uniqueString(paramNames); + this.returnTypeName = stringTable.uniqueString(returnTypeName); + this.fullMethodName = stringTable.uniqueDebugString(constructClassAndMethodNameWithParams()); + this.lo = lo; + this.hi = hi; + this.line = line; + this.primary = primary; + } + + public boolean contains(Range other) { + return (lo <= other.lo && hi >= other.hi); + } + + public boolean isPrimary() { + return getPrimary() == null; + } + + public Range getPrimary() { + return primary; + } + + public String getFileName() { + return fileName; + } + + public String getClassName() { + return className; + } + + public String getMethodName() { + return methodName; + } + + public int getHi() { + return hi; + } + + public int getLo() { + return lo; + } + + public int getLine() { + return line; + } + + public String getFullMethodName() { + return fullMethodName; + } + + public String getExtendedMethodName(boolean includeParams, boolean includeReturnType) { + StringBuilder builder = new StringBuilder(); + if (includeReturnType && returnTypeName.length() > 0) { + builder.append(returnTypeName); + builder.append(' '); + } + if (className != null) { + builder.append(className); + builder.append("::"); + } + builder.append(methodName); + if (includeParams && !paramNames.isEmpty()) { + builder.append('('); + builder.append(paramNames); + builder.append(')'); + } + return builder.toString(); + } + + private String constructClassAndMethodNameWithParams() { + return getExtendedMethodName(true, false); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java new file mode 100644 index 000000000000..d9a40dc2b3ed --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +/** + * class used to retain a unique (up to equals) copy of + * a String and also to flag whether the String needs to be + * located in the .debug_string section and track the offset + * where it gets written. + */ +public class StringEntry { + private String string; + private int offset; + private boolean addToStrSection; + + StringEntry(String string) { + this.string = string; + this.offset = -1; + } + + public String getString() { + return string; + } + + public int getOffset() { + // offset must be set before this can be fetched + assert offset >= 0; + return offset; + } + + public void setOffset(int offset) { + assert this.offset < 0; + assert offset >= 0; + this.offset = offset; + } + + public boolean isAddToStrSection() { + return addToStrSection; + } + + public void setAddToStrSection() { + this.addToStrSection = true; + } + + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof StringEntry)) { + return false; + } else { + StringEntry other = (StringEntry) object; + return this == other || string.equals(other.string); + } + } + + @Override + public int hashCode() { + return string.hashCode() + 37; + } + + @Override + public String toString() { + return string; + } + + public boolean isEmpty() { + return string.length() == 0; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java new file mode 100644 index 000000000000..87b0af1eb437 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +import java.util.HashMap; +import java.util.Iterator; + +/** + * a class which allows incoming strings to be reduced + * to unique (up to equaals) instances and allows marking + * of strings which need to be written to the debug_str + * section and retrieval of the lcoation offset after writing. + */ +public class StringTable implements Iterable { + + private final HashMap table; + + public StringTable() { + this.table = new HashMap<>(); + } + + /** + * ensures a unique instance of a string exists in the + * table, inserting the supplied String if no equivalent + * String is already present. this should only be called + * before the string section has been written. + * @param string the string to be included in the table + * @return the unique instance of the String + */ + public String uniqueString(String string) { + return ensureString(string, false); + } + + /** + * ensures a unique instance of a string exists in the + * table and is marked for inclusion in the debug_str + * section, inserting the supplied String if no equivalent + * String is already present. this should only be called + * before the string section has been written. + * @param string the string to be included in the table + * and marked for inclusion in the debug_str section + * @return the unique instance of the String + */ + public String uniqueDebugString(String string) { + return ensureString(string, true); + } + + private String ensureString(String string, boolean addToStrSection) { + StringEntry stringEntry = table.get(string); + if (stringEntry == null) { + stringEntry = new StringEntry(string); + table.put(string, stringEntry); + } + if (addToStrSection && !stringEntry.isAddToStrSection()) { + stringEntry.setAddToStrSection(); + } + return stringEntry.getString(); + } + + /** + * retrieves the offset at which a given string was written + * into the debug_str section. this should only be called + * after the string section has been written. + * @param string + * @return the offset or -1 if the string does not + * define an entry or the entry has nto been written + * to the debug_str section + */ + public int debugStringIndex(String string) { + StringEntry stringEntry = table.get(string); + assert stringEntry != null; + if (stringEntry == null) { + return -1; + } + return stringEntry.getOffset(); + } + + @Override + public Iterator iterator() { + return table.values().iterator(); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java index 5f121d3ad5ee..30b121818e66 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.hosted.image; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.EXTEND; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.CONTRACT; import static com.oracle.svm.core.SubstrateUtil.mangleName; import static com.oracle.svm.core.util.VMError.shouldNotReachHere; @@ -44,18 +46,27 @@ import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import com.oracle.svm.core.option.HostedOptionValues; +import com.oracle.svm.hosted.meta.HostedType; +import jdk.vm.ci.code.site.Mark; +import jdk.vm.ci.meta.LineNumberTable; import org.graalvm.collections.Pair; import org.graalvm.compiler.code.CompilationResult; +import org.graalvm.compiler.code.SourceMapping; import org.graalvm.compiler.core.common.CompressEncoding; +import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.Indent; +import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.compiler.serviceprovider.BufferUtil; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.CFunctionPointer; @@ -67,6 +78,10 @@ import com.oracle.objectfile.LayoutDecision; import com.oracle.objectfile.LayoutDecisionMap; import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLineInfo; import com.oracle.objectfile.ObjectFile.Element; import com.oracle.objectfile.ObjectFile.ProgbitsSectionImpl; import com.oracle.objectfile.ObjectFile.RelocationKind; @@ -456,6 +471,12 @@ public void build(DebugContext debug, ImageHeapLayouter layouter) { cGlobals.writeData(rwDataBuffer, (offset, symbolName) -> defineDataSymbol(symbolName, rwDataSection, offset + RWDATA_CGLOBALS_PARTITION_OFFSET)); defineDataSymbol(CGlobalDataInfo.CGLOBALDATA_BASE_SYMBOL_NAME, rwDataSection, RWDATA_CGLOBALS_PARTITION_OFFSET); + // if we have constructed any debug info then + // give the object file a chance to install it + if (GraalOptions.TrackNodeSourcePosition.getValue(HostedOptionValues.singleton())) { + DebugInfoProvider provider = new NativeImageDebugInfoProvider(codeCache, heap); + objectFile.installDebugInfo(provider); + } // - Write the heap, either to its own section, or to the ro and rw data sections. RelocatableBuffer heapSectionBuffer = null; ProgbitsSectionImpl heapSectionImpl = null; @@ -950,4 +971,299 @@ protected NativeTextSectionImpl(RelocatableBuffer relocatableBuffer, ObjectFile protected final ObjectFile objectFile; protected final NativeImageCodeCache codeCache; } + + /** + * implementation of the DebugInfoProvider API interface + * that allows type, code and heap data info to be passed to + * an ObjectFile when generation of debug info is enabled. + */ + private class NativeImageDebugInfoProvider implements DebugInfoProvider { + private final NativeImageCodeCache codeCache; + private final NativeImageHeap heap; + private final Iterator> codeCacheIterator; + private final Iterator> heapIterator; + + NativeImageDebugInfoProvider(NativeImageCodeCache codeCache, NativeImageHeap heap) { + super(); + this.codeCache = codeCache; + this.heap = heap; + this.codeCacheIterator = codeCache.compilations.entrySet().iterator(); + this.heapIterator = heap.objects.entrySet().iterator(); + } + + @Override + public DebugTypeInfoProvider typeInfoProvider() { + return () -> new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public DebugTypeInfo next() { + return null; + } + }; + } + + @Override + public DebugCodeInfoProvider codeInfoProvider() { + return () -> new Iterator() { + @Override + public boolean hasNext() { + return codeCacheIterator.hasNext(); + } + + @Override + public DebugCodeInfo next() { + Map.Entry entry = codeCacheIterator.next(); + return new NativeImageDebugCodeInfo(entry.getKey(), entry.getValue()); + } + }; + } + + @Override + public DebugDataInfoProvider dataInfoProvider() { + return () -> new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public DebugDataInfo next() { + return null; + } + }; + } + } + + /** + * implementation of the DebugCodeInfo API interface + * that allows code info to be passed to an ObjectFile + * when generation of debug info is enabled. + */ + private class NativeImageDebugCodeInfo implements DebugCodeInfo { + private final HostedMethod method; + private final CompilationResult compilation; + + NativeImageDebugCodeInfo(HostedMethod method, CompilationResult compilation) { + this.method = method; + this.compilation = compilation; + } + + @Override + public String fileName() { + HostedType declaringClass = method.getDeclaringClass(); + String name = declaringClass.getSourceFileName(); + if (name != null) { + // the file name will not include any path + // use the package to create a path prefix + Package pkg = declaringClass.getJavaClass().getPackage(); + if (pkg != null) { + String prefix = pkg.getName(); + prefix = prefix.replace('.', '/'); + name = prefix + "/" + name; + } + } else { + // build file name from the class name which includes the package + name = className(); + // try to map inner classes back to their parent class's file + int idx = name.indexOf('$'); + if (idx == 0) { + // name is $XXX so cannot associate with a file + return ""; + } + if (idx > 0) { + // name is XXX$YYY so use outer class to derive file name + name = name.substring(0, idx); + } + name = name.replace('.', '/') + ".java"; + } + return name; + } + + @Override + public String className() { + return method.format("%H"); + } + + @Override + public String methodName() { + return method.format("%n"); + } + + @Override + public String paramNames() { + return method.format("%P"); + } + + @Override + public String returnTypeName() { + return method.format("%R"); + } + + @Override + public int addressLo() { + return method.getCodeAddressOffset(); + } + + @Override + public int addressHi() { + return method.getCodeAddressOffset() + compilation.getTargetCodeSize(); + } + + @Override + public int line() { + LineNumberTable lineNumberTable = method.getLineNumberTable(); + if (lineNumberTable != null) { + return lineNumberTable.getLineNumber(0); + } + return -1; + } + + @Override + public DebugInfoProvider.DebugLineInfoProvider lineInfoProvider() { + if (fileName().length() == 0) { + return () -> new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public DebugLineInfo next() { + return null; + } + }; + } + return () -> new Iterator() { + final Iterator sourceIterator = compilation.getSourceMappings().iterator(); + + @Override + public boolean hasNext() { + return sourceIterator.hasNext(); + } + + @Override + public DebugLineInfo next() { + return new NativeImageDebugLineInfo(sourceIterator.next()); + } + }; + } + + public int getFrameSize() { + return compilation.getTotalFrameSize(); + } + + public List getFrameSizeChanges() { + List frameSizeChanges = new LinkedList<>(); + for (Mark mark : compilation.getMarks()) { + // we only need to observe stack increment or decrement points + if (mark.id.equals("PROLOGUE_DECD_RSP")) { + NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, EXTEND); + frameSizeChanges.add(sizeChange); + // } else if (mark.id.equals("PROLOGUE_END")) { + // can ignore these + // } else if (mark.id.equals("EPILOGUE_START")) { + // can ignore these + } else if (mark.id.equals("EPILOGUE_INCD_RSP")) { + NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, CONTRACT); + frameSizeChanges.add(sizeChange); + // } else if(mark.id.equals("EPILOGUE_END")) { + } + } + return frameSizeChanges; + } + } + + /** + * implementation of the DebugLineInfo API interface + * that allows line number info to be passed to an + * ObjectFile when generation of debug info is enabled. + */ + private class NativeImageDebugLineInfo implements DebugLineInfo { + private final int bci; + private final ResolvedJavaMethod method; + private final int lo; + private final int hi; + + NativeImageDebugLineInfo(SourceMapping sourceMapping) { + NodeSourcePosition position = sourceMapping.getSourcePosition(); + int bci = position.getBCI(); + this.bci = (bci >= 0 ? bci : 0); + this.method = position.getMethod(); + this.lo = sourceMapping.getStartOffset(); + this.hi = sourceMapping.getEndOffset(); + } + + @Override + public String fileName() { + String name = className(); + int idx = name.indexOf('$'); + if (idx == 0) { + // name is $XXX so cannot associate with a file + return ""; + } + if (idx > 0) { + // name is XXX$YYY so use outer class to derive file name + name = name.substring(0, idx); + } + return name.replace('.', '/') + ".java"; + } + + @Override + public String className() { + return method.format("%H"); + } + + @Override + public String methodName() { + return method.format("%n"); + } + + @Override + public int addressLo() { + return lo; + } + + @Override + public int addressHi() { + return hi; + } + + @Override + public int line() { + LineNumberTable lineNumberTable = method.getLineNumberTable(); + if (lineNumberTable != null) { + return lineNumberTable.getLineNumber(bci); + } + return -1; + } + } + + /** + * implementation of the DebugFrameSizeChange API interface + * that allows stack frame size change info to be passed to + * an ObjectFile when generation of debug info is enabled. + */ + private class NativeImageDebugFrameSizeChange implements DebugFrameSizeChange { + private int offset; + private Type type; + + NativeImageDebugFrameSizeChange(int offset, Type type) { + this.offset = offset; + this.type = type; + } + + @Override + public int getOffset() { + return offset; + } + + @Override + public Type getType() { + return type; + } + } } diff --git a/substratevm/write_gdbsourcepath b/substratevm/write_gdbsourcepath new file mode 100644 index 000000000000..b211b305a543 --- /dev/null +++ b/substratevm/write_gdbsourcepath @@ -0,0 +1,193 @@ +#!/bin/bash + + +function usage() +{ + echo "write_gdbsourcepath [-h | -v]" + echo "writes command to set source path to .gdbsourcepath" + echo "set GRAAL_SRC_ROOT to graal git repo root" + echo " defaults to .. when . = sdk, compile, substratevm, truffle" + echo "set GRAAL_JDK_SRC_ROOT to unzipped src.zip root" + echo " defaults to JAVA_HOME/src" + exit $1 +} + +function check_args() +{ + while [ $# -ge 1 ]; do + if [ "$1" == "-v" ]; then + VERBOSE=1 + shift + elif [ "$1" == "-h" ]; then + usage 0 + else + usage 1 + fi + done +} + +typeset -i VERBOSE +VERBOSE=0 + +# debug +function verbose() +{ + if [ $VERBOSE -eq 1 ]; then + echo $* + fi +} + +# check which java we are using and set up JAVA_VERSION +function check_java_version() +{ + JAVA_VERSION_STRING=$(java -version |& grep version | cut -d' ' -f3) + if [ "${JAVA_VERSION_STRING#\"1.8.}" != "${JAVA_VERSION_STRING}" ]; then + JAVA_VERSION=8 + elif [ "${JAVA_VERSION_STRING#\"9.}" != "${JAVA_VERSION_STRING}" ]; then + JAVA_VERSION=9 + elif [ "${JAVA_VERSION_STRING#\"11.}" != "${JAVA_VERSION_STRING}" ]; then + JAVA_VERSION=11 + elif [ "${JAVA_VERSION_STRING#\"14.}" != "${JAVA_VERSION_STRING}" ]; then + JAVA_VERSION=14 + else + echo "Unrecognized java version : $JAVA_VERSION_STRING" + exit 1 + fi +} + +# check for the required source trees and +# set up GRAAL_SRC_ROOT and GRAAL_JDK_SRC_ROOT +function check_source_dirs() +{ + if [ -z "$GRAAL_SRC_ROOT" ]; then + # see if we are in one of the Graal trees + if [ "${PWD#*/sdk}" != "$PWD" -o \ + "${PWD#*/compiler}" != "$PWD" -o \ + "${PWD#*/substratevm}" != "$PWD" -o \ + "${PWD#*/truffle}" != "$PWD" ]; then + GRAAL_SRC_ROOT=$(cd ..; pwd) + echo "defaulting GRAAL_SRC_ROOT to .. : $GRAAL_SRC_ROOT" + else + echo "Please set GRAAL_SRC_ROOT to git repo checkout dir" + exit 1 + fi + fi + + if [ -z "$GRAAL_JDK_SRC_ROOT" ]; then + if [ ! -z "$JAVA_HOME" ]; then + GRAAL_JDK_SRC_ROOT=$JAVA_HOME/src + echo "defaulting GRAAL_JDK_SRC_ROOT to JAVA_HOME : $GRAAL_JDK_SRC_ROOT" + else + JAVA_EXE=`which java` + GRAAL_JDK_SRC_ROOT=${JAVA_EXE%/bin/java}/src + fi + fi +} + +# add sources from a supplied graal source dir +function add_sources() +{ + # sanity check + if [ ! -d $1 ]; then + echo "hmm, was expecting a graal component directory, not this : $1" + fi + if [ ! -d $1/src ]; then + echo "hmm, was expecting to find a graal component source tree, not this : $1/src" + fi + root=$1 + for dir in $1/src/* + do + typeset -i ignore + ignore=0 + verbose "considering $dir" + if [ ! -d ${dir}/src ]; then + ignore=1 + else + # look for test or jdk in the trailing path + tail=${dir#$root} + if [ "${tail%*test}" != "$tail" -o \ + "${tail#*test}" != "$tail" ] ; then + # ignore test dirs + ignore=1 + elif [ "${tail#*jdk}" != "$tail" ]; then + # check for a specific jdk release + if [ "${tail#jdk.}" != "$tail" ]; then + # jdk. as part of a package name is ok + if ["${tail#jdk.}" != "$tail" ]; then + echo allow $dir + fi + ignore=0 + elif [ "${tail#*jdk}" != "${JAVA_VERSION}" ]; then + # jdk must match JAVA_VERSION + if [ "${dir#jdk.}" != "$dir" ]; then + echo disallow $dir + fi + ignore=1 + fi + fi + fi + if [ $ignore -eq 1 ] ; then + verbose "ignoring $dir" + else + verbose "including $dir" + SOURCEPATH=${SOURCEPATH}:$dir/src + fi + done +} + +# add sources from a supplied java source dir +function add_java_sources() +{ + # sanity check + if [ ! -d $1 ]; then + echo "hmm, was expecting to find a JDK source dir, not this : $1" + fi + SOURCEPATH=${SOURCEPATH}:$1 +} + +check_args $* + +check_java_version + +check_source_dirs + +GRAAL_SDK_SRC_ROOT=${GRAAL_SRC_ROOT}/sdk +GRAAL_COMPILER_SRC_ROOT=${GRAAL_SRC_ROOT}/compiler +GRAAL_SUBSTRATEVM_SRC_ROOT=${GRAAL_SRC_ROOT}/substratevm +GRAAL_TRUFFLE_SRC_ROOT=${GRAAL_SRC_ROOT}/truffle + +SOURCEPATH= + +if [ -d ${GRAAL_SDK_SRC_ROOT} ]; then + add_sources ${GRAAL_SDK_SRC_ROOT} +else + echo "Unable to find sdk sources in ${GRAAL_SDK_SRC_ROOT}" +fi +if [ -d ${GRAAL_COMPILER_SRC_ROOT} ]; then + add_sources ${GRAAL_COMPILER_SRC_ROOT} +else + echo "Unable to find compiler sources in ${GRAAL_COMPILER_SRC_ROOT}" +fi +if [ -d ${GRAAL_SUBSTRATEVM_SRC_ROOT} ]; then + add_sources ${GRAAL_SUBSTRATEVM_SRC_ROOT} +else + echo "Unable to find substratevm sources in ${GRAAL_SUBSTRATEVM_SRC_ROOT}" +fi +if [ -d ${GRAAL_TRUFFLE_SRC_ROOT} ]; then + add_sources ${GRAAL_TRUFFLE_SRC_ROOT} +else + echo "Unable to find truffle sources in ${GRAAL_TRUFFLE_SRC_ROOT}" +fi + +if [ -d ${GRAAL_JDK_SRC_ROOT} ]; then + add_java_sources ${GRAAL_JDK_SRC_ROOT} +else + echo "Unable to find JDK sources in ${GRAAL_JDK_SRC_ROOT}/src" + echo "unzip src.zip into \${GRAAL_JDK_SRC_ROOT}/src" +fi + +SOURCEPATH=${SOURCEPATH#:*} +echo "set directories $SOURCEPATH" > .gdbsourcepath +if [ $VERBOSE -gt 0 ]; then + cat .gdbsourcepath +fi From 84516d367c30a9493ceab8b96043512a45442536 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Thu, 6 Feb 2020 14:29:32 +0000 Subject: [PATCH 2/9] Add option GenerateDebugInfo= --- .../oracle/objectfile/elf/ELFObjectFile.java | 20 +- .../objectfile/elf/dwarf/ClassEntry.java | 8 +- .../oracle/objectfile/elf/dwarf/DirEntry.java | 9 +- .../elf/dwarf/DwarfARangesSectionImpl.java | 185 ++ .../elf/dwarf/DwarfAbbrevSectionImpl.java | 222 ++ .../elf/dwarf/DwarfFrameSectionImpl.java | 380 +++ .../dwarf/DwarfFrameSectionImplAArch64.java | 65 + .../dwarf/DwarfFrameSectionImplX86_64.java | 68 + .../elf/dwarf/DwarfInfoSectionImpl.java | 247 ++ .../elf/dwarf/DwarfLineSectionImpl.java | 893 +++++++ .../elf/dwarf/DwarfSectionImpl.java | 385 +++ .../objectfile/elf/dwarf/DwarfSections.java | 2260 ++--------------- .../elf/dwarf/DwarfStrSectionImpl.java | 107 + .../objectfile/elf/dwarf/FileEntry.java | 14 +- .../objectfile/elf/dwarf/PrimaryEntry.java | 86 +- .../oracle/objectfile/elf/dwarf/Range.java | 22 +- .../objectfile/elf/dwarf/StringEntry.java | 12 +- .../objectfile/elf/dwarf/StringTable.java | 20 +- .../com/oracle/svm/core/SubstrateOptions.java | 13 + .../svm/hosted/image/NativeBootImage.java | 2 +- 20 files changed, 2849 insertions(+), 2169 deletions(-) create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java index be10b81edd80..68b33d97287d 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java @@ -43,7 +43,13 @@ import com.oracle.objectfile.StringTable; import com.oracle.objectfile.SymbolTable; import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import com.oracle.objectfile.elf.dwarf.DwarfARangesSectionImpl; +import com.oracle.objectfile.elf.dwarf.DwarfAbbrevSectionImpl; +import com.oracle.objectfile.elf.dwarf.DwarfFrameSectionImpl; +import com.oracle.objectfile.elf.dwarf.DwarfInfoSectionImpl; +import com.oracle.objectfile.elf.dwarf.DwarfLineSectionImpl; import com.oracle.objectfile.elf.dwarf.DwarfSections; +import com.oracle.objectfile.elf.dwarf.DwarfStrSectionImpl; import com.oracle.objectfile.io.AssemblyBuffer; import com.oracle.objectfile.io.OutputAssembler; @@ -1161,14 +1167,14 @@ protected int getMinimumFileSize() { @Override public void installDebugInfo(DebugInfoProvider debugInfoProvider) { - DwarfSections dwarfSections = new DwarfSections(getMachine()); + DwarfSections dwarfSections = new DwarfSections(getMachine(), getByteOrder()); // we need an implementation for each section - DwarfSections.DwarfStrSectionImpl elfStrSectionImpl = dwarfSections.getStrSectionImpl(); - DwarfSections.DwarfAbbrevSectionImpl elfAbbrevSectionImpl = dwarfSections.getAbbrevSectionImpl(); - DwarfSections.DwarfFrameSectionImpl frameSectionImpl = dwarfSections.getFrameSectionImpl(); - DwarfSections.DwarfInfoSectionImpl elfInfoSectionImpl = dwarfSections.getInfoSectionImpl(); - DwarfSections.DwarfARangesSectionImpl elfARangesSectionImpl = dwarfSections.getARangesSectionImpl(); - DwarfSections.DwarfLineSectionImpl elfLineSectionImpl = dwarfSections.getLineSectionImpl(); + DwarfStrSectionImpl elfStrSectionImpl = dwarfSections.getStrSectionImpl(); + DwarfAbbrevSectionImpl elfAbbrevSectionImpl = dwarfSections.getAbbrevSectionImpl(); + DwarfFrameSectionImpl frameSectionImpl = dwarfSections.getFrameSectionImpl(); + DwarfInfoSectionImpl elfInfoSectionImpl = dwarfSections.getInfoSectionImpl(); + DwarfARangesSectionImpl elfARangesSectionImpl = dwarfSections.getARangesSectionImpl(); + DwarfLineSectionImpl elfLineSectionImpl = dwarfSections.getLineSectionImpl(); // now we can create the section elements with empty content newUserDefinedSection(elfStrSectionImpl.getSectionName(), elfStrSectionImpl); newUserDefinedSection(elfAbbrevSectionImpl.getSectionName(), elfAbbrevSectionImpl); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java index 9e429a565e49..0ff7290cb43f 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java @@ -129,10 +129,14 @@ PrimaryEntry addPrimary(Range primary, List frameSizeInfos void addSubRange(Range subrange, FileEntry subFileEntry) { Range primary = subrange.getPrimary(); - // the subrange should belong to a primary range + /* + * the subrange should belong to a primary range + */ assert primary != null; PrimaryEntry primaryEntry = primaryIndex.get(primary); - // we should already have seen the primary range + /* + * we should already have seen the primary range + */ assert primaryEntry != null; assert primaryEntry.getClassEntry() == this; primaryEntry.addSubRange(subrange, subFileEntry); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java index 5caac5a20997..dde46b4b8828 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java @@ -27,8 +27,15 @@ package com.oracle.objectfile.elf.dwarf; /** - * track the directory associated with one or + * Tracks the directory associated with one or * more source files. + * + * This is identified separately from each FileEntry + * idenityfing files that reside in the directory. + * That is necessary because the line info generator + * needs to collect and write out directory names + * into directory tables once only rather than once + * per file. */ public class DirEntry { private String path; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java new file mode 100644 index 000000000000..8ae11c436937 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.LayoutDecisionMap; +import com.oracle.objectfile.ObjectFile; + +import java.util.LinkedList; +import java.util.Map; + +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ARANGES_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_INFO_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_VERSION_2; +/** + * Section generator for debug_aranges section. + */ +public class DwarfARangesSectionImpl extends DwarfSectionImpl { + private static final int DW_AR_HEADER_SIZE = 12; + private static final int DW_AR_HEADER_PAD_SIZE = 4; // align up to 2 * address size + + public DwarfARangesSectionImpl(DwarfSections dwarfSections) { + super(dwarfSections); + } + + @Override + public String getSectionName() { + return DW_ARANGES_SECTION_NAME; + } + + @Override + public void createContent() { + int pos = 0; + /* + * we need an entry for each compilation unit + * + * uint32 length ............ in bytes (not counting these 4 bytes) + * uint16 dwarf_version ..... always 2 + * uint32 info_offset ....... offset of compilation unit on debug_info + * uint8 address_size ....... always 8 + * uint8 segment_desc_size .. ??? + * + * i.e. 12 bytes followed by padding + * aligning up to 2 * address size + * + * uint8 pad[4] + * + * followed by N + 1 times + * + * uint64 lo ................ lo address of range + * uint64 length ............ number of bytes in range + * + * where N is the number of ranges belonging to the compilation unit + * and the last range contains two zeroes + */ + + for (ClassEntry classEntry : getPrimaryClasses()) { + pos += DW_AR_HEADER_SIZE; + /* + * align to 2 * address size + */ + pos += DW_AR_HEADER_PAD_SIZE; + pos += classEntry.getPrimaryEntries().size() * 2 * 8; + pos += 2 * 8; + } + byte[] buffer = new byte[pos]; + super.setContent(buffer); + } + + @Override + public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { + ObjectFile.Element textElement = getElement().getOwner().elementForName(".text"); + LayoutDecisionMap decisionMap = alreadyDecided.get(textElement); + if (decisionMap != null) { + Object valueObj = decisionMap.getDecidedValue(LayoutDecision.Kind.VADDR); + if (valueObj != null && valueObj instanceof Number) { + /* + * this may not be the final vaddr for the text segment + * but it will be close enough to make debug easier + * i.e. to within a 4k page or two + */ + debugTextBase = ((Number) valueObj).longValue(); + } + } + return super.getOrDecideContent(alreadyDecided, contentHint); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + int size = buffer.length; + int pos = 0; + + checkDebug(pos); + + debug(" [0x%08x] DEBUG_ARANGES\n", pos); + for (ClassEntry classEntry : getPrimaryClasses()) { + int lastpos = pos; + int length = DW_AR_HEADER_SIZE + DW_AR_HEADER_PAD_SIZE - 4; + int cuIndex = classEntry.getCUIndex(); + LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + /* + * add room for each entry into length count + */ + length += classPrimaryEntries.size() * 2 * 8; + length += 2 * 8; + debug(" [0x%08x] %s CU %d length 0x%x\n", pos, classEntry.getFileName(), cuIndex, length); + pos = putInt(length, buffer, pos); + /* dwarf version is always 2 */ + pos = putShort(DW_VERSION_2, buffer, pos); + pos = putInt(cuIndex, buffer, pos); + /* address size is always 8 */ + pos = putByte((byte) 8, buffer, pos); + /* segment size is always 0 */ + pos = putByte((byte) 0, buffer, pos); + assert (pos - lastpos) == DW_AR_HEADER_SIZE; + /* + * align to 2 * address size + */ + for (int i = 0; i < DW_AR_HEADER_PAD_SIZE; i++) { + pos = putByte((byte) 0, buffer, pos); + } + debug(" [0x%08x] Address Length Name\n", pos); + for (PrimaryEntry classPrimaryEntry : classPrimaryEntries) { + Range primary = classPrimaryEntry.getPrimary(); + debug(" [0x%08x] %016x %016x %s\n", pos, debugTextBase + primary.getLo(), primary.getHi() - primary.getLo(), primary.getFullMethodName()); + pos = putRelocatableCodeOffset(primary.getLo(), buffer, pos); + pos = putLong(primary.getHi() - primary.getLo(), buffer, pos); + } + pos = putLong(0, buffer, pos); + pos = putLong(0, buffer, pos); + } + + assert pos == size; + } + + @Override + protected void debug(String format, Object... args) { + super.debug(format, args); + } + + /* + * debug_aranges section content depends on debug_info section content and offset + */ + public static final String TARGET_SECTION_NAME = DW_INFO_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java new file mode 100644 index 000000000000..c8f97729dd95 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +import com.oracle.objectfile.LayoutDecision; + +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_CODE_compile_unit; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_CODE_subprogram; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_external; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_hi_pc; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_language; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_low_pc; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_name; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_null; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_stmt_list; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CHILDREN_no; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CHILDREN_yes; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_addr; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_data1; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_data4; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_flag; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_null; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_strp; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FRAME_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_TAG_compile_unit; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_TAG_subprogram; + +/** + * Section generator for debug_abbrev section. + */ +public class DwarfAbbrevSectionImpl extends DwarfSectionImpl { + + public DwarfAbbrevSectionImpl(DwarfSections dwarfSections) { + super(dwarfSections); + } + + @Override + public String getSectionName() { + return DW_ABBREV_SECTION_NAME; + } + + @Override + public void createContent() { + int pos = 0; + /* + * an abbrev table contains abbrev entries for one or + * more CUs. the table includes a sequence of abbrev + * entries each of which defines a specific DIE layout + * employed to describe some DIE in a CU. a table is + * terminated by a null entry + * + * a null entry has consists of just a 0 abbrev code + * LEB128 abbrev_code; ...... == 0 + * + * non-null entries have the following format + * LEB128 abbrev_code; ...... unique noncode for this layout != 0 + * LEB128 tag; .............. defines the type of the DIE (class, subprogram, var etc) + * uint8 has_chldren; ....... is the DIE followed by child DIEs or a sibling DIE + * * ........ zero or more attributes + * .... terminator + * + * An attribute_spec consists of an attribute name and form + * LEB128 attr_name; ........ 0 for the null attribute name + * LEB128 attr_form; ........ 0 for the null attribute form + * + * For the moment we only use one abbrev table for all CUs. + * It contains two DIEs, the first to describe the compilation + * unit itself and the second to describe each method within + * that compilation unit. + * + * The DIE layouts are as follows: + * + * abbrev_code == 1, tag == DW_TAG_compilation_unit, has_children + * DW_AT_language : ... DW_FORM_data1 + * DW_AT_name : ....... DW_FORM_strp + * DW_AT_low_pc : ..... DW_FORM_address + * DW_AT_hi_pc : ...... DW_FORM_address + * DW_AT_stmt_list : .. DW_FORM_data4 + * + * abbrev_code == 2, tag == DW_TAG_subprogram, no_children + * DW_AT_name : ....... DW_FORM_strp + * DW_AT_low_pc : ..... DW_FORM_addr + * DW_AT_hi_pc : ...... DW_FORM_addr + * DW_AT_external : ... DW_FORM_flag + */ + + pos = writeAbbrev1(null, pos); + pos = writeAbbrev2(null, pos); + + byte[] buffer = new byte[pos]; + super.setContent(buffer); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + int size = buffer.length; + int pos = 0; + + checkDebug(pos); + + pos = writeAbbrev1(buffer, pos); + pos = writeAbbrev2(buffer, pos); + assert pos == size; + } + + public int writeAttrType(long code, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putSLEB(code, scratch, 0); + } else { + return putSLEB(code, buffer, pos); + } + } + + public int writeAttrForm(long code, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putSLEB(code, scratch, 0); + } else { + return putSLEB(code, buffer, pos); + } + } + + public int writeAbbrev1(byte[] buffer, int p) { + int pos = p; + /* + * abbrev 1 compile unit + */ + pos = writeAbbrevCode(DW_ABBREV_CODE_compile_unit, buffer, pos); + pos = writeTag(DW_TAG_compile_unit, buffer, pos); + pos = writeFlag(DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DW_AT_language, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_low_pc, buffer, pos); + pos = writeAttrForm(DW_FORM_addr, buffer, pos); + pos = writeAttrType(DW_AT_hi_pc, buffer, pos); + pos = writeAttrForm(DW_FORM_addr, buffer, pos); + pos = writeAttrType(DW_AT_stmt_list, buffer, pos); + pos = writeAttrForm(DW_FORM_data4, buffer, pos); + /* + * now terminate + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + public int writeAbbrev2(byte[] buffer, int p) { + int pos = p; + /* + * abbrev 2 compile unit + */ + pos = writeAbbrevCode(DW_ABBREV_CODE_subprogram, buffer, pos); + pos = writeTag(DW_TAG_subprogram, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_low_pc, buffer, pos); + pos = writeAttrForm(DW_FORM_addr, buffer, pos); + pos = writeAttrType(DW_AT_hi_pc, buffer, pos); + pos = writeAttrForm(DW_FORM_addr, buffer, pos); + pos = writeAttrType(DW_AT_external, buffer, pos); + pos = writeAttrForm(DW_FORM_flag, buffer, pos); + /* + * now terminate + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + @Override + protected void debug(String format, Object... args) { + super.debug(format, args); + } + + /** + * debug_abbrev section content depends on debug_frame section content and offset. + */ + public static final String TARGET_SECTION_NAME = DW_FRAME_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java new file mode 100644 index 000000000000..3923045bacd4 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; +import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.debuginfo.DebugInfoProvider; + +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_CIE_id; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_CIE_version; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_advance_loc; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_advance_loc1; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_advance_loc2; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_advance_loc4; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_def_cfa; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_def_cfa_offset; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_nop; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_offset; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_register; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FRAME_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_LINE_SECTION_NAME; +/** + * Section generic generator for debug_frame section. + */ +public abstract class DwarfFrameSectionImpl extends DwarfSectionImpl { + + public DwarfFrameSectionImpl(DwarfSections dwarfSections) { + super(dwarfSections); + } + + @Override + public String getSectionName() { + return DW_FRAME_SECTION_NAME; + } + + @Override + public void createContent() { + int pos = 0; + + /* + * the frame section contains one CIE at offset 0 + * followed by an FIE for each method + */ + pos = writeCIE(null, pos); + pos = writeMethodFrames(null, pos); + + byte[] buffer = new byte[pos]; + super.setContent(buffer); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + int size = buffer.length; + int pos = 0; + + checkDebug(pos); + + /* + * there are entries for the prologue region where the + * stack is being built, the method body region(s) where + * the code executes with a fixed size frame and the + * epilogue region(s) where the stack is torn down + */ + pos = writeCIE(buffer, pos); + pos = writeMethodFrames(buffer, pos); + + if (pos != size) { + System.out.format("pos = 0x%x size = 0x%x", pos, size); + } + assert pos == size; + } + + public int writeCIE(byte[] buffer, int p) { + /* + * we only need a vanilla CIE with default fields + * because we have to have at least one + * the layout is + * + * uint32 : length ............... length of remaining fields in this CIE + * uint32 : CIE_id ................ unique id for CIE == 0xffffff + * uint8 : version ................ == 1 + * uint8[] : augmentation ......... == "" so always 1 byte + * ULEB : code_alignment_factor ... == 1 (could use 4 for Aarch64) + * ULEB : data_alignment_factor ... == -8 + * byte : ret_addr reg id ......... x86_64 => 16 AArch64 => 32 + * byte[] : initial_instructions .. includes pad to 8-byte boundary + */ + int pos = p; + if (buffer == null) { + pos += putInt(0, scratch, 0); // don't care about length + pos += putInt(DW_CFA_CIE_id, scratch, 0); + pos += putByte(DW_CFA_CIE_version, scratch, 0); + pos += putAsciiStringBytes("", scratch, 0); + pos += putULEB(1, scratch, 0); + pos += putSLEB(-8, scratch, 0); + pos += putByte((byte) getPCIdx(), scratch, 0); + /* + * write insns to set up empty frame + */ + pos = writeInitialInstructions(buffer, pos); + /* + * pad to word alignment + */ + pos = writePaddingNops(8, buffer, pos); + /* + * no need to write length + */ + return pos; + } else { + int lengthPos = pos; + pos = putInt(0, buffer, pos); + pos = putInt(DW_CFA_CIE_id, buffer, pos); + pos = putByte(DW_CFA_CIE_version, buffer, pos); + pos = putAsciiStringBytes("", buffer, pos); + pos = putULEB(1, buffer, pos); + pos = putSLEB(-8, buffer, pos); + pos = putByte((byte) getPCIdx(), buffer, pos); + /* + * write insns to set up empty frame + */ + pos = writeInitialInstructions(buffer, pos); + /* + * pad to word alignment + */ + pos = writePaddingNops(8, buffer, pos); + patchLength(lengthPos, buffer, pos); + return pos; + } + } + + public int writeMethodFrames(byte[] buffer, int p) { + int pos = p; + for (ClassEntry classEntry : getPrimaryClasses()) { + for (PrimaryEntry primaryEntry : classEntry.getPrimaryEntries()) { + long lo = primaryEntry.getPrimary().getLo(); + long hi = primaryEntry.getPrimary().getHi(); + int frameSize = primaryEntry.getFrameSize(); + int currentOffset = 0; + int lengthPos = pos; + pos = writeFDEHeader((int) lo, (int) hi, buffer, pos); + for (DebugInfoProvider.DebugFrameSizeChange debugFrameSizeInfo : primaryEntry.getFrameSizeInfos()) { + int advance = debugFrameSizeInfo.getOffset() - currentOffset; + currentOffset += advance; + pos = writeAdvanceLoc(advance, buffer, pos); + if (debugFrameSizeInfo.getType() == DebugInfoProvider.DebugFrameSizeChange.Type.EXTEND) { + /* + * SP has been extended so rebase CFA using full frame + */ + pos = writeDefCFAOffset(frameSize, buffer, pos); + } else { + /* + * SP has been contracted so rebase CFA using empty frame + */ + pos = writeDefCFAOffset(8, buffer, pos); + } + } + pos = writePaddingNops(8, buffer, pos); + patchLength(lengthPos, buffer, pos); + } + } + return pos; + } + + public int writeFDEHeader(int lo, int hi, byte[] buffer, int p) { + /* + * we only need a vanilla FDE header with default fields + * the layout is + * + * uint32 : length ........... length of remaining fields in this FDE + * uint32 : CIE_offset ........ always 0 i.e. identifies our only CIE header + * uint64 : initial_location .. i.e. method lo address + * uint64 : address_range ..... i.e. method hi - lo + * byte[] : instructions ...... includes pad to 8-byte boundary + */ + + int pos = p; + if (buffer == null) { + /* dummy length */ + pos += putInt(0, scratch, 0); + /* CIE_offset */ + pos += putInt(0, scratch, 0); + /* initial address */ + pos += putLong(lo, scratch, 0); + /* address range */ + return pos + putLong(hi - lo, scratch, 0); + } else { + /* dummy length */ + pos = putInt(0, buffer, pos); + /* CIE_offset */ + pos = putInt(0, buffer, pos); + /* initial address */ + pos = putRelocatableCodeOffset(lo, buffer, pos); + /* address range */ + return putLong(hi - lo, buffer, pos); + } + } + + public int writePaddingNops(int alignment, byte[] buffer, int p) { + int pos = p; + assert (alignment & (alignment - 1)) == 0; + while ((pos & (alignment - 1)) != 0) { + if (buffer == null) { + pos++; + } else { + pos = putByte(DW_CFA_nop, buffer, pos); + } + } + return pos; + } + + public int writeDefCFA(int register, int offset, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + pos += putByte(DW_CFA_def_cfa, scratch, 0); + pos += putSLEB(register, scratch, 0); + return pos + putULEB(offset, scratch, 0); + } else { + pos = putByte(DW_CFA_def_cfa, buffer, pos); + pos = putULEB(register, buffer, pos); + return putULEB(offset, buffer, pos); + } + } + + public int writeDefCFAOffset(int offset, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + pos += putByte(DW_CFA_def_cfa_offset, scratch, 0); + return pos + putULEB(offset, scratch, 0); + } else { + pos = putByte(DW_CFA_def_cfa_offset, buffer, pos); + return putULEB(offset, buffer, pos); + } + } + + public int writeAdvanceLoc(int offset, byte[] buffer, int pos) { + if (offset <= 0x3f) { + return writeAdvanceLoc0((byte) offset, buffer, pos); + } else if (offset <= 0xff) { + return writeAdvanceLoc1((byte) offset, buffer, pos); + } else if (offset <= 0xffff) { + return writeAdvanceLoc2((short) offset, buffer, pos); + } else { + return writeAdvanceLoc4(offset, buffer, pos); + } + } + + public int writeAdvanceLoc0(byte offset, byte[] buffer, int pos) { + byte op = advanceLoc0Op(offset); + if (buffer == null) { + return pos + putByte(op, scratch, 0); + } else { + return putByte(op, buffer, pos); + } + } + + public int writeAdvanceLoc1(byte offset, byte[] buffer, int p) { + int pos = p; + byte op = DW_CFA_advance_loc1; + if (buffer == null) { + pos += putByte(op, scratch, 0); + return pos + putByte(offset, scratch, 0); + } else { + pos = putByte(op, buffer, pos); + return putByte(offset, buffer, pos); + } + } + + public int writeAdvanceLoc2(short offset, byte[] buffer, int p) { + byte op = DW_CFA_advance_loc2; + int pos = p; + if (buffer == null) { + pos += putByte(op, scratch, 0); + return pos + putShort(offset, scratch, 0); + } else { + pos = putByte(op, buffer, pos); + return putShort(offset, buffer, pos); + } + } + + public int writeAdvanceLoc4(int offset, byte[] buffer, int p) { + byte op = DW_CFA_advance_loc4; + int pos = p; + if (buffer == null) { + pos += putByte(op, scratch, 0); + return pos + putInt(offset, scratch, 0); + } else { + pos = putByte(op, buffer, pos); + return putInt(offset, buffer, pos); + } + } + + public int writeOffset(int register, int offset, byte[] buffer, int p) { + byte op = offsetOp(register); + int pos = p; + if (buffer == null) { + pos += putByte(op, scratch, 0); + return pos + putULEB(offset, scratch, 0); + } else { + pos = putByte(op, buffer, pos); + return putULEB(offset, buffer, pos); + } + } + + public int writeRegister(int savedReg, int savedToReg, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + pos += putByte(DW_CFA_register, scratch, 0); + pos += putULEB(savedReg, scratch, 0); + return pos + putULEB(savedToReg, scratch, 0); + } else { + pos = putByte(DW_CFA_register, buffer, pos); + pos = putULEB(savedReg, buffer, pos); + return putULEB(savedToReg, buffer, pos); + } + } + + public abstract int getPCIdx(); + + public abstract int getSPIdx(); + + public abstract int writeInitialInstructions(byte[] buffer, int pos); + + @Override + protected void debug(String format, Object... args) { + super.debug(format, args); + } + + /** + * debug_frame section content depends on debug_line section content and offset. + */ + public static final String TARGET_SECTION_NAME = DW_LINE_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } + + private static byte offsetOp(int register) { + assert (register >> 6) == 0; + return (byte) ((DW_CFA_offset << 6) | register); + } + + private static byte advanceLoc0Op(int offset) { + assert (offset >= 0 && offset <= 0x3f); + return (byte) ((DW_CFA_advance_loc << 6) | offset); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java new file mode 100644 index 000000000000..8f4ab1818ec4 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +/** + * AArch64-specific section generator for debug_frame section + * that knows details of AArch64 registers and frame layout. + */ +public class DwarfFrameSectionImplAArch64 extends DwarfFrameSectionImpl { + public static final int DW_CFA_FP_IDX = 29; + public static final int DW_CFA_LR_IDX = 30; + public static final int DW_CFA_SP_IDX = 31; + public static final int DW_CFA_PC_IDX = 32; + + public DwarfFrameSectionImplAArch64(DwarfSections dwarfSections) { + super(dwarfSections); + } + + @Override + public int getPCIdx() { + return DW_CFA_PC_IDX; + } + + @Override + public int getSPIdx() { + return DW_CFA_SP_IDX; + } + + @Override + public int writeInitialInstructions(byte[] buffer, int p) { + int pos = p; + /* + * rsp has not been updated + * caller pc is in lr + * register r32 (rpc), r30 (lr) + */ + pos = writeRegister(DW_CFA_PC_IDX, DW_CFA_LR_IDX, buffer, pos); + return pos; + } +} + diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java new file mode 100644 index 000000000000..7fabf34ad449 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +/** + * x86_64-specific section generator for debug_frame section + * that knows details of x86_64 registers and frame layout. + */ +public class DwarfFrameSectionImplX86_64 extends DwarfFrameSectionImpl { + public static final int DW_CFA_RSP_IDX = 7; + public static final int DW_CFA_RIP_IDX = 16; + + public DwarfFrameSectionImplX86_64(DwarfSections dwarfSections) { + super(dwarfSections); + } + + @Override + public int getPCIdx() { + return DW_CFA_RIP_IDX; + } + + @Override + public int getSPIdx() { + return DW_CFA_RSP_IDX; + } + + @Override + public int writeInitialInstructions(byte[] buffer, int p) { + int pos = p; + /* + * rsp points at the word containing the saved rip + * so the frame base (cfa) is at rsp + 8 (why not - ???) + * def_cfa r7 (sp) offset 8 + */ + pos = writeDefCFA(DW_CFA_RSP_IDX, 8, buffer, pos); + /* + * and rip is saved at offset 8 (coded as 1 which gets scaled by dataAlignment) from cfa + * (why not -1 ???) + * offset r16 (rip) cfa - 8 + */ + pos = writeOffset(DW_CFA_RIP_IDX, 1, buffer, pos); + return pos; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java new file mode 100644 index 000000000000..6a07284cb46a --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +import com.oracle.objectfile.LayoutDecision; + +import java.util.LinkedList; + +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_CODE_compile_unit; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_CODE_subprogram; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FLAG_true; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_INFO_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_LANG_Java; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_VERSION_2; +/** + * Section generator for debug_info section. + */ +public class DwarfInfoSectionImpl extends DwarfSectionImpl { + /** + * an info header section always contains a fixed number of bytes. + */ + private static final int DW_DIE_HEADER_SIZE = 11; + + public DwarfInfoSectionImpl(DwarfSections dwarfSections) { + super(dwarfSections); + } + + @Override + public String getSectionName() { + return DW_INFO_SECTION_NAME; + } + + @Override + public void createContent() { + /* + * we need a single level 0 DIE for each compilation unit (CU) + * Each CU's Level 0 DIE is preceded by a fixed header: + * and terminated by a null DIE + * uint32 length ......... excluding this length field + * uint16 dwarf_version .. always 2 ?? + * uint32 abbrev offset .. always 0 ?? + * uint8 address_size .... always 8 + * * ................ sequence of top-level and nested child entries + * ............ == 0 + * + * a DIE is a recursively defined structure + * it starts with a code for the associated + * abbrev entry followed by a series of attribute + * values as determined by the entry terminated by + * a null value and followed by zero or more child + * DIEs (zero iff has_children == no_children) + * + * LEB128 abbrev_code != 0 .. non-zero value indexes tag + attr layout of DIE + * * ....... value sequence as determined by abbrev entry + * * ................... sequence of child DIEs (if appropriate) + * ............. == 0 + * + * note that a null_DIE looks like + * LEB128 abbrev_code ....... == 0 + * i.e. it also looks like a null_value + */ + + byte[] buffer = null; + int pos = 0; + + for (ClassEntry classEntry : getPrimaryClasses()) { + int lengthPos = pos; + pos = writeCUHeader(buffer, pos); + assert pos == lengthPos + DW_DIE_HEADER_SIZE; + pos = writeCU(classEntry, buffer, pos); + /* + * no need to backpatch length at lengthPos + */ + } + buffer = new byte[pos]; + super.setContent(buffer); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + int size = buffer.length; + int pos = 0; + + checkDebug(pos); + + debug(" [0x%08x] DEBUG_INFO\n", pos); + debug(" [0x%08x] size = 0x%08x\n", pos, size); + for (ClassEntry classEntry : getPrimaryClasses()) { + /* + * save the offset of this file's CU so it can + * be used when writing the aranges section + */ + classEntry.setCUIndex(pos); + int lengthPos = pos; + pos = writeCUHeader(buffer, pos); + debug(" [0x%08x] Compilation Unit\n", pos, size); + assert pos == lengthPos + DW_DIE_HEADER_SIZE; + pos = writeCU(classEntry, buffer, pos); + /* + * backpatch length at lengthPos (excluding length field) + */ + patchLength(lengthPos, buffer, pos); + } + assert pos == size; + } + + public int writeCUHeader(byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + /* CU length */ + pos += putInt(0, scratch, 0); + /* dwarf version */ + pos += putShort(DW_VERSION_2, scratch, 0); + /* abbrev offset */ + pos += putInt(0, scratch, 0); + /* address size */ + return pos + putByte((byte) 8, scratch, 0); + } else { + /* CU length */ + pos = putInt(0, buffer, pos); + /* dwarf version */ + pos = putShort(DW_VERSION_2, buffer, pos); + /* abbrev offset */ + pos = putInt(0, buffer, pos); + /* address size */ + return putByte((byte) 8, buffer, pos); + } + } + + public int writeCU(ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + debug(" [0x%08x] <0> Abbrev Number %d\n", pos, DW_ABBREV_CODE_compile_unit); + pos = writeAbbrevCode(DW_ABBREV_CODE_compile_unit, buffer, pos); + debug(" [0x%08x] language %s\n", pos, "DW_LANG_Java"); + pos = writeAttrData1(DW_LANG_Java, buffer, pos); + debug(" [0x%08x] name 0x%x (%s)\n", pos, debugStringIndex(classEntry.getFileName()), classEntry.getFileName()); + pos = writeAttrStrp(classEntry.getFileName(), buffer, pos); + debug(" [0x%08x] low_pc 0x%08x\n", pos, classPrimaryEntries.getFirst().getPrimary().getLo()); + pos = writeAttrAddress(classPrimaryEntries.getFirst().getPrimary().getLo(), buffer, pos); + debug(" [0x%08x] hi_pc 0x%08x\n", pos, classPrimaryEntries.getLast().getPrimary().getHi()); + pos = writeAttrAddress(classPrimaryEntries.getLast().getPrimary().getHi(), buffer, pos); + debug(" [0x%08x] stmt_list 0x%08x\n", pos, classEntry.getLineIndex()); + pos = writeAttrData4(classEntry.getLineIndex(), buffer, pos); + for (PrimaryEntry primaryEntry : classPrimaryEntries) { + pos = writePrimary(primaryEntry, buffer, pos); + } + /* + * write a terminating null attribute for the the level 2 primaries + */ + return writeAttrNull(buffer, pos); + + } + + public int writePrimary(PrimaryEntry primaryEntry, byte[] buffer, int p) { + int pos = p; + Range primary = primaryEntry.getPrimary(); + debug(" [0x%08x] <1> Abbrev Number %d\n", pos, DW_ABBREV_CODE_subprogram); + pos = writeAbbrevCode(DW_ABBREV_CODE_subprogram, buffer, pos); + debug(" [0x%08x] name 0x%X (%s)\n", pos, debugStringIndex(primary.getFullMethodName()), primary.getFullMethodName()); + pos = writeAttrStrp(primary.getFullMethodName(), buffer, pos); + debug(" [0x%08x] low_pc 0x%08x\n", pos, primary.getLo()); + pos = writeAttrAddress(primary.getLo(), buffer, pos); + debug(" [0x%08x] high_pc 0x%08x\n", pos, primary.getHi()); + pos = writeAttrAddress(primary.getHi(), buffer, pos); + /* + * need to pass true only if method is public + */ + debug(" [0x%08x] external true\n", pos); + return writeFlag(DW_FLAG_true, buffer, pos); + } + + public int writeAttrStrp(String value, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + return pos + putInt(0, scratch, 0); + } else { + int idx = debugStringIndex(value); + return putInt(idx, buffer, pos); + } + } + + public int writeAttrString(String value, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + return pos + value.length() + 1; + } else { + return putAsciiStringBytes(value, buffer, pos); + } + } + + @Override + protected void debug(String format, Object... args) { + if (((int) args[0] - debugBase) < 0x100000) { + super.debug(format, args); + } else if (format.startsWith(" [0x%08x] primary file")) { + super.debug(format, args); + } + } + + /** + * debug_info section content depends on abbrev section content and offset. + */ + public static final String TARGET_SECTION_NAME = DW_ABBREV_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } +} + diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java new file mode 100644 index 000000000000..d19ce896362d --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java @@ -0,0 +1,893 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.LayoutDecisionMap; +import com.oracle.objectfile.ObjectFile; + +import java.util.Map; + +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_LINE_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_STR_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_VERSION_2; +/** + * Section generator for debug_line section. + */ +public class DwarfLineSectionImpl extends DwarfSectionImpl { + /** + * line header section always contains fixed number of bytes. + */ + private static final int DW_LN_HEADER_SIZE = 27; + /** + * current generator follows C++ with line base -5. + */ + private static final int DW_LN_LINE_BASE = -5; + /** + * current generator follows C++ with line range 14 + * giving full range -5 to 8. + */ + private static final int DW_LN_LINE_RANGE = 14; + /** + * current generator uses opcode base of 13 + * which must equal DW_LNS_define_file + 1. + */ + private static final int DW_LN_OPCODE_BASE = 13; + + /* + * standard opcodes defined by Dwarf 2 + */ + /* + * 0 can be returned to indicate an invalid opcode + */ + private static final byte DW_LNS_undefined = 0; + /* + * 0 can be inserted as a prefix for extended opcodes + */ + private static final byte DW_LNS_extended_prefix = 0; + /* + * append current state as matrix row 0 args + */ + private static final byte DW_LNS_copy = 1; + /* + * increment address 1 uleb arg + */ + private static final byte DW_LNS_advance_pc = 2; + /* + * increment line 1 sleb arg + */ + private static final byte DW_LNS_advance_line = 3; + /* + * set file 1 uleb arg + */ + private static final byte DW_LNS_set_file = 4; + /* + * set column 1 uleb arg + */ + private static final byte DW_LNS_set_column = 5; + /* + * flip is_stmt 0 args + */ + private static final byte DW_LNS_negate_stmt = 6; + /* + * set end sequence and copy row 0 args + */ + private static final byte DW_LNS_set_basic_block = 7; + /* + * increment address as per opcode 255 0 args + */ + private static final byte DW_LNS_const_add_pc = 8; + /* + * increment address 1 ushort arg + */ + private static final byte DW_LNS_fixed_advance_pc = 9; + + /* + * extended opcodes defined by Dwarf 2 + */ + /* + * there is no extended opcode 0 + */ + // private static final byte DW_LNE_undefined = 0; + /* + * end sequence of addresses + */ + private static final byte DW_LNE_end_sequence = 1; + /* + * set address as explicit long argument + */ + private static final byte DW_LNE_set_address = 2; + /* + * set file as explicit string argument + */ + private static final byte DW_LNE_define_file = 3; + + DwarfLineSectionImpl(DwarfSections dwarfSections) { + super(dwarfSections); + } + + @Override + public String getSectionName() { + return DW_LINE_SECTION_NAME; + } + + @Override + public void createContent() { + /* + * we need to create a header, dir table, file table and line + * number table encoding for each CU + */ + + /* + * write entries for each file listed in the primary list + */ + int pos = 0; + for (ClassEntry classEntry : getPrimaryClasses()) { + int startPos = pos; + classEntry.setLineIndex(startPos); + int headerSize = headerSize(); + int dirTableSize = computeDirTableSize(classEntry); + int fileTableSize = computeFileTableSize(classEntry); + int prologueSize = headerSize + dirTableSize + fileTableSize; + classEntry.setLinePrologueSize(prologueSize); + int lineNumberTableSize = computeLineNUmberTableSize(classEntry); + int totalSize = prologueSize + lineNumberTableSize; + classEntry.setTotalSize(totalSize); + pos += totalSize; + } + byte[] buffer = new byte[pos]; + super.setContent(buffer); + } + + public int headerSize() { + /* + * header size is standard 31 bytes + * uint32 total_length + * uint16 version + * uint32 prologue_length + * uint8 min_insn_length + * uint8 default_is_stmt + * int8 line_base + * uint8 line_range + * uint8 opcode_base + * uint8 li_opcode_base + * uint8[opcode_base-1] standard_opcode_lengths + */ + + return DW_LN_HEADER_SIZE; + } + + public int computeDirTableSize(ClassEntry classEntry) { + /* + * table contains a sequence of 'nul'-terminated + * dir name bytes followed by an extra 'nul' + * and then a sequence of 'nul'-terminated + * file name bytes followed by an extra 'nul' + * + * for now we assume dir and file names are ASCII + * byte strings + */ + int dirSize = 0; + for (DirEntry dir : classEntry.getLocalDirs()) { + dirSize += dir.getPath().length() + 1; + } + /* + * allow for separator nul + */ + dirSize++; + return dirSize; + } + + public int computeFileTableSize(ClassEntry classEntry) { + /* + * table contains a sequence of 'nul'-terminated + * dir name bytes followed by an extra 'nul' + * and then a sequence of 'nul'-terminated + * file name bytes followed by an extra 'nul' + + * for now we assume dir and file names are ASCII + * byte strings + */ + int fileSize = 0; + for (FileEntry localEntry : classEntry.getLocalFiles()) { + /* + * we want the file base name excluding path + */ + String baseName = localEntry.getBaseName(); + int length = baseName.length(); + fileSize += length + 1; + DirEntry dirEntry = localEntry.dirEntry; + int idx = classEntry.localDirsIdx(dirEntry); + fileSize += putULEB(idx, scratch, 0); + /* + * the two zero timestamps require 1 byte each + */ + fileSize += 2; + } + /* + * allow for terminator nul + */ + fileSize++; + return fileSize; + } + + public int computeLineNUmberTableSize(ClassEntry classEntry) { + /* + * sigh -- we have to do this by generating the + * content even though we cannot write it into a byte[] + */ + return writeLineNumberTable(classEntry, null, 0); + } + + @Override + public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { + ObjectFile.Element textElement = getElement().getOwner().elementForName(".text"); + LayoutDecisionMap decisionMap = alreadyDecided.get(textElement); + if (decisionMap != null) { + Object valueObj = decisionMap.getDecidedValue(LayoutDecision.Kind.VADDR); + if (valueObj != null && valueObj instanceof Number) { + /* + * this may not be the final vaddr for the text segment + * but it will be close enough to make debug easier + * i.e. to within a 4k page or two + */ + debugTextBase = ((Number) valueObj).longValue(); + } + } + return super.getOrDecideContent(alreadyDecided, contentHint); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + + int pos = 0; + checkDebug(pos); + debug(" [0x%08x] DEBUG_LINE\n", pos); + + for (ClassEntry classEntry : getPrimaryClasses()) { + int startPos = pos; + assert classEntry.getLineIndex() == startPos; + debug(" [0x%08x] Compile Unit for %s\n", pos, classEntry.getFileName()); + pos = writeHeader(classEntry, buffer, pos); + debug(" [0x%08x] headerSize = 0x%08x\n", pos, pos - startPos); + int dirTablePos = pos; + pos = writeDirTable(classEntry, buffer, pos); + debug(" [0x%08x] dirTableSize = 0x%08x\n", pos, pos - dirTablePos); + int fileTablePos = pos; + pos = writeFileTable(classEntry, buffer, pos); + debug(" [0x%08x] fileTableSize = 0x%08x\n", pos, pos - fileTablePos); + int lineNumberTablePos = pos; + pos = writeLineNumberTable(classEntry, buffer, pos); + debug(" [0x%08x] lineNumberTableSize = 0x%x\n", pos, pos - lineNumberTablePos); + debug(" [0x%08x] size = 0x%x\n", pos, pos - startPos); + } + assert pos == buffer.length; + } + + public int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + /* + * 4 ubyte length field + */ + pos = putInt(classEntry.getTotalSize() - 4, buffer, pos); + /* + * 2 ubyte version is always 2 + */ + pos = putShort(DW_VERSION_2, buffer, pos); + /* + * 4 ubyte prologue length includes rest of header and + * dir + file table section + */ + int prologueSize = classEntry.getLinePrologueSize() - 6; + pos = putInt(prologueSize, buffer, pos); + /* + * 1 ubyte min instruction length is always 1 + */ + pos = putByte((byte) 1, buffer, pos); + /* + * 1 byte default is_stmt is always 1 + */ + pos = putByte((byte) 1, buffer, pos); + /* + * 1 byte line base is always -5 + */ + pos = putByte((byte) DW_LN_LINE_BASE, buffer, pos); + /* + * 1 ubyte line range is always 14 giving range -5 to 8 + */ + pos = putByte((byte) DW_LN_LINE_RANGE, buffer, pos); + /* + * 1 ubyte opcode base is always 13 + */ + pos = putByte((byte) DW_LN_OPCODE_BASE, buffer, pos); + /* + * specify opcode arg sizes for the standard opcodes + */ + /* DW_LNS_copy */ + putByte((byte) 0, buffer, pos); + /* DW_LNS_advance_pc */ + putByte((byte) 1, buffer, pos + 1); + /* DW_LNS_advance_line */ + putByte((byte) 1, buffer, pos + 2); + /* DW_LNS_set_file */ + putByte((byte) 1, buffer, pos + 3); + /* DW_LNS_set_column */ + putByte((byte) 1, buffer, pos + 4); + /* DW_LNS_negate_stmt */ + putByte((byte) 0, buffer, pos + 5); + /* DW_LNS_set_basic_block */ + putByte((byte) 0, buffer, pos + 6); + /* DW_LNS_const_add_pc */ + putByte((byte) 0, buffer, pos + 7); + /* DW_LNS_fixed_advance_pc */ + putByte((byte) 1, buffer, pos + 8); + /* DW_LNS_end_sequence */ + putByte((byte) 0, buffer, pos + 9); + /* DW_LNS_set_address */ + putByte((byte) 0, buffer, pos + 10); + /* DW_LNS_define_file */ + pos = putByte((byte) 1, buffer, pos + 11); + return pos; + } + + public int writeDirTable(ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + debug(" [0x%08x] Dir Name\n", pos); + /* + * write out the list of dirs referenced form this file entry + */ + int dirIdx = 1; + for (DirEntry dir : classEntry.getLocalDirs()) { + /* + * write nul terminated string text. + */ + debug(" [0x%08x] %-4d %s\n", pos, dirIdx, dir.getPath()); + pos = putAsciiStringBytes(dir.getPath(), buffer, pos); + dirIdx++; + } + /* + * separate dirs from files with a nul + */ + pos = putByte((byte) 0, buffer, pos); + return pos; + } + + public int writeFileTable(ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + int fileIdx = 1; + debug(" [0x%08x] Entry Dir Name\n", pos); + for (FileEntry localEntry : classEntry.getLocalFiles()) { + /* + * we need the file name minus path, the associated dir index, and 0 for time stamps + */ + String baseName = localEntry.getBaseName(); + DirEntry dirEntry = localEntry.dirEntry; + int dirIdx = classEntry.localDirsIdx(dirEntry); + debug(" [0x%08x] %-5d %-5d %s\n", pos, fileIdx, dirIdx, baseName); + pos = putAsciiStringBytes(baseName, buffer, pos); + pos = putULEB(dirIdx, buffer, pos); + pos = putULEB(0, buffer, pos); + pos = putULEB(0, buffer, pos); + fileIdx++; + } + /* + * terminate files with a nul + */ + pos = putByte((byte) 0, buffer, pos); + return pos; + } + + public int debugLine = 1; + public int debugCopyCount = 0; + + public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + /* + * the primary file entry should always be first in the local files list + */ + assert classEntry.localFilesIdx(classEntry.getFileEntry()) == 1; + String primaryClassName = classEntry.getClassName(); + String primaryFileName = classEntry.getFileName(); + String file = primaryFileName; + int fileIdx = 1; + debug(" [0x%08x] primary class %s\n", pos, primaryClassName); + debug(" [0x%08x] primary file %s\n", pos, primaryFileName); + for (PrimaryEntry primaryEntry : classEntry.getPrimaryEntries()) { + Range primaryRange = primaryEntry.getPrimary(); + assert primaryRange.getFileName().equals(primaryFileName); + /* + * each primary represents a method i.e. a contiguous + * sequence of subranges. we assume the default state + * at the start of each sequence because we always post an + * end_sequence when we finish all the subranges in the method + */ + long line = primaryRange.getLine(); + if (line < 0 && primaryEntry.getSubranges().size() > 0) { + line = primaryEntry.getSubranges().get(0).getLine(); + } + if (line < 0) { + line = 0; + } + long address = primaryRange.getLo(); + + /* + * set state for primary + */ + debug(" [0x%08x] primary range [0x%08x, 0x%08x] %s:%d\n", pos, debugTextBase + primaryRange.getLo(), debugTextBase + primaryRange.getHi(), primaryRange.getFullMethodName(), + primaryRange.getLine()); + + /* + * initialize and write a row for the start of the primary method + */ + pos = putSetFile(file, fileIdx, buffer, pos); + pos = putSetBasicBlock(buffer, pos); + /* + * address is currently 0 + */ + pos = putSetAddress(address, buffer, pos); + /* + * state machine value of line is currently 1 + * increment to desired line + */ + if (line != 1) { + pos = putAdvanceLine(line - 1, buffer, pos); + } + pos = putCopy(buffer, pos); + + /* + * now write a row for each subrange lo and hi + */ + for (Range subrange : primaryEntry.getSubranges()) { + assert subrange.getLo() >= primaryRange.getLo(); + assert subrange.getHi() <= primaryRange.getHi(); + FileEntry subFileEntry = primaryEntry.getSubrangeFileEntry(subrange); + String subfile = subFileEntry.getFileName(); + int subFileIdx = classEntry.localFilesIdx(subFileEntry); + long subLine = subrange.getLine(); + long subAddressLo = subrange.getLo(); + long subAddressHi = subrange.getHi(); + debug(" [0x%08x] sub range [0x%08x, 0x%08x] %s:%d\n", pos, debugTextBase + subAddressLo, debugTextBase + subAddressHi, subrange.getFullMethodName(), subLine); + if (subLine < 0) { + /* + * no line info so stay at previous file:line + */ + subLine = line; + subfile = file; + subFileIdx = fileIdx; + debug(" [0x%08x] missing line info - staying put at %s:%d\n", pos, file, line); + } + /* + * there is a temptation to append end sequence at here + * when the hiAddress lies strictly between the current + * address and the start of the next subrange because, + * ostensibly, we have void space between the end of + * the current subrange and the start of the next one. + * however, debug works better if we treat all the insns up + * to the next range start as belonging to the current line + * + * if we have to update to a new file then do so + */ + if (subFileIdx != fileIdx) { + /* + * update the current file + */ + pos = putSetFile(subfile, subFileIdx, buffer, pos); + file = subfile; + fileIdx = subFileIdx; + } + /* + * check if we can advance line and/or address in + * one byte with a special opcode + */ + long lineDelta = subLine - line; + long addressDelta = subAddressLo - address; + byte opcode = isSpecialOpcode(addressDelta, lineDelta); + if (opcode != DW_LNS_undefined) { + /* + * ignore pointless write when addressDelta == lineDelta == 0 + */ + if (addressDelta != 0 || lineDelta != 0) { + pos = putSpecialOpcode(opcode, buffer, pos); + } + } else { + /* + * does it help to divide and conquer using + * a fixed address increment + */ + int remainder = isConstAddPC(addressDelta); + if (remainder > 0) { + pos = putConstAddPC(buffer, pos); + /* + * the remaining address can be handled with a + * special opcode but what about the line delta + */ + opcode = isSpecialOpcode(remainder, lineDelta); + if (opcode != DW_LNS_undefined) { + /* + * address remainder and line now fit + */ + pos = putSpecialOpcode(opcode, buffer, pos); + } else { + /* + * ok, bump the line separately then use a + * special opcode for the address remainder + */ + opcode = isSpecialOpcode(remainder, 0); + assert opcode != DW_LNS_undefined; + pos = putAdvanceLine(lineDelta, buffer, pos); + pos = putSpecialOpcode(opcode, buffer, pos); + } + } else { + /* + * increment line and pc separately + */ + if (lineDelta != 0) { + pos = putAdvanceLine(lineDelta, buffer, pos); + } + /* + * n.b. we might just have had an out of range line increment + * with a zero address increment + */ + if (addressDelta > 0) { + /* + * see if we can use a ushort for the increment + */ + if (isFixedAdvancePC(addressDelta)) { + pos = putFixedAdvancePC((short) addressDelta, buffer, pos); + } else { + pos = putAdvancePC(addressDelta, buffer, pos); + } + } + pos = putCopy(buffer, pos); + } + } + /* + * move line and address range on + */ + line += lineDelta; + address += addressDelta; + } + /* + * append a final end sequence just below the next primary range + */ + if (address < primaryRange.getHi()) { + long addressDelta = primaryRange.getHi() - address; + /* + * increment address before we write the end sequence + */ + pos = putAdvancePC(addressDelta, buffer, pos); + } + pos = putEndSequence(buffer, pos); + } + debug(" [0x%08x] primary file processed %s\n", pos, primaryFileName); + + return pos; + } + + @Override + protected void debug(String format, Object... args) { + if (((int) args[0] - debugBase) < 0x100000) { + super.debug(format, args); + } else if (format.startsWith(" [0x%08x] primary file")) { + super.debug(format, args); + } + } + + public int putCopy(byte[] buffer, int p) { + byte opcode = DW_LNS_copy; + int pos = p; + if (buffer == null) { + return pos + putByte(opcode, scratch, 0); + } else { + debugCopyCount++; + debug(" [0x%08x] Copy %d\n", pos, debugCopyCount); + return putByte(opcode, buffer, pos); + } + } + + public int putAdvancePC(long uleb, byte[] buffer, int p) { + byte opcode = DW_LNS_advance_pc; + int pos = p; + if (buffer == null) { + pos = pos + putByte(opcode, scratch, 0); + return pos + putULEB(uleb, scratch, 0); + } else { + debugAddress += uleb; + debug(" [0x%08x] Advance PC by %d to 0x%08x\n", pos, uleb, debugAddress); + pos = putByte(opcode, buffer, pos); + return putULEB(uleb, buffer, pos); + } + } + + public int putAdvanceLine(long sleb, byte[] buffer, int p) { + byte opcode = DW_LNS_advance_line; + int pos = p; + if (buffer == null) { + pos = pos + putByte(opcode, scratch, 0); + return pos + putSLEB(sleb, scratch, 0); + } else { + debugLine += sleb; + debug(" [0x%08x] Advance Line by %d to %d\n", pos, sleb, debugLine); + pos = putByte(opcode, buffer, pos); + return putSLEB(sleb, buffer, pos); + } + } + + public int putSetFile(String file, long uleb, byte[] buffer, int p) { + byte opcode = DW_LNS_set_file; + int pos = p; + if (buffer == null) { + pos = pos + putByte(opcode, scratch, 0); + return pos + putULEB(uleb, scratch, 0); + } else { + debug(" [0x%08x] Set File Name to entry %d in the File Name Table (%s)\n", pos, uleb, file); + pos = putByte(opcode, buffer, pos); + return putULEB(uleb, buffer, pos); + } + } + + public int putSetColumn(long uleb, byte[] buffer, int p) { + byte opcode = DW_LNS_set_column; + int pos = p; + if (buffer == null) { + pos = pos + putByte(opcode, scratch, 0); + return pos + putULEB(uleb, scratch, 0); + } else { + pos = putByte(opcode, buffer, pos); + return putULEB(uleb, buffer, pos); + } + } + + public int putNegateStmt(byte[] buffer, int p) { + byte opcode = DW_LNS_negate_stmt; + int pos = p; + if (buffer == null) { + return pos + putByte(opcode, scratch, 0); + } else { + return putByte(opcode, buffer, pos); + } + } + + public int putSetBasicBlock(byte[] buffer, int p) { + byte opcode = DW_LNS_set_basic_block; + int pos = p; + if (buffer == null) { + return pos + putByte(opcode, scratch, 0); + } else { + debug(" [0x%08x] Set basic block\n", pos); + return putByte(opcode, buffer, pos); + } + } + + public int putConstAddPC(byte[] buffer, int p) { + byte opcode = DW_LNS_const_add_pc; + int pos = p; + if (buffer == null) { + return pos + putByte(opcode, scratch, 0); + } else { + int advance = opcodeAddress((byte) 255); + debugAddress += advance; + debug(" [0x%08x] Advance PC by constant %d to 0x%08x\n", pos, advance, debugAddress); + return putByte(opcode, buffer, pos); + } + } + + public int putFixedAdvancePC(short arg, byte[] buffer, int p) { + byte opcode = DW_LNS_fixed_advance_pc; + int pos = p; + if (buffer == null) { + pos = pos + putByte(opcode, scratch, 0); + return pos + putShort(arg, scratch, 0); + } else { + debugAddress += arg; + debug(" [0x%08x] Fixed advance Address by %d to 0x%08x\n", pos, arg, debugAddress); + pos = putByte(opcode, buffer, pos); + return putShort(arg, buffer, pos); + } + } + + public int putEndSequence(byte[] buffer, int p) { + byte opcode = DW_LNE_end_sequence; + int pos = p; + if (buffer == null) { + pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); + /* + * insert extended insn byte count as ULEB + */ + pos = pos + putULEB(1, scratch, 0); + return pos + putByte(opcode, scratch, 0); + } else { + debug(" [0x%08x] Extended opcode 1: End sequence\n", pos); + debugAddress = debugTextBase; + debugLine = 1; + debugCopyCount = 0; + pos = putByte(DW_LNS_extended_prefix, buffer, pos); + /* + * insert extended insn byte count as ULEB + */ + pos = putULEB(1, buffer, pos); + return putByte(opcode, buffer, pos); + } + } + + public int putSetAddress(long arg, byte[] buffer, int p) { + byte opcode = DW_LNE_set_address; + int pos = p; + if (buffer == null) { + pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); + /* + * insert extended insn byte count as ULEB + */ + pos = pos + putULEB(9, scratch, 0); + pos = pos + putByte(opcode, scratch, 0); + return pos + putLong(arg, scratch, 0); + } else { + debugAddress = debugTextBase + (int) arg; + debug(" [0x%08x] Extended opcode 2: Set Address to 0x%08x\n", pos, debugAddress); + pos = putByte(DW_LNS_extended_prefix, buffer, pos); + /* + * insert extended insn byte count as ULEB + */ + pos = putULEB(9, buffer, pos); + pos = putByte(opcode, buffer, pos); + return putRelocatableCodeOffset(arg, buffer, pos); + } + } + + public int putDefineFile(String file, long uleb1, long uleb2, long uleb3, byte[] buffer, int p) { + byte opcode = DW_LNE_define_file; + int pos = p; + /* + * calculate bytes needed for opcode + args + */ + int fileBytes = file.length() + 1; + long insnBytes = 1; + insnBytes += fileBytes; + insnBytes += putULEB(uleb1, scratch, 0); + insnBytes += putULEB(uleb2, scratch, 0); + insnBytes += putULEB(uleb3, scratch, 0); + if (buffer == null) { + pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); + /* + * write insnBytes as a ULEB + */ + pos += putULEB(insnBytes, scratch, 0); + return pos + (int) insnBytes; + } else { + debug(" [0x%08x] Extended opcode 3: Define File %s idx %d ts1 %d ts2 %d\n", pos, file, uleb1, uleb2, uleb3); + pos = putByte(DW_LNS_extended_prefix, buffer, pos); + /* + * insert insn length as uleb + */ + pos = putULEB(insnBytes, buffer, pos); + /* + * insert opcode and args + */ + pos = putByte(opcode, buffer, pos); + pos = putAsciiStringBytes(file, buffer, pos); + pos = putULEB(uleb1, buffer, pos); + pos = putULEB(uleb2, buffer, pos); + return putULEB(uleb3, buffer, pos); + } + } + + public static int opcodeId(byte opcode) { + int iopcode = opcode & 0xff; + return iopcode - DW_LN_OPCODE_BASE; + } + + public static int opcodeAddress(byte opcode) { + int iopcode = opcode & 0xff; + return (iopcode - DW_LN_OPCODE_BASE) / DW_LN_LINE_RANGE; + } + + public static int opcodeLine(byte opcode) { + int iopcode = opcode & 0xff; + return ((iopcode - DW_LN_OPCODE_BASE) % DW_LN_LINE_RANGE) + DW_LN_LINE_BASE; + } + + public int putSpecialOpcode(byte opcode, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + return pos + putByte(opcode, scratch, 0); + } else { + if (debug && opcode == 0) { + debug(" [0x%08x] ERROR Special Opcode %d: Address 0x%08x Line %d\n", debugAddress, debugLine); + } + debugAddress += opcodeAddress(opcode); + debugLine += opcodeLine(opcode); + debug(" [0x%08x] Special Opcode %d: advance Address by %d to 0x%08x and Line by %d to %d\n", + pos, opcodeId(opcode), opcodeAddress(opcode), debugAddress, opcodeLine(opcode), debugLine); + return putByte(opcode, buffer, pos); + } + } + + private static final int MAX_ADDRESS_ONLY_DELTA = (0xff - DW_LN_OPCODE_BASE) / DW_LN_LINE_RANGE; + private static final int MAX_ADDPC_DELTA = MAX_ADDRESS_ONLY_DELTA + (MAX_ADDRESS_ONLY_DELTA - 1); + + public static byte isSpecialOpcode(long addressDelta, long lineDelta) { + if (addressDelta < 0) { + return DW_LNS_undefined; + } + if (lineDelta >= DW_LN_LINE_BASE) { + long offsetLineDelta = lineDelta - DW_LN_LINE_BASE; + if (offsetLineDelta < DW_LN_LINE_RANGE) { + /* + * line_delta can be encoded + * check if address is ok + */ + if (addressDelta <= MAX_ADDRESS_ONLY_DELTA) { + long opcode = DW_LN_OPCODE_BASE + (addressDelta * DW_LN_LINE_RANGE) + offsetLineDelta; + if (opcode <= 255) { + return (byte) opcode; + } + } + } + } + + /* + * answer no by returning an invalid opcode + */ + return DW_LNS_undefined; + } + + public static int isConstAddPC(long addressDelta) { + if (addressDelta < MAX_ADDRESS_ONLY_DELTA) { + return 0; + } + if (addressDelta <= MAX_ADDPC_DELTA) { + return (int) (addressDelta - MAX_ADDRESS_ONLY_DELTA); + } else { + return 0; + } + } + + public static boolean isFixedAdvancePC(long addressDiff) { + return addressDiff >= 0 && addressDiff < 0xffff; + } + + /** + * debug_line section content depends on debug_str section content and offset. + */ + public static final String TARGET_SECTION_NAME = DW_STR_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET, + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java new file mode 100644 index 000000000000..b508ef12f6a9 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +import com.oracle.objectfile.BasicProgbitsSectionImpl; +import com.oracle.objectfile.BuildDependency; +import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.LayoutDecisionMap; +import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.elf.ELFObjectFile; + +import java.nio.ByteOrder; +import java.util.Map; +import java.util.Set; + +import static com.oracle.objectfile.elf.dwarf.DwarfSections.TEXT_SECTION_NAME; + +/** + * class from which all DWARF debug sections + * inherit providing common behaviours. + */ +public abstract class DwarfSectionImpl extends BasicProgbitsSectionImpl { + protected DwarfSections dwarfSections; + public boolean debug = false; + public long debugTextBase = 0; + public long debugAddress = 0; + public int debugBase = 0; + + public DwarfSectionImpl(DwarfSections dwarfSections) { + this.dwarfSections = dwarfSections; + } + + /** + * creates the target byte[] array used to define the section + * contents. + * + * the main task of this method is to precompute the + * size of the debug section. given the complexity of the + * data layouts that invariably requires performing a dummy + * write of the contents, inserting bytes into a small, + * scratch buffer only when absolutely necessary. subclasses + * may also cache some information for use when writing the + * contents. + */ + public abstract void createContent(); + + /** + * populates the byte[] array used to contain the section + * contents. + * + * in most cases this task reruns the operations performed + * under createContent but this time actually writing data + * to the target byte[]. + */ + public abstract void writeContent(); + + @Override + public boolean isLoadable() { + /* + * even though we're a progbits section impl we're not actually loadable + */ + return false; + } + + public void checkDebug(int pos) { + /* + * if the env var relevant to this element + * type is set then switch on debugging + */ + String name = getSectionName(); + String envVarName = "DWARF_" + name.substring(1).toUpperCase(); + if (System.getenv(envVarName) != null) { + debug = true; + debugBase = pos; + debugAddress = debugTextBase; + } + } + + protected void debug(String format, Object... args) { + if (debug) { + System.out.format(format, args); + } + } + + protected boolean littleEndian() { + return dwarfSections.getByteOrder() == ByteOrder.LITTLE_ENDIAN; + } + + /* + * base level put methods that assume a non-null buffer + */ + + public int putByte(byte b, byte[] buffer, int p) { + int pos = p; + buffer[pos++] = b; + return pos; + } + + public int putShort(short s, byte[] buffer, int p) { + int pos = p; + if (littleEndian()) { + buffer[pos++] = (byte) (s & 0xff); + buffer[pos++] = (byte) ((s >> 8) & 0xff); + } else { + buffer[pos++] = (byte) ((s >> 8) & 0xff); + buffer[pos++] = (byte) (s & 0xff); + } + return pos; + } + + public int putInt(int i, byte[] buffer, int p) { + int pos = p; + if (littleEndian()) { + buffer[pos++] = (byte) (i & 0xff); + buffer[pos++] = (byte) ((i >> 8) & 0xff); + buffer[pos++] = (byte) ((i >> 16) & 0xff); + buffer[pos++] = (byte) ((i >> 24) & 0xff); + } else { + buffer[pos++] = (byte) ((i >> 24) & 0xff); + buffer[pos++] = (byte) ((i >> 16) & 0xff); + buffer[pos++] = (byte) ((i >> 8) & 0xff); + buffer[pos++] = (byte) (i & 0xff); + } + return pos; + } + + public int putLong(long l, byte[] buffer, int p) { + int pos = p; + if (littleEndian()) { + buffer[pos++] = (byte) (l & 0xff); + buffer[pos++] = (byte) ((l >> 8) & 0xff); + buffer[pos++] = (byte) ((l >> 16) & 0xff); + buffer[pos++] = (byte) ((l >> 24) & 0xff); + buffer[pos++] = (byte) ((l >> 32) & 0xff); + buffer[pos++] = (byte) ((l >> 40) & 0xff); + buffer[pos++] = (byte) ((l >> 48) & 0xff); + buffer[pos++] = (byte) ((l >> 56) & 0xff); + } else { + buffer[pos++] = (byte) ((l >> 56) & 0xff); + buffer[pos++] = (byte) ((l >> 48) & 0xff); + buffer[pos++] = (byte) ((l >> 40) & 0xff); + buffer[pos++] = (byte) ((l >> 32) & 0xff); + buffer[pos++] = (byte) ((l >> 16) & 0xff); + buffer[pos++] = (byte) ((l >> 24) & 0xff); + buffer[pos++] = (byte) ((l >> 8) & 0xff); + buffer[pos++] = (byte) (l & 0xff); + } + return pos; + } + + public int putRelocatableCodeOffset(long l, byte[] buffer, int p) { + int pos = p; + /* + * mark address so it is relocated relative to the start of the text segment + */ + markRelocationSite(pos, 8, ObjectFile.RelocationKind.DIRECT, TEXT_SECTION_NAME, false, Long.valueOf(l)); + pos = putLong(0, buffer, pos); + return pos; + } + + public int putULEB(long val, byte[] buffer, int p) { + long l = val; + int pos = p; + for (int i = 0; i < 9; i++) { + byte b = (byte) (l & 0x7f); + l = l >>> 7; + boolean done = (l == 0); + if (!done) { + b = (byte) (b | 0x80); + } + pos = putByte(b, buffer, pos); + if (done) { + break; + } + } + return pos; + } + + public int putSLEB(long val, byte[] buffer, int p) { + long l = val; + int pos = p; + for (int i = 0; i < 9; i++) { + byte b = (byte) (l & 0x7f); + l = l >> 7; + boolean bIsSigned = (b & 0x40) != 0; + boolean done = ((bIsSigned && l == -1) || (!bIsSigned && l == 0)); + if (!done) { + b = (byte) (b | 0x80); + } + pos = putByte(b, buffer, pos); + if (done) { + break; + } + } + return pos; + } + + public int putAsciiStringBytes(String s, byte[] buffer, int pos) { + return putAsciiStringBytes(s, 0, buffer, pos); + } + + public int putAsciiStringBytes(String s, int startChar, byte[] buffer, int p) { + int pos = p; + for (int l = startChar; l < s.length(); l++) { + char c = s.charAt(l); + if (c > 127) { + throw new RuntimeException("oops : expected ASCII string! " + s); + } + buffer[pos++] = (byte) c; + } + buffer[pos++] = '\0'; + return pos; + } + + /* + * common write methods that check for a null buffer + */ + + public void patchLength(int lengthPos, byte[] buffer, int pos) { + if (buffer != null) { + int length = pos - (lengthPos + 4); + putInt(length, buffer, lengthPos); + } + } + + public int writeAbbrevCode(long code, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putSLEB(code, scratch, 0); + } else { + return putSLEB(code, buffer, pos); + } + } + + public int writeTag(long code, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putSLEB(code, scratch, 0); + } else { + return putSLEB(code, buffer, pos); + } + } + + public int writeFlag(byte flag, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putByte(flag, scratch, 0); + } else { + return putByte(flag, buffer, pos); + } + } + + public int writeAttrAddress(long address, byte[] buffer, int pos) { + if (buffer == null) { + return pos + 8; + } else { + return putRelocatableCodeOffset(address, buffer, pos); + } + } + + public int writeAttrData8(long value, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putLong(value, scratch, 0); + } else { + return putLong(value, buffer, pos); + } + } + + public int writeAttrData4(int value, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putInt(value, scratch, 0); + } else { + return putInt(value, buffer, pos); + } + } + + public int writeAttrData1(byte value, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putByte(value, scratch, 0); + } else { + return putByte(value, buffer, pos); + } + } + + public int writeAttrNull(byte[] buffer, int pos) { + if (buffer == null) { + return pos + putSLEB(0, scratch, 0); + } else { + return putSLEB(0, buffer, pos); + } + } + + /** + * identify the section after which this debug section + * needs to be ordered when sizing and creating content. + * @return the name of the preceding section + */ + public abstract String targetSectionName(); + + /** + * identify the layout properties of the target section + * which need to have been decided before the contents + * of this section can be created. + * @return an array of the relevant decision kinds + */ + public abstract LayoutDecision.Kind[] targetSectionKinds(); + + /** + * identify this debug section by name. + * @return the name of the debug section + */ + public abstract String getSectionName(); + + @Override + public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { + /* + * ensure content byte[] has been created before calling super method + */ + createContent(); + + /* + * ensure content byte[] has been written before calling super method + */ + writeContent(); + + return super.getOrDecideContent(alreadyDecided, contentHint); + } + + @Override + public Set getDependencies(Map decisions) { + Set deps = super.getDependencies(decisions); + String targetName = targetSectionName(); + ELFObjectFile.ELFSection targetSection = (ELFObjectFile.ELFSection) getElement().getOwner().elementForName(targetName); + LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT); + LayoutDecision ourSize = decisions.get(getElement()).getDecision(LayoutDecision.Kind.SIZE); + LayoutDecision.Kind[] targetKinds = targetSectionKinds(); + /* + * make our content depend on the size and content of the target + */ + for (LayoutDecision.Kind targetKind : targetKinds) { + LayoutDecision targetDecision = decisions.get(targetSection).getDecision(targetKind); + deps.add(BuildDependency.createOrGet(ourContent, targetDecision)); + } + /* + * make our size depend on our content + */ + deps.add(BuildDependency.createOrGet(ourSize, ourContent)); + + return deps; + } + + /** + * a scratch buffer used during computation of a section's size. + */ + protected static final byte[] scratch = new byte[10]; + + protected Iterable getPrimaryClasses() { + return dwarfSections.getPrimaryClasses(); + } + + protected int debugStringIndex(String str) { + return dwarfSections.debugStringIndex(str); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java index 474e74d20735..afd996cda14d 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java @@ -26,11 +26,6 @@ package com.oracle.objectfile.elf.dwarf; -import com.oracle.objectfile.BasicProgbitsSectionImpl; -import com.oracle.objectfile.BuildDependency; -import com.oracle.objectfile.LayoutDecision; -import com.oracle.objectfile.LayoutDecisionMap; -import com.oracle.objectfile.ObjectFile; import com.oracle.objectfile.debuginfo.DebugInfoProvider; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfoProvider; @@ -38,27 +33,28 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLineInfo; // import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfoProvider; -import com.oracle.objectfile.ObjectFile.Element; import com.oracle.objectfile.elf.ELFMachine; -import com.oracle.objectfile.elf.ELFObjectFile.ELFSection; +import java.nio.ByteOrder; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; /** - * an outer class that models the debug info in an + * A class that models the debug info in an * organization that facilitates generation of the * required DWARF sections. It groups common data and * behaviours for use by the various subclasses of - * inner class DwarfSectionImpl that take responsibility + * class DwarfSectionImpl that take responsibility * for generating content for a specific section type. */ public class DwarfSections { - // names of the different ELF sections we create or reference - // in reverse dependency order + + /* + * names of the different ELF sections we create or reference + * in reverse dependency order + */ public static final String TEXT_SECTION_NAME = ".text"; public static final String DW_STR_SECTION_NAME = ".debug_str"; public static final String DW_LINE_SECTION_NAME = ".debug_line"; @@ -70,85 +66,113 @@ public class DwarfSections { /** * currently generated debug info relies on DWARF spec vesion 2. */ - private static final short DW_VERSION_2 = 2; + public static final short DW_VERSION_2 = 2; - // define all the abbrev section codes we need for our DIEs - // private static final int DW_ABBREV_CODE_null = 0; - private static final int DW_ABBREV_CODE_compile_unit = 1; - private static final int DW_ABBREV_CODE_subprogram = 2; + /* + * define all the abbrev section codes we need for our DIEs + */ + // public static final int DW_ABBREV_CODE_null = 0; + public static final int DW_ABBREV_CODE_compile_unit = 1; + public static final int DW_ABBREV_CODE_subprogram = 2; - // define all the Dwarf tags we need for our DIEs - private static final int DW_TAG_compile_unit = 0x11; - private static final int DW_TAG_subprogram = 0x2e; - // define all the Dwarf attributes we need for our DIEs - private static final int DW_AT_null = 0x0; - private static final int DW_AT_name = 0x3; - // private static final int DW_AT_comp_dir = 0x1b; - private static final int DW_AT_stmt_list = 0x10; - private static final int DW_AT_low_pc = 0x11; - private static final int DW_AT_hi_pc = 0x12; - private static final int DW_AT_language = 0x13; - private static final int DW_AT_external = 0x3f; - // private static final int DW_AT_return_addr = 0x2a; - // private static final int DW_AT_frame_base = 0x40; - // define all the Dwarf attribute forms we need for our DIEs - private static final int DW_FORM_null = 0x0; + /* + * define all the Dwarf tags we need for our DIEs + */ + public static final int DW_TAG_compile_unit = 0x11; + public static final int DW_TAG_subprogram = 0x2e; + /* + * define all the Dwarf attributes we need for our DIEs + */ + public static final int DW_AT_null = 0x0; + public static final int DW_AT_name = 0x3; + /* + * public static final int DW_AT_comp_dir = 0x1b; + */ + public static final int DW_AT_stmt_list = 0x10; + public static final int DW_AT_low_pc = 0x11; + public static final int DW_AT_hi_pc = 0x12; + public static final int DW_AT_language = 0x13; + public static final int DW_AT_external = 0x3f; + // public static final int DW_AT_return_addr = 0x2a; + // public static final int DW_AT_frame_base = 0x40; + /* + * define all the Dwarf attribute forms we need for our DIEs + */ + public static final int DW_FORM_null = 0x0; // private static final int DW_FORM_string = 0x8; - private static final int DW_FORM_strp = 0xe; // not currently used - private static final int DW_FORM_addr = 0x1; - private static final int DW_FORM_data1 = 0x0b; // use flag instead - private static final int DW_FORM_data4 = 0x6; - // private static final int DW_FORM_data8 = 0x7; - // private static final int DW_FORM_block1 = 0x0a; - private static final int DW_FORM_flag = 0xc; - - // define specific attribute values for given attribute or form types - // DIE header has_children attribute values - private static final byte DW_CHILDREN_no = 0; - private static final byte DW_CHILDREN_yes = 1; - // DW_FORM_flag attribute values - // private static final byte DW_FLAG_false = 0; - private static final byte DW_FLAG_true = 1; - // value for DW_AT_language attribute with form DATA1 - private static final byte DW_LANG_Java = 0xb; - // access not needed until we make functions members - // DW_AT_Accessibility attribute values - // private static final byte DW_ACCESS_public = 1; - // private static final byte DW_ACCESS_protected = 2; - // private static final byte DW_ACCESS_private = 3; - - // not yet needed - // private static final int DW_AT_type = 0; // only present for non-void functions - // private static final int DW_AT_accessibility = 0; - - // CIE and FDE entries + public static final int DW_FORM_strp = 0xe; + public static final int DW_FORM_addr = 0x1; + public static final int DW_FORM_data1 = 0x0b; + public static final int DW_FORM_data4 = 0x6; + // public static final int DW_FORM_data8 = 0x7; + // public static final int DW_FORM_block1 = 0x0a; + public static final int DW_FORM_flag = 0xc; + + /* + * define specific attribute values for given attribute or form types + */ + /* + * DIE header has_children attribute values + */ + public static final byte DW_CHILDREN_no = 0; + public static final byte DW_CHILDREN_yes = 1; + /* + * DW_FORM_flag attribute values + */ + // public static final byte DW_FLAG_false = 0; + public static final byte DW_FLAG_true = 1; + /* + * value for DW_AT_language attribute with form DATA1 + */ + public static final byte DW_LANG_Java = 0xb; - private static final int DW_CFA_CIE_id = -1; - // private static final int DW_CFA_FDE_id = 0; + /* + * DW_AT_Accessibility attribute values + * + * not needed until we make functions members + */ + // public static final byte DW_ACCESS_public = 1; + // public static final byte DW_ACCESS_protected = 2; + // public static final byte DW_ACCESS_private = 3; - private static final byte DW_CFA_CIE_version = 1; + /* + * others not yet needed + */ + // public static final int DW_AT_type = 0; // only present for non-void functions + // public static final int DW_AT_accessibility = 0; - // values for high 2 bits - private static final byte DW_CFA_advance_loc = 0x1; - private static final byte DW_CFA_offset = 0x2; - // private static final byte DW_CFA_restore = 0x3; + /* + * CIE and FDE entries + */ - // values for low 6 bits - private static final byte DW_CFA_nop = 0x0; - // private static final byte DW_CFA_set_loc1 = 0x1; - private static final byte DW_CFA_advance_loc1 = 0x2; - private static final byte DW_CFA_advance_loc2 = 0x3; - private static final byte DW_CFA_advance_loc4 = 0x4; - // private static final byte DW_CFA_offset_extended = 0x5; - // private static final byte DW_CFA_restore_extended = 0x6; - // private static final byte DW_CFA_undefined = 0x7; - // private static final byte DW_CFA_same_value = 0x8; - private static final byte DW_CFA_register = 0x9; - private static final byte DW_CFA_def_cfa = 0xc; - // private static final byte DW_CFA_def_cfa_register = 0xd; - private static final byte DW_CFA_def_cfa_offset = 0xe; + /* full byte/word values */ + public static final int DW_CFA_CIE_id = -1; + // public static final int DW_CFA_FDE_id = 0; + + public static final byte DW_CFA_CIE_version = 1; + + /* values encoded in high 2 bits */ + public static final byte DW_CFA_advance_loc = 0x1; + public static final byte DW_CFA_offset = 0x2; + // public static final byte DW_CFA_restore = 0x3; + + /* values encoded in low 6 bits */ + public static final byte DW_CFA_nop = 0x0; + // public static final byte DW_CFA_set_loc1 = 0x1; + public static final byte DW_CFA_advance_loc1 = 0x2; + public static final byte DW_CFA_advance_loc2 = 0x3; + public static final byte DW_CFA_advance_loc4 = 0x4; + // public static final byte DW_CFA_offset_extended = 0x5; + // public static final byte DW_CFA_restore_extended = 0x6; + // public static final byte DW_CFA_undefined = 0x7; + // public static final byte DW_CFA_same_value = 0x8; + public static final byte DW_CFA_register = 0x9; + public static final byte DW_CFA_def_cfa = 0xc; + // public static final byte DW_CFA_def_cfa_register = 0xd; + public static final byte DW_CFA_def_cfa_offset = 0xe; private ELFMachine elfMachine; + private ByteOrder byteOrder; private DwarfStrSectionImpl dwarfStrSection; private DwarfAbbrevSectionImpl dwarfAbbrevSection; private DwarfInfoSectionImpl dwarfInfoSection; @@ -156,16 +180,19 @@ public class DwarfSections { private DwarfLineSectionImpl dwarfLineSection; private DwarfFrameSectionImpl dwarfFameSection; - public DwarfSections(ELFMachine elfMachine) { + public DwarfSections(ELFMachine elfMachine, ByteOrder byteOrder) { this.elfMachine = elfMachine; - dwarfStrSection = new DwarfStrSectionImpl(); - dwarfAbbrevSection = new DwarfAbbrevSectionImpl(); - dwarfInfoSection = new DwarfInfoSectionImpl(); - dwarfARangesSection = new DwarfARangesSectionImpl(); - dwarfLineSection = new DwarfLineSectionImpl(); - dwarfFameSection = (elfMachine == ELFMachine.AArch64 - ? new DwarfFrameSectionImplAArch64() - : new DwarfFrameSectionImplX86_64()); + this.byteOrder = byteOrder; + dwarfStrSection = new DwarfStrSectionImpl(this); + dwarfAbbrevSection = new DwarfAbbrevSectionImpl(this); + dwarfInfoSection = new DwarfInfoSectionImpl(this); + dwarfARangesSection = new DwarfARangesSectionImpl(this); + dwarfLineSection = new DwarfLineSectionImpl(this); + if (elfMachine == ELFMachine.AArch64) { + dwarfFameSection = new DwarfFrameSectionImplAArch64(this); + } else { + dwarfFameSection = new DwarfFrameSectionImplX86_64(this); + } } public DwarfStrSectionImpl getStrSectionImpl() { @@ -192,15 +219,6 @@ public DwarfLineSectionImpl getLineSectionImpl() { return dwarfLineSection; } - public ELFMachine getElfMachine() { - return elfMachine; - } - - /** - * a scratch buffer used during computation of a section's size. - */ - protected static final byte[] scratch = new byte[10]; - /** * a table listing all known strings, some of * which may be marked for insertion into the @@ -218,31 +236,33 @@ public ELFMachine getElfMachine() { */ private Map 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. + /* + * 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. @@ -293,7 +313,7 @@ public String uniqueDebugString(String string) { * @return the offset of the string in the .debug_str * section */ - private int debugStringIndex(String string) { + public int debugStringIndex(String string) { return stringTable.debugStringIndex(string); } @@ -304,17 +324,23 @@ private int debugStringIndex(String string) { * ObjectFile client */ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { - // DebugTypeInfoProvider typeInfoProvider = debugInfoProvider.typeInfoProvider(); - // for (DebugTypeInfo debugTypeInfo : typeInfoProvider) { - // install types - // } + /* + * DebugTypeInfoProvider typeInfoProvider = debugInfoProvider.typeInfoProvider(); + * for (DebugTypeInfo debugTypeInfo : typeInfoProvider) { + * install types + * } + */ - // ensure we have a null string in the string section + /* + * ensure we have a null string in the string section + */ uniqueDebugString(""); DebugCodeInfoProvider codeInfoProvider = debugInfoProvider.codeInfoProvider(); for (DebugCodeInfo debugCodeInfo : codeInfoProvider) { - // primary file name and full method name need to be written to the debug_str section + /* + * primary file name and full method name need to be written to the debug_str section + */ String fileName = debugCodeInfo.fileName(); String className = debugCodeInfo.className(); String methodName = debugCodeInfo.methodName(); @@ -324,9 +350,11 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { int hi = debugCodeInfo.addressHi(); int primaryLine = debugCodeInfo.line(); Range primaryRange = new Range(fileName, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, primaryLine); - // System.out.format("arange: [0x%08x,0x%08x) %s %s::%s(%s) %s\n", lo, hi, - // returnTypeName, className, methodName, paramNames, fileName); - // create an infoSection entry for the method + /* + * System.out.format("arange: [0x%08x,0x%08x) %s %s::%s(%s) %s\n", lo, hi, + * returnTypeName, className, methodName, paramNames, fileName); + * create an infoSection entry for the method + */ addRange(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); for (DebugLineInfo debugLineInfo : debugCodeInfo.lineInfoProvider()) { String fileNameAtLine = debugLineInfo.fileName(); @@ -335,25 +363,33 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { int loAtLine = lo + debugLineInfo.addressLo(); int hiAtLine = lo + debugLineInfo.addressHi(); int line = debugLineInfo.line(); - // record all subranges even if they have no line or file so we at least get a - // symbol for them + /* + * 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, classNameAtLine, methodNameAtLine, "", "", stringTable, loAtLine, hiAtLine, line, primaryRange); addSubRange(primaryRange, subRange); } } - // DebugDataInfoProvider dataInfoProvider = debugInfoProvider.dataInfoProvider(); - // for (DebugDataInfo debugDataInfo : dataInfoProvider) { - // install details of heap elements - // String name = debugDataInfo.toString(); - // } + /* + * DebugDataInfoProvider dataInfoProvider = debugInfoProvider.dataInfoProvider(); + * for (DebugDataInfo debugDataInfo : dataInfoProvider) { + * install details of heap elements + * String name = debugDataInfo.toString(); + * } + */ } public ClassEntry ensureClassEntry(Range range) { String className = range.getClassName(); - // see if we already have an entry + /* + * 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 + /* + * create and index the entry associating it with the right file + */ FileEntry fileEntry = ensureFileEntry(range); classEntry = new ClassEntry(className, fileEntry); primaryClasses.add(classEntry); @@ -365,7 +401,9 @@ public ClassEntry ensureClassEntry(Range range) { public FileEntry ensureFileEntry(Range range) { String fileName = range.getFileName(); - // ensure we have an entry + /* + * ensure we have an entry + */ FileEntry fileEntry = filesIndex.get(fileName); if (fileEntry == null) { DirEntry dirEntry = ensureDirEntry(fileName); @@ -375,7 +413,9 @@ public FileEntry ensureFileEntry(Range range) { dirEntry); files.add(fileEntry); filesIndex.put(fileName, fileEntry); - // if this is a primary entry then add it to the primary list + /* + * if this is a primary entry then add it to the primary list + */ if (range.isPrimary()) { primaryFiles.add(fileEntry); } else { @@ -399,16 +439,20 @@ public void addSubRange(Range primaryRange, Range subrange) { String className = primaryRange.getClassName(); ClassEntry classEntry = primaryClassesIndex.get(className); FileEntry subrangeEntry = ensureFileEntry(subrange); - // the primary range should already have been seen - // and associated with a primary class entry + /* + * the primary range should already have been seen + * and associated with a primary class entry + */ assert classEntry.primaryIndexFor(primaryRange) != null; classEntry.addSubRange(subrange, subrangeEntry); } - private DirEntry ensureDirEntry(String file) { + public DirEntry ensureDirEntry(String file) { int pathLength = file.lastIndexOf('/'); if (pathLength < 0) { - // no path/package means use dir entry 0 + /* + * no path/package means use dir entry 0 + */ return null; } String filePath = file.substring(0, pathLength); @@ -420,1917 +464,13 @@ private DirEntry ensureDirEntry(String file) { } return dirEntry; } - - /** - * class from which all DWARF debug section - * inherit providing common behaviours. - */ - // shared implementation methods to manage content creation - public abstract class DwarfSectionImpl extends BasicProgbitsSectionImpl { - public boolean debug = false; - public long debugTextBase = 0; - public long debugAddress = 0; - public int debugBase = 0; - - public DwarfSectionImpl() { - } - - /** - * creates the target byte[] array used to define the section - * contents. - * - * the main task of this method is to precompute the - * size of the debug section. given the complexity of the - * data layouts that invariably requires performing a dummy - * write of the contents, inserting bytes into a small, - * scratch buffer only when absolutely necessary. subclasses - * may also cache some information for use when writing the - * contents. - */ - public abstract void createContent(); - - /** - * populates the byte[] array used to contain the section - * contents. - * - * in most cases this task reruns the operations performed - * under createContent but this time actually writing data - * to the target byte[]. - */ - public abstract void writeContent(); - - @Override - public boolean isLoadable() { - // even though we're a progbits section impl we're not actually loadable - return false; - } - - public void checkDebug(int pos) { - // if the env var relevant to this element - // type is set then switch on debugging - String name = getSectionName(); - String envVarName = "DWARF_" + name.substring(1).toUpperCase(); - if (System.getenv(envVarName) != null) { - debug = true; - debugBase = pos; - debugAddress = debugTextBase; - } - } - - protected void debug(String format, Object... args) { - if (debug) { - System.out.format(format, args); - } - } - - // base level put methods that assume a non-null buffer - - public int putByte(byte b, byte[] buffer, int p) { - int pos = p; - buffer[pos++] = b; - return pos; - } - - public int putShort(short s, byte[] buffer, int p) { - int pos = p; - buffer[pos++] = (byte) (s & 0xff); - buffer[pos++] = (byte) ((s >> 8) & 0xff); - return pos; - } - - public int putInt(int i, byte[] buffer, int p) { - int pos = p; - buffer[pos++] = (byte) (i & 0xff); - buffer[pos++] = (byte) ((i >> 8) & 0xff); - buffer[pos++] = (byte) ((i >> 16) & 0xff); - buffer[pos++] = (byte) ((i >> 24) & 0xff); - return pos; - } - - public int putLong(long l, byte[] buffer, int p) { - int pos = p; - buffer[pos++] = (byte) (l & 0xff); - buffer[pos++] = (byte) ((l >> 8) & 0xff); - buffer[pos++] = (byte) ((l >> 16) & 0xff); - buffer[pos++] = (byte) ((l >> 24) & 0xff); - buffer[pos++] = (byte) ((l >> 32) & 0xff); - buffer[pos++] = (byte) ((l >> 40) & 0xff); - buffer[pos++] = (byte) ((l >> 48) & 0xff); - buffer[pos++] = (byte) ((l >> 56) & 0xff); - return pos; - } - - public int putRelocatableCodeOffset(long l, byte[] buffer, int p) { - int pos = p; - // mark address so it is relocated relative to the start of the text segment - markRelocationSite(pos, 8, ObjectFile.RelocationKind.DIRECT, TEXT_SECTION_NAME, false, Long.valueOf(l)); - pos = putLong(0, buffer, pos); - return pos; - } - - public int putULEB(long val, byte[] buffer, int p) { - long l = val; - int pos = p; - for (int i = 0; i < 9; i++) { - byte b = (byte) (l & 0x7f); - l = l >>> 7; - boolean done = (l == 0); - if (!done) { - b = (byte) (b | 0x80); - } - pos = putByte(b, buffer, pos); - if (done) { - break; - } - } - return pos; - } - - public int putSLEB(long val, byte[] buffer, int p) { - long l = val; - int pos = p; - for (int i = 0; i < 9; i++) { - byte b = (byte) (l & 0x7f); - l = l >> 7; - boolean bIsSigned = (b & 0x40) != 0; - boolean done = ((bIsSigned && l == -1) || (!bIsSigned && l == 0)); - if (!done) { - b = (byte) (b | 0x80); - } - pos = putByte(b, buffer, pos); - if (done) { - break; - } - } - return pos; - } - - public int putAsciiStringBytes(String s, byte[] buffer, int pos) { - return putAsciiStringBytes(s, 0, buffer, pos); - } - - public int putAsciiStringBytes(String s, int startChar, byte[] buffer, int p) { - int pos = p; - for (int l = startChar; l < s.length(); l++) { - char c = s.charAt(l); - if (c > 127) { - throw new RuntimeException("oops : expected ASCII string! " + s); - } - buffer[pos++] = (byte) c; - } - buffer[pos++] = '\0'; - return pos; - } - - // common write methods that check for a null buffer - - public void patchLength(int lengthPos, byte[] buffer, int pos) { - if (buffer != null) { - int length = pos - (lengthPos + 4); - putInt(length, buffer, lengthPos); - } - } - - public int writeAbbrevCode(long code, byte[] buffer, int pos) { - if (buffer == null) { - return pos + putSLEB(code, scratch, 0); - } else { - return putSLEB(code, buffer, pos); - } - } - - public int writeTag(long code, byte[] buffer, int pos) { - if (buffer == null) { - return pos + putSLEB(code, scratch, 0); - } else { - return putSLEB(code, buffer, pos); - } - } - - public int writeFlag(byte flag, byte[] buffer, int pos) { - if (buffer == null) { - return pos + putByte(flag, scratch, 0); - } else { - return putByte(flag, buffer, pos); - } - } - - public int writeAttrAddress(long address, byte[] buffer, int pos) { - if (buffer == null) { - return pos + 8; - } else { - return putRelocatableCodeOffset(address, buffer, pos); - } - } - - public int writeAttrData8(long value, byte[] buffer, int pos) { - if (buffer == null) { - return pos + putLong(value, scratch, 0); - } else { - return putLong(value, buffer, pos); - } - } - - public int writeAttrData4(int value, byte[] buffer, int pos) { - if (buffer == null) { - return pos + putInt(value, scratch, 0); - } else { - return putInt(value, buffer, pos); - } - } - - public int writeAttrData1(byte value, byte[] buffer, int pos) { - if (buffer == null) { - return pos + putByte(value, scratch, 0); - } else { - return putByte(value, buffer, pos); - } - } - - public int writeAttrNull(byte[] buffer, int pos) { - if (buffer == null) { - return pos + putSLEB(0, scratch, 0); - } else { - return putSLEB(0, buffer, pos); - } - } - - /** - * identify the section after which this debug section - * needs to be ordered when sizing and creating content. - * @return the name of the preceding section - */ - public abstract String targetSectionName(); - - /** - * identify the layout properties of the target section - * which need to have been decided before the contents - * of this section can be created. - * @return an array of the relevant decision kinds - */ - public abstract LayoutDecision.Kind[] targetSectionKinds(); - - /** - * identify this debug section by name. - * @return the name of the debug section - */ - public abstract String getSectionName(); - - @Override - public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { - // ensure content byte[] has been created before calling super method - createContent(); - - // ensure content byte[] has been written before calling super method - writeContent(); - - return super.getOrDecideContent(alreadyDecided, contentHint); - } - - @Override - public Set getDependencies(Map decisions) { - Set deps = super.getDependencies(decisions); - String targetName = targetSectionName(); - ELFSection targetSection = (ELFSection) getElement().getOwner().elementForName(targetName); - LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT); - LayoutDecision ourSize = decisions.get(getElement()).getDecision(LayoutDecision.Kind.SIZE); - LayoutDecision.Kind[] targetKinds = targetSectionKinds(); - // make our content depend on the size and content of the target - for (LayoutDecision.Kind targetKind : targetKinds) { - LayoutDecision targetDecision = decisions.get(targetSection).getDecision(targetKind); - deps.add(BuildDependency.createOrGet(ourContent, targetDecision)); - } - // make our size depend on our content - deps.add(BuildDependency.createOrGet(ourSize, ourContent)); - - return deps; - } - } - - /** - * generator for debug_str section. - */ - public class DwarfStrSectionImpl extends DwarfSectionImpl { - public DwarfStrSectionImpl() { - super(); - } - - @Override - public String getSectionName() { - return DW_STR_SECTION_NAME; - } - - @Override - public void createContent() { - int pos = 0; - for (StringEntry stringEntry : stringTable) { - if (stringEntry.isAddToStrSection()) { - stringEntry.setOffset(pos); - String string = stringEntry.getString(); - pos += string.length() + 1; - } - } - byte[] buffer = new byte[pos]; - super.setContent(buffer); - } - - @Override - public void writeContent() { - byte[] buffer = getContent(); - int size = buffer.length; - int pos = 0; - - checkDebug(pos); - - for (StringEntry stringEntry : stringTable) { - if (stringEntry.isAddToStrSection()) { - assert stringEntry.getOffset() == pos; - String string = stringEntry.getString(); - pos = putAsciiStringBytes(string, buffer, pos); - } - } - assert pos == size; - } - - @Override - protected void debug(String format, Object... args) { - super.debug(format, args); - } - - /** - * debug_str section content depends on text section content and offset. - */ - public static final String TARGET_SECTION_NAME = TEXT_SECTION_NAME; - - @Override - public String targetSectionName() { - return TARGET_SECTION_NAME; - } - - /** - * debug_str section content depends on text section content and offset. - */ - public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET, - LayoutDecision.Kind.VADDR, // add this so we can use the base address - }; - - @Override - public LayoutDecision.Kind[] targetSectionKinds() { - return targetSectionKinds; - } - } - - /** - * generator for debug_abbrev section. - */ - public class DwarfAbbrevSectionImpl extends DwarfSectionImpl { - - public DwarfAbbrevSectionImpl() { - super(); - } - - @Override - public String getSectionName() { - return DW_ABBREV_SECTION_NAME; - } - - @Override - public void createContent() { - int pos = 0; - // an abbrev table contains abbrev entries for one or - // more CUs. the table includes a sequence of abbrev - // entries each of which defines a specific DIE layout - // employed to describe some DIE in a CU. a table is - // terminated by a null entry - // - // a null entry has consists of just a 0 abbrev code - // LEB128 abbrev_code; ...... == 0 - // - // non-null entries have the following format - // LEB128 abbrev_code; ...... unique noncode for this layout != 0 - // LEB128 tag; .............. defines the type of the DIE (class, subprogram, var etc) - // uint8 has_chldren; ....... is the DIE followed by child DIEs or a sibling DIE - // * ........ zero or more attributes - // .... terminator - // - // An attribute_spec consists of an attribute name and form - // LEB128 attr_name; ........ 0 for the null attribute name - // LEB128 attr_form; ........ 0 for the null attribute form - // - // For the moment we only use one abbrev table for all CUs. - // It contains two DIEs, the first to describe the compilation - // unit itself and the second to describe each method within - // that compilation unit. - // - // The DIE layouts are as follows: - // - // abbrev_code == 1, tag == DW_TAG_compilation_unit, has_children - // DW_AT_language : ... DW_FORM_data1 - // DW_AT_name : ....... DW_FORM_strp - // DW_AT_low_pc : ..... DW_FORM_address - // DW_AT_hi_pc : ...... DW_FORM_address - // DW_AT_stmt_list : .. DW_FORM_data4 - // - // abbrev_code == 2, tag == DW_TAG_subprogram, no_children - // DW_AT_name : ....... DW_FORM_strp - // DW_AT_low_pc : ..... DW_FORM_addr - // DW_AT_hi_pc : ...... DW_FORM_addr - // DW_AT_external : ... DW_FORM_flag - - pos = writeAbbrev1(null, pos); - pos = writeAbbrev2(null, pos); - - byte[] buffer = new byte[pos]; - super.setContent(buffer); - } - - @Override - public void writeContent() { - byte[] buffer = getContent(); - int size = buffer.length; - int pos = 0; - - checkDebug(pos); - - pos = writeAbbrev1(buffer, pos); - pos = writeAbbrev2(buffer, pos); - assert pos == size; - } - - public int writeAttrType(long code, byte[] buffer, int pos) { - if (buffer == null) { - return pos + putSLEB(code, scratch, 0); - } else { - return putSLEB(code, buffer, pos); - } - } - - public int writeAttrForm(long code, byte[] buffer, int pos) { - if (buffer == null) { - return pos + putSLEB(code, scratch, 0); - } else { - return putSLEB(code, buffer, pos); - } - } - - public int writeAbbrev1(byte[] buffer, int p) { - int pos = p; - // abbrev 1 compile unit - pos = writeAbbrevCode(DW_ABBREV_CODE_compile_unit, buffer, pos); - pos = writeTag(DW_TAG_compile_unit, buffer, pos); - pos = writeFlag(DW_CHILDREN_yes, buffer, pos); - pos = writeAttrType(DW_AT_language, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_low_pc, buffer, pos); - pos = writeAttrForm(DW_FORM_addr, buffer, pos); - pos = writeAttrType(DW_AT_hi_pc, buffer, pos); - pos = writeAttrForm(DW_FORM_addr, buffer, pos); - pos = writeAttrType(DW_AT_stmt_list, buffer, pos); - pos = writeAttrForm(DW_FORM_data4, buffer, pos); - // now terminate - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); - return pos; - } - - public int writeAbbrev2(byte[] buffer, int p) { - int pos = p; - // abbrev 2 compile unit - pos = writeAbbrevCode(DW_ABBREV_CODE_subprogram, buffer, pos); - pos = writeTag(DW_TAG_subprogram, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_low_pc, buffer, pos); - pos = writeAttrForm(DW_FORM_addr, buffer, pos); - pos = writeAttrType(DW_AT_hi_pc, buffer, pos); - pos = writeAttrForm(DW_FORM_addr, buffer, pos); - pos = writeAttrType(DW_AT_external, buffer, pos); - pos = writeAttrForm(DW_FORM_flag, buffer, pos); - // now terminate - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); - return pos; - } - - @Override - protected void debug(String format, Object... args) { - super.debug(format, args); - } - - /** - * debug_abbrev section content depends on debug_frame section content and offset. - */ - public static final String TARGET_SECTION_NAME = DW_FRAME_SECTION_NAME; - - @Override - public String targetSectionName() { - return TARGET_SECTION_NAME; - } - - public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET - }; - - @Override - public LayoutDecision.Kind[] targetSectionKinds() { - return targetSectionKinds; - } - } - - /** - * generic generator for debug_frame section. - */ - public abstract class DwarfFrameSectionImpl extends DwarfSectionImpl { - - public DwarfFrameSectionImpl() { - super(); - } - - @Override - public String getSectionName() { - return DW_FRAME_SECTION_NAME; - } - - @Override - public void createContent() { - int pos = 0; - - // the frame section contains one CIE at offset 0 - // followed by an FIE for each method - pos = writeCIE(null, pos); - pos = writeMethodFrames(null, pos); - - byte[] buffer = new byte[pos]; - super.setContent(buffer); - } - - @Override - public void writeContent() { - byte[] buffer = getContent(); - int size = buffer.length; - int pos = 0; - - checkDebug(pos); - - // there are entries for the prologue region where the - // stack is being built, the method body region(s) where - // the code executes with a fixed size frame and the - // epilogue region(s) where the stack is torn down - pos = writeCIE(buffer, pos); - pos = writeMethodFrames(buffer, pos); - - if (pos != size) { - System.out.format("pos = 0x%x size = 0x%x", pos, size); - } - assert pos == size; - } - - public int writeCIE(byte[] buffer, int p) { - // we only need a vanilla CIE with default fields - // because we have to have at least one - // the layout is - // - // uint32 : length ............... length of remaining fields in this CIE - // uint32 : CIE_id ................ unique id for CIE == 0xffffff - // uint8 : version ................ == 1 - // uint8[] : augmentation ......... == "" so always 1 byte - // ULEB : code_alignment_factor ... == 1 (could use 4 for Aarch64) - // ULEB : data_alignment_factor ... == -8 - // byte : ret_addr reg id ......... x86_64 => 16 AArch64 => 32 - // byte[] : initial_instructions .. includes pad to 8-byte boundary - int pos = p; - if (buffer == null) { - pos += putInt(0, scratch, 0); // don't care about length - pos += putInt(DW_CFA_CIE_id, scratch, 0); - pos += putByte(DW_CFA_CIE_version, scratch, 0); - pos += putAsciiStringBytes("", scratch, 0); - pos += putULEB(1, scratch, 0); - pos += putSLEB(-8, scratch, 0); - pos += putByte((byte) getPCIdx(), scratch, 0); - // write insns to set up empty frame - pos = writeInitialInstructions(buffer, pos); - // pad to word alignment - pos = writePaddingNops(8, buffer, pos); - // no need to write length - return pos; - } else { - int lengthPos = pos; - pos = putInt(0, buffer, pos); - pos = putInt(DW_CFA_CIE_id, buffer, pos); - pos = putByte(DW_CFA_CIE_version, buffer, pos); - pos = putAsciiStringBytes("", buffer, pos); - pos = putULEB(1, buffer, pos); - pos = putSLEB(-8, buffer, pos); - pos = putByte((byte) getPCIdx(), buffer, pos); - // write insns to set up empty frame - pos = writeInitialInstructions(buffer, pos); - // pad to word alignment - pos = writePaddingNops(8, buffer, pos); - patchLength(lengthPos, buffer, pos); - return pos; - } - } - - public int writeMethodFrames(byte[] buffer, int p) { - int pos = p; - for (ClassEntry classEntry : primaryClasses) { - for (PrimaryEntry primaryEntry : classEntry.getPrimaryEntries()) { - long lo = primaryEntry.getPrimary().getLo(); - long hi = primaryEntry.getPrimary().getHi(); - int frameSize = primaryEntry.getFrameSize(); - int currentOffset = 0; - int lengthPos = pos; - pos = writeFDEHeader((int) lo, (int) hi, buffer, pos); - for (DebugFrameSizeChange debugFrameSizeInfo : primaryEntry.getFrameSizeInfos()) { - int advance = debugFrameSizeInfo.getOffset() - currentOffset; - currentOffset += advance; - pos = writeAdvanceLoc(advance, buffer, pos); - if (debugFrameSizeInfo.getType() == DebugFrameSizeChange.Type.EXTEND) { - // SP has been extended so rebase CFA using full frame - pos = writeDefCFAOffset(frameSize, buffer, pos); - } else { - // SP has been contracted so rebase CFA using empty frame - pos = writeDefCFAOffset(8, buffer, pos); - } - } - pos = writePaddingNops(8, buffer, pos); - patchLength(lengthPos, buffer, pos); - } - } - return pos; - } - - public int writeFDEHeader(int lo, int hi, byte[] buffer, int p) { - // we only need a vanilla FDE header with default fields - // the layout is - // - // uint32 : length ........... length of remaining fields in this FDE - // uint32 : CIE_offset ........ always 0 i.e. identifies our only CIE header - // uint64 : initial_location .. i.e. method lo address - // uint64 : address_range ..... i.e. method hi - lo - // byte[] : instructions ...... includes pad to 8-byte boundary - - int pos = p; - if (buffer == null) { - pos += putInt(0, scratch, 0); // dummy length - pos += putInt(0, scratch, 0); // CIE_offset - pos += putLong(lo, scratch, 0); // initial address - return pos + putLong(hi - lo, scratch, 0); // address range - } else { - pos = putInt(0, buffer, pos); // dummy length - pos = putInt(0, buffer, pos); // CIE_offset - pos = putRelocatableCodeOffset(lo, buffer, pos); // initial address - return putLong(hi - lo, buffer, pos); // address range - } - } - - public int writePaddingNops(int alignment, byte[] buffer, int p) { - int pos = p; - assert (alignment & (alignment - 1)) == 0; - while ((pos & (alignment - 1)) != 0) { - if (buffer == null) { - pos++; - } else { - pos = putByte(DW_CFA_nop, buffer, pos); - } - } - return pos; - } - - public int writeDefCFA(int register, int offset, byte[] buffer, int p) { - int pos = p; - if (buffer == null) { - pos += putByte(DW_CFA_def_cfa, scratch, 0); - pos += putSLEB(register, scratch, 0); - return pos + putULEB(offset, scratch, 0); - } else { - pos = putByte(DW_CFA_def_cfa, buffer, pos); - pos = putULEB(register, buffer, pos); - return putULEB(offset, buffer, pos); - } - } - - public int writeDefCFAOffset(int offset, byte[] buffer, int p) { - int pos = p; - if (buffer == null) { - pos += putByte(DW_CFA_def_cfa_offset, scratch, 0); - return pos + putULEB(offset, scratch, 0); - } else { - pos = putByte(DW_CFA_def_cfa_offset, buffer, pos); - return putULEB(offset, buffer, pos); - } - } - - public int writeAdvanceLoc(int offset, byte[] buffer, int pos) { - if (offset <= 0x3f) { - return writeAdvanceLoc0((byte) offset, buffer, pos); - } else if (offset <= 0xff) { - return writeAdvanceLoc1((byte) offset, buffer, pos); - } else if (offset <= 0xffff) { - return writeAdvanceLoc2((short) offset, buffer, pos); - } else { - return writeAdvanceLoc4(offset, buffer, pos); - } - } - - public int writeAdvanceLoc0(byte offset, byte[] buffer, int pos) { - byte op = advanceLoc0Op(offset); - if (buffer == null) { - return pos + putByte(op, scratch, 0); - } else { - return putByte(op, buffer, pos); - } - } - - public int writeAdvanceLoc1(byte offset, byte[] buffer, int p) { - int pos = p; - byte op = DW_CFA_advance_loc1; - if (buffer == null) { - pos += putByte(op, scratch, 0); - return pos + putByte(offset, scratch, 0); - } else { - pos = putByte(op, buffer, pos); - return putByte(offset, buffer, pos); - } - } - - public int writeAdvanceLoc2(short offset, byte[] buffer, int p) { - byte op = DW_CFA_advance_loc2; - int pos = p; - if (buffer == null) { - pos += putByte(op, scratch, 0); - return pos + putShort(offset, scratch, 0); - } else { - pos = putByte(op, buffer, pos); - return putShort(offset, buffer, pos); - } - } - - public int writeAdvanceLoc4(int offset, byte[] buffer, int p) { - byte op = DW_CFA_advance_loc4; - int pos = p; - if (buffer == null) { - pos += putByte(op, scratch, 0); - return pos + putInt(offset, scratch, 0); - } else { - pos = putByte(op, buffer, pos); - return putInt(offset, buffer, pos); - } - } - - public int writeOffset(int register, int offset, byte[] buffer, int p) { - byte op = offsetOp(register); - int pos = p; - if (buffer == null) { - pos += putByte(op, scratch, 0); - return pos + putULEB(offset, scratch, 0); - } else { - pos = putByte(op, buffer, pos); - return putULEB(offset, buffer, pos); - } - } - - public int writeRegister(int savedReg, int savedToReg, byte[] buffer, int p) { - int pos = p; - if (buffer == null) { - pos += putByte(DW_CFA_register, scratch, 0); - pos += putULEB(savedReg, scratch, 0); - return pos + putULEB(savedToReg, scratch, 0); - } else { - pos = putByte(DW_CFA_register, buffer, pos); - pos = putULEB(savedReg, buffer, pos); - return putULEB(savedToReg, buffer, pos); - } - } - - public abstract int getPCIdx(); - - public abstract int getSPIdx(); - - public abstract int writeInitialInstructions(byte[] buffer, int pos); - - @Override - protected void debug(String format, Object... args) { - super.debug(format, args); - } - - /** - * debug_frame section content depends on debug_line section content and offset. - */ - public static final String TARGET_SECTION_NAME = DW_LINE_SECTION_NAME; - - @Override - public String targetSectionName() { - return TARGET_SECTION_NAME; - } - - public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET - }; - - @Override - public LayoutDecision.Kind[] targetSectionKinds() { - return targetSectionKinds; - } - - private byte offsetOp(int register) { - assert (register >> 6) == 0; - return (byte) ((DW_CFA_offset << 6) | register); - } - - private byte advanceLoc0Op(int offset) { - assert (offset >= 0 && offset <= 0x3f); - return (byte) ((DW_CFA_advance_loc << 6) | offset); - } - } - - /** - * x86_64-specific generator for debug_frame section - * that knows details of x86_64 registers and frame layout. - */ - public class DwarfFrameSectionImplX86_64 extends DwarfFrameSectionImpl { - public static final int DW_CFA_RSP_IDX = 7; - public static final int DW_CFA_RIP_IDX = 16; - - public DwarfFrameSectionImplX86_64() { - super(); - } - - @Override - public int getPCIdx() { - return DW_CFA_RIP_IDX; - } - - @Override - public int getSPIdx() { - return DW_CFA_RSP_IDX; - } - - @Override - public int writeInitialInstructions(byte[] buffer, int p) { - int pos = p; - // rsp points at the word containing the saved rip - // so the frame base (cfa) is at rsp + 8 (why not - ???) - // def_cfa r7 (sp) offset 8 - pos = writeDefCFA(DW_CFA_RSP_IDX, 8, buffer, pos); - // and rip is saved at offset 8 (coded as 1 which gets scaled by dataAlignment) from cfa - // (why not -1 ???) - // offset r16 (rip) cfa - 8 - pos = writeOffset(DW_CFA_RIP_IDX, 1, buffer, pos); - return pos; - } - } - - /** - * AArch64-specific generator for debug_frame section - * that knows details of AArch64 registers and frame layout. - */ - public class DwarfFrameSectionImplAArch64 extends DwarfFrameSectionImpl { - public static final int DW_CFA_FP_IDX = 29; - public static final int DW_CFA_LR_IDX = 30; - public static final int DW_CFA_SP_IDX = 31; - public static final int DW_CFA_PC_IDX = 32; - - public DwarfFrameSectionImplAArch64() { - super(); - } - - @Override - public int getPCIdx() { - return DW_CFA_PC_IDX; - } - - @Override - public int getSPIdx() { - return DW_CFA_SP_IDX; - } - - @Override - public int writeInitialInstructions(byte[] buffer, int p) { - int pos = p; - // rsp has not been updated - // caller pc is in lr - // register r32 (rpc), r30 (lr) - pos = writeRegister(DW_CFA_PC_IDX, DW_CFA_LR_IDX, buffer, pos); - return pos; - } - } - - /** - * generator for debug_info section. - */ - public class DwarfInfoSectionImpl extends DwarfSectionImpl { - /** - * an info header section always contains a fixed number of bytes. - */ - private static final int DW_DIE_HEADER_SIZE = 11; - - public DwarfInfoSectionImpl() { - super(); - } - - @Override - public String getSectionName() { - return DW_INFO_SECTION_NAME; - } - - @Override - public void createContent() { - // we need a single level 0 DIE for each compilation unit (CU) - // Each CU's Level 0 DIE is preceded by a fixed header: - // and terminated by a null DIE - // uint32 length ......... excluding this length field - // uint16 dwarf_version .. always 2 ?? - // uint32 abbrev offset .. always 0 ?? - // uint8 address_size .... always 8 - // * ................ sequence of top-level and nested child entries - // ............ == 0 - // - // a DIE is a recursively defined structure - // it starts with a code for the associated - // abbrev entry followed by a series of attribute - // values as determined by the entry terminated by - // a null value and followed by zero or more child - // DIEs (zero iff has_children == no_children) - // - // LEB128 abbrev_code != 0 .. non-zero value indexes tag + attr layout of DIE - // * ....... value sequence as determined by abbrev entry - // * ................... sequence of child DIEs (if appropriate) - // ............. == 0 - // - // note that a null_DIE looks like - // LEB128 abbrev_code ....... == 0 - // i.e. it also looks like a null_value - - byte[] buffer = null; - int pos = 0; - - for (ClassEntry classEntry : primaryClasses) { - int lengthPos = pos; - pos = writeCUHeader(buffer, pos); - assert pos == lengthPos + DW_DIE_HEADER_SIZE; - pos = writeCU(classEntry, buffer, pos); - // no need to backpatch length at lengthPos - } - buffer = new byte[pos]; - super.setContent(buffer); - } - - @Override - public void writeContent() { - byte[] buffer = getContent(); - int size = buffer.length; - int pos = 0; - - checkDebug(pos); - - debug(" [0x%08x] DEBUG_INFO\n", pos); - debug(" [0x%08x] size = 0x%08x\n", pos, size); - for (ClassEntry classEntry : primaryClasses) { - // save the offset of this file's CU so it can - // be used when writing the aranges section - classEntry.setCUIndex(pos); - int lengthPos = pos; - pos = writeCUHeader(buffer, pos); - debug(" [0x%08x] Compilation Unit\n", pos, size); - assert pos == lengthPos + DW_DIE_HEADER_SIZE; - pos = writeCU(classEntry, buffer, pos); - // backpatch length at lengthPos (excluding length field) - patchLength(lengthPos, buffer, pos); - } - assert pos == size; - } - - public int writeCUHeader(byte[] buffer, int p) { - int pos = p; - if (buffer == null) { - pos += putInt(0, scratch, 0); // CU length - pos += putShort(DW_VERSION_2, scratch, 0); // dwarf version - pos += putInt(0, scratch, 0); // abbrev offset - return pos + putByte((byte) 8, scratch, 0); // address size - } else { - pos = putInt(0, buffer, pos); // CU length - pos = putShort(DW_VERSION_2, buffer, pos); // dwarf version - pos = putInt(0, buffer, pos); // abbrev offset - return putByte((byte) 8, buffer, pos); // address size - } - } - - public int writeCU(ClassEntry classEntry, byte[] buffer, int p) { - int pos = p; - LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); - debug(" [0x%08x] <0> Abbrev Number %d\n", pos, DW_ABBREV_CODE_compile_unit); - pos = writeAbbrevCode(DW_ABBREV_CODE_compile_unit, buffer, pos); - debug(" [0x%08x] language %s\n", pos, "DW_LANG_Java"); - pos = writeAttrData1(DW_LANG_Java, buffer, pos); - debug(" [0x%08x] name 0x%x (%s)\n", pos, debugStringIndex(classEntry.getFileName()), classEntry.getFileName()); - pos = writeAttrStrp(classEntry.getFileName(), buffer, pos); - debug(" [0x%08x] low_pc 0x%08x\n", pos, classPrimaryEntries.getFirst().getPrimary().getLo()); - pos = writeAttrAddress(classPrimaryEntries.getFirst().getPrimary().getLo(), buffer, pos); - debug(" [0x%08x] hi_pc 0x%08x\n", pos, classPrimaryEntries.getLast().getPrimary().getHi()); - pos = writeAttrAddress(classPrimaryEntries.getLast().getPrimary().getHi(), buffer, pos); - debug(" [0x%08x] stmt_list 0x%08x\n", pos, classEntry.getLineIndex()); - pos = writeAttrData4(classEntry.getLineIndex(), buffer, pos); - for (PrimaryEntry primaryEntry : classPrimaryEntries) { - pos = writePrimary(primaryEntry, buffer, pos); - } - // write a terminating null attribute for the the level 2 primaries - return writeAttrNull(buffer, pos); - - } - - public int writePrimary(PrimaryEntry primaryEntry, byte[] buffer, int p) { - int pos = p; - Range primary = primaryEntry.getPrimary(); - debug(" [0x%08x] <1> Abbrev Number %d\n", pos, DW_ABBREV_CODE_subprogram); - pos = writeAbbrevCode(DW_ABBREV_CODE_subprogram, buffer, pos); - debug(" [0x%08x] name 0x%X (%s)\n", pos, debugStringIndex(primary.getFullMethodName()), primary.getFullMethodName()); - pos = writeAttrStrp(primary.getFullMethodName(), buffer, pos); - debug(" [0x%08x] low_pc 0x%08x\n", pos, primary.getLo()); - pos = writeAttrAddress(primary.getLo(), buffer, pos); - debug(" [0x%08x] high_pc 0x%08x\n", pos, primary.getHi()); - pos = writeAttrAddress(primary.getHi(), buffer, pos); - // need to pass true only if method is public - debug(" [0x%08x] external true\n", pos); - return writeFlag(DW_FLAG_true, buffer, pos); - } - - public int writeAttrStrp(String value, byte[] buffer, int p) { - int pos = p; - if (buffer == null) { - return pos + putInt(0, scratch, 0); - } else { - int idx = debugStringIndex(value); - return putInt(idx, buffer, pos); - } - } - - public int writeAttrString(String value, byte[] buffer, int p) { - int pos = p; - if (buffer == null) { - return pos + value.length() + 1; - } else { - return putAsciiStringBytes(value, buffer, pos); - } - } - - @Override - protected void debug(String format, Object... args) { - if (((int) args[0] - debugBase) < 0x100000) { - super.debug(format, args); - } else if (format.startsWith(" [0x%08x] primary file")) { - super.debug(format, args); - } - } - - /** - * debug_info section content depends on abbrev section content and offset. - */ - public static final String TARGET_SECTION_NAME = DW_ABBREV_SECTION_NAME; - - @Override - public String targetSectionName() { - return TARGET_SECTION_NAME; - } - - public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET - }; - - @Override - public LayoutDecision.Kind[] targetSectionKinds() { - return targetSectionKinds; - } + public StringTable getStringTable() { + return stringTable; } - - /** - * generator for debug_aranges section. - */ - public class DwarfARangesSectionImpl extends DwarfSectionImpl { - private static final int DW_AR_HEADER_SIZE = 12; - private static final int DW_AR_HEADER_PAD_SIZE = 4; // align up to 2 * address size - - public DwarfARangesSectionImpl() { - super(); - } - - @Override - public String getSectionName() { - return DW_ARANGES_SECTION_NAME; - } - - @Override - public void createContent() { - int pos = 0; - // we need an entry for each compilation unit - // - // uint32 length ............ in bytes (not counting these 4 bytes) - // uint16 dwarf_version ..... always 2 - // uint32 info_offset ....... offset of compilation unit on debug_info - // uint8 address_size ....... always 8 - // uint8 segment_desc_size .. ??? - // - // i.e. 12 bytes followed by padding - // aligning up to 2 * address size - // - // uint8 pad[4] - // - // followed by N + 1 times - // - // uint64 lo ................ lo address of range - // uint64 length ............ number of bytes in range - // - // where N is the number of ranges belonging to the compilation unit - // and the last range contains two zeroes - - for (ClassEntry classEntry : primaryClasses) { - pos += DW_AR_HEADER_SIZE; - // align to 2 * address size - pos += DW_AR_HEADER_PAD_SIZE; - pos += classEntry.getPrimaryEntries().size() * 2 * 8; - pos += 2 * 8; - } - byte[] buffer = new byte[pos]; - super.setContent(buffer); - } - - @Override - public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { - Element textElement = getElement().getOwner().elementForName(".text"); - LayoutDecisionMap decisionMap = alreadyDecided.get(textElement); - if (decisionMap != null) { - Object valueObj = decisionMap.getDecidedValue(LayoutDecision.Kind.VADDR); - if (valueObj != null && valueObj instanceof Number) { - // this may not be the final vaddr for the text segment - // but it will be close enough to make debug easier - // i.e. to within a 4k page or two - debugTextBase = ((Number) valueObj).longValue(); - } - } - return super.getOrDecideContent(alreadyDecided, contentHint); - } - - @Override - public void writeContent() { - byte[] buffer = getContent(); - int size = buffer.length; - int pos = 0; - - checkDebug(pos); - - debug(" [0x%08x] DEBUG_ARANGES\n", pos); - for (ClassEntry classEntry : primaryClasses) { - int lastpos = pos; - int length = DW_AR_HEADER_SIZE + DW_AR_HEADER_PAD_SIZE - 4; - int cuIndex = classEntry.getCUIndex(); - LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); - // add room for each entry into length count - length += classPrimaryEntries.size() * 2 * 8; - length += 2 * 8; - debug(" [0x%08x] %s CU %d length 0x%x\n", pos, classEntry.getFileName(), cuIndex, length); - pos = putInt(length, buffer, pos); - pos = putShort(DW_VERSION_2, buffer, pos); // dwarf version is always 2 - pos = putInt(cuIndex, buffer, pos); - pos = putByte((byte) 8, buffer, pos); // address size is always 8 - pos = putByte((byte) 0, buffer, pos); // segment size is always 0 - assert (pos - lastpos) == DW_AR_HEADER_SIZE; - // align to 2 * address size - for (int i = 0; i < DW_AR_HEADER_PAD_SIZE; i++) { - pos = putByte((byte) 0, buffer, pos); - } - debug(" [0x%08x] Address Length Name\n", pos); - for (PrimaryEntry classPrimaryEntry : classPrimaryEntries) { - Range primary = classPrimaryEntry.getPrimary(); - debug(" [0x%08x] %016x %016x %s\n", pos, debugTextBase + primary.getLo(), primary.getHi() - primary.getLo(), primary.getFullMethodName()); - pos = putRelocatableCodeOffset(primary.getLo(), buffer, pos); - pos = putLong(primary.getHi() - primary.getLo(), buffer, pos); - } - pos = putLong(0, buffer, pos); - pos = putLong(0, buffer, pos); - } - - assert pos == size; - } - - @Override - protected void debug(String format, Object... args) { - super.debug(format, args); - } - - // .debug_aranges section content depends on .debug_info section content and offset - public static final String TARGET_SECTION_NAME = DW_INFO_SECTION_NAME; - - @Override - public String targetSectionName() { - return TARGET_SECTION_NAME; - } - - public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET - }; - - @Override - public LayoutDecision.Kind[] targetSectionKinds() { - return targetSectionKinds; - } + public LinkedList getPrimaryClasses() { + return primaryClasses; } - - /** - * generator for debug_line section. - */ - public class DwarfLineSectionImpl extends DwarfSectionImpl { - /** - * line header section always contains fixed number of bytes. - */ - private static final int DW_LN_HEADER_SIZE = 27; - /** - * current generator follows C++ with line base -5. - */ - private static final int DW_LN_LINE_BASE = -5; - /** - * current generator follows C++ with line range 14 - * giving full range -5 to 8. - */ - private static final int DW_LN_LINE_RANGE = 14; - /** - * current generator uses opcode base of 13 - * which must equal DW_LNS_define_file + 1. - */ - private static final int DW_LN_OPCODE_BASE = 13; - - /* - * standard opcodes defined by Dwarf 2 - */ - private static final byte DW_LNS_undefined = 0; // 0 can be returned to indicate an - // invalid opcode - private static final byte DW_LNS_extended_prefix = 0; // 0 can be inserted as a prefix for - // extended opcodes - private static final byte DW_LNS_copy = 1; // append current state as matrix row - // 0 args - private static final byte DW_LNS_advance_pc = 2; // increment address 1 uleb arg - private static final byte DW_LNS_advance_line = 3; // increment line 1 sleb arg - private static final byte DW_LNS_set_file = 4; // set file 1 uleb arg - private static final byte DW_LNS_set_column = 5; // set column 1 uleb arg - private static final byte DW_LNS_negate_stmt = 6; // flip is_stmt 0 args - private static final byte DW_LNS_set_basic_block = 7; // set end sequence and copy row - private static final byte DW_LNS_const_add_pc = 8; // increment address as per opcode - // 255 0 args - private static final byte DW_LNS_fixed_advance_pc = 9; // increment address 1 ushort arg - - /* - * extended opcodes defined by Dwarf 2 - */ - // private static final byte DW_LNE_undefined = 0; // there is no extended opcode 0 - private static final byte DW_LNE_end_sequence = 1; // end sequence of addresses - private static final byte DW_LNE_set_address = 2; // there is no extended opcode 0 - private static final byte DW_LNE_define_file = 3; // there is no extended opcode 0 - - DwarfLineSectionImpl() { - super(); - } - - @Override - public String getSectionName() { - return DW_LINE_SECTION_NAME; - } - - @Override - public void createContent() { - // we need to create a header, dir table, file table and line - // number table encoding for each CU - - // write entries for each file listed in the primary list - int pos = 0; - for (ClassEntry classEntry : primaryClasses) { - int startPos = pos; - classEntry.setLineIndex(startPos); - int headerSize = headerSize(); - int dirTableSize = computeDirTableSize(classEntry); - int fileTableSize = computeFileTableSize(classEntry); - int prologueSize = headerSize + dirTableSize + fileTableSize; - classEntry.setLinePrologueSize(prologueSize); - int lineNumberTableSize = computeLineNUmberTableSize(classEntry); - int totalSize = prologueSize + lineNumberTableSize; - classEntry.setTotalSize(totalSize); - pos += totalSize; - } - byte[] buffer = new byte[pos]; - super.setContent(buffer); - } - - public int headerSize() { - // header size is standard 31 bytes - // uint32 total_length - // uint16 version - // uint32 prologue_length - // uint8 min_insn_length - // uint8 default_is_stmt - // int8 line_base - // uint8 line_range - // uint8 opcode_base - // uint8 li_opcode_base - // uint8[opcode_base-1] standard_opcode_lengths - - return DW_LN_HEADER_SIZE; - } - - public int computeDirTableSize(ClassEntry classEntry) { - // table contains a sequence of 'nul'-terminated - // dir name bytes followed by an extra 'nul' - // and then a sequence of 'nul'-terminated - // file name bytes followed by an extra 'nul' - - // for now we assume dir and file names are ASCII - // byte strings - int dirSize = 0; - for (DirEntry dir : classEntry.getLocalDirs()) { - dirSize += dir.getPath().length() + 1; - } - // allow for separator nul - dirSize++; - return dirSize; - } - - public int computeFileTableSize(ClassEntry classEntry) { - // table contains a sequence of 'nul'-terminated - // dir name bytes followed by an extra 'nul' - // and then a sequence of 'nul'-terminated - // file name bytes followed by an extra 'nul' - - // for now we assume dir and file names are ASCII - // byte strings - int fileSize = 0; - for (FileEntry localEntry : classEntry.getLocalFiles()) { - // we want the file base name excluding path - String baseName = localEntry.getBaseName(); - int length = baseName.length(); - fileSize += length + 1; - DirEntry dirEntry = localEntry.dirEntry; - int idx = classEntry.localDirsIdx(dirEntry); - fileSize += putULEB(idx, scratch, 0); - // the two zero timestamps require 1 byte each - fileSize += 2; - } - // allow for terminator nul - fileSize++; - return fileSize; - } - - public int computeLineNUmberTableSize(ClassEntry classEntry) { - // sigh -- we have to do this by generating the - // content even though we cannot write it into a byte[] - return writeLineNumberTable(classEntry, null, 0); - } - - @Override - public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { - Element textElement = getElement().getOwner().elementForName(".text"); - LayoutDecisionMap decisionMap = alreadyDecided.get(textElement); - if (decisionMap != null) { - Object valueObj = decisionMap.getDecidedValue(LayoutDecision.Kind.VADDR); - if (valueObj != null && valueObj instanceof Number) { - // this may not be the final vaddr for the text segment - // but it will be close enough to make debug easier - // i.e. to within a 4k page or two - debugTextBase = ((Number) valueObj).longValue(); - } - } - return super.getOrDecideContent(alreadyDecided, contentHint); - } - - @Override - public void writeContent() { - byte[] buffer = getContent(); - - int pos = 0; - checkDebug(pos); - debug(" [0x%08x] DEBUG_LINE\n", pos); - - for (ClassEntry classEntry : primaryClasses) { - int startPos = pos; - assert classEntry.getLineIndex() == startPos; - debug(" [0x%08x] Compile Unit for %s\n", pos, classEntry.getFileName()); - pos = writeHeader(classEntry, buffer, pos); - debug(" [0x%08x] headerSize = 0x%08x\n", pos, pos - startPos); - int dirTablePos = pos; - pos = writeDirTable(classEntry, buffer, pos); - debug(" [0x%08x] dirTableSize = 0x%08x\n", pos, pos - dirTablePos); - int fileTablePos = pos; - pos = writeFileTable(classEntry, buffer, pos); - debug(" [0x%08x] fileTableSize = 0x%08x\n", pos, pos - fileTablePos); - int lineNumberTablePos = pos; - pos = writeLineNumberTable(classEntry, buffer, pos); - debug(" [0x%08x] lineNumberTableSize = 0x%x\n", pos, pos - lineNumberTablePos); - debug(" [0x%08x] size = 0x%x\n", pos, pos - startPos); - } - assert pos == buffer.length; - } - - public int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { - int pos = p; - // 4 ubyte length field - pos = putInt(classEntry.getTotalSize() - 4, buffer, pos); - // 2 ubyte version is always 2 - pos = putShort(DW_VERSION_2, buffer, pos); - // 4 ubyte prologue length includes rest of header and - // dir + file table section - int prologueSize = classEntry.getLinePrologueSize() - 6; - pos = putInt(prologueSize, buffer, pos); - // 1 ubyte min instruction length is always 1 - pos = putByte((byte) 1, buffer, pos); - // 1 byte default is_stmt is always 1 - pos = putByte((byte) 1, buffer, pos); - // 1 byte line base is always -5 - pos = putByte((byte) DW_LN_LINE_BASE, buffer, pos); - // 1 ubyte line range is always 14 giving range -5 to 8 - pos = putByte((byte) DW_LN_LINE_RANGE, buffer, pos); - // 1 ubyte opcode base is always 13 - pos = putByte((byte) DW_LN_OPCODE_BASE, buffer, pos); - // specify opcode arg sizes for the standard opcodes - putByte((byte) 0, buffer, pos); // DW_LNS_copy - putByte((byte) 1, buffer, pos + 1); // DW_LNS_advance_pc - putByte((byte) 1, buffer, pos + 2); // DW_LNS_advance_line - putByte((byte) 1, buffer, pos + 3); // DW_LNS_set_file - putByte((byte) 1, buffer, pos + 4); // DW_LNS_set_column - putByte((byte) 0, buffer, pos + 5); // DW_LNS_negate_stmt - putByte((byte) 0, buffer, pos + 6); // DW_LNS_set_basic_block - putByte((byte) 0, buffer, pos + 7); // DW_LNS_const_add_pc - putByte((byte) 1, buffer, pos + 8); // DW_LNS_fixed_advance_pc - putByte((byte) 0, buffer, pos + 9); // DW_LNS_end_sequence - putByte((byte) 0, buffer, pos + 10); // DW_LNS_set_address - pos = putByte((byte) 1, buffer, pos + 11); // DW_LNS_define_file - return pos; - } - - public int writeDirTable(ClassEntry classEntry, byte[] buffer, int p) { - int pos = p; - debug(" [0x%08x] Dir Name\n", pos); - // write out the list of dirs referenced form this file entry - int dirIdx = 1; - for (DirEntry dir : classEntry.getLocalDirs()) { - // write nul terminated string text. - debug(" [0x%08x] %-4d %s\n", pos, dirIdx, dir.getPath()); - pos = putAsciiStringBytes(dir.getPath(), buffer, pos); - dirIdx++; - } - // separate dirs from files with a nul - pos = putByte((byte) 0, buffer, pos); - return pos; - } - - public int writeFileTable(ClassEntry classEntry, byte[] buffer, int p) { - int pos = p; - int fileIdx = 1; - debug(" [0x%08x] Entry Dir Name\n", pos); - for (FileEntry localEntry : classEntry.getLocalFiles()) { - // we need the file name minus path, the associated dir index, and 0 for time stamps - String baseName = localEntry.getBaseName(); - DirEntry dirEntry = localEntry.dirEntry; - int dirIdx = classEntry.localDirsIdx(dirEntry); - debug(" [0x%08x] %-5d %-5d %s\n", pos, fileIdx, dirIdx, baseName); - pos = putAsciiStringBytes(baseName, buffer, pos); - pos = putULEB(dirIdx, buffer, pos); - pos = putULEB(0, buffer, pos); - pos = putULEB(0, buffer, pos); - fileIdx++; - } - // terminate files with a nul - pos = putByte((byte) 0, buffer, pos); - return pos; - } - - public int debugLine = 1; - public int debugCopyCount = 0; - - public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { - int pos = p; - // the primary file entry should always be first in the local files list - assert classEntry.localFilesIdx(classEntry.getFileEntry()) == 1; - String primaryClassName = classEntry.getClassName(); - String primaryFileName = classEntry.getFileName(); - String file = primaryFileName; - int fileIdx = 1; - debug(" [0x%08x] primary class %s\n", pos, primaryClassName); - debug(" [0x%08x] primary file %s\n", pos, primaryFileName); - for (PrimaryEntry primaryEntry : classEntry.getPrimaryEntries()) { - Range primaryRange = primaryEntry.getPrimary(); - assert primaryRange.getFileName().equals(primaryFileName); - // each primary represents a method i.e. a contiguous - // sequence of subranges. we assume the default state - // at the start of each sequence because we always post an - // end_sequence when we finish all the subranges in the method - long line = primaryRange.getLine(); - if (line < 0 && primaryEntry.getSubranges().size() > 0) { - line = primaryEntry.getSubranges().get(0).getLine(); - } - if (line < 0) { - line = 0; - } - long address = primaryRange.getLo(); - - // set state for primary - debug(" [0x%08x] primary range [0x%08x, 0x%08x] %s:%d\n", pos, debugTextBase + primaryRange.getLo(), debugTextBase + primaryRange.getHi(), primaryRange.getFullMethodName(), - primaryRange.getLine()); - - // initialize and write a row for the start of the primary method - pos = putSetFile(file, fileIdx, buffer, pos); - pos = putSetBasicBlock(buffer, pos); - // address is currently 0 - pos = putSetAddress(address, buffer, pos); - // state machine value of line is currently 1 - // increment to desired line - if (line != 1) { - pos = putAdvanceLine(line - 1, buffer, pos); - } - pos = putCopy(buffer, pos); - - // now write a row for each subrange lo and hi - for (Range subrange : primaryEntry.getSubranges()) { - assert subrange.getLo() >= primaryRange.getLo(); - assert subrange.getHi() <= primaryRange.getHi(); - FileEntry subFileEntry = primaryEntry.getSubrangeFileEntry(subrange); - String subfile = subFileEntry.getFileName(); - int subFileIdx = classEntry.localFilesIdx(subFileEntry); - long subLine = subrange.getLine(); - long subAddressLo = subrange.getLo(); - long subAddressHi = subrange.getHi(); - debug(" [0x%08x] sub range [0x%08x, 0x%08x] %s:%d\n", pos, debugTextBase + subAddressLo, debugTextBase + subAddressHi, subrange.getFullMethodName(), subLine); - if (subLine < 0) { - // no line info so stay at previous file:line - subLine = line; - subfile = file; - subFileIdx = fileIdx; - debug(" [0x%08x] missing line info - staying put at %s:%d\n", pos, file, line); - } - // there is a temptation to append end sequence at here - // when the hiAddress lies strictly between the current - // address and the start of the next subrange because, - // ostensibly, we have void space between the end of - // the current subrange and the start of the next one. - // however, debug works better if we treat all the insns up - // to the next range start as belonging to the current line - // - // if we have to update to a new file then do so - if (subFileIdx != fileIdx) { - // update the current file - pos = putSetFile(subfile, subFileIdx, buffer, pos); - file = subfile; - fileIdx = subFileIdx; - } - // check if we can advance line and/or address in - // one byte with a special opcode - long lineDelta = subLine - line; - long addressDelta = subAddressLo - address; - byte opcode = isSpecialOpcode(addressDelta, lineDelta); - if (opcode != DW_LNS_undefined) { - // ignore pointless write when addressDelta == lineDelta == 0 - if (addressDelta != 0 || lineDelta != 0) { - pos = putSpecialOpcode(opcode, buffer, pos); - } - } else { - // does it help to divide and conquer using - // a fixed address increment - int remainder = isConstAddPC(addressDelta); - if (remainder > 0) { - pos = putConstAddPC(buffer, pos); - // the remaining address can be handled with a - // special opcode but what about the line delta - opcode = isSpecialOpcode(remainder, lineDelta); - if (opcode != DW_LNS_undefined) { - // address remainder and line now fit - pos = putSpecialOpcode(opcode, buffer, pos); - } else { - // ok, bump the line separately then use a - // special opcode for the address remainder - opcode = isSpecialOpcode(remainder, 0); - assert opcode != DW_LNS_undefined; - pos = putAdvanceLine(lineDelta, buffer, pos); - pos = putSpecialOpcode(opcode, buffer, pos); - } - } else { - // increment line and pc separately - if (lineDelta != 0) { - pos = putAdvanceLine(lineDelta, buffer, pos); - } - // n.b. we might just have had an out of range line increment - // with a zero address increment - if (addressDelta > 0) { - // see if we can use a ushort for the increment - if (isFixedAdvancePC(addressDelta)) { - pos = putFixedAdvancePC((short) addressDelta, buffer, pos); - } else { - pos = putAdvancePC(addressDelta, buffer, pos); - } - } - pos = putCopy(buffer, pos); - } - } - // move line and address range on - line += lineDelta; - address += addressDelta; - } - // append a final end sequence just below the next primary range - if (address < primaryRange.getHi()) { - long addressDelta = primaryRange.getHi() - address; - // increment address before we write the end sequence - pos = putAdvancePC(addressDelta, buffer, pos); - } - pos = putEndSequence(buffer, pos); - } - debug(" [0x%08x] primary file processed %s\n", pos, primaryFileName); - - return pos; - } - - @Override - protected void debug(String format, Object... args) { - if (((int) args[0] - debugBase) < 0x100000) { - super.debug(format, args); - } else if (format.startsWith(" [0x%08x] primary file")) { - super.debug(format, args); - } - } - - public int putCopy(byte[] buffer, int p) { - byte opcode = DW_LNS_copy; - int pos = p; - if (buffer == null) { - return pos + putByte(opcode, scratch, 0); - } else { - debugCopyCount++; - debug(" [0x%08x] Copy %d\n", pos, debugCopyCount); - return putByte(opcode, buffer, pos); - } - } - - public int putAdvancePC(long uleb, byte[] buffer, int p) { - byte opcode = DW_LNS_advance_pc; - int pos = p; - if (buffer == null) { - pos = pos + putByte(opcode, scratch, 0); - return pos + putULEB(uleb, scratch, 0); - } else { - debugAddress += uleb; - debug(" [0x%08x] Advance PC by %d to 0x%08x\n", pos, uleb, debugAddress); - pos = putByte(opcode, buffer, pos); - return putULEB(uleb, buffer, pos); - } - } - - public int putAdvanceLine(long sleb, byte[] buffer, int p) { - byte opcode = DW_LNS_advance_line; - int pos = p; - if (buffer == null) { - pos = pos + putByte(opcode, scratch, 0); - return pos + putSLEB(sleb, scratch, 0); - } else { - debugLine += sleb; - debug(" [0x%08x] Advance Line by %d to %d\n", pos, sleb, debugLine); - pos = putByte(opcode, buffer, pos); - return putSLEB(sleb, buffer, pos); - } - } - - public int putSetFile(String file, long uleb, byte[] buffer, int p) { - byte opcode = DW_LNS_set_file; - int pos = p; - if (buffer == null) { - pos = pos + putByte(opcode, scratch, 0); - return pos + putULEB(uleb, scratch, 0); - } else { - debug(" [0x%08x] Set File Name to entry %d in the File Name Table (%s)\n", pos, uleb, file); - pos = putByte(opcode, buffer, pos); - return putULEB(uleb, buffer, pos); - } - } - - public int putSetColumn(long uleb, byte[] buffer, int p) { - byte opcode = DW_LNS_set_column; - int pos = p; - if (buffer == null) { - pos = pos + putByte(opcode, scratch, 0); - return pos + putULEB(uleb, scratch, 0); - } else { - pos = putByte(opcode, buffer, pos); - return putULEB(uleb, buffer, pos); - } - } - - public int putNegateStmt(byte[] buffer, int p) { - byte opcode = DW_LNS_negate_stmt; - int pos = p; - if (buffer == null) { - return pos + putByte(opcode, scratch, 0); - } else { - return putByte(opcode, buffer, pos); - } - } - - public int putSetBasicBlock(byte[] buffer, int p) { - byte opcode = DW_LNS_set_basic_block; - int pos = p; - if (buffer == null) { - return pos + putByte(opcode, scratch, 0); - } else { - debug(" [0x%08x] Set basic block\n", pos); - return putByte(opcode, buffer, pos); - } - } - - public int putConstAddPC(byte[] buffer, int p) { - byte opcode = DW_LNS_const_add_pc; - int pos = p; - if (buffer == null) { - return pos + putByte(opcode, scratch, 0); - } else { - int advance = opcodeAddress((byte) 255); - debugAddress += advance; - debug(" [0x%08x] Advance PC by constant %d to 0x%08x\n", pos, advance, debugAddress); - return putByte(opcode, buffer, pos); - } - } - - public int putFixedAdvancePC(short arg, byte[] buffer, int p) { - byte opcode = DW_LNS_fixed_advance_pc; - int pos = p; - if (buffer == null) { - pos = pos + putByte(opcode, scratch, 0); - return pos + putShort(arg, scratch, 0); - } else { - debugAddress += arg; - debug(" [0x%08x] Fixed advance Address by %d to 0x%08x\n", pos, arg, debugAddress); - pos = putByte(opcode, buffer, pos); - return putShort(arg, buffer, pos); - } - } - - public int putEndSequence(byte[] buffer, int p) { - byte opcode = DW_LNE_end_sequence; - int pos = p; - if (buffer == null) { - pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); - // insert extended insn byte count as ULEB - pos = pos + putULEB(1, scratch, 0); - return pos + putByte(opcode, scratch, 0); - } else { - debug(" [0x%08x] Extended opcode 1: End sequence\n", pos); - debugAddress = debugTextBase; - debugLine = 1; - debugCopyCount = 0; - pos = putByte(DW_LNS_extended_prefix, buffer, pos); - // insert extended insn byte count as ULEB - pos = putULEB(1, buffer, pos); - return putByte(opcode, buffer, pos); - } - } - - public int putSetAddress(long arg, byte[] buffer, int p) { - byte opcode = DW_LNE_set_address; - int pos = p; - if (buffer == null) { - pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); - // insert extended insn byte count as ULEB - pos = pos + putULEB(9, scratch, 0); - pos = pos + putByte(opcode, scratch, 0); - return pos + putLong(arg, scratch, 0); - } else { - debugAddress = debugTextBase + (int) arg; - debug(" [0x%08x] Extended opcode 2: Set Address to 0x%08x\n", pos, debugAddress); - pos = putByte(DW_LNS_extended_prefix, buffer, pos); - // insert extended insn byte count as ULEB - pos = putULEB(9, buffer, pos); - pos = putByte(opcode, buffer, pos); - return putRelocatableCodeOffset(arg, buffer, pos); - } - } - - public int putDefineFile(String file, long uleb1, long uleb2, long uleb3, byte[] buffer, int p) { - byte opcode = DW_LNE_define_file; - int pos = p; - // calculate bytes needed for opcode + args - int fileBytes = file.length() + 1; - long insnBytes = 1; - insnBytes += fileBytes; - insnBytes += putULEB(uleb1, scratch, 0); - insnBytes += putULEB(uleb2, scratch, 0); - insnBytes += putULEB(uleb3, scratch, 0); - if (buffer == null) { - pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); - // write insnBytes as a ULEB - pos += putULEB(insnBytes, scratch, 0); - return pos + (int) insnBytes; - } else { - debug(" [0x%08x] Extended opcode 3: Define File %s idx %d ts1 %d ts2 %d\n", pos, file, uleb1, uleb2, uleb3); - pos = putByte(DW_LNS_extended_prefix, buffer, pos); - // insert insn length as uleb - pos = putULEB(insnBytes, buffer, pos); - // insert opcode and args - pos = putByte(opcode, buffer, pos); - pos = putAsciiStringBytes(file, buffer, pos); - pos = putULEB(uleb1, buffer, pos); - pos = putULEB(uleb2, buffer, pos); - return putULEB(uleb3, buffer, pos); - } - } - - public int opcodeId(byte opcode) { - int iopcode = opcode & 0xff; - return iopcode - DW_LN_OPCODE_BASE; - } - - public int opcodeAddress(byte opcode) { - int iopcode = opcode & 0xff; - return (iopcode - DW_LN_OPCODE_BASE) / DW_LN_LINE_RANGE; - } - - public int opcodeLine(byte opcode) { - int iopcode = opcode & 0xff; - return ((iopcode - DW_LN_OPCODE_BASE) % DW_LN_LINE_RANGE) + DW_LN_LINE_BASE; - } - - public int putSpecialOpcode(byte opcode, byte[] buffer, int p) { - int pos = p; - if (buffer == null) { - return pos + putByte(opcode, scratch, 0); - } else { - if (debug && opcode == 0) { - debug(" [0x%08x] ERROR Special Opcode %d: Address 0x%08x Line %d\n", debugAddress, debugLine); - } - debugAddress += opcodeAddress(opcode); - debugLine += opcodeLine(opcode); - debug(" [0x%08x] Special Opcode %d: advance Address by %d to 0x%08x and Line by %d to %d\n", - pos, opcodeId(opcode), opcodeAddress(opcode), debugAddress, opcodeLine(opcode), debugLine); - return putByte(opcode, buffer, pos); - } - } - - private static final int MAX_ADDRESS_ONLY_DELTA = (0xff - DW_LN_OPCODE_BASE) / DW_LN_LINE_RANGE; - private static final int MAX_ADDPC_DELTA = MAX_ADDRESS_ONLY_DELTA + (MAX_ADDRESS_ONLY_DELTA - 1); - - public byte isSpecialOpcode(long addressDelta, long lineDelta) { - if (addressDelta < 0) { - return DW_LNS_undefined; - } - if (lineDelta >= DW_LN_LINE_BASE) { - long offsetLineDelta = lineDelta - DW_LN_LINE_BASE; - if (offsetLineDelta < DW_LN_LINE_RANGE) { - // line_delta can be encoded - // check if address is ok - if (addressDelta <= MAX_ADDRESS_ONLY_DELTA) { - long opcode = DW_LN_OPCODE_BASE + (addressDelta * DW_LN_LINE_RANGE) + offsetLineDelta; - if (opcode <= 255) { - return (byte) opcode; - } - } - } - } - - // return invalid opcode - return DW_LNS_undefined; - } - - public int isConstAddPC(long addressDelta) { - if (addressDelta < MAX_ADDRESS_ONLY_DELTA) { - return 0; - } - if (addressDelta <= MAX_ADDPC_DELTA) { - return (int) (addressDelta - MAX_ADDRESS_ONLY_DELTA); - } else { - return 0; - } - } - - public boolean isFixedAdvancePC(long addressDiff) { - return addressDiff >= 0 && addressDiff < 0xffff; - } - - /** - * debug_line section content depends on debug_str section content and offset. - */ - public static final String TARGET_SECTION_NAME = DW_STR_SECTION_NAME; - - @Override - public String targetSectionName() { - return TARGET_SECTION_NAME; - } - - public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET, - }; - - @Override - public LayoutDecision.Kind[] targetSectionKinds() { - return targetSectionKinds; - } + public ByteOrder getByteOrder() { + return byteOrder; } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java new file mode 100644 index 000000000000..0bcdbccb5992 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +import com.oracle.objectfile.LayoutDecision; + +import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_STR_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfSections.TEXT_SECTION_NAME; +/** + * generator for debug_str section. + */ +public class DwarfStrSectionImpl extends DwarfSectionImpl { + public DwarfStrSectionImpl(DwarfSections dwarfSections) { + super(dwarfSections); + } + + @Override + public String getSectionName() { + return DW_STR_SECTION_NAME; + } + + @Override + public void createContent() { + int pos = 0; + for (StringEntry stringEntry : dwarfSections.getStringTable()) { + if (stringEntry.isAddToStrSection()) { + stringEntry.setOffset(pos); + String string = stringEntry.getString(); + pos += string.length() + 1; + } + } + byte[] buffer = new byte[pos]; + super.setContent(buffer); + } + + @Override + public void writeContent() { + byte[] buffer = getContent(); + int size = buffer.length; + int pos = 0; + + checkDebug(pos); + + for (StringEntry stringEntry : dwarfSections.getStringTable()) { + if (stringEntry.isAddToStrSection()) { + assert stringEntry.getOffset() == pos; + String string = stringEntry.getString(); + pos = putAsciiStringBytes(string, buffer, pos); + } + } + assert pos == size; + } + + @Override + protected void debug(String format, Object... args) { + super.debug(format, args); + } + + /** + * debug_str section content depends on text section content and offset. + */ + public static final String TARGET_SECTION_NAME = TEXT_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + /** + * debug_str section content depends on text section content and offset. + */ + public final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET, + /* add this so we can use the text section base address for debug */ + LayoutDecision.Kind.VADDR, + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java index cd530c68de04..a597a4df17bc 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java @@ -27,14 +27,20 @@ package com.oracle.objectfile.elf.dwarf; /** - * track debug info associated with a Java source file. + * Tracks debug info associated with a Java source file. */ public class FileEntry { - // the name of the associated file including path + /** + * The name of the associated file including path + */ private String fileName; - // the name of the associated file excluding path + /** + * The name of the associated file excluding path + */ private String baseName; - // the directory entry associated with this file entry + /** + * The directory entry associated with this file entry + */ DirEntry dirEntry; public FileEntry(String fileName, String baseName, DirEntry dirEntry) { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/PrimaryEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/PrimaryEntry.java index 6720703cf921..cd356dd00535 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/PrimaryEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/PrimaryEntry.java @@ -33,51 +33,34 @@ import java.util.List; /** - * track debug info associated with a primary method. + * Tracks debug info associated with a primary method. * i.e. a top level compiled method */ public class PrimaryEntry { /** - * the primary range detailed by this object. + * The primary range detailed by this object. */ Range primary; /** - * details of the class owning this range. + * Details of the class owning this range. */ ClassEntry classEntry; /** - * a list of subranges associated with the primary range. + * A list of subranges associated with the primary range. */ List subranges; /** - * a mapping from subranges to their associated file entry. + * A mapping from subranges to their associated file entry. */ HashMap subrangeIndex; /** - * details of of compiled method frame size changes. + * Details of of compiled method frame size changes. */ private List frameSizeInfos; /** - * size of compiled method frame. + * Size of compiled method frame. */ private int frameSize; - /** - * index of debug_info section compilation unit for this file. - */ - private int cuIndex; - /** - * index into debug_line section for associated compilation unit. - */ - private int lineIndex; - /** - * size of line number info prologue region for associated compilation unit. - */ - private int linePrologueSize; - /** - * total size of line number info region for associated compilation unit. - */ - private int totalSize; - public PrimaryEntry(Range primary, List frameSizeInfos, int frameSize, ClassEntry classEntry) { this.primary = primary; this.classEntry = classEntry; @@ -85,17 +68,18 @@ public PrimaryEntry(Range primary, List frameSizeInfos, in this.subrangeIndex = new HashMap<>(); this.frameSizeInfos = frameSizeInfos; this.frameSize = frameSize; - // initialize indices into other sections to illegal values - this.cuIndex = -1; - this.lineIndex = -1; } public void addSubRange(Range subrange, FileEntry subFileEntry) { - // we should not see a subrange more than once + /* + * we should not see a subrange more than once + */ assert !subranges.contains(subrange); assert subrangeIndex.get(subrange) == null; - // we need to generate a file table entry - // for all ranges + /* + * we need to generate a file table entry + * for all ranges + */ subranges.add(subrange); subrangeIndex.put(subrange, subFileEntry); } @@ -127,46 +111,4 @@ List getFrameSizeInfos() { int getFrameSize() { return frameSize; } - - void setCUIndex(int cuIndex) { - // should only get set once to a non-negative value - assert cuIndex >= 0; - assert this.cuIndex == -1; - this.cuIndex = cuIndex; - } - - int getCUIndex() { - // should have been set before being read - assert cuIndex >= 0; - return cuIndex; - } - - int getLineIndex() { - // should have been set before being read - assert lineIndex >= 0; - return lineIndex; - } - - void setLineIndex(int lineIndex) { - // should only get set once to a non-negative value - assert lineIndex >= 0; - assert this.lineIndex == -1; - this.lineIndex = lineIndex; - } - - public int getLinePrologueSize() { - return linePrologueSize; - } - - public void setLinePrologueSize(int linePrologueSize) { - this.linePrologueSize = linePrologueSize; - } - - public int getTotalSize() { - return totalSize; - } - - public void setTotalSize(int totalSize) { - this.totalSize = totalSize; - } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java index 758825272a7e..41c78a176b2d 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java @@ -27,10 +27,10 @@ package com.oracle.objectfile.elf.dwarf; /** - * details of a specific address range in a compiled method + * Details of a specific address range in a compiled method * either a primary range identifying a whole method * or a sub-range identifying a sequence of - * instructions that belong to an inlined method + * instructions that belong to an inlined method. */ public class Range { @@ -43,18 +43,26 @@ public class Range { private int lo; private int hi; private int line; - // this is null for a primary range + /* + * this is null for a primary range + */ private Range primary; - // create a primary range + /* + * create a primary range + */ Range(String fileName, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line) { this(fileName, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, line, null); } - // create a primary or secondary range + /* + * create a primary or secondary range + */ Range(String fileName, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line, Range primary) { - // currently file name and full method name need to go into the debug_str section - // other strings just need to be deduplicated to save space + /* + * currently file name and full method name need to go into the debug_str section + * other strings just need to be deduplicated to save space + */ this.fileName = stringTable.uniqueDebugString(fileName); this.className = stringTable.uniqueString(className); this.methodName = stringTable.uniqueString(methodName); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java index d9a40dc2b3ed..b7963faf9661 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java @@ -27,10 +27,10 @@ package com.oracle.objectfile.elf.dwarf; /** - * class used to retain a unique (up to equals) copy of - * a String and also to flag whether the String needs to be - * located in the .debug_string section and track the offset - * where it gets written. + * Used to retain a unique (up to equals) copy of a + * String. Also flag swhether the String needs to be + * located in the debug_string section and, if so, + * tracks the offset at which it gets written. */ public class StringEntry { private String string; @@ -47,7 +47,9 @@ public String getString() { } public int getOffset() { - // offset must be set before this can be fetched + /* + * offset must be set before this can be fetched + */ assert offset >= 0; return offset; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java index 87b0af1eb437..1ba2def55f73 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java @@ -30,10 +30,10 @@ import java.util.Iterator; /** - * a class which allows incoming strings to be reduced - * to unique (up to equaals) instances and allows marking - * of strings which need to be written to the debug_str - * section and retrieval of the lcoation offset after writing. + * Allows incoming strings to be reduced to unique (up + * to equals) instances and supports marking of strings + * which need to be written to the debug_str section + * and retrieval of the location offset after writing. */ public class StringTable implements Iterable { @@ -44,9 +44,9 @@ public StringTable() { } /** - * ensures a unique instance of a string exists in the + * Wnsures a unique instance of a string exists in the * table, inserting the supplied String if no equivalent - * String is already present. this should only be called + * String is already present. This should only be called * before the string section has been written. * @param string the string to be included in the table * @return the unique instance of the String @@ -56,10 +56,10 @@ public String uniqueString(String string) { } /** - * ensures a unique instance of a string exists in the + * Ensures a unique instance of a string exists in the * table and is marked for inclusion in the debug_str * section, inserting the supplied String if no equivalent - * String is already present. this should only be called + * String is already present. This should only be called * before the string section has been written. * @param string the string to be included in the table * and marked for inclusion in the debug_str section @@ -82,8 +82,8 @@ private String ensureString(String string, boolean addToStrSection) { } /** - * retrieves the offset at which a given string was written - * into the debug_str section. this should only be called + * Retrieves the offset at which a given string was written + * into the debug_str section. This should only be called * after the string section has been written. * @param string * @return the offset or -1 if the string does not diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index a1000b3125f5..2d6ecba957b6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -52,6 +52,8 @@ import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.option.XOptions; +import static org.graalvm.compiler.core.common.GraalOptions.TrackNodeSourcePosition; + public class SubstrateOptions { @Option(help = "Class containing the default entry point method. Optional if --shared is used.", type = OptionType.User)// @@ -446,4 +448,15 @@ public static int codeAlignment() { @Option(help = "Populate reference queues in a separate thread rather than after a garbage collection.", type = OptionType.Expert) // public static final HostedOptionKey UseReferenceHandlerThread = new HostedOptionKey<>(false); + + @Option(help = "Insert debug info into the generated native image or library")// + public static final HostedOptionKey GenerateDebugInfo = new HostedOptionKey(0) { + @Override + protected void onValueUpdate(EconomicMap, Object> values, Integer oldValue, Integer newValue) { + // force update of TrackNodeSourcePosition + if (newValue > 0 && !Boolean.TRUE.equals(values.get(TrackNodeSourcePosition))) { + TrackNodeSourcePosition.update(values, true); + } + } + }; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java index 30b121818e66..0d03818a228f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java @@ -473,7 +473,7 @@ public void build(DebugContext debug, ImageHeapLayouter layouter) { // if we have constructed any debug info then // give the object file a chance to install it - if (GraalOptions.TrackNodeSourcePosition.getValue(HostedOptionValues.singleton())) { + if (SubstrateOptions.GenerateDebugInfo.getValue(HostedOptionValues.singleton()) > 0) { DebugInfoProvider provider = new NativeImageDebugInfoProvider(codeCache, heap); objectFile.installDebugInfo(provider); } From 25c7101fcc02d41865ef60545b6177ee7842dbef Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 11 Feb 2020 16:59:32 +0000 Subject: [PATCH 3/9] Use Path expressions for directories and file names --- .../debuginfo/DebugInfoProvider.java | 96 ++++++++-- .../objectfile/elf/dwarf/ClassEntry.java | 10 +- .../oracle/objectfile/elf/dwarf/DirEntry.java | 12 +- .../elf/dwarf/DwarfLineSectionImpl.java | 68 +++---- .../objectfile/elf/dwarf/DwarfSections.java | 43 +++-- .../objectfile/elf/dwarf/FileEntry.java | 32 ++-- .../oracle/objectfile/elf/dwarf/Range.java | 22 ++- .../svm/hosted/image/NativeBootImage.java | 171 ++++++++++++++---- 8 files changed, 326 insertions(+), 128 deletions(-) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index 13e8cb73dab3..5aff1370bb33 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -26,6 +26,7 @@ package com.oracle.objectfile.debuginfo; +import java.nio.file.Path; import java.util.List; /** @@ -45,26 +46,63 @@ interface DebugTypeInfo { * access details of a specific compiled method. */ interface DebugCodeInfo { + /** + * @return the name of the file containing a compiled + * method excluding any path + */ String fileName(); - + /** + * @return a relative path to the file containing a compiled + * method derived from its package name or null if the method + * is in the empty package + */ + Path filePath(); + /** + * @return the fully qualified name of the class owning the + * compiled method + */ String className(); - + /** + * @return the name of the compiled method including + * signature + */ String methodName(); - + /** + * @return the lowest address containing code generated for + * the method represented as an offset into the code segment + */ int addressLo(); - + /** + * @return the first address above the code generated for + * the method represented as an offset into the code segment + */ int addressHi(); - + /** + * @return the starting line number for the method + */ int line(); - + /** + * @return a provider detailing line numbers + * addresses within the compiled method + */ DebugLineInfoProvider lineInfoProvider(); - + /** + * @return a string identifying the method parameters + */ String paramNames(); - + /** + * @return a string identifying the method return type + */ String returnTypeName(); - + /** + * @return the size of the method frame between prologue + * and epilogue + */ int getFrameSize(); - + /** + * @return a list of positions at which the stack is extended + * to a full frame or torn down to an empty frame + */ List getFrameSizeChanges(); } @@ -75,19 +113,45 @@ interface DebugDataInfo { } /** - * access details of a specific outer or inlined method at a given line number. + * access details of code generated for a specific outer + * or inlined method at a given line number. */ interface DebugLineInfo { + /** + * @return the name of the file containing the outer + * or inlined method excluding any path + */ String fileName(); - + /** + * @return a relative path to the file containing the outer + * or inlined method derived from its package name or null + * if the method is in the empty package + */ + Path filePath(); + /** + * @return the fully qualified name of the class owning the + * outer or inlined method + */ String className(); - + /** + * @return the name of the outer or inlined method including signature + */ String methodName(); - + /** + * @return the lowest address containing code generated for + * an outer or inlined code segment reported at this line + * represented as an offset into the code segment + */ int addressLo(); - + /** + * @return the first address above the code generated for + * an outer or inlined code segment reported at this line + * represented as an offset into the code segment + */ int addressHi(); - + /** + * @return the line number for the outer or inlined segment + */ int line(); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java index 0ff7290cb43f..6d8714c5ae38 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java @@ -106,7 +106,7 @@ public ClassEntry(String className, FileEntry fileEntry) { this.localDirsIndex = new HashMap<>(); localFiles.add(fileEntry); localFilesIndex.put(fileEntry, localFiles.size()); - DirEntry dirEntry = fileEntry.dirEntry; + DirEntry dirEntry = fileEntry.getDirEntry(); if (dirEntry != null) { localDirs.add(dirEntry); localDirsIndex.put(dirEntry, localDirs.size()); @@ -144,7 +144,7 @@ void addSubRange(Range subrange, FileEntry subFileEntry) { localFiles.add(subFileEntry); localFilesIndex.put(subFileEntry, localFiles.size()); } - DirEntry dirEntry = subFileEntry.dirEntry; + DirEntry dirEntry = subFileEntry.getDirEntry(); if (dirEntry != null && localDirsIndex.get(dirEntry) == null) { localDirs.add(dirEntry); localDirsIndex.put(dirEntry, localDirs.size()); @@ -167,8 +167,12 @@ String getFileName() { return fileEntry.getFileName(); } + String getFullFileName() { + return fileEntry.getFullName(); + } + String getDirName() { - return fileEntry.getDirName(); + return fileEntry.getPathName(); } void setCUIndex(int cuIndex) { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java index dde46b4b8828..c033986b8be6 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java @@ -26,6 +26,8 @@ package com.oracle.objectfile.elf.dwarf; +import java.nio.file.Path; + /** * Tracks the directory associated with one or * more source files. @@ -38,13 +40,17 @@ * per file. */ public class DirEntry { - private String path; + private Path path; - public DirEntry(String path) { + public DirEntry(Path path) { this.path = path; } - public String getPath() { + public Path getPath() { return path; } + + public String getPathString() { + return path.toString(); + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java index d19ce896362d..10305904c0ea 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java @@ -147,17 +147,19 @@ public void createContent() { */ int pos = 0; for (ClassEntry classEntry : getPrimaryClasses()) { - int startPos = pos; - classEntry.setLineIndex(startPos); - int headerSize = headerSize(); - int dirTableSize = computeDirTableSize(classEntry); - int fileTableSize = computeFileTableSize(classEntry); - int prologueSize = headerSize + dirTableSize + fileTableSize; - classEntry.setLinePrologueSize(prologueSize); - int lineNumberTableSize = computeLineNUmberTableSize(classEntry); - int totalSize = prologueSize + lineNumberTableSize; - classEntry.setTotalSize(totalSize); - pos += totalSize; + if (classEntry.getFileName().length() != 0) { + int startPos = pos; + classEntry.setLineIndex(startPos); + int headerSize = headerSize(); + int dirTableSize = computeDirTableSize(classEntry); + int fileTableSize = computeFileTableSize(classEntry); + int prologueSize = headerSize + dirTableSize + fileTableSize; + classEntry.setLinePrologueSize(prologueSize); + int lineNumberTableSize = computeLineNUmberTableSize(classEntry); + int totalSize = prologueSize + lineNumberTableSize; + classEntry.setTotalSize(totalSize); + pos += totalSize; + } } byte[] buffer = new byte[pos]; super.setContent(buffer); @@ -193,7 +195,7 @@ public int computeDirTableSize(ClassEntry classEntry) { */ int dirSize = 0; for (DirEntry dir : classEntry.getLocalDirs()) { - dirSize += dir.getPath().length() + 1; + dirSize += dir.getPathString().length() + 1; } /* * allow for separator nul @@ -217,10 +219,10 @@ public int computeFileTableSize(ClassEntry classEntry) { /* * we want the file base name excluding path */ - String baseName = localEntry.getBaseName(); + String baseName = localEntry.getFileName(); int length = baseName.length(); fileSize += length + 1; - DirEntry dirEntry = localEntry.dirEntry; + DirEntry dirEntry = localEntry.getDirEntry(); int idx = classEntry.localDirsIdx(dirEntry); fileSize += putULEB(idx, scratch, 0); /* @@ -270,21 +272,23 @@ public void writeContent() { debug(" [0x%08x] DEBUG_LINE\n", pos); for (ClassEntry classEntry : getPrimaryClasses()) { - int startPos = pos; - assert classEntry.getLineIndex() == startPos; - debug(" [0x%08x] Compile Unit for %s\n", pos, classEntry.getFileName()); - pos = writeHeader(classEntry, buffer, pos); - debug(" [0x%08x] headerSize = 0x%08x\n", pos, pos - startPos); - int dirTablePos = pos; - pos = writeDirTable(classEntry, buffer, pos); - debug(" [0x%08x] dirTableSize = 0x%08x\n", pos, pos - dirTablePos); - int fileTablePos = pos; - pos = writeFileTable(classEntry, buffer, pos); - debug(" [0x%08x] fileTableSize = 0x%08x\n", pos, pos - fileTablePos); - int lineNumberTablePos = pos; - pos = writeLineNumberTable(classEntry, buffer, pos); - debug(" [0x%08x] lineNumberTableSize = 0x%x\n", pos, pos - lineNumberTablePos); - debug(" [0x%08x] size = 0x%x\n", pos, pos - startPos); + if (classEntry.getFileName().length() != 0) { + int startPos = pos; + assert classEntry.getLineIndex() == startPos; + debug(" [0x%08x] Compile Unit for %s\n", pos, classEntry.getFileName()); + pos = writeHeader(classEntry, buffer, pos); + debug(" [0x%08x] headerSize = 0x%08x\n", pos, pos - startPos); + int dirTablePos = pos; + pos = writeDirTable(classEntry, buffer, pos); + debug(" [0x%08x] dirTableSize = 0x%08x\n", pos, pos - dirTablePos); + int fileTablePos = pos; + pos = writeFileTable(classEntry, buffer, pos); + debug(" [0x%08x] fileTableSize = 0x%08x\n", pos, pos - fileTablePos); + int lineNumberTablePos = pos; + pos = writeLineNumberTable(classEntry, buffer, pos); + debug(" [0x%08x] lineNumberTableSize = 0x%x\n", pos, pos - lineNumberTablePos); + debug(" [0x%08x] size = 0x%x\n", pos, pos - startPos); + } } assert pos == buffer.length; } @@ -367,7 +371,7 @@ public int writeDirTable(ClassEntry classEntry, byte[] buffer, int p) { * write nul terminated string text. */ debug(" [0x%08x] %-4d %s\n", pos, dirIdx, dir.getPath()); - pos = putAsciiStringBytes(dir.getPath(), buffer, pos); + pos = putAsciiStringBytes(dir.getPathString(), buffer, pos); dirIdx++; } /* @@ -385,8 +389,8 @@ public int writeFileTable(ClassEntry classEntry, byte[] buffer, int p) { /* * we need the file name minus path, the associated dir index, and 0 for time stamps */ - String baseName = localEntry.getBaseName(); - DirEntry dirEntry = localEntry.dirEntry; + String baseName = localEntry.getFileName(); + DirEntry dirEntry = localEntry.getDirEntry(); int dirIdx = classEntry.localDirsIdx(dirEntry); debug(" [0x%08x] %-5d %-5d %s\n", pos, fileIdx, dirIdx, baseName); pos = putAsciiStringBytes(baseName, buffer, pos); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java index afd996cda14d..42df6ae317cb 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java @@ -36,6 +36,7 @@ import com.oracle.objectfile.elf.ELFMachine; import java.nio.ByteOrder; +import java.nio.file.Path; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -234,7 +235,7 @@ public DwarfLineSectionImpl getLineSectionImpl() { /** * index of already seen dirs. */ - private Map dirsIndex = new HashMap<>(); + private Map dirsIndex = new HashMap<>(); /* * The obvious traversal structure for debug records is: @@ -284,7 +285,7 @@ public DwarfLineSectionImpl getLineSectionImpl() { /** * index of already seen files. */ - private Map filesIndex = new HashMap<>(); + private Map filesIndex = new HashMap<>(); /** * indirects this call to the string table. @@ -342,14 +343,16 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { * primary file name and full method name need to be written to the debug_str section */ String fileName = debugCodeInfo.fileName(); - String className = debugCodeInfo.className(); + Path filePath = debugCodeInfo.filePath(); + // switch '$' in class names for '.' + String className = debugCodeInfo.className().replaceAll("\\$", "."); String methodName = debugCodeInfo.methodName(); String paramNames = debugCodeInfo.paramNames(); String returnTypeName = debugCodeInfo.returnTypeName(); int lo = debugCodeInfo.addressLo(); int hi = debugCodeInfo.addressHi(); int primaryLine = debugCodeInfo.line(); - Range primaryRange = new Range(fileName, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, primaryLine); + Range primaryRange = new Range(fileName, filePath, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, primaryLine); /* * System.out.format("arange: [0x%08x,0x%08x) %s %s::%s(%s) %s\n", lo, hi, * returnTypeName, className, methodName, paramNames, fileName); @@ -358,7 +361,9 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { addRange(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); for (DebugLineInfo debugLineInfo : debugCodeInfo.lineInfoProvider()) { String fileNameAtLine = debugLineInfo.fileName(); - String classNameAtLine = debugLineInfo.className(); + Path filePathAtLine = debugLineInfo.filePath(); + // switch '$' in class names for '.' + String classNameAtLine = debugLineInfo.className().replaceAll("\\$", "."); String methodNameAtLine = debugLineInfo.methodName(); int loAtLine = lo + debugLineInfo.addressLo(); int hiAtLine = lo + debugLineInfo.addressHi(); @@ -367,7 +372,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { * 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, classNameAtLine, methodNameAtLine, "", "", stringTable, loAtLine, hiAtLine, line, primaryRange); + Range subRange = new Range(fileNameAtLine, filePathAtLine, classNameAtLine, methodNameAtLine, "", "", stringTable, loAtLine, hiAtLine, line, primaryRange); addSubRange(primaryRange, subRange); } } @@ -401,18 +406,17 @@ public ClassEntry ensureClassEntry(Range range) { public FileEntry ensureFileEntry(Range range) { String fileName = range.getFileName(); + Path filePath = range.getFilePath(); + Path fileAsPath = range.getFileAsPath(); /* * ensure we have an entry */ - FileEntry fileEntry = filesIndex.get(fileName); + FileEntry fileEntry = filesIndex.get(fileAsPath); if (fileEntry == null) { - DirEntry dirEntry = ensureDirEntry(fileName); - String baseName = (dirEntry == null ? fileName : fileName.substring(dirEntry.getPath().length() + 1)); - fileEntry = new FileEntry(stringTable.uniqueDebugString(fileName), - stringTable.uniqueString(baseName), - dirEntry); + DirEntry dirEntry = ensureDirEntry(filePath); + fileEntry = new FileEntry(fileName, dirEntry); files.add(fileEntry); - filesIndex.put(fileName, fileEntry); + filesIndex.put(fileAsPath, fileEntry); /* * if this is a primary entry then add it to the primary list */ @@ -420,7 +424,7 @@ public FileEntry ensureFileEntry(Range range) { primaryFiles.add(fileEntry); } else { Range primaryRange = range.getPrimary(); - FileEntry primaryEntry = filesIndex.get(primaryRange.getFileName()); + FileEntry primaryEntry = filesIndex.get(primaryRange.getFileAsPath()); assert primaryEntry != null; } } @@ -447,18 +451,13 @@ public void addSubRange(Range primaryRange, Range subrange) { classEntry.addSubRange(subrange, subrangeEntry); } - public DirEntry ensureDirEntry(String file) { - int pathLength = file.lastIndexOf('/'); - if (pathLength < 0) { - /* - * no path/package means use dir entry 0 - */ + public DirEntry ensureDirEntry(Path filePath) { + if (filePath == null) { return null; } - String filePath = file.substring(0, pathLength); DirEntry dirEntry = dirsIndex.get(filePath); if (dirEntry == null) { - dirEntry = new DirEntry(stringTable.uniqueString(filePath)); + dirEntry = new DirEntry(filePath); dirsIndex.put(filePath, dirEntry); dirs.add(dirEntry); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java index a597a4df17bc..bc53ec654e64 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java @@ -30,34 +30,32 @@ * Tracks debug info associated with a Java source file. */ public class FileEntry { - /** - * The name of the associated file including path - */ private String fileName; - /** - * The name of the associated file excluding path - */ - private String baseName; - /** - * The directory entry associated with this file entry - */ - DirEntry dirEntry; + private DirEntry dirEntry; - public FileEntry(String fileName, String baseName, DirEntry dirEntry) { + public FileEntry(String fileName, DirEntry dirEntry) { this.fileName = fileName; - this.baseName = baseName; this.dirEntry = dirEntry; } + /** + * The name of the associated file excluding path elements. + */ public String getFileName() { return fileName; } - public String getBaseName() { - return baseName; + public String getPathName() { + return getDirEntry().getPathString(); } - String getDirName() { - return (dirEntry != null ? dirEntry.getPath() : ""); + public String getFullName() { + return getDirEntry().getPath().resolve(getFileName()).toString(); + } + /** + * The directory entry associated with this file entry. + */ + public DirEntry getDirEntry() { + return dirEntry; } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java index 41c78a176b2d..2608be71322b 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java @@ -26,6 +26,8 @@ package com.oracle.objectfile.elf.dwarf; +import java.nio.file.Path; +import java.nio.file.Paths; /** * Details of a specific address range in a compiled method * either a primary range identifying a whole method @@ -35,6 +37,7 @@ public class Range { private String fileName; + private Path filePath; private String className; private String methodName; private String paramNames; @@ -51,19 +54,20 @@ public class Range { /* * create a primary range */ - Range(String fileName, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line) { - this(fileName, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, line, null); + Range(String fileName, Path filePath, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line) { + this(fileName, filePath, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, line, null); } /* * create a primary or secondary range */ - Range(String fileName, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line, Range primary) { + Range(String fileName, Path filePath, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line, Range primary) { /* * currently file name and full method name need to go into the debug_str section * other strings just need to be deduplicated to save space */ this.fileName = stringTable.uniqueDebugString(fileName); + this.filePath = filePath; this.className = stringTable.uniqueString(className); this.methodName = stringTable.uniqueString(methodName); this.paramNames = stringTable.uniqueString(paramNames); @@ -91,6 +95,18 @@ public String getFileName() { return fileName; } + public Path getFilePath() { + return filePath; + } + + public Path getFileAsPath() { + if (filePath != null) { + return filePath.resolve(fileName); + } else { + return Paths.get(fileName); + } + } + public String getClassName() { return className; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java index 0d03818a228f..2df5cfd46148 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java @@ -39,6 +39,7 @@ import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.Collection; @@ -54,15 +55,16 @@ import java.util.Set; import java.util.stream.Collectors; +import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.util.ModuleSupport; import jdk.vm.ci.code.site.Mark; import jdk.vm.ci.meta.LineNumberTable; import org.graalvm.collections.Pair; import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.code.SourceMapping; import org.graalvm.compiler.core.common.CompressEncoding; -import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.Indent; @@ -1038,6 +1040,43 @@ public DebugDataInfo next() { } } + private static final String[] GRAAL_SRC_PACKAGE_PREFIXES = { + "org.graalvm", + "com.oracle.graal", + "com.oracle.objectfile", + "com.oracle.svm", + "com.oracle.truffle", + }; + + + /** + * compute a prefix to be added to the front of the file path for + * a class in order to locate it under a GRaal or JDK-specific + * search root + * @param packageName the name of the package the class belongs to + * or possibly an empty string if it is in the default package. + * @param moduleName the name of the module the class belongs to + * or possibly null or an empty string if it is not located + * in a module + * @return any required prefix or the empty string if no prefix is required + */ + private String getPathPrefix(String packageName, String moduleName) { + /* + * if we have a module name it is used as a prefix except + * when the class belongs to Graal itself. + */ + if (moduleName == null || moduleName.length() == 0) { + return ""; + } else { + for (String prefix : GRAAL_SRC_PACKAGE_PREFIXES) { + if (packageName.startsWith(prefix)) { + return ""; + } + } + return moduleName; + } + } + /** * implementation of the DebugCodeInfo API interface * that allows code info to be passed to an ObjectFile @@ -1055,32 +1094,51 @@ private class NativeImageDebugCodeInfo implements DebugCodeInfo { @Override public String fileName() { HostedType declaringClass = method.getDeclaringClass(); - String name = declaringClass.getSourceFileName(); - if (name != null) { - // the file name will not include any path - // use the package to create a path prefix - Package pkg = declaringClass.getJavaClass().getPackage(); - if (pkg != null) { - String prefix = pkg.getName(); - prefix = prefix.replace('.', '/'); - name = prefix + "/" + name; + String sourceFileName = declaringClass.getSourceFileName(); + + if (sourceFileName == null) { + String className = declaringClass.getJavaClass().getName(); + int idx = className.lastIndexOf('.'); + if (idx > 0) { + // strip off package prefix + className = className.substring(idx + 1); } - } else { - // build file name from the class name which includes the package - name = className(); - // try to map inner classes back to their parent class's file - int idx = name.indexOf('$'); + idx = className.indexOf('$'); if (idx == 0) { // name is $XXX so cannot associate with a file - return ""; - } - if (idx > 0) { - // name is XXX$YYY so use outer class to derive file name - name = name.substring(0, idx); + // create a path with an empty name + sourceFileName = ""; + } else { + if (idx > 0) { + // name is XXX$YYY so use outer class to derive file name + className = className.substring(0, idx); + } + sourceFileName = className + ".java"; } - name = name.replace('.', '/') + ".java"; } - return name; + + return sourceFileName; + } + @Override + public Path filePath() { + HostedType declaringClass = method.getDeclaringClass(); + Class javaClass = declaringClass.getJavaClass(); + Package pkg = javaClass.getPackage(); + String packageName = (pkg != null ? pkg.getName() : ""); + String module = ModuleSupport.getModuleName(javaClass); + if (packageName.length() != 0) { + String prefix = getPathPrefix(packageName, module); + /* + * use the package name as a path to the file + * for jdk11 classes we assume that the path includes + * the module name then the package name components + * for jdk8 classes this will just collapse to + * the sequence of package name elements + */ + return Paths.get(prefix, pkg.getName().split("\\.")); + } else { + return null; + } } @Override @@ -1124,7 +1182,7 @@ public int line() { @Override public DebugInfoProvider.DebugLineInfoProvider lineInfoProvider() { - if (fileName().length() == 0) { + if (fileName().toString().length() == 0) { return () -> new Iterator() { @Override public boolean hasNext() { @@ -1199,17 +1257,66 @@ private class NativeImageDebugLineInfo implements DebugLineInfo { @Override public String fileName() { - String name = className(); - int idx = name.indexOf('$'); - if (idx == 0) { - // name is $XXX so cannot associate with a file - return ""; + ResolvedJavaType declaringClass = method.getDeclaringClass(); + String sourceFileName = declaringClass.getSourceFileName(); + + if (sourceFileName == null) { + String className = declaringClass.getName(); + int idx = className.lastIndexOf('.'); + if (idx > 0) { + // strip off package prefix + className = className.substring(idx + 1); + } + idx = className.indexOf('$'); + if (idx == 0) { + // name is $XXX so cannot associate with a file + // create a path with an empty name + sourceFileName = ""; + } else { + if (idx > 0) { + // name is XXX$YYY so use outer class to derive file name + className = className.substring(0, idx); + } + sourceFileName = className + ".java"; + } } - if (idx > 0) { - // name is XXX$YYY so use outer class to derive file name - name = name.substring(0, idx); + + return sourceFileName; + } + + public Path filePath() { + ResolvedJavaType declaringClass = (method.getDeclaringClass()); + if (declaringClass instanceof OriginalClassProvider) { + Class javaClass = ((OriginalClassProvider) declaringClass).getJavaClass(); + Package pkg = javaClass.getPackage(); + String packageName = (pkg != null ? pkg.getName() : ""); + String module = ModuleSupport.getModuleName(javaClass); + if (packageName.length() != 0) { + String prefix = getPathPrefix(packageName, module); + /* + * use the package name as a path to the file + * + * for jdk11 classes we assume that the path includes + * the module name then the package name components + * + * for jdk8 classes this will just collapse to + * the sequence of package name components + */ + return Paths.get(prefix, pkg.getName().split("\\.")); + } else { + return null; + } + } else { + // use the class name to generate a path + String name = className(); + int idx = name.lastIndexOf('.'); + if (idx > 0) { + name = name.substring(0, idx); + return Paths.get("", name.split("\\.")); + } else { + return null; + } } - return name.replace('.', '/') + ".java"; } @Override From 53278b9babb1b18ffe927b1adbfbc73c7b3dddf1 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Mon, 24 Feb 2020 17:55:22 +0000 Subject: [PATCH 4/9] Implement source file cache --- .../svm/hosted/image/NativeBootImage.java | 160 ++-------- .../image/sources/ApplicationSourceCache.java | 93 ++++++ .../image/sources/GraalVMSourceCache.java | 112 +++++++ .../hosted/image/sources/JDKSourceCache.java | 71 +++++ .../svm/hosted/image/sources/SourceCache.java | 299 ++++++++++++++++++ .../hosted/image/sources/SourceCacheType.java | 33 ++ .../hosted/image/sources/SourceManager.java | 267 ++++++++++++++++ substratevm/write_gdbsourcepath | 193 ----------- 8 files changed, 908 insertions(+), 320 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCacheType.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java delete mode 100644 substratevm/write_gdbsourcepath diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java index 2df5cfd46148..17c611e6c774 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java @@ -39,7 +39,6 @@ import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.Collection; @@ -55,10 +54,9 @@ import java.util.Set; import java.util.stream.Collectors; -import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.svm.core.option.HostedOptionValues; +import com.oracle.svm.hosted.image.sources.SourceManager; import com.oracle.svm.hosted.meta.HostedType; -import com.oracle.svm.util.ModuleSupport; import jdk.vm.ci.code.site.Mark; import jdk.vm.ci.meta.LineNumberTable; import org.graalvm.collections.Pair; @@ -476,6 +474,7 @@ public void build(DebugContext debug, ImageHeapLayouter layouter) { // if we have constructed any debug info then // give the object file a chance to install it if (SubstrateOptions.GenerateDebugInfo.getValue(HostedOptionValues.singleton()) > 0) { + ImageSingletons.add(SourceManager.class, new SourceManager()); DebugInfoProvider provider = new NativeImageDebugInfoProvider(codeCache, heap); objectFile.installDebugInfo(provider); } @@ -1049,34 +1048,6 @@ public DebugDataInfo next() { }; - /** - * compute a prefix to be added to the front of the file path for - * a class in order to locate it under a GRaal or JDK-specific - * search root - * @param packageName the name of the package the class belongs to - * or possibly an empty string if it is in the default package. - * @param moduleName the name of the module the class belongs to - * or possibly null or an empty string if it is not located - * in a module - * @return any required prefix or the empty string if no prefix is required - */ - private String getPathPrefix(String packageName, String moduleName) { - /* - * if we have a module name it is used as a prefix except - * when the class belongs to Graal itself. - */ - if (moduleName == null || moduleName.length() == 0) { - return ""; - } else { - for (String prefix : GRAAL_SRC_PACKAGE_PREFIXES) { - if (packageName.startsWith(prefix)) { - return ""; - } - } - return moduleName; - } - } - /** * implementation of the DebugCodeInfo API interface * that allows code info to be passed to an ObjectFile @@ -1085,60 +1056,35 @@ private String getPathPrefix(String packageName, String moduleName) { private class NativeImageDebugCodeInfo implements DebugCodeInfo { private final HostedMethod method; private final CompilationResult compilation; + private Path fullFilePath; NativeImageDebugCodeInfo(HostedMethod method, CompilationResult compilation) { this.method = method; this.compilation = compilation; + this.fullFilePath = null; } @Override public String fileName() { - HostedType declaringClass = method.getDeclaringClass(); - String sourceFileName = declaringClass.getSourceFileName(); - - if (sourceFileName == null) { - String className = declaringClass.getJavaClass().getName(); - int idx = className.lastIndexOf('.'); - if (idx > 0) { - // strip off package prefix - className = className.substring(idx + 1); - } - idx = className.indexOf('$'); - if (idx == 0) { - // name is $XXX so cannot associate with a file - // create a path with an empty name - sourceFileName = ""; - } else { - if (idx > 0) { - // name is XXX$YYY so use outer class to derive file name - className = className.substring(0, idx); - } - sourceFileName = className + ".java"; - } + if (fullFilePath == null) { + HostedType declaringClass = method.getDeclaringClass(); + fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass); } - - return sourceFileName; + if (fullFilePath != null) { + return fullFilePath.getFileName().toString(); + } + return null; } @Override public Path filePath() { - HostedType declaringClass = method.getDeclaringClass(); - Class javaClass = declaringClass.getJavaClass(); - Package pkg = javaClass.getPackage(); - String packageName = (pkg != null ? pkg.getName() : ""); - String module = ModuleSupport.getModuleName(javaClass); - if (packageName.length() != 0) { - String prefix = getPathPrefix(packageName, module); - /* - * use the package name as a path to the file - * for jdk11 classes we assume that the path includes - * the module name then the package name components - * for jdk8 classes this will just collapse to - * the sequence of package name elements - */ - return Paths.get(prefix, pkg.getName().split("\\.")); - } else { - return null; + if (fullFilePath == null) { + HostedType declaringClass = method.getDeclaringClass(); + fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass); + } + if (fullFilePath != null) { + return fullFilePath.getParent(); } + return null; } @Override @@ -1245,6 +1191,7 @@ private class NativeImageDebugLineInfo implements DebugLineInfo { private final ResolvedJavaMethod method; private final int lo; private final int hi; + private Path fullFilePath = null; NativeImageDebugLineInfo(SourceMapping sourceMapping) { NodeSourcePosition position = sourceMapping.getSourcePosition(); @@ -1257,66 +1204,25 @@ private class NativeImageDebugLineInfo implements DebugLineInfo { @Override public String fileName() { - ResolvedJavaType declaringClass = method.getDeclaringClass(); - String sourceFileName = declaringClass.getSourceFileName(); - - if (sourceFileName == null) { - String className = declaringClass.getName(); - int idx = className.lastIndexOf('.'); - if (idx > 0) { - // strip off package prefix - className = className.substring(idx + 1); - } - idx = className.indexOf('$'); - if (idx == 0) { - // name is $XXX so cannot associate with a file - // create a path with an empty name - sourceFileName = ""; - } else { - if (idx > 0) { - // name is XXX$YYY so use outer class to derive file name - className = className.substring(0, idx); - } - sourceFileName = className + ".java"; - } + if (fullFilePath == null) { + ResolvedJavaType declaringClass = method.getDeclaringClass(); + fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass); } - - return sourceFileName; + if (fullFilePath != null) { + return fullFilePath.getFileName().toString(); + } + return null; } public Path filePath() { - ResolvedJavaType declaringClass = (method.getDeclaringClass()); - if (declaringClass instanceof OriginalClassProvider) { - Class javaClass = ((OriginalClassProvider) declaringClass).getJavaClass(); - Package pkg = javaClass.getPackage(); - String packageName = (pkg != null ? pkg.getName() : ""); - String module = ModuleSupport.getModuleName(javaClass); - if (packageName.length() != 0) { - String prefix = getPathPrefix(packageName, module); - /* - * use the package name as a path to the file - * - * for jdk11 classes we assume that the path includes - * the module name then the package name components - * - * for jdk8 classes this will just collapse to - * the sequence of package name components - */ - return Paths.get(prefix, pkg.getName().split("\\.")); - } else { - return null; - } - } else { - // use the class name to generate a path - String name = className(); - int idx = name.lastIndexOf('.'); - if (idx > 0) { - name = name.substring(0, idx); - return Paths.get("", name.split("\\.")); - } else { - return null; - } + if (fullFilePath == null) { + ResolvedJavaType declaringClass = method.getDeclaringClass(); + fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass); + } + if (fullFilePath != null) { + return fullFilePath.getParent(); } + return null; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java new file mode 100644 index 000000000000..1a9ebc2304fa --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.svm.hosted.image.sources; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class ApplicationSourceCache extends SourceCache { + /** + * create an application source cache + */ + protected ApplicationSourceCache() { + super(SourceCache.APPLICATION_CACHE_KEY); + initSrcRoots(); + } + + private void initSrcRoots() { + String javaClassPath = System.getProperty(JAVA_CLASSPATH_PROP); + assert javaClassPath != null; + String[] classPathEntries = javaClassPath.split(File.pathSeparator); + /* add dirs or jars found in the classpath */ + for (String classPathEntry : classPathEntries) { + Path entryPath = Paths.get(classPathEntry); + String fileNameString = entryPath.getFileName().toString(); + if (fileNameString.endsWith(".jar")) { + // application jar /path/to/xxx.jar should have + // sources /path/to/xxx-sources.jar + int length = fileNameString.length(); + String srcFileNameString = fileNameString.substring(0, length - 4) + "-sources.zip"; + Path srcPath = entryPath.getParent().resolve(srcFileNameString); + if (srcPath.toFile().exists()) { + try { + FileSystem fileSystem = FileSystems.newFileSystem(srcPath, null); + for (Path root : fileSystem.getRootDirectories()) { + srcRoots.add(root); + } + } catch (IOException ioe) { + /* ignore this entry */ + } catch (FileSystemNotFoundException fnfe) { + /* ignore this entry */ + } + } + } else { + /* + * for dir entries ending in classes or target/classes + * look for a parallel src tree + */ + if (entryPath.endsWith("classes")) { + Path parent = entryPath.getParent(); + if (parent.endsWith("target")) { + parent = parent.getParent(); + } + Path srcPath = (parent.resolve("src")); + File file = srcPath.toFile(); + if (file.exists() && file.isDirectory()) { + srcRoots.add(srcPath); + } + } + } + } + /* add the current working directory as a path of last resort */ + srcRoots.add(Paths.get(".")); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java new file mode 100644 index 000000000000..da8bfd6cde07 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.svm.hosted.image.sources; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static com.oracle.svm.hosted.image.sources.SourceManager.GRAALVM_SRC_PACKAGE_PREFIXES; +public class GraalVMSourceCache extends SourceCache { + /** + * create a GraalVM source cache + */ + protected GraalVMSourceCache() { + super(SourceCache.GRAALVM_CACHE_KEY); + initSrcRoots(); + } + + private static final String JAVA_CLASSPATH_PROP = "java.class.path"; + + private void initSrcRoots() { + String javaClassPath = System.getProperty(JAVA_CLASSPATH_PROP); + assert javaClassPath != null; + String[] classPathEntries = javaClassPath.split(File.pathSeparator); + for (String classPathEntry : classPathEntries) { + Path entryPath = Paths.get(classPathEntry); + String fileNameString = entryPath.getFileName().toString(); + if (fileNameString.endsWith(".jar")) { + // GraalVM jar /path/to/xxx.jar should have + // sources /path/to/xxx.src.zip.jar + int length = fileNameString.length(); + String srcFileNameString = fileNameString.substring(0, length - 3) + "src.zip"; + Path srcPath = entryPath.getParent().resolve(srcFileNameString); + if (srcPath.toFile().exists()) { + try { + FileSystem fileSystem = FileSystems.newFileSystem(srcPath, null); + for (Path root : fileSystem.getRootDirectories()) { + if (filterSrcRoot(root)) { + srcRoots.add(root); + } + } + } catch (IOException ioe) { + /* ignore this entry */ + } catch (FileSystemNotFoundException fnfe) { + /* ignore this entry */ + } + } + } else { + /* graal classpath dir entries should have a src and/or src_gen subdirectory */ + Path srcPath = entryPath.resolve("src"); + if (filterSrcRoot(srcPath)) { + srcRoots.add(srcPath); + } + srcPath = entryPath.resolve("src_gen"); + if (filterSrcRoot(srcPath)) { + srcRoots.add(srcPath); + } + } + } + } + /** + * Ensure that the supplied root dir contains + * at least one subdirectory that matches one + * of the expected Graal package dir hierarchies. + * + * @param root A root path under which to locate + * the desired subdirectory + * @return true if a + */ + private boolean filterSrcRoot(Path root) { + String separator = root.getFileSystem().getSeparator(); + + /* if any of the graal paths exist accept this root */ + for (String prefix : GRAALVM_SRC_PACKAGE_PREFIXES) { + String subDir = prefix.replaceAll("\\.", separator); + if (Files.isDirectory(root.resolve(subDir))) { + return true; + } + } + + return false; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java new file mode 100644 index 000000000000..e53d75f75c9b --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.svm.hosted.image.sources; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class JDKSourceCache extends SourceCache { + /** + * create a JDK runtime class source cache. + */ + protected JDKSourceCache() { + super(SourceCache.JDK_CACHE_KEY); + initSrcRoots(); + } + + private void initSrcRoots() { + String javaHome = System.getProperty(JAVA_HOME_PROP); + assert javaHome != null; + Path javaHomePath = Paths.get("", javaHome); + Path srcZipPath; + String javaSpecVersion = System.getProperty(JAVA_SPEC_VERSION_PROP); + if (javaSpecVersion.equals("1.8")) { + srcZipPath = javaHomePath.resolve("src.zip"); + } else { + assert javaSpecVersion.matches("[1-9][0-9]"); + srcZipPath = javaHomePath.resolve("lib").resolve("src.zip"); + } + if (srcZipPath.toFile().exists()) { + try { + FileSystem srcFileSystem = FileSystems.newFileSystem(srcZipPath, null); + for (Path root : srcFileSystem.getRootDirectories()) { + srcRoots.add(root); + } + } catch (IOException ioe) { + /* ignore this entry */ + } catch (FileSystemNotFoundException fnfe) { + /* ignore this entry */ + } + } + } +} + diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java new file mode 100644 index 000000000000..91384cfcb553 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2020, 2020, 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. 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.svm.hosted.image.sources; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.FileTime; +import java.util.ArrayList; +import java.util.List; +/** + * An abstract cache manager for some subspace of the + * JDK, GraalVM or application source file space. This class + * implements core behaviours that manage a cache of source + * files in a specific subdirectory of the local sources + * directory. It allows source files to be located + * when present in the local cache or cached when not + * already present. Subclasses are responsible for providing + * behaviours that identify an original source for addition + * to the cache and for verifying that a cached file is not + * out of date with respect to its original. + */ + +public abstract class SourceCache { + + /* + * properties needed to locate relevant JDK and app source roots + */ + protected static final String JAVA_CLASSPATH_PROP = "java.class.path"; + protected static final String JAVA_HOME_PROP = "java.home"; + protected static final String JAVA_SPEC_VERSION_PROP = "java.specification.version"; + /** + * A list of root directories which may contain source files + * from which this cache can be populated + */ + protected List srcRoots; + + /** + * Create a source cache with a specific base type. + * @param key a String identifying the subdir under + * which sources should be cached which should also + * match the type of content being cached + */ + protected SourceCache(String key) { + basePath = Paths.get(SOURCE_CACHE_ROOT_DIR, key); + srcRoots = new ArrayList<>(); + + } + + /** + * A local directory serving as the root for all + * source trees maintained by the different + * available source caches. + */ + private static final String SOURCE_CACHE_ROOT_DIR = "sources"; + /** + * The top level path relative to the root directory + * under which files belonging to this specific cache + * are located. + */ + private Path basePath; + /** + * JDK runtime code sources are cached using this key as a + * leading path prefix with a module name as a sub-path + * prefix when we have a modular JDK. + * + * For example, the full file path to a file under the cache + * root directory might be jdk/java/lang/Throwable.java on jdk8 or + * jdk/java.base/java/lang/Throwable.java on jdk11 + */ + protected static final String JDK_CACHE_KEY = "jdk"; + /** + * GraalVM code sources are cached using this key as a + * leading path prefix with an enclosing package name + * and the name src or src_gen forming a sub-path prefix. + * + * For example, the full file path to a file under the cache + * root directory might be + * graal/com/oracle/svm/core/Isolates.java + * or + * graal/org/graalvm/compiler/core/phases/LowTier_OptionDescriptors.java + */ + protected static final String GRAALVM_CACHE_KEY = "graal"; + /** + * Application code sources are cached using this key as + * a leading path prefix with a name or sequence of + * names derived from a classpath jar or dir entry + * employed as a sub-path prefix. + * + * For example, the full file path to a file under the cache + * root directory might be + * src/Hello.java + * or + * src/hello/impl/HelloImpl.java + * or + * src/hibernate-core-5.4.4.Final/org/hibernate/annotations/Entity.java + */ + protected static final String APPLICATION_CACHE_KEY = "src"; + /** + * Cache the source file identified by the supplied prototype + * path if a legitimate candidate for inclusion in this cache + * can be identified and is not yet included in the cache or + * alternatively identify and validate any existing candidate + * cache entry to ensure it is not out of date refreshing it + * if need be. + * + * @param filePath a prototype path for a file to be included + * in the cache derived from the name of some associated class. + * @return a path identifying the cached file or null + * if the candidate cannot be found. + */ + public Path resolve(Path filePath) { + File cachedFile = findCandidate(filePath); + if (cachedFile == null) { + return tryCacheFile(filePath); + } else { + return checkCacheFile(filePath); + } + } + + /** + * Given a prototype path for a file to be resolved + * return a File identifying a cached candidate for + * for that Path or null if no cached candidate exists. + * @param filePath a prototype path for a file to be included + * in the cache derived from the name of some associated class. + * @return a File identifying a cached candidate or null. + */ + public File findCandidate(Path filePath) { + /* + * JDK source candidates are stored in the src.zip file + * using the path we are being asked for. A cached version + * should exist under this cache's root using that same + * path. + */ + File file = cachedFile(filePath); + if (file.exists()) { + return file; + } + return null; + } + public Path tryCacheFile(Path filePath) { + for (Path root : srcRoots) { + Path targetPath = cachedPath(filePath); + Path sourcePath = extendPath(root, filePath); + try { + if (checkSourcePath(sourcePath)) { + ensureTargetDirs(targetPath.getParent()); + Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); + // return the original filePath + // we don't want the sources/jdk prefix to go into the debuginfo + return filePath; + } + } catch (IOException e) { + } + } + return null; + } + public Path checkCacheFile(Path filePath) { + for (Path root : srcRoots) { + Path targetPath = cachedPath(filePath); + Path sourcePath = extendPath(root, filePath); + try { + if (checkSourcePath(sourcePath)) { + FileTime sourceTime = Files.getLastModifiedTime(sourcePath); + FileTime destTime = Files.getLastModifiedTime(targetPath); + if (destTime.compareTo(sourceTime) < 0) { + try { + Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); + } catch (IOException e) { + return null; + } + } + return filePath; + } else { + /* delete the target file as it is out of date */ + targetPath.toFile().delete(); + } + } catch (IOException e) { + // hmm last modified time blew up? + return tryCacheFile(filePath); + } + } + return null; + } + /** + * Create and intialize the source cache used to locate and cache + * sources of a given type as determined by the supplied key. + * @param type an enum identifying both the type of Java sources + * cached by the returned cache and the subdir of the cached + * source subdirectory in which those sources are located. + * @return the desired source cache. + */ + public static SourceCache createSourceCache(SourceCacheType type) { + SourceCache sourceCache = null; + switch (type) { + case JDK: + sourceCache = new JDKSourceCache(); + break; + case GRAALVM: + sourceCache = new GraalVMSourceCache(); + break; + case APPLICATION: + sourceCache = new ApplicationSourceCache(); + break; + default: + assert false; + } + return sourceCache; + } + /** + * Extend a root path form one file system using a path potentially derived + * from another file system by converting he latter to a text string and + * replacing the file separator if necessary. + * @param root the path to be extended + * @param filePath the subpath to extend it with + * @return the extended path + */ + protected Path extendPath(Path root, Path filePath) { + String filePathString = filePath.toString(); + String fileSeparator = filePath.getFileSystem().getSeparator(); + String newSeparator = root.getFileSystem().getSeparator(); + if (!fileSeparator.equals(newSeparator)) { + filePathString = filePathString.replaceAll(fileSeparator, newSeparator); + } + return root.resolve(filePathString); + } + + /** + * convert a potential resolved candidate path to + * the corresponding local Path in this cache. + * @param candidate a resolved candidate path for + * some given resolution request + * @return the corresponding local Path + */ + protected Path cachedPath(Path candidate) { + return basePath.resolve(candidate); + } + /** + * convert a potential resolved candidate path to + * the corresponding local File in this cache. + * @param candidate a resolved candidate path for + * some given resolution request + * @return the corresponding local File + */ + protected File cachedFile(Path candidate) { + return cachedPath(candidate).toFile(); + } + /** + * indicate whether a source path identifies a fie in the associated file system + * @param sourcePath + * @return true if the path identifies a file or false if no such file can be found + * @throws IOException if there is some error in resolving the path + */ + private boolean checkSourcePath(Path sourcePath) throws IOException { + return Files.isRegularFile(sourcePath); + } + /** + * ensure the directory hierarchy for a path exists + * creating any missing directories if needed + * @param targetDir a path to the desired directory + * @throws IOException if it is not possible to create + * one or more directories in the path + */ + private void ensureTargetDirs(Path targetDir) throws IOException { + if (targetDir != null) { + File targetFile = targetDir.toFile(); + if (!targetFile.exists()) { + targetDir.toFile().mkdirs(); + } + } + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCacheType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCacheType.java new file mode 100644 index 000000000000..10863a5e572c --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCacheType.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.svm.hosted.image.sources; + +public enum SourceCacheType { + JDK, + GRAALVM, + APPLICATION +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java new file mode 100644 index 000000000000..459acebe6a08 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.svm.hosted.image.sources; + +import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; +import com.oracle.svm.util.ModuleSupport; +import jdk.vm.ci.meta.ResolvedJavaType; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; + +/** + * A singleton class responsible for locating source files + * for classes included in a native image and copying them + * into the local sources. + */ +public class SourceManager { + /** + * Find and cache a source file for a give Java class and + * return a Path to the file relative to the source. + * @param resolvedType the Java type whose source file + * should be located and cached + * @return a path identifying the location of a successfully + * cached file for inclusion in the generated debug info or + * null if a source file cannot be found or cached. + */ + public Path findAndCacheSource(ResolvedJavaType resolvedType) { + Path path = null; + String fileName = computeBaseName(resolvedType); + /* + * null for the name means this class + * will not have a source so we skip on that + */ + if (fileName != null) { + /* + * we can only provide sources + * for known classes and interfaces + */ + if (resolvedType.isInstanceClass() || resolvedType.isInterface()) { + /* + * if we have an OriginalClassProvider we + * can use the underlying Java class + * to provide the details we need to locate + * a source + */ + if (resolvedType instanceof OriginalClassProvider) { + Class javaClass = ((OriginalClassProvider) resolvedType).getJavaClass(); + String packageName = computePackageName(javaClass); + SourceCacheType type = sourceCacheType(packageName, javaClass); + path = locateSource(fileName, packageName, type, javaClass); + } + /* + * if we could not locate a source via the cache + * then the fallback is to generate a path to the + * file based on the class name and let the + * user configure a path to the sources + */ + if (path == null) { + String name = resolvedType.toJavaName(); + int idx = name.lastIndexOf('.'); + if (idx >= 0 && idx < name.length() - 1) { + name = name.substring(0, idx); + path = Paths.get("", name.split("\\.")); + path = path.resolve(fileName); + } + } + } + } + return path; + } + + /** + * Construct the base file name for a resolved + * Java class excluding path elements using either + * the source name embedded in the class file or + * the class name itself. + * @param resolvedType the resolved java type whose + * source file name is required + * @return the file name or null if it the class cannot + * be associated with a source file + */ + private String computeBaseName(ResolvedJavaType resolvedType) { + String fileName = resolvedType.getSourceFileName(); + if (fileName == null) { + /* ok, try to construct it from the class name */ + fileName = resolvedType.toJavaName(); + int idx = fileName.lastIndexOf('.'); + if (idx > 0) { + // strip off package prefix + fileName = fileName.substring(idx + 1); + } + idx = fileName.indexOf('$'); + if (idx == 0) { + // name is $XXX so cannot associate with a file + // + fileName = null; + } else { + if (idx > 0) { + // name is XXX$YYY so use outer class to derive file name + fileName = fileName.substring(0, idx); + } + fileName = fileName + ".java"; + } + } + return fileName; + } + /** + * Construct the package name for a Java class or + * the empty String if it has no package. + * @param javaClass the java class whose package + * name is required + * @return the package name or the empty String + * if it has no package + */ + private String computePackageName(Class javaClass) { + Package pkg = javaClass.getPackage(); + return (pkg == null ? "" : pkg.getName()); + } + /** + * Construct the prototype name for a Java source file + * which can be used to resolve and cache an actual source + * file. + * @param fileName the base file name for the source file + * @param packageName the name of the package for the associated Java class + * @param type the type of cache in which to lookup or cache this class's source file + * @param javaClass the java class whose prototype name is required + * @return a protoype name for the source file + */ + private Path computePrototypeName(String fileName, String packageName, SourceCacheType type, Class javaClass) { + String prefix = ""; + if (type == SourceCacheType.JDK) { + /* JDK paths may require the module name as prefix */ + String moduleName = ModuleSupport.getModuleName(javaClass); + if (moduleName != null) { + prefix = moduleName; + } + } + if (packageName.length() == 0) { + return Paths.get("", fileName); + } else { + return Paths.get(prefix, packageName.split("\\.")).resolve(fileName); + } + } + /** + * A whitelist of packages prefixes used to + * pre-filter JDK runtime class lookups. + */ + public static final String[] JDK_SRC_PACKAGE_PREFIXES = { + "java.", + "jdk.", + "javax.", + "sun.", + "com.sun.", + "org.ietf.", + "org.jcp.", + "org.omg.", + "org.w3c.", + "org.xml", + }; + /** + * A whitelist of packages prefixes used to + * pre-filter GraalVM class lookups. + */ + public static final String[] GRAALVM_SRC_PACKAGE_PREFIXES = { + "com.oracle.graal.", + "com.oracle.objectfile.", + "com.oracle.svm.", + "com.oracle.truffle.", + "org.graalvm.", + }; + + /** + * A whitelist of packages prefixes used to + * pre-filter app class lookups which + * includes just the empty string because + * any package will do. + */ + private static final String[] APP_SRC_PACKAGE_PREFIXES = { + "", + }; + + /** + * Check a package name against a whitelist of acceptable packages. + * @param packageName the package name of the class to be checked + * @param whitelist a list of prefixes one of which may form + * the initial prefix of the package name being checked + * @return true if the package name matches an entry in the + * whitelist otherwise false + */ + private boolean whiteListPackage(String packageName, String[] whitelist) { + for (String prefix : whitelist) { + if (packageName.startsWith(prefix)) { + return true; + } + } + return false; + } + + /** + * Identify which type of source cache should be used + * to locate a given class's source code. + */ + private SourceCacheType sourceCacheType(String packageName, Class javaClass) { + if (whiteListPackage(packageName, JDK_SRC_PACKAGE_PREFIXES)) { + return SourceCacheType.JDK; + } + if (whiteListPackage(packageName, GRAALVM_SRC_PACKAGE_PREFIXES)) { + return SourceCacheType.GRAALVM; + } + return SourceCacheType.APPLICATION; + } + /** + * A map from each of the top level root keys to a + * cache that knows how to handle lookup and caching + * of the associated type of source file. + */ + private static HashMap caches = new HashMap<>(); + + /** + * Retrieve the source cache used to locate and cache sources + * of a given type as determined by the supplied key, creating + * and initializing it if it does not already exist. + * @param type an enum identifying the type of Java sources + * cached by the returned cache. + * @return the desired source cache. + */ + private SourceCache getOrCreateCache(SourceCacheType type) { + SourceCache sourceCache = caches.get(type); + if (sourceCache == null) { + sourceCache = SourceCache.createSourceCache(type); + caches.put(type, sourceCache); + } + return sourceCache; + } + + private Path locateSource(String fileName, String packagename, SourceCacheType type, Class javaClass) { + SourceCache cache = getOrCreateCache(type); + Path prototypeName = computePrototypeName(fileName, packagename, type, javaClass); + return cache.resolve(prototypeName); + } +} + diff --git a/substratevm/write_gdbsourcepath b/substratevm/write_gdbsourcepath deleted file mode 100644 index b211b305a543..000000000000 --- a/substratevm/write_gdbsourcepath +++ /dev/null @@ -1,193 +0,0 @@ -#!/bin/bash - - -function usage() -{ - echo "write_gdbsourcepath [-h | -v]" - echo "writes command to set source path to .gdbsourcepath" - echo "set GRAAL_SRC_ROOT to graal git repo root" - echo " defaults to .. when . = sdk, compile, substratevm, truffle" - echo "set GRAAL_JDK_SRC_ROOT to unzipped src.zip root" - echo " defaults to JAVA_HOME/src" - exit $1 -} - -function check_args() -{ - while [ $# -ge 1 ]; do - if [ "$1" == "-v" ]; then - VERBOSE=1 - shift - elif [ "$1" == "-h" ]; then - usage 0 - else - usage 1 - fi - done -} - -typeset -i VERBOSE -VERBOSE=0 - -# debug -function verbose() -{ - if [ $VERBOSE -eq 1 ]; then - echo $* - fi -} - -# check which java we are using and set up JAVA_VERSION -function check_java_version() -{ - JAVA_VERSION_STRING=$(java -version |& grep version | cut -d' ' -f3) - if [ "${JAVA_VERSION_STRING#\"1.8.}" != "${JAVA_VERSION_STRING}" ]; then - JAVA_VERSION=8 - elif [ "${JAVA_VERSION_STRING#\"9.}" != "${JAVA_VERSION_STRING}" ]; then - JAVA_VERSION=9 - elif [ "${JAVA_VERSION_STRING#\"11.}" != "${JAVA_VERSION_STRING}" ]; then - JAVA_VERSION=11 - elif [ "${JAVA_VERSION_STRING#\"14.}" != "${JAVA_VERSION_STRING}" ]; then - JAVA_VERSION=14 - else - echo "Unrecognized java version : $JAVA_VERSION_STRING" - exit 1 - fi -} - -# check for the required source trees and -# set up GRAAL_SRC_ROOT and GRAAL_JDK_SRC_ROOT -function check_source_dirs() -{ - if [ -z "$GRAAL_SRC_ROOT" ]; then - # see if we are in one of the Graal trees - if [ "${PWD#*/sdk}" != "$PWD" -o \ - "${PWD#*/compiler}" != "$PWD" -o \ - "${PWD#*/substratevm}" != "$PWD" -o \ - "${PWD#*/truffle}" != "$PWD" ]; then - GRAAL_SRC_ROOT=$(cd ..; pwd) - echo "defaulting GRAAL_SRC_ROOT to .. : $GRAAL_SRC_ROOT" - else - echo "Please set GRAAL_SRC_ROOT to git repo checkout dir" - exit 1 - fi - fi - - if [ -z "$GRAAL_JDK_SRC_ROOT" ]; then - if [ ! -z "$JAVA_HOME" ]; then - GRAAL_JDK_SRC_ROOT=$JAVA_HOME/src - echo "defaulting GRAAL_JDK_SRC_ROOT to JAVA_HOME : $GRAAL_JDK_SRC_ROOT" - else - JAVA_EXE=`which java` - GRAAL_JDK_SRC_ROOT=${JAVA_EXE%/bin/java}/src - fi - fi -} - -# add sources from a supplied graal source dir -function add_sources() -{ - # sanity check - if [ ! -d $1 ]; then - echo "hmm, was expecting a graal component directory, not this : $1" - fi - if [ ! -d $1/src ]; then - echo "hmm, was expecting to find a graal component source tree, not this : $1/src" - fi - root=$1 - for dir in $1/src/* - do - typeset -i ignore - ignore=0 - verbose "considering $dir" - if [ ! -d ${dir}/src ]; then - ignore=1 - else - # look for test or jdk in the trailing path - tail=${dir#$root} - if [ "${tail%*test}" != "$tail" -o \ - "${tail#*test}" != "$tail" ] ; then - # ignore test dirs - ignore=1 - elif [ "${tail#*jdk}" != "$tail" ]; then - # check for a specific jdk release - if [ "${tail#jdk.}" != "$tail" ]; then - # jdk. as part of a package name is ok - if ["${tail#jdk.}" != "$tail" ]; then - echo allow $dir - fi - ignore=0 - elif [ "${tail#*jdk}" != "${JAVA_VERSION}" ]; then - # jdk must match JAVA_VERSION - if [ "${dir#jdk.}" != "$dir" ]; then - echo disallow $dir - fi - ignore=1 - fi - fi - fi - if [ $ignore -eq 1 ] ; then - verbose "ignoring $dir" - else - verbose "including $dir" - SOURCEPATH=${SOURCEPATH}:$dir/src - fi - done -} - -# add sources from a supplied java source dir -function add_java_sources() -{ - # sanity check - if [ ! -d $1 ]; then - echo "hmm, was expecting to find a JDK source dir, not this : $1" - fi - SOURCEPATH=${SOURCEPATH}:$1 -} - -check_args $* - -check_java_version - -check_source_dirs - -GRAAL_SDK_SRC_ROOT=${GRAAL_SRC_ROOT}/sdk -GRAAL_COMPILER_SRC_ROOT=${GRAAL_SRC_ROOT}/compiler -GRAAL_SUBSTRATEVM_SRC_ROOT=${GRAAL_SRC_ROOT}/substratevm -GRAAL_TRUFFLE_SRC_ROOT=${GRAAL_SRC_ROOT}/truffle - -SOURCEPATH= - -if [ -d ${GRAAL_SDK_SRC_ROOT} ]; then - add_sources ${GRAAL_SDK_SRC_ROOT} -else - echo "Unable to find sdk sources in ${GRAAL_SDK_SRC_ROOT}" -fi -if [ -d ${GRAAL_COMPILER_SRC_ROOT} ]; then - add_sources ${GRAAL_COMPILER_SRC_ROOT} -else - echo "Unable to find compiler sources in ${GRAAL_COMPILER_SRC_ROOT}" -fi -if [ -d ${GRAAL_SUBSTRATEVM_SRC_ROOT} ]; then - add_sources ${GRAAL_SUBSTRATEVM_SRC_ROOT} -else - echo "Unable to find substratevm sources in ${GRAAL_SUBSTRATEVM_SRC_ROOT}" -fi -if [ -d ${GRAAL_TRUFFLE_SRC_ROOT} ]; then - add_sources ${GRAAL_TRUFFLE_SRC_ROOT} -else - echo "Unable to find truffle sources in ${GRAAL_TRUFFLE_SRC_ROOT}" -fi - -if [ -d ${GRAAL_JDK_SRC_ROOT} ]; then - add_java_sources ${GRAAL_JDK_SRC_ROOT} -else - echo "Unable to find JDK sources in ${GRAAL_JDK_SRC_ROOT}/src" - echo "unzip src.zip into \${GRAAL_JDK_SRC_ROOT}/src" -fi - -SOURCEPATH=${SOURCEPATH#:*} -echo "set directories $SOURCEPATH" > .gdbsourcepath -if [ $VERBOSE -gt 0 ]; then - cat .gdbsourcepath -fi From 4161ea3d06d8f9c91dded5625a23a7b6ba7bd945 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 25 Feb 2020 15:56:24 +0000 Subject: [PATCH 5/9] Modify debug info API to use streams instead of iterators --- substratevm/DEBUGINFO.md | 152 +++++++++++++----- .../debuginfo/DebugInfoProvider.java | 37 +---- .../objectfile/elf/dwarf/DwarfSections.java | 14 +- .../objectfile/elf/dwarf/StringEntry.java | 2 +- .../objectfile/elf/dwarf/StringTable.java | 2 +- .../svm/hosted/image/NativeBootImage.java | 77 ++------- 6 files changed, 139 insertions(+), 145 deletions(-) diff --git a/substratevm/DEBUGINFO.md b/substratevm/DEBUGINFO.md index 5b859cd3cd7a..58e8bd48b63e 100644 --- a/substratevm/DEBUGINFO.md +++ b/substratevm/DEBUGINFO.md @@ -2,11 +2,22 @@ Using the ptototype debug info feature -------------------------------------- To add debug info to a generated native image add flag --H:+TrackNodeSourcePosition to the native image command line. +-H:+GenerateDebugInfo to the native image command line. - mx native-image -H:+TrackNodeSourcePosition Hello.java + $ javac Hello.java + $ mx native-image -H:GenerateDebugInfo=1 Hello + +The resulting image should contain code (method) debug records in a +format gdb understands (VS support is still under development). + +The flag also enables caching of sources for JDK runtime classes, +GraalVM classes and application classes which can be located during +native image generation. The cache is created under local subdirectory +sources and can be used to configure source file search path roots for +the debugger. Files in the cache are located in a directory hierarchy +that matches the file path information included in the native image +debug records -The resulting image should contain code (method) debug symbols. What is currently implemented ----------------------------- @@ -25,50 +36,119 @@ compiled method. Identifying the location of source code --------------------------------------- -In order for gdb to be able to locate the source files for your app -methods, Graal methods and JDK runtime methods you need to provide gdb -with a list of source root dirs a 'set directories' command: +One goal of the implementation is to make it simple to configure your +debugger so that it can identify the relevant source file when it +stops during program execution. The native image generator tries to +achieve this by accumulating the relevant sources in a suitably +structured file cache. + +The native image generator uses different strategies to locate source +files for JDK runtime classes, GraalVM classses and application source +classes for inclusion in the local sources cache. It identifies which +strategy to use based on the package name of the class. So, for +example, packages starting with java.* or jdk.* are JDK classes; +packages starting with org.graal.* or com.oracle.svm.* are GraalVM +classes; any other packages are regarded as application classes. + +Sources for JDK runtime classes are retrieved from the src.zip found +in the JDK release used to run the native image generation process. +Retrieved files are cached under subdirectory sources/jdk, using the +module name (for JDK11) and package name of the associated class to +define the directory hierarchy in which the source is located. + +So, for example, on Linux the source for class java.util.HashMap will +be cached in file sources/jdk/java.base/java/util/HashMap.java. Debug +info records for this class and its methods will identify this source +file using the relative directory path java.base/java/util and file +name HashMap.java. On Windows things will be the same modulo use of +'\' rather than '/' as the file separator. + +Sources for GraalVM classes are retrieved from zip files or source +directories derived from entries in the classpath. Retrieved files are +cached under subdirectory sources/graal, using the package name of the +associated class to define the directory hierarchy in which the source +is located (e.g. class com.oracle.svm.core.VM has its source file +cached at sources/graal/com/oracle/svm/core/VM.java). + +The lookup scheme for cached GraalVM sources varies depending upon +what is found in each classpath entry. Given a jar file entry like +/path/to/foo.jar, the corresponding file /path/to/foo.src.zip is +considered as a candidate zip file system from which source files may +be extracted. When the entry specifies a dir like /path/to/bar then +directories /path/to/bar/src and /path/to/bar/src_gen are considered +as candidates. Candidates are skipped when i) the zip file or source +directory does not exist or ii) it does not contain at least one +subdirectory hierarchy that matches one of the the expected GraalVM +package hierarchies. + +Sources for application classes are retrieved from source jar files or +source directories derived from entries in the classpath. Retrieved +files are cached under subdirectory sources/src, using the package +name of the associated class to define the directory hierarchy in +which the source is located (e.g. class org.my.foo.Foo has its +source file cached as sources/src/org/my/foo/Foo.java). + +The lookup scheme for cached Application sources varies depending upon +what is found in each classpath entry. Given a jar file entry like +/path/to/foo.jar, the corresponding jar /path/to/foo-sources.jar is +considered as a candidate zip file system from which source files may +be extracted. When the entry specifies a dir like /path/to/bar/classes +or /path/to/bar/target/classes then directory /path/to/bar/src is +considered as a candidate. Finally, the current directory in which the +native image program is being run is also considered as a candidate. + +These lookup strategies are only provisional and may need extending in +future. Note however that it is possible to make missing sources +available by other means. One option is to unzip extra app source jars +or copying extra app source trees into the cache. Another is to +configure extra source search paths (see below). + +Configuring source paths in gdb +------------------------------- - (gdb) set directories /home/adinn/hello/src:/home/adinn/graal/sdk/src/org/graalvm.word/src:/home/adinn/graal/sdk/src/org.graalvm.options/src:... - -The argument is a comma separated list of source roots. It needs to -identify: +In order for gdb to be able to locate the source files for your app +classes, Graal classes and JDK runtime classes you need to provide gdb +with a list of source root dirs using the 'set directories' command: - - sources for your app - - sources under the Graal sdk, compiler, substratevm and truffle trees - - sources in the JDK src.zip file + (gdb) set directories /path/to/sources/jdk:/path/to/sources/graal:/path/to/sources/src -Needless to say the list for Graal is long and complex. Also, the JDK -sources are in a zip file and gdb does not understand zip sources. So -you need to extract the JDK sources as a preparatory step, +Directory .../sources/jdk should contain source files for all JDK runtime +classes referenced from debug records. -You can use shell script write_gdbsourcepath (added to Graal dir -substratevm) to auto-generate settings for the GRaal and JDK sources. +Directory .../sources/graal should contain source files for all GraalVM +classes referenced from debug records. Note that the current +implementation does not yet find some sources for the GraalVM JIT +compiler in the org.graalvm.compiler* package subspace. - $ bash write_gdbsourcepath +Directory .../sources/src should contain source files for all +application classes referenced from debug records, assuming they can +be located using the lookup strategy described above. -It creates a local file .gdbsourcepath which sets the relevant -directories. Before running it you can set two env vars to tell it -where to locate the source trees it needs to include: +You can supplement the files cached in sources/src by unzipping +application source jars or copying application source trees into the +cache. You need to ensure that any new subdirectory you add to +sources/src corresponds to the top level package for the classes whose +sources are being included. - - GRAAL_JAVA_SRC_ROOT should point to the dir into which you have - unzipped the src.zip from your Graal JDK release - - GRAAL_SRC_ROOT should point to the dir in which your Graal git - tree checkout is located +You can also add extra directories to the search path. Note that gdb +does not understand zip format file systems so any extra entries you +add must identify a directory tree containing the relevant +sources. Once again. top leel entries in the directory added to the +search path must correspond to the top level package for the classes +whose sources are being included. -Note that the script ignores test source dirs and jdk dirs that do not -match the release level of the JDK (i.e. if you use jdk8 it will ony -include jdk8 src dirs). The script runs the java command available via -JAVA_HOME to idenitfy which JDK is in use). +Configuring source paths in VS +------------------------------ -If you run the script from the substratevm dir of your Graal git repo -checkout the script will default GRAAL_SRC_ROOT to the parent dir. +TO BE ADDED -If JAVA_HOME is set the script will default GRAAL_JAVA_SRC_ROOT to -$JAVA_HOME/src +Checking debug info on Linux +---------------------------- -Checking debug info -------------------- +n.b. this is only of interest to those who want to understand how the +debug info implemetation works or want to trouble shoot problems +encountered during debugging that might relate to the debug info +encoding. The objdump command can be used to display the dbeug info embedded into a native image. The following commands (which all assume the diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index 5aff1370bb33..444be49613ac 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -28,6 +28,7 @@ import java.nio.file.Path; import java.util.List; +import java.util.stream.Stream; /** * interfaces used to allow a native image to communicate @@ -82,10 +83,10 @@ interface DebugCodeInfo { */ int line(); /** - * @return a provider detailing line numbers - * addresses within the compiled method + * @return a stream of records detailing line numbers + * and addresses within the compiled method */ - DebugLineInfoProvider lineInfoProvider(); + Stream lineInfoProvider(); /** * @return a string identifying the method parameters */ @@ -166,33 +167,9 @@ enum Type { DebugFrameSizeChange.Type getType(); } - /** - * convenience interface defining iterator type. - */ - interface DebugTypeInfoProvider extends Iterable { - } - - /** - * convenience interface defining iterator type. - */ - interface DebugCodeInfoProvider extends Iterable { - } - - /** - * convenience interface defining iterator type. - */ - interface DebugLineInfoProvider extends Iterable { - } - - /** - * convenience interface defining iterator type. - */ - interface DebugDataInfoProvider extends Iterable { - } - - DebugTypeInfoProvider typeInfoProvider(); + Stream typeInfoProvider(); - DebugCodeInfoProvider codeInfoProvider(); + Stream codeInfoProvider(); - DebugDataInfoProvider dataInfoProvider(); + Stream dataInfoProvider(); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java index 42df6ae317cb..0ba4bd7543ea 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java @@ -27,12 +27,7 @@ package com.oracle.objectfile.elf.dwarf; import com.oracle.objectfile.debuginfo.DebugInfoProvider; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfo; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfoProvider; -// import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugDataInfoProvider; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLineInfo; -// import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfoProvider; import com.oracle.objectfile.elf.ELFMachine; import java.nio.ByteOrder; @@ -337,8 +332,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { */ uniqueDebugString(""); - DebugCodeInfoProvider codeInfoProvider = debugInfoProvider.codeInfoProvider(); - for (DebugCodeInfo debugCodeInfo : codeInfoProvider) { + debugInfoProvider.codeInfoProvider().forEach(debugCodeInfo -> { /* * primary file name and full method name need to be written to the debug_str section */ @@ -359,7 +353,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { * create an infoSection entry for the method */ addRange(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); - for (DebugLineInfo debugLineInfo : debugCodeInfo.lineInfoProvider()) { + debugCodeInfo.lineInfoProvider().forEach(debugLineInfo -> { String fileNameAtLine = debugLineInfo.fileName(); Path filePathAtLine = debugLineInfo.filePath(); // switch '$' in class names for '.' @@ -374,8 +368,8 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { */ Range subRange = new Range(fileNameAtLine, filePathAtLine, classNameAtLine, methodNameAtLine, "", "", stringTable, loAtLine, hiAtLine, line, primaryRange); addSubRange(primaryRange, subRange); - } - } + }); + }); /* * DebugDataInfoProvider dataInfoProvider = debugInfoProvider.dataInfoProvider(); * for (DebugDataInfo debugDataInfo : dataInfoProvider) { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java index b7963faf9661..f08fa4da79c7 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java @@ -48,7 +48,7 @@ public String getString() { public int getOffset() { /* - * offset must be set before this can be fetched + * Offset must be set before this can be fetched */ assert offset >= 0; return offset; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java index 1ba2def55f73..b2e0479e5e31 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java @@ -44,7 +44,7 @@ public StringTable() { } /** - * Wnsures a unique instance of a string exists in the + * Ensures a unique instance of a string exists in the * table, inserting the supplied String if no equivalent * String is already present. This should only be called * before the string section has been written. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java index 17c611e6c774..f30236f99dd1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java @@ -46,13 +46,13 @@ import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.hosted.image.sources.SourceManager; @@ -981,61 +981,26 @@ protected NativeTextSectionImpl(RelocatableBuffer relocatableBuffer, ObjectFile private class NativeImageDebugInfoProvider implements DebugInfoProvider { private final NativeImageCodeCache codeCache; private final NativeImageHeap heap; - private final Iterator> codeCacheIterator; - private final Iterator> heapIterator; NativeImageDebugInfoProvider(NativeImageCodeCache codeCache, NativeImageHeap heap) { super(); this.codeCache = codeCache; this.heap = heap; - this.codeCacheIterator = codeCache.compilations.entrySet().iterator(); - this.heapIterator = heap.objects.entrySet().iterator(); } @Override - public DebugTypeInfoProvider typeInfoProvider() { - return () -> new Iterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public DebugTypeInfo next() { - return null; - } - }; + public Stream typeInfoProvider() { + return Stream.empty(); } @Override - public DebugCodeInfoProvider codeInfoProvider() { - return () -> new Iterator() { - @Override - public boolean hasNext() { - return codeCacheIterator.hasNext(); - } - - @Override - public DebugCodeInfo next() { - Map.Entry entry = codeCacheIterator.next(); - return new NativeImageDebugCodeInfo(entry.getKey(), entry.getValue()); - } - }; + public Stream codeInfoProvider() { + return codeCache.compilations.entrySet().stream().map(entry -> new NativeImageDebugCodeInfo(entry.getKey(), entry.getValue())); } @Override - public DebugDataInfoProvider dataInfoProvider() { - return () -> new Iterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public DebugDataInfo next() { - return null; - } - }; + public Stream dataInfoProvider() { + return Stream.empty(); } } @@ -1127,33 +1092,11 @@ public int line() { } @Override - public DebugInfoProvider.DebugLineInfoProvider lineInfoProvider() { + public Stream lineInfoProvider() { if (fileName().toString().length() == 0) { - return () -> new Iterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public DebugLineInfo next() { - return null; - } - }; + return Stream.empty(); } - return () -> new Iterator() { - final Iterator sourceIterator = compilation.getSourceMappings().iterator(); - - @Override - public boolean hasNext() { - return sourceIterator.hasNext(); - } - - @Override - public DebugLineInfo next() { - return new NativeImageDebugLineInfo(sourceIterator.next()); - } - }; + return compilation.getSourceMappings().stream().map(sourceMapping -> new NativeImageDebugLineInfo(sourceMapping)); } public int getFrameSize() { From 259171c5618e71769cbc7610e0ffc136d7d77562 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Thu, 27 Feb 2020 13:32:43 +0000 Subject: [PATCH 6/9] Added option DebugInfoSourceSearchPath= Tweak SourceCacheType enum Clean up SourceCache initialization Memoize source cache checks --- substratevm/DEBUGINFO.md | 69 ++++- .../src/com/oracle/objectfile/ObjectFile.java | 9 +- .../{elf/dwarf => debugentry}/ClassEntry.java | 96 ++++--- .../{elf/dwarf => debugentry}/DirEntry.java | 14 +- .../{elf/dwarf => debugentry}/FileEntry.java | 3 +- .../dwarf => debugentry}/PrimaryEntry.java | 17 +- .../{elf/dwarf => debugentry}/Range.java | 26 +- .../dwarf => debugentry}/StringEntry.java | 8 +- .../dwarf => debugentry}/StringTable.java | 41 ++- .../debuginfo/DebugInfoProvider.java | 81 +++--- .../objectfile/elf/ELFRelocationSection.java | 5 +- .../elf/dwarf/DwarfARangesSectionImpl.java | 53 ++-- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 86 ++++-- .../elf/dwarf/DwarfFrameSectionImpl.java | 72 +++-- .../dwarf/DwarfFrameSectionImplAArch64.java | 17 +- .../dwarf/DwarfFrameSectionImplX86_64.java | 24 +- .../elf/dwarf/DwarfInfoSectionImpl.java | 78 +++-- .../elf/dwarf/DwarfLineSectionImpl.java | 167 ++++++----- .../elf/dwarf/DwarfSectionImpl.java | 42 ++- .../objectfile/elf/dwarf/DwarfSections.java | 193 +++++++------ .../elf/dwarf/DwarfStrSectionImpl.java | 10 +- .../com/oracle/svm/core/SubstrateOptions.java | 3 + .../svm/hosted/image/NativeBootImage.java | 86 +++--- .../image/sources/ApplicationSourceCache.java | 96 ++++--- .../image/sources/GraalVMSourceCache.java | 92 +++--- .../hosted/image/sources/JDKSourceCache.java | 19 +- .../svm/hosted/image/sources/SourceCache.java | 271 ++++++++++-------- .../hosted/image/sources/SourceCacheType.java | 20 +- .../hosted/image/sources/SourceManager.java | 224 ++++++++------- 29 files changed, 1106 insertions(+), 816 deletions(-) rename substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/{elf/dwarf => debugentry}/ClassEntry.java (69%) rename substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/{elf/dwarf => debugentry}/DirEntry.java (80%) rename substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/{elf/dwarf => debugentry}/FileEntry.java (97%) rename substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/{elf/dwarf => debugentry}/PrimaryEntry.java (89%) rename substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/{elf/dwarf => debugentry}/Range.java (82%) rename substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/{elf/dwarf => debugentry}/StringEntry.java (91%) rename substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/{elf/dwarf => debugentry}/StringTable.java (69%) diff --git a/substratevm/DEBUGINFO.md b/substratevm/DEBUGINFO.md index 58e8bd48b63e..4ea2b3f640c3 100644 --- a/substratevm/DEBUGINFO.md +++ b/substratevm/DEBUGINFO.md @@ -2,22 +2,69 @@ Using the ptototype debug info feature -------------------------------------- To add debug info to a generated native image add flag --H:+GenerateDebugInfo to the native image command line. +-H:GenerateDebugInfo= to the native image command line (where N is +a positive integer value -- the default value 0 means generate no +debug info). For example, $ javac Hello.java $ mx native-image -H:GenerateDebugInfo=1 Hello The resulting image should contain code (method) debug records in a -format gdb understands (VS support is still under development). - -The flag also enables caching of sources for JDK runtime classes, -GraalVM classes and application classes which can be located during -native image generation. The cache is created under local subdirectory -sources and can be used to configure source file search path roots for -the debugger. Files in the cache are located in a directory hierarchy -that matches the file path information included in the native image -debug records - +format gdb understands (VS support is still under development). At +present it makes no difference which positive value is supplied as +argument to the GenerateDebugInfo option. + +The GenerateDebugInfo option also enables caching of sources for any +JDK runtime classes, GraalVM classes and application classes which can +be located during native image generation. The cache is created under +local subdirectory sources. It is used to configure source file search +path roots for the debugger. Files in the cache are located in a +directory hierarchy that matches the file path information included in +the native image debug records. The source cache should contain all +the files needed to debug the generated image and nothing more. This +local cache provides a convenient way of making just the necessary +sources available to the debugger/IDE when debugging a native image. + +The implementation tries to be smart about locating source files. It +uses the current JAVA_HOME to locate the JDK src.zip when searching +for JDK runtime sources. It also uses entries in the classpath to +suggest locations for GraalVM source files and application source +files (see below for precise details of the scheme used to identify +source locations). However, source layouts do vary and it may no tbe +possible to find all sources. Hence, users can specify the location of +source files explicitly on the command line using option +DebugInfoSourceSearchPath: + + $ javac --source-path apps/greeter/src \ + -d apps/greeter/classes org/my/greeter/*Greeter.java + $ javac -cp apps/greeter/classes \ + --source-path apps/hello/src \ + -d apps/hello/classes org/my/hello/Hello.java + $ mx native-image -H:GenerateDebugInfo=1 \ + -H:DebugInfoSourceSearchPath=apps/hello/src \ + -H:DebugInfoSourceSearchPath=apps/greeter/src \ + -cp apps/hello/classes:apps/greeter/classes org.my.hello.Hello + +Option DebugInfoSourceSearchPath can be repeated as many times as +required to notify all the target source locations. The value passed +to this option can be either an absolute or relative path. It can +identify either a directory, a source jar or a source zip file. It is +also possible to specify several source roots at once using a comma +separator: + + $ mx native-image -H:GenerateDebugInfo=1 \ + -H:DebugInfoSourceSearchPath=apps/hello/target/hello-sources.jar,apps/greeter/target/greeter-sources.jar \ + -cp apps/target/hello.jar:apps/target/greeter.jar \ + org.my.Hello + +Note that in both the examples above the DebugInfoSourceSearchPath +options are actually redundant. In the first case the classpath +entries for apps/hello/classes and apps/greeter/classes will be used +to derive the default search roots apps/hello/src and +apps/greeter/src. In the second case classpath entires +apps/target/hello.jar and apps/target/greeter.jar will be used to +derive the default search roots apps/target/hello-sources.jar and +apps/target/greeter-sources.jar. What is currently implemented ----------------------------- diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java index 518b171882d4..386978567ebf 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java @@ -1088,10 +1088,11 @@ protected boolean elementsCanSharePage(Element s1, Element s2, int offset1, int } /** - * API method provided to allow a native image generator to provide details of - * types, code and heap data inserted into a native image. - * @param debugInfoProvider an implementation of the provider interface that - * communicates details of the relevant types, code and heap data. + * API method provided to allow a native image generator to provide details of types, code and + * heap data inserted into a native image. + * + * @param debugInfoProvider an implementation of the provider interface that communicates + * details of the relevant types, code and heap data. */ public void installDebugInfo(@SuppressWarnings("unused") DebugInfoProvider debugInfoProvider) { // do nothing by default diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java similarity index 69% rename from substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java rename to substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index 6d8714c5ae38..bb4917254b59 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.objectfile.elf.dwarf; +package com.oracle.objectfile.debugentry; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; @@ -37,28 +37,25 @@ * track debug info associated with a Java class. */ public class ClassEntry { - /** + /** * the name of the associated class. - */ + */ private String className; /** * details of the associated file. */ FileEntry fileEntry; /** - * a list recording details of all primary - * ranges included in this class sorted by - * ascending address range. + * a list recording details of all primary ranges included in this class sorted by ascending + * address range. */ private LinkedList primaryEntries; /** - * an index identifying primary ranges which - * have already been encountered. + * an index identifying primary ranges which have already been encountered. */ private Map primaryIndex; /** - * an index of all primary and secondary files - * referenced from this class's compilation unit. + * an index of all primary and secondary files referenced from this class's compilation unit. */ private Map localFilesIndex; /** @@ -66,8 +63,7 @@ public class ClassEntry { */ private LinkedList localFiles; /** - * an index of all primary and secondary dirs - * referenced from this class's compilation unit. + * an index of all primary and secondary dirs referenced from this class's compilation unit. */ private HashMap localDirsIndex; /** @@ -75,23 +71,19 @@ public class ClassEntry { */ private LinkedList localDirs; /** - * index of debug_info section compilation unit - * for this class. + * index of debug_info section compilation unit for this class. */ private int cuIndex; /** - * index into debug_line section for associated - * compilation unit. + * index into debug_line section for associated compilation unit. */ private int lineIndex; /** - * size of line number info prologue region for - * associated compilation unit. + * size of line number info prologue region for associated compilation unit. */ private int linePrologueSize; /** - * total size of line number info region for - * associated compilation unit. + * total size of line number info region for associated compilation unit. */ private int totalSize; @@ -104,12 +96,14 @@ public ClassEntry(String className, FileEntry fileEntry) { this.localFilesIndex = new HashMap<>(); this.localDirs = new LinkedList<>(); this.localDirsIndex = new HashMap<>(); - localFiles.add(fileEntry); - localFilesIndex.put(fileEntry, localFiles.size()); - DirEntry dirEntry = fileEntry.getDirEntry(); - if (dirEntry != null) { - localDirs.add(dirEntry); - localDirsIndex.put(dirEntry, localDirs.size()); + if (fileEntry != null) { + localFiles.add(fileEntry); + localFilesIndex.put(fileEntry, localFiles.size()); + DirEntry dirEntry = fileEntry.getDirEntry(); + if (dirEntry != null) { + localDirs.add(dirEntry); + localDirsIndex.put(dirEntry, localDirs.size()); + } } this.cuIndex = -1; this.lineIndex = -1; @@ -117,7 +111,7 @@ public ClassEntry(String className, FileEntry fileEntry) { this.totalSize = -1; } - PrimaryEntry addPrimary(Range primary, List frameSizeInfos, int frameSize) { + public PrimaryEntry addPrimary(Range primary, List frameSizeInfos, int frameSize) { if (primaryIndex.get(primary) == null) { PrimaryEntry primaryEntry = new PrimaryEntry(primary, frameSizeInfos, frameSize, this); primaryEntries.add(primaryEntry); @@ -127,7 +121,7 @@ PrimaryEntry addPrimary(Range primary, List frameSizeInfos return null; } - void addSubRange(Range subrange, FileEntry subFileEntry) { + public void addSubRange(Range subrange, FileEntry subFileEntry) { Range primary = subrange.getPrimary(); /* * the subrange should belong to a primary range @@ -140,14 +134,16 @@ void addSubRange(Range subrange, FileEntry subFileEntry) { assert primaryEntry != null; assert primaryEntry.getClassEntry() == this; primaryEntry.addSubRange(subrange, subFileEntry); - if (localFilesIndex.get(subFileEntry) == null) { - localFiles.add(subFileEntry); - localFilesIndex.put(subFileEntry, localFiles.size()); - } - DirEntry dirEntry = subFileEntry.getDirEntry(); - if (dirEntry != null && localDirsIndex.get(dirEntry) == null) { - localDirs.add(dirEntry); - localDirsIndex.put(dirEntry, localDirs.size()); + if (subFileEntry != null) { + if (localFilesIndex.get(subFileEntry) == null) { + localFiles.add(subFileEntry); + localFilesIndex.put(subFileEntry, localFiles.size()); + } + DirEntry dirEntry = subFileEntry.getDirEntry(); + if (dirEntry != null && localDirsIndex.get(dirEntry) == null) { + localDirs.add(dirEntry); + localDirsIndex.put(dirEntry, localDirs.size()); + } } } @@ -163,36 +159,48 @@ public int localFilesIdx(@SuppressWarnings("hiding") FileEntry fileEntry) { return localFilesIndex.get(fileEntry); } - String getFileName() { - return fileEntry.getFileName(); + public String getFileName() { + if (fileEntry != null) { + return fileEntry.getFileName(); + } else { + return ""; + } } String getFullFileName() { - return fileEntry.getFullName(); + if (fileEntry != null) { + return fileEntry.getFullName(); + } else { + return null; + } } String getDirName() { - return fileEntry.getPathName(); + if (fileEntry != null) { + return fileEntry.getPathName(); + } else { + return ""; + } } - void setCUIndex(int cuIndex) { + public void setCUIndex(int cuIndex) { // should only get set once to a non-negative value assert cuIndex >= 0; assert this.cuIndex == -1; this.cuIndex = cuIndex; } - int getCUIndex() { + public int getCUIndex() { // should have been set before being read assert cuIndex >= 0; return cuIndex; } - int getLineIndex() { + public int getLineIndex() { return lineIndex; } - void setLineIndex(int lineIndex) { + public void setLineIndex(int lineIndex) { this.lineIndex = lineIndex; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java similarity index 80% rename from substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java rename to substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java index c033986b8be6..019611539160 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DirEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java @@ -24,20 +24,16 @@ * questions. */ -package com.oracle.objectfile.elf.dwarf; +package com.oracle.objectfile.debugentry; import java.nio.file.Path; /** - * Tracks the directory associated with one or - * more source files. + * Tracks the directory associated with one or more source files. * - * This is identified separately from each FileEntry - * idenityfing files that reside in the directory. - * That is necessary because the line info generator - * needs to collect and write out directory names - * into directory tables once only rather than once - * per file. + * This is identified separately from each FileEntry idenityfing files that reside in the directory. + * That is necessary because the line info generator needs to collect and write out directory names + * into directory tables once only rather than once per file. */ public class DirEntry { private Path path; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java similarity index 97% rename from substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java rename to substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java index bc53ec654e64..1aedf4dc879b 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/FileEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.objectfile.elf.dwarf; +package com.oracle.objectfile.debugentry; /** * Tracks debug info associated with a Java source file. @@ -52,6 +52,7 @@ public String getPathName() { public String getFullName() { return getDirEntry().getPath().resolve(getFileName()).toString(); } + /** * The directory entry associated with this file entry. */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/PrimaryEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java similarity index 89% rename from substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/PrimaryEntry.java rename to substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java index cd356dd00535..b0199d3c3f65 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/PrimaryEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.objectfile.elf.dwarf; +package com.oracle.objectfile.debugentry; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; @@ -33,8 +33,7 @@ import java.util.List; /** - * Tracks debug info associated with a primary method. - * i.e. a top level compiled method + * Tracks debug info associated with a primary method. i.e. a top level compiled method */ public class PrimaryEntry { /** @@ -61,6 +60,7 @@ public class PrimaryEntry { * Size of compiled method frame. */ private int frameSize; + public PrimaryEntry(Range primary, List frameSizeInfos, int frameSize, ClassEntry classEntry) { this.primary = primary; this.classEntry = classEntry; @@ -77,8 +77,7 @@ public void addSubRange(Range subrange, FileEntry subFileEntry) { assert !subranges.contains(subrange); assert subrangeIndex.get(subrange) == null; /* - * we need to generate a file table entry - * for all ranges + * we need to generate a file table entry for all ranges */ subranges.add(subrange); subrangeIndex.put(subrange, subFileEntry); @@ -92,10 +91,6 @@ public ClassEntry getClassEntry() { return classEntry; } - public FileEntry getFileEntry() { - return classEntry.getFileEntry(); - } - public List getSubranges() { return subranges; } @@ -104,11 +99,11 @@ public FileEntry getSubrangeFileEntry(Range subrange) { return subrangeIndex.get(subrange); } - List getFrameSizeInfos() { + public List getFrameSizeInfos() { return frameSizeInfos; } - int getFrameSize() { + public int getFrameSize() { return frameSize; } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java similarity index 82% rename from substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java rename to substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 2608be71322b..191ec29b0532 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -24,15 +24,15 @@ * questions. */ -package com.oracle.objectfile.elf.dwarf; +package com.oracle.objectfile.debugentry; import java.nio.file.Path; import java.nio.file.Paths; + /** - * Details of a specific address range in a compiled method - * either a primary range identifying a whole method - * or a sub-range identifying a sequence of - * instructions that belong to an inlined method. + * Details of a specific address range in a compiled method either a primary range identifying a + * whole method or a sub-range identifying a sequence of instructions that belong to an inlined + * method. */ public class Range { @@ -54,20 +54,20 @@ public class Range { /* * create a primary range */ - Range(String fileName, Path filePath, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line) { + public Range(String fileName, Path filePath, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line) { this(fileName, filePath, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, line, null); } /* * create a primary or secondary range */ - Range(String fileName, Path filePath, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line, Range primary) { + public Range(String fileName, Path filePath, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line, Range primary) { /* - * currently file name and full method name need to go into the debug_str section - * other strings just need to be deduplicated to save space + * currently file name and full method name need to go into the debug_str section other + * strings just need to be deduplicated to save space */ - this.fileName = stringTable.uniqueDebugString(fileName); - this.filePath = filePath; + this.fileName = (fileName == null ? fileName : stringTable.uniqueDebugString(fileName)); + this.filePath = filePath; this.className = stringTable.uniqueString(className); this.methodName = stringTable.uniqueString(methodName); this.paramNames = stringTable.uniqueString(paramNames); @@ -102,8 +102,10 @@ public Path getFilePath() { public Path getFileAsPath() { if (filePath != null) { return filePath.resolve(fileName); - } else { + } else if (fileName != null) { return Paths.get(fileName); + } else { + return null; } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringEntry.java similarity index 91% rename from substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java rename to substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringEntry.java index f08fa4da79c7..f8fe7445d8d2 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringEntry.java @@ -24,13 +24,11 @@ * questions. */ -package com.oracle.objectfile.elf.dwarf; +package com.oracle.objectfile.debugentry; /** - * Used to retain a unique (up to equals) copy of a - * String. Also flag swhether the String needs to be - * located in the debug_string section and, if so, - * tracks the offset at which it gets written. + * Used to retain a unique (up to equals) copy of a String. Also flags whether the String needs to + * be located in the debug_string section and, if so, tracks the offset at which it gets written. */ public class StringEntry { private String string; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringTable.java similarity index 69% rename from substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java rename to substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringTable.java index b2e0479e5e31..9ea823ff00d4 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/StringTable.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringTable.java @@ -24,16 +24,15 @@ * questions. */ -package com.oracle.objectfile.elf.dwarf; +package com.oracle.objectfile.debugentry; import java.util.HashMap; import java.util.Iterator; /** - * Allows incoming strings to be reduced to unique (up - * to equals) instances and supports marking of strings - * which need to be written to the debug_str section - * and retrieval of the location offset after writing. + * Allows incoming strings to be reduced to unique (up to equals) instances and supports marking of + * strings which need to be written to the debug_str section and retrieval of the location offset + * after writing. */ public class StringTable implements Iterable { @@ -44,10 +43,10 @@ public StringTable() { } /** - * Ensures a unique instance of a string exists in the - * table, inserting the supplied String if no equivalent - * String is already present. This should only be called - * before the string section has been written. + * Ensures a unique instance of a string exists in the table, inserting the supplied String if + * no equivalent String is already present. This should only be called before the string section + * has been written. + * * @param string the string to be included in the table * @return the unique instance of the String */ @@ -56,13 +55,12 @@ public String uniqueString(String string) { } /** - * Ensures a unique instance of a string exists in the - * table and is marked for inclusion in the debug_str - * section, inserting the supplied String if no equivalent - * String is already present. This should only be called - * before the string section has been written. - * @param string the string to be included in the table - * and marked for inclusion in the debug_str section + * Ensures a unique instance of a string exists in the table and is marked for inclusion in the + * debug_str section, inserting the supplied String if no equivalent String is already present. + * This should only be called before the string section has been written. + * + * @param string the string to be included in the table and marked for inclusion in the + * debug_str section * @return the unique instance of the String */ public String uniqueDebugString(String string) { @@ -82,13 +80,12 @@ private String ensureString(String string, boolean addToStrSection) { } /** - * Retrieves the offset at which a given string was written - * into the debug_str section. This should only be called - * after the string section has been written. + * Retrieves the offset at which a given string was written into the debug_str section. This + * should only be called after the string section has been written. + * * @param string - * @return the offset or -1 if the string does not - * define an entry or the entry has nto been written - * to the debug_str section + * @return the offset or -1 if the string does not define an entry or the entry has not been + * written to the debug_str section */ public int debugStringIndex(String string) { StringEntry stringEntry = table.get(string); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index 444be49613ac..185de9e86e85 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -31,10 +31,8 @@ import java.util.stream.Stream; /** - * interfaces used to allow a native image to communicate - * details of types, code and data to the underlying - * object file so that the latter can insert appropriate - * debug info. + * Interfaces used to allow a native image to communicate details of types, code and data to the + * underlying object file so that the latter can insert appropriate debug info. */ public interface DebugInfoProvider { /** @@ -48,61 +46,67 @@ interface DebugTypeInfo { */ interface DebugCodeInfo { /** - * @return the name of the file containing a compiled - * method excluding any path + * @return the name of the file containing a compiled method excluding any path */ String fileName(); + /** - * @return a relative path to the file containing a compiled - * method derived from its package name or null if the method - * is in the empty package + * @return a relative path to the file containing a compiled method derived from its package + * name or null if the method is in the empty package */ Path filePath(); + /** - * @return the fully qualified name of the class owning the - * compiled method + * @return the fully qualified name of the class owning the compiled method */ String className(); + /** - * @return the name of the compiled method including - * signature + * @return the name of the compiled method including signature */ String methodName(); + /** - * @return the lowest address containing code generated for - * the method represented as an offset into the code segment + * @return the lowest address containing code generated for the method represented as an + * offset into the code segment */ int addressLo(); + /** - * @return the first address above the code generated for - * the method represented as an offset into the code segment + * @return the first address above the code generated for the method represented as an + * offset into the code segment */ int addressHi(); + /** * @return the starting line number for the method */ int line(); + /** - * @return a stream of records detailing line numbers - * and addresses within the compiled method + * @return a stream of records detailing line numbers and addresses within the compiled + * method */ Stream lineInfoProvider(); + /** * @return a string identifying the method parameters */ String paramNames(); + /** * @return a string identifying the method return type */ String returnTypeName(); + /** - * @return the size of the method frame between prologue - * and epilogue + * @return the size of the method frame between prologue and epilogue */ int getFrameSize(); + /** - * @return a list of positions at which the stack is extended - * to a full frame or torn down to an empty frame + * @return a list of positions at which the stack is extended to a full frame or torn down + * to an empty frame */ List getFrameSizeChanges(); } @@ -114,42 +118,43 @@ interface DebugDataInfo { } /** - * access details of code generated for a specific outer - * or inlined method at a given line number. + * access details of code generated for a specific outer or inlined method at a given line + * number. */ interface DebugLineInfo { /** - * @return the name of the file containing the outer - * or inlined method excluding any path + * @return the name of the file containing the outer or inlined method excluding any path */ String fileName(); + /** - * @return a relative path to the file containing the outer - * or inlined method derived from its package name or null - * if the method is in the empty package + * @return a relative path to the file containing the outer or inlined method derived from + * its package name or null if the method is in the empty package */ Path filePath(); + /** - * @return the fully qualified name of the class owning the - * outer or inlined method + * @return the fully qualified name of the class owning the outer or inlined method */ String className(); + /** * @return the name of the outer or inlined method including signature */ String methodName(); + /** - * @return the lowest address containing code generated for - * an outer or inlined code segment reported at this line - * represented as an offset into the code segment + * @return the lowest address containing code generated for an outer or inlined code segment + * reported at this line represented as an offset into the code segment */ int addressLo(); + /** - * @return the first address above the code generated for - * an outer or inlined code segment reported at this line - * represented as an offset into the code segment + * @return the first address above the code generated for an outer or inlined code segment + * reported at this line represented as an offset into the code segment */ int addressHi(); + /** * @return the line number for the outer or inlined segment */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFRelocationSection.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFRelocationSection.java index 7826a6a786ba..8aa18d5344f4 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFRelocationSection.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFRelocationSection.java @@ -240,10 +240,9 @@ public Iterable getDependencies(Map // our content depends on the content of the section being relocated // (because entries only get registered during generation) if (relocated != null) { - LayoutDecision relocatedSectionContent = - decisions.get(relocated).getDecision(LayoutDecision.Kind.CONTENT); + LayoutDecision relocatedSectionContent = decisions.get(relocated).getDecision(LayoutDecision.Kind.CONTENT); deps.add(BuildDependency.createOrGet(ourContent, - relocatedSectionContent)); + relocatedSectionContent)); } if (isDynamic()) { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java index 8ae11c436937..e2bcb27740f7 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java @@ -29,6 +29,9 @@ import com.oracle.objectfile.LayoutDecision; import com.oracle.objectfile.LayoutDecisionMap; import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.PrimaryEntry; +import com.oracle.objectfile.debugentry.Range; import java.util.LinkedList; import java.util.Map; @@ -36,6 +39,7 @@ import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ARANGES_SECTION_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_INFO_SECTION_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_VERSION_2; + /** * Section generator for debug_aranges section. */ @@ -58,25 +62,39 @@ public void createContent() { /* * we need an entry for each compilation unit * - * uint32 length ............ in bytes (not counting these 4 bytes) - * uint16 dwarf_version ..... always 2 - * uint32 info_offset ....... offset of compilation unit on debug_info - * uint8 address_size ....... always 8 - * uint8 segment_desc_size .. ??? + *
    + * + *
  • uint32 length ............ in bytes (not counting these 4 bytes) + * + *
  • uint16 dwarf_version ..... always 2 + * + *
  • uint32 info_offset ....... offset of compilation unit on debug_info + * + *
  • uint8 address_size ....... always 8 + * + *
  • uint8 segment_desc_size .. ??? * - * i.e. 12 bytes followed by padding - * aligning up to 2 * address size + *
* - * uint8 pad[4] + * i.e. 12 bytes followed by padding aligning up to 2 * address size + * + *
    + * + *
  • uint8 pad[4] + * + *
* * followed by N + 1 times * - * uint64 lo ................ lo address of range - * uint64 length ............ number of bytes in range + *
  • uint64 lo ................ lo address of range + * + *
  • uint64 length ............ number of bytes in range + * + *
* - * where N is the number of ranges belonging to the compilation unit - * and the last range contains two zeroes - */ + * where N is the number of ranges belonging to the compilation unit and the last range + * contains two zeroes + */ for (ClassEntry classEntry : getPrimaryClasses()) { pos += DW_AR_HEADER_SIZE; @@ -99,9 +117,8 @@ public byte[] getOrDecideContent(Map alre Object valueObj = decisionMap.getDecidedValue(LayoutDecision.Kind.VADDR); if (valueObj != null && valueObj instanceof Number) { /* - * this may not be the final vaddr for the text segment - * but it will be close enough to make debug easier - * i.e. to within a 4k page or two + * this may not be the final vaddr for the text segment but it will be close enough + * to make debug easier i.e. to within a 4k page or two */ debugTextBase = ((Number) valueObj).longValue(); } @@ -174,8 +191,8 @@ public String targetSectionName() { } public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET }; @Override diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java index c8f97729dd95..b3cc40de399b 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java @@ -68,45 +68,71 @@ public String getSectionName() { public void createContent() { int pos = 0; /* - * an abbrev table contains abbrev entries for one or - * more CUs. the table includes a sequence of abbrev - * entries each of which defines a specific DIE layout - * employed to describe some DIE in a CU. a table is - * terminated by a null entry + * an abbrev table contains abbrev entries for one or more CUs. the table includes a + * sequence of abbrev entries each of which defines a specific DIE layout employed to + * describe some DIE in a CU. a table is terminated by a null entry * * a null entry has consists of just a 0 abbrev code - * LEB128 abbrev_code; ...... == 0 + * + *
    + * + *
  • LEB128 abbrev_code; ...... == 0 + * + *
* * non-null entries have the following format - * LEB128 abbrev_code; ...... unique noncode for this layout != 0 - * LEB128 tag; .............. defines the type of the DIE (class, subprogram, var etc) - * uint8 has_chldren; ....... is the DIE followed by child DIEs or a sibling DIE - * * ........ zero or more attributes - * .... terminator + * + *
    + * + *
  • LEB128 abbrev_code; ......unique noncode for this layout != 0 + * + *
  • LEB128 tag; .............. defines the type of the DIE (class, subprogram, var + * etc) + * + *
  • uint8 has_chldren; ....... is the DIE followed by child DIEs or a sibling + * DIE + * + *
  • attribute_spec* .......... zero or more attributes + * + *
  • null_attribute_spec ...... terminator
* * An attribute_spec consists of an attribute name and form - * LEB128 attr_name; ........ 0 for the null attribute name - * LEB128 attr_form; ........ 0 for the null attribute form * - * For the moment we only use one abbrev table for all CUs. - * It contains two DIEs, the first to describe the compilation - * unit itself and the second to describe each method within + *
    + * + *
  • LEB128 attr_name; ........ 0 for the null attribute name + * + *
  • LEB128 attr_form; ........ 0 for the null attribute form + * + *
+ * + * For the moment we only use one abbrev table for all CUs. It contains two DIEs, the first + * to describe the compilation unit itself and the second to describe each method within * that compilation unit. * * The DIE layouts are as follows: * - * abbrev_code == 1, tag == DW_TAG_compilation_unit, has_children - * DW_AT_language : ... DW_FORM_data1 - * DW_AT_name : ....... DW_FORM_strp - * DW_AT_low_pc : ..... DW_FORM_address - * DW_AT_hi_pc : ...... DW_FORM_address - * DW_AT_stmt_list : .. DW_FORM_data4 - * - * abbrev_code == 2, tag == DW_TAG_subprogram, no_children - * DW_AT_name : ....... DW_FORM_strp - * DW_AT_low_pc : ..... DW_FORM_addr - * DW_AT_hi_pc : ...... DW_FORM_addr - * DW_AT_external : ... DW_FORM_flag + *
  • abbrev_code == 1, tag == DW_TAG_compilation_unit, has_children + * + *
  • DW_AT_language : ... DW_FORM_data1 + * + *
  • DW_AT_name : ....... DW_FORM_strp + * + *
  • DW_AT_low_pc : ..... DW_FORM_address + * + *
  • DW_AT_hi_pc : ...... DW_FORM_address + * + *
  • DW_AT_stmt_list : .. DW_FORM_data4
+ * + *
  • abbrev_code == 2, tag == DW_TAG_subprogram, no_children + * + *
  • DW_AT_name : ....... DW_FORM_strp + * + *
  • DW_AT_hi_pc : ...... DW_FORM_addr + * + *
  • DW_AT_external : ... DW_FORM_flag + * + *
*/ pos = writeAbbrev1(null, pos); @@ -211,8 +237,8 @@ public String targetSectionName() { } public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET }; @Override diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java index 3923045bacd4..a7abe33f8034 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java @@ -25,7 +25,10 @@ */ package com.oracle.objectfile.elf.dwarf; + import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.PrimaryEntry; import com.oracle.objectfile.debuginfo.DebugInfoProvider; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_CIE_id; @@ -41,6 +44,7 @@ import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_register; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FRAME_SECTION_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_LINE_SECTION_NAME; + /** * Section generic generator for debug_frame section. */ @@ -60,8 +64,7 @@ public void createContent() { int pos = 0; /* - * the frame section contains one CIE at offset 0 - * followed by an FIE for each method + * the frame section contains one CIE at offset 0 followed by an FIE for each method */ pos = writeCIE(null, pos); pos = writeMethodFrames(null, pos); @@ -79,10 +82,9 @@ public void writeContent() { checkDebug(pos); /* - * there are entries for the prologue region where the - * stack is being built, the method body region(s) where - * the code executes with a fixed size frame and the - * epilogue region(s) where the stack is torn down + * there are entries for the prologue region where the stack is being built, the method body + * region(s) where the code executes with a fixed size frame and the epilogue region(s) + * where the stack is torn down */ pos = writeCIE(buffer, pos); pos = writeMethodFrames(buffer, pos); @@ -95,18 +97,28 @@ public void writeContent() { public int writeCIE(byte[] buffer, int p) { /* - * we only need a vanilla CIE with default fields - * because we have to have at least one - * the layout is + * we only need a vanilla CIE with default fields because we have to have at least one the + * layout is + * + *
    + * + *
  • uint32 : length ............... length of remaining fields in this CIE + * + *
  • uint32 : CIE_id ................ unique id for CIE == 0xffffff + * + *
  • uint8 : version ................ == 1 + * + *
  • uint8[] : augmentation ......... == "" so always 1 byte + * + *
  • ULEB : code_alignment_factor ... == 1 (could use 4 for Aarch64) + * + *
  • ULEB : data_alignment_factor ... == -8 * - * uint32 : length ............... length of remaining fields in this CIE - * uint32 : CIE_id ................ unique id for CIE == 0xffffff - * uint8 : version ................ == 1 - * uint8[] : augmentation ......... == "" so always 1 byte - * ULEB : code_alignment_factor ... == 1 (could use 4 for Aarch64) - * ULEB : data_alignment_factor ... == -8 - * byte : ret_addr reg id ......... x86_64 => 16 AArch64 => 32 - * byte[] : initial_instructions .. includes pad to 8-byte boundary + *
  • byte : ret_addr reg id ......... x86_64 => 16 AArch64 => 32 + * + *
  • byte[] : initial_instructions .. includes pad to 8-byte boundary + * + *
*/ int pos = p; if (buffer == null) { @@ -186,14 +198,22 @@ public int writeMethodFrames(byte[] buffer, int p) { public int writeFDEHeader(int lo, int hi, byte[] buffer, int p) { /* - * we only need a vanilla FDE header with default fields - * the layout is + * we only need a vanilla FDE header with default fields the layout is + * + *
    + * + *
  • uint32 : length ............ length of remaining fields in this FDE + * + *
  • uint32 : CIE_offset ........ always 0 i.e. identifies our only CIE + * header + * + *
  • uint64 : initial_location .. i.e. method lo address + * + *
  • uint64 : address_range ..... i.e. method hi - lo + * + *
  • byte[] : instructions ...... includes pad to 8-byte boundary * - * uint32 : length ........... length of remaining fields in this FDE - * uint32 : CIE_offset ........ always 0 i.e. identifies our only CIE header - * uint64 : initial_location .. i.e. method lo address - * uint64 : address_range ..... i.e. method hi - lo - * byte[] : instructions ...... includes pad to 8-byte boundary + *
*/ int pos = p; @@ -359,8 +379,8 @@ public String targetSectionName() { } public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET }; @Override diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java index 8f4ab1818ec4..89268fd72b7c 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java @@ -27,11 +27,11 @@ package com.oracle.objectfile.elf.dwarf; /** - * AArch64-specific section generator for debug_frame section - * that knows details of AArch64 registers and frame layout. + * AArch64-specific section generator for debug_frame section that knows details of AArch64 + * registers and frame layout. */ public class DwarfFrameSectionImplAArch64 extends DwarfFrameSectionImpl { - public static final int DW_CFA_FP_IDX = 29; + // public static final int DW_CFA_FP_IDX = 29; public static final int DW_CFA_LR_IDX = 30; public static final int DW_CFA_SP_IDX = 31; public static final int DW_CFA_PC_IDX = 32; @@ -54,12 +54,15 @@ public int getSPIdx() { public int writeInitialInstructions(byte[] buffer, int p) { int pos = p; /* - * rsp has not been updated - * caller pc is in lr - * register r32 (rpc), r30 (lr) + * rsp has not been updated and caller pc is in lr + * + *
    + * + *
  • register r32 (rpc), r30 (lr) + * + *
*/ pos = writeRegister(DW_CFA_PC_IDX, DW_CFA_LR_IDX, buffer, pos); return pos; } } - diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java index 7fabf34ad449..275c4ca00a32 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java @@ -27,8 +27,8 @@ package com.oracle.objectfile.elf.dwarf; /** - * x86_64-specific section generator for debug_frame section - * that knows details of x86_64 registers and frame layout. + * x86_64-specific section generator for debug_frame section that knows details of x86_64 registers + * and frame layout. */ public class DwarfFrameSectionImplX86_64 extends DwarfFrameSectionImpl { public static final int DW_CFA_RSP_IDX = 7; @@ -52,15 +52,23 @@ public int getSPIdx() { public int writeInitialInstructions(byte[] buffer, int p) { int pos = p; /* - * rsp points at the word containing the saved rip - * so the frame base (cfa) is at rsp + 8 (why not - ???) - * def_cfa r7 (sp) offset 8 + * rsp points at the word containing the saved rip so the frame base (cfa) is at rsp + 8 + * (why not - ???) + * + *
    + * + *
  • def_cfa r7 (sp) offset 8 + * + *
*/ pos = writeDefCFA(DW_CFA_RSP_IDX, 8, buffer, pos); /* - * and rip is saved at offset 8 (coded as 1 which gets scaled by dataAlignment) from cfa - * (why not -1 ???) - * offset r16 (rip) cfa - 8 + * rip is saved at offset 8 (coded as 1 which gets scaled by dataAlignment) from cfa (why + * not -1 ???) + * + *
  • offset r16 (rip) cfa - 8 + * + *
*/ pos = writeOffset(DW_CFA_RIP_IDX, 1, buffer, pos); return pos; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index 6a07284cb46a..f13c5c3e1701 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -27,6 +27,9 @@ package com.oracle.objectfile.elf.dwarf; import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.PrimaryEntry; +import com.oracle.objectfile.debugentry.Range; import java.util.LinkedList; @@ -37,6 +40,7 @@ import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_INFO_SECTION_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_LANG_Java; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_VERSION_2; + /** * Section generator for debug_info section. */ @@ -58,30 +62,52 @@ public String getSectionName() { @Override public void createContent() { /* - * we need a single level 0 DIE for each compilation unit (CU) - * Each CU's Level 0 DIE is preceded by a fixed header: - * and terminated by a null DIE - * uint32 length ......... excluding this length field - * uint16 dwarf_version .. always 2 ?? - * uint32 abbrev offset .. always 0 ?? - * uint8 address_size .... always 8 - * * ................ sequence of top-level and nested child entries - * ............ == 0 - * - * a DIE is a recursively defined structure - * it starts with a code for the associated - * abbrev entry followed by a series of attribute - * values as determined by the entry terminated by - * a null value and followed by zero or more child - * DIEs (zero iff has_children == no_children) - * - * LEB128 abbrev_code != 0 .. non-zero value indexes tag + attr layout of DIE - * * ....... value sequence as determined by abbrev entry - * * ................... sequence of child DIEs (if appropriate) - * ............. == 0 + * we need a single level 0 DIE for each compilation unit (CU). Each CU's Level 0 DIE is + * preceded by a fixed header and terminated by a null DIE: + * + *
    + * + *
  • uint32 length ......... excluding this length field + * + *
  • uint16 dwarf_version .. always 2 ?? + * + *
  • uint32 abbrev offset .. always 0 ?? + * + *
  • uint8 address_size .... always 8 + * + *
  • DIE* .................. sequence of top-level and nested child entries + * + *
  • null_DIE .............. == 0 + * + *
+ * + * a DIE is a recursively defined structure. it starts with a code for the associated abbrev + * entry followed by a series of attribute values, as determined by the entry, terminated by + * a null value and followed by zero or more child DIEs (zero iff has_children == + * no_children). + * + *
    + * + *
  • LEB128 abbrev_code != 0 .. non-zero value indexes tag + attr layout of + * DIE + * + *
  • attribute_value* ......... value sequence as determined by abbrev entry + * + *
  • DIE* ..................... sequence of child DIEs (if appropriate) + *
  • + * + *
  • null_value ............... == 0 + * + *
* * note that a null_DIE looks like - * LEB128 abbrev_code ....... == 0 + * + *
    + * + *
  • LEB128 abbrev_code ....... == 0 + * + *
+ * * i.e. it also looks like a null_value */ @@ -113,8 +139,7 @@ public void writeContent() { debug(" [0x%08x] size = 0x%08x\n", pos, size); for (ClassEntry classEntry : getPrimaryClasses()) { /* - * save the offset of this file's CU so it can - * be used when writing the aranges section + * save the offset of this file's CU so it can be used when writing the aranges section */ classEntry.setCUIndex(pos); int lengthPos = pos; @@ -235,8 +260,8 @@ public String targetSectionName() { } public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET }; @Override @@ -244,4 +269,3 @@ public LayoutDecision.Kind[] targetSectionKinds() { return targetSectionKinds; } } - diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java index 10305904c0ea..0dc635f2ad65 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java @@ -29,12 +29,18 @@ import com.oracle.objectfile.LayoutDecision; import com.oracle.objectfile.LayoutDecisionMap; import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.DirEntry; +import com.oracle.objectfile.debugentry.FileEntry; +import com.oracle.objectfile.debugentry.PrimaryEntry; +import com.oracle.objectfile.debugentry.Range; import java.util.Map; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_LINE_SECTION_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_STR_SECTION_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_VERSION_2; + /** * Section generator for debug_line section. */ @@ -48,13 +54,11 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { */ private static final int DW_LN_LINE_BASE = -5; /** - * current generator follows C++ with line range 14 - * giving full range -5 to 8. + * current generator follows C++ with line range 14 giving full range -5 to 8. */ private static final int DW_LN_LINE_RANGE = 14; /** - * current generator uses opcode base of 13 - * which must equal DW_LNS_define_file + 1. + * current generator uses opcode base of 13 which must equal DW_LNS_define_file + 1. */ private static final int DW_LN_OPCODE_BASE = 13; @@ -62,15 +66,15 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { * standard opcodes defined by Dwarf 2 */ /* - * 0 can be returned to indicate an invalid opcode + * 0 can be returned to indicate an invalid opcode */ private static final byte DW_LNS_undefined = 0; /* - * 0 can be inserted as a prefix for extended opcodes + * 0 can be inserted as a prefix for extended opcodes */ private static final byte DW_LNS_extended_prefix = 0; /* - * append current state as matrix row 0 args + * append current state as matrix row 0 args */ private static final byte DW_LNS_copy = 1; /* @@ -78,7 +82,7 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { */ private static final byte DW_LNS_advance_pc = 2; /* - * increment line 1 sleb arg + * increment line 1 sleb arg */ private static final byte DW_LNS_advance_line = 3; /* @@ -86,15 +90,15 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { */ private static final byte DW_LNS_set_file = 4; /* - * set column 1 uleb arg + * set column 1 uleb arg */ private static final byte DW_LNS_set_column = 5; /* - * flip is_stmt 0 args + * flip is_stmt 0 args */ private static final byte DW_LNS_negate_stmt = 6; /* - * set end sequence and copy row 0 args + * set end sequence and copy row 0 args */ private static final byte DW_LNS_set_basic_block = 7; /* @@ -112,9 +116,10 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { /* * there is no extended opcode 0 */ - // private static final byte DW_LNE_undefined = 0; + @SuppressWarnings("unused") + private static final byte DW_LNE_undefined = 0; /* - * end sequence of addresses + * end sequence of addresses */ private static final byte DW_LNE_end_sequence = 1; /* @@ -138,8 +143,8 @@ public String getSectionName() { @Override public void createContent() { /* - * we need to create a header, dir table, file table and line - * number table encoding for each CU + * we need to create a header, dir table, file table and line number table encoding for each + * CU */ /* @@ -168,16 +173,30 @@ public void createContent() { public int headerSize() { /* * header size is standard 31 bytes - * uint32 total_length - * uint16 version - * uint32 prologue_length - * uint8 min_insn_length - * uint8 default_is_stmt - * int8 line_base - * uint8 line_range - * uint8 opcode_base - * uint8 li_opcode_base - * uint8[opcode_base-1] standard_opcode_lengths + * + *
    + * + *
  • uint32 total_length + * + *
  • uint16 version + * + *
  • uint32 prologue_length + * + *
  • uint8 min_insn_length + * + *
  • uint8 default_is_stmt + * + *
  • int8 line_base + * + *
  • uint8 line_range + * + *
  • uint8 opcode_base + * + *
  • uint8 li_opcode_base + * + *
  • uint8[opcode_base-1] standard_opcode_lengths + * + *
*/ return DW_LN_HEADER_SIZE; @@ -185,13 +204,10 @@ public int headerSize() { public int computeDirTableSize(ClassEntry classEntry) { /* - * table contains a sequence of 'nul'-terminated - * dir name bytes followed by an extra 'nul' - * and then a sequence of 'nul'-terminated - * file name bytes followed by an extra 'nul' + * table contains a sequence of 'nul'-terminated dir name bytes followed by an extra 'nul' + * and then a sequence of 'nul'-terminated file name bytes followed by an extra 'nul' * - * for now we assume dir and file names are ASCII - * byte strings + * for now we assume dir and file names are ASCII byte strings */ int dirSize = 0; for (DirEntry dir : classEntry.getLocalDirs()) { @@ -206,13 +222,10 @@ public int computeDirTableSize(ClassEntry classEntry) { public int computeFileTableSize(ClassEntry classEntry) { /* - * table contains a sequence of 'nul'-terminated - * dir name bytes followed by an extra 'nul' - * and then a sequence of 'nul'-terminated - * file name bytes followed by an extra 'nul' - - * for now we assume dir and file names are ASCII - * byte strings + * table contains a sequence of 'nul'-terminated dir name bytes followed by an extra 'nul' + * and then a sequence of 'nul'-terminated file name bytes followed by an extra 'nul' + * + * for now we assume dir and file names are ASCII byte strings */ int fileSize = 0; for (FileEntry localEntry : classEntry.getLocalFiles()) { @@ -221,6 +234,8 @@ public int computeFileTableSize(ClassEntry classEntry) { */ String baseName = localEntry.getFileName(); int length = baseName.length(); + /* we should never have a null or zero length entry in local files */ + assert length > 0; fileSize += length + 1; DirEntry dirEntry = localEntry.getDirEntry(); int idx = classEntry.localDirsIdx(dirEntry); @@ -239,9 +254,9 @@ public int computeFileTableSize(ClassEntry classEntry) { public int computeLineNUmberTableSize(ClassEntry classEntry) { /* - * sigh -- we have to do this by generating the - * content even though we cannot write it into a byte[] - */ + * sigh -- we have to do this by generating the content even though we cannot write it into + * a byte[] + */ return writeLineNumberTable(classEntry, null, 0); } @@ -253,9 +268,8 @@ public byte[] getOrDecideContent(Map alre Object valueObj = decisionMap.getDecidedValue(LayoutDecision.Kind.VADDR); if (valueObj != null && valueObj instanceof Number) { /* - * this may not be the final vaddr for the text segment - * but it will be close enough to make debug easier - * i.e. to within a 4k page or two + * this may not be the final vaddr for the text segment but it will be close enough + * to make debug easier i.e. to within a 4k page or two */ debugTextBase = ((Number) valueObj).longValue(); } @@ -304,8 +318,7 @@ public int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { */ pos = putShort(DW_VERSION_2, buffer, pos); /* - * 4 ubyte prologue length includes rest of header and - * dir + file table section + * 4 ubyte prologue length includes rest of header and dir + file table section */ int prologueSize = classEntry.getLinePrologueSize() - 6; pos = putInt(prologueSize, buffer, pos); @@ -346,7 +359,7 @@ public int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { putByte((byte) 0, buffer, pos + 5); /* DW_LNS_set_basic_block */ putByte((byte) 0, buffer, pos + 6); - /* DW_LNS_const_add_pc */ + /* DW_LNS_const_add_pc */ putByte((byte) 0, buffer, pos + 7); /* DW_LNS_fixed_advance_pc */ putByte((byte) 1, buffer, pos + 8); @@ -411,10 +424,14 @@ public int writeFileTable(ClassEntry classEntry, byte[] buffer, int p) { public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { int pos = p; + FileEntry fileEntry = classEntry.getFileEntry(); + if (fileEntry == null) { + return pos; + } /* * the primary file entry should always be first in the local files list */ - assert classEntry.localFilesIdx(classEntry.getFileEntry()) == 1; + assert classEntry.localFilesIdx(fileEntry) == 1; String primaryClassName = classEntry.getClassName(); String primaryFileName = classEntry.getFileName(); String file = primaryFileName; @@ -425,9 +442,8 @@ public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { Range primaryRange = primaryEntry.getPrimary(); assert primaryRange.getFileName().equals(primaryFileName); /* - * each primary represents a method i.e. a contiguous - * sequence of subranges. we assume the default state - * at the start of each sequence because we always post an + * each primary represents a method i.e. a contiguous sequence of subranges. we assume + * the default state at the start of each sequence because we always post an * end_sequence when we finish all the subranges in the method */ long line = primaryRange.getLine(); @@ -443,7 +459,7 @@ public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { * set state for primary */ debug(" [0x%08x] primary range [0x%08x, 0x%08x] %s:%d\n", pos, debugTextBase + primaryRange.getLo(), debugTextBase + primaryRange.getHi(), primaryRange.getFullMethodName(), - primaryRange.getLine()); + primaryRange.getLine()); /* * initialize and write a row for the start of the primary method @@ -452,11 +468,10 @@ public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { pos = putSetBasicBlock(buffer, pos); /* * address is currently 0 - */ + */ pos = putSetAddress(address, buffer, pos); /* - * state machine value of line is currently 1 - * increment to desired line + * state machine value of line is currently 1 increment to desired line */ if (line != 1) { pos = putAdvanceLine(line - 1, buffer, pos); @@ -470,6 +485,9 @@ public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { assert subrange.getLo() >= primaryRange.getLo(); assert subrange.getHi() <= primaryRange.getHi(); FileEntry subFileEntry = primaryEntry.getSubrangeFileEntry(subrange); + if (subFileEntry == null) { + continue; + } String subfile = subFileEntry.getFileName(); int subFileIdx = classEntry.localFilesIdx(subFileEntry); long subLine = subrange.getLine(); @@ -486,12 +504,10 @@ public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { debug(" [0x%08x] missing line info - staying put at %s:%d\n", pos, file, line); } /* - * there is a temptation to append end sequence at here - * when the hiAddress lies strictly between the current - * address and the start of the next subrange because, - * ostensibly, we have void space between the end of - * the current subrange and the start of the next one. - * however, debug works better if we treat all the insns up + * there is a temptation to append end sequence at here when the hiAddress lies + * strictly between the current address and the start of the next subrange because, + * ostensibly, we have void space between the end of the current subrange and the + * start of the next one. however, debug works better if we treat all the insns up * to the next range start as belonging to the current line * * if we have to update to a new file then do so @@ -505,8 +521,7 @@ public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { fileIdx = subFileIdx; } /* - * check if we can advance line and/or address in - * one byte with a special opcode + * check if we can advance line and/or address in one byte with a special opcode */ long lineDelta = subLine - line; long addressDelta = subAddressLo - address; @@ -520,15 +535,14 @@ public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { } } else { /* - * does it help to divide and conquer using - * a fixed address increment + * does it help to divide and conquer using a fixed address increment */ int remainder = isConstAddPC(addressDelta); if (remainder > 0) { pos = putConstAddPC(buffer, pos); /* - * the remaining address can be handled with a - * special opcode but what about the line delta + * the remaining address can be handled with a special opcode but what about + * the line delta */ opcode = isSpecialOpcode(remainder, lineDelta); if (opcode != DW_LNS_undefined) { @@ -538,8 +552,8 @@ public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { pos = putSpecialOpcode(opcode, buffer, pos); } else { /* - * ok, bump the line separately then use a - * special opcode for the address remainder + * ok, bump the line separately then use a special opcode for the + * address remainder */ opcode = isSpecialOpcode(remainder, 0); assert opcode != DW_LNS_undefined; @@ -554,8 +568,8 @@ public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { pos = putAdvanceLine(lineDelta, buffer, pos); } /* - * n.b. we might just have had an out of range line increment - * with a zero address increment + * n.b. we might just have had an out of range line increment with a zero + * address increment */ if (addressDelta > 0) { /* @@ -655,6 +669,7 @@ public int putSetFile(String file, long uleb, byte[] buffer, int p) { } } + @SuppressWarnings("unused") public int putSetColumn(long uleb, byte[] buffer, int p) { byte opcode = DW_LNS_set_column; int pos = p; @@ -667,6 +682,7 @@ public int putSetColumn(long uleb, byte[] buffer, int p) { } } + @SuppressWarnings("unused") public int putNegateStmt(byte[] buffer, int p) { byte opcode = DW_LNS_negate_stmt; int pos = p; @@ -826,7 +842,7 @@ public int putSpecialOpcode(byte opcode, byte[] buffer, int p) { debugAddress += opcodeAddress(opcode); debugLine += opcodeLine(opcode); debug(" [0x%08x] Special Opcode %d: advance Address by %d to 0x%08x and Line by %d to %d\n", - pos, opcodeId(opcode), opcodeAddress(opcode), debugAddress, opcodeLine(opcode), debugLine); + pos, opcodeId(opcode), opcodeAddress(opcode), debugAddress, opcodeLine(opcode), debugLine); return putByte(opcode, buffer, pos); } } @@ -842,8 +858,7 @@ public static byte isSpecialOpcode(long addressDelta, long lineDelta) { long offsetLineDelta = lineDelta - DW_LN_LINE_BASE; if (offsetLineDelta < DW_LN_LINE_RANGE) { /* - * line_delta can be encoded - * check if address is ok + * line_delta can be encoded check if address is ok */ if (addressDelta <= MAX_ADDRESS_ONLY_DELTA) { long opcode = DW_LN_OPCODE_BASE + (addressDelta * DW_LN_LINE_RANGE) + offsetLineDelta; @@ -886,8 +901,8 @@ public String targetSectionName() { } public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET, + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET, }; @Override diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java index b508ef12f6a9..548ac07ab61a 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java @@ -31,6 +31,7 @@ import com.oracle.objectfile.LayoutDecision; import com.oracle.objectfile.LayoutDecisionMap; import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.elf.ELFObjectFile; import java.nio.ByteOrder; @@ -40,8 +41,7 @@ import static com.oracle.objectfile.elf.dwarf.DwarfSections.TEXT_SECTION_NAME; /** - * class from which all DWARF debug sections - * inherit providing common behaviours. + * class from which all DWARF debug sections inherit providing common behaviours. */ public abstract class DwarfSectionImpl extends BasicProgbitsSectionImpl { protected DwarfSections dwarfSections; @@ -55,26 +55,20 @@ public DwarfSectionImpl(DwarfSections dwarfSections) { } /** - * creates the target byte[] array used to define the section - * contents. + * creates the target byte[] array used to define the section contents. * - * the main task of this method is to precompute the - * size of the debug section. given the complexity of the - * data layouts that invariably requires performing a dummy - * write of the contents, inserting bytes into a small, - * scratch buffer only when absolutely necessary. subclasses - * may also cache some information for use when writing the - * contents. + * the main task of this method is to precompute the size of the debug section. given the + * complexity of the data layouts that invariably requires performing a dummy write of the + * contents, inserting bytes into a small, scratch buffer only when absolutely necessary. + * subclasses may also cache some information for use when writing the contents. */ public abstract void createContent(); /** - * populates the byte[] array used to contain the section - * contents. + * populates the byte[] array used to contain the section contents. * - * in most cases this task reruns the operations performed - * under createContent but this time actually writing data - * to the target byte[]. + * in most cases this task reruns the operations performed under createContent but this time + * actually writing data to the target byte[]. */ public abstract void writeContent(); @@ -88,8 +82,7 @@ public boolean isLoadable() { public void checkDebug(int pos) { /* - * if the env var relevant to this element - * type is set then switch on debugging + * if the env var relevant to this element type is set then switch on debugging */ String name = getSectionName(); String envVarName = "DWARF_" + name.substring(1).toUpperCase(); @@ -279,6 +272,7 @@ public int writeAttrAddress(long address, byte[] buffer, int pos) { } } + @SuppressWarnings("unused") public int writeAttrData8(long value, byte[] buffer, int pos) { if (buffer == null) { return pos + putLong(value, scratch, 0); @@ -312,22 +306,24 @@ public int writeAttrNull(byte[] buffer, int pos) { } /** - * identify the section after which this debug section - * needs to be ordered when sizing and creating content. + * identify the section after which this debug section needs to be ordered when sizing and + * creating content. + * * @return the name of the preceding section */ public abstract String targetSectionName(); /** - * identify the layout properties of the target section - * which need to have been decided before the contents - * of this section can be created. + * identify the layout properties of the target section which need to have been decided before + * the contents of this section can be created. + * * @return an array of the relevant decision kinds */ public abstract LayoutDecision.Kind[] targetSectionKinds(); /** * identify this debug section by name. + * * @return the name of the debug section */ public abstract String getSectionName(); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java index 0ba4bd7543ea..5d26129bf746 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java @@ -26,6 +26,11 @@ package com.oracle.objectfile.elf.dwarf; +import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.DirEntry; +import com.oracle.objectfile.debugentry.FileEntry; +import com.oracle.objectfile.debugentry.Range; +import com.oracle.objectfile.debugentry.StringTable; import com.oracle.objectfile.debuginfo.DebugInfoProvider; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; import com.oracle.objectfile.elf.ELFMachine; @@ -38,18 +43,14 @@ import java.util.Map; /** - * A class that models the debug info in an - * organization that facilitates generation of the - * required DWARF sections. It groups common data and - * behaviours for use by the various subclasses of - * class DwarfSectionImpl that take responsibility - * for generating content for a specific section type. + * A class that models the debug info in an organization that facilitates generation of the required + * DWARF sections. It groups common data and behaviours for use by the various subclasses of class + * DwarfSectionImpl that take responsibility for generating content for a specific section type. */ public class DwarfSections { /* - * names of the different ELF sections we create or reference - * in reverse dependency order + * names of the different ELF sections we create or reference in reverse dependency order */ public static final String TEXT_SECTION_NAME = ".text"; public static final String DW_STR_SECTION_NAME = ".debug_str"; @@ -89,19 +90,24 @@ public class DwarfSections { public static final int DW_AT_hi_pc = 0x12; public static final int DW_AT_language = 0x13; public static final int DW_AT_external = 0x3f; - // public static final int DW_AT_return_addr = 0x2a; - // public static final int DW_AT_frame_base = 0x40; + @SuppressWarnings("unused") + public static final int DW_AT_return_addr = 0x2a; + @SuppressWarnings("unused") + public static final int DW_AT_frame_base = 0x40; /* * define all the Dwarf attribute forms we need for our DIEs */ public static final int DW_FORM_null = 0x0; - // private static final int DW_FORM_string = 0x8; + @SuppressWarnings("unused") + private static final int DW_FORM_string = 0x8; public static final int DW_FORM_strp = 0xe; public static final int DW_FORM_addr = 0x1; public static final int DW_FORM_data1 = 0x0b; public static final int DW_FORM_data4 = 0x6; - // public static final int DW_FORM_data8 = 0x7; - // public static final int DW_FORM_block1 = 0x0a; + @SuppressWarnings("unused") + public static final int DW_FORM_data8 = 0x7; + @SuppressWarnings("unused") + public static final int DW_FORM_block1 = 0x0a; public static final int DW_FORM_flag = 0xc; /* @@ -115,7 +121,8 @@ public class DwarfSections { /* * DW_FORM_flag attribute values */ - // public static final byte DW_FLAG_false = 0; + @SuppressWarnings("unused") + public static final byte DW_FLAG_false = 0; public static final byte DW_FLAG_true = 1; /* * value for DW_AT_language attribute with form DATA1 @@ -127,15 +134,21 @@ public class DwarfSections { * * not needed until we make functions members */ - // public static final byte DW_ACCESS_public = 1; - // public static final byte DW_ACCESS_protected = 2; - // public static final byte DW_ACCESS_private = 3; + @SuppressWarnings("unused") + public static final byte DW_ACCESS_public = 1; + @SuppressWarnings("unused") + public static final byte DW_ACCESS_protected = 2; + @SuppressWarnings("unused") + public static final byte DW_ACCESS_private = 3; /* * others not yet needed */ - // public static final int DW_AT_type = 0; // only present for non-void functions - // public static final int DW_AT_accessibility = 0; + @SuppressWarnings("unused") + public static final int DW_AT_type = 0; // only present for non-void + // functions + @SuppressWarnings("unused") + public static final int DW_AT_accessibility = 0; /* * CIE and FDE entries @@ -143,31 +156,38 @@ public class DwarfSections { /* full byte/word values */ public static final int DW_CFA_CIE_id = -1; - // public static final int DW_CFA_FDE_id = 0; + @SuppressWarnings("unused") + public static final int DW_CFA_FDE_id = 0; public static final byte DW_CFA_CIE_version = 1; /* values encoded in high 2 bits */ public static final byte DW_CFA_advance_loc = 0x1; public static final byte DW_CFA_offset = 0x2; - // public static final byte DW_CFA_restore = 0x3; + @SuppressWarnings("unused") + public static final byte DW_CFA_restore = 0x3; /* values encoded in low 6 bits */ public static final byte DW_CFA_nop = 0x0; - // public static final byte DW_CFA_set_loc1 = 0x1; + @SuppressWarnings("unused") + public static final byte DW_CFA_set_loc1 = 0x1; public static final byte DW_CFA_advance_loc1 = 0x2; public static final byte DW_CFA_advance_loc2 = 0x3; public static final byte DW_CFA_advance_loc4 = 0x4; - // public static final byte DW_CFA_offset_extended = 0x5; - // public static final byte DW_CFA_restore_extended = 0x6; - // public static final byte DW_CFA_undefined = 0x7; - // public static final byte DW_CFA_same_value = 0x8; + @SuppressWarnings("unused") + public static final byte DW_CFA_offset_extended = 0x5; + @SuppressWarnings("unused") + public static final byte DW_CFA_restore_extended = 0x6; + @SuppressWarnings("unused") + public static final byte DW_CFA_undefined = 0x7; + @SuppressWarnings("unused") + public static final byte DW_CFA_same_value = 0x8; public static final byte DW_CFA_register = 0x9; public static final byte DW_CFA_def_cfa = 0xc; - // public static final byte DW_CFA_def_cfa_register = 0xd; + @SuppressWarnings("unused") + public static final byte DW_CFA_def_cfa_register = 0xd; public static final byte DW_CFA_def_cfa_offset = 0xe; - private ELFMachine elfMachine; private ByteOrder byteOrder; private DwarfStrSectionImpl dwarfStrSection; private DwarfAbbrevSectionImpl dwarfAbbrevSection; @@ -177,7 +197,6 @@ public class DwarfSections { private DwarfFrameSectionImpl dwarfFameSection; public DwarfSections(ELFMachine elfMachine, ByteOrder byteOrder) { - this.elfMachine = elfMachine; this.byteOrder = byteOrder; dwarfStrSection = new DwarfStrSectionImpl(this); dwarfAbbrevSection = new DwarfAbbrevSectionImpl(this); @@ -216,19 +235,14 @@ public DwarfLineSectionImpl getLineSectionImpl() { } /** - * a table listing all known strings, some of - * which may be marked for insertion into the + * a table listing all known strings, some of which may be marked for insertion into the * debug_str section. */ private StringTable stringTable = new StringTable(); /** - * list detailing all dirs in which files are found to reside - * either as part of substrate/compiler or user code. - */ - private LinkedList dirs = new LinkedList<>(); - /** - * index of already seen dirs. + * index of all dirs in which files are found to reside either as part of substrate/compiler or + * user code. */ private Map dirsIndex = new HashMap<>(); @@ -236,6 +250,7 @@ public DwarfLineSectionImpl getLineSectionImpl() { * 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 @@ -243,21 +258,22 @@ public DwarfLineSectionImpl getLineSectionImpl() { * 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. + * 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. + * 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. */ /** @@ -265,25 +281,18 @@ public DwarfLineSectionImpl getLineSectionImpl() { */ private LinkedList primaryClasses = new LinkedList<>(); /** - * index of already seen classes. + * index of already seen classes. */ private Map primaryClassesIndex = new HashMap<>(); /** - * list of files which contain primary ranges. - */ - private LinkedList primaryFiles = new LinkedList<>(); - /** - * List of files which contain primary or secondary ranges. - */ - private LinkedList files = new LinkedList<>(); - /** - * index of already seen files. + * index of files which contain primary or secondary ranges. */ private Map filesIndex = new HashMap<>(); /** * indirects this call to the string table. + * * @param string the string to be inserted * @return a unique equivalent String */ @@ -292,11 +301,10 @@ public String uniqueString(String string) { } /** - * indirects this call to the string table, ensuring - * the table entry is marked for inclusion in the - * debug_str section. - * @param string the string to be inserted and - * marked for inclusion in the debug_str section + * indirects this call to the string table, ensuring the table entry is marked for inclusion in + * the debug_str section. + * + * @param string the string to be inserted and marked for inclusion in the debug_str section * @return a unique equivalent String */ public String uniqueDebugString(String string) { @@ -305,26 +313,23 @@ public String uniqueDebugString(String string) { /** * 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 + * @return the offset of the string in the .debug_str section */ public int debugStringIndex(String string) { return stringTable.debugStringIndex(string); } /** - * entry point allowing ELFObjectFile to pass on information - * about types, code and heap data. - * @param 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 */ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { /* - * DebugTypeInfoProvider typeInfoProvider = debugInfoProvider.typeInfoProvider(); - * for (DebugTypeInfo debugTypeInfo : typeInfoProvider) { - * install types - * } + * DebugTypeInfoProvider typeInfoProvider = debugInfoProvider.typeInfoProvider(); for + * (DebugTypeInfo debugTypeInfo : typeInfoProvider) { install types } */ /* @@ -349,8 +354,8 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { Range primaryRange = new Range(fileName, filePath, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, primaryLine); /* * System.out.format("arange: [0x%08x,0x%08x) %s %s::%s(%s) %s\n", lo, hi, - * returnTypeName, className, methodName, paramNames, fileName); - * create an infoSection entry for the method + * returnTypeName, className, methodName, paramNames, fileName); create an infoSection + * entry for the method */ addRange(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); debugCodeInfo.lineInfoProvider().forEach(debugLineInfo -> { @@ -371,11 +376,9 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { }); }); /* - * DebugDataInfoProvider dataInfoProvider = debugInfoProvider.dataInfoProvider(); - * for (DebugDataInfo debugDataInfo : dataInfoProvider) { - * install details of heap elements - * String name = debugDataInfo.toString(); - * } + * DebugDataInfoProvider dataInfoProvider = debugInfoProvider.dataInfoProvider(); for + * (DebugDataInfo debugDataInfo : dataInfoProvider) { install details of heap elements + * String name = debugDataInfo.toString(); } */ } @@ -400,6 +403,9 @@ public ClassEntry ensureClassEntry(Range range) { public FileEntry ensureFileEntry(Range range) { String fileName = range.getFileName(); + if (fileName == null) { + return null; + } Path filePath = range.getFilePath(); Path fileAsPath = range.getFileAsPath(); /* @@ -409,17 +415,15 @@ public FileEntry ensureFileEntry(Range range) { if (fileEntry == null) { DirEntry dirEntry = ensureDirEntry(filePath); fileEntry = new FileEntry(fileName, dirEntry); - files.add(fileEntry); - filesIndex.put(fileAsPath, fileEntry); /* - * if this is a primary entry then add it to the primary list + * index the file entry by file path */ - if (range.isPrimary()) { - primaryFiles.add(fileEntry); - } else { + filesIndex.put(fileAsPath, fileEntry); + if (!range.isPrimary()) { + /* check we have a file for the corresponding primary range */ Range primaryRange = range.getPrimary(); - FileEntry primaryEntry = filesIndex.get(primaryRange.getFileAsPath()); - assert primaryEntry != null; + FileEntry primaryFileEntry = filesIndex.get(primaryRange.getFileAsPath()); + assert primaryFileEntry != null; } } return fileEntry; @@ -428,7 +432,7 @@ public FileEntry ensureFileEntry(Range range) { public void addRange(Range primaryRange, List frameSizeInfos, int frameSize) { assert primaryRange.isPrimary(); ClassEntry classEntry = ensureClassEntry(primaryRange); - PrimaryEntry entry = classEntry.addPrimary(primaryRange, frameSizeInfos, frameSize); + classEntry.addPrimary(primaryRange, frameSizeInfos, frameSize); } public void addSubRange(Range primaryRange, Range subrange) { @@ -436,13 +440,14 @@ public void addSubRange(Range primaryRange, Range subrange) { assert !subrange.isPrimary(); String className = primaryRange.getClassName(); ClassEntry classEntry = primaryClassesIndex.get(className); - FileEntry subrangeEntry = ensureFileEntry(subrange); + FileEntry subrangeFileEntry = ensureFileEntry(subrange); /* - * the primary range should already have been seen - * and associated with a primary class entry + * the primary range should already have been seen and associated with a primary class entry */ assert classEntry.primaryIndexFor(primaryRange) != null; - classEntry.addSubRange(subrange, subrangeEntry); + if (subrangeFileEntry != null) { + classEntry.addSubRange(subrange, subrangeFileEntry); + } } public DirEntry ensureDirEntry(Path filePath) { @@ -453,16 +458,18 @@ public DirEntry ensureDirEntry(Path filePath) { if (dirEntry == null) { dirEntry = new DirEntry(filePath); dirsIndex.put(filePath, dirEntry); - dirs.add(dirEntry); } return dirEntry; } + public StringTable getStringTable() { return stringTable; } + public LinkedList getPrimaryClasses() { return primaryClasses; } + public ByteOrder getByteOrder() { return byteOrder; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java index 0bcdbccb5992..845ab12ad8b2 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java @@ -27,9 +27,11 @@ package com.oracle.objectfile.elf.dwarf; import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.debugentry.StringEntry; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_STR_SECTION_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfSections.TEXT_SECTION_NAME; + /** * generator for debug_str section. */ @@ -94,10 +96,10 @@ public String targetSectionName() { * debug_str section content depends on text section content and offset. */ public final LayoutDecision.Kind[] targetSectionKinds = { - LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET, - /* add this so we can use the text section base address for debug */ - LayoutDecision.Kind.VADDR, + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.OFFSET, + /* add this so we can use the text section base address for debug */ + LayoutDecision.Kind.VADDR, }; @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 2d6ecba957b6..61fbd491316c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -459,4 +459,7 @@ protected void onValueUpdate(EconomicMap, Object> values, Integer o } } }; + @Option(help = "Search path for source files for Application or GraalVM classes (list of comma-separated directories or jar files)")// + public static final HostedOptionKey DebugInfoSourceSearchPath = new HostedOptionKey(null) { + }; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java index f30236f99dd1..56409f77dda4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java @@ -54,6 +54,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.hosted.image.sources.SourceManager; import com.oracle.svm.hosted.meta.HostedType; @@ -974,12 +975,12 @@ protected NativeTextSectionImpl(RelocatableBuffer relocatableBuffer, ObjectFile } /** - * implementation of the DebugInfoProvider API interface - * that allows type, code and heap data info to be passed to - * an ObjectFile when generation of debug info is enabled. + * implementation of the DebugInfoProvider API interface that allows type, code and heap data + * info to be passed to an ObjectFile when generation of debug info is enabled. */ private class NativeImageDebugInfoProvider implements DebugInfoProvider { private final NativeImageCodeCache codeCache; + @SuppressWarnings("unused") private final NativeImageHeap heap; NativeImageDebugInfoProvider(NativeImageCodeCache codeCache, NativeImageHeap heap) { @@ -1004,48 +1005,35 @@ public Stream dataInfoProvider() { } } - private static final String[] GRAAL_SRC_PACKAGE_PREFIXES = { - "org.graalvm", - "com.oracle.graal", - "com.oracle.objectfile", - "com.oracle.svm", - "com.oracle.truffle", - }; - - /** - * implementation of the DebugCodeInfo API interface - * that allows code info to be passed to an ObjectFile - * when generation of debug info is enabled. + * implementation of the DebugCodeInfo API interface that allows code info to be passed to an + * ObjectFile when generation of debug info is enabled. */ private class NativeImageDebugCodeInfo implements DebugCodeInfo { private final HostedMethod method; + private final ResolvedJavaType javaType; private final CompilationResult compilation; private Path fullFilePath; NativeImageDebugCodeInfo(HostedMethod method, CompilationResult compilation) { this.method = method; + HostedType declaringClass = method.getDeclaringClass(); + Class clazz = declaringClass.getJavaClass(); + this.javaType = declaringClass.getWrapped(); this.compilation = compilation; - this.fullFilePath = null; + fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(javaType, clazz); } @Override public String fileName() { - if (fullFilePath == null) { - HostedType declaringClass = method.getDeclaringClass(); - fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass); - } if (fullFilePath != null) { return fullFilePath.getFileName().toString(); } return null; } + @Override public Path filePath() { - if (fullFilePath == null) { - HostedType declaringClass = method.getDeclaringClass(); - fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass); - } if (fullFilePath != null) { return fullFilePath.getParent(); } @@ -1054,7 +1042,7 @@ public Path filePath() { @Override public String className() { - return method.format("%H"); + return javaType.toClassName(); } @Override @@ -1099,10 +1087,12 @@ public Stream lineInfoProvider() { return compilation.getSourceMappings().stream().map(sourceMapping -> new NativeImageDebugLineInfo(sourceMapping)); } + @Override public int getFrameSize() { return compilation.getTotalFrameSize(); } + @Override public List getFrameSizeChanges() { List frameSizeChanges = new LinkedList<>(); for (Mark mark : compilation.getMarks()) { @@ -1125,43 +1115,36 @@ public List getFrameSizeChanges() { } /** - * implementation of the DebugLineInfo API interface - * that allows line number info to be passed to an - * ObjectFile when generation of debug info is enabled. + * implementation of the DebugLineInfo API interface that allows line number info to be passed + * to an ObjectFile when generation of debug info is enabled. */ private class NativeImageDebugLineInfo implements DebugLineInfo { private final int bci; private final ResolvedJavaMethod method; private final int lo; private final int hi; - private Path fullFilePath = null; + private Path fullFilePath; NativeImageDebugLineInfo(SourceMapping sourceMapping) { NodeSourcePosition position = sourceMapping.getSourcePosition(); - int bci = position.getBCI(); - this.bci = (bci >= 0 ? bci : 0); + int posbci = position.getBCI(); + this.bci = (posbci >= 0 ? posbci : 0); this.method = position.getMethod(); this.lo = sourceMapping.getStartOffset(); this.hi = sourceMapping.getEndOffset(); + computeFullFilePath(); } @Override public String fileName() { - if (fullFilePath == null) { - ResolvedJavaType declaringClass = method.getDeclaringClass(); - fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass); - } if (fullFilePath != null) { return fullFilePath.getFileName().toString(); } return null; } + @Override public Path filePath() { - if (fullFilePath == null) { - ResolvedJavaType declaringClass = method.getDeclaringClass(); - fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass); - } if (fullFilePath != null) { return fullFilePath.getParent(); } @@ -1196,12 +1179,31 @@ public int line() { } return -1; } + + private void computeFullFilePath() { + ResolvedJavaType declaringClass = method.getDeclaringClass(); + Class clazz = null; + if (declaringClass instanceof OriginalClassProvider) { + clazz = ((OriginalClassProvider) declaringClass).getJavaClass(); + } + /* + * HostedType and AnalysisType punt calls to getSourceFilename to the wrapped class so + * for consistency we need to do the path lookup relative to the wrapped class + */ + if (declaringClass instanceof HostedType) { + declaringClass = ((HostedType) declaringClass).getWrapped(); + } + if (declaringClass instanceof AnalysisType) { + declaringClass = ((AnalysisType) declaringClass).getWrapped(); + } + fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass, clazz); + } + } /** - * implementation of the DebugFrameSizeChange API interface - * that allows stack frame size change info to be passed to - * an ObjectFile when generation of debug info is enabled. + * implementation of the DebugFrameSizeChange API interface that allows stack frame size change + * info to be passed to an ObjectFile when generation of debug info is enabled. */ private class NativeImageDebugFrameSizeChange implements DebugFrameSizeChange { private int offset; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java index 1a9ebc2304fa..2f6dd613ba45 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java @@ -34,60 +34,82 @@ import java.nio.file.Path; import java.nio.file.Paths; +import static com.oracle.svm.hosted.image.sources.SourceCacheType.APPLICATION; + public class ApplicationSourceCache extends SourceCache { /** - * create an application source cache + * Create an application source cache. */ protected ApplicationSourceCache() { - super(SourceCache.APPLICATION_CACHE_KEY); initSrcRoots(); } + @Override + protected final SourceCacheType getType() { + return APPLICATION; + } + private void initSrcRoots() { - String javaClassPath = System.getProperty(JAVA_CLASSPATH_PROP); - assert javaClassPath != null; - String[] classPathEntries = javaClassPath.split(File.pathSeparator); - /* add dirs or jars found in the classpath */ + /* Add dirs or jars found in the classpath */ for (String classPathEntry : classPathEntries) { - Path entryPath = Paths.get(classPathEntry); - String fileNameString = entryPath.getFileName().toString(); - if (fileNameString.endsWith(".jar")) { - // application jar /path/to/xxx.jar should have - // sources /path/to/xxx-sources.jar + tryClassPathRoot(classPathEntry); + } + for (String sourcePathEntry : sourcePathEntries) { + trySourceRoot(sourcePathEntry); + } + } + + private void tryClassPathRoot(String classPathEntry) { + trySourceRoot(classPathEntry, true); + } + + private void trySourceRoot(String sourcePathEntry) { + trySourceRoot(sourcePathEntry, false); + } + + private void trySourceRoot(String sourceRoot, boolean fromClassPath) { + Path sourcePath = Paths.get(sourceRoot); + String fileNameString = sourcePath.getFileName().toString(); + if (fileNameString.endsWith(".jar") || fileNameString.endsWith(".zip")) { + if (fromClassPath && fileNameString.endsWith(".jar")) { + /* + * application jar /path/to/xxx.jar should have sources /path/to/xxx-sources.jar + */ int length = fileNameString.length(); - String srcFileNameString = fileNameString.substring(0, length - 4) + "-sources.zip"; - Path srcPath = entryPath.getParent().resolve(srcFileNameString); - if (srcPath.toFile().exists()) { - try { - FileSystem fileSystem = FileSystems.newFileSystem(srcPath, null); - for (Path root : fileSystem.getRootDirectories()) { - srcRoots.add(root); - } - } catch (IOException ioe) { - /* ignore this entry */ - } catch (FileSystemNotFoundException fnfe) { - /* ignore this entry */ + fileNameString = fileNameString.substring(0, length - 4) + "-sources.zip"; + } + sourcePath = sourcePath.getParent().resolve(fileNameString); + if (sourcePath.toFile().exists()) { + try { + FileSystem fileSystem = FileSystems.newFileSystem(sourcePath, null); + for (Path root : fileSystem.getRootDirectories()) { + srcRoots.add(root); } + } catch (IOException ioe) { + /* ignore this entry */ + } catch (FileSystemNotFoundException fnfe) { + /* ignore this entry */ } - } else { + } + } else { + if (fromClassPath) { /* - * for dir entries ending in classes or target/classes - * look for a parallel src tree - */ - if (entryPath.endsWith("classes")) { - Path parent = entryPath.getParent(); + * for dir entries ending in classes or target/classes translate to a parallel src + * tree + */ + if (sourcePath.endsWith("classes")) { + Path parent = sourcePath.getParent(); if (parent.endsWith("target")) { parent = parent.getParent(); } - Path srcPath = (parent.resolve("src")); - File file = srcPath.toFile(); - if (file.exists() && file.isDirectory()) { - srcRoots.add(srcPath); - } + sourcePath = (parent.resolve("src")); } } + // try the path as provided + File file = sourcePath.toFile(); + if (file.exists() && file.isDirectory()) { + srcRoots.add(sourcePath); + } } - /* add the current working directory as a path of last resort */ - srcRoots.add(Paths.get(".")); } -} +} \ No newline at end of file diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java index da8bfd6cde07..833166c2daff 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java @@ -35,68 +35,96 @@ import java.nio.file.Path; import java.nio.file.Paths; +import static com.oracle.svm.hosted.image.sources.SourceCacheType.GRAALVM; import static com.oracle.svm.hosted.image.sources.SourceManager.GRAALVM_SRC_PACKAGE_PREFIXES; + public class GraalVMSourceCache extends SourceCache { /** - * create a GraalVM source cache + * Create a GraalVM source cache. */ protected GraalVMSourceCache() { - super(SourceCache.GRAALVM_CACHE_KEY); initSrcRoots(); } + @Override + protected final SourceCacheType getType() { + return GRAALVM; + } + private static final String JAVA_CLASSPATH_PROP = "java.class.path"; private void initSrcRoots() { - String javaClassPath = System.getProperty(JAVA_CLASSPATH_PROP); - assert javaClassPath != null; - String[] classPathEntries = javaClassPath.split(File.pathSeparator); for (String classPathEntry : classPathEntries) { - Path entryPath = Paths.get(classPathEntry); - String fileNameString = entryPath.getFileName().toString(); - if (fileNameString.endsWith(".jar")) { - // GraalVM jar /path/to/xxx.jar should have - // sources /path/to/xxx.src.zip.jar + tryClassPathRoot(classPathEntry); + } + for (String sourcePathEntry : sourcePathEntries) { + trySourceRoot(sourcePathEntry); + } + } + + private void tryClassPathRoot(String classPathEntry) { + trySourceRoot(classPathEntry, true); + } + + private void trySourceRoot(String sourcePathEntry) { + trySourceRoot(sourcePathEntry, false); + } + + private void trySourceRoot(String sourceRoot, boolean fromClassPath) { + Path sourcePath = Paths.get(sourceRoot); + String fileNameString = sourcePath.getFileName().toString(); + if (fileNameString.endsWith(".jar") || fileNameString.endsWith(".src.zip")) { + if (fromClassPath && fileNameString.endsWith(".jar")) { + /* + * GraalVM jar /path/to/xxx.jar in classpath should have sources + * /path/to/xxx.src.zip + */ int length = fileNameString.length(); - String srcFileNameString = fileNameString.substring(0, length - 3) + "src.zip"; - Path srcPath = entryPath.getParent().resolve(srcFileNameString); - if (srcPath.toFile().exists()) { - try { - FileSystem fileSystem = FileSystems.newFileSystem(srcPath, null); - for (Path root : fileSystem.getRootDirectories()) { - if (filterSrcRoot(root)) { - srcRoots.add(root); - } + fileNameString = fileNameString.substring(0, length - 3) + "src.zip"; + } + Path srcPath = sourcePath.getParent().resolve(fileNameString); + if (srcPath.toFile().exists()) { + try { + FileSystem fileSystem = FileSystems.newFileSystem(srcPath, null); + for (Path root : fileSystem.getRootDirectories()) { + if (filterSrcRoot(root)) { + srcRoots.add(root); } - } catch (IOException ioe) { - /* ignore this entry */ - } catch (FileSystemNotFoundException fnfe) { - /* ignore this entry */ } + } catch (IOException ioe) { + /* ignore this entry */ + } catch (FileSystemNotFoundException fnfe) { + /* ignore this entry */ } - } else { + } + } else { + if (fromClassPath) { /* graal classpath dir entries should have a src and/or src_gen subdirectory */ - Path srcPath = entryPath.resolve("src"); + Path srcPath = sourcePath.resolve("src"); if (filterSrcRoot(srcPath)) { srcRoots.add(srcPath); } - srcPath = entryPath.resolve("src_gen"); + srcPath = sourcePath.resolve("src_gen"); if (filterSrcRoot(srcPath)) { srcRoots.add(srcPath); } + } else { + // try the path as provided + if (filterSrcRoot(sourcePath)) { + srcRoots.add(sourcePath); + } } } } + /** - * Ensure that the supplied root dir contains - * at least one subdirectory that matches one - * of the expected Graal package dir hierarchies. + * Ensure that the supplied root dir contains at least one subdirectory that matches one of the + * expected Graal package dir hierarchies. * - * @param root A root path under which to locate - * the desired subdirectory + * @param root A root path under which to locate the desired subdirectory * @return true if a */ - private boolean filterSrcRoot(Path root) { + private static boolean filterSrcRoot(Path root) { String separator = root.getFileSystem().getSeparator(); /* if any of the graal paths exist accept this root */ diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java index e53d75f75c9b..fc93a0154994 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java @@ -33,21 +33,33 @@ import java.nio.file.Path; import java.nio.file.Paths; +import static com.oracle.svm.hosted.image.sources.SourceCacheType.JDK; + public class JDKSourceCache extends SourceCache { /** - * create a JDK runtime class source cache. + * Create a JDK runtime class source cache.. */ protected JDKSourceCache() { - super(SourceCache.JDK_CACHE_KEY); initSrcRoots(); } + @Override + protected final SourceCacheType getType() { + return JDK; + } + + /* + * properties needed to locate relevant JDK and app source roots + */ + private static final String JAVA_HOME_PROP = "java.home"; + private static final String JAVA_SPEC_VERSION_PROP = "java.specification.version"; + private void initSrcRoots() { String javaHome = System.getProperty(JAVA_HOME_PROP); assert javaHome != null; Path javaHomePath = Paths.get("", javaHome); Path srcZipPath; - String javaSpecVersion = System.getProperty(JAVA_SPEC_VERSION_PROP); + String javaSpecVersion = System.getProperty(JAVA_SPEC_VERSION_PROP); if (javaSpecVersion.equals("1.8")) { srcZipPath = javaHomePath.resolve("src.zip"); } else { @@ -68,4 +80,3 @@ private void initSrcRoots() { } } } - diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java index 91384cfcb553..0507702ea3a2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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 @@ -25,6 +26,13 @@ package com.oracle.svm.hosted.image.sources; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.option.OptionUtils; +import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.hosted.ImageClassLoader; +import org.graalvm.nativeimage.hosted.Feature; + import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -34,106 +42,67 @@ import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.List; + /** - * An abstract cache manager for some subspace of the - * JDK, GraalVM or application source file space. This class - * implements core behaviours that manage a cache of source - * files in a specific subdirectory of the local sources - * directory. It allows source files to be located - * when present in the local cache or cached when not - * already present. Subclasses are responsible for providing - * behaviours that identify an original source for addition - * to the cache and for verifying that a cached file is not - * out of date with respect to its original. + * An abstract cache manager for some subspace of the JDK, GraalVM or application source file space. + * This class implements core behaviours that manage a cache of source files in a specific + * subdirectory of the local sources directory. It allows source files to be located when present in + * the local cache or cached when not already present. Subclasses are responsible for providing + * behaviours that identify an original source for addition to the cache and for verifying that a + * cached file is not out of date with respect to its original. */ public abstract class SourceCache { - /* - * properties needed to locate relevant JDK and app source roots + /** + * A list of all entries in the classpath used by the native image classloader */ - protected static final String JAVA_CLASSPATH_PROP = "java.class.path"; - protected static final String JAVA_HOME_PROP = "java.home"; - protected static final String JAVA_SPEC_VERSION_PROP = "java.specification.version"; + protected static final List classPathEntries = new ArrayList<>(); /** - * A list of root directories which may contain source files - * from which this cache can be populated + * A list of all entries in the classpath used by the native image classloader + */ + protected static final List sourcePathEntries = new ArrayList<>(); + /** + * A list of root directories which may contain source files from which this cache can be + * populated */ protected List srcRoots; /** - * Create a source cache with a specific base type. - * @param key a String identifying the subdir under - * which sources should be cached which should also - * match the type of content being cached + * Create some flavour of source cache. */ - protected SourceCache(String key) { - basePath = Paths.get(SOURCE_CACHE_ROOT_DIR, key); + protected SourceCache() { + basePath = Paths.get(SOURCE_CACHE_ROOT_DIR).resolve(getType().getSubdir()); srcRoots = new ArrayList<>(); - } /** - * A local directory serving as the root for all - * source trees maintained by the different - * available source caches. - */ - private static final String SOURCE_CACHE_ROOT_DIR = "sources"; - /** - * The top level path relative to the root directory - * under which files belonging to this specific cache - * are located. - */ - private Path basePath; - /** - * JDK runtime code sources are cached using this key as a - * leading path prefix with a module name as a sub-path - * prefix when we have a modular JDK. - * - * For example, the full file path to a file under the cache - * root directory might be jdk/java/lang/Throwable.java on jdk8 or - * jdk/java.base/java/lang/Throwable.java on jdk11 + * Identify the specific type of this source cache + * + * @return the source cache type */ - protected static final String JDK_CACHE_KEY = "jdk"; + protected abstract SourceCacheType getType(); + /** - * GraalVM code sources are cached using this key as a - * leading path prefix with an enclosing package name - * and the name src or src_gen forming a sub-path prefix. - * - * For example, the full file path to a file under the cache - * root directory might be - * graal/com/oracle/svm/core/Isolates.java - * or - * graal/org/graalvm/compiler/core/phases/LowTier_OptionDescriptors.java + * A local directory serving as the root for all source trees maintained by the different + * available source caches. */ - protected static final String GRAALVM_CACHE_KEY = "graal"; + private static final String SOURCE_CACHE_ROOT_DIR = "sources"; /** - * Application code sources are cached using this key as - * a leading path prefix with a name or sequence of - * names derived from a classpath jar or dir entry - * employed as a sub-path prefix. - * - * For example, the full file path to a file under the cache - * root directory might be - * src/Hello.java - * or - * src/hello/impl/HelloImpl.java - * or - * src/hibernate-core-5.4.4.Final/org/hibernate/annotations/Entity.java + * The top level path relative to the root directory under which files belonging to this + * specific cache are located. */ - protected static final String APPLICATION_CACHE_KEY = "src"; + private final Path basePath; + /** - * Cache the source file identified by the supplied prototype - * path if a legitimate candidate for inclusion in this cache - * can be identified and is not yet included in the cache or - * alternatively identify and validate any existing candidate - * cache entry to ensure it is not out of date refreshing it - * if need be. + * Cache the source file identified by the supplied prototype path if a legitimate candidate for + * inclusion in this cache can be identified and is not yet included in the cache or + * alternatively identify and validate any existing candidate cache entry to ensure it is not + * out of date refreshing it if need be. * - * @param filePath a prototype path for a file to be included - * in the cache derived from the name of some associated class. - * @return a path identifying the cached file or null - * if the candidate cannot be found. + * @param filePath a prototype path for a file to be included in the cache derived from the name + * of some associated class. + * @return a path identifying the cached file or null if the candidate cannot be found. */ public Path resolve(Path filePath) { File cachedFile = findCandidate(filePath); @@ -145,19 +114,17 @@ public Path resolve(Path filePath) { } /** - * Given a prototype path for a file to be resolved - * return a File identifying a cached candidate for - * for that Path or null if no cached candidate exists. - * @param filePath a prototype path for a file to be included - * in the cache derived from the name of some associated class. + * Given a prototype path for a file to be resolved return a File identifying a cached candidate + * for for that Path or null if no cached candidate exists. + * + * @param filePath a prototype path for a file to be included in the cache derived from the name + * of some associated class. * @return a File identifying a cached candidate or null. */ public File findCandidate(Path filePath) { /* - * JDK source candidates are stored in the src.zip file - * using the path we are being asked for. A cached version - * should exist under this cache's root using that same - * path. + * JDK source candidates are stored in the src.zip file using the path we are being asked + * for. A cached version should exist under this cache's root using that same path. */ File file = cachedFile(filePath); if (file.exists()) { @@ -165,6 +132,16 @@ public File findCandidate(Path filePath) { } return null; } + + /** + * Attempt to copy a source file from one of this cache's source roots to the local sources + * directory storing it in the subdirectory that belongs to this cache. + * + * @param filePath a path appended to each of the cache's source roots in turn until an + * acceptable source file is found and copied to the local source directory. + * @return the supplied path if the file has been located and copied to the local sources + * directory or null if it was not found or the copy failed. + */ public Path tryCacheFile(Path filePath) { for (Path root : srcRoots) { Path targetPath = cachedPath(filePath); @@ -182,9 +159,20 @@ public Path tryCacheFile(Path filePath) { } return null; } + + /** + * Check whether the copy of a given source file in the local source cache is up to date with + * respect to any original located in this cache's and if not copy the original to the + * subdirectory that belongs to this cache. + * + * @param filePath a path appended to each of the cache's source roots in turn until an matching + * original source is found for comparison against the local source directory. + * @return the supplied path if the file is up to date or if an updated version has been copied + * to the local sources directory or null if was not found or the copy failed. + */ public Path checkCacheFile(Path filePath) { + Path targetPath = cachedPath(filePath); for (Path root : srcRoots) { - Path targetPath = cachedPath(filePath); Path sourcePath = extendPath(root, filePath); try { if (checkSourcePath(sourcePath)) { @@ -194,27 +182,33 @@ public Path checkCacheFile(Path filePath) { try { Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); } catch (IOException e) { + /* delete the target file as it is invalid */ + targetPath.toFile().delete(); return null; } } return filePath; - } else { - /* delete the target file as it is out of date */ - targetPath.toFile().delete(); } } catch (IOException e) { - // hmm last modified time blew up? + /* delete the target file as it is invalid */ + targetPath.toFile().delete(); + /* have another go at caching it */ return tryCacheFile(filePath); } } + /* delete the target file as it is invalid */ + targetPath.toFile().delete(); + return null; } + /** - * Create and intialize the source cache used to locate and cache - * sources of a given type as determined by the supplied key. - * @param type an enum identifying both the type of Java sources - * cached by the returned cache and the subdir of the cached - * source subdirectory in which those sources are located. + * Create and intialize the source cache used to locate and cache sources of a given type as + * determined by the supplied key. + * + * @param type an enum identifying both the type of Java sources cached by the returned cache + * and the subdir of the cached source subdirectory in which those sources are + * located. * @return the desired source cache. */ public static SourceCache createSourceCache(SourceCacheType type) { @@ -234,10 +228,12 @@ public static SourceCache createSourceCache(SourceCacheType type) { } return sourceCache; } + /** - * Extend a root path form one file system using a path potentially derived - * from another file system by converting he latter to a text string and - * replacing the file separator if necessary. + * Extend a root path form one file system using a path potentially derived from another file + * system by converting he latter to a text string and replacing the file separator if + * necessary. + * * @param root the path to be extended * @param filePath the subpath to extend it with * @return the extended path @@ -247,48 +243,49 @@ protected Path extendPath(Path root, Path filePath) { String fileSeparator = filePath.getFileSystem().getSeparator(); String newSeparator = root.getFileSystem().getSeparator(); if (!fileSeparator.equals(newSeparator)) { - filePathString = filePathString.replaceAll(fileSeparator, newSeparator); + filePathString = filePathString.replace(fileSeparator, newSeparator); } return root.resolve(filePathString); } /** - * convert a potential resolved candidate path to - * the corresponding local Path in this cache. - * @param candidate a resolved candidate path for - * some given resolution request + * Convert a potential resolved candidate path to the corresponding local Path in this cache. + * + * @param candidate a resolved candidate path for some given resolution request * @return the corresponding local Path */ protected Path cachedPath(Path candidate) { return basePath.resolve(candidate); } + /** - * convert a potential resolved candidate path to - * the corresponding local File in this cache. - * @param candidate a resolved candidate path for - * some given resolution request + * Convert a potential resolved candidate path to the corresponding local File in this cache. + * + * @param candidate a resolved candidate path for some given resolution request * @return the corresponding local File */ protected File cachedFile(Path candidate) { return cachedPath(candidate).toFile(); } + /** - * indicate whether a source path identifies a fie in the associated file system + * Indicate whether a source path identifies a file in the associated file system. + * * @param sourcePath - * @return true if the path identifies a file or false if no such file can be found - * @throws IOException if there is some error in resolving the path + * @return true if the path identifies a file or false if no such file can be found. + * @throws IOException if there is some error in resolving the path. */ - private boolean checkSourcePath(Path sourcePath) throws IOException { + private static boolean checkSourcePath(Path sourcePath) throws IOException { return Files.isRegularFile(sourcePath); } + /** - * ensure the directory hierarchy for a path exists - * creating any missing directories if needed + * Ensure the directory hierarchy for a path exists creating any missing directories if needed. + * * @param targetDir a path to the desired directory - * @throws IOException if it is not possible to create - * one or more directories in the path + * @throws IOException if it is not possible to create one or more directories in the path */ - private void ensureTargetDirs(Path targetDir) throws IOException { + private static void ensureTargetDirs(Path targetDir) throws IOException { if (targetDir != null) { File targetFile = targetDir.toFile(); if (!targetFile.exists()) { @@ -296,4 +293,44 @@ private void ensureTargetDirs(Path targetDir) throws IOException { } } } + + /** + * Add a path to the list of classpath entries + * + * @param path The path to add. + */ + private static void addClassPathEntry(String path) { + classPathEntries.add(path); + } + + /** + * Add a path to the list of source path entries + * + * @param path The path to add. + */ + private static void addSourcePathEntry(String path) { + sourcePathEntries.add(path); + } + + /** + * An automatic feature class which acquires the image loader class path via the afterAnalysis + * callback. + */ + @AutomaticFeature + public static class SourceCacheFeature implements Feature { + @Override + public void afterAnalysis(AfterAnalysisAccess access) { + FeatureImpl.AfterAnalysisAccessImpl accessImpl = (FeatureImpl.AfterAnalysisAccessImpl) access; + ImageClassLoader loader = accessImpl.getImageClassLoader(); + for (String entry : loader.getClasspath()) { + addClassPathEntry(entry); + } + // also add any necessary source path entries + if (SubstrateOptions.DebugInfoSourceSearchPath.getValue() != null) { + for (String searchPathEntry : OptionUtils.flatten(",", SubstrateOptions.DebugInfoSourceSearchPath.getValue())) { + addSourcePathEntry(searchPathEntry); + } + } + } + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCacheType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCacheType.java index 10863a5e572c..5e37d3883141 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCacheType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCacheType.java @@ -26,8 +26,22 @@ package com.oracle.svm.hosted.image.sources; +import java.nio.file.Path; +import java.nio.file.Paths; + public enum SourceCacheType { - JDK, - GRAALVM, - APPLICATION + JDK("jdk"), + GRAALVM("graal"), + APPLICATION("src"); + + final Path subdir; + + SourceCacheType(String subdir) { + this.subdir = Paths.get(subdir); + } + + public Path getSubdir() { + return subdir; + } + } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java index 459acebe6a08..cca64372df04 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java @@ -26,7 +26,6 @@ package com.oracle.svm.hosted.image.sources; -import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.svm.util.ModuleSupport; import jdk.vm.ci.meta.ResolvedJavaType; @@ -35,74 +34,59 @@ import java.util.HashMap; /** - * A singleton class responsible for locating source files - * for classes included in a native image and copying them - * into the local sources. + * A singleton class responsible for locating source files for classes included in a native image + * and copying them into the local sources. */ public class SourceManager { /** - * Find and cache a source file for a give Java class and - * return a Path to the file relative to the source. - * @param resolvedType the Java type whose source file - * should be located and cached - * @return a path identifying the location of a successfully - * cached file for inclusion in the generated debug info or - * null if a source file cannot be found or cached. + * Find and cache a source file for a give Java class and return a Path to the file relative to + * the source. + * + * @param resolvedType the Java type whose source file should be located and cached + * @param clazz the Java class associated with the resolved type + * @return a path identifying the location of a successfully cached file for inclusion in the + * generated debug info or null if a source file cannot be found or cached. */ - public Path findAndCacheSource(ResolvedJavaType resolvedType) { - Path path = null; + public Path findAndCacheSource(ResolvedJavaType resolvedType, Class clazz) { + /* short circuit if we have already seen this type */ + Path path = verifiedPaths.get(resolvedType); + if (path != null) { + return (path != INVALID_PATH ? path : null); + } + String fileName = computeBaseName(resolvedType); /* - * null for the name means this class - * will not have a source so we skip on that + * null for the name means this class will not have a source so we skip on that */ if (fileName != null) { /* - * we can only provide sources - * for known classes and interfaces + * we can only provide sources for known classes and interfaces */ if (resolvedType.isInstanceClass() || resolvedType.isInterface()) { - /* - * if we have an OriginalClassProvider we - * can use the underlying Java class - * to provide the details we need to locate - * a source - */ - if (resolvedType instanceof OriginalClassProvider) { - Class javaClass = ((OriginalClassProvider) resolvedType).getJavaClass(); - String packageName = computePackageName(javaClass); - SourceCacheType type = sourceCacheType(packageName, javaClass); - path = locateSource(fileName, packageName, type, javaClass); - } - /* - * if we could not locate a source via the cache - * then the fallback is to generate a path to the - * file based on the class name and let the - * user configure a path to the sources - */ + String packageName = computePackageName(resolvedType); + SourceCacheType sourceCacheType = sourceCacheType(packageName); + path = locateSource(fileName, packageName, sourceCacheType, resolvedType, clazz); if (path == null) { - String name = resolvedType.toJavaName(); - int idx = name.lastIndexOf('.'); - if (idx >= 0 && idx < name.length() - 1) { - name = name.substring(0, idx); - path = Paths.get("", name.split("\\.")); + // as a last ditch effort derive path from the Java class name + if (packageName.length() > 0) { + path = Paths.get("", packageName.split("\\.")); path = path.resolve(fileName); } } } } + /* memoize the lookup */ + verifiedPaths.put(resolvedType, (path != null ? path : INVALID_PATH)); + return path; } /** - * Construct the base file name for a resolved - * Java class excluding path elements using either - * the source name embedded in the class file or - * the class name itself. - * @param resolvedType the resolved java type whose - * source file name is required - * @return the file name or null if it the class cannot - * be associated with a source file + * Construct the base file name for a resolved Java class excluding path elements using either + * the source name embedded in the class file or the class name itself. + * + * @param resolvedType the resolved java type whose source file name is required + * @return the file name or null if it the class cannot be associated with a source file */ private String computeBaseName(ResolvedJavaType resolvedType) { String fileName = resolvedType.getSourceFileName(); @@ -129,33 +113,41 @@ private String computeBaseName(ResolvedJavaType resolvedType) { } return fileName; } + /** - * Construct the package name for a Java class or - * the empty String if it has no package. - * @param javaClass the java class whose package - * name is required - * @return the package name or the empty String - * if it has no package + * Construct the package name for a Java type or the empty String if it has no package. + * + * @param javaType the Java type whose package name is required + * @return the package name or the empty String if it has no package */ - private String computePackageName(Class javaClass) { - Package pkg = javaClass.getPackage(); - return (pkg == null ? "" : pkg.getName()); + private String computePackageName(ResolvedJavaType javaType) { + String name = javaType.toClassName(); + int idx = name.lastIndexOf('.'); + if (idx > 0) { + return name.substring(0, idx); + } else { + return ""; + } } + /** - * Construct the prototype name for a Java source file - * which can be used to resolve and cache an actual source - * file. + * Construct the prototype name for a Java source file which can be used to resolve and cache an + * actual source file. + * * @param fileName the base file name for the source file * @param packageName the name of the package for the associated Java class - * @param type the type of cache in which to lookup or cache this class's source file - * @param javaClass the java class whose prototype name is required + * @param sourceCacheType the sourceCacheType of cache in which to lookup or cache this class's + * source file + * @param javaType the java sourceCacheType whose prototype name is required + * @param clazz the class associated with the sourceCacheType used to identify the module prefix + * for JDK classes * @return a protoype name for the source file */ - private Path computePrototypeName(String fileName, String packageName, SourceCacheType type, Class javaClass) { + private Path computePrototypeName(String fileName, String packageName, SourceCacheType sourceCacheType, ResolvedJavaType javaType, Class clazz) { String prefix = ""; - if (type == SourceCacheType.JDK) { - /* JDK paths may require the module name as prefix */ - String moduleName = ModuleSupport.getModuleName(javaClass); + if (sourceCacheType == SourceCacheType.JDK && clazz != null) { + /* JDK11+ paths will require the module name as prefix */ + String moduleName = ModuleSupport.getModuleName(clazz); if (moduleName != null) { prefix = moduleName; } @@ -166,51 +158,48 @@ private Path computePrototypeName(String fileName, String packageName, SourceCac return Paths.get(prefix, packageName.split("\\.")).resolve(fileName); } } + /** - * A whitelist of packages prefixes used to - * pre-filter JDK runtime class lookups. + * A whitelist of packages prefixes used to pre-filter JDK runtime class lookups. */ public static final String[] JDK_SRC_PACKAGE_PREFIXES = { - "java.", - "jdk.", - "javax.", - "sun.", - "com.sun.", - "org.ietf.", - "org.jcp.", - "org.omg.", - "org.w3c.", - "org.xml", + "java.", + "jdk.", + "javax.", + "sun.", + "com.sun.", + "org.ietf.", + "org.jcp.", + "org.omg.", + "org.w3c.", + "org.xml", }; /** - * A whitelist of packages prefixes used to - * pre-filter GraalVM class lookups. + * A whitelist of packages prefixes used to pre-filter GraalVM class lookups. */ public static final String[] GRAALVM_SRC_PACKAGE_PREFIXES = { - "com.oracle.graal.", - "com.oracle.objectfile.", - "com.oracle.svm.", - "com.oracle.truffle.", - "org.graalvm.", + "com.oracle.graal.", + "com.oracle.objectfile.", + "com.oracle.svm.", + "com.oracle.truffle.", + "org.graalvm.", }; /** - * A whitelist of packages prefixes used to - * pre-filter app class lookups which - * includes just the empty string because - * any package will do. + * A whitelist of packages prefixes used to pre-filter app class lookups which includes just the + * empty string because any package will do. */ private static final String[] APP_SRC_PACKAGE_PREFIXES = { - "", + "", }; /** * Check a package name against a whitelist of acceptable packages. + * * @param packageName the package name of the class to be checked - * @param whitelist a list of prefixes one of which may form - * the initial prefix of the package name being checked - * @return true if the package name matches an entry in the - * whitelist otherwise false + * @param whitelist a list of prefixes one of which may form the initial prefix of the package + * name being checked + * @return true if the package name matches an entry in the whitelist otherwise false */ private boolean whiteListPackage(String packageName, String[] whitelist) { for (String prefix : whitelist) { @@ -222,10 +211,13 @@ private boolean whiteListPackage(String packageName, String[] whitelist) { } /** - * Identify which type of source cache should be used - * to locate a given class's source code. + * Identify which type of source cache should be used to locate a given class's source code as + * determined by it's package name. + * + * @param packageName the package name of the class. + * @return the corresponding source cache type */ - private SourceCacheType sourceCacheType(String packageName, Class javaClass) { + private SourceCacheType sourceCacheType(String packageName) { if (whiteListPackage(packageName, JDK_SRC_PACKAGE_PREFIXES)) { return SourceCacheType.JDK; } @@ -234,19 +226,30 @@ private SourceCacheType sourceCacheType(String packageName, Class javaClass) } return SourceCacheType.APPLICATION; } + /** - * A map from each of the top level root keys to a - * cache that knows how to handle lookup and caching - * of the associated type of source file. + * A map from each of the top level root keys to a cache that knows how to handle lookup and + * caching of the associated type of source file. */ private static HashMap caches = new HashMap<>(); /** - * Retrieve the source cache used to locate and cache sources - * of a given type as determined by the supplied key, creating - * and initializing it if it does not already exist. - * @param type an enum identifying the type of Java sources - * cached by the returned cache. + * A map from a Java type to an associated source paths which is known to have an up to date + * entry in the relevant source file cache. This is used to memoize previous lookups. + */ + private static HashMap verifiedPaths = new HashMap<>(); + + /** + * An invalid path used as a marker to track failed lookups so we don't waste time looking up + * the source again. Note that all legitimate paths will end with a ".java" suffix. + */ + private static final Path INVALID_PATH = Paths.get("invalid"); + + /** + * Retrieve the source cache used to locate and cache sources of a given type as determined by + * the supplied key, creating and initializing it if it does not already exist. + * + * @param type an enum identifying the type of Java sources cached by the returned cache. * @return the desired source cache. */ private SourceCache getOrCreateCache(SourceCacheType type) { @@ -258,10 +261,13 @@ private SourceCache getOrCreateCache(SourceCacheType type) { return sourceCache; } - private Path locateSource(String fileName, String packagename, SourceCacheType type, Class javaClass) { + private Path locateSource(String fileName, String packagename, SourceCacheType type, ResolvedJavaType javaType, Class clazz) { SourceCache cache = getOrCreateCache(type); - Path prototypeName = computePrototypeName(fileName, packagename, type, javaClass); - return cache.resolve(prototypeName); + Path prototypeName = computePrototypeName(fileName, packagename, type, javaType, clazz); + if (prototypeName != null) { + return cache.resolve(prototypeName); + } else { + return null; + } } } - From 4b0aec7af896b5b20a0cbee931cf963906187669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 11 Mar 2020 14:34:55 +0100 Subject: [PATCH 7/9] Support hierarchical logging --- substratevm/mx.substratevm/suite.py | 8 +- .../.checkstyle_checks.xml | 233 +++++++++++ .../src/com/oracle/objectfile/ObjectFile.java | 48 +++ .../objectfile/debugentry/ClassEntry.java | 14 +- .../objectfile/debugentry/DirEntry.java | 2 +- .../objectfile/debugentry/FileEntry.java | 2 +- .../objectfile/debugentry/PrimaryEntry.java | 14 +- .../oracle/objectfile/debugentry/Range.java | 16 +- .../objectfile/debugentry/StringEntry.java | 2 +- .../objectfile/debugentry/StringTable.java | 4 +- .../debuginfo/DebugInfoProvider.java | 51 +-- .../oracle/objectfile/elf/ELFObjectFile.java | 26 +- .../elf/dwarf/DwarfARangesSectionImpl.java | 50 ++- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 50 ++- .../elf/dwarf/DwarfFrameSectionImpl.java | 96 +++-- .../dwarf/DwarfFrameSectionImplAArch64.java | 8 +- .../dwarf/DwarfFrameSectionImplX86_64.java | 16 +- .../elf/dwarf/DwarfInfoSectionImpl.java | 97 +++-- .../elf/dwarf/DwarfLineSectionImpl.java | 362 +++++++++--------- .../elf/dwarf/DwarfSectionImpl.java | 122 +++--- .../objectfile/elf/dwarf/DwarfSections.java | 214 +++++------ .../elf/dwarf/DwarfStrSectionImpl.java | 22 +- .../.checkstyle_checks.xml | 4 +- .../svm/hosted/image/NativeBootImage.java | 283 +------------- .../hosted/image/NativeBootImageViaCC.java | 2 +- .../image/NativeImageDebugInfoProvider.java | 321 ++++++++++++++++ .../image/sources/ApplicationSourceCache.java | 78 ++-- .../image/sources/GraalVMSourceCache.java | 79 ++-- .../hosted/image/sources/JDKSourceCache.java | 4 +- .../svm/hosted/image/sources/SourceCache.java | 23 +- .../hosted/image/sources/SourceManager.java | 29 +- 31 files changed, 1292 insertions(+), 988 deletions(-) create mode 100644 substratevm/src/com.oracle.objectfile/.checkstyle_checks.xml create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 545421d5e532..985d3b400a04 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -604,7 +604,9 @@ "com.oracle.objectfile" : { "subDir": "src", "sourceDirs" : ["src"], - "dependencies" : [], + "dependencies" : [ + "compiler:GRAAL" + ], "checkstyle" : "com.oracle.svm.hosted", "javaCompliance" : "8+", "annotationProcessors" : ["compiler:GRAAL_PROCESSOR"], @@ -920,7 +922,9 @@ "dependencies": [ "com.oracle.objectfile" ], - }, + "distDependencies": [ + "compiler:GRAAL", + ], }, "GRAAL_HOTSPOT_LIBRARY": { "subDir": "src", diff --git a/substratevm/src/com.oracle.objectfile/.checkstyle_checks.xml b/substratevm/src/com.oracle.objectfile/.checkstyle_checks.xml new file mode 100644 index 000000000000..788e1fa0b557 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/.checkstyle_checks.xml @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java index 386978567ebf..c9dabe238e12 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java @@ -43,6 +43,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.function.Consumer; import java.util.stream.StreamSupport; import com.oracle.objectfile.debuginfo.DebugInfoProvider; @@ -51,6 +52,7 @@ import com.oracle.objectfile.pecoff.PECoffObjectFile; import sun.nio.ch.DirectBuffer; +import org.graalvm.compiler.debug.DebugContext; /** * Abstract superclass for object files. An object file is a binary container for sections, @@ -1737,4 +1739,50 @@ public final SymbolTable getOrCreateSymbolTable() { return createSymbolTable(); } } + + /** + * Temporary storage for a debug context installed in a nested scope under a call. to + * {@link #withDebugContext} + */ + private DebugContext debugContext = null; + + /** + * Allows a task to be executed with a debug context in a named subscope bound to the object + * file and accessible to code executed during the lifetime of the task. Invoked code may obtain + * access to the debug context using method {@link #debugContext}. + * + * @param context a context to be bound to the object file for the duration of the task + * execution. + * @param scopeName a name to be used to define a subscope current while the task is being + * executed. + * @param task a task to be executed while the context is bound to the object file. + */ + @SuppressWarnings("try") + public void withDebugContext(DebugContext context, String scopeName, Runnable task) { + try (DebugContext.Scope s = context.scope(scopeName)) { + this.debugContext = context; + task.run(); + } catch (Throwable e) { + throw debugContext.handle(e); + } finally { + debugContext = null; + } + } + + /** + * Allows a consumer to retrieve the debug context currently bound to this object file. This + * method must only called underneath an invocation of method {@link #withDebugContext}. + * + * @param scopeName a name to be used to define a subscope current while the consumer is active. + * @param action an action parameterised by the debug context. + */ + @SuppressWarnings("try") + public void debugContext(String scopeName, Consumer action) { + assert debugContext != null; + try (DebugContext.Scope s = debugContext.scope(scopeName)) { + action.accept(debugContext); + } catch (Throwable e) { + throw debugContext.handle(e); + } + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index bb4917254b59..32e856a7e6e9 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, Red Hat Inc. 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 @@ -44,7 +44,7 @@ public class ClassEntry { /** * details of the associated file. */ - FileEntry fileEntry; + private FileEntry fileEntry; /** * a list recording details of all primary ranges included in this class sorted by ascending * address range. @@ -111,14 +111,12 @@ public ClassEntry(String className, FileEntry fileEntry) { this.totalSize = -1; } - public PrimaryEntry addPrimary(Range primary, List frameSizeInfos, int frameSize) { + public void addPrimary(Range primary, List frameSizeInfos, int frameSize) { if (primaryIndex.get(primary) == null) { PrimaryEntry primaryEntry = new PrimaryEntry(primary, frameSizeInfos, frameSize, this); primaryEntries.add(primaryEntry); primaryIndex.put(primary, primaryEntry); - return primaryEntry; } - return null; } public void addSubRange(Range subrange, FileEntry subFileEntry) { @@ -167,6 +165,7 @@ public String getFileName() { } } + @SuppressWarnings("unused") String getFullFileName() { if (fileEntry != null) { return fileEntry.getFullName(); @@ -175,6 +174,7 @@ String getFullFileName() { } } + @SuppressWarnings("unused") String getDirName() { if (fileEntry != null) { return fileEntry.getPathName(); @@ -184,14 +184,14 @@ String getDirName() { } public void setCUIndex(int cuIndex) { - // should only get set once to a non-negative value + // Should only get set once to a non-negative value. assert cuIndex >= 0; assert this.cuIndex == -1; this.cuIndex = cuIndex; } public int getCUIndex() { - // should have been set before being read + // Should have been set before being read. assert cuIndex >= 0; return cuIndex; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java index 019611539160..034653171103 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, Red Hat Inc. 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 diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java index 1aedf4dc879b..50764f2aa564 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, Red Hat Inc. 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 diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java index b0199d3c3f65..1948cf019ba3 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimaryEntry.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, Red Hat Inc. 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 @@ -39,19 +39,19 @@ public class PrimaryEntry { /** * The primary range detailed by this object. */ - Range primary; + private Range primary; /** * Details of the class owning this range. */ - ClassEntry classEntry; + private ClassEntry classEntry; /** * A list of subranges associated with the primary range. */ - List subranges; + private List subranges; /** * A mapping from subranges to their associated file entry. */ - HashMap subrangeIndex; + private HashMap subrangeIndex; /** * Details of of compiled method frame size changes. */ @@ -72,12 +72,12 @@ public PrimaryEntry(Range primary, List frameSizeInfos, in public void addSubRange(Range subrange, FileEntry subFileEntry) { /* - * we should not see a subrange more than once + * We should not see a subrange more than once. */ assert !subranges.contains(subrange); assert subrangeIndex.get(subrange) == null; /* - * we need to generate a file table entry for all ranges + * We need to generate a file table entry for all ranges. */ subranges.add(subrange); subrangeIndex.put(subrange, subFileEntry); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 191ec29b0532..1b65c315e8a7 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020 Red Hat, Inc. 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 @@ -47,26 +47,26 @@ public class Range { private int hi; private int line; /* - * this is null for a primary range + * This is null for a primary range. */ private Range primary; /* - * create a primary range + * Create a primary range. */ public Range(String fileName, Path filePath, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line) { this(fileName, filePath, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, line, null); } /* - * create a primary or secondary range + * Create a primary or secondary range. */ public Range(String fileName, Path filePath, String className, String methodName, String paramNames, String returnTypeName, StringTable stringTable, int lo, int hi, int line, Range primary) { /* - * currently file name and full method name need to go into the debug_str section other - * strings just need to be deduplicated to save space + * Currently file name and full method name need to go into the debug_str section other + * strings just need to be deduplicated to save space. */ - this.fileName = (fileName == null ? fileName : stringTable.uniqueDebugString(fileName)); + this.fileName = (fileName == null ? null : stringTable.uniqueDebugString(fileName)); this.filePath = filePath; this.className = stringTable.uniqueString(className); this.methodName = stringTable.uniqueString(methodName); @@ -133,7 +133,7 @@ public String getFullMethodName() { return fullMethodName; } - public String getExtendedMethodName(boolean includeParams, boolean includeReturnType) { + private String getExtendedMethodName(boolean includeParams, boolean includeReturnType) { StringBuilder builder = new StringBuilder(); if (includeReturnType && returnTypeName.length() > 0) { builder.append(returnTypeName); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringEntry.java index f8fe7445d8d2..c6d077661f64 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringEntry.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020 Red Hat, Inc. 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 diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringTable.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringTable.java index 9ea823ff00d4..4b8537228aec 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringTable.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringTable.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020 Red Hat, Inc. 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 @@ -83,7 +83,7 @@ private String ensureString(String string, boolean addToStrSection) { * Retrieves the offset at which a given string was written into the debug_str section. This * should only be called after the string section has been written. * - * @param string + * @param string the strng whose offset is to be retrieved * @return the offset or -1 if the string does not define an entry or the entry has not been * written to the debug_str section */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java index 185de9e86e85..63523cb22340 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debuginfo/DebugInfoProvider.java @@ -28,79 +28,84 @@ import java.nio.file.Path; import java.util.List; +import java.util.function.Consumer; import java.util.stream.Stream; +import org.graalvm.compiler.debug.DebugContext; + /** * Interfaces used to allow a native image to communicate details of types, code and data to the * underlying object file so that the latter can insert appropriate debug info. */ public interface DebugInfoProvider { /** - * access details of a specific type. + * Access details of a specific type. */ interface DebugTypeInfo { } /** - * access details of a specific compiled method. + * Access details of a specific compiled method. */ interface DebugCodeInfo { + void debugContext(Consumer action); + /** - * @return the name of the file containing a compiled method excluding any path + * @return the name of the file containing a compiled method excluding any path. */ String fileName(); /** * @return a relative path to the file containing a compiled method derived from its package - * name or null if the method is in the empty package + * name or null if the method is in the empty package. */ Path filePath(); /** - * @return the fully qualified name of the class owning the compiled method + * @return the fully qualified name of the class owning the compiled method. */ String className(); /** - * @return the name of the compiled method including signature + * @return the name of the compiled method including signature. */ String methodName(); /** * @return the lowest address containing code generated for the method represented as an - * offset into the code segment + * offset into the code segment. */ int addressLo(); /** * @return the first address above the code generated for the method represented as an - * offset into the code segment + * offset into the code segment. */ int addressHi(); /** - * @return the starting line number for the method + * @return the starting line number for the method. */ int line(); /** * @return a stream of records detailing line numbers and addresses within the compiled - * method + * method. */ Stream lineInfoProvider(); /** - * @return a string identifying the method parameters + * @return a string identifying the method parameters. */ String paramNames(); /** - * @return a string identifying the method return type + * @return a string identifying the method return type. */ String returnTypeName(); /** - * @return the size of the method frame between prologue and epilogue + * @return the size of the method frame between prologue and epilogue. */ int getFrameSize(); @@ -112,51 +117,51 @@ interface DebugCodeInfo { } /** - * access details of a specific heap object. + * Access details of a specific heap object. */ interface DebugDataInfo { } /** - * access details of code generated for a specific outer or inlined method at a given line + * Access details of code generated for a specific outer or inlined method at a given line * number. */ interface DebugLineInfo { /** - * @return the name of the file containing the outer or inlined method excluding any path + * @return the name of the file containing the outer or inlined method excluding any path. */ String fileName(); /** * @return a relative path to the file containing the outer or inlined method derived from - * its package name or null if the method is in the empty package + * its package name or null if the method is in the empty package. */ Path filePath(); /** - * @return the fully qualified name of the class owning the outer or inlined method + * @return the fully qualified name of the class owning the outer or inlined method. */ String className(); /** - * @return the name of the outer or inlined method including signature + * @return the name of the outer or inlined method including signature. */ String methodName(); /** * @return the lowest address containing code generated for an outer or inlined code segment - * reported at this line represented as an offset into the code segment + * reported at this line represented as an offset into the code segment. */ int addressLo(); /** * @return the first address above the code generated for an outer or inlined code segment - * reported at this line represented as an offset into the code segment + * reported at this line represented as an offset into the code segment. */ int addressHi(); /** - * @return the line number for the outer or inlined segment + * @return the line number for the outer or inlined segment. */ int line(); } @@ -172,9 +177,11 @@ enum Type { DebugFrameSizeChange.Type getType(); } + @SuppressWarnings("unused") Stream typeInfoProvider(); Stream codeInfoProvider(); + @SuppressWarnings("unused") Stream dataInfoProvider(); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java index 68b33d97287d..ef1312dbcb5c 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java @@ -1168,36 +1168,38 @@ protected int getMinimumFileSize() { @Override public void installDebugInfo(DebugInfoProvider debugInfoProvider) { DwarfSections dwarfSections = new DwarfSections(getMachine(), getByteOrder()); - // we need an implementation for each section + /* We need an implementation for each generated DWARF section. */ DwarfStrSectionImpl elfStrSectionImpl = dwarfSections.getStrSectionImpl(); DwarfAbbrevSectionImpl elfAbbrevSectionImpl = dwarfSections.getAbbrevSectionImpl(); DwarfFrameSectionImpl frameSectionImpl = dwarfSections.getFrameSectionImpl(); DwarfInfoSectionImpl elfInfoSectionImpl = dwarfSections.getInfoSectionImpl(); DwarfARangesSectionImpl elfARangesSectionImpl = dwarfSections.getARangesSectionImpl(); DwarfLineSectionImpl elfLineSectionImpl = dwarfSections.getLineSectionImpl(); - // now we can create the section elements with empty content + /* Now we can create the section elements with empty content. */ newUserDefinedSection(elfStrSectionImpl.getSectionName(), elfStrSectionImpl); newUserDefinedSection(elfAbbrevSectionImpl.getSectionName(), elfAbbrevSectionImpl); newUserDefinedSection(frameSectionImpl.getSectionName(), frameSectionImpl); newUserDefinedSection(elfInfoSectionImpl.getSectionName(), elfInfoSectionImpl); newUserDefinedSection(elfARangesSectionImpl.getSectionName(), elfARangesSectionImpl); newUserDefinedSection(elfLineSectionImpl.getSectionName(), elfLineSectionImpl); - // the byte[] for each implementation's content are created and - // written under getOrDecideContent. doing that ensures that all - // dependent sections are filled in and then sized according to the - // declared dependencies. however, if we leave it at that then - // associated reloc sections only get created when the first reloc - // is inserted during content write that's too late for them to have - // layout constraints included in the layout decision set and causes - // an NPE during reloc section write. so we need to create the relevant - // reloc sections here in advance + /* + * The byte[] for each implementation's content are created and + * written under getOrDecideContent. Doing that ensures that all + * dependent sections are filled in and then sized according to the + * declared dependencies. However, if we leave it at that then + * associated reloc sections only get created when the first reloc + * is inserted during content write that's too late for them to have + * layout constraints included in the layout decision set and causes + * an NPE during reloc section write. So we need to create the relevant + * reloc sections here in advance. + */ elfStrSectionImpl.getOrCreateRelocationElement(false); elfAbbrevSectionImpl.getOrCreateRelocationElement(false); frameSectionImpl.getOrCreateRelocationElement(false); elfInfoSectionImpl.getOrCreateRelocationElement(false); elfARangesSectionImpl.getOrCreateRelocationElement(false); elfLineSectionImpl.getOrCreateRelocationElement(false); - // ok now we can populate the implementations + /* Ok now we can populate the debug info model. */ dwarfSections.installDebugInfo(debugInfoProvider); } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java index e2bcb27740f7..2d5405f26fd7 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java @@ -32,6 +32,7 @@ import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.debugentry.PrimaryEntry; import com.oracle.objectfile.debugentry.Range; +import org.graalvm.compiler.debug.DebugContext; import java.util.LinkedList; import java.util.Map; @@ -60,7 +61,7 @@ public String getSectionName() { public void createContent() { int pos = 0; /* - * we need an entry for each compilation unit + * We need an entry for each compilation unit. * *
    * @@ -76,7 +77,7 @@ public void createContent() { * *
* - * i.e. 12 bytes followed by padding aligning up to 2 * address size + * That is 12 bytes followed by padding aligning up to 2 * address size. * *
    * @@ -84,7 +85,7 @@ public void createContent() { * *
* - * followed by N + 1 times + * Followed by N + 1 times: * *
  • uint64 lo ................ lo address of range * @@ -92,14 +93,14 @@ public void createContent() { * *
* - * where N is the number of ranges belonging to the compilation unit and the last range - * contains two zeroes + * Where N is the number of ranges belonging to the compilation unit and the last range + * contains two zeroes. */ for (ClassEntry classEntry : getPrimaryClasses()) { pos += DW_AR_HEADER_SIZE; /* - * align to 2 * address size + * Align to 2 * address size. */ pos += DW_AR_HEADER_PAD_SIZE; pos += classEntry.getPrimaryEntries().size() * 2 * 8; @@ -117,8 +118,8 @@ public byte[] getOrDecideContent(Map alre Object valueObj = decisionMap.getDecidedValue(LayoutDecision.Kind.VADDR); if (valueObj != null && valueObj instanceof Number) { /* - * this may not be the final vaddr for the text segment but it will be close enough - * to make debug easier i.e. to within a 4k page or two + * This may not be the final vaddr for the text segment but it will be close enough + * to make debug easier i.e. to within a 4k page or two. */ debugTextBase = ((Number) valueObj).longValue(); } @@ -127,44 +128,44 @@ public byte[] getOrDecideContent(Map alre } @Override - public void writeContent() { + public void writeContent(DebugContext context) { byte[] buffer = getContent(); int size = buffer.length; int pos = 0; - checkDebug(pos); + enableLog(context, pos); - debug(" [0x%08x] DEBUG_ARANGES\n", pos); + log(context, " [0x%08x] DEBUG_ARANGES", pos); for (ClassEntry classEntry : getPrimaryClasses()) { int lastpos = pos; int length = DW_AR_HEADER_SIZE + DW_AR_HEADER_PAD_SIZE - 4; int cuIndex = classEntry.getCUIndex(); LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); /* - * add room for each entry into length count + * Add room for each entry into length count. */ length += classPrimaryEntries.size() * 2 * 8; length += 2 * 8; - debug(" [0x%08x] %s CU %d length 0x%x\n", pos, classEntry.getFileName(), cuIndex, length); + log(context, " [0x%08x] %s CU %d length 0x%x", pos, classEntry.getFileName(), cuIndex, length); pos = putInt(length, buffer, pos); - /* dwarf version is always 2 */ + /* DWARF version is always 2. */ pos = putShort(DW_VERSION_2, buffer, pos); pos = putInt(cuIndex, buffer, pos); - /* address size is always 8 */ + /* Address size is always 8. */ pos = putByte((byte) 8, buffer, pos); - /* segment size is always 0 */ + /* Segment size is always 0. */ pos = putByte((byte) 0, buffer, pos); assert (pos - lastpos) == DW_AR_HEADER_SIZE; /* - * align to 2 * address size + * Align to 2 * address size. */ for (int i = 0; i < DW_AR_HEADER_PAD_SIZE; i++) { pos = putByte((byte) 0, buffer, pos); } - debug(" [0x%08x] Address Length Name\n", pos); + log(context, " [0x%08x] Address Length Name", pos); for (PrimaryEntry classPrimaryEntry : classPrimaryEntries) { Range primary = classPrimaryEntry.getPrimary(); - debug(" [0x%08x] %016x %016x %s\n", pos, debugTextBase + primary.getLo(), primary.getHi() - primary.getLo(), primary.getFullMethodName()); + log(context, " [0x%08x] %016x %016x %s", pos, debugTextBase + primary.getLo(), primary.getHi() - primary.getLo(), primary.getFullMethodName()); pos = putRelocatableCodeOffset(primary.getLo(), buffer, pos); pos = putLong(primary.getHi() - primary.getLo(), buffer, pos); } @@ -175,22 +176,17 @@ public void writeContent() { assert pos == size; } - @Override - protected void debug(String format, Object... args) { - super.debug(format, args); - } - /* - * debug_aranges section content depends on debug_info section content and offset + * The debug_aranges section content depends on debug_info section content and offset. */ - public static final String TARGET_SECTION_NAME = DW_INFO_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DW_INFO_SECTION_NAME; @Override public String targetSectionName() { return TARGET_SECTION_NAME; } - public final LayoutDecision.Kind[] targetSectionKinds = { + private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, LayoutDecision.Kind.OFFSET }; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java index b3cc40de399b..6f8d7d44c2d4 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java @@ -27,6 +27,7 @@ package com.oracle.objectfile.elf.dwarf; import com.oracle.objectfile.LayoutDecision; +import org.graalvm.compiler.debug.DebugContext; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_CODE_compile_unit; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_CODE_subprogram; @@ -68,11 +69,11 @@ public String getSectionName() { public void createContent() { int pos = 0; /* - * an abbrev table contains abbrev entries for one or more CUs. the table includes a + * An abbrev table contains abbrev entries for one or more CUs. the table includes a * sequence of abbrev entries each of which defines a specific DIE layout employed to - * describe some DIE in a CU. a table is terminated by a null entry + * describe some DIE in a CU. a table is terminated by a null entry. * - * a null entry has consists of just a 0 abbrev code + * A null entry has consists of just a 0 abbrev code. * *
    * @@ -80,7 +81,7 @@ public void createContent() { * *
* - * non-null entries have the following format + * Mon-null entries have the following format. * *
    * @@ -135,27 +136,27 @@ public void createContent() { *
*/ - pos = writeAbbrev1(null, pos); - pos = writeAbbrev2(null, pos); + pos = writeAbbrev1(null, null, pos); + pos = writeAbbrev2(null, null, pos); byte[] buffer = new byte[pos]; super.setContent(buffer); } @Override - public void writeContent() { + public void writeContent(DebugContext context) { byte[] buffer = getContent(); int size = buffer.length; int pos = 0; - checkDebug(pos); + enableLog(context, pos); - pos = writeAbbrev1(buffer, pos); - pos = writeAbbrev2(buffer, pos); + pos = writeAbbrev1(context, buffer, pos); + pos = writeAbbrev2(context, buffer, pos); assert pos == size; } - public int writeAttrType(long code, byte[] buffer, int pos) { + private int writeAttrType(long code, byte[] buffer, int pos) { if (buffer == null) { return pos + putSLEB(code, scratch, 0); } else { @@ -163,7 +164,7 @@ public int writeAttrType(long code, byte[] buffer, int pos) { } } - public int writeAttrForm(long code, byte[] buffer, int pos) { + private int writeAttrForm(long code, byte[] buffer, int pos) { if (buffer == null) { return pos + putSLEB(code, scratch, 0); } else { @@ -171,10 +172,11 @@ public int writeAttrForm(long code, byte[] buffer, int pos) { } } - public int writeAbbrev1(byte[] buffer, int p) { + @SuppressWarnings("unused") + private int writeAbbrev1(DebugContext context, byte[] buffer, int p) { int pos = p; /* - * abbrev 1 compile unit + * Abbrev 1 compile unit. */ pos = writeAbbrevCode(DW_ABBREV_CODE_compile_unit, buffer, pos); pos = writeTag(DW_TAG_compile_unit, buffer, pos); @@ -190,17 +192,18 @@ public int writeAbbrev1(byte[] buffer, int p) { pos = writeAttrType(DW_AT_stmt_list, buffer, pos); pos = writeAttrForm(DW_FORM_data4, buffer, pos); /* - * now terminate + * Now terminate. */ pos = writeAttrType(DW_AT_null, buffer, pos); pos = writeAttrForm(DW_FORM_null, buffer, pos); return pos; } - public int writeAbbrev2(byte[] buffer, int p) { + @SuppressWarnings("unused") + private int writeAbbrev2(DebugContext context, byte[] buffer, int p) { int pos = p; /* - * abbrev 2 compile unit + * Abbrev 2 compile unit. */ pos = writeAbbrevCode(DW_ABBREV_CODE_subprogram, buffer, pos); pos = writeTag(DW_TAG_subprogram, buffer, pos); @@ -214,29 +217,24 @@ public int writeAbbrev2(byte[] buffer, int p) { pos = writeAttrType(DW_AT_external, buffer, pos); pos = writeAttrForm(DW_FORM_flag, buffer, pos); /* - * now terminate + * Now terminate. */ pos = writeAttrType(DW_AT_null, buffer, pos); pos = writeAttrForm(DW_FORM_null, buffer, pos); return pos; } - @Override - protected void debug(String format, Object... args) { - super.debug(format, args); - } - /** - * debug_abbrev section content depends on debug_frame section content and offset. + * The debug_abbrev section content depends on debug_frame section content and offset. */ - public static final String TARGET_SECTION_NAME = DW_FRAME_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DW_FRAME_SECTION_NAME; @Override public String targetSectionName() { return TARGET_SECTION_NAME; } - public final LayoutDecision.Kind[] targetSectionKinds = { + private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, LayoutDecision.Kind.OFFSET }; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java index a7abe33f8034..9e5420d8b499 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java @@ -30,6 +30,7 @@ import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.debugentry.PrimaryEntry; import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import org.graalvm.compiler.debug.DebugContext; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_CIE_id; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_CIE_version; @@ -50,6 +51,8 @@ */ public abstract class DwarfFrameSectionImpl extends DwarfSectionImpl { + private static final int PADDING_NOPS_ALIGNMENT = 8; + public DwarfFrameSectionImpl(DwarfSections dwarfSections) { super(dwarfSections); } @@ -64,7 +67,7 @@ public void createContent() { int pos = 0; /* - * the frame section contains one CIE at offset 0 followed by an FIE for each method + * The frame section contains one CIE at offset 0 followed by an FIE for each method. */ pos = writeCIE(null, pos); pos = writeMethodFrames(null, pos); @@ -74,17 +77,17 @@ public void createContent() { } @Override - public void writeContent() { + public void writeContent(DebugContext context) { byte[] buffer = getContent(); int size = buffer.length; int pos = 0; - checkDebug(pos); + enableLog(context, pos); /* - * there are entries for the prologue region where the stack is being built, the method body + * There are entries for the prologue region where the stack is being built, the method body * region(s) where the code executes with a fixed size frame and the epilogue region(s) - * where the stack is torn down + * where the stack is torn down. */ pos = writeCIE(buffer, pos); pos = writeMethodFrames(buffer, pos); @@ -95,10 +98,10 @@ public void writeContent() { assert pos == size; } - public int writeCIE(byte[] buffer, int p) { + private int writeCIE(byte[] buffer, int p) { /* - * we only need a vanilla CIE with default fields because we have to have at least one the - * layout is + * We only need a vanilla CIE with default fields because we have to have at least one the + * layout is: * *
    * @@ -130,15 +133,15 @@ public int writeCIE(byte[] buffer, int p) { pos += putSLEB(-8, scratch, 0); pos += putByte((byte) getPCIdx(), scratch, 0); /* - * write insns to set up empty frame + * Write insns to set up empty frame. */ pos = writeInitialInstructions(buffer, pos); /* - * pad to word alignment + * Pad to word alignment. */ - pos = writePaddingNops(8, buffer, pos); + pos = writePaddingNops(buffer, pos); /* - * no need to write length + * No need to write length. */ return pos; } else { @@ -155,15 +158,15 @@ public int writeCIE(byte[] buffer, int p) { */ pos = writeInitialInstructions(buffer, pos); /* - * pad to word alignment + * Pad to word alignment. */ - pos = writePaddingNops(8, buffer, pos); + pos = writePaddingNops(buffer, pos); patchLength(lengthPos, buffer, pos); return pos; } } - public int writeMethodFrames(byte[] buffer, int p) { + private int writeMethodFrames(byte[] buffer, int p) { int pos = p; for (ClassEntry classEntry : getPrimaryClasses()) { for (PrimaryEntry primaryEntry : classEntry.getPrimaryEntries()) { @@ -179,26 +182,26 @@ public int writeMethodFrames(byte[] buffer, int p) { pos = writeAdvanceLoc(advance, buffer, pos); if (debugFrameSizeInfo.getType() == DebugInfoProvider.DebugFrameSizeChange.Type.EXTEND) { /* - * SP has been extended so rebase CFA using full frame + * SP has been extended so rebase CFA using full frame. */ pos = writeDefCFAOffset(frameSize, buffer, pos); } else { /* - * SP has been contracted so rebase CFA using empty frame + * SP has been contracted so rebase CFA using empty frame. */ pos = writeDefCFAOffset(8, buffer, pos); } } - pos = writePaddingNops(8, buffer, pos); + pos = writePaddingNops(buffer, pos); patchLength(lengthPos, buffer, pos); } } return pos; } - public int writeFDEHeader(int lo, int hi, byte[] buffer, int p) { + private int writeFDEHeader(int lo, int hi, byte[] buffer, int p) { /* - * we only need a vanilla FDE header with default fields the layout is + * We only need a vanilla FDE header with default fields the layout is: * *
      * @@ -218,30 +221,29 @@ public int writeFDEHeader(int lo, int hi, byte[] buffer, int p) { int pos = p; if (buffer == null) { - /* dummy length */ + /* Dummy length. */ pos += putInt(0, scratch, 0); /* CIE_offset */ pos += putInt(0, scratch, 0); - /* initial address */ + /* Initial address. */ pos += putLong(lo, scratch, 0); - /* address range */ + /* Address range. */ return pos + putLong(hi - lo, scratch, 0); } else { - /* dummy length */ + /* Dummy length. */ pos = putInt(0, buffer, pos); /* CIE_offset */ pos = putInt(0, buffer, pos); - /* initial address */ + /* Initial address. */ pos = putRelocatableCodeOffset(lo, buffer, pos); - /* address range */ + /* Address range. */ return putLong(hi - lo, buffer, pos); } } - public int writePaddingNops(int alignment, byte[] buffer, int p) { + private int writePaddingNops(byte[] buffer, int p) { int pos = p; - assert (alignment & (alignment - 1)) == 0; - while ((pos & (alignment - 1)) != 0) { + while ((pos & (PADDING_NOPS_ALIGNMENT - 1)) != 0) { if (buffer == null) { pos++; } else { @@ -251,7 +253,7 @@ public int writePaddingNops(int alignment, byte[] buffer, int p) { return pos; } - public int writeDefCFA(int register, int offset, byte[] buffer, int p) { + protected int writeDefCFA(int register, int offset, byte[] buffer, int p) { int pos = p; if (buffer == null) { pos += putByte(DW_CFA_def_cfa, scratch, 0); @@ -264,7 +266,7 @@ public int writeDefCFA(int register, int offset, byte[] buffer, int p) { } } - public int writeDefCFAOffset(int offset, byte[] buffer, int p) { + protected int writeDefCFAOffset(int offset, byte[] buffer, int p) { int pos = p; if (buffer == null) { pos += putByte(DW_CFA_def_cfa_offset, scratch, 0); @@ -275,7 +277,7 @@ public int writeDefCFAOffset(int offset, byte[] buffer, int p) { } } - public int writeAdvanceLoc(int offset, byte[] buffer, int pos) { + protected int writeAdvanceLoc(int offset, byte[] buffer, int pos) { if (offset <= 0x3f) { return writeAdvanceLoc0((byte) offset, buffer, pos); } else if (offset <= 0xff) { @@ -287,7 +289,7 @@ public int writeAdvanceLoc(int offset, byte[] buffer, int pos) { } } - public int writeAdvanceLoc0(byte offset, byte[] buffer, int pos) { + protected int writeAdvanceLoc0(byte offset, byte[] buffer, int pos) { byte op = advanceLoc0Op(offset); if (buffer == null) { return pos + putByte(op, scratch, 0); @@ -296,7 +298,7 @@ public int writeAdvanceLoc0(byte offset, byte[] buffer, int pos) { } } - public int writeAdvanceLoc1(byte offset, byte[] buffer, int p) { + protected int writeAdvanceLoc1(byte offset, byte[] buffer, int p) { int pos = p; byte op = DW_CFA_advance_loc1; if (buffer == null) { @@ -308,7 +310,7 @@ public int writeAdvanceLoc1(byte offset, byte[] buffer, int p) { } } - public int writeAdvanceLoc2(short offset, byte[] buffer, int p) { + protected int writeAdvanceLoc2(short offset, byte[] buffer, int p) { byte op = DW_CFA_advance_loc2; int pos = p; if (buffer == null) { @@ -320,7 +322,7 @@ public int writeAdvanceLoc2(short offset, byte[] buffer, int p) { } } - public int writeAdvanceLoc4(int offset, byte[] buffer, int p) { + protected int writeAdvanceLoc4(int offset, byte[] buffer, int p) { byte op = DW_CFA_advance_loc4; int pos = p; if (buffer == null) { @@ -332,7 +334,7 @@ public int writeAdvanceLoc4(int offset, byte[] buffer, int p) { } } - public int writeOffset(int register, int offset, byte[] buffer, int p) { + protected int writeOffset(int register, int offset, byte[] buffer, int p) { byte op = offsetOp(register); int pos = p; if (buffer == null) { @@ -344,7 +346,7 @@ public int writeOffset(int register, int offset, byte[] buffer, int p) { } } - public int writeRegister(int savedReg, int savedToReg, byte[] buffer, int p) { + protected int writeRegister(int savedReg, int savedToReg, byte[] buffer, int p) { int pos = p; if (buffer == null) { pos += putByte(DW_CFA_register, scratch, 0); @@ -357,28 +359,24 @@ public int writeRegister(int savedReg, int savedToReg, byte[] buffer, int p) { } } - public abstract int getPCIdx(); - - public abstract int getSPIdx(); + protected abstract int getPCIdx(); - public abstract int writeInitialInstructions(byte[] buffer, int pos); + @SuppressWarnings("unused") + protected abstract int getSPIdx(); - @Override - protected void debug(String format, Object... args) { - super.debug(format, args); - } + protected abstract int writeInitialInstructions(byte[] buffer, int pos); /** - * debug_frame section content depends on debug_line section content and offset. + * The debug_frame section content depends on debug_line section content and offset. */ - public static final String TARGET_SECTION_NAME = DW_LINE_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DW_LINE_SECTION_NAME; @Override public String targetSectionName() { return TARGET_SECTION_NAME; } - public final LayoutDecision.Kind[] targetSectionKinds = { + private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, LayoutDecision.Kind.OFFSET }; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java index 89268fd72b7c..02d66f83134a 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java @@ -32,9 +32,9 @@ */ public class DwarfFrameSectionImplAArch64 extends DwarfFrameSectionImpl { // public static final int DW_CFA_FP_IDX = 29; - public static final int DW_CFA_LR_IDX = 30; - public static final int DW_CFA_SP_IDX = 31; - public static final int DW_CFA_PC_IDX = 32; + private static final int DW_CFA_LR_IDX = 30; + private static final int DW_CFA_SP_IDX = 31; + private static final int DW_CFA_PC_IDX = 32; public DwarfFrameSectionImplAArch64(DwarfSections dwarfSections) { super(dwarfSections); @@ -54,7 +54,7 @@ public int getSPIdx() { public int writeInitialInstructions(byte[] buffer, int p) { int pos = p; /* - * rsp has not been updated and caller pc is in lr + * Register rsp has not been updated and caller pc is in lr: * *
        * diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java index 275c4ca00a32..9baec098d6c8 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java @@ -31,8 +31,8 @@ * and frame layout. */ public class DwarfFrameSectionImplX86_64 extends DwarfFrameSectionImpl { - public static final int DW_CFA_RSP_IDX = 7; - public static final int DW_CFA_RIP_IDX = 16; + private static final int DW_CFA_RSP_IDX = 7; + private static final int DW_CFA_RIP_IDX = 16; public DwarfFrameSectionImplX86_64(DwarfSections dwarfSections) { super(dwarfSections); @@ -52,8 +52,8 @@ public int getSPIdx() { public int writeInitialInstructions(byte[] buffer, int p) { int pos = p; /* - * rsp points at the word containing the saved rip so the frame base (cfa) is at rsp + 8 - * (why not - ???) + * Register rsp points at the word containing the saved rip so the frame base (cfa) is at + * rsp + 8: * *
          * @@ -63,10 +63,12 @@ public int writeInitialInstructions(byte[] buffer, int p) { */ pos = writeDefCFA(DW_CFA_RSP_IDX, 8, buffer, pos); /* - * rip is saved at offset 8 (coded as 1 which gets scaled by dataAlignment) from cfa (why - * not -1 ???) + * Register rip is saved at offset 8 (coded as 1 which gets scaled by dataAlignment) from + * cfa * - *
          • offset r16 (rip) cfa - 8 + *
              + * + *
            • offset r16 (rip) cfa - 8 * *
            */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index f13c5c3e1701..4a0ce9f21b45 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -30,6 +30,7 @@ import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.debugentry.PrimaryEntry; import com.oracle.objectfile.debugentry.Range; +import org.graalvm.compiler.debug.DebugContext; import java.util.LinkedList; @@ -62,7 +63,7 @@ public String getSectionName() { @Override public void createContent() { /* - * we need a single level 0 DIE for each compilation unit (CU). Each CU's Level 0 DIE is + * We need a single level 0 DIE for each compilation unit (CU). Each CU's Level 0 DIE is * preceded by a fixed header and terminated by a null DIE: * *
              @@ -81,7 +82,7 @@ public void createContent() { * *
            * - * a DIE is a recursively defined structure. it starts with a code for the associated abbrev + * A DIE is a recursively defined structure. it starts with a code for the associated abbrev * entry followed by a series of attribute values, as determined by the entry, terminated by * a null value and followed by zero or more child DIEs (zero iff has_children == * no_children). @@ -100,7 +101,7 @@ public void createContent() { * *
          * - * note that a null_DIE looks like + * Note that a null_DIE looks like: * *
            * @@ -108,7 +109,7 @@ public void createContent() { * *
          * - * i.e. it also looks like a null_value + * i.e. it also looks like a null_value. */ byte[] buffer = null; @@ -118,9 +119,9 @@ public void createContent() { int lengthPos = pos; pos = writeCUHeader(buffer, pos); assert pos == lengthPos + DW_DIE_HEADER_SIZE; - pos = writeCU(classEntry, buffer, pos); + pos = writeCU(null, classEntry, buffer, pos); /* - * no need to backpatch length at lengthPos + * No need to backpatch length at lengthPos. */ } buffer = new byte[pos]; @@ -128,100 +129,100 @@ public void createContent() { } @Override - public void writeContent() { + public void writeContent(DebugContext context) { byte[] buffer = getContent(); int size = buffer.length; int pos = 0; - checkDebug(pos); + enableLog(context, pos); - debug(" [0x%08x] DEBUG_INFO\n", pos); - debug(" [0x%08x] size = 0x%08x\n", pos, size); + log(context, " [0x%08x] DEBUG_INFO", pos); + log(context, " [0x%08x] size = 0x%08x", pos, size); for (ClassEntry classEntry : getPrimaryClasses()) { /* - * save the offset of this file's CU so it can be used when writing the aranges section + * Save the offset of this file's CU so it can be used when writing the aranges section. */ classEntry.setCUIndex(pos); int lengthPos = pos; pos = writeCUHeader(buffer, pos); - debug(" [0x%08x] Compilation Unit\n", pos, size); + log(context, " [0x%08x] Compilation Unit", pos, size); assert pos == lengthPos + DW_DIE_HEADER_SIZE; - pos = writeCU(classEntry, buffer, pos); + pos = writeCU(context, classEntry, buffer, pos); /* - * backpatch length at lengthPos (excluding length field) + * Backpatch length at lengthPos (excluding length field). */ patchLength(lengthPos, buffer, pos); } assert pos == size; } - public int writeCUHeader(byte[] buffer, int p) { + private int writeCUHeader(byte[] buffer, int p) { int pos = p; if (buffer == null) { - /* CU length */ + /* CU length. */ pos += putInt(0, scratch, 0); - /* dwarf version */ + /* DWARF version. */ pos += putShort(DW_VERSION_2, scratch, 0); - /* abbrev offset */ + /* Abbrev offset. */ pos += putInt(0, scratch, 0); - /* address size */ + /* Address size. */ return pos + putByte((byte) 8, scratch, 0); } else { - /* CU length */ + /* CU length. */ pos = putInt(0, buffer, pos); - /* dwarf version */ + /* DWARF version. */ pos = putShort(DW_VERSION_2, buffer, pos); - /* abbrev offset */ + /* Abbrev offset. */ pos = putInt(0, buffer, pos); - /* address size */ + /* Address size. */ return putByte((byte) 8, buffer, pos); } } - public int writeCU(ClassEntry classEntry, byte[] buffer, int p) { + private int writeCU(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); - debug(" [0x%08x] <0> Abbrev Number %d\n", pos, DW_ABBREV_CODE_compile_unit); + log(context, " [0x%08x] <0> Abbrev Number %d", pos, DW_ABBREV_CODE_compile_unit); pos = writeAbbrevCode(DW_ABBREV_CODE_compile_unit, buffer, pos); - debug(" [0x%08x] language %s\n", pos, "DW_LANG_Java"); + log(context, " [0x%08x] language %s", pos, "DW_LANG_Java"); pos = writeAttrData1(DW_LANG_Java, buffer, pos); - debug(" [0x%08x] name 0x%x (%s)\n", pos, debugStringIndex(classEntry.getFileName()), classEntry.getFileName()); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(classEntry.getFileName()), classEntry.getFileName()); pos = writeAttrStrp(classEntry.getFileName(), buffer, pos); - debug(" [0x%08x] low_pc 0x%08x\n", pos, classPrimaryEntries.getFirst().getPrimary().getLo()); + log(context, " [0x%08x] lo_pc 0x%08x", pos, classPrimaryEntries.getFirst().getPrimary().getLo()); pos = writeAttrAddress(classPrimaryEntries.getFirst().getPrimary().getLo(), buffer, pos); - debug(" [0x%08x] hi_pc 0x%08x\n", pos, classPrimaryEntries.getLast().getPrimary().getHi()); + log(context, " [0x%08x] hi_pc 0x%08x", pos, classPrimaryEntries.getLast().getPrimary().getHi()); pos = writeAttrAddress(classPrimaryEntries.getLast().getPrimary().getHi(), buffer, pos); - debug(" [0x%08x] stmt_list 0x%08x\n", pos, classEntry.getLineIndex()); + log(context, " [0x%08x] stmt_list 0x%08x", pos, classEntry.getLineIndex()); pos = writeAttrData4(classEntry.getLineIndex(), buffer, pos); for (PrimaryEntry primaryEntry : classPrimaryEntries) { - pos = writePrimary(primaryEntry, buffer, pos); + pos = writePrimary(context, primaryEntry, buffer, pos); } /* - * write a terminating null attribute for the the level 2 primaries + * Write a terminating null attribute for the the level 2 primaries. */ return writeAttrNull(buffer, pos); } - public int writePrimary(PrimaryEntry primaryEntry, byte[] buffer, int p) { + private int writePrimary(DebugContext context, PrimaryEntry primaryEntry, byte[] buffer, int p) { int pos = p; Range primary = primaryEntry.getPrimary(); - debug(" [0x%08x] <1> Abbrev Number %d\n", pos, DW_ABBREV_CODE_subprogram); + verboseLog(context, " [0x%08x] <1> Abbrev Number %d", pos, DW_ABBREV_CODE_subprogram); pos = writeAbbrevCode(DW_ABBREV_CODE_subprogram, buffer, pos); - debug(" [0x%08x] name 0x%X (%s)\n", pos, debugStringIndex(primary.getFullMethodName()), primary.getFullMethodName()); + verboseLog(context, " [0x%08x] name 0x%X (%s)", pos, debugStringIndex(primary.getFullMethodName()), primary.getFullMethodName()); pos = writeAttrStrp(primary.getFullMethodName(), buffer, pos); - debug(" [0x%08x] low_pc 0x%08x\n", pos, primary.getLo()); + verboseLog(context, " [0x%08x] lo_pc 0x%08x", pos, primary.getLo()); pos = writeAttrAddress(primary.getLo(), buffer, pos); - debug(" [0x%08x] high_pc 0x%08x\n", pos, primary.getHi()); + verboseLog(context, " [0x%08x] hi_pc 0x%08x", pos, primary.getHi()); pos = writeAttrAddress(primary.getHi(), buffer, pos); /* - * need to pass true only if method is public + * Need to pass true only if method is public. */ - debug(" [0x%08x] external true\n", pos); + verboseLog(context, " [0x%08x] external true", pos); return writeFlag(DW_FLAG_true, buffer, pos); } - public int writeAttrStrp(String value, byte[] buffer, int p) { + private int writeAttrStrp(String value, byte[] buffer, int p) { int pos = p; if (buffer == null) { return pos + putInt(0, scratch, 0); @@ -231,6 +232,7 @@ public int writeAttrStrp(String value, byte[] buffer, int p) { } } + @SuppressWarnings("unused") public int writeAttrString(String value, byte[] buffer, int p) { int pos = p; if (buffer == null) { @@ -240,26 +242,17 @@ public int writeAttrString(String value, byte[] buffer, int p) { } } - @Override - protected void debug(String format, Object... args) { - if (((int) args[0] - debugBase) < 0x100000) { - super.debug(format, args); - } else if (format.startsWith(" [0x%08x] primary file")) { - super.debug(format, args); - } - } - /** - * debug_info section content depends on abbrev section content and offset. + * The debug_info section content depends on abbrev section content and offset. */ - public static final String TARGET_SECTION_NAME = DW_ABBREV_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DW_ABBREV_SECTION_NAME; @Override public String targetSectionName() { return TARGET_SECTION_NAME; } - public final LayoutDecision.Kind[] targetSectionKinds = { + private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, LayoutDecision.Kind.OFFSET }; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java index 0dc635f2ad65..91a433f69347 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java @@ -34,6 +34,7 @@ import com.oracle.objectfile.debugentry.FileEntry; import com.oracle.objectfile.debugentry.PrimaryEntry; import com.oracle.objectfile.debugentry.Range; +import org.graalvm.compiler.debug.DebugContext; import java.util.Map; @@ -46,88 +47,87 @@ */ public class DwarfLineSectionImpl extends DwarfSectionImpl { /** - * line header section always contains fixed number of bytes. + * Line header section always contains fixed number of bytes. */ private static final int DW_LN_HEADER_SIZE = 27; /** - * current generator follows C++ with line base -5. + * Current generator follows C++ with line base -5. */ private static final int DW_LN_LINE_BASE = -5; /** - * current generator follows C++ with line range 14 giving full range -5 to 8. + * Current generator follows C++ with line range 14 giving full range -5 to 8. */ private static final int DW_LN_LINE_RANGE = 14; /** - * current generator uses opcode base of 13 which must equal DW_LNS_define_file + 1. + * Current generator uses opcode base of 13 which must equal DW_LNS_define_file + 1. */ private static final int DW_LN_OPCODE_BASE = 13; /* - * standard opcodes defined by Dwarf 2 + * Standard opcodes defined by Dwarf 2 */ /* - * 0 can be returned to indicate an invalid opcode + * 0 can be returned to indicate an invalid opcode. */ private static final byte DW_LNS_undefined = 0; /* - * 0 can be inserted as a prefix for extended opcodes + * 0 can be inserted as a prefix for extended opcodes. */ private static final byte DW_LNS_extended_prefix = 0; /* - * append current state as matrix row 0 args + * Append current state as matrix row 0 args. */ private static final byte DW_LNS_copy = 1; /* - * increment address 1 uleb arg + * Increment address 1 uleb arg. */ private static final byte DW_LNS_advance_pc = 2; /* - * increment line 1 sleb arg + * Increment line 1 sleb arg. */ private static final byte DW_LNS_advance_line = 3; /* - * set file 1 uleb arg + * Set file 1 uleb arg. */ private static final byte DW_LNS_set_file = 4; /* - * set column 1 uleb arg + * sSet column 1 uleb arg. */ private static final byte DW_LNS_set_column = 5; /* - * flip is_stmt 0 args + * Flip is_stmt 0 args. */ private static final byte DW_LNS_negate_stmt = 6; /* - * set end sequence and copy row 0 args + * Set end sequence and copy row 0 args. */ private static final byte DW_LNS_set_basic_block = 7; /* - * increment address as per opcode 255 0 args + * Increment address as per opcode 255 0 args. */ private static final byte DW_LNS_const_add_pc = 8; /* - * increment address 1 ushort arg + * Increment address 1 ushort arg. */ private static final byte DW_LNS_fixed_advance_pc = 9; /* - * extended opcodes defined by Dwarf 2 + * Extended opcodes defined by DWARF 2. */ /* - * there is no extended opcode 0 + * There is no extended opcode 0. */ - @SuppressWarnings("unused") - private static final byte DW_LNE_undefined = 0; + @SuppressWarnings("unused") private static final byte DW_LNE_undefined = 0; /* - * end sequence of addresses + * End sequence of addresses. */ private static final byte DW_LNE_end_sequence = 1; /* - * set address as explicit long argument + * Set address as explicit long argument. */ private static final byte DW_LNE_set_address = 2; /* - * set file as explicit string argument + * Set file as explicit string argument. */ private static final byte DW_LNE_define_file = 3; @@ -143,12 +143,12 @@ public String getSectionName() { @Override public void createContent() { /* - * we need to create a header, dir table, file table and line number table encoding for each - * CU + * We need to create a header, dir table, file table and line number table encoding for each + * CU. */ /* - * write entries for each file listed in the primary list + * Write entries for each file listed in the primary list. */ int pos = 0; for (ClassEntry classEntry : getPrimaryClasses()) { @@ -170,9 +170,9 @@ public void createContent() { super.setContent(buffer); } - public int headerSize() { + private static int headerSize() { /* - * header size is standard 31 bytes + * Header size is standard 31 bytes: * *
            * @@ -202,62 +202,62 @@ public int headerSize() { return DW_LN_HEADER_SIZE; } - public int computeDirTableSize(ClassEntry classEntry) { + private static int computeDirTableSize(ClassEntry classEntry) { /* - * table contains a sequence of 'nul'-terminated dir name bytes followed by an extra 'nul' - * and then a sequence of 'nul'-terminated file name bytes followed by an extra 'nul' + * Table contains a sequence of 'nul'-terminated dir name bytes followed by an extra 'nul' + * and then a sequence of 'nul'-terminated file name bytes followed by an extra 'nul'. * - * for now we assume dir and file names are ASCII byte strings + * For now we assume dir and file names are ASCII byte strings. */ int dirSize = 0; for (DirEntry dir : classEntry.getLocalDirs()) { dirSize += dir.getPathString().length() + 1; } /* - * allow for separator nul + * Allow for separator nul. */ dirSize++; return dirSize; } - public int computeFileTableSize(ClassEntry classEntry) { + private int computeFileTableSize(ClassEntry classEntry) { /* - * table contains a sequence of 'nul'-terminated dir name bytes followed by an extra 'nul' - * and then a sequence of 'nul'-terminated file name bytes followed by an extra 'nul' + * Table contains a sequence of 'nul'-terminated dir name bytes followed by an extra 'nul' + * and then a sequence of 'nul'-terminated file name bytes followed by an extra 'nul'. * - * for now we assume dir and file names are ASCII byte strings + * For now we assume dir and file names are ASCII byte strings. */ int fileSize = 0; for (FileEntry localEntry : classEntry.getLocalFiles()) { /* - * we want the file base name excluding path + * We want the file base name excluding path. */ String baseName = localEntry.getFileName(); int length = baseName.length(); - /* we should never have a null or zero length entry in local files */ + /* We should never have a null or zero length entry in local files. */ assert length > 0; fileSize += length + 1; DirEntry dirEntry = localEntry.getDirEntry(); int idx = classEntry.localDirsIdx(dirEntry); fileSize += putULEB(idx, scratch, 0); /* - * the two zero timestamps require 1 byte each + * The two zero timestamps require 1 byte each. */ fileSize += 2; } /* - * allow for terminator nul + * Allow for terminator nul. */ fileSize++; return fileSize; } - public int computeLineNUmberTableSize(ClassEntry classEntry) { + private int computeLineNUmberTableSize(ClassEntry classEntry) { /* - * sigh -- we have to do this by generating the content even though we cannot write it into - * a byte[] + * Sigh -- we have to do this by generating the content even though we cannot write it into + * a byte[]. */ - return writeLineNumberTable(classEntry, null, 0); + return writeLineNumberTable(null, classEntry, null, 0); } @Override @@ -268,8 +268,8 @@ public byte[] getOrDecideContent(Map alre Object valueObj = decisionMap.getDecidedValue(LayoutDecision.Kind.VADDR); if (valueObj != null && valueObj instanceof Number) { /* - * this may not be the final vaddr for the text segment but it will be close enough - * to make debug easier i.e. to within a 4k page or two + * This may not be the final vaddr for the text segment but it will be close enough + * to make debug easier i.e. to within a 4k page or two. */ debugTextBase = ((Number) valueObj).longValue(); } @@ -278,72 +278,72 @@ public byte[] getOrDecideContent(Map alre } @Override - public void writeContent() { + public void writeContent(DebugContext context) { byte[] buffer = getContent(); int pos = 0; - checkDebug(pos); - debug(" [0x%08x] DEBUG_LINE\n", pos); + enableLog(context, pos); + log(context, " [0x%08x] DEBUG_LINE", pos); for (ClassEntry classEntry : getPrimaryClasses()) { if (classEntry.getFileName().length() != 0) { int startPos = pos; assert classEntry.getLineIndex() == startPos; - debug(" [0x%08x] Compile Unit for %s\n", pos, classEntry.getFileName()); + log(context, " [0x%08x] Compile Unit for %s", pos, classEntry.getFileName()); pos = writeHeader(classEntry, buffer, pos); - debug(" [0x%08x] headerSize = 0x%08x\n", pos, pos - startPos); + log(context, " [0x%08x] headerSize = 0x%08x", pos, pos - startPos); int dirTablePos = pos; - pos = writeDirTable(classEntry, buffer, pos); - debug(" [0x%08x] dirTableSize = 0x%08x\n", pos, pos - dirTablePos); + pos = writeDirTable(context, classEntry, buffer, pos); + log(context, " [0x%08x] dirTableSize = 0x%08x", pos, pos - dirTablePos); int fileTablePos = pos; - pos = writeFileTable(classEntry, buffer, pos); - debug(" [0x%08x] fileTableSize = 0x%08x\n", pos, pos - fileTablePos); + pos = writeFileTable(context, classEntry, buffer, pos); + log(context, " [0x%08x] fileTableSize = 0x%08x", pos, pos - fileTablePos); int lineNumberTablePos = pos; - pos = writeLineNumberTable(classEntry, buffer, pos); - debug(" [0x%08x] lineNumberTableSize = 0x%x\n", pos, pos - lineNumberTablePos); - debug(" [0x%08x] size = 0x%x\n", pos, pos - startPos); + pos = writeLineNumberTable(context, classEntry, buffer, pos); + log(context, " [0x%08x] lineNumberTableSize = 0x%x", pos, pos - lineNumberTablePos); + log(context, " [0x%08x] size = 0x%x", pos, pos - startPos); } } assert pos == buffer.length; } - public int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { + private int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { int pos = p; /* - * 4 ubyte length field + * 4 ubyte length field. */ pos = putInt(classEntry.getTotalSize() - 4, buffer, pos); /* - * 2 ubyte version is always 2 + * 2 ubyte version is always 2. */ pos = putShort(DW_VERSION_2, buffer, pos); /* - * 4 ubyte prologue length includes rest of header and dir + file table section + * 4 ubyte prologue length includes rest of header and dir + file table section. */ int prologueSize = classEntry.getLinePrologueSize() - 6; pos = putInt(prologueSize, buffer, pos); /* - * 1 ubyte min instruction length is always 1 + * 1 ubyte min instruction length is always 1. */ pos = putByte((byte) 1, buffer, pos); /* - * 1 byte default is_stmt is always 1 + * 1 byte default is_stmt is always 1. */ pos = putByte((byte) 1, buffer, pos); /* - * 1 byte line base is always -5 + * 1 byte line base is always -5. */ pos = putByte((byte) DW_LN_LINE_BASE, buffer, pos); /* - * 1 ubyte line range is always 14 giving range -5 to 8 + * 1 ubyte line range is always 14 giving range -5 to 8. */ pos = putByte((byte) DW_LN_LINE_RANGE, buffer, pos); /* - * 1 ubyte opcode base is always 13 + * 1 ubyte opcode base is always 13. */ pos = putByte((byte) DW_LN_OPCODE_BASE, buffer, pos); /* - * specify opcode arg sizes for the standard opcodes + * specify opcode arg sizes for the standard opcodes. */ /* DW_LNS_copy */ putByte((byte) 0, buffer, pos); @@ -372,40 +372,40 @@ public int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { return pos; } - public int writeDirTable(ClassEntry classEntry, byte[] buffer, int p) { + private int writeDirTable(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; - debug(" [0x%08x] Dir Name\n", pos); + verboseLog(context, " [0x%08x] Dir Name", pos); /* - * write out the list of dirs referenced form this file entry + * Write out the list of dirs referenced form this file entry. */ int dirIdx = 1; for (DirEntry dir : classEntry.getLocalDirs()) { /* * write nul terminated string text. */ - debug(" [0x%08x] %-4d %s\n", pos, dirIdx, dir.getPath()); + verboseLog(context, " [0x%08x] %-4d %s", pos, dirIdx, dir.getPath()); pos = putAsciiStringBytes(dir.getPathString(), buffer, pos); dirIdx++; } /* - * separate dirs from files with a nul + * Separate dirs from files with a nul. */ pos = putByte((byte) 0, buffer, pos); return pos; } - public int writeFileTable(ClassEntry classEntry, byte[] buffer, int p) { + private int writeFileTable(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; int fileIdx = 1; - debug(" [0x%08x] Entry Dir Name\n", pos); + verboseLog(context, " [0x%08x] Entry Dir Name", pos); for (FileEntry localEntry : classEntry.getLocalFiles()) { /* - * we need the file name minus path, the associated dir index, and 0 for time stamps + * We need the file name minus path, the associated dir index, and 0 for time stamps. */ String baseName = localEntry.getFileName(); DirEntry dirEntry = localEntry.getDirEntry(); int dirIdx = classEntry.localDirsIdx(dirEntry); - debug(" [0x%08x] %-5d %-5d %s\n", pos, fileIdx, dirIdx, baseName); + verboseLog(context, " [0x%08x] %-5d %-5d %s", pos, fileIdx, dirIdx, baseName); pos = putAsciiStringBytes(baseName, buffer, pos); pos = putULEB(dirIdx, buffer, pos); pos = putULEB(0, buffer, pos); @@ -413,38 +413,38 @@ public int writeFileTable(ClassEntry classEntry, byte[] buffer, int p) { fileIdx++; } /* - * terminate files with a nul + * Terminate files with a nul. */ pos = putByte((byte) 0, buffer, pos); return pos; } - public int debugLine = 1; - public int debugCopyCount = 0; + private int debugLine = 1; + private int debugCopyCount = 0; - public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { + private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; FileEntry fileEntry = classEntry.getFileEntry(); if (fileEntry == null) { return pos; } /* - * the primary file entry should always be first in the local files list + * The primary file entry should always be first in the local files list. */ assert classEntry.localFilesIdx(fileEntry) == 1; String primaryClassName = classEntry.getClassName(); String primaryFileName = classEntry.getFileName(); String file = primaryFileName; int fileIdx = 1; - debug(" [0x%08x] primary class %s\n", pos, primaryClassName); - debug(" [0x%08x] primary file %s\n", pos, primaryFileName); + log(context, " [0x%08x] primary class %s", pos, primaryClassName); + log(context, " [0x%08x] primary file %s", pos, primaryFileName); for (PrimaryEntry primaryEntry : classEntry.getPrimaryEntries()) { Range primaryRange = primaryEntry.getPrimary(); assert primaryRange.getFileName().equals(primaryFileName); /* - * each primary represents a method i.e. a contiguous sequence of subranges. we assume + * Each primary represents a method i.e. a contiguous sequence of subranges. we assume * the default state at the start of each sequence because we always post an - * end_sequence when we finish all the subranges in the method + * end_sequence when we finish all the subranges in the method. */ long line = primaryRange.getLine(); if (line < 0 && primaryEntry.getSubranges().size() > 0) { @@ -456,30 +456,30 @@ public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { long address = primaryRange.getLo(); /* - * set state for primary + * Set state for primary. */ - debug(" [0x%08x] primary range [0x%08x, 0x%08x] %s:%d\n", pos, debugTextBase + primaryRange.getLo(), debugTextBase + primaryRange.getHi(), primaryRange.getFullMethodName(), + log(context, " [0x%08x] primary range [0x%08x, 0x%08x] %s:%d", pos, debugTextBase + primaryRange.getLo(), debugTextBase + primaryRange.getHi(), primaryRange.getFullMethodName(), primaryRange.getLine()); /* - * initialize and write a row for the start of the primary method + * Initialize and write a row for the start of the primary method. */ - pos = putSetFile(file, fileIdx, buffer, pos); - pos = putSetBasicBlock(buffer, pos); + pos = writeSetFileOp(context, file, fileIdx, buffer, pos); + pos = writeSetBasicBlockOp(context, buffer, pos); /* - * address is currently 0 + * Address is currently 0. */ - pos = putSetAddress(address, buffer, pos); + pos = writeSetAddressOp(context, address, buffer, pos); /* - * state machine value of line is currently 1 increment to desired line + * State machine value of line is currently 1 increment to desired line. */ if (line != 1) { - pos = putAdvanceLine(line - 1, buffer, pos); + pos = writeAdvanceLineOp(context, line - 1, buffer, pos); } - pos = putCopy(buffer, pos); + pos = writeCopyOp(context, buffer, pos); /* - * now write a row for each subrange lo and hi + * Now write a row for each subrange lo and hi. */ for (Range subrange : primaryEntry.getSubranges()) { assert subrange.getLo() >= primaryRange.getLo(); @@ -493,142 +493,133 @@ public int writeLineNumberTable(ClassEntry classEntry, byte[] buffer, int p) { long subLine = subrange.getLine(); long subAddressLo = subrange.getLo(); long subAddressHi = subrange.getHi(); - debug(" [0x%08x] sub range [0x%08x, 0x%08x] %s:%d\n", pos, debugTextBase + subAddressLo, debugTextBase + subAddressHi, subrange.getFullMethodName(), subLine); + log(context, " [0x%08x] sub range [0x%08x, 0x%08x] %s:%d", pos, debugTextBase + subAddressLo, debugTextBase + subAddressHi, subrange.getFullMethodName(), subLine); if (subLine < 0) { /* - * no line info so stay at previous file:line + * No line info so stay at previous file:line. */ subLine = line; subfile = file; subFileIdx = fileIdx; - debug(" [0x%08x] missing line info - staying put at %s:%d\n", pos, file, line); + verboseLog(context, " [0x%08x] missing line info - staying put at %s:%d", pos, file, line); } /* - * there is a temptation to append end sequence at here when the hiAddress lies + * There is a temptation to append end sequence at here when the hiAddress lies * strictly between the current address and the start of the next subrange because, * ostensibly, we have void space between the end of the current subrange and the * start of the next one. however, debug works better if we treat all the insns up - * to the next range start as belonging to the current line + * to the next range start as belonging to the current line. * - * if we have to update to a new file then do so + * If we have to update to a new file then do so. */ if (subFileIdx != fileIdx) { /* - * update the current file + * Update the current file. */ - pos = putSetFile(subfile, subFileIdx, buffer, pos); + pos = writeSetFileOp(context, subfile, subFileIdx, buffer, pos); file = subfile; fileIdx = subFileIdx; } /* - * check if we can advance line and/or address in one byte with a special opcode + * Check if we can advance line and/or address in one byte with a special opcode. */ long lineDelta = subLine - line; long addressDelta = subAddressLo - address; byte opcode = isSpecialOpcode(addressDelta, lineDelta); if (opcode != DW_LNS_undefined) { /* - * ignore pointless write when addressDelta == lineDelta == 0 + * Ignore pointless write when addressDelta == lineDelta == 0. */ if (addressDelta != 0 || lineDelta != 0) { - pos = putSpecialOpcode(opcode, buffer, pos); + pos = writeSpecialOpcode(context, opcode, buffer, pos); } } else { /* - * does it help to divide and conquer using a fixed address increment + * Does it help to divide and conquer using a fixed address increment. */ int remainder = isConstAddPC(addressDelta); if (remainder > 0) { - pos = putConstAddPC(buffer, pos); + pos = writeConstAddPCOp(context, buffer, pos); /* - * the remaining address can be handled with a special opcode but what about - * the line delta + * The remaining address can be handled with a special opcode but what about + * the line delta. */ opcode = isSpecialOpcode(remainder, lineDelta); if (opcode != DW_LNS_undefined) { /* - * address remainder and line now fit + * Address remainder and line now fit. */ - pos = putSpecialOpcode(opcode, buffer, pos); + pos = writeSpecialOpcode(context, opcode, buffer, pos); } else { /* - * ok, bump the line separately then use a special opcode for the - * address remainder + * Ok, bump the line separately then use a special opcode for the + * address remainder. */ opcode = isSpecialOpcode(remainder, 0); assert opcode != DW_LNS_undefined; - pos = putAdvanceLine(lineDelta, buffer, pos); - pos = putSpecialOpcode(opcode, buffer, pos); + pos = writeAdvanceLineOp(context, lineDelta, buffer, pos); + pos = writeSpecialOpcode(context, opcode, buffer, pos); } } else { /* - * increment line and pc separately + * Increment line and pc separately. */ if (lineDelta != 0) { - pos = putAdvanceLine(lineDelta, buffer, pos); + pos = writeAdvanceLineOp(context, lineDelta, buffer, pos); } /* * n.b. we might just have had an out of range line increment with a zero - * address increment + * address increment. */ if (addressDelta > 0) { /* - * see if we can use a ushort for the increment + * See if we can use a ushort for the increment. */ if (isFixedAdvancePC(addressDelta)) { - pos = putFixedAdvancePC((short) addressDelta, buffer, pos); + pos = writeFixedAdvancePCOp(context, (short) addressDelta, buffer, pos); } else { - pos = putAdvancePC(addressDelta, buffer, pos); + pos = writeAdvancePCOp(context, addressDelta, buffer, pos); } } - pos = putCopy(buffer, pos); + pos = writeCopyOp(context, buffer, pos); } } /* - * move line and address range on + * Move line and address range on. */ line += lineDelta; address += addressDelta; } /* - * append a final end sequence just below the next primary range + * Append a final end sequence just below the next primary range. */ if (address < primaryRange.getHi()) { long addressDelta = primaryRange.getHi() - address; /* - * increment address before we write the end sequence + * Increment address before we write the end sequence. */ - pos = putAdvancePC(addressDelta, buffer, pos); + pos = writeAdvancePCOp(context, addressDelta, buffer, pos); } - pos = putEndSequence(buffer, pos); + pos = writeEndSequenceOp(context, buffer, pos); } - debug(" [0x%08x] primary file processed %s\n", pos, primaryFileName); + log(context, " [0x%08x] primary file processed %s", pos, primaryFileName); return pos; } - @Override - protected void debug(String format, Object... args) { - if (((int) args[0] - debugBase) < 0x100000) { - super.debug(format, args); - } else if (format.startsWith(" [0x%08x] primary file")) { - super.debug(format, args); - } - } - - public int putCopy(byte[] buffer, int p) { + private int writeCopyOp(DebugContext context, byte[] buffer, int p) { byte opcode = DW_LNS_copy; int pos = p; if (buffer == null) { return pos + putByte(opcode, scratch, 0); } else { debugCopyCount++; - debug(" [0x%08x] Copy %d\n", pos, debugCopyCount); + verboseLog(context, " [0x%08x] Copy %d", pos, debugCopyCount); return putByte(opcode, buffer, pos); } } - public int putAdvancePC(long uleb, byte[] buffer, int p) { + private int writeAdvancePCOp(DebugContext context, long uleb, byte[] buffer, int p) { byte opcode = DW_LNS_advance_pc; int pos = p; if (buffer == null) { @@ -636,13 +627,13 @@ public int putAdvancePC(long uleb, byte[] buffer, int p) { return pos + putULEB(uleb, scratch, 0); } else { debugAddress += uleb; - debug(" [0x%08x] Advance PC by %d to 0x%08x\n", pos, uleb, debugAddress); + verboseLog(context, " [0x%08x] Advance PC by %d to 0x%08x", pos, uleb, debugAddress); pos = putByte(opcode, buffer, pos); return putULEB(uleb, buffer, pos); } } - public int putAdvanceLine(long sleb, byte[] buffer, int p) { + private int writeAdvanceLineOp(DebugContext context, long sleb, byte[] buffer, int p) { byte opcode = DW_LNS_advance_line; int pos = p; if (buffer == null) { @@ -650,27 +641,27 @@ public int putAdvanceLine(long sleb, byte[] buffer, int p) { return pos + putSLEB(sleb, scratch, 0); } else { debugLine += sleb; - debug(" [0x%08x] Advance Line by %d to %d\n", pos, sleb, debugLine); + verboseLog(context, " [0x%08x] Advance Line by %d to %d", pos, sleb, debugLine); pos = putByte(opcode, buffer, pos); return putSLEB(sleb, buffer, pos); } } - public int putSetFile(String file, long uleb, byte[] buffer, int p) { + private int writeSetFileOp(DebugContext context, String file, long uleb, byte[] buffer, int p) { byte opcode = DW_LNS_set_file; int pos = p; if (buffer == null) { pos = pos + putByte(opcode, scratch, 0); return pos + putULEB(uleb, scratch, 0); } else { - debug(" [0x%08x] Set File Name to entry %d in the File Name Table (%s)\n", pos, uleb, file); + verboseLog(context, " [0x%08x] Set File Name to entry %d in the File Name Table (%s)", pos, uleb, file); pos = putByte(opcode, buffer, pos); return putULEB(uleb, buffer, pos); } } @SuppressWarnings("unused") - public int putSetColumn(long uleb, byte[] buffer, int p) { + private int writeSetColumnOp(DebugContext context, long uleb, byte[] buffer, int p) { byte opcode = DW_LNS_set_column; int pos = p; if (buffer == null) { @@ -683,7 +674,7 @@ public int putSetColumn(long uleb, byte[] buffer, int p) { } @SuppressWarnings("unused") - public int putNegateStmt(byte[] buffer, int p) { + private int writeNegateStmtOp(DebugContext context, byte[] buffer, int p) { byte opcode = DW_LNS_negate_stmt; int pos = p; if (buffer == null) { @@ -693,18 +684,18 @@ public int putNegateStmt(byte[] buffer, int p) { } } - public int putSetBasicBlock(byte[] buffer, int p) { + private int writeSetBasicBlockOp(DebugContext context, byte[] buffer, int p) { byte opcode = DW_LNS_set_basic_block; int pos = p; if (buffer == null) { return pos + putByte(opcode, scratch, 0); } else { - debug(" [0x%08x] Set basic block\n", pos); + verboseLog(context, " [0x%08x] Set basic block", pos); return putByte(opcode, buffer, pos); } } - public int putConstAddPC(byte[] buffer, int p) { + private int writeConstAddPCOp(DebugContext context, byte[] buffer, int p) { byte opcode = DW_LNS_const_add_pc; int pos = p; if (buffer == null) { @@ -712,12 +703,12 @@ public int putConstAddPC(byte[] buffer, int p) { } else { int advance = opcodeAddress((byte) 255); debugAddress += advance; - debug(" [0x%08x] Advance PC by constant %d to 0x%08x\n", pos, advance, debugAddress); + verboseLog(context, " [0x%08x] Advance PC by constant %d to 0x%08x", pos, advance, debugAddress); return putByte(opcode, buffer, pos); } } - public int putFixedAdvancePC(short arg, byte[] buffer, int p) { + private int writeFixedAdvancePCOp(DebugContext context, short arg, byte[] buffer, int p) { byte opcode = DW_LNS_fixed_advance_pc; int pos = p; if (buffer == null) { @@ -725,53 +716,53 @@ public int putFixedAdvancePC(short arg, byte[] buffer, int p) { return pos + putShort(arg, scratch, 0); } else { debugAddress += arg; - debug(" [0x%08x] Fixed advance Address by %d to 0x%08x\n", pos, arg, debugAddress); + verboseLog(context, " [0x%08x] Fixed advance Address by %d to 0x%08x", pos, arg, debugAddress); pos = putByte(opcode, buffer, pos); return putShort(arg, buffer, pos); } } - public int putEndSequence(byte[] buffer, int p) { + private int writeEndSequenceOp(DebugContext context, byte[] buffer, int p) { byte opcode = DW_LNE_end_sequence; int pos = p; if (buffer == null) { pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); /* - * insert extended insn byte count as ULEB + * Insert extended insn byte count as ULEB. */ pos = pos + putULEB(1, scratch, 0); return pos + putByte(opcode, scratch, 0); } else { - debug(" [0x%08x] Extended opcode 1: End sequence\n", pos); + verboseLog(context, " [0x%08x] Extended opcode 1: End sequence", pos); debugAddress = debugTextBase; debugLine = 1; debugCopyCount = 0; pos = putByte(DW_LNS_extended_prefix, buffer, pos); /* - * insert extended insn byte count as ULEB + * Insert extended insn byte count as ULEB. */ pos = putULEB(1, buffer, pos); return putByte(opcode, buffer, pos); } } - public int putSetAddress(long arg, byte[] buffer, int p) { + private int writeSetAddressOp(DebugContext context, long arg, byte[] buffer, int p) { byte opcode = DW_LNE_set_address; int pos = p; if (buffer == null) { pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); /* - * insert extended insn byte count as ULEB + * Insert extended insn byte count as ULEB. */ pos = pos + putULEB(9, scratch, 0); pos = pos + putByte(opcode, scratch, 0); return pos + putLong(arg, scratch, 0); } else { debugAddress = debugTextBase + (int) arg; - debug(" [0x%08x] Extended opcode 2: Set Address to 0x%08x\n", pos, debugAddress); + verboseLog(context, " [0x%08x] Extended opcode 2: Set Address to 0x%08x", pos, debugAddress); pos = putByte(DW_LNS_extended_prefix, buffer, pos); /* - * insert extended insn byte count as ULEB + * Insert extended insn byte count as ULEB. */ pos = putULEB(9, buffer, pos); pos = putByte(opcode, buffer, pos); @@ -779,11 +770,12 @@ public int putSetAddress(long arg, byte[] buffer, int p) { } } - public int putDefineFile(String file, long uleb1, long uleb2, long uleb3, byte[] buffer, int p) { + @SuppressWarnings("unused") + private int writeDefineFileOp(DebugContext context, String file, long uleb1, long uleb2, long uleb3, byte[] buffer, int p) { byte opcode = DW_LNE_define_file; int pos = p; /* - * calculate bytes needed for opcode + args + * Calculate bytes needed for opcode + args. */ int fileBytes = file.length() + 1; long insnBytes = 1; @@ -794,19 +786,19 @@ public int putDefineFile(String file, long uleb1, long uleb2, long uleb3, byte[] if (buffer == null) { pos = pos + putByte(DW_LNS_extended_prefix, scratch, 0); /* - * write insnBytes as a ULEB + * Write insnBytes as a ULEB. */ pos += putULEB(insnBytes, scratch, 0); return pos + (int) insnBytes; } else { - debug(" [0x%08x] Extended opcode 3: Define File %s idx %d ts1 %d ts2 %d\n", pos, file, uleb1, uleb2, uleb3); + verboseLog(context, " [0x%08x] Extended opcode 3: Define File %s idx %d ts1 %d ts2 %d", pos, file, uleb1, uleb2, uleb3); pos = putByte(DW_LNS_extended_prefix, buffer, pos); /* - * insert insn length as uleb + * Insert insn length as uleb. */ pos = putULEB(insnBytes, buffer, pos); /* - * insert opcode and args + * Insert opcode and args. */ pos = putByte(opcode, buffer, pos); pos = putAsciiStringBytes(file, buffer, pos); @@ -816,32 +808,32 @@ public int putDefineFile(String file, long uleb1, long uleb2, long uleb3, byte[] } } - public static int opcodeId(byte opcode) { + private static int opcodeId(byte opcode) { int iopcode = opcode & 0xff; return iopcode - DW_LN_OPCODE_BASE; } - public static int opcodeAddress(byte opcode) { + private static int opcodeAddress(byte opcode) { int iopcode = opcode & 0xff; return (iopcode - DW_LN_OPCODE_BASE) / DW_LN_LINE_RANGE; } - public static int opcodeLine(byte opcode) { + private static int opcodeLine(byte opcode) { int iopcode = opcode & 0xff; return ((iopcode - DW_LN_OPCODE_BASE) % DW_LN_LINE_RANGE) + DW_LN_LINE_BASE; } - public int putSpecialOpcode(byte opcode, byte[] buffer, int p) { + private int writeSpecialOpcode(DebugContext context, byte opcode, byte[] buffer, int p) { int pos = p; if (buffer == null) { return pos + putByte(opcode, scratch, 0); } else { if (debug && opcode == 0) { - debug(" [0x%08x] ERROR Special Opcode %d: Address 0x%08x Line %d\n", debugAddress, debugLine); + verboseLog(context, " [0x%08x] ERROR Special Opcode %d: Address 0x%08x Line %d", debugAddress, debugLine); } debugAddress += opcodeAddress(opcode); debugLine += opcodeLine(opcode); - debug(" [0x%08x] Special Opcode %d: advance Address by %d to 0x%08x and Line by %d to %d\n", + verboseLog(context, " [0x%08x] Special Opcode %d: advance Address by %d to 0x%08x and Line by %d to %d", pos, opcodeId(opcode), opcodeAddress(opcode), debugAddress, opcodeLine(opcode), debugLine); return putByte(opcode, buffer, pos); } @@ -850,7 +842,7 @@ public int putSpecialOpcode(byte opcode, byte[] buffer, int p) { private static final int MAX_ADDRESS_ONLY_DELTA = (0xff - DW_LN_OPCODE_BASE) / DW_LN_LINE_RANGE; private static final int MAX_ADDPC_DELTA = MAX_ADDRESS_ONLY_DELTA + (MAX_ADDRESS_ONLY_DELTA - 1); - public static byte isSpecialOpcode(long addressDelta, long lineDelta) { + private static byte isSpecialOpcode(long addressDelta, long lineDelta) { if (addressDelta < 0) { return DW_LNS_undefined; } @@ -858,7 +850,7 @@ public static byte isSpecialOpcode(long addressDelta, long lineDelta) { long offsetLineDelta = lineDelta - DW_LN_LINE_BASE; if (offsetLineDelta < DW_LN_LINE_RANGE) { /* - * line_delta can be encoded check if address is ok + * The line delta can be encoded. Check if address is ok. */ if (addressDelta <= MAX_ADDRESS_ONLY_DELTA) { long opcode = DW_LN_OPCODE_BASE + (addressDelta * DW_LN_LINE_RANGE) + offsetLineDelta; @@ -870,12 +862,12 @@ public static byte isSpecialOpcode(long addressDelta, long lineDelta) { } /* - * answer no by returning an invalid opcode + * Answer no by returning an invalid opcode. */ return DW_LNS_undefined; } - public static int isConstAddPC(long addressDelta) { + private static int isConstAddPC(long addressDelta) { if (addressDelta < MAX_ADDRESS_ONLY_DELTA) { return 0; } @@ -886,21 +878,21 @@ public static int isConstAddPC(long addressDelta) { } } - public static boolean isFixedAdvancePC(long addressDiff) { + private static boolean isFixedAdvancePC(long addressDiff) { return addressDiff >= 0 && addressDiff < 0xffff; } /** - * debug_line section content depends on debug_str section content and offset. + * The debug_line section content depends on debug_str section content and offset. */ - public static final String TARGET_SECTION_NAME = DW_STR_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DW_STR_SECTION_NAME; @Override public String targetSectionName() { return TARGET_SECTION_NAME; } - public final LayoutDecision.Kind[] targetSectionKinds = { + private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, LayoutDecision.Kind.OFFSET, }; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java index 548ac07ab61a..f13d2d9fd67f 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java @@ -33,6 +33,7 @@ import com.oracle.objectfile.ObjectFile; import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.elf.ELFObjectFile; +import org.graalvm.compiler.debug.DebugContext; import java.nio.ByteOrder; import java.util.Map; @@ -41,23 +42,23 @@ import static com.oracle.objectfile.elf.dwarf.DwarfSections.TEXT_SECTION_NAME; /** - * class from which all DWARF debug sections inherit providing common behaviours. + * A class from which all DWARF debug sections inherit providing common behaviours. */ public abstract class DwarfSectionImpl extends BasicProgbitsSectionImpl { protected DwarfSections dwarfSections; - public boolean debug = false; - public long debugTextBase = 0; - public long debugAddress = 0; - public int debugBase = 0; + protected boolean debug = false; + protected long debugTextBase = 0; + protected long debugAddress = 0; + protected int debugBase = 0; public DwarfSectionImpl(DwarfSections dwarfSections) { this.dwarfSections = dwarfSections; } /** - * creates the target byte[] array used to define the section contents. + * Creates the target byte[] array used to define the section contents. * - * the main task of this method is to precompute the size of the debug section. given the + * The main task of this method is to precompute the size of the debug section. given the * complexity of the data layouts that invariably requires performing a dummy write of the * contents, inserting bytes into a small, scratch buffer only when absolutely necessary. * subclasses may also cache some information for use when writing the contents. @@ -65,37 +66,53 @@ public DwarfSectionImpl(DwarfSections dwarfSections) { public abstract void createContent(); /** - * populates the byte[] array used to contain the section contents. + * Populates the byte[] array used to contain the section contents. * - * in most cases this task reruns the operations performed under createContent but this time + * In most cases this task reruns the operations performed under createContent but this time * actually writing data to the target byte[]. */ - public abstract void writeContent(); + public abstract void writeContent(DebugContext debugContext); @Override public boolean isLoadable() { /* - * even though we're a progbits section impl we're not actually loadable + * Even though we're a progbits section impl we're not actually loadable. */ return false; } - public void checkDebug(int pos) { + private String debugSectionLogName() { /* - * if the env var relevant to this element type is set then switch on debugging + * Use prefix dwarf plus the section name (which already includes a dot separator) for the + * context key. For example messages for info section will be keyed using dwarf.debug_info. + * Other info formats use their own format-specific prefix. */ - String name = getSectionName(); - String envVarName = "DWARF_" + name.substring(1).toUpperCase(); - if (System.getenv(envVarName) != null) { + assert getSectionName().startsWith(".debug"); + return "dwarf" + getSectionName(); + } + + protected void enableLog(DebugContext context, int pos) { + /* + * Debug output is disabled during the first pass where we size the buffer. this is called + * to enable it during the second pass where the buffer gets written, but only if the scope + * is enabled. + */ + if (context.areScopesEnabled()) { debug = true; debugBase = pos; debugAddress = debugTextBase; } } - protected void debug(String format, Object... args) { + protected void log(DebugContext context, String format, Object... args) { + if (debug) { + context.logv(DebugContext.INFO_LEVEL, format, args); + } + } + + protected void verboseLog(DebugContext context, String format, Object... args) { if (debug) { - System.out.format(format, args); + context.logv(DebugContext.VERBOSE_LEVEL, format, args); } } @@ -104,16 +121,16 @@ protected boolean littleEndian() { } /* - * base level put methods that assume a non-null buffer + * Base level put methods that assume a non-null buffer. */ - public int putByte(byte b, byte[] buffer, int p) { + protected int putByte(byte b, byte[] buffer, int p) { int pos = p; buffer[pos++] = b; return pos; } - public int putShort(short s, byte[] buffer, int p) { + protected int putShort(short s, byte[] buffer, int p) { int pos = p; if (littleEndian()) { buffer[pos++] = (byte) (s & 0xff); @@ -125,7 +142,7 @@ public int putShort(short s, byte[] buffer, int p) { return pos; } - public int putInt(int i, byte[] buffer, int p) { + protected int putInt(int i, byte[] buffer, int p) { int pos = p; if (littleEndian()) { buffer[pos++] = (byte) (i & 0xff); @@ -141,7 +158,7 @@ public int putInt(int i, byte[] buffer, int p) { return pos; } - public int putLong(long l, byte[] buffer, int p) { + protected int putLong(long l, byte[] buffer, int p) { int pos = p; if (littleEndian()) { buffer[pos++] = (byte) (l & 0xff); @@ -165,17 +182,17 @@ public int putLong(long l, byte[] buffer, int p) { return pos; } - public int putRelocatableCodeOffset(long l, byte[] buffer, int p) { + protected int putRelocatableCodeOffset(long l, byte[] buffer, int p) { int pos = p; /* - * mark address so it is relocated relative to the start of the text segment + * Mark address so it is relocated relative to the start of the text segment. */ markRelocationSite(pos, 8, ObjectFile.RelocationKind.DIRECT, TEXT_SECTION_NAME, false, Long.valueOf(l)); pos = putLong(0, buffer, pos); return pos; } - public int putULEB(long val, byte[] buffer, int p) { + protected int putULEB(long val, byte[] buffer, int p) { long l = val; int pos = p; for (int i = 0; i < 9; i++) { @@ -193,7 +210,7 @@ public int putULEB(long val, byte[] buffer, int p) { return pos; } - public int putSLEB(long val, byte[] buffer, int p) { + protected int putSLEB(long val, byte[] buffer, int p) { long l = val; int pos = p; for (int i = 0; i < 9; i++) { @@ -212,11 +229,11 @@ public int putSLEB(long val, byte[] buffer, int p) { return pos; } - public int putAsciiStringBytes(String s, byte[] buffer, int pos) { + protected int putAsciiStringBytes(String s, byte[] buffer, int pos) { return putAsciiStringBytes(s, 0, buffer, pos); } - public int putAsciiStringBytes(String s, int startChar, byte[] buffer, int p) { + protected int putAsciiStringBytes(String s, int startChar, byte[] buffer, int p) { int pos = p; for (int l = startChar; l < s.length(); l++) { char c = s.charAt(l); @@ -230,17 +247,17 @@ public int putAsciiStringBytes(String s, int startChar, byte[] buffer, int p) { } /* - * common write methods that check for a null buffer + * Common write methods that check for a null buffer. */ - public void patchLength(int lengthPos, byte[] buffer, int pos) { + protected void patchLength(int lengthPos, byte[] buffer, int pos) { if (buffer != null) { int length = pos - (lengthPos + 4); putInt(length, buffer, lengthPos); } } - public int writeAbbrevCode(long code, byte[] buffer, int pos) { + protected int writeAbbrevCode(long code, byte[] buffer, int pos) { if (buffer == null) { return pos + putSLEB(code, scratch, 0); } else { @@ -248,7 +265,7 @@ public int writeAbbrevCode(long code, byte[] buffer, int pos) { } } - public int writeTag(long code, byte[] buffer, int pos) { + protected int writeTag(long code, byte[] buffer, int pos) { if (buffer == null) { return pos + putSLEB(code, scratch, 0); } else { @@ -256,7 +273,7 @@ public int writeTag(long code, byte[] buffer, int pos) { } } - public int writeFlag(byte flag, byte[] buffer, int pos) { + protected int writeFlag(byte flag, byte[] buffer, int pos) { if (buffer == null) { return pos + putByte(flag, scratch, 0); } else { @@ -264,7 +281,7 @@ public int writeFlag(byte flag, byte[] buffer, int pos) { } } - public int writeAttrAddress(long address, byte[] buffer, int pos) { + protected int writeAttrAddress(long address, byte[] buffer, int pos) { if (buffer == null) { return pos + 8; } else { @@ -273,7 +290,7 @@ public int writeAttrAddress(long address, byte[] buffer, int pos) { } @SuppressWarnings("unused") - public int writeAttrData8(long value, byte[] buffer, int pos) { + protected int writeAttrData8(long value, byte[] buffer, int pos) { if (buffer == null) { return pos + putLong(value, scratch, 0); } else { @@ -281,7 +298,7 @@ public int writeAttrData8(long value, byte[] buffer, int pos) { } } - public int writeAttrData4(int value, byte[] buffer, int pos) { + protected int writeAttrData4(int value, byte[] buffer, int pos) { if (buffer == null) { return pos + putInt(value, scratch, 0); } else { @@ -289,7 +306,7 @@ public int writeAttrData4(int value, byte[] buffer, int pos) { } } - public int writeAttrData1(byte value, byte[] buffer, int pos) { + protected int writeAttrData1(byte value, byte[] buffer, int pos) { if (buffer == null) { return pos + putByte(value, scratch, 0); } else { @@ -297,7 +314,7 @@ public int writeAttrData1(byte value, byte[] buffer, int pos) { } } - public int writeAttrNull(byte[] buffer, int pos) { + protected int writeAttrNull(byte[] buffer, int pos) { if (buffer == null) { return pos + putSLEB(0, scratch, 0); } else { @@ -306,39 +323,42 @@ public int writeAttrNull(byte[] buffer, int pos) { } /** - * identify the section after which this debug section needs to be ordered when sizing and + * Identify the section after which this debug section needs to be ordered when sizing and * creating content. * - * @return the name of the preceding section + * @return the name of the preceding section. */ public abstract String targetSectionName(); /** - * identify the layout properties of the target section which need to have been decided before + * Identify the layout properties of the target section which need to have been decided before * the contents of this section can be created. * - * @return an array of the relevant decision kinds + * @return an array of the relevant decision kinds. */ public abstract LayoutDecision.Kind[] targetSectionKinds(); /** - * identify this debug section by name. + * Identify this debug section by name. * - * @return the name of the debug section + * @return the name of the debug section. */ public abstract String getSectionName(); @Override public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { /* - * ensure content byte[] has been created before calling super method + * Ensure content byte[] has been created before calling super method. */ createContent(); /* - * ensure content byte[] has been written before calling super method + * Ensure content byte[] has been written before calling super method. + * + * we do this in a nested debug scope derived from the one set up under the object file + * write */ - writeContent(); + getOwner().debugContext(debugSectionLogName(), this::writeContent); return super.getOrDecideContent(alreadyDecided, contentHint); } @@ -352,14 +372,14 @@ public Set getDependencies(Map getDependencies(Map dirsIndex = new HashMap<>(); @@ -253,7 +234,7 @@ public DwarfLineSectionImpl getLineSectionImpl() { * * 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 + * These can be used to ensure that all debug records are generated in increasing address order * * An alternative traversal option is * @@ -263,7 +244,7 @@ public DwarfLineSectionImpl getLineSectionImpl() { * * 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 + * 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. @@ -277,7 +258,7 @@ public DwarfLineSectionImpl getLineSectionImpl() { */ /** - * list of class entries detailing class info for primary ranges. + * List of class entries detailing class info for primary ranges. */ private LinkedList primaryClasses = new LinkedList<>(); /** @@ -286,58 +267,41 @@ public DwarfLineSectionImpl getLineSectionImpl() { private Map primaryClassesIndex = new HashMap<>(); /** - * index of files which contain primary or secondary ranges. + * Index of files which contain primary or secondary ranges. */ private Map filesIndex = new HashMap<>(); /** - * indirects this call to the string table. - * - * @param string the string to be inserted - * @return a unique equivalent String - */ - public String uniqueString(String string) { - return stringTable.uniqueString(string); - } - - /** - * indirects this call to the string table, ensuring the table entry is marked for inclusion in - * the debug_str section. - * - * @param string the string to be inserted and marked for inclusion in the debug_str section - * @return a unique equivalent String - */ - public String uniqueDebugString(String string) { - return stringTable.uniqueDebugString(string); - } - - /** - * 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 + * 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); } /** - * entry point allowing ELFObjectFile to pass on information about types, code and heap data. - * - * @param 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 + * Ensure we have a null string in the string section. */ - uniqueDebugString(""); + stringTable.uniqueDebugString(""); - debugInfoProvider.codeInfoProvider().forEach(debugCodeInfo -> { + debugInfoProvider.codeInfoProvider().forEach(debugCodeInfo -> debugCodeInfo.debugContext((debugContext) -> { /* * primary file name and full method name need to be written to the debug_str section */ @@ -351,46 +315,48 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { int lo = debugCodeInfo.addressLo(); int hi = debugCodeInfo.addressHi(); int primaryLine = debugCodeInfo.line(); + Range primaryRange = new Range(fileName, filePath, className, methodName, paramNames, returnTypeName, stringTable, lo, hi, primaryLine); - /* - * System.out.format("arange: [0x%08x,0x%08x) %s %s::%s(%s) %s\n", lo, hi, - * returnTypeName, className, methodName, paramNames, fileName); create an infoSection - * entry for the method - */ + 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 '.' + // Switch '$' in class names for '.' String classNameAtLine = debugLineInfo.className().replaceAll("\\$", "."); String methodNameAtLine = debugLineInfo.methodName(); int loAtLine = lo + debugLineInfo.addressLo(); int hiAtLine = lo + debugLineInfo.addressHi(); int line = debugLineInfo.line(); /* - * record all subranges even if they have no line or file so we at least get a - * symbol for them + * 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, classNameAtLine, methodNameAtLine, "", "", 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(); } */ } - public ClassEntry ensureClassEntry(Range range) { + private ClassEntry ensureClassEntry(Range range) { String className = range.getClassName(); /* - * see if we already have an entry + * 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 + * Create and index the entry associating it with the right file. */ FileEntry fileEntry = ensureFileEntry(range); classEntry = new ClassEntry(className, fileEntry); @@ -401,7 +367,7 @@ public ClassEntry ensureClassEntry(Range range) { return classEntry; } - public FileEntry ensureFileEntry(Range range) { + private FileEntry ensureFileEntry(Range range) { String fileName = range.getFileName(); if (fileName == null) { return null; @@ -409,18 +375,18 @@ public FileEntry ensureFileEntry(Range range) { Path filePath = range.getFilePath(); Path fileAsPath = range.getFileAsPath(); /* - * ensure we have an entry + * Ensure we have an entry. */ FileEntry fileEntry = filesIndex.get(fileAsPath); if (fileEntry == null) { DirEntry dirEntry = ensureDirEntry(filePath); fileEntry = new FileEntry(fileName, dirEntry); /* - * index the file entry by file path + * Index the file entry by file path. */ filesIndex.put(fileAsPath, fileEntry); if (!range.isPrimary()) { - /* check we have a file for the corresponding primary range */ + /* Check we have a file for the corresponding primary range. */ Range primaryRange = range.getPrimary(); FileEntry primaryFileEntry = filesIndex.get(primaryRange.getFileAsPath()); assert primaryFileEntry != null; @@ -429,20 +395,20 @@ public FileEntry ensureFileEntry(Range range) { return fileEntry; } - public void addRange(Range primaryRange, List frameSizeInfos, int frameSize) { + private void addRange(Range primaryRange, List frameSizeInfos, int frameSize) { assert primaryRange.isPrimary(); ClassEntry classEntry = ensureClassEntry(primaryRange); classEntry.addPrimary(primaryRange, frameSizeInfos, frameSize); } - public void addSubRange(Range primaryRange, Range subrange) { + 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 + * The primary range should already have been seen and associated with a primary class entry. */ assert classEntry.primaryIndexFor(primaryRange) != null; if (subrangeFileEntry != null) { @@ -450,7 +416,7 @@ public void addSubRange(Range primaryRange, Range subrange) { } } - public DirEntry ensureDirEntry(Path filePath) { + private DirEntry ensureDirEntry(Path filePath) { if (filePath == null) { return null; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java index 845ab12ad8b2..a371c3a772a8 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java @@ -28,12 +28,13 @@ import com.oracle.objectfile.LayoutDecision; import com.oracle.objectfile.debugentry.StringEntry; +import org.graalvm.compiler.debug.DebugContext; import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_STR_SECTION_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfSections.TEXT_SECTION_NAME; /** - * generator for debug_str section. + * Generator for debug_str section. */ public class DwarfStrSectionImpl extends DwarfSectionImpl { public DwarfStrSectionImpl(DwarfSections dwarfSections) { @@ -60,12 +61,12 @@ public void createContent() { } @Override - public void writeContent() { + public void writeContent(DebugContext context) { byte[] buffer = getContent(); int size = buffer.length; int pos = 0; - checkDebug(pos); + enableLog(context, pos); for (StringEntry stringEntry : dwarfSections.getStringTable()) { if (stringEntry.isAddToStrSection()) { @@ -77,15 +78,10 @@ public void writeContent() { assert pos == size; } - @Override - protected void debug(String format, Object... args) { - super.debug(format, args); - } - /** - * debug_str section content depends on text section content and offset. + * The debug_str section content depends on text section content and offset. */ - public static final String TARGET_SECTION_NAME = TEXT_SECTION_NAME; + private static final String TARGET_SECTION_NAME = TEXT_SECTION_NAME; @Override public String targetSectionName() { @@ -93,12 +89,12 @@ public String targetSectionName() { } /** - * debug_str section content depends on text section content and offset. + * The debug_str section content depends on text section content and offset. */ - public final LayoutDecision.Kind[] targetSectionKinds = { + private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, LayoutDecision.Kind.OFFSET, - /* add this so we can use the text section base address for debug */ + /* Add this so we can use the text section base address for debug. */ LayoutDecision.Kind.VADDR, }; diff --git a/substratevm/src/com.oracle.svm.hosted/.checkstyle_checks.xml b/substratevm/src/com.oracle.svm.hosted/.checkstyle_checks.xml index 2ad8df2430cd..788e1fa0b557 100644 --- a/substratevm/src/com.oracle.svm.hosted/.checkstyle_checks.xml +++ b/substratevm/src/com.oracle.svm.hosted/.checkstyle_checks.xml @@ -208,8 +208,8 @@ - - + + diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java index 56409f77dda4..50078e9ea84e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java @@ -24,8 +24,6 @@ */ package com.oracle.svm.hosted.image; -import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.EXTEND; -import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.CONTRACT; import static com.oracle.svm.core.SubstrateUtil.mangleName; import static com.oracle.svm.core.util.VMError.shouldNotReachHere; @@ -46,28 +44,18 @@ import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; -import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; -import com.oracle.svm.core.option.HostedOptionValues; -import com.oracle.svm.hosted.image.sources.SourceManager; -import com.oracle.svm.hosted.meta.HostedType; -import jdk.vm.ci.code.site.Mark; -import jdk.vm.ci.meta.LineNumberTable; import org.graalvm.collections.Pair; import org.graalvm.compiler.code.CompilationResult; -import org.graalvm.compiler.code.SourceMapping; import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.Indent; -import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.compiler.serviceprovider.BufferUtil; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.CFunctionPointer; @@ -79,15 +67,12 @@ import com.oracle.objectfile.LayoutDecision; import com.oracle.objectfile.LayoutDecisionMap; import com.oracle.objectfile.ObjectFile; -import com.oracle.objectfile.debuginfo.DebugInfoProvider; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugCodeInfo; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugLineInfo; import com.oracle.objectfile.ObjectFile.Element; import com.oracle.objectfile.ObjectFile.ProgbitsSectionImpl; import com.oracle.objectfile.ObjectFile.RelocationKind; import com.oracle.objectfile.ObjectFile.Section; import com.oracle.objectfile.SectionName; +import com.oracle.objectfile.debuginfo.DebugInfoProvider; import com.oracle.objectfile.macho.MachOObjectFile; import com.oracle.svm.core.FrameAccess; import com.oracle.svm.core.Isolates; @@ -108,6 +93,7 @@ import com.oracle.svm.core.image.ImageHeapLayouter; import com.oracle.svm.core.image.ImageHeapPartition; import com.oracle.svm.core.meta.SubstrateObjectConstant; +import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.util.UserError; import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.hosted.c.CGlobalDataFeature; @@ -120,6 +106,7 @@ import com.oracle.svm.hosted.code.CEntryPointData; import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; import com.oracle.svm.hosted.image.RelocatableBuffer.Info; +import com.oracle.svm.hosted.image.sources.SourceManager; import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedUniverse; @@ -177,14 +164,16 @@ public Section getTextSection() { @Override public abstract String[] makeLaunchCommand(NativeImageKind k, String imageName, Path binPath, Path workPath, java.lang.reflect.Method method); - protected final void write(Path outputFile) { + protected final void write(DebugContext context, Path outputFile) { try { Path outFileParent = outputFile.normalize().getParent(); if (outFileParent != null) { Files.createDirectories(outFileParent); } try (FileChannel channel = FileChannel.open(outputFile, StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { - objectFile.write(channel); + objectFile.withDebugContext(context, "ObjectFile.write", () -> { + objectFile.write(channel); + }); } } catch (Exception ex) { throw shouldNotReachHere(ex); @@ -472,11 +461,12 @@ public void build(DebugContext debug, ImageHeapLayouter layouter) { cGlobals.writeData(rwDataBuffer, (offset, symbolName) -> defineDataSymbol(symbolName, rwDataSection, offset + RWDATA_CGLOBALS_PARTITION_OFFSET)); defineDataSymbol(CGlobalDataInfo.CGLOBALDATA_BASE_SYMBOL_NAME, rwDataSection, RWDATA_CGLOBALS_PARTITION_OFFSET); - // if we have constructed any debug info then - // give the object file a chance to install it + /* + * If we constructed debug info give the object file a chance to install it + */ if (SubstrateOptions.GenerateDebugInfo.getValue(HostedOptionValues.singleton()) > 0) { ImageSingletons.add(SourceManager.class, new SourceManager()); - DebugInfoProvider provider = new NativeImageDebugInfoProvider(codeCache, heap); + DebugInfoProvider provider = new NativeImageDebugInfoProvider(debug, codeCache, heap); objectFile.installDebugInfo(provider); } // - Write the heap, either to its own section, or to the ro and rw data sections. @@ -973,255 +963,4 @@ protected NativeTextSectionImpl(RelocatableBuffer relocatableBuffer, ObjectFile protected final ObjectFile objectFile; protected final NativeImageCodeCache codeCache; } - - /** - * implementation of the DebugInfoProvider API interface that allows type, code and heap data - * info to be passed to an ObjectFile when generation of debug info is enabled. - */ - private class NativeImageDebugInfoProvider implements DebugInfoProvider { - private final NativeImageCodeCache codeCache; - @SuppressWarnings("unused") - private final NativeImageHeap heap; - - NativeImageDebugInfoProvider(NativeImageCodeCache codeCache, NativeImageHeap heap) { - super(); - this.codeCache = codeCache; - this.heap = heap; - } - - @Override - public Stream typeInfoProvider() { - return Stream.empty(); - } - - @Override - public Stream codeInfoProvider() { - return codeCache.compilations.entrySet().stream().map(entry -> new NativeImageDebugCodeInfo(entry.getKey(), entry.getValue())); - } - - @Override - public Stream dataInfoProvider() { - return Stream.empty(); - } - } - - /** - * implementation of the DebugCodeInfo API interface that allows code info to be passed to an - * ObjectFile when generation of debug info is enabled. - */ - private class NativeImageDebugCodeInfo implements DebugCodeInfo { - private final HostedMethod method; - private final ResolvedJavaType javaType; - private final CompilationResult compilation; - private Path fullFilePath; - - NativeImageDebugCodeInfo(HostedMethod method, CompilationResult compilation) { - this.method = method; - HostedType declaringClass = method.getDeclaringClass(); - Class clazz = declaringClass.getJavaClass(); - this.javaType = declaringClass.getWrapped(); - this.compilation = compilation; - fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(javaType, clazz); - } - - @Override - public String fileName() { - if (fullFilePath != null) { - return fullFilePath.getFileName().toString(); - } - return null; - } - - @Override - public Path filePath() { - if (fullFilePath != null) { - return fullFilePath.getParent(); - } - return null; - } - - @Override - public String className() { - return javaType.toClassName(); - } - - @Override - public String methodName() { - return method.format("%n"); - } - - @Override - public String paramNames() { - return method.format("%P"); - } - - @Override - public String returnTypeName() { - return method.format("%R"); - } - - @Override - public int addressLo() { - return method.getCodeAddressOffset(); - } - - @Override - public int addressHi() { - return method.getCodeAddressOffset() + compilation.getTargetCodeSize(); - } - - @Override - public int line() { - LineNumberTable lineNumberTable = method.getLineNumberTable(); - if (lineNumberTable != null) { - return lineNumberTable.getLineNumber(0); - } - return -1; - } - - @Override - public Stream lineInfoProvider() { - if (fileName().toString().length() == 0) { - return Stream.empty(); - } - return compilation.getSourceMappings().stream().map(sourceMapping -> new NativeImageDebugLineInfo(sourceMapping)); - } - - @Override - public int getFrameSize() { - return compilation.getTotalFrameSize(); - } - - @Override - public List getFrameSizeChanges() { - List frameSizeChanges = new LinkedList<>(); - for (Mark mark : compilation.getMarks()) { - // we only need to observe stack increment or decrement points - if (mark.id.equals("PROLOGUE_DECD_RSP")) { - NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, EXTEND); - frameSizeChanges.add(sizeChange); - // } else if (mark.id.equals("PROLOGUE_END")) { - // can ignore these - // } else if (mark.id.equals("EPILOGUE_START")) { - // can ignore these - } else if (mark.id.equals("EPILOGUE_INCD_RSP")) { - NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, CONTRACT); - frameSizeChanges.add(sizeChange); - // } else if(mark.id.equals("EPILOGUE_END")) { - } - } - return frameSizeChanges; - } - } - - /** - * implementation of the DebugLineInfo API interface that allows line number info to be passed - * to an ObjectFile when generation of debug info is enabled. - */ - private class NativeImageDebugLineInfo implements DebugLineInfo { - private final int bci; - private final ResolvedJavaMethod method; - private final int lo; - private final int hi; - private Path fullFilePath; - - NativeImageDebugLineInfo(SourceMapping sourceMapping) { - NodeSourcePosition position = sourceMapping.getSourcePosition(); - int posbci = position.getBCI(); - this.bci = (posbci >= 0 ? posbci : 0); - this.method = position.getMethod(); - this.lo = sourceMapping.getStartOffset(); - this.hi = sourceMapping.getEndOffset(); - computeFullFilePath(); - } - - @Override - public String fileName() { - if (fullFilePath != null) { - return fullFilePath.getFileName().toString(); - } - return null; - } - - @Override - public Path filePath() { - if (fullFilePath != null) { - return fullFilePath.getParent(); - } - return null; - } - - @Override - public String className() { - return method.format("%H"); - } - - @Override - public String methodName() { - return method.format("%n"); - } - - @Override - public int addressLo() { - return lo; - } - - @Override - public int addressHi() { - return hi; - } - - @Override - public int line() { - LineNumberTable lineNumberTable = method.getLineNumberTable(); - if (lineNumberTable != null) { - return lineNumberTable.getLineNumber(bci); - } - return -1; - } - - private void computeFullFilePath() { - ResolvedJavaType declaringClass = method.getDeclaringClass(); - Class clazz = null; - if (declaringClass instanceof OriginalClassProvider) { - clazz = ((OriginalClassProvider) declaringClass).getJavaClass(); - } - /* - * HostedType and AnalysisType punt calls to getSourceFilename to the wrapped class so - * for consistency we need to do the path lookup relative to the wrapped class - */ - if (declaringClass instanceof HostedType) { - declaringClass = ((HostedType) declaringClass).getWrapped(); - } - if (declaringClass instanceof AnalysisType) { - declaringClass = ((AnalysisType) declaringClass).getWrapped(); - } - fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass, clazz); - } - - } - - /** - * implementation of the DebugFrameSizeChange API interface that allows stack frame size change - * info to be passed to an ObjectFile when generation of debug info is enabled. - */ - private class NativeImageDebugFrameSizeChange implements DebugFrameSizeChange { - private int offset; - private Type type; - - NativeImageDebugFrameSizeChange(int offset, Type type) { - this.offset = offset; - this.type = type; - } - - @Override - public int getOffset() { - return offset; - } - - @Override - public Type getType() { - return type; - } - } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImageViaCC.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImageViaCC.java index 5b540ed60333..7d7aa41e57df 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImageViaCC.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImageViaCC.java @@ -357,7 +357,7 @@ private static List diagnoseLinkerFailure(String linkerOutput) { public LinkerInvocation write(DebugContext debug, Path outputDirectory, Path tempDirectory, String imageName, BeforeImageWriteAccessImpl config) { try (Indent indent = debug.logAndIndent("Writing native image")) { // 1. write the relocatable file - write(tempDirectory.resolve(imageName + ObjectFile.getFilenameSuffix())); + write(debug, tempDirectory.resolve(imageName + ObjectFile.getFilenameSuffix())); if (NativeImageOptions.ExitAfterRelocatableImageWrite.getValue()) { return null; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java new file mode 100644 index 000000000000..24d748b54daf --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -0,0 +1,321 @@ +/* + * 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.svm.hosted.image; + +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.CONTRACT; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.EXTEND; + +import java.nio.file.Path; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import org.graalvm.compiler.code.CompilationResult; +import org.graalvm.compiler.code.SourceMapping; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.graph.NodeSourcePosition; +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import com.oracle.svm.hosted.image.sources.SourceManager; +import com.oracle.svm.hosted.meta.HostedMethod; +import com.oracle.svm.hosted.meta.HostedType; + +import jdk.vm.ci.code.site.Mark; +import jdk.vm.ci.meta.LineNumberTable; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; + +/** + * Implementation of the DebugInfoProvider API interface that allows type, code and heap data info + * to be passed to an ObjectFile when generation of debug info is enabled. + */ +class NativeImageDebugInfoProvider implements DebugInfoProvider { + private final DebugContext debugContext; + private final NativeImageCodeCache codeCache; + @SuppressWarnings("unused") private final NativeImageHeap heap; + + NativeImageDebugInfoProvider(DebugContext debugContext, NativeImageCodeCache codeCache, NativeImageHeap heap) { + super(); + this.debugContext = debugContext; + this.codeCache = codeCache; + this.heap = heap; + } + + @Override + public Stream typeInfoProvider() { + return Stream.empty(); + } + + @Override + public Stream codeInfoProvider() { + return codeCache.compilations.entrySet().stream().map(entry -> new NativeImageDebugCodeInfo(entry.getKey(), entry.getValue())); + } + + @Override + public Stream dataInfoProvider() { + return Stream.empty(); + } + + /** + * Implementation of the DebugCodeInfo API interface that allows code info to be passed to an + * ObjectFile when generation of debug info is enabled. + */ + private class NativeImageDebugCodeInfo implements DebugCodeInfo { + private final HostedMethod method; + private final ResolvedJavaType javaType; + private final CompilationResult compilation; + private Path fullFilePath; + + NativeImageDebugCodeInfo(HostedMethod method, CompilationResult compilation) { + this.method = method; + HostedType declaringClass = method.getDeclaringClass(); + Class clazz = declaringClass.getJavaClass(); + this.javaType = declaringClass.getWrapped(); + this.compilation = compilation; + fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(javaType, clazz); + } + + @SuppressWarnings("try") + @Override + public void debugContext(Consumer action) { + try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", method)) { + action.accept(debugContext); + } catch (Throwable e) { + throw debugContext.handle(e); + } + } + + @Override + public String fileName() { + if (fullFilePath != null) { + Path filename = fullFilePath.getFileName(); + if (filename != null) { + return filename.toString(); + } + } + return ""; + } + + @Override + public Path filePath() { + if (fullFilePath != null) { + return fullFilePath.getParent(); + } + return null; + } + + @Override + public String className() { + return javaType.toClassName(); + } + + @Override + public String methodName() { + return method.format("%n"); + } + + @Override + public String paramNames() { + return method.format("%P"); + } + + @Override + public String returnTypeName() { + return method.format("%R"); + } + + @Override + public int addressLo() { + return method.getCodeAddressOffset(); + } + + @Override + public int addressHi() { + return method.getCodeAddressOffset() + compilation.getTargetCodeSize(); + } + + @Override + public int line() { + LineNumberTable lineNumberTable = method.getLineNumberTable(); + if (lineNumberTable != null) { + return lineNumberTable.getLineNumber(0); + } + return -1; + } + + @Override + public Stream lineInfoProvider() { + if (fileName().length() == 0) { + return Stream.empty(); + } + return compilation.getSourceMappings().stream().map(sourceMapping -> new NativeImageDebugLineInfo(sourceMapping)); + } + + @Override + public int getFrameSize() { + return compilation.getTotalFrameSize(); + } + + @Override + public List getFrameSizeChanges() { + List frameSizeChanges = new LinkedList<>(); + for (Mark mark : compilation.getMarks()) { + // we only need to observe stack increment or decrement points + if (mark.id.equals("PROLOGUE_DECD_RSP")) { + NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, EXTEND); + frameSizeChanges.add(sizeChange); + // } else if (mark.id.equals("PROLOGUE_END")) { + // can ignore these + // } else if (mark.id.equals("EPILOGUE_START")) { + // can ignore these + } else if (mark.id.equals("EPILOGUE_INCD_RSP")) { + NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, CONTRACT); + frameSizeChanges.add(sizeChange); + // } else if(mark.id.equals("EPILOGUE_END")) { + } + } + return frameSizeChanges; + } + } + + /** + * Implementation of the DebugLineInfo API interface that allows line number info to be passed + * to an ObjectFile when generation of debug info is enabled. + */ + private class NativeImageDebugLineInfo implements DebugLineInfo { + private final int bci; + private final ResolvedJavaMethod method; + private final int lo; + private final int hi; + private Path fullFilePath; + + NativeImageDebugLineInfo(SourceMapping sourceMapping) { + NodeSourcePosition position = sourceMapping.getSourcePosition(); + int posbci = position.getBCI(); + this.bci = (posbci >= 0 ? posbci : 0); + this.method = position.getMethod(); + this.lo = sourceMapping.getStartOffset(); + this.hi = sourceMapping.getEndOffset(); + computeFullFilePath(); + } + + @Override + public String fileName() { + if (fullFilePath != null) { + Path fileName = fullFilePath.getFileName(); + if (fileName != null) { + return fileName.toString(); + } + } + return null; + } + + @Override + public Path filePath() { + if (fullFilePath != null) { + return fullFilePath.getParent(); + } + return null; + } + + @Override + public String className() { + return method.format("%H"); + } + + @Override + public String methodName() { + return method.format("%n"); + } + + @Override + public int addressLo() { + return lo; + } + + @Override + public int addressHi() { + return hi; + } + + @Override + public int line() { + LineNumberTable lineNumberTable = method.getLineNumberTable(); + if (lineNumberTable != null) { + return lineNumberTable.getLineNumber(bci); + } + return -1; + } + + private void computeFullFilePath() { + ResolvedJavaType declaringClass = method.getDeclaringClass(); + Class clazz = null; + if (declaringClass instanceof OriginalClassProvider) { + clazz = ((OriginalClassProvider) declaringClass).getJavaClass(); + } + /* + * HostedType and AnalysisType punt calls to getSourceFilename to the wrapped class so + * for consistency we need to do the path lookup relative to the wrapped class. + */ + if (declaringClass instanceof HostedType) { + declaringClass = ((HostedType) declaringClass).getWrapped(); + } + if (declaringClass instanceof AnalysisType) { + declaringClass = ((AnalysisType) declaringClass).getWrapped(); + } + fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass, clazz); + } + + } + + /** + * Implementation of the DebugFrameSizeChange API interface that allows stack frame size change + * info to be passed to an ObjectFile when generation of debug info is enabled. + */ + private class NativeImageDebugFrameSizeChange implements DebugFrameSizeChange { + private int offset; + private Type type; + + NativeImageDebugFrameSizeChange(int offset, Type type) { + this.offset = offset; + this.type = type; + } + + @Override + public int getOffset() { + return offset; + } + + @Override + public Type getType() { + return type; + } + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java index 2f6dd613ba45..83cda2b0b38a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/ApplicationSourceCache.java @@ -68,48 +68,50 @@ private void trySourceRoot(String sourcePathEntry) { } private void trySourceRoot(String sourceRoot, boolean fromClassPath) { - Path sourcePath = Paths.get(sourceRoot); - String fileNameString = sourcePath.getFileName().toString(); - if (fileNameString.endsWith(".jar") || fileNameString.endsWith(".zip")) { - if (fromClassPath && fileNameString.endsWith(".jar")) { - /* - * application jar /path/to/xxx.jar should have sources /path/to/xxx-sources.jar - */ - int length = fileNameString.length(); - fileNameString = fileNameString.substring(0, length - 4) + "-sources.zip"; - } - sourcePath = sourcePath.getParent().resolve(fileNameString); - if (sourcePath.toFile().exists()) { - try { - FileSystem fileSystem = FileSystems.newFileSystem(sourcePath, null); - for (Path root : fileSystem.getRootDirectories()) { - srcRoots.add(root); + try { + Path sourcePath = Paths.get(sourceRoot); + String fileNameString = sourcePath.getFileName().toString(); + if (fileNameString.endsWith(".jar") || fileNameString.endsWith(".zip")) { + if (fromClassPath && fileNameString.endsWith(".jar")) { + /* + * application jar /path/to/xxx.jar should have sources /path/to/xxx-sources.jar + */ + int length = fileNameString.length(); + fileNameString = fileNameString.substring(0, length - 4) + "-sources.zip"; + } + sourcePath = sourcePath.getParent().resolve(fileNameString); + if (sourcePath.toFile().exists()) { + try { + FileSystem fileSystem = FileSystems.newFileSystem(sourcePath, null); + for (Path root : fileSystem.getRootDirectories()) { + srcRoots.add(root); + } + } catch (IOException | FileSystemNotFoundException ioe) { + /* ignore this entry */ } - } catch (IOException ioe) { - /* ignore this entry */ - } catch (FileSystemNotFoundException fnfe) { - /* ignore this entry */ } - } - } else { - if (fromClassPath) { - /* - * for dir entries ending in classes or target/classes translate to a parallel src - * tree - */ - if (sourcePath.endsWith("classes")) { - Path parent = sourcePath.getParent(); - if (parent.endsWith("target")) { - parent = parent.getParent(); + } else { + if (fromClassPath) { + /* + * for dir entries ending in classes or target/classes translate to a parallel + * src tree + */ + if (sourcePath.endsWith("classes")) { + Path parent = sourcePath.getParent(); + if (parent.endsWith("target")) { + parent = parent.getParent(); + } + sourcePath = (parent.resolve("src")); } - sourcePath = (parent.resolve("src")); + } + // try the path as provided + File file = sourcePath.toFile(); + if (file.exists() && file.isDirectory()) { + srcRoots.add(sourcePath); } } - // try the path as provided - File file = sourcePath.toFile(); - if (file.exists() && file.isDirectory()) { - srcRoots.add(sourcePath); - } + } catch (NullPointerException npe) { + // do nothing } } -} \ No newline at end of file +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java index 833166c2daff..c583a300dbbb 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java @@ -26,7 +26,6 @@ package com.oracle.svm.hosted.image.sources; -import java.io.File; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.FileSystemNotFoundException; @@ -51,8 +50,6 @@ protected final SourceCacheType getType() { return GRAALVM; } - private static final String JAVA_CLASSPATH_PROP = "java.class.path"; - private void initSrcRoots() { for (String classPathEntry : classPathEntries) { tryClassPathRoot(classPathEntry); @@ -71,49 +68,51 @@ private void trySourceRoot(String sourcePathEntry) { } private void trySourceRoot(String sourceRoot, boolean fromClassPath) { - Path sourcePath = Paths.get(sourceRoot); - String fileNameString = sourcePath.getFileName().toString(); - if (fileNameString.endsWith(".jar") || fileNameString.endsWith(".src.zip")) { - if (fromClassPath && fileNameString.endsWith(".jar")) { - /* - * GraalVM jar /path/to/xxx.jar in classpath should have sources - * /path/to/xxx.src.zip - */ - int length = fileNameString.length(); - fileNameString = fileNameString.substring(0, length - 3) + "src.zip"; - } - Path srcPath = sourcePath.getParent().resolve(fileNameString); - if (srcPath.toFile().exists()) { - try { - FileSystem fileSystem = FileSystems.newFileSystem(srcPath, null); - for (Path root : fileSystem.getRootDirectories()) { - if (filterSrcRoot(root)) { - srcRoots.add(root); + try { + Path sourcePath = Paths.get(sourceRoot); + String fileNameString = sourcePath.getFileName().toString(); + if (fileNameString.endsWith(".jar") || fileNameString.endsWith(".src.zip")) { + if (fromClassPath && fileNameString.endsWith(".jar")) { + /* + * GraalVM jar /path/to/xxx.jar in classpath should have sources + * /path/to/xxx.src.zip + */ + int length = fileNameString.length(); + fileNameString = fileNameString.substring(0, length - 3) + "src.zip"; + } + Path srcPath = sourcePath.getParent().resolve(fileNameString); + if (srcPath.toFile().exists()) { + try { + FileSystem fileSystem = FileSystems.newFileSystem(srcPath, null); + for (Path root : fileSystem.getRootDirectories()) { + if (filterSrcRoot(root)) { + srcRoots.add(root); + } } + } catch (IOException | FileSystemNotFoundException ioe) { + /* ignore this entry */ } - } catch (IOException ioe) { - /* ignore this entry */ - } catch (FileSystemNotFoundException fnfe) { - /* ignore this entry */ - } - } - } else { - if (fromClassPath) { - /* graal classpath dir entries should have a src and/or src_gen subdirectory */ - Path srcPath = sourcePath.resolve("src"); - if (filterSrcRoot(srcPath)) { - srcRoots.add(srcPath); - } - srcPath = sourcePath.resolve("src_gen"); - if (filterSrcRoot(srcPath)) { - srcRoots.add(srcPath); } } else { - // try the path as provided - if (filterSrcRoot(sourcePath)) { - srcRoots.add(sourcePath); + if (fromClassPath) { + /* graal classpath dir entries should have a src and/or src_gen subdirectory */ + Path srcPath = sourcePath.resolve("src"); + if (filterSrcRoot(srcPath)) { + srcRoots.add(srcPath); + } + srcPath = sourcePath.resolve("src_gen"); + if (filterSrcRoot(srcPath)) { + srcRoots.add(srcPath); + } + } else { + // try the path as provided + if (filterSrcRoot(sourcePath)) { + srcRoots.add(sourcePath); + } } } + } catch (NullPointerException npe) { + // do nothing } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java index fc93a0154994..4bc3bd208898 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/JDKSourceCache.java @@ -72,9 +72,7 @@ private void initSrcRoots() { for (Path root : srcFileSystem.getRootDirectories()) { srcRoots.add(root); } - } catch (IOException ioe) { - /* ignore this entry */ - } catch (FileSystemNotFoundException fnfe) { + } catch (IOException | FileSystemNotFoundException ioe) { /* ignore this entry */ } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java index 0507702ea3a2..ffd353ed43a9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceCache.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, Red Hat Inc. 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 @@ -55,16 +55,16 @@ public abstract class SourceCache { /** - * A list of all entries in the classpath used by the native image classloader + * A list of all entries in the classpath used by the native image classloader. */ protected static final List classPathEntries = new ArrayList<>(); /** - * A list of all entries in the classpath used by the native image classloader + * A list of all entries in the classpath used by the native image classloader. */ protected static final List sourcePathEntries = new ArrayList<>(); /** * A list of root directories which may contain source files from which this cache can be - * populated + * populated. */ protected List srcRoots; @@ -77,7 +77,7 @@ protected SourceCache() { } /** - * Identify the specific type of this source cache + * Identify the specific type of this source cache. * * @return the source cache type */ @@ -271,11 +271,10 @@ protected File cachedFile(Path candidate) { /** * Indicate whether a source path identifies a file in the associated file system. * - * @param sourcePath + * @param sourcePath the path to check * @return true if the path identifies a file or false if no such file can be found. - * @throws IOException if there is some error in resolving the path. */ - private static boolean checkSourcePath(Path sourcePath) throws IOException { + private static boolean checkSourcePath(Path sourcePath) { return Files.isRegularFile(sourcePath); } @@ -283,9 +282,8 @@ private static boolean checkSourcePath(Path sourcePath) throws IOException { * Ensure the directory hierarchy for a path exists creating any missing directories if needed. * * @param targetDir a path to the desired directory - * @throws IOException if it is not possible to create one or more directories in the path */ - private static void ensureTargetDirs(Path targetDir) throws IOException { + private static void ensureTargetDirs(Path targetDir) { if (targetDir != null) { File targetFile = targetDir.toFile(); if (!targetFile.exists()) { @@ -295,7 +293,7 @@ private static void ensureTargetDirs(Path targetDir) throws IOException { } /** - * Add a path to the list of classpath entries + * Add a path to the list of classpath entries. * * @param path The path to add. */ @@ -304,7 +302,7 @@ private static void addClassPathEntry(String path) { } /** - * Add a path to the list of source path entries + * Add a path to the list of source path entries. * * @param path The path to add. */ @@ -317,6 +315,7 @@ private static void addSourcePathEntry(String path) { * callback. */ @AutomaticFeature + @SuppressWarnings("unused") public static class SourceCacheFeature implements Feature { @Override public void afterAnalysis(AfterAnalysisAccess access) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java index cca64372df04..9cdb738fd6f5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/SourceManager.java @@ -65,7 +65,7 @@ public Path findAndCacheSource(ResolvedJavaType resolvedType, Class clazz) { if (resolvedType.isInstanceClass() || resolvedType.isInterface()) { String packageName = computePackageName(resolvedType); SourceCacheType sourceCacheType = sourceCacheType(packageName); - path = locateSource(fileName, packageName, sourceCacheType, resolvedType, clazz); + path = locateSource(fileName, packageName, sourceCacheType, clazz); if (path == null) { // as a last ditch effort derive path from the Java class name if (packageName.length() > 0) { @@ -88,7 +88,7 @@ public Path findAndCacheSource(ResolvedJavaType resolvedType, Class clazz) { * @param resolvedType the resolved java type whose source file name is required * @return the file name or null if it the class cannot be associated with a source file */ - private String computeBaseName(ResolvedJavaType resolvedType) { + private static String computeBaseName(ResolvedJavaType resolvedType) { String fileName = resolvedType.getSourceFileName(); if (fileName == null) { /* ok, try to construct it from the class name */ @@ -120,7 +120,7 @@ private String computeBaseName(ResolvedJavaType resolvedType) { * @param javaType the Java type whose package name is required * @return the package name or the empty String if it has no package */ - private String computePackageName(ResolvedJavaType javaType) { + private static String computePackageName(ResolvedJavaType javaType) { String name = javaType.toClassName(); int idx = name.lastIndexOf('.'); if (idx > 0) { @@ -138,12 +138,11 @@ private String computePackageName(ResolvedJavaType javaType) { * @param packageName the name of the package for the associated Java class * @param sourceCacheType the sourceCacheType of cache in which to lookup or cache this class's * source file - * @param javaType the java sourceCacheType whose prototype name is required * @param clazz the class associated with the sourceCacheType used to identify the module prefix * for JDK classes * @return a protoype name for the source file */ - private Path computePrototypeName(String fileName, String packageName, SourceCacheType sourceCacheType, ResolvedJavaType javaType, Class clazz) { + private static Path computePrototypeName(String fileName, String packageName, SourceCacheType sourceCacheType, Class clazz) { String prefix = ""; if (sourceCacheType == SourceCacheType.JDK && clazz != null) { /* JDK11+ paths will require the module name as prefix */ @@ -185,14 +184,6 @@ private Path computePrototypeName(String fileName, String packageName, SourceCac "org.graalvm.", }; - /** - * A whitelist of packages prefixes used to pre-filter app class lookups which includes just the - * empty string because any package will do. - */ - private static final String[] APP_SRC_PACKAGE_PREFIXES = { - "", - }; - /** * Check a package name against a whitelist of acceptable packages. * @@ -201,7 +192,7 @@ private Path computePrototypeName(String fileName, String packageName, SourceCac * name being checked * @return true if the package name matches an entry in the whitelist otherwise false */ - private boolean whiteListPackage(String packageName, String[] whitelist) { + private static boolean whiteListPackage(String packageName, String[] whitelist) { for (String prefix : whitelist) { if (packageName.startsWith(prefix)) { return true; @@ -217,7 +208,7 @@ private boolean whiteListPackage(String packageName, String[] whitelist) { * @param packageName the package name of the class. * @return the corresponding source cache type */ - private SourceCacheType sourceCacheType(String packageName) { + private static SourceCacheType sourceCacheType(String packageName) { if (whiteListPackage(packageName, JDK_SRC_PACKAGE_PREFIXES)) { return SourceCacheType.JDK; } @@ -252,7 +243,7 @@ private SourceCacheType sourceCacheType(String packageName) { * @param type an enum identifying the type of Java sources cached by the returned cache. * @return the desired source cache. */ - private SourceCache getOrCreateCache(SourceCacheType type) { + private static SourceCache getOrCreateCache(SourceCacheType type) { SourceCache sourceCache = caches.get(type); if (sourceCache == null) { sourceCache = SourceCache.createSourceCache(type); @@ -261,9 +252,9 @@ private SourceCache getOrCreateCache(SourceCacheType type) { return sourceCache; } - private Path locateSource(String fileName, String packagename, SourceCacheType type, ResolvedJavaType javaType, Class clazz) { - SourceCache cache = getOrCreateCache(type); - Path prototypeName = computePrototypeName(fileName, packagename, type, javaType, clazz); + private static Path locateSource(String fileName, String packagename, SourceCacheType cacheType, Class clazz) { + SourceCache cache = getOrCreateCache(cacheType); + Path prototypeName = computePrototypeName(fileName, packagename, cacheType, clazz); if (prototypeName != null) { return cache.resolve(prototypeName); } else { From e4c876066b1122091bcaf68d869f94961d751724 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Mon, 30 Mar 2020 19:15:15 +0100 Subject: [PATCH 8/9] Abstract generic code for modelling debug info from DwarfSections into DebugInfoBase Rename subclass DwarfSections to DwarfDebugInfo --- .../DebugInfoBase.java} | 248 +++--------------- .../oracle/objectfile/elf/ELFObjectFile.java | 4 +- .../elf/dwarf/DwarfARangesSectionImpl.java | 8 +- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 44 ++-- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 204 ++++++++++++++ .../elf/dwarf/DwarfFrameSectionImpl.java | 28 +- .../dwarf/DwarfFrameSectionImplAArch64.java | 2 +- .../dwarf/DwarfFrameSectionImplX86_64.java | 2 +- .../elf/dwarf/DwarfInfoSectionImpl.java | 16 +- .../elf/dwarf/DwarfLineSectionImpl.java | 8 +- .../elf/dwarf/DwarfSectionImpl.java | 6 +- .../elf/dwarf/DwarfStrSectionImpl.java | 6 +- 12 files changed, 306 insertions(+), 270 deletions(-) rename substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/{elf/dwarf/DwarfSections.java => debugentry/DebugInfoBase.java} (59%) create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java similarity index 59% rename from substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java rename to substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index fbb4d2e2d5fb..f9254549b817 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSections.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, Red Hat Inc. 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 @@ -24,7 +24,10 @@ * questions. */ -package com.oracle.objectfile.elf.dwarf; +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; @@ -32,195 +35,17 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; - -import org.graalvm.compiler.debug.DebugContext; - -import com.oracle.objectfile.debugentry.ClassEntry; -import com.oracle.objectfile.debugentry.DirEntry; -import com.oracle.objectfile.debugentry.FileEntry; -import com.oracle.objectfile.debugentry.Range; -import com.oracle.objectfile.debugentry.StringTable; -import com.oracle.objectfile.debuginfo.DebugInfoProvider; -import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; -import com.oracle.objectfile.elf.ELFMachine; - /** - * A class that models the debug info in an organization that facilitates generation of the required - * DWARF sections. It groups common data and behaviours for use by the various subclasses of class - * DwarfSectionImpl that take responsibility for generating content for a specific section type. + * 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 class DwarfSections { - - /* - * Names of the different ELF sections we create or reference in reverse dependency order. - */ - public static final String TEXT_SECTION_NAME = ".text"; - public static final String DW_STR_SECTION_NAME = ".debug_str"; - public static final String DW_LINE_SECTION_NAME = ".debug_line"; - public static final String DW_FRAME_SECTION_NAME = ".debug_frame"; - public static final String DW_ABBREV_SECTION_NAME = ".debug_abbrev"; - public static final String DW_INFO_SECTION_NAME = ".debug_info"; - public static final String DW_ARANGES_SECTION_NAME = ".debug_aranges"; - - /** - * Currently generated debug info relies on DWARF spec vesion 2. - */ - public static final short DW_VERSION_2 = 2; - - /* - * Define all the abbrev section codes we need for our DIEs. - */ - @SuppressWarnings("unused") public static final int DW_ABBREV_CODE_null = 0; - public static final int DW_ABBREV_CODE_compile_unit = 1; - public static final int DW_ABBREV_CODE_subprogram = 2; - - /* - * Define all the Dwarf tags we need for our DIEs. - */ - public static final int DW_TAG_compile_unit = 0x11; - public static final int DW_TAG_subprogram = 0x2e; - /* - * Define all the Dwarf attributes we need for our DIEs. - */ - public static final int DW_AT_null = 0x0; - public static final int DW_AT_name = 0x3; - @SuppressWarnings("unused") public static final int DW_AT_comp_dir = 0x1b; - public static final int DW_AT_stmt_list = 0x10; - public static final int DW_AT_low_pc = 0x11; - public static final int DW_AT_hi_pc = 0x12; - public static final int DW_AT_language = 0x13; - public static final int DW_AT_external = 0x3f; - @SuppressWarnings("unused") public static final int DW_AT_return_addr = 0x2a; - @SuppressWarnings("unused") public static final int DW_AT_frame_base = 0x40; - /* - * Define all the Dwarf attribute forms we need for our DIEs. - */ - public static final int DW_FORM_null = 0x0; - @SuppressWarnings("unused") private static final int DW_FORM_string = 0x8; - public static final int DW_FORM_strp = 0xe; - public static final int DW_FORM_addr = 0x1; - public static final int DW_FORM_data1 = 0x0b; - public static final int DW_FORM_data4 = 0x6; - @SuppressWarnings("unused") public static final int DW_FORM_data8 = 0x7; - @SuppressWarnings("unused") public static final int DW_FORM_block1 = 0x0a; - public static final int DW_FORM_flag = 0xc; - - /* - * Define specific attribute values for given attribute or form types. - */ - /* - * DIE header has_children attribute values. - */ - public static final byte DW_CHILDREN_no = 0; - public static final byte DW_CHILDREN_yes = 1; - /* - * DW_FORM_flag attribute values. - */ - @SuppressWarnings("unused") public static final byte DW_FLAG_false = 0; - public static final byte DW_FLAG_true = 1; - /* - * Value for DW_AT_language attribute with form DATA1. - */ - public static final byte DW_LANG_Java = 0xb; - - /* - * DW_AT_Accessibility attribute values. - * - * These are not needed until we make functions members. - */ - @SuppressWarnings("unused") public static final byte DW_ACCESS_public = 1; - @SuppressWarnings("unused") public static final byte DW_ACCESS_protected = 2; - @SuppressWarnings("unused") public static final byte DW_ACCESS_private = 3; - - /* - * Others that are not yet needed. - */ - @SuppressWarnings("unused") public static final int DW_AT_type = 0; // only present for non-void - // functions - @SuppressWarnings("unused") public static final int DW_AT_accessibility = 0; - - /* - * CIE and FDE entries. - */ - - /* Full byte/word values. */ - public static final int DW_CFA_CIE_id = -1; - @SuppressWarnings("unused") public static final int DW_CFA_FDE_id = 0; - - public static final byte DW_CFA_CIE_version = 1; - - /* Values encoded in high 2 bits. */ - public static final byte DW_CFA_advance_loc = 0x1; - public static final byte DW_CFA_offset = 0x2; - @SuppressWarnings("unused") public static final byte DW_CFA_restore = 0x3; - - /* Values encoded in low 6 bits. */ - public static final byte DW_CFA_nop = 0x0; - @SuppressWarnings("unused") public static final byte DW_CFA_set_loc1 = 0x1; - public static final byte DW_CFA_advance_loc1 = 0x2; - public static final byte DW_CFA_advance_loc2 = 0x3; - public static final byte DW_CFA_advance_loc4 = 0x4; - @SuppressWarnings("unused") public static final byte DW_CFA_offset_extended = 0x5; - @SuppressWarnings("unused") public static final byte DW_CFA_restore_extended = 0x6; - @SuppressWarnings("unused") public static final byte DW_CFA_undefined = 0x7; - @SuppressWarnings("unused") public static final byte DW_CFA_same_value = 0x8; - public static final byte DW_CFA_register = 0x9; - public static final byte DW_CFA_def_cfa = 0xc; - @SuppressWarnings("unused") public static final byte DW_CFA_def_cfa_register = 0xd; - public static final byte DW_CFA_def_cfa_offset = 0xe; - - private ByteOrder byteOrder; - private DwarfStrSectionImpl dwarfStrSection; - private DwarfAbbrevSectionImpl dwarfAbbrevSection; - private DwarfInfoSectionImpl dwarfInfoSection; - private DwarfARangesSectionImpl dwarfARangesSection; - private DwarfLineSectionImpl dwarfLineSection; - private DwarfFrameSectionImpl dwarfFameSection; - - public DwarfSections(ELFMachine elfMachine, ByteOrder byteOrder) { - this.byteOrder = byteOrder; - dwarfStrSection = new DwarfStrSectionImpl(this); - dwarfAbbrevSection = new DwarfAbbrevSectionImpl(this); - dwarfInfoSection = new DwarfInfoSectionImpl(this); - dwarfARangesSection = new DwarfARangesSectionImpl(this); - dwarfLineSection = new DwarfLineSectionImpl(this); - if (elfMachine == ELFMachine.AArch64) { - dwarfFameSection = new DwarfFrameSectionImplAArch64(this); - } else { - dwarfFameSection = new DwarfFrameSectionImplX86_64(this); - } - } - - public DwarfStrSectionImpl getStrSectionImpl() { - return dwarfStrSection; - } - - public DwarfAbbrevSectionImpl getAbbrevSectionImpl() { - return dwarfAbbrevSection; - } - - public DwarfFrameSectionImpl getFrameSectionImpl() { - return dwarfFameSection; - } - - public DwarfInfoSectionImpl getInfoSectionImpl() { - return dwarfInfoSection; - } - - public DwarfARangesSectionImpl getARangesSectionImpl() { - return dwarfARangesSection; - } - - public DwarfLineSectionImpl getLineSectionImpl() { - return dwarfLineSection; - } - +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. */ - private StringTable stringTable = new StringTable(); - + protected StringTable stringTable = new StringTable(); /** * Index of all dirs in which files are found to reside either as part of substrate/compiler or * user code. @@ -265,23 +90,18 @@ public DwarfLineSectionImpl getLineSectionImpl() { * index of already seen classes. */ private Map primaryClassesIndex = new HashMap<>(); - /** * Index of files which contain primary or secondary ranges. */ private Map filesIndex = new HashMap<>(); - /** - * 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. + * List of of files which contain primary or secondary ranges. */ - public int debugStringIndex(String string) { - return stringTable.debugStringIndex(string); - } + private LinkedList files = new LinkedList<>(); + public DebugInfoBase(ByteOrder byteOrder) { + this.byteOrder = byteOrder; + } /** * Entry point allowing ELFObjectFile to pass on information about types, code and heap data. * @@ -347,7 +167,6 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { * String name = debugDataInfo.toString(); } */ } - private ClassEntry ensureClassEntry(Range range) { String className = range.getClassName(); /* @@ -366,7 +185,6 @@ private ClassEntry ensureClassEntry(Range range) { assert classEntry.getClassName().equals(className); return classEntry; } - private FileEntry ensureFileEntry(Range range) { String fileName = range.getFileName(); if (fileName == null) { @@ -381,6 +199,7 @@ private FileEntry ensureFileEntry(Range range) { if (fileEntry == null) { DirEntry dirEntry = ensureDirEntry(filePath); fileEntry = new FileEntry(fileName, dirEntry); + files.add(fileEntry); /* * Index the file entry by file path. */ @@ -394,13 +213,11 @@ private FileEntry ensureFileEntry(Range range) { } return fileEntry; } - - private void addRange(Range primaryRange, List frameSizeInfos, int frameSize) { + private void addRange(Range primaryRange, List 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(); @@ -415,7 +232,6 @@ private void addSubRange(Range primaryRange, Range subrange) { classEntry.addSubRange(subrange, subrangeFileEntry); } } - private DirEntry ensureDirEntry(Path filePath) { if (filePath == null) { return null; @@ -427,16 +243,32 @@ private DirEntry ensureDirEntry(Path filePath) { } return dirEntry; } - - public StringTable getStringTable() { - return stringTable; + /* Accessors to query the debug info model. */ + public ByteOrder getByteOrder() { + return byteOrder; } - public LinkedList getPrimaryClasses() { return primaryClasses; } - - public ByteOrder getByteOrder() { - return byteOrder; + @SuppressWarnings("unused") + public LinkedList 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. + * + * @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); } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java index ef1312dbcb5c..740779f08408 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java @@ -48,7 +48,7 @@ import com.oracle.objectfile.elf.dwarf.DwarfFrameSectionImpl; import com.oracle.objectfile.elf.dwarf.DwarfInfoSectionImpl; import com.oracle.objectfile.elf.dwarf.DwarfLineSectionImpl; -import com.oracle.objectfile.elf.dwarf.DwarfSections; +import com.oracle.objectfile.elf.dwarf.DwarfDebugInfo; import com.oracle.objectfile.elf.dwarf.DwarfStrSectionImpl; import com.oracle.objectfile.io.AssemblyBuffer; import com.oracle.objectfile.io.OutputAssembler; @@ -1167,7 +1167,7 @@ protected int getMinimumFileSize() { @Override public void installDebugInfo(DebugInfoProvider debugInfoProvider) { - DwarfSections dwarfSections = new DwarfSections(getMachine(), getByteOrder()); + DwarfDebugInfo dwarfSections = new DwarfDebugInfo(getMachine(), getByteOrder()); /* We need an implementation for each generated DWARF section. */ DwarfStrSectionImpl elfStrSectionImpl = dwarfSections.getStrSectionImpl(); DwarfAbbrevSectionImpl elfAbbrevSectionImpl = dwarfSections.getAbbrevSectionImpl(); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java index 2d5405f26fd7..5e459ee9a0be 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java @@ -37,9 +37,9 @@ import java.util.LinkedList; import java.util.Map; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ARANGES_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_INFO_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_VERSION_2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ARANGES_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_INFO_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_VERSION_2; /** * Section generator for debug_aranges section. @@ -48,7 +48,7 @@ public class DwarfARangesSectionImpl extends DwarfSectionImpl { private static final int DW_AR_HEADER_SIZE = 12; private static final int DW_AR_HEADER_PAD_SIZE = 4; // align up to 2 * address size - public DwarfARangesSectionImpl(DwarfSections dwarfSections) { + public DwarfARangesSectionImpl(DwarfDebugInfo dwarfSections) { super(dwarfSections); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java index 6f8d7d44c2d4..c8ed13ca2cd4 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java @@ -29,34 +29,34 @@ import com.oracle.objectfile.LayoutDecision; import org.graalvm.compiler.debug.DebugContext; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_CODE_compile_unit; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_CODE_subprogram; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_external; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_hi_pc; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_language; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_low_pc; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_name; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_null; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_AT_stmt_list; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CHILDREN_no; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CHILDREN_yes; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_addr; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_data1; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_data4; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_flag; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_null; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FORM_strp; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FRAME_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_TAG_compile_unit; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_TAG_subprogram; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_compile_unit; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_subprogram; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_external; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_hi_pc; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_language; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_low_pc; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_name; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_null; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_stmt_list; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CHILDREN_no; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CHILDREN_yes; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_addr; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_data1; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_data4; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_flag; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_null; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_strp; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FRAME_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_compile_unit; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_subprogram; /** * Section generator for debug_abbrev section. */ public class DwarfAbbrevSectionImpl extends DwarfSectionImpl { - public DwarfAbbrevSectionImpl(DwarfSections dwarfSections) { + public DwarfAbbrevSectionImpl(DwarfDebugInfo dwarfSections) { super(dwarfSections); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java new file mode 100644 index 000000000000..2a6081280b4b --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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.elf.dwarf; + +import java.nio.ByteOrder; + +import com.oracle.objectfile.debugentry.DebugInfoBase; + +import com.oracle.objectfile.elf.ELFMachine; + +/** + * A class that models the debug info in an organization that facilitates generation of the required + * DWARF sections. It groups common data and behaviours for use by the various subclasses of class + * DwarfSectionImpl that take responsibility for generating content for a specific section type. + */ +public class DwarfDebugInfo extends DebugInfoBase { + + /* + * Names of the different ELF sections we create or reference in reverse dependency order. + */ + public static final String TEXT_SECTION_NAME = ".text"; + public static final String DW_STR_SECTION_NAME = ".debug_str"; + public static final String DW_LINE_SECTION_NAME = ".debug_line"; + public static final String DW_FRAME_SECTION_NAME = ".debug_frame"; + public static final String DW_ABBREV_SECTION_NAME = ".debug_abbrev"; + public static final String DW_INFO_SECTION_NAME = ".debug_info"; + public static final String DW_ARANGES_SECTION_NAME = ".debug_aranges"; + + /** + * Currently generated debug info relies on DWARF spec vesion 2. + */ + public static final short DW_VERSION_2 = 2; + + /* + * Define all the abbrev section codes we need for our DIEs. + */ + @SuppressWarnings("unused") public static final int DW_ABBREV_CODE_null = 0; + public static final int DW_ABBREV_CODE_compile_unit = 1; + public static final int DW_ABBREV_CODE_subprogram = 2; + + /* + * Define all the Dwarf tags we need for our DIEs. + */ + public static final int DW_TAG_compile_unit = 0x11; + public static final int DW_TAG_subprogram = 0x2e; + /* + * Define all the Dwarf attributes we need for our DIEs. + */ + public static final int DW_AT_null = 0x0; + public static final int DW_AT_name = 0x3; + @SuppressWarnings("unused") public static final int DW_AT_comp_dir = 0x1b; + public static final int DW_AT_stmt_list = 0x10; + public static final int DW_AT_low_pc = 0x11; + public static final int DW_AT_hi_pc = 0x12; + public static final int DW_AT_language = 0x13; + public static final int DW_AT_external = 0x3f; + @SuppressWarnings("unused") public static final int DW_AT_return_addr = 0x2a; + @SuppressWarnings("unused") public static final int DW_AT_frame_base = 0x40; + /* + * Define all the Dwarf attribute forms we need for our DIEs. + */ + public static final int DW_FORM_null = 0x0; + @SuppressWarnings("unused") private static final int DW_FORM_string = 0x8; + public static final int DW_FORM_strp = 0xe; + public static final int DW_FORM_addr = 0x1; + public static final int DW_FORM_data1 = 0x0b; + public static final int DW_FORM_data4 = 0x6; + @SuppressWarnings("unused") public static final int DW_FORM_data8 = 0x7; + @SuppressWarnings("unused") public static final int DW_FORM_block1 = 0x0a; + public static final int DW_FORM_flag = 0xc; + + /* + * Define specific attribute values for given attribute or form types. + */ + /* + * DIE header has_children attribute values. + */ + public static final byte DW_CHILDREN_no = 0; + public static final byte DW_CHILDREN_yes = 1; + /* + * DW_FORM_flag attribute values. + */ + @SuppressWarnings("unused") public static final byte DW_FLAG_false = 0; + public static final byte DW_FLAG_true = 1; + /* + * Value for DW_AT_language attribute with form DATA1. + */ + public static final byte DW_LANG_Java = 0xb; + + /* + * DW_AT_Accessibility attribute values. + * + * These are not needed until we make functions members. + */ + @SuppressWarnings("unused") public static final byte DW_ACCESS_public = 1; + @SuppressWarnings("unused") public static final byte DW_ACCESS_protected = 2; + @SuppressWarnings("unused") public static final byte DW_ACCESS_private = 3; + + /* + * Others that are not yet needed. + */ + @SuppressWarnings("unused") public static final int DW_AT_type = 0; // only present for non-void + // functions + @SuppressWarnings("unused") public static final int DW_AT_accessibility = 0; + + /* + * CIE and FDE entries. + */ + + /* Full byte/word values. */ + public static final int DW_CFA_CIE_id = -1; + @SuppressWarnings("unused") public static final int DW_CFA_FDE_id = 0; + + public static final byte DW_CFA_CIE_version = 1; + + /* Values encoded in high 2 bits. */ + public static final byte DW_CFA_advance_loc = 0x1; + public static final byte DW_CFA_offset = 0x2; + @SuppressWarnings("unused") public static final byte DW_CFA_restore = 0x3; + + /* Values encoded in low 6 bits. */ + public static final byte DW_CFA_nop = 0x0; + @SuppressWarnings("unused") public static final byte DW_CFA_set_loc1 = 0x1; + public static final byte DW_CFA_advance_loc1 = 0x2; + public static final byte DW_CFA_advance_loc2 = 0x3; + public static final byte DW_CFA_advance_loc4 = 0x4; + @SuppressWarnings("unused") public static final byte DW_CFA_offset_extended = 0x5; + @SuppressWarnings("unused") public static final byte DW_CFA_restore_extended = 0x6; + @SuppressWarnings("unused") public static final byte DW_CFA_undefined = 0x7; + @SuppressWarnings("unused") public static final byte DW_CFA_same_value = 0x8; + public static final byte DW_CFA_register = 0x9; + public static final byte DW_CFA_def_cfa = 0xc; + @SuppressWarnings("unused") public static final byte DW_CFA_def_cfa_register = 0xd; + public static final byte DW_CFA_def_cfa_offset = 0xe; + + private DwarfStrSectionImpl dwarfStrSection; + private DwarfAbbrevSectionImpl dwarfAbbrevSection; + private DwarfInfoSectionImpl dwarfInfoSection; + private DwarfARangesSectionImpl dwarfARangesSection; + private DwarfLineSectionImpl dwarfLineSection; + private DwarfFrameSectionImpl dwarfFameSection; + + public DwarfDebugInfo(ELFMachine elfMachine, ByteOrder byteOrder) { + super(byteOrder); + dwarfStrSection = new DwarfStrSectionImpl(this); + dwarfAbbrevSection = new DwarfAbbrevSectionImpl(this); + dwarfInfoSection = new DwarfInfoSectionImpl(this); + dwarfARangesSection = new DwarfARangesSectionImpl(this); + dwarfLineSection = new DwarfLineSectionImpl(this); + if (elfMachine == ELFMachine.AArch64) { + dwarfFameSection = new DwarfFrameSectionImplAArch64(this); + } else { + dwarfFameSection = new DwarfFrameSectionImplX86_64(this); + } + } + + public DwarfStrSectionImpl getStrSectionImpl() { + return dwarfStrSection; + } + + public DwarfAbbrevSectionImpl getAbbrevSectionImpl() { + return dwarfAbbrevSection; + } + + public DwarfFrameSectionImpl getFrameSectionImpl() { + return dwarfFameSection; + } + + public DwarfInfoSectionImpl getInfoSectionImpl() { + return dwarfInfoSection; + } + + public DwarfARangesSectionImpl getARangesSectionImpl() { + return dwarfARangesSection; + } + + public DwarfLineSectionImpl getLineSectionImpl() { + return dwarfLineSection; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java index 9e5420d8b499..e9000985be4d 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java @@ -32,19 +32,19 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider; import org.graalvm.compiler.debug.DebugContext; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_CIE_id; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_CIE_version; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_advance_loc; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_advance_loc1; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_advance_loc2; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_advance_loc4; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_def_cfa; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_def_cfa_offset; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_nop; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_offset; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_CFA_register; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FRAME_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_LINE_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_CIE_id; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_CIE_version; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_advance_loc; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_advance_loc1; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_advance_loc2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_advance_loc4; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_def_cfa; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_def_cfa_offset; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_nop; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_offset; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_register; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FRAME_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_LINE_SECTION_NAME; /** * Section generic generator for debug_frame section. @@ -53,7 +53,7 @@ public abstract class DwarfFrameSectionImpl extends DwarfSectionImpl { private static final int PADDING_NOPS_ALIGNMENT = 8; - public DwarfFrameSectionImpl(DwarfSections dwarfSections) { + public DwarfFrameSectionImpl(DwarfDebugInfo dwarfSections) { super(dwarfSections); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java index 02d66f83134a..28c3c5f9550f 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java @@ -36,7 +36,7 @@ public class DwarfFrameSectionImplAArch64 extends DwarfFrameSectionImpl { private static final int DW_CFA_SP_IDX = 31; private static final int DW_CFA_PC_IDX = 32; - public DwarfFrameSectionImplAArch64(DwarfSections dwarfSections) { + public DwarfFrameSectionImplAArch64(DwarfDebugInfo dwarfSections) { super(dwarfSections); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java index 9baec098d6c8..1649cbd43ea8 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplX86_64.java @@ -34,7 +34,7 @@ public class DwarfFrameSectionImplX86_64 extends DwarfFrameSectionImpl { private static final int DW_CFA_RSP_IDX = 7; private static final int DW_CFA_RIP_IDX = 16; - public DwarfFrameSectionImplX86_64(DwarfSections dwarfSections) { + public DwarfFrameSectionImplX86_64(DwarfDebugInfo dwarfSections) { super(dwarfSections); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index 4a0ce9f21b45..1221485ff464 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -34,13 +34,13 @@ import java.util.LinkedList; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_CODE_compile_unit; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_CODE_subprogram; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_ABBREV_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_FLAG_true; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_INFO_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_LANG_Java; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_VERSION_2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_compile_unit; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_subprogram; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FLAG_true; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_INFO_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_LANG_Java; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_VERSION_2; /** * Section generator for debug_info section. @@ -51,7 +51,7 @@ public class DwarfInfoSectionImpl extends DwarfSectionImpl { */ private static final int DW_DIE_HEADER_SIZE = 11; - public DwarfInfoSectionImpl(DwarfSections dwarfSections) { + public DwarfInfoSectionImpl(DwarfDebugInfo dwarfSections) { super(dwarfSections); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java index 91a433f69347..15f97e9df7cd 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java @@ -38,9 +38,9 @@ import java.util.Map; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_LINE_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_STR_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_VERSION_2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_LINE_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_STR_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_VERSION_2; /** * Section generator for debug_line section. @@ -131,7 +131,7 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { */ private static final byte DW_LNE_define_file = 3; - DwarfLineSectionImpl(DwarfSections dwarfSections) { + DwarfLineSectionImpl(DwarfDebugInfo dwarfSections) { super(dwarfSections); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java index f13d2d9fd67f..fad408696d96 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java @@ -39,19 +39,19 @@ import java.util.Map; import java.util.Set; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.TEXT_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.TEXT_SECTION_NAME; /** * A class from which all DWARF debug sections inherit providing common behaviours. */ public abstract class DwarfSectionImpl extends BasicProgbitsSectionImpl { - protected DwarfSections dwarfSections; + protected DwarfDebugInfo dwarfSections; protected boolean debug = false; protected long debugTextBase = 0; protected long debugAddress = 0; protected int debugBase = 0; - public DwarfSectionImpl(DwarfSections dwarfSections) { + public DwarfSectionImpl(DwarfDebugInfo dwarfSections) { this.dwarfSections = dwarfSections; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java index a371c3a772a8..ee68bcb71050 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java @@ -30,14 +30,14 @@ import com.oracle.objectfile.debugentry.StringEntry; import org.graalvm.compiler.debug.DebugContext; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.DW_STR_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfSections.TEXT_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_STR_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.TEXT_SECTION_NAME; /** * Generator for debug_str section. */ public class DwarfStrSectionImpl extends DwarfSectionImpl { - public DwarfStrSectionImpl(DwarfSections dwarfSections) { + public DwarfStrSectionImpl(DwarfDebugInfo dwarfSections) { super(dwarfSections); } From 261045f807bdc6649c56fdbe8f93cd54b52e96a5 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Tue, 31 Mar 2020 09:25:08 +0100 Subject: [PATCH 9/9] Add test of debuginfo generation to gate tests --- .travis.yml | 1 + substratevm/DEBUGINFO.md | 30 +++++++++---------- substratevm/mx.substratevm/mx_substratevm.py | 14 +++++++++ .../objectfile/debugentry/DebugInfoBase.java | 22 +++++++++++--- .../oracle/objectfile/elf/ELFObjectFile.java | 16 +++++----- 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/.travis.yml b/.travis.yml index cc2cdc43444b..f741a5e210d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,7 @@ matrix: - env: JDK="jdk8" GATE="style,fullbuild" PRIMARY="substratevm" - env: JDK="jdk8" GATE="build,test" PRIMARY="compiler" - env: JDK="jdk8" GATE="build,test,helloworld" PRIMARY="substratevm" + - env: JDK="jdk8" GATE="build,test,helloworld_debug" PRIMARY="substratevm" - env: JDK="jdk8" GATE="build,bootstraplite" PRIMARY="compiler" - env: JDK="jdk8" GATE="style,fullbuild,sulongBasic" PRIMARY="sulong" addons: diff --git a/substratevm/DEBUGINFO.md b/substratevm/DEBUGINFO.md index 4ea2b3f640c3..af5d0ed297d3 100644 --- a/substratevm/DEBUGINFO.md +++ b/substratevm/DEBUGINFO.md @@ -1,5 +1,5 @@ -Using the ptototype debug info feature --------------------------------------- +Using the debug info feature +---------------------------- To add debug info to a generated native image add flag -H:GenerateDebugInfo= to the native image command line (where N is @@ -10,8 +10,8 @@ debug info). For example, $ mx native-image -H:GenerateDebugInfo=1 Hello The resulting image should contain code (method) debug records in a -format gdb understands (VS support is still under development). At -present it makes no difference which positive value is supplied as +format gdb understands (Windows support is still under development). +At present it makes no difference which positive value is supplied as argument to the GenerateDebugInfo option. The GenerateDebugInfo option also enables caching of sources for any @@ -30,7 +30,7 @@ uses the current JAVA_HOME to locate the JDK src.zip when searching for JDK runtime sources. It also uses entries in the classpath to suggest locations for GraalVM source files and application source files (see below for precise details of the scheme used to identify -source locations). However, source layouts do vary and it may no tbe +source locations). However, source layouts do vary and it may not be possible to find all sources. Hence, users can specify the location of source files explicitly on the command line using option DebugInfoSourceSearchPath: @@ -61,7 +61,7 @@ Note that in both the examples above the DebugInfoSourceSearchPath options are actually redundant. In the first case the classpath entries for apps/hello/classes and apps/greeter/classes will be used to derive the default search roots apps/hello/src and -apps/greeter/src. In the second case classpath entires +apps/greeter/src. In the second case classpath entries apps/target/hello.jar and apps/target/greeter.jar will be used to derive the default search roots apps/target/hello-sources.jar and apps/target/greeter-sources.jar. @@ -90,7 +90,7 @@ achieve this by accumulating the relevant sources in a suitably structured file cache. The native image generator uses different strategies to locate source -files for JDK runtime classes, GraalVM classses and application source +files for JDK runtime classes, GraalVM classes and application source classes for inclusion in the local sources cache. It identifies which strategy to use based on the package name of the class. So, for example, packages starting with java.* or jdk.* are JDK classes; @@ -180,7 +180,7 @@ sources are being included. You can also add extra directories to the search path. Note that gdb does not understand zip format file systems so any extra entries you add must identify a directory tree containing the relevant -sources. Once again. top leel entries in the directory added to the +sources. Once again. top level entries in the directory added to the search path must correspond to the top level package for the classes whose sources are being included. @@ -193,13 +193,13 @@ Checking debug info on Linux ---------------------------- n.b. this is only of interest to those who want to understand how the -debug info implemetation works or want to trouble shoot problems +debug info implementation works or want to trouble shoot problems encountered during debugging that might relate to the debug info encoding. -The objdump command can be used to display the dbeug info embedded +The objdump command can be used to display the debug info embedded into a native image. The following commands (which all assume the -target binary is called hello) can be used to display all currentyl +target binary is called hello) can be used to display all currently generated content: $ objdump --dwarf=info hello > info @@ -212,7 +212,7 @@ generated content: The *info* section includes details of all compiled Java methods. -The *abbrev* sectio defines the layout of records in the info section +The *abbrev* section defines the layout of records in the info section that describe Java files (compilation units) and methods. The *ranges* section details the start and end addresses of method @@ -222,8 +222,8 @@ The *decodedline* section maps subsegments of method code range segments to files and line numbers. This mapping includes entries for files and line numbers for inlined methods. -The *rawline* segment provides deatails of how the line table is -generated using DWARF state machine instuctions that encode file, +The *rawline* segment provides details of how the line table is +generated using DWARF state machine instructions that encode file, line and address transitions. The *str* section provides a lookup table for strings referenced @@ -244,7 +244,7 @@ Currently supported targets The prototype is currently implemented only for gdb on Linux. - - Linux/x86_64 suppoort has been tested and should work + - Linux/x86_64 support has been tested and should work correctly. - Linux/AArch64 support is present but has not yet been fully diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 1a91d5c6a317..99f2b3f11561 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -359,6 +359,7 @@ def __getattr__(self, name): GraalTags = Tags([ 'helloworld', + 'helloworld_debug', 'test', 'maven', 'js', @@ -468,6 +469,19 @@ def svm_gate_body(args, tasks): cinterfacetutorial([]) clinittest([]) + with Task('image demos debuginfo', tasks, tags=[GraalTags.helloworld_debug]) as t: + if t: + if svm_java8(): + javac_image(['--output-path', svmbuild_dir(), '-H:GenerateDebugInfo=1']) + javac_command = ['--javac-command', ' '.join(javac_image_command(svmbuild_dir())), '-H:GenerateDebugInfo=1'] + else: + # Building javac image currently only supported for Java 8 + javac_command = ['-H:GenerateDebugInfo=1'] + helloworld(['--output-path', svmbuild_dir()] + javac_command) + helloworld(['--output-path', svmbuild_dir(), '--shared', '-H:GenerateDebugInfo=1']) # Build and run helloworld as shared library + cinterfacetutorial(['-H:GenerateDebugInfo=1']) + clinittest([]) + with Task('native unittests', tasks, tags=[GraalTags.test]) as t: if t: with tempfile.NamedTemporaryFile(mode='w') as blacklist: diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index f9254549b817..9497dbe2bd15 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -35,9 +35,10 @@ 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; @@ -45,7 +46,7 @@ public abstract class DebugInfoBase { * A table listing all known strings, some of which may be marked for insertion into the * debug_str section. */ - protected StringTable stringTable = new StringTable(); + 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. @@ -102,6 +103,7 @@ public abstract class DebugInfoBase { public DebugInfoBase(ByteOrder byteOrder) { this.byteOrder = byteOrder; } + /** * Entry point allowing ELFObjectFile to pass on information about types, code and heap data. * @@ -167,6 +169,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { * String name = debugDataInfo.toString(); } */ } + private ClassEntry ensureClassEntry(Range range) { String className = range.getClassName(); /* @@ -185,6 +188,7 @@ private ClassEntry ensureClassEntry(Range range) { assert classEntry.getClassName().equals(className); return classEntry; } + private FileEntry ensureFileEntry(Range range) { String fileName = range.getFileName(); if (fileName == null) { @@ -213,11 +217,13 @@ private FileEntry ensureFileEntry(Range range) { } return fileEntry; } + private void addRange(Range primaryRange, List 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(); @@ -225,13 +231,15 @@ private void addSubRange(Range primaryRange, Range subrange) { ClassEntry classEntry = primaryClassesIndex.get(className); FileEntry subrangeFileEntry = ensureFileEntry(subrange); /* - * The primary range should already have been seen and associated with a primary class entry. + * 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; @@ -243,24 +251,30 @@ private DirEntry ensureDirEntry(Path filePath) { } return dirEntry; } + /* Accessors to query the debug info model. */ public ByteOrder getByteOrder() { return byteOrder; } + public LinkedList getPrimaryClasses() { return primaryClasses; } + @SuppressWarnings("unused") public LinkedList 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. * diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java index 740779f08408..729ca948b6cd 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java @@ -1183,15 +1183,13 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { newUserDefinedSection(elfARangesSectionImpl.getSectionName(), elfARangesSectionImpl); newUserDefinedSection(elfLineSectionImpl.getSectionName(), elfLineSectionImpl); /* - * The byte[] for each implementation's content are created and - * written under getOrDecideContent. Doing that ensures that all - * dependent sections are filled in and then sized according to the - * declared dependencies. However, if we leave it at that then - * associated reloc sections only get created when the first reloc - * is inserted during content write that's too late for them to have - * layout constraints included in the layout decision set and causes - * an NPE during reloc section write. So we need to create the relevant - * reloc sections here in advance. + * The byte[] for each implementation's content are created and written under + * getOrDecideContent. Doing that ensures that all dependent sections are filled in and then + * sized according to the declared dependencies. However, if we leave it at that then + * associated reloc sections only get created when the first reloc is inserted during + * content write that's too late for them to have layout constraints included in the layout + * decision set and causes an NPE during reloc section write. So we need to create the + * relevant reloc sections here in advance. */ elfStrSectionImpl.getOrCreateRelocationElement(false); elfAbbrevSectionImpl.getOrCreateRelocationElement(false);