From b05d1a75240735d3c263efb1eb9ae506b1cc0176 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Mon, 11 Aug 2014 18:26:25 +0400 Subject: [PATCH] Adds line CFG to debug information --- .../org/teavm/debugging/DebugInformation.java | 35 ++++++++ .../debugging/DebugInformationBuilder.java | 83 +++++++++++++++++++ .../debugging/DebugInformationEmitter.java | 2 + .../debugging/DebugInformationReader.java | 43 ++++++++++ .../debugging/DebugInformationWriter.java | 34 ++++++++ .../DummyDebugInformationEmitter.java | 4 + .../org/teavm/model/InstructionLocation.java | 23 +++++ .../org/teavm/model/util/ProgramUtils.java | 63 +++++++++++++- 8 files changed, 286 insertions(+), 1 deletion(-) diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java index 58cf889e9..b75fe157a 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java @@ -45,6 +45,7 @@ public class DebugInformation { Mapping methodMapping; Mapping lineMapping; MultiMapping[] variableMappings; + CFG[] controlFlowGraphs; List classesMetadata; public String[] getCoveredSourceFiles() { @@ -121,6 +122,34 @@ public class DebugInformation { return componentByKey(mapping, variableNames, location); } + public SourceLocation[] getFollowingLines(SourceLocation location) { + Integer fileIndex = fileNameMap.get(location.getFileName()); + if (fileIndex == null) { + return null; + } + CFG cfg = controlFlowGraphs[fileIndex]; + if (cfg == null) { + return null; + } + int start = cfg.offsets[location.getLine()]; + int end = cfg.offsets[location.getLine() + 1]; + if (end - start == 1 && cfg.offsets[start] == -1) { + return new SourceLocation[0]; + } else if (start == end) { + return null; + } + SourceLocation[] result = new SourceLocation[end - start]; + for (int i = 0; i < result.length; ++i) { + int line = cfg.lines[i + start]; + if (line >= 0) { + result[i] = new SourceLocation(fileNames[cfg.files[i + start]], line); + } else { + result[i] = null; + } + } + return result; + } + public String getFieldMeaning(String className, String jsName) { Integer classIndex = classNameMap.get(className); if (classIndex == null) { @@ -391,4 +420,10 @@ public class DebugInformation { Integer parentId; Map fieldMap = new HashMap<>(); } + + static class CFG { + int[] lines; + int[] files; + int[] offsets; + } } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java index 981a467bf..ee79b31b4 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java @@ -42,6 +42,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter { private String currentFileName; private int currentClassMetadata = -1; private List classesMetadata = new ArrayList<>(); + private List cfgs = new ArrayList<>(); private int currentLine; public LocationProvider getLocationProvider() { @@ -122,6 +123,26 @@ public class DebugInformationBuilder implements DebugInformationEmitter { metadata.fieldMap.put(jsIndex, fieldIndex); } + @Override + public void addSuccessors(SourceLocation location, SourceLocation[] successors) { + int fileIndex = files.index(location.getFileName()); + if (cfgs.size() <= fileIndex) { + cfgs.addAll(Collections.nCopies(fileIndex - cfgs.size() + 1, null)); + } + CFG cfg = cfgs.get(fileIndex); + if (cfg == null) { + cfg = new CFG(); + cfgs.set(fileIndex, cfg); + } + for (SourceLocation succ : successors) { + if (succ == null) { + cfg.add(location.getLine(), fileIndex, -1); + } else { + cfg.add(location.getLine(), files.index(succ.getFileName()), succ.getLine()); + } + } + } + public DebugInformation getDebugInformation() { if (debugInformation == null) { debugInformation = new DebugInformation(); @@ -156,6 +177,13 @@ public class DebugInformationBuilder implements DebugInformationEmitter { } debugInformation.classesMetadata = builtMetadata; + DebugInformation.CFG[] cfgs = new DebugInformation.CFG[files.list.size()]; + for (int i = 0; i < this.cfgs.size(); ++i) { + if (this.cfgs.get(i) != null) { + cfgs[i] = this.cfgs.get(i).build(); + } + } + debugInformation.rebuildFileDescriptions(); debugInformation.rebuildMaps(); } @@ -293,4 +321,59 @@ public class DebugInformationBuilder implements DebugInformationEmitter { int parentIndex; Map fieldMap = new HashMap<>(); } + + static class CFG { + IntegerArray start = new IntegerArray(1); + IntegerArray next = new IntegerArray(1); + IntegerArray lines = new IntegerArray(1); + IntegerArray files = new IntegerArray(1); + + public void add(int line, int succLine, int succFile) { + while (start.size() <= line) { + start.add(-1); + } + int ptr = start.get(line); + start.set(line, lines.size()); + next.add(ptr); + lines.add(succLine); + files.add(succFile); + } + + public DebugInformation.CFG build() { + int[] offsets = new int[start.size() + 1]; + IntegerArray linesData = new IntegerArray(1); + IntegerArray filesData = new IntegerArray(1); + for (int i = 0; i < start.size(); ++i) { + IntegerArray linesChunk = new IntegerArray(1); + IntegerArray filesChunk = new IntegerArray(1); + int ptr = start.get(i); + while (ptr > 0) { + linesChunk.add(lines.get(ptr)); + filesChunk.add(files.get(ptr)); + ptr = next.get(ptr); + } + long[] pairs = new long[linesChunk.size()]; + for (int j = 0; j < pairs.length; ++j) { + pairs[j] = (filesChunk.get(j) << 32) | linesChunk.get(j); + } + Arrays.sort(pairs); + int distinctSize = 0; + for (int j = 0; j < pairs.length; ++j) { + long pair = pairs[j]; + if (distinctSize == 0 || pair != pairs[distinctSize]) { + pairs[distinctSize++] = pair; + linesData.add((int)(pair >>> 32)); + filesData.add((int)pair); + } + } + offsets[i + 1] = linesData.size(); + } + DebugInformation.CFG cfg = new DebugInformation.CFG(); + cfg.offsets = offsets; + cfg.lines = lines.getAll(); + cfg.files = files.getAll(); + return cfg; + } + } + } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java index 5e9cc499a..8789a3153 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java @@ -36,4 +36,6 @@ public interface DebugInformationEmitter { void addClass(String className, String parentName); void addField(String fieldName, String jsName); + + void addSuccessors(SourceLocation location, SourceLocation[] successors); } \ No newline at end of file diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java index 2558e6a41..11973fa51 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import org.teavm.common.IntegerArray; /** * @@ -46,6 +47,7 @@ class DebugInformationReader { debugInfo.methodMapping = readMapping(); debugInfo.variableMappings = readVariableMappings(debugInfo.variableNames.length); debugInfo.classesMetadata = readClassesMetadata(debugInfo.classNames.length); + debugInfo.controlFlowGraphs = readCFGs(debugInfo.fileNames.length); debugInfo.rebuildFileDescriptions(); debugInfo.rebuildMaps(); return debugInfo; @@ -82,6 +84,47 @@ class DebugInformationReader { return classes; } + private DebugInformation.CFG[] readCFGs(int count) throws IOException { + DebugInformation.CFG[] cfgs = new DebugInformation.CFG[count]; + for (int i = 0; i < count; ++i) { + cfgs[i] = readCFG(i); + } + return cfgs; + } + + private DebugInformation.CFG readCFG(int index) throws IOException { + int[] offsets = new int[readUnsignedNumber() + 1]; + IntegerArray lines = new IntegerArray(1); + IntegerArray files = new IntegerArray(1); + for (int i = 0; i < offsets.length - 1; ++i) { + offsets[i] = lines.size(); + int sz = readUnsignedNumber(); + if (sz == 0) { + continue; + } else if (sz == 1) { + lines.add(-1); + files.add(-1); + } else if (sz == 2) { + lines.add(i + 1); + files.add(index); + } else { + sz -= 2; + int last = i; + for (int j = 0; j < sz; ++j) { + last += readNumber(); + lines.add(last); + files.add(index + readNumber()); + } + } + } + offsets[offsets.length - 1] = lines.size(); + DebugInformation.CFG cfg = new DebugInformation.CFG(); + cfg.offsets = offsets; + cfg.lines = lines.getAll(); + cfg.files = files.getAll(); + return cfg; + } + private int processSign(int number) { boolean negative = (number & 1) != 0; number >>>= 1; diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java index de3eecb87..3a7414395 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java @@ -18,6 +18,7 @@ package org.teavm.debugging; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import org.teavm.debugging.DebugInformation.ClassMetadata; @@ -47,6 +48,7 @@ class DebugInformationWriter { writeMapping(debugInfo.methodMapping); writeVariableMappings(debugInfo); writeClassMetadata(debugInfo.classesMetadata); + writeCFGs(debugInfo); } private void writeVariableMappings(DebugInformation debugInfo) throws IOException { @@ -138,6 +140,38 @@ class DebugInformationWriter { } } + private void writeCFGs(DebugInformation debugInfo) throws IOException { + for (int i = 0; i < debugInfo.controlFlowGraphs.length; ++i) { + writeCFG(debugInfo.controlFlowGraphs[i], i); + } + } + + private void writeCFG(DebugInformation.CFG mapping, int fileIndex) throws IOException { + writeUnsignedNumber(mapping.offsets.length - 1); + for (int i = 0; i < mapping.offsets.length - 1; ++i) { + int start = mapping.offsets[i]; + int sz = mapping.offsets[i + 1] - start; + if (sz == 0) { + writeUnsignedNumber(0); + } else if (sz == 1 && mapping.lines[start] == -1) { + writeUnsignedNumber(1); + } else if (sz == 1 && mapping.lines[start] == i + 1 && mapping.files[start] == fileIndex) { + writeUnsignedNumber(2); + } else { + writeUnsignedNumber(2 + sz); + int[] lines = Arrays.copyOfRange(mapping.lines, start, start + sz); + int[] files = Arrays.copyOfRange(mapping.files, start, start + sz); + int last = i; + for (int j = 0; j < sz; ++j) { + int succ = lines[j]; + writeNumber(succ - last); + writeNumber(files[j] - fileIndex); + last = succ; + } + } + } + } + private void writeNumber(int number) throws IOException { writeUnsignedNumber(convertToSigned(number)); } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java b/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java index edfbf2e3d..70aeaa412 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java @@ -51,4 +51,8 @@ public class DummyDebugInformationEmitter implements DebugInformationEmitter { @Override public void addField(String fieldName, String jsName) { } + + @Override + public void addSuccessors(SourceLocation location, SourceLocation[] successors) { + } } diff --git a/teavm-core/src/main/java/org/teavm/model/InstructionLocation.java b/teavm-core/src/main/java/org/teavm/model/InstructionLocation.java index 531c0da23..40bee89cb 100644 --- a/teavm-core/src/main/java/org/teavm/model/InstructionLocation.java +++ b/teavm-core/src/main/java/org/teavm/model/InstructionLocation.java @@ -15,6 +15,8 @@ */ package org.teavm.model; +import java.util.Objects; + /** * * @author Alexey Andreev @@ -35,4 +37,25 @@ public class InstructionLocation { public int getLine() { return line; } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (fileName == null ? 0 : fileName.hashCode()); + result = prime * result + line; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof InstructionLocation)) { + return false; + } + InstructionLocation other = (InstructionLocation)obj; + return Objects.equals(fileName, other.fileName) && line == other.line; + } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java index 01b8de81e..3d31047ff 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java +++ b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java @@ -15,7 +15,7 @@ */ package org.teavm.model.util; -import java.util.List; +import java.util.*; import org.teavm.common.Graph; import org.teavm.common.GraphBuilder; import org.teavm.model.*; @@ -68,6 +68,67 @@ public final class ProgramUtils { return graphBuilder.build(); } + public static Map getLocationCFG(Program program) { + Graph graph = buildControlFlowGraph(program); + class Step { + InstructionLocation location; + int block; + } + Deque stack = new ArrayDeque<>(); + for (int i = 0; i < graph.size(); ++i) { + if (graph.incomingEdgesCount(i) == 0) { + Step step = new Step(); + step.block = i; + step.location = null; + stack.push(step); + } + } + boolean[] visited = new boolean[graph.size()]; + Map> locationGraphBuilder = new HashMap<>(); + while (!stack.isEmpty()) { + Step step = stack.pop(); + if (visited[step.block]) { + continue; + } + visited[step.block] = true; + BasicBlock block = program.basicBlockAt(step.block); + InstructionLocation location = step.location; + for (Instruction insn : block.getInstructions()) { + if (insn.getLocation() != null) { + if (location != null) { + Set successors = locationGraphBuilder.get(location); + if (successors == null) { + successors = new HashSet<>(); + locationGraphBuilder.put(location, successors); + } + successors.add(insn.getLocation()); + } + location = insn.getLocation(); + } + } + if (graph.outgoingEdgesCount(step.block) == 0) { + Set successors = locationGraphBuilder.get(location); + if (successors == null) { + successors = new HashSet<>(); + locationGraphBuilder.put(location, successors); + } + successors.add(new InstructionLocation(null, -1)); + } else { + for (int next : graph.outgoingEdges(step.block)) { + step = new Step(); + step.location = location; + step.block = next; + stack.push(step); + } + } + } + Map locationGraph = new HashMap<>(); + for (Map.Entry> entry : locationGraphBuilder.entrySet()) { + locationGraph.put(entry.getKey(), entry.getValue().toArray(new InstructionLocation[0])); + } + return locationGraph; + } + public static Program copy(ProgramReader program) { Program copy = new Program(); InstructionCopyReader insnCopier = new InstructionCopyReader();