/**
* ****************************************************************************
* Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com).
* <p>
* This file is part of the Archimulator multicore architectural simulator.
* <p>
* Archimulator is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* <p>
* Archimulator 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 for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with Archimulator. If not, see <http://www.gnu.org/licenses/>.
* ****************************************************************************
*/
package archimulator.analysis;
import archimulator.isa.StaticInstruction;
import archimulator.isa.StaticInstructionType;
import archimulator.os.elf.ElfFile;
import archimulator.os.elf.Symbol;
import archimulator.util.dateTime.DateHelper;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.stream.Collectors;
/**
* ELF file analyzer.
*
* @author Min Cai
*/
public class ElfAnalyzer {
private Program program;
private ElfFile elfFile;
private Map<String, SortedMap<Integer, Instruction>> instructions;
private int programEntry;
/**
* Create an ELF file analyzer.
*
* @param fileName the file name
* @param elfFile the ELF file object
* @param instructions the map of constituent instructions sorted by section names
* @param programEntry the program entry's program counter (PC) value
*/
public ElfAnalyzer(String fileName, ElfFile elfFile, Map<String, SortedMap<Integer, Instruction>> instructions, int programEntry) {
this.elfFile = elfFile;
this.instructions = instructions;
this.programEntry = programEntry;
this.program = new Program(fileName);
}
/**
* Build the control flow graphs.
*/
public void buildControlFlowGraphs() {
this.instructions.forEach((sectionName, instructionsInSection) -> {
Function currentFunction = null;
for (int pc : instructionsInSection.keySet()) {
boolean isEntry = this.elfFile.getLocalFunctionSymbols().containsKey(pc);
if (isEntry) {
Symbol localFunctionSymbol = this.elfFile.getLocalFunctionSymbols().get(pc);
currentFunction = new Function(this.program, sectionName, localFunctionSymbol);
this.program.getFunctions().add(currentFunction);
currentFunction.setNumInstructions(1);
} else {
if (currentFunction != null) {
currentFunction.setNumInstructions(currentFunction.getNumInstructions() + 1);
}
}
}
});
this.instructions.forEach((sectionName, instructionsInSection) -> instructionsInSection.values().forEach(instruction -> instruction.setSectionName(sectionName)));
this.program.getFunctions().forEach(this::createControlFlowGraph);
}
/**
* Create the control flow graph for the specified function.
*
* @param function the function
*/
private void createControlFlowGraph(Function function) {
this.scanBasicBlocks(function);
BasicBlock newBasicBlock = null;
for (int i = 0; i < function.getNumInstructions(); i++) {
int pc = (int) function.getSymbol().getValue() + i * 4;
Instruction instruction = this.instructions.get(function.getSectionName()).get(pc);
if (instruction.isLeader()) {
newBasicBlock = new BasicBlock(function, function.getBasicBlocks().size());
function.getBasicBlocks().add(newBasicBlock);
}
if (newBasicBlock == null) {
throw new IllegalArgumentException();
}
instruction.setBasicBlock(newBasicBlock);
newBasicBlock.getInstructions().add(instruction);
}
this.createControlFlowGraphEdges(function);
function.getBasicBlocks().stream()
.filter(basicBlock -> basicBlock.getType() == BasicBlockType.FUNCTION_CALL)
.forEach(basicBlock -> basicBlock.setType(BasicBlockType.SEQUENTIAL)); //TODO: caller-callee information!!!
//TODO: ... identify loops
}
/**
* Create the control flow graph edges for the specified function.
*
* @param function the function
*/
private void createControlFlowGraphEdges(Function function) {
function.getBasicBlocks().stream().filter(basicBlock -> basicBlock.getInstructions().size() >= 2).forEach(basicBlock -> {
Instruction lastInstruction = basicBlock.getInstructions().get(basicBlock.getInstructions().size() - 2);
StaticInstructionType staticInstructionType = lastInstruction.getStaticInstruction().getMnemonic().getType();
if (staticInstructionType == StaticInstructionType.CONDITIONAL) {
BasicBlock nextBasicBlock = function.getBasicBlocks().get(basicBlock.getNum() + 1);
this.createControlFlowGraphEdge(basicBlock, nextBasicBlock, ControlFlowGraphEdgeType.NOT_TAKEN);
Instruction targetInstruction = this.getTargetInstruction(function, lastInstruction);
if (targetInstruction.getBasicBlock() != null) {
this.createControlFlowGraphEdge(basicBlock, targetInstruction.getBasicBlock(), ControlFlowGraphEdgeType.TAKEN);
} else {
System.out.print(String.format("[%s WARN] Cannot find parent basic block for instruction: %s in section %s\n", DateHelper.toString(new Date()), targetInstruction, targetInstruction.getSectionName()));
}
basicBlock.setType(BasicBlockType.CONDITIONAL);
} else if (staticInstructionType == StaticInstructionType.UNCONDITIONAL) {
Instruction targetInstruction = this.getTargetInstruction(function, lastInstruction);
this.createControlFlowGraphEdge(basicBlock, targetInstruction.getBasicBlock(), ControlFlowGraphEdgeType.TAKEN);
basicBlock.setType(BasicBlockType.UNCONDITIONAL);
} else if (staticInstructionType == StaticInstructionType.FUNCTION_RETURN) {
basicBlock.setType(BasicBlockType.FUNCTION_RETURN);
} else {
if (basicBlock.getNum() < function.getBasicBlocks().size() - 1) {
BasicBlock nextBasicBlock = function.getBasicBlocks().get(basicBlock.getNum() + 1);
this.createControlFlowGraphEdge(basicBlock, nextBasicBlock, ControlFlowGraphEdgeType.NOT_TAKEN);
}
if (staticInstructionType == StaticInstructionType.FUNCTION_CALL) {
basicBlock.setType(BasicBlockType.FUNCTION_CALL);
} else {
basicBlock.setType(BasicBlockType.SEQUENTIAL);
}
}
});
}
/**
* Create a control flow graph edge.
*
* @param from the "from" basic block
* @param to the "to" basic block
* @param edgeType the edge type
*/
private void createControlFlowGraphEdge(BasicBlock from, BasicBlock to, ControlFlowGraphEdgeType edgeType) {
ControlFlowGraphEdge edge = new ControlFlowGraphEdge(from, to);
if (edgeType == ControlFlowGraphEdgeType.NOT_TAKEN) {
from.setOutgoingNotTakenEdge(edge);
} else {
from.setOutgoingTakenEdge(edge);
}
to.getIncomingEdges().add(edge);
}
/**
* Scan the list of basic blocks for the specified function.
*
* @param function the function
*/
private void scanBasicBlocks(Function function) {
this.instructions.get(function.getSectionName()).get((int) function.getSymbol().getValue()).setLeader(true, 0);
for (int i = 0; i < function.getNumInstructions(); i++) {
int pc = (int) function.getSymbol().getValue() + i * 4;
Instruction instruction = this.instructions.get(function.getSectionName()).get(pc);
Instruction nextNextInstruction = this.instructions.get(function.getSectionName()).get(pc + 8);
StaticInstructionType staticInstructionType = instruction.getStaticInstruction().getMnemonic().getType();
if (staticInstructionType == StaticInstructionType.CONDITIONAL || staticInstructionType == StaticInstructionType.UNCONDITIONAL) {
Instruction targetInstruction = this.getTargetInstruction(function, instruction);
if (!targetInstruction.isLeader()) {
targetInstruction.setLeader(true, 1);
}
if (nextNextInstruction != null && !nextNextInstruction.isLeader()) {
nextNextInstruction.setLeader(true, 2);
}
} else if (staticInstructionType == StaticInstructionType.FUNCTION_CALL) {
if (nextNextInstruction != null && !nextNextInstruction.isLeader()) {
nextNextInstruction.setLeader(true, 3);
}
} else if (staticInstructionType == StaticInstructionType.FUNCTION_RETURN) {
if (nextNextInstruction != null && i < function.getNumInstructions() - 2 && !nextNextInstruction.isLeader()) {
nextNextInstruction.setLeader(true, 4);
}
}
}
}
/**
* Dump the analysis result using the specified writer.
*
* @param writer the writer
*/
public void dumpAnalysisResult(Writer writer) {
PrintWriter pw = new PrintWriter(writer);
for (Function function : this.program.getFunctions()) {
pw.println(String.format("0x%08x <%s>: ", function.getBasicBlocks().get(0).getInstructions().get(0).getPc(), function.getSymbol().getName()));
for (BasicBlock basicBlock : function.getBasicBlocks()) {
pw.print("\tbb" + basicBlock.getNum() + ": \t; type = " + basicBlock.getType() + "; ");
pw.print(" preds = ");
List<String> preds = basicBlock.getIncomingEdges().stream().map(incomingEdge -> "bb" + incomingEdge.getFrom().getNum()).collect(Collectors.toList());
pw.println(StringUtils.join(preds, ", "));
for (Instruction instruction : basicBlock.getInstructions()) {
pw.println("\t\t" + instruction + (instruction.isLeader() ? " <leader:" + instruction.getLeaderType() + ">" : ""));
}
pw.println();
}
}
pw.close();
try {
writer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Get the target instruction for the specified function and instruction.
*
* @param function the function
* @param instruction the instruction
* @return the target instruction
*/
private Instruction getTargetInstruction(Function function, Instruction instruction) {
int targetPc = StaticInstruction.getTargetPcForControl(instruction.getPc() + 4, instruction.getStaticInstruction().getMachineInstruction(), instruction.getStaticInstruction().getMnemonic());
return this.instructions.get(function.getSectionName()).get(targetPc);
}
/**
* Get the program.
*
* @return the program
*/
public Program getProgram() {
return program;
}
/**
* Get the ELF file.
*
* @return the ELF file
*/
public ElfFile getElfFile() {
return elfFile;
}
/**
* Get the map of constituent instructions sorted by section names.
*
* @return the map of constituent instructions sorted by section names
*/
public Map<String, SortedMap<Integer, Instruction>> getInstructions() {
return instructions;
}
/**
* Get the program entry's program counter (PC) value,
*
* @return the program entry's program counter (PC) value
*/
public int getProgramEntry() {
return programEntry;
}
}