From 32b1de2b6fd43514a5cd03de4623eb1edb4f9377 Mon Sep 17 00:00:00 2001 From: "Keith W. Campbell" Date: Fri, 18 Sep 2020 09:04:36 -0400 Subject: [PATCH] Improve jextract for ELF core files Attempt to un-resolve symbolic links so jdmpview can find files by soname. Close files. [ci skip] Signed-off-by: Keith W. Campbell --- .../com/ibm/dtfj/corereaders/NewElfDump.java | 99 ++++++++++++++----- .../com/ibm/jvm/j9/dump/extract/Main.java | 11 ++- 2 files changed, 86 insertions(+), 24 deletions(-) diff --git a/jcl/src/openj9.dtfj/share/classes/com/ibm/dtfj/corereaders/NewElfDump.java b/jcl/src/openj9.dtfj/share/classes/com/ibm/dtfj/corereaders/NewElfDump.java index db813522de5..3640fc3c28d 100644 --- a/jcl/src/openj9.dtfj/share/classes/com/ibm/dtfj/corereaders/NewElfDump.java +++ b/jcl/src/openj9.dtfj/share/classes/com/ibm/dtfj/corereaders/NewElfDump.java @@ -26,10 +26,11 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; - import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -816,7 +817,8 @@ private static abstract class ElfFile { private short _programHeaderCount = 0; private short _sectionHeaderEntrySize = 0; private short _sectionHeaderCount = 0; - final Set _fileNotes = new HashSet<>(); + // Maps to a set of paths of loaded shared libraries for a particular 'soname'. + final Map> _librariesBySOName = new HashMap<>(); private List _processEntries = new ArrayList<>(); private List _threadEntries = new ArrayList<>(); private List _auxiliaryVectorEntries = new ArrayList<>(); @@ -841,6 +843,10 @@ private static abstract class ElfFile { _offset = offset; } + void close() throws IOException { + _reader.releaseResources(); + } + Iterator processEntries() { return _processEntries.iterator(); } @@ -1008,13 +1014,64 @@ private void readFileNotes(long offset) throws IOException { long wordSizeBytes = addressSize() / 8; long fixedOffset = offset + (2 * wordSizeBytes); long stringsOffset = fixedOffset + (count * 3 * wordSizeBytes); + Set fileNames = new HashSet<>(); _reader.seek(stringsOffset); for (int index = 0; index < count; ++index) { - String file = readString(); + String fileName = readString(); + + fileNames.add(fileName); + } + + for (String fileName : fileNames) { + File file = new File(fileName); + + try (ClosingFileReader fileReader = new ClosingFileReader(file)) { + ElfFile result = elfFileFrom(fileReader, this); + + if (result == null) { + // not an existing ELF file + continue; + } - _fileNotes.add(file); + String soname = result.getSONAME(); + + if (soname != null) { + /* + * Often the file name matches the soname, but some system libraries are + * installed such that the soname is a symbolic link to the latest version + * of that shared library (in the same directory). The file note in this + * situation may refer to the file rather than the symbolic link. We need + * to use the symbolic link (if it exists and refers to the same file) so + * jdmpview can resolve the reference using the soname. + * An example may make this clearer. A recent Linux distribution includes + * the following for soname 'libc.so.6': + * /lib/x86_64-linux-gnu/libc-2.31.so + * /lib/x86_64-linux-gnu/libc.so.6 + * with the latter being a symbolic link to the former. + */ + File sofile = new File(file.getParentFile(), soname); + + if (isSameFile(file, sofile)) { + Set paths = _librariesBySOName.computeIfAbsent(soname, key -> new HashSet<>()); + + paths.add(sofile.getAbsolutePath()); + } + } + + result.close(); + } catch (IOException e) { + // ignore + } + } + } + + private static boolean isSameFile(File file, File sofile) { + try { + return Files.isSameFile(file.toPath(), sofile.toPath()); + } catch (IOException e) { + return false; } } @@ -1603,25 +1660,14 @@ private List readModules(Builder builder, Object addressSpace, String executa continue; } - boolean found = false; - // add all matching names found in the file notes - if (!soname.startsWith(File.separator)) { - int sonameLength = soname.length(); - - for (String name : _file._fileNotes) { - int index = name.length() - sonameLength - 1; + Set libs = _file._librariesBySOName.get(soname); - if ((index > 0) && (name.charAt(index) == File.separatorChar) && name.endsWith(soname)) { - _additionalFileNames.add(name); - found = true; - } - } - } - - // use soname if we could't find something better in the file notes - if (!found) { + if (libs == null || libs.isEmpty()) { + // use soname if we could't find something better in the file notes _additionalFileNames.add(soname); + } else { + _additionalFileNames.addAll(libs); } } catch (Exception ex) { // We can't tell a loaded module from a loaded something else without trying to open it @@ -2209,15 +2255,24 @@ private NewElfDump(ElfFile file, DumpReader reader, boolean isLittleEndian, bool } private ElfFile elfFileFrom(ClosingFileReader file) throws IOException { - file.seek(0); + return elfFileFrom(file, _file); + } + + static ElfFile elfFileFrom(ClosingFileReader file, ElfFile container) throws IOException { byte[] signature = new byte[EI_NIDENT]; + + file.seek(0); file.readFully(signature); + if (ElfFile.isELF(signature)) { DumpReader reader = readerForEndianess(signature[5], signature[4], file); ElfFile result = fileForClass(signature[4], reader); - if (result.getClass().isInstance(_file)) + + if (result.getClass().isInstance(container)) { return result; + } } + return null; } diff --git a/jcl/src/openj9.dtfj/share/classes/com/ibm/jvm/j9/dump/extract/Main.java b/jcl/src/openj9.dtfj/share/classes/com/ibm/jvm/j9/dump/extract/Main.java index 27e1b8030b9..789ff684e69 100644 --- a/jcl/src/openj9.dtfj/share/classes/com/ibm/jvm/j9/dump/extract/Main.java +++ b/jcl/src/openj9.dtfj/share/classes/com/ibm/jvm/j9/dump/extract/Main.java @@ -350,6 +350,14 @@ public static void main(String[] args) { } report("jextract complete."); //$NON-NLS-1$ } + + if (dumper._dump != null) { + try { + dumper._dump.releaseResources(); + } catch (IOException e) { + // ignore + } + } } private static void ensure(boolean condition, String errorMessage) { @@ -567,8 +575,7 @@ private static void createZipFromFileNames(String zipFileName, Iterator byte[] buffer = new byte[ZIP_BUFFER_SIZE]; while (fileNames.hasNext()) { String name = fileNames.next(); - try { - ClosingFileReader in = fileResolver.openFile(name); + try (ClosingFileReader in = fileResolver.openFile(name)) { boolean mvsfile = in.isMVSFile(); String absolute = in.getAbsolutePath(); if (mvsfile || absolute.equals(new File(name).getAbsolutePath())) {