< prev index next >

src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotObjdumpDisassemblerProvider.java

Print this page

        

@@ -37,10 +37,11 @@
 
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.code.CompilationResult.CodeAnnotation;
 import org.graalvm.compiler.code.DisassemblerProvider;
 import org.graalvm.compiler.serviceprovider.ServiceProvider;
+import org.graalvm.util.CollectionsUtil;
 
 import jdk.vm.ci.code.CodeCacheProvider;
 import jdk.vm.ci.code.CodeUtil;
 import jdk.vm.ci.code.CodeUtil.DefaultRefMapFormatter;
 import jdk.vm.ci.code.CodeUtil.RefMapFormatter;

@@ -54,32 +55,35 @@
 import jdk.vm.ci.code.site.Mark;
 import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider;
 import jdk.vm.ci.services.Services;
 
 /**
- * This disassembles the code immediatly with objdump.
+ * A provider that uses the {@code GNU objdump} utility to disassemble code.
  */
 @ServiceProvider(DisassemblerProvider.class)
 public class HotSpotObjdumpDisassemblerProvider extends HotSpotDisassemblerProvider {
 
-    /**
-     * Uses objdump to disassemble the compiled code.
-     */
+    private final String objdump = getObjdump();
+
     @Override
     public String disassembleCompiledCode(CodeCacheProvider codeCache, CompilationResult compResult) {
+        if (objdump == null) {
+            return null;
+        }
         File tmp = null;
         try {
             tmp = File.createTempFile("compiledBinary", ".bin");
             try (FileOutputStream fos = new FileOutputStream(tmp)) {
                 fos.write(compResult.getTargetCode());
             }
+
             String[] cmdline;
             String arch = Services.getSavedProperties().get("os.arch");
-            if (arch.equals("amd64")) {
-                cmdline = new String[]{"objdump", "-D", "-b", "binary", "-M", "x86-64", "-m", "i386", tmp.getAbsolutePath()};
+            if (arch.equals("amd64") || arch.equals("x86_64")) {
+                cmdline = new String[]{objdump, "-D", "-b", "binary", "-M", "x86-64", "-m", "i386", tmp.getAbsolutePath()};
             } else if (arch.equals("aarch64")) {
-                cmdline = new String[]{"objdump", "-D", "-b", "binary", "-m", "aarch64", tmp.getAbsolutePath()};
+                cmdline = new String[]{objdump, "-D", "-b", "binary", "-m", "aarch64", tmp.getAbsolutePath()};
             } else {
                 return null;
             }
 
             Pattern p = Pattern.compile(" *(([0-9a-fA-F]+):\t.*)");

@@ -114,47 +118,103 @@
                 }
             }
 
             Process proc = Runtime.getRuntime().exec(cmdline);
             InputStream is = proc.getInputStream();
+            StringBuilder sb = new StringBuilder();
 
             InputStreamReader isr = new InputStreamReader(is);
-            BufferedReader br = new BufferedReader(isr);
-            String line;
-
-            StringBuilder sb = new StringBuilder();
-            while ((line = br.readLine()) != null) {
-                Matcher m = p.matcher(line);
-                if (m.find()) {
-                    int address = Integer.parseInt(m.group(2), 16);
-                    String annotation = annotations.get(address);
-                    if (annotation != null) {
-                        annotation = annotation.replace("\n", "\n; ");
-                        sb.append("; ").append(annotation).append('\n');
+            try (BufferedReader br = new BufferedReader(isr)) {
+                String line;
+                while ((line = br.readLine()) != null) {
+                    Matcher m = p.matcher(line);
+                    if (m.find()) {
+                        int address = Integer.parseInt(m.group(2), 16);
+                        String annotation = annotations.get(address);
+                        if (annotation != null) {
+                            annotation = annotation.replace("\n", "\n; ");
+                            sb.append("; ").append(annotation).append('\n');
+                        }
+                        line = m.replaceAll("0x$1");
                     }
-                    line = m.replaceAll("0x$1");
+                    sb.append(line).append("\n");
                 }
-                sb.append(line).append("\n");
             }
-            BufferedReader ebr = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
-            while ((line = ebr.readLine()) != null) {
-                System.err.println(line);
+            try (BufferedReader ebr = new BufferedReader(new InputStreamReader(proc.getErrorStream()))) {
+                String errLine = ebr.readLine();
+                if (errLine != null) {
+                    System.err.println("Error output from executing: " + CollectionsUtil.mapAndJoin(cmdline, e -> quoteShellArg(String.valueOf(e)), " "));
+                    System.err.println(errLine);
+                    while ((errLine = ebr.readLine()) != null) {
+                        System.err.println(errLine);
+                    }
+                }
             }
-            ebr.close();
             return sb.toString();
         } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        } finally {
             if (tmp != null) {
                 tmp.delete();
             }
-            e.printStackTrace();
-            return null;
         }
     }
 
+    /**
+     * Pattern for a single shell command argument that does not need to quoted.
+     */
+    private static final Pattern SAFE_SHELL_ARG = Pattern.compile("[A-Za-z0-9@%_\\-\\+=:,\\./]+");
+
+    /**
+     * Reliably quote a string as a single shell command argument.
+     */
+    public static String quoteShellArg(String arg) {
+        if (arg.isEmpty()) {
+            return "\"\"";
+        }
+        Matcher m = SAFE_SHELL_ARG.matcher(arg);
+        if (m.matches()) {
+            return arg;
+        }
+        // See http://stackoverflow.com/a/1250279
+        return "'" + arg.replace("'", "'\"'\"'") + "'";
+    }
+
+    /**
+     * Searches for a valid GNU objdump executable.
+     */
+    private static String getObjdump() {
+        // On macOS, `brew install binutils` will provide
+        // an executable named gobjdump
+        for (String candidate : new String[]{"objdump", "gobjdump"}) {
+            try {
+                String[] cmd = {candidate, "--version"};
+                Process proc = Runtime.getRuntime().exec(cmd);
+                InputStream is = proc.getInputStream();
+                int exitValue = proc.waitFor();
+                if (exitValue == 0) {
+                    byte[] buf = new byte[is.available()];
+                    int pos = 0;
+                    while (pos < buf.length) {
+                        int read = is.read(buf, pos, buf.length - pos);
+                        pos += read;
+                    }
+                    String output = new String(buf);
+                    if (output.contains("GNU objdump")) {
+                        return candidate;
+                    }
+                }
+            } catch (IOException | InterruptedException e) {
+            }
+        }
+        return null;
+    }
+
     private static void putAnnotation(Map<Integer, String> annotations, int idx, String txt) {
-        String newAnnoation = annotations.getOrDefault(idx, "") + "\n" + txt;
-        annotations.put(idx, newAnnoation);
+        String newAnnotation = annotations.getOrDefault(idx, "") + "\n" + txt;
+        annotations.put(idx, newAnnotation);
     }
 
     @Override
     public String disassembleInstalledCode(CodeCacheProvider codeCache, CompilationResult compResult, InstalledCode code) {
         return ((HotSpotCodeCacheProvider) codeCache).disassemble(code);
< prev index next >