blob: 724459ec2cd9c6cb1b9b920bd46814a194bdc298 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sysml.debug;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.Map.Entry;
import org.apache.sysml.parser.DMLProgram;
import org.apache.sysml.runtime.controlprogram.ExternalFunctionProgramBlock;
import org.apache.sysml.runtime.controlprogram.ForProgramBlock;
import org.apache.sysml.runtime.controlprogram.FunctionProgramBlock;
import org.apache.sysml.runtime.controlprogram.IfProgramBlock;
import org.apache.sysml.runtime.controlprogram.Program;
import org.apache.sysml.runtime.controlprogram.ProgramBlock;
import org.apache.sysml.runtime.controlprogram.WhileProgramBlock;
import org.apache.sysml.runtime.instructions.Instruction;
import org.apache.sysml.runtime.instructions.MRJobInstruction;
import org.apache.sysml.runtime.instructions.cp.BreakPointInstruction;
import org.apache.sysml.runtime.instructions.cp.CPInstruction;
import org.apache.sysml.runtime.instructions.cp.BreakPointInstruction.BPINSTRUCTION_STATUS;
import org.apache.sysml.runtime.instructions.spark.SPInstruction;
/**
* This class contains the parsed and compiled DML script w/ hops, lops and runtime program.
* Additionally, it provides an interface for managing breakpoints and disassembling a DML script.
* Note: ONLY USED FOR DEBUGGING PURPOSES
*/
public class DMLDebuggerProgramInfo
{
public Program rtprog; //DML runtime program
private TreeMap<Integer, ArrayList<Instruction>> disassembler; //map between DML program line numbers and corresponding runtime instruction(s)
private int prevLineNum = 0; //used for approximating line numbers for instructions (if necessary)
private long instID = 1; //runtime instruction ID
private String location=null; //DML program namespace and function name
/**
* Constructor for DMLDebuggerProgramInfo class.
*/
public DMLDebuggerProgramInfo() {
disassembler = new TreeMap<Integer, ArrayList<Instruction>>();
}
/**
* Getter for DML script to instruction map field
* @return DMLInstMap Map between DML script line numbers and runtime instruction(s)
*/
public TreeMap<Integer, ArrayList<Instruction>> getDMLInstMap() {
this.setDMLInstMap();
return disassembler;
}
/**
* Access breakpoint instruction at specified line number in runtime program (if valid)
* @param lineNumber Location for breakpoint operation
* @param op Breakpoint operation (op=0 for create, op=1 for enable/disable, op=2 for delete)
* @param status Current breakpoint status
*/
public void accessBreakpoint(int lineNumber, int op, BPINSTRUCTION_STATUS status)
{
if (this.rtprog != null)
{
//Functions: For each function program block (if any), get instructions corresponding to each line number
HashMap<String,FunctionProgramBlock> funcMap = this.rtprog.getFunctionProgramBlocks();
if (funcMap != null && !funcMap.isEmpty() ) {
for (Entry<String, FunctionProgramBlock> e : funcMap.entrySet())
{
location = e.getKey();
FunctionProgramBlock fpb = e.getValue();
if(fpb instanceof ExternalFunctionProgramBlock)
continue;
else
{
for (ProgramBlock pb : fpb.getChildBlocks())
accessProgramBlockBreakpoint(pb, lineNumber, op, status);
}
}
}
//Main program: for each program block, get instructions corresponding to current line number (if any)
location=DMLProgram.constructFunctionKey(DMLProgram.DEFAULT_NAMESPACE, "main");
for (ProgramBlock pb : this.rtprog.getProgramBlocks())
{
if (pb != null)
accessProgramBlockBreakpoint(pb, lineNumber, op, status);
}
}
}
/**
* Access breakpoint instruction at specified line number in program block (if valid)
* @param pb Current program block
* @param lineNumber Location for inserting breakpoint
* @param status Current breakpoint status
*/
private void accessProgramBlockBreakpoint(ProgramBlock pb, int lineNumber, int op, BPINSTRUCTION_STATUS status)
{
if (pb instanceof FunctionProgramBlock)
{
FunctionProgramBlock fpb = (FunctionProgramBlock)pb;
for( ProgramBlock pbc : fpb.getChildBlocks() )
accessProgramBlockBreakpoint(pbc, lineNumber, op, status);
}
else if (pb instanceof WhileProgramBlock)
{
WhileProgramBlock wpb = (WhileProgramBlock) pb;
this.accesBreakpointInstruction(wpb.getPredicate(), lineNumber, op, status);
for( ProgramBlock pbc : wpb.getChildBlocks() )
accessProgramBlockBreakpoint(pbc, lineNumber, op, status);
}
else if (pb instanceof IfProgramBlock)
{
IfProgramBlock ipb = (IfProgramBlock) pb;
this.accesBreakpointInstruction(ipb.getPredicate(), lineNumber, op, status);
for( ProgramBlock pbc : ipb.getChildBlocksIfBody() )
accessProgramBlockBreakpoint(pbc, lineNumber, op, status);
if( !ipb.getChildBlocksElseBody().isEmpty() )
{
for( ProgramBlock pbc : ipb.getChildBlocksElseBody() )
accessProgramBlockBreakpoint(pbc, lineNumber, op, status);
}
}
else if (pb instanceof ForProgramBlock) //incl parfor
{
ForProgramBlock fpb = (ForProgramBlock) pb;
this.accesBreakpointInstruction(fpb.getFromInstructions(), lineNumber, op, status);
this.accesBreakpointInstruction(fpb.getToInstructions(), lineNumber, op, status);
this.accesBreakpointInstruction(fpb.getIncrementInstructions(), lineNumber, op, status);
for( ProgramBlock pbc : fpb.getChildBlocks() )
accessProgramBlockBreakpoint(pbc, lineNumber, op, status);
}
else
{
this.accesBreakpointInstruction(pb.getInstructions(), lineNumber, op, status);
}
}
/**
* Access breakpoint instruction at specified line number in set of instructions (if valid)
* @param instructions Instructions for current program block
* @param lineNumber Location for inserting breakpoint
* @param status Current breakpoint status
*/
private void accesBreakpointInstruction(ArrayList<Instruction> instructions, int lineNumber, int op, BPINSTRUCTION_STATUS status)
{
for (int i = 0; i < instructions.size() ; i++)
{
Instruction currInst = instructions.get(i);
if (op == 0)
{
if (currInst instanceof MRJobInstruction)
{
MRJobInstruction currMRInst = (MRJobInstruction) currInst;
// Check if current instruction line number correspond to breakpoint line number
if (currMRInst.findMRInstructions(lineNumber)) {
BreakPointInstruction breakpoint = new BreakPointInstruction();
breakpoint.setLocation(currInst);
breakpoint.setInstID(instID++);
breakpoint.setBPInstructionLocation(location);
instructions.add(i, breakpoint);
DMLBreakpointManager.insertBreakpoint(breakpoint, lineNumber);
return;
}
}
else if (currInst instanceof CPInstruction || currInst instanceof SPInstruction)
{
// Check if current instruction line number correspond to breakpoint line number
if (currInst.getLineNum() == lineNumber) {
BreakPointInstruction breakpoint = new BreakPointInstruction();
breakpoint.setLocation(currInst);
breakpoint.setInstID(instID++);
breakpoint.setBPInstructionLocation(location);
instructions.add(i, breakpoint);
DMLBreakpointManager.insertBreakpoint(breakpoint, lineNumber);
return;
}
}
else if (currInst instanceof BreakPointInstruction && currInst.getLineNum() == lineNumber)
{
BreakPointInstruction breakpoint = (BreakPointInstruction) currInst;
breakpoint.setBPInstructionStatus(BPINSTRUCTION_STATUS.ENABLED);
breakpoint.setBPInstructionLocation(location);
instructions.set(i, breakpoint);
DMLBreakpointManager.updateBreakpoint(lineNumber, status);
return;
}
}
else
{
// Check if current instruction line number correspond to breakpoint line number
if (currInst instanceof BreakPointInstruction && currInst.getLineNum() == lineNumber)
{
if (op == 1)
{
BreakPointInstruction breakpoint = (BreakPointInstruction) currInst;
breakpoint.setLocation(currInst);
breakpoint.setInstID(currInst.getInstID());
breakpoint.setBPInstructionStatus(status);
breakpoint.setBPInstructionLocation(location);
instructions.set(i, breakpoint);
DMLBreakpointManager.updateBreakpoint(lineNumber, status);
}
else
{
instructions.remove(i);
DMLBreakpointManager.removeBreakpoint(lineNumber, status);
}
return;
}
}
}
}
/**
* Getter for DML instruction map field (i.e. disassembler functionality)
* For each DML script line, get runtime instructions (if any)
*/
public void setDMLInstMap()
{
disassembler = new TreeMap<Integer, ArrayList<Instruction>>();
if (this.rtprog != null)
{
//Functions: For each function program block (if any), get instructions corresponding to each line number
HashMap<String,FunctionProgramBlock> funcMap = this.rtprog.getFunctionProgramBlocks();
if (funcMap != null && !funcMap.isEmpty() )
{
for (Entry<String, FunctionProgramBlock> e : funcMap.entrySet())
{
FunctionProgramBlock fpb = e.getValue();
if(fpb instanceof ExternalFunctionProgramBlock)
continue;
else
{
for (ProgramBlock pb : fpb.getChildBlocks())
setProgramBlockInstMap(pb);
}
}
}
//Main program: for each program block, get instructions corresponding to current line number (if any)
for (ProgramBlock pb : this.rtprog.getProgramBlocks())
{
if (pb != null)
setProgramBlockInstMap(pb);
}
}
}
/**
* For each program block, get runtime instructions (if any)
* @param pb Current program block
*/
private void setProgramBlockInstMap(ProgramBlock pb)
{
if (pb instanceof FunctionProgramBlock)
{
FunctionProgramBlock fpb = (FunctionProgramBlock)pb;
for( ProgramBlock pbc : fpb.getChildBlocks() )
setProgramBlockInstMap(pbc);
}
else if (pb instanceof WhileProgramBlock)
{
WhileProgramBlock wpb = (WhileProgramBlock) pb;
this.setInstMap(wpb.getPredicate());
for( ProgramBlock pbc : wpb.getChildBlocks() )
setProgramBlockInstMap(pbc);
}
else if (pb instanceof IfProgramBlock)
{
IfProgramBlock ipb = (IfProgramBlock) pb;
this.setInstMap(ipb.getPredicate());
for( ProgramBlock pbc : ipb.getChildBlocksIfBody() )
setProgramBlockInstMap(pbc);
if( !ipb.getChildBlocksElseBody().isEmpty() )
{
for( ProgramBlock pbc : ipb.getChildBlocksElseBody() )
setProgramBlockInstMap(pbc);
}
}
else if (pb instanceof ForProgramBlock) //incl parfor
{
ForProgramBlock fpb = (ForProgramBlock) pb;
this.setInstMap(fpb.getFromInstructions());
this.setInstMap(fpb.getToInstructions());
this.setInstMap(fpb.getIncrementInstructions());
for( ProgramBlock pbc : fpb.getChildBlocks() )
setProgramBlockInstMap(pbc);
}
else
{
this.setInstMap(pb.getInstructions());
}
}
/**
* For each instruction, generate map with corresponding DML
* script line number
* @param instructions Instructions for current program block
*/
private void setInstMap(ArrayList<Instruction> instructions)
{
for (int i = 0; i < instructions.size() ; i++)
{
Instruction currInst = instructions.get(i);
//set instruction unique identifier
if (currInst.getInstID() == 0) {
currInst.setInstID(instID++);
}
if (currInst instanceof MRJobInstruction)
{
MRJobInstruction currMRInst = (MRJobInstruction) currInst;
int min = Integer.MAX_VALUE;
//iterate of MR job instructions to identify minimum line number
for (Integer lineNumber : currMRInst.getMRJobInstructionsLineNumbers())
{
if (lineNumber < min)
min = lineNumber;
}
//set MR job line number
if (min == 0 || min == Integer.MAX_VALUE)
currMRInst.setLocation(prevLineNum, prevLineNum, -1, -1); //last seen instruction line number
else
currMRInst.setLocation(min, min, -1, -1); //minimum instruction line number for this MR job
//insert current MR instruction into corresponding source code line
if (!disassembler.containsKey(currMRInst.getLineNum()))
disassembler.put(currMRInst.getLineNum(), new ArrayList<Instruction>());
disassembler.get(currMRInst.getLineNum()).add(currMRInst);
}
else if (currInst instanceof CPInstruction || currInst instanceof SPInstruction)
{
//if CP instruction line number is not set, then approximate to last seen line number
if (currInst.getLineNum() == 0)
currInst.setLocation(prevLineNum, prevLineNum, -1, -1);
//insert current CP instruction into corresponding source code line
if (!disassembler.containsKey(currInst.getLineNum()))
disassembler.put(currInst.getLineNum(), new ArrayList<Instruction>());
disassembler.get(currInst.getLineNum()).add(currInst);
}
else if (currInst instanceof BreakPointInstruction)
{
BreakPointInstruction currBPInst = (BreakPointInstruction) currInst;
//insert current BP instruction into corresponding source code line
if (!disassembler.containsKey(currBPInst.getLineNum()))
disassembler.put(currBPInst.getLineNum(), new ArrayList<Instruction>());
disassembler.get(currInst.getLineNum()).add(currBPInst);
}
//save instruction's line number as last seen
if (currInst.getLineNum() != 0)
prevLineNum = currInst.getLineNum();
}
}
}