/* * 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 op Breakpoint operation * @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 op Breakpoint operation * @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(); } } }