Debug (breakpoints, step) P-code for both AS1/2 and AS3

This commit is contained in:
Jindra Petřík
2015-12-06 06:29:08 +01:00
parent e2c299afa0
commit 0724b4a3f0
75 changed files with 670 additions and 264 deletions

View File

@@ -3149,13 +3149,54 @@ public final class SWF implements SWFContainerItem, Timelined {
* @param telemetry Enable telemetry info?
*/
public void enableDebugging(boolean injectAS3Code, File decompileDir, boolean telemetry) {
enableDebugging(injectAS3Code, decompileDir, telemetry, false);
}
/**
* Injects debugline and debugfile instructions to AS3 P-code (lines of
* P-code)
*/
public void injectAS3PcodeDebugInfo() {
List<ScriptPack> packs = getAS3Packs();
for (ScriptPack s : packs) {
int abcIndex = s.allABCs.indexOf(s.abc);
if (s.isSimple) {
s.injectPCodeDebugInfo(abcIndex);
}
}
}
/**
* Injects debugline and debugfile instructions to AS3 code
*
* @param decompileDir Directory to set file information paths
*/
public void injectAS3DebugInfo(File decompileDir) {
List<ScriptPack> packs = getAS3Packs();
for (ScriptPack s : packs) {
if (s.isSimple) {
s.injectDebugInfo(decompileDir);
}
}
}
/**
* Enables debugging. Adds tags to enable debugging and injects debugline
* and debugfile instructions to AS3 code. Optionally enables Telemetry
*
* @param injectAS3Code Modify AS3 code with debugfile / debugline ?
* @param decompileDir Directory to virtual decompile (will affect
* debugfile)
* @param telemetry Enable telemetry info?
* @param pcodeLevel inject Pcode lines instead of decompiled lines
*/
public void enableDebugging(boolean injectAS3Code, File decompileDir, boolean telemetry, boolean pcodeLevel) {
if (injectAS3Code) {
List<ScriptPack> packs = getAS3Packs();
for (ScriptPack s : packs) {
if (s.isSimple) {
s.injectDebugInfo(decompileDir);
}
if (pcodeLevel) {
injectAS3PcodeDebugInfo();
} else {
injectAS3DebugInfo(decompileDir);
}
}
@@ -3233,6 +3274,82 @@ public final class SWF implements SWFContainerItem, Timelined {
return r;
}
public boolean generatePCodeSwdFile(File file, Map<String, Set<Integer>> breakpoints) throws IOException {
DebugIDTag dit = getDebugId();
if (dit == null) {
return false;
}
List<SWD.DebugItem> items = new ArrayList<>();
Map<String, ASMSource> asms = getASMs(true);
try {
items.add(new SWD.DebugId(dit.debugId));
} catch (Throwable t) {
Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "message", t);
return false;
}
int moduleId = 0;
List<String> names = new ArrayList<>(asms.keySet());
Collections.sort(names);
for (String name : names) {
moduleId++;
String sname = "#PCODE " + name;
int bitmap = SWD.bitmapAction;
items.add(new SWD.DebugScript(moduleId, bitmap, sname, ""));
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
try {
asms.get(name).getASMSource(ScriptExportMode.PCODE, writer, asms.get(name).getActions());
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, null, ex);
}
List<Highlighting> hls = writer.instructionHilights;
Map<Integer, Integer> offsetToLine = new TreeMap<>();
String txt = writer.toString();
txt = txt.replace("\r", "");
int line = 1;
for (int i = 0; i < txt.length(); i++) {
Highlighting h = Highlighting.searchPos(hls, i);
if (h != null) {
int of = (int) h.getProperties().fileOffset;
if (of > -1 && !offsetToLine.containsKey(of) && !offsetToLine.containsValue(line)) {
offsetToLine.put(of, line);
}
}
if (txt.charAt(i) == '\n') {
line++;
}
}
for (int ofs : offsetToLine.keySet()) {
items.add(new SWD.DebugOffset(moduleId, offsetToLine.get(ofs), ofs));
}
if (breakpoints.containsKey(sname)) {
Set<Integer> bplines = breakpoints.get(sname);
for (int bpline : bplines) {
if (offsetToLine.containsValue(bpline)) {
try {
SWD.DebugBreakpoint dbp = new SWD.DebugBreakpoint(moduleId, bpline);
items.add(dbp);
} catch (IllegalArgumentException iex) {
Logger.getLogger(SWF.class.getName()).log(Level.WARNING, "Cannot generate breakpoint to SWD: {0}", iex.getMessage());
}
}
}
}
}
SWD swd = new SWD(7, items);
try (FileOutputStream fis = new FileOutputStream(file)) {
swd.saveTo(fis);
}
return true;
}
public boolean generateSwdFile(File file, Map<String, Set<Integer>> breakpoints) throws IOException {
DebugIDTag dit = getDebugId();
if (dit == null) {
@@ -3243,15 +3360,10 @@ public final class SWF implements SWFContainerItem, Timelined {
try {
items.add(new SWD.DebugId(dit.debugId));
Random rnd = new Random();
//Map<String, Integer> moduleIds = new HashMap<>();
List<SWD.DebugOffset> swdOffsets = new ArrayList<>();
List<SWD.DebugBreakpoint> swfBps = new ArrayList<>();
int moduleId = 0;
List<String> names = new ArrayList<>(asms.keySet());
Collections.sort(names);
//Collections.reverse(names);
for (String name : names) {
List<SWD.DebugRegisters> regitems = new ArrayList<>();
moduleId++;

View File

@@ -37,6 +37,8 @@ import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings;
import com.jpexs.decompiler.flash.helpers.FileTextWriter;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.HighlightedText;
import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
import com.jpexs.decompiler.flash.helpers.NulWriter;
import com.jpexs.decompiler.flash.helpers.hilight.Highlighting;
import com.jpexs.decompiler.flash.tags.Tag;
@@ -510,4 +512,94 @@ public class ScriptPack extends AS3ClassTreeItem {
((Tag) abc.parentTag).setModified(true);
}
public void injectPCodeDebugInfo(int abcIndex) {
Map<Integer, String> bodyToIdentifier = new HashMap<>();
try {
CachedDecompilation decompiled = SWF.getCached(this);
String txt = decompiled.text;
txt = txt.replace("\r", "");
for (int i = 0; i < txt.length(); i++) {
blk:
{
Highlighting sh = Highlighting.searchPos(decompiled.specialHilights, i);
Highlighting cls = Highlighting.searchPos(decompiled.classHilights, i);
Highlighting trt = Highlighting.searchPos(decompiled.traitHilights, i);
Highlighting method = Highlighting.searchPos(decompiled.methodHilights, i);
if (method == null) {
break blk;
}
int classIndex = cls == null ? -1 : (int) cls.getProperties().index;
int methodIndex = (int) method.getProperties().index;
int bodyIndex = abc.findBodyIndex(methodIndex);
if (bodyIndex == -1) {
break blk;
}
Trait trait;
int traitIndex = -10;
if (trt != null && cls != null) {
traitIndex = (int) trt.getProperties().index;
trait = abc.findTraitByTraitId(classIndex, traitIndex);
if (((trait instanceof TraitMethodGetterSetter) && (((TraitMethodGetterSetter) trait).method_info != methodIndex))
|| ((trait instanceof TraitFunction) && (((TraitFunction) trait).method_info != methodIndex))) {
continue; //inner anonymous function - ignore. TODO: make work
}
}
bodyToIdentifier.put(bodyIndex, "abc:" + abcIndex + ",script:" + scriptIndex + ",class:" + classIndex + ",trait:" + traitIndex + ",method:" + methodIndex + ",body:" + bodyIndex);
}
}
} catch (InterruptedException ex) {
Logger.getLogger(ScriptPack.class.getName()).log(Level.SEVERE, "Cannot decompile", ex);
}
int scriptInitBody = abc.findBodyIndex(abc.script_info.get(scriptIndex).init_index);
if (!bodyToIdentifier.containsKey(scriptInitBody)) {
bodyToIdentifier.put(scriptInitBody, "abc:" + abcIndex + ",script:" + scriptIndex + ",class:-1,trait:-3,method:" + abc.script_info.get(scriptIndex).init_index);
}
String pkg = path.packageStr.toString();
String cls = path.className;
for (int bodyIndex : bodyToIdentifier.keySet()) {
String bodyName = bodyToIdentifier.get(bodyIndex);
MethodBody b = abc.bodies.get(bodyIndex);
List<AVM2Instruction> list = b.getCode().code;
int siz = list.size();
for (int i = 0; i < siz; i++) {
b.insertInstruction(i * 2, new AVM2Instruction(0, AVM2Instructions.DebugLine, new int[]{i + 1}));
}
for (int i = 1 /*odd, even are new debuglines*/; i < list.size(); i += 2) {
if (list.get(i).definition instanceof DebugLineIns) {
b.removeInstruction(i);
b.removeInstruction(i - 1); //remove its new debugline too
i -= 2; //for loop to work correctly
} else if (list.get(i).definition instanceof DebugFileIns) {
b.removeInstruction(i);
b.removeInstruction(i - 1);
i -= 2;
} else if (list.get(i).definition instanceof DebugIns) {
b.removeInstruction(i);
b.removeInstruction(i - 1);
i -= 2;
}
}
String filename = "#PCODE " + bodyName + ";" + pkg.replace(".", File.separator) + ";" + cls + ".as";
b.insertInstruction(0, new AVM2Instruction(0, AVM2Instructions.DebugFile, new int[]{abc.constants.getStringId(filename, true)}));
b.setModified();
}
((Tag) abc.parentTag).setModified(true);
}
}

View File

@@ -1263,7 +1263,7 @@ public class AVM2Code implements Cloneable {
if (!ins.isIgnored()) {
if (markOffsets) {
writer.append("", ofs);
writer.append("", ofs, ins.getFileOffset());
}
writer.appendNoHilight(ins.toStringNoAddress(constants, new ArrayList<>()));

View File

@@ -57,6 +57,11 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
private String file;
@Override
public long getFileOffset() {
return -1;
}
@Override
public long getLineOffset() {
return getOffset();

View File

@@ -375,6 +375,11 @@ public abstract class Action implements GraphSourceItem {
return baos2.toByteArray();
}
@Override
public long getFileOffset() {
return fileOffset;
}
/**
* Converts list of Actions to bytes
*
@@ -602,7 +607,7 @@ public abstract class Action implements GraphSourceItem {
//lastPush = false;
}
writer.append("", offset);
writer.append("", offset, a.getFileOffset());
int fixBranch = -1;
if (a instanceof ActionIf) {

View File

@@ -337,7 +337,7 @@ public class ActionPush extends Action {
if (pos > 0) {
writer.appendNoHilight(" ");
}
writer.append(toString(i), getAddress() + pos + 1);
writer.append(toString(i), getAddress() + pos + 1, getFileOffset());
pos++;
}
return writer;

View File

@@ -65,7 +65,7 @@ public class FileTextWriter extends GraphTextWriter implements AutoCloseable {
}
@Override
public FileTextWriter append(String str, long offset) {
public FileTextWriter append(String str, long offset, long fileOffset) {
writeToFile(str);
return this;
}

View File

@@ -171,7 +171,7 @@ public abstract class GraphTextWriter {
public abstract GraphTextWriter append(String str);
public abstract GraphTextWriter append(String str, long offset);
public abstract GraphTextWriter append(String str, long offset, long fileOffset);
public abstract GraphTextWriter appendNoHilight(int i);

View File

@@ -202,6 +202,7 @@ public class HighlightedTextWriter extends GraphTextWriter {
ndata.merge(itemPos.data);
ndata.merge(data);
ndata.offset = src.getOffset() + pos;
ndata.fileOffset = src.getFileOffset();
if (itemPos.startLineItem != null) {
ndata.firstLineOffset = itemPos.startLineItem.getLineOffset();
}
@@ -217,11 +218,12 @@ public class HighlightedTextWriter extends GraphTextWriter {
}
@Override
public HighlightedTextWriter append(String str, long offset) {
public HighlightedTextWriter append(String str, long offset, long fileOffset) {
Highlighting h = null;
if (hilight) {
HighlightData data = new HighlightData();
data.offset = offset;
data.fileOffset = fileOffset;
h = new Highlighting(sb.length() - newLineCount, data, HighlightType.OFFSET, str);
instructionHilights.add(h);
}

View File

@@ -132,7 +132,7 @@ public class NulWriter extends GraphTextWriter {
}
@Override
public NulWriter append(String str, long offset) {
public NulWriter append(String str, long offset, long fileOffset) {
stringAdded = true;
return this;
}

View File

@@ -39,6 +39,8 @@ public class HighlightData implements Cloneable, Serializable {
public long offset;
public long fileOffset = -1;
public long firstLineOffset = -1;
public int regIndex = -1;
@@ -46,7 +48,8 @@ public class HighlightData implements Cloneable, Serializable {
public boolean isEmpty() {
return !declaration && declaredType == null && localName == null
&& subtype == null && specialValue == null
&& index == 0 && offset == 0 && regIndex == -1 && firstLineOffset == -1;
&& index == 0 && offset == 0 && regIndex == -1 && firstLineOffset == -1
&& fileOffset == -1;
}
public void merge(HighlightData data) {
@@ -80,6 +83,9 @@ public class HighlightData implements Cloneable, Serializable {
if (data.firstLineOffset != -1) {
firstLineOffset = data.firstLineOffset;
}
if (data.fileOffset != -1) {
fileOffset = data.fileOffset;
}
}
@Override

View File

@@ -100,7 +100,7 @@ public class Highlighting implements Serializable {
continue;
}
}
if (pos == -1 || (pos >= h.startPos && (pos < h.startPos + h.len))) {
if (pos == -1 || (pos >= h.startPos && ((h.len == 0 && pos == h.startPos) || pos < h.startPos + h.len))) {
if (ret == null || h.startPos > ret.startPos) { //get the closest one
ret = h;
}

View File

@@ -29,6 +29,8 @@ public interface GraphSourceItem extends Serializable, Cloneable {
public int getStackPushCount(BaseLocalData localData, TranslateStack stack);
public long getFileOffset();
public boolean isJump();
public boolean isBranch();