Adds line CFG to debug information

This commit is contained in:
konsoletyper 2014-08-11 18:26:25 +04:00
parent fa1a620fdb
commit b05d1a7524
8 changed files with 286 additions and 1 deletions

View File

@ -45,6 +45,7 @@ public class DebugInformation {
Mapping methodMapping;
Mapping lineMapping;
MultiMapping[] variableMappings;
CFG[] controlFlowGraphs;
List<ClassMetadata> 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<Integer, Integer> fieldMap = new HashMap<>();
}
static class CFG {
int[] lines;
int[] files;
int[] offsets;
}
}

View File

@ -42,6 +42,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
private String currentFileName;
private int currentClassMetadata = -1;
private List<ClassMetadata> classesMetadata = new ArrayList<>();
private List<CFG> 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.<CFG>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<Integer, Integer> 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;
}
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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));
}

View File

@ -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) {
}
}

View File

@ -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;
}
}

View File

@ -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<InstructionLocation, InstructionLocation[]> getLocationCFG(Program program) {
Graph graph = buildControlFlowGraph(program);
class Step {
InstructionLocation location;
int block;
}
Deque<Step> 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<InstructionLocation, Set<InstructionLocation>> 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<InstructionLocation> 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<InstructionLocation> 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<InstructionLocation, InstructionLocation[]> locationGraph = new HashMap<>();
for (Map.Entry<InstructionLocation, Set<InstructionLocation>> 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();