mirror of
https://github.com/skylot/jadx.git
synced 2024-11-27 14:40:54 +00:00
chore(debugger): improve code and add details to error messages (PR #1982)
* chore: created list entry getter getRegListEntry for problematic code; use specific containers/fields instead of SimpleEntry; * chore: include details on the used ArtAdapter in error message * add device info to registers exception --------- Co-authored-by: Skylot <skylot@gmail.com>
This commit is contained in:
parent
f6ab105ef7
commit
f695fafe74
@ -2,7 +2,7 @@ package jadx.gui.device.debugger;
|
||||
|
||||
public class ArtAdapter {
|
||||
|
||||
public interface Debugger {
|
||||
public interface IArtAdapter {
|
||||
int getRuntimeRegNum(int smaliNum, int regCount, int paramStart);
|
||||
|
||||
boolean readNullObject();
|
||||
@ -10,7 +10,7 @@ public class ArtAdapter {
|
||||
String typeForNull();
|
||||
}
|
||||
|
||||
public static Debugger getAdapter(int androidReleaseVer) {
|
||||
public static IArtAdapter getAdapter(int androidReleaseVer) {
|
||||
if (androidReleaseVer <= 8) {
|
||||
return new AndroidOreoAndBelow();
|
||||
} else {
|
||||
@ -18,7 +18,7 @@ public class ArtAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
public static class AndroidOreoAndBelow implements Debugger {
|
||||
public static class AndroidOreoAndBelow implements IArtAdapter {
|
||||
@Override
|
||||
public int getRuntimeRegNum(int smaliNum, int regCount, int paramStart) {
|
||||
int localRegCount = regCount - paramStart;
|
||||
@ -36,7 +36,7 @@ public class ArtAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
public static class AndroidPieAndAbove implements Debugger {
|
||||
public static class AndroidPieAndAbove implements IArtAdapter {
|
||||
@Override
|
||||
public int getRuntimeRegNum(int smaliNum, int regCount, int paramStart) {
|
||||
return smaliNum;
|
||||
|
@ -52,7 +52,7 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
|
||||
|
||||
private JDebuggerPanel debuggerPanel;
|
||||
private SmaliDebugger debugger;
|
||||
private ArtAdapter.Debugger art;
|
||||
private ArtAdapter.IArtAdapter art;
|
||||
private final CurrentInfo cur = new CurrentInfo();
|
||||
|
||||
private BreakpointStore bpStore;
|
||||
@ -409,7 +409,7 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
|
||||
for (RegisterObserver.Info info : list) {
|
||||
RegTreeNode reg = cur.frame.getRegNodes().get(info.getSmaliRegNum());
|
||||
if (info.isLoad()) {
|
||||
applyDbgInfo(reg, info.getInfo());
|
||||
applyDbgInfo(reg, info.getName(), info.getType());
|
||||
} else {
|
||||
reg.setAlias("");
|
||||
reg.setAbsoluteType(false);
|
||||
@ -552,7 +552,7 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
|
||||
} catch (SmaliDebuggerException e) {
|
||||
logErr(e);
|
||||
}
|
||||
if (frames.size() == 0) {
|
||||
if (frames.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<FrameNode> frameEleList = new ArrayList<>(frames.size());
|
||||
@ -671,7 +671,7 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
|
||||
private void fetchAllRegisters(FrameNode frame) {
|
||||
List<SmaliRegister> regs = cur.regAdapter.getInitializedList(frame.getCodeOffset());
|
||||
for (SmaliRegister reg : regs) {
|
||||
Entry<String, String> info = cur.regAdapter.getInfo(reg.getRuntimeRegNum(), frame.getCodeOffset());
|
||||
RuntimeVarInfo info = cur.regAdapter.getInfo(reg.getRuntimeRegNum(), frame.getCodeOffset());
|
||||
RegTreeNode regNode = frame.getRegNodes().get(reg.getRegNum());
|
||||
if (info != null) {
|
||||
applyDbgInfo(regNode, info);
|
||||
@ -680,9 +680,13 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
|
||||
}
|
||||
}
|
||||
|
||||
private void applyDbgInfo(RegTreeNode rn, Entry<String, String> info) {
|
||||
rn.setAlias(info.getKey());
|
||||
rn.updateType(info.getValue());
|
||||
private void applyDbgInfo(RegTreeNode rn, RuntimeVarInfo info) {
|
||||
applyDbgInfo(rn, info.getName(), info.getType());
|
||||
}
|
||||
|
||||
private void applyDbgInfo(RegTreeNode rn, String alias, String type) {
|
||||
rn.setAlias(alias);
|
||||
rn.updateType(type);
|
||||
rn.setAbsoluteType(true);
|
||||
}
|
||||
|
||||
@ -877,7 +881,9 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
|
||||
cur.regAdapter = regAdaMap.computeIfAbsent(cur.mthFullID,
|
||||
k -> RegisterObserver.merge(
|
||||
getRuntimeDebugInfo(cur.frame),
|
||||
getRegisterList()));
|
||||
getSmaliRegisterList(),
|
||||
art,
|
||||
cur.mthFullID));
|
||||
|
||||
if (cur.smali.getRegCount(cur.mthFullID) > 0) {
|
||||
updateAllRegisters(cur.frame);
|
||||
@ -889,7 +895,7 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
|
||||
});
|
||||
}
|
||||
|
||||
private List<SmaliRegister> getRegisterList() {
|
||||
private List<SmaliRegister> getSmaliRegisterList() {
|
||||
int regCount = cur.smali.getRegCount(cur.mthFullID);
|
||||
int paramStart = cur.smali.getParamRegStart(cur.mthFullID);
|
||||
List<SmaliRegister> srs = cur.smali.getRegisterList(cur.mthFullID);
|
||||
|
@ -0,0 +1,158 @@
|
||||
package jadx.gui.device.debugger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.gui.device.protocol.ADB;
|
||||
import jadx.gui.device.protocol.ADBDevice;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
public class DebugSettings {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DebugSettings.class);
|
||||
|
||||
private static final int FORWARD_TCP_PORT = 33233;
|
||||
|
||||
public static final DebugSettings INSTANCE = new DebugSettings();
|
||||
|
||||
private int ver;
|
||||
private String pid;
|
||||
private String name;
|
||||
private ADBDevice device;
|
||||
private int forwardTcpPort = FORWARD_TCP_PORT;
|
||||
private String expectPkg = "";
|
||||
private boolean autoAttachPkg = false;
|
||||
|
||||
private DebugSettings() {
|
||||
}
|
||||
|
||||
public void set(ADBDevice device, int ver, String pid, String name) {
|
||||
this.ver = ver;
|
||||
this.pid = pid;
|
||||
this.name = name;
|
||||
this.device = device;
|
||||
this.autoAttachPkg = false;
|
||||
this.expectPkg = "";
|
||||
}
|
||||
|
||||
public DebugSettings setPid(String pid) {
|
||||
this.pid = pid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DebugSettings setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String forwardJDWP() {
|
||||
int localPort = forwardTcpPort;
|
||||
String resultDesc = "";
|
||||
try {
|
||||
do {
|
||||
ADBDevice.ForwardResult rst = device.forwardJDWP(localPort + "", pid);
|
||||
if (rst.state == 0) {
|
||||
forwardTcpPort = localPort;
|
||||
return "";
|
||||
}
|
||||
if (rst.state == 1) {
|
||||
if (rst.desc.contains("Only one usage of each socket address")) { // port is taken by other process
|
||||
if (localPort < 65536) {
|
||||
localPort++; // retry
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
resultDesc = rst.desc;
|
||||
break;
|
||||
} while (true);
|
||||
} catch (Exception e) {
|
||||
LOG.error("JDWP forward error", e);
|
||||
}
|
||||
if (StringUtils.isEmpty(resultDesc)) {
|
||||
resultDesc = NLS.str("adb_dialog.forward_fail");
|
||||
}
|
||||
return resultDesc;
|
||||
}
|
||||
|
||||
// we have to remove all ports that forwarding the jdwp:pid, otherwise our JDWP handshake may fail.
|
||||
public void clearForward() {
|
||||
String jdwpPid = " jdwp:" + pid;
|
||||
String tcpPort = " tcp:" + forwardTcpPort;
|
||||
try {
|
||||
List<String> list = ADB.listForward(device.getDeviceInfo().getAdbHost(),
|
||||
device.getDeviceInfo().getAdbPort());
|
||||
for (String s : list) {
|
||||
if (s.startsWith(device.getSerial()) && s.endsWith(jdwpPid) && !s.contains(tcpPort)) {
|
||||
String[] fields = s.split("\\s+");
|
||||
for (String field : fields) {
|
||||
if (field.startsWith("tcp:")) {
|
||||
try {
|
||||
device.removeForward(field.substring("tcp:".length()));
|
||||
} catch (Exception e) {
|
||||
LOG.error("JDWP remove forward error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("JDWP clear forward error", e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBeingDebugged() {
|
||||
String jdwpPid = " jdwp:" + pid;
|
||||
String tcpPort = " tcp:" + forwardTcpPort;
|
||||
try {
|
||||
List<String> list = ADB.listForward(device.getDeviceInfo().getAdbHost(),
|
||||
device.getDeviceInfo().getAdbPort());
|
||||
for (String s : list) {
|
||||
if (s.startsWith(device.getSerial()) && s.endsWith(jdwpPid)) {
|
||||
return !s.contains(tcpPort);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("ADB list forward error", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getVer() {
|
||||
return ver;
|
||||
}
|
||||
|
||||
public String getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ADBDevice getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
public int getForwardTcpPort() {
|
||||
return forwardTcpPort;
|
||||
}
|
||||
|
||||
public String getExpectPkg() {
|
||||
return expectPkg;
|
||||
}
|
||||
|
||||
public void setExpectPkg(String expectPkg) {
|
||||
this.expectPkg = expectPkg;
|
||||
}
|
||||
|
||||
public boolean isAutoAttachPkg() {
|
||||
return autoAttachPkg;
|
||||
}
|
||||
|
||||
public void setAutoAttachPkg(boolean autoAttachPkg) {
|
||||
this.autoAttachPkg = autoAttachPkg;
|
||||
}
|
||||
}
|
@ -1,48 +1,50 @@
|
||||
package jadx.gui.device.debugger;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import io.reactivex.annotations.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.gui.device.debugger.SmaliDebugger.RuntimeVarInfo;
|
||||
import jadx.gui.device.debugger.smali.RegisterInfo;
|
||||
import jadx.gui.device.debugger.smali.SmaliRegister;
|
||||
|
||||
public class RegisterObserver {
|
||||
|
||||
private Map<Long, List<Info>> infoMap;
|
||||
private final List<Entry<SmaliRegister, List<RuntimeVarInfo>>> regList;
|
||||
private final List<SmaliRegisterMapping> regList;
|
||||
private final ArtAdapter.IArtAdapter art;
|
||||
private final String mthFullID;
|
||||
private boolean hasDbgInfo = false;
|
||||
|
||||
private RegisterObserver() {
|
||||
regList = new ArrayList<>();
|
||||
infoMap = Collections.emptyMap();
|
||||
private RegisterObserver(ArtAdapter.IArtAdapter art, String mthFullID) {
|
||||
this.regList = new ArrayList<>();
|
||||
this.infoMap = Collections.emptyMap();
|
||||
this.art = art;
|
||||
this.mthFullID = mthFullID;
|
||||
}
|
||||
|
||||
public static RegisterObserver merge(List<RuntimeVarInfo> rtRegs, List<SmaliRegister> smaliRegs) {
|
||||
RegisterObserver adapter = new RegisterObserver();
|
||||
adapter.hasDbgInfo = rtRegs.size() > 0;
|
||||
@NotNull
|
||||
public static RegisterObserver merge(List<RuntimeVarInfo> rtRegs, List<SmaliRegister> smaliRegs, ArtAdapter.IArtAdapter art,
|
||||
String mthFullID) {
|
||||
RegisterObserver adapter = new RegisterObserver(art, mthFullID);
|
||||
adapter.hasDbgInfo = !rtRegs.isEmpty();
|
||||
if (adapter.hasDbgInfo) {
|
||||
adapter.infoMap = new HashMap<>();
|
||||
}
|
||||
for (SmaliRegister sr : smaliRegs) {
|
||||
adapter.regList.add(new SimpleEntry<>(sr, Collections.emptyList()));
|
||||
adapter.regList.add(new SmaliRegisterMapping(sr));
|
||||
}
|
||||
adapter.regList.sort(Comparator.comparingInt(r -> r.getKey().getRuntimeRegNum()));
|
||||
adapter.regList.sort(Comparator.comparingInt(r -> r.getSmaliRegister().getRuntimeRegNum()));
|
||||
for (RuntimeVarInfo rt : rtRegs) {
|
||||
Entry<SmaliRegister, List<RuntimeVarInfo>> entry = adapter.regList.get(rt.getRegNum());
|
||||
if (entry.getValue().isEmpty()) {
|
||||
entry.setValue(new ArrayList<>());
|
||||
}
|
||||
entry.getValue().add(rt);
|
||||
final SmaliRegisterMapping smaliRegMapping = adapter.getRegListEntry(rt.getRegNum());
|
||||
final SmaliRegister smaliReg = smaliRegMapping.getSmaliRegister();
|
||||
smaliRegMapping.addRuntimeVarInfo(rt);
|
||||
|
||||
String type = rt.getSignature();
|
||||
if (type.isEmpty()) {
|
||||
@ -52,9 +54,8 @@ public class RegisterObserver {
|
||||
if (at != null) {
|
||||
type = at.toString();
|
||||
}
|
||||
Info load = new Info(entry.getKey().getRegNum(), true,
|
||||
new SimpleEntry<>(rt.getName(), type));
|
||||
Info unload = new Info(entry.getKey().getRegNum(), false, null);
|
||||
Info load = new Info(smaliReg.getRegNum(), true, rt.getName(), type);
|
||||
Info unload = new Info(smaliReg.getRegNum(), false, null, null);
|
||||
adapter.infoMap.computeIfAbsent((long) rt.getStartOffset(), k -> new ArrayList<>())
|
||||
.add(load);
|
||||
adapter.infoMap.computeIfAbsent((long) rt.getEndOffset(), k -> new ArrayList<>())
|
||||
@ -63,33 +64,53 @@ public class RegisterObserver {
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<SmaliRegister> getInitializedList(long codeOffset) {
|
||||
List<SmaliRegister> ret = Collections.emptyList();
|
||||
for (Entry<SmaliRegister, List<RuntimeVarInfo>> info : regList) {
|
||||
if (info.getKey().isInitialized(codeOffset)) {
|
||||
for (SmaliRegisterMapping smaliRegisterMapping : regList) {
|
||||
if (smaliRegisterMapping.getSmaliRegister().isInitialized(codeOffset)) {
|
||||
if (ret.isEmpty()) {
|
||||
ret = new ArrayList<>();
|
||||
}
|
||||
ret.add(info.getKey());
|
||||
ret.add(smaliRegisterMapping.getSmaliRegister());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Entry<String, String> getInfo(int runtimeNum, long codeOffset) {
|
||||
Entry<SmaliRegister, List<RuntimeVarInfo>> list = regList.get(runtimeNum);
|
||||
for (RegisterInfo info : list.getValue()) {
|
||||
public RuntimeVarInfo getInfo(int runtimeNum, long codeOffset) {
|
||||
SmaliRegisterMapping list = getRegListEntry(runtimeNum);
|
||||
for (RuntimeVarInfo info : list.getRuntimeVarInfoList()) {
|
||||
if (info.getStartOffset() > codeOffset) {
|
||||
break;
|
||||
}
|
||||
if (info.isInitialized(codeOffset)) {
|
||||
return new SimpleEntry<>(info.getName(), info.getType());
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private SmaliRegisterMapping getRegListEntry(int regNum) {
|
||||
try {
|
||||
return regList.get(regNum);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new RuntimeException(
|
||||
String.format("Register %d does not exist (size: %d).\n %s\n Method: %s",
|
||||
regNum, regList.size(), buildDeviceInfo(), mthFullID),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildDeviceInfo() {
|
||||
DebugSettings debugSettings = DebugSettings.INSTANCE;
|
||||
return "Device: " + debugSettings.getDevice().getDeviceInfo()
|
||||
+ ", Android: " + debugSettings.getVer()
|
||||
+ ", ArtAdapter: " + art.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Info> getInfoAt(long codeOffset) {
|
||||
if (hasDbgInfo) {
|
||||
List<Info> list = infoMap.get(codeOffset);
|
||||
@ -100,15 +121,43 @@ public class RegisterObserver {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static class SmaliRegisterMapping {
|
||||
private final SmaliRegister smaliRegister;
|
||||
|
||||
private List<RuntimeVarInfo> rtList = Collections.emptyList();
|
||||
|
||||
public SmaliRegisterMapping(SmaliRegister smaliRegister) {
|
||||
this.smaliRegister = smaliRegister;
|
||||
}
|
||||
|
||||
public SmaliRegister getSmaliRegister() {
|
||||
return smaliRegister;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<RuntimeVarInfo> getRuntimeVarInfoList() {
|
||||
return rtList;
|
||||
}
|
||||
|
||||
public void addRuntimeVarInfo(RuntimeVarInfo rt) {
|
||||
if (rtList.isEmpty()) {
|
||||
rtList = new ArrayList<>();
|
||||
}
|
||||
rtList.add(rt);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Info {
|
||||
private final int smaliRegNum;
|
||||
private final boolean load;
|
||||
private final Entry<String, String> info;
|
||||
private final String name;
|
||||
private final String type;
|
||||
|
||||
private Info(int smaliRegNum, boolean load, Entry<String, String> info) {
|
||||
private Info(int smaliRegNum, boolean load, String name, String type) {
|
||||
this.smaliRegNum = smaliRegNum;
|
||||
this.load = load;
|
||||
this.info = info;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getSmaliRegNum() {
|
||||
@ -119,8 +168,12 @@ public class RegisterObserver {
|
||||
return load;
|
||||
}
|
||||
|
||||
public Entry<String, String> getInfo() {
|
||||
return info;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -71,7 +72,6 @@ import io.github.skylot.jdwp.JDWP.VirtualMachine.AllClassesWithGeneric.AllClasse
|
||||
import io.github.skylot.jdwp.JDWP.VirtualMachine.AllThreads.AllThreadsReplyData;
|
||||
import io.github.skylot.jdwp.JDWP.VirtualMachine.AllThreads.AllThreadsReplyDataThreads;
|
||||
import io.github.skylot.jdwp.JDWP.VirtualMachine.CreateString.CreateStringReplyData;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
|
||||
import jadx.api.plugins.input.data.AccessFlags;
|
||||
import jadx.gui.device.debugger.smali.RegisterInfo;
|
||||
@ -1292,7 +1292,7 @@ public class SmaliDebugger {
|
||||
return gen;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@NotNull
|
||||
@Override
|
||||
public String getSignature() {
|
||||
return this.slot.genericSignature.trim();
|
||||
|
@ -41,6 +41,7 @@ import org.slf4j.LoggerFactory;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.device.debugger.DbgUtils;
|
||||
import jadx.gui.device.debugger.DebugSettings;
|
||||
import jadx.gui.device.protocol.ADB;
|
||||
import jadx.gui.device.protocol.ADBDevice;
|
||||
import jadx.gui.device.protocol.ADBDeviceInfo;
|
||||
@ -51,15 +52,12 @@ import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.SystemInfo;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
import static jadx.gui.device.protocol.ADBDevice.ForwardResult;
|
||||
|
||||
public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.JDWPProcessListener {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ADBDialog.class);
|
||||
|
||||
private static final long serialVersionUID = -1111111202102181630L;
|
||||
private static final ImageIcon ICON_DEVICE = UiUtils.openSvgIcon("adb/androidDevice");
|
||||
private static final ImageIcon ICON_PROCESS = UiUtils.openSvgIcon("adb/addToWatch");
|
||||
private static DebugSetting debugSetter = null;
|
||||
|
||||
private final transient MainWindow mainWindow;
|
||||
private transient Label tipLabel;
|
||||
@ -75,15 +73,12 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
|
||||
public ADBDialog(MainWindow mainWindow) {
|
||||
super(mainWindow);
|
||||
this.mainWindow = mainWindow;
|
||||
if (debugSetter == null) {
|
||||
debugSetter = new DebugSetting();
|
||||
}
|
||||
initUI();
|
||||
pathTextField.setText(mainWindow.getSettings().getAdbDialogPath());
|
||||
hostTextField.setText(mainWindow.getSettings().getAdbDialogHost());
|
||||
portTextField.setText(mainWindow.getSettings().getAdbDialogPort());
|
||||
|
||||
if (pathTextField.getText().equals("")) {
|
||||
if (pathTextField.getText().isEmpty()) {
|
||||
detectADBPath();
|
||||
} else {
|
||||
pathTextField.setText("");
|
||||
@ -329,7 +324,7 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
|
||||
if (!setupArgs(deviceNode.device, pid, (String) node.getUserObject())) {
|
||||
return;
|
||||
}
|
||||
if (debugSetter.isBeingDebugged()) {
|
||||
if (DebugSettings.INSTANCE.isBeingDebugged()) {
|
||||
if (JOptionPane.showConfirmDialog(mainWindow,
|
||||
NLS.str("adb_dialog.being_debugged_msg"),
|
||||
NLS.str("adb_dialog.being_debugged_title"),
|
||||
@ -347,23 +342,21 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
|
||||
}
|
||||
|
||||
private static boolean attachProcess(MainWindow mainWindow) {
|
||||
if (debugSetter == null) {
|
||||
return false;
|
||||
}
|
||||
debugSetter.clearForward();
|
||||
String rst = debugSetter.forwardJDWP();
|
||||
DebugSettings debugSettings = DebugSettings.INSTANCE;
|
||||
debugSettings.clearForward();
|
||||
String rst = debugSettings.forwardJDWP();
|
||||
if (!rst.isEmpty()) {
|
||||
UiUtils.showMessageBox(mainWindow, rst);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return mainWindow.getDebuggerPanel().showDebugger(
|
||||
debugSetter.name,
|
||||
debugSetter.device.getDeviceInfo().getAdbHost(),
|
||||
debugSetter.forwardTcpPort,
|
||||
debugSetter.ver,
|
||||
debugSetter.device,
|
||||
debugSetter.pid);
|
||||
debugSettings.getName(),
|
||||
debugSettings.getDevice().getDeviceInfo().getAdbHost(),
|
||||
debugSettings.getForwardTcpPort(),
|
||||
debugSettings.getVer(),
|
||||
debugSettings.getDevice(),
|
||||
debugSettings.getPid());
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to attach to process", e);
|
||||
return false;
|
||||
@ -371,18 +364,17 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
|
||||
}
|
||||
|
||||
public static boolean launchForDebugging(MainWindow mainWindow, String fullAppPath, boolean autoAttach) {
|
||||
if (debugSetter != null) {
|
||||
debugSetter.autoAttachPkg = autoAttach;
|
||||
try {
|
||||
int pid = debugSetter.device.launchApp(fullAppPath);
|
||||
if (pid != -1) {
|
||||
debugSetter.setPid(String.valueOf(pid))
|
||||
.setName(fullAppPath);
|
||||
return attachProcess(mainWindow);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to launch app", e);
|
||||
DebugSettings debugSettings = DebugSettings.INSTANCE;
|
||||
debugSettings.setAutoAttachPkg(autoAttach);
|
||||
try {
|
||||
int pid = debugSettings.getDevice().launchApp(fullAppPath);
|
||||
if (pid != -1) {
|
||||
debugSettings.setPid(String.valueOf(pid))
|
||||
.setName(fullAppPath);
|
||||
return attachProcess(mainWindow);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to launch app", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -458,7 +450,7 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
|
||||
procs = Collections.emptyList();
|
||||
}
|
||||
List<String> procList = new ArrayList<>(id.size());
|
||||
if (procs.size() == 0) {
|
||||
if (procs.isEmpty()) {
|
||||
procList.addAll(id);
|
||||
} else {
|
||||
for (ADB.Process proc : procs) {
|
||||
@ -479,12 +471,13 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
node.tNode.removeAllChildren();
|
||||
DefaultMutableTreeNode foundNode = null;
|
||||
DebugSettings debugSettings = DebugSettings.INSTANCE;
|
||||
for (String procStr : procList) {
|
||||
DefaultMutableTreeNode pnode = new DefaultMutableTreeNode(procStr);
|
||||
node.tNode.add(pnode);
|
||||
if (!debugSetter.expectPkg.isEmpty() && procStr.endsWith(debugSetter.expectPkg)) {
|
||||
if (debugSetter.autoAttachPkg && debugSetter.device.equals(node.device)) {
|
||||
debugSetter.set(node.device, debugSetter.ver, getPid(procStr), procStr);
|
||||
if (!debugSettings.getExpectPkg().isEmpty() && procStr.endsWith(debugSettings.getExpectPkg())) {
|
||||
if (debugSettings.isAutoAttachPkg() && debugSettings.getDevice().equals(node.device)) {
|
||||
debugSettings.set(node.device, debugSettings.getVer(), getPid(procStr), procStr);
|
||||
if (attachProcess(mainWindow)) {
|
||||
dispose();
|
||||
return;
|
||||
@ -504,7 +497,7 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
|
||||
}
|
||||
|
||||
private void launchApp() {
|
||||
if (deviceNodes.size() == 0) {
|
||||
if (deviceNodes.isEmpty()) {
|
||||
UiUtils.showMessageBox(mainWindow, NLS.str("adb_dialog.no_devices"));
|
||||
return;
|
||||
}
|
||||
@ -533,13 +526,13 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
|
||||
if (pkg.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
debugSetter.expectPkg = " " + pkg;
|
||||
DebugSettings.INSTANCE.setExpectPkg(" " + pkg);
|
||||
for (int i = 0; i < procTreeRoot.getChildCount(); i++) {
|
||||
DefaultMutableTreeNode rn = (DefaultMutableTreeNode) procTreeRoot.getChildAt(i);
|
||||
for (int j = 0; j < rn.getChildCount(); j++) {
|
||||
DefaultMutableTreeNode n = (DefaultMutableTreeNode) rn.getChildAt(j);
|
||||
String pName = (String) n.getUserObject();
|
||||
if (pName.endsWith(debugSetter.expectPkg)) {
|
||||
if (pName.endsWith(DebugSettings.INSTANCE.getExpectPkg())) {
|
||||
TreePath path = new TreePath(n.getPath());
|
||||
procTree.scrollPathToVisible(path);
|
||||
procTree.setSelectionPath(path);
|
||||
@ -594,7 +587,7 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
|
||||
ver = "8";
|
||||
}
|
||||
ver = getMajorVer(ver);
|
||||
debugSetter.set(device, Integer.parseInt(ver), pid, name);
|
||||
DebugSettings.INSTANCE.set(device, Integer.parseInt(ver), pid, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -605,107 +598,4 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
|
||||
}
|
||||
return ver;
|
||||
}
|
||||
|
||||
private class DebugSetting {
|
||||
private static final int FORWARD_TCP_PORT = 33233;
|
||||
private int ver;
|
||||
private String pid;
|
||||
private String name;
|
||||
private ADBDevice device;
|
||||
private int forwardTcpPort = FORWARD_TCP_PORT;
|
||||
private String expectPkg = "";
|
||||
private boolean autoAttachPkg = false;
|
||||
|
||||
private void set(ADBDevice device, int ver, String pid, String name) {
|
||||
this.ver = ver;
|
||||
this.pid = pid;
|
||||
this.name = name;
|
||||
this.device = device;
|
||||
this.autoAttachPkg = false;
|
||||
this.expectPkg = "";
|
||||
}
|
||||
|
||||
private DebugSetting setPid(String pid) {
|
||||
this.pid = pid;
|
||||
return this;
|
||||
}
|
||||
|
||||
private DebugSetting setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
private String forwardJDWP() {
|
||||
int localPort = forwardTcpPort;
|
||||
String resultDesc = "";
|
||||
try {
|
||||
do {
|
||||
ForwardResult rst = device.forwardJDWP(localPort + "", pid);
|
||||
if (rst.state == 0) {
|
||||
forwardTcpPort = localPort;
|
||||
return "";
|
||||
}
|
||||
if (rst.state == 1) {
|
||||
if (rst.desc.contains("Only one usage of each socket address")) { // port is taken by other process
|
||||
if (localPort < 65536) {
|
||||
localPort++; // retry
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
resultDesc = rst.desc;
|
||||
break;
|
||||
} while (true);
|
||||
} catch (Exception e) {
|
||||
LOG.error("JDWP forward error", e);
|
||||
}
|
||||
if (StringUtils.isEmpty(resultDesc)) {
|
||||
resultDesc = NLS.str("adb_dialog.forward_fail");
|
||||
}
|
||||
return resultDesc;
|
||||
}
|
||||
|
||||
// we have to remove all ports that forwarding the jdwp:pid, otherwise our JDWP handshake may fail.
|
||||
private void clearForward() {
|
||||
String jdwpPid = " jdwp:" + pid;
|
||||
String tcpPort = " tcp:" + forwardTcpPort;
|
||||
try {
|
||||
List<String> list = ADB.listForward(device.getDeviceInfo().getAdbHost(),
|
||||
device.getDeviceInfo().getAdbPort());
|
||||
for (String s : list) {
|
||||
if (s.startsWith(device.getSerial()) && s.endsWith(jdwpPid) && !s.contains(tcpPort)) {
|
||||
String[] fields = s.split("\\s+");
|
||||
for (String field : fields) {
|
||||
if (field.startsWith("tcp:")) {
|
||||
try {
|
||||
device.removeForward(field.substring("tcp:".length()));
|
||||
} catch (Exception e) {
|
||||
LOG.error("JDWP remove forward error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("JDWP clear forward error", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBeingDebugged() {
|
||||
String jdwpPid = " jdwp:" + pid;
|
||||
String tcpPort = " tcp:" + forwardTcpPort;
|
||||
try {
|
||||
List<String> list = ADB.listForward(device.getDeviceInfo().getAdbHost(),
|
||||
device.getDeviceInfo().getAdbPort());
|
||||
for (String s : list) {
|
||||
if (s.startsWith(device.getSerial()) && s.endsWith(jdwpPid)) {
|
||||
return !s.contains(tcpPort);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("ADB list forward error", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user