001/*
002 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.code;
024
025import java.lang.reflect.*;
026import java.util.*;
027
028import jdk.internal.jvmci.code.*;
029import jdk.internal.jvmci.code.CodeUtil.*;
030import jdk.internal.jvmci.code.CompilationResult.*;
031import jdk.internal.jvmci.service.*;
032
033/**
034 * {@link HexCodeFile} based implementation of {@link DisassemblerProvider}.
035 */
036@ServiceProvider(DisassemblerProvider.class)
037public class HexCodeFileDisassemblerProvider implements DisassemblerProvider {
038
039    @Override
040    public String disassembleCompiledCode(CodeCacheProvider codeCache, CompilationResult compResult) {
041        assert compResult != null;
042        return disassemble(codeCache, compResult, null);
043    }
044
045    public String getName() {
046        return "hcf";
047    }
048
049    @Override
050    public String disassembleInstalledCode(CodeCacheProvider codeCache, CompilationResult compResult, InstalledCode installedCode) {
051        assert installedCode != null;
052        return installedCode.isValid() ? disassemble(codeCache, compResult, installedCode) : null;
053    }
054
055    private static String disassemble(CodeCacheProvider codeCache, CompilationResult compResult, InstalledCode installedCode) {
056        TargetDescription target = codeCache.getTarget();
057        RegisterConfig regConfig = codeCache.getRegisterConfig();
058        byte[] code = installedCode == null ? Arrays.copyOf(compResult.getTargetCode(), compResult.getTargetCodeSize()) : installedCode.getCode();
059        if (code == null) {
060            // Method was deoptimized/invalidated
061            return "";
062        }
063        long start = installedCode == null ? 0L : installedCode.getStart();
064        HexCodeFile hcf = new HexCodeFile(code, start, target.arch.getName(), target.wordSize * 8);
065        if (compResult != null) {
066            HexCodeFile.addAnnotations(hcf, compResult.getAnnotations());
067            addExceptionHandlersComment(compResult, hcf);
068            Register fp = regConfig.getFrameRegister();
069            RefMapFormatter slotFormatter = new DefaultRefMapFormatter(target.arch, target.wordSize, fp, 0);
070            for (Infopoint infopoint : compResult.getInfopoints()) {
071                if (infopoint instanceof Call) {
072                    Call call = (Call) infopoint;
073                    if (call.debugInfo != null) {
074                        hcf.addComment(call.pcOffset + call.size, CodeUtil.append(new StringBuilder(100), call.debugInfo, slotFormatter).toString());
075                    }
076                    addOperandComment(hcf, call.pcOffset, "{" + codeCache.getTargetName(call) + "}");
077                } else {
078                    if (infopoint.debugInfo != null) {
079                        hcf.addComment(infopoint.pcOffset, CodeUtil.append(new StringBuilder(100), infopoint.debugInfo, slotFormatter).toString());
080                    }
081                    addOperandComment(hcf, infopoint.pcOffset, "{infopoint: " + infopoint.reason + "}");
082                }
083            }
084            for (DataPatch site : compResult.getDataPatches()) {
085                hcf.addOperandComment(site.pcOffset, "{" + site.reference.toString() + "}");
086            }
087            for (Mark mark : compResult.getMarks()) {
088                hcf.addComment(mark.pcOffset, codeCache.getMarkName(mark));
089            }
090        }
091        String hcfEmbeddedString = hcf.toEmbeddedString();
092        return HexCodeFileDisTool.tryDisassemble(hcfEmbeddedString);
093    }
094
095    private static void addExceptionHandlersComment(CompilationResult compResult, HexCodeFile hcf) {
096        if (!compResult.getExceptionHandlers().isEmpty()) {
097            String nl = HexCodeFile.NEW_LINE;
098            StringBuilder buf = new StringBuilder("------ Exception Handlers ------").append(nl);
099            for (CompilationResult.ExceptionHandler e : compResult.getExceptionHandlers()) {
100                buf.append("    ").append(e.pcOffset).append(" -> ").append(e.handlerPos).append(nl);
101                hcf.addComment(e.pcOffset, "[exception -> " + e.handlerPos + "]");
102                hcf.addComment(e.handlerPos, "[exception handler for " + e.pcOffset + "]");
103            }
104            hcf.addComment(0, buf.toString());
105        }
106    }
107
108    private static void addOperandComment(HexCodeFile hcf, int pos, String comment) {
109        String oldValue = hcf.addOperandComment(pos, comment);
110        assert oldValue == null : "multiple comments for operand of instruction at " + pos + ": " + comment + ", " + oldValue;
111    }
112
113    /**
114     * Interface to the tool for disassembling an {@link HexCodeFile#toEmbeddedString() embedded}
115     * {@link HexCodeFile}.
116     */
117    static class HexCodeFileDisTool {
118        static final Method processMethod;
119        static {
120            Method toolMethod = null;
121            try {
122                Class<?> toolClass = Class.forName("com.oracle.max.hcfdis.HexCodeFileDis", true, ClassLoader.getSystemClassLoader());
123                toolMethod = toolClass.getDeclaredMethod("processEmbeddedString", String.class);
124            } catch (Exception e) {
125                // Tool not available on the class path
126            }
127            processMethod = toolMethod;
128        }
129
130        public static String tryDisassemble(String hcfEmbeddedString) {
131            if (processMethod != null) {
132                try {
133                    return (String) processMethod.invoke(null, hcfEmbeddedString);
134                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
135                    // If the tool is available, for now let's be noisy when it fails
136                    throw new InternalError(e);
137                }
138            }
139            return hcfEmbeddedString;
140        }
141    }
142
143}