blob: f29f9cb1c8445dcdb33b4fbe447ae454fb5e03b6 [file] [log] [blame]
// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils.positions;
import com.android.tools.r8.debuginfo.DebugRepresentation;
import com.android.tools.r8.dex.code.DexInstruction;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexDebugEvent;
import com.android.tools.r8.graph.DexDebugEvent.Default;
import com.android.tools.r8.graph.DexDebugEventVisitor;
import com.android.tools.r8.graph.DexDebugInfo;
import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
import com.android.tools.r8.graph.DexDebugInfoForSingleLineMethod;
import com.android.tools.r8.graph.DexDebugPositionState;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.OutlineCallerPosition;
import com.android.tools.r8.ir.code.Position.OutlineCallerPosition.OutlineCallerPositionBuilder;
import com.android.tools.r8.ir.code.Position.OutlinePosition;
import com.android.tools.r8.ir.code.Position.PositionBuilder;
import com.android.tools.r8.ir.code.Position.SourcePosition;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.positions.PositionToMappedRangeMapper.PcBasedDebugInfoRecorder;
import java.util.ArrayList;
import java.util.List;
public class DexPositionToPcMappedRangeMapper {
private final AppView<?> appView;
private final PcBasedDebugInfoRecorder pcBasedDebugInfo;
public DexPositionToPcMappedRangeMapper(
AppView<?> appView, PcBasedDebugInfoRecorder pcBasedDebugInfo) {
this.appView = appView;
this.pcBasedDebugInfo = pcBasedDebugInfo;
}
public List<MappedPosition> optimizeDexCodePositionsForPc(
ProgramMethod method, PositionRemapper positionRemapper, int pcEncodingCutoff) {
List<MappedPosition> mappedPositions = new ArrayList<>();
// Do the actual processing for each method.
DexCode dexCode = method.getDefinition().getCode().asDexCode();
EventBasedDebugInfo debugInfo =
getEventBasedDebugInfo(method.getDefinition(), dexCode, appView);
IntBox firstDefaultEventPc = new IntBox(-1);
BooleanBox singleOriginalLine = new BooleanBox(true);
Pair<Integer, Position> lastPosition = new Pair<>();
DexDebugEventVisitor visitor =
new DexDebugPositionState(
debugInfo.startLine,
appView.graphLens().getOriginalMethodSignature(method.getReference())) {
@Override
public void visit(Default defaultEvent) {
super.visit(defaultEvent);
assert getCurrentLine() >= 0;
if (firstDefaultEventPc.get() < 0) {
firstDefaultEventPc.set(getCurrentPc());
}
Position currentPosition = getPositionFromPositionState(this);
if (lastPosition.getSecond() != null) {
if (singleOriginalLine.isTrue()
&& !currentPosition.equals(lastPosition.getSecond())) {
singleOriginalLine.set(false);
}
remapAndAddForPc(
pcBasedDebugInfo,
lastPosition.getFirst(),
getCurrentPc(),
lastPosition.getSecond(),
positionRemapper,
mappedPositions);
}
lastPosition.setFirst(getCurrentPc());
lastPosition.setSecond(currentPosition);
resetOutlineInformation();
}
};
for (DexDebugEvent event : debugInfo.events) {
event.accept(visitor);
}
// If the method has a single non-preamble line, check that the preamble is not active on any
// throwing instruction before the single line becomes active.
if (singleOriginalLine.isTrue() && firstDefaultEventPc.get() > 0) {
for (DexInstruction instruction : dexCode.instructions) {
if (instruction.getOffset() < firstDefaultEventPc.get()) {
if (instruction.canThrow()) {
singleOriginalLine.set(false);
}
} else {
break;
}
}
}
int lastInstructionPc = DebugRepresentation.getLastExecutableInstruction(dexCode).getOffset();
if (lastPosition.getSecond() != null) {
remapAndAddForPc(
pcBasedDebugInfo,
lastPosition.getFirst(),
lastInstructionPc + 1,
lastPosition.getSecond(),
positionRemapper,
mappedPositions);
}
assert !mappedPositions.isEmpty() || dexCode.instructions.length == 1;
if (singleOriginalLine.isTrue()
&& lastPosition.getSecond() != null
&& (mappedPositions.isEmpty() || !mappedPositions.get(0).isOutlineCaller())) {
dexCode.setDebugInfo(DexDebugInfoForSingleLineMethod.getInstance());
pcBasedDebugInfo.recordSingleLineFor(method, pcEncodingCutoff);
} else {
pcBasedDebugInfo.recordPcMappingFor(method, pcEncodingCutoff);
}
return mappedPositions;
}
private static void remapAndAddForPc(
PcBasedDebugInfoRecorder debugInfoProvider,
int startPc,
int endPc,
Position position,
PositionRemapper remapper,
List<MappedPosition> mappedPositions) {
Pair<Position, Position> remappedPosition = remapper.createRemappedPosition(position);
Position oldPosition = remappedPosition.getFirst();
for (int currentPc = startPc; currentPc < endPc; currentPc++) {
boolean firstEntry = currentPc == startPc;
mappedPositions.add(
new MappedPosition(
oldPosition.getMethod(),
oldPosition.getLine(),
oldPosition.getCallerPosition(),
debugInfoProvider.getPcEncoding(currentPc),
// Outline info is placed exactly on the positions that relate to it so we should
// only emit it for the first entry.
firstEntry && oldPosition.isOutline(),
firstEntry ? oldPosition.getOutlineCallee() : null,
firstEntry ? oldPosition.getOutlinePositions() : null));
}
}
// This conversion *always* creates an event based debug info encoding as any non-info will
// be created as an implicit PC encoding.
private static EventBasedDebugInfo getEventBasedDebugInfo(
DexEncodedMethod method, DexCode dexCode, AppView<?> appView) {
// TODO(b/213411850): Do we need to reconsider conversion here to support pc-based D8 merging?
if (dexCode.getDebugInfo() == null) {
return DexDebugInfo.createEventBasedInfoForMethodWithoutDebugInfo(
method, appView.dexItemFactory());
}
assert method.getParameters().size() == dexCode.getDebugInfo().getParameterCount();
EventBasedDebugInfo debugInfo =
DexDebugInfo.convertToEventBased(dexCode, appView.dexItemFactory());
assert debugInfo != null;
return debugInfo;
}
private static Position getPositionFromPositionState(DexDebugPositionState state) {
PositionBuilder<?, ?> positionBuilder;
if (state.getOutlineCallee() != null) {
OutlineCallerPositionBuilder outlineCallerPositionBuilder =
OutlineCallerPosition.builder()
.setOutlineCallee(state.getOutlineCallee())
.setIsOutline(state.isOutline());
state.getOutlineCallerPositions().forEach(outlineCallerPositionBuilder::addOutlinePosition);
positionBuilder = outlineCallerPositionBuilder;
} else if (state.isOutline()) {
positionBuilder = OutlinePosition.builder();
} else {
positionBuilder = SourcePosition.builder().setFile(state.getCurrentFile());
}
return positionBuilder
.setLine(state.getCurrentLine())
.setMethod(state.getCurrentMethod())
.setCallerPosition(state.getCurrentCallerPosition())
.build();
}
private static boolean verifyIdentityMapping(
EventBasedDebugInfo originalDebugInfo, EventBasedDebugInfo optimizedDebugInfo) {
assert optimizedDebugInfo.startLine == originalDebugInfo.startLine;
assert optimizedDebugInfo.events.length == originalDebugInfo.events.length;
for (int i = 0; i < originalDebugInfo.events.length; ++i) {
assert optimizedDebugInfo.events[i].equals(originalDebugInfo.events[i]);
}
return true;
}
}