/**
* erlyberly, erlang trace debugger
* Copyright (C) 2016 Andy Till
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package erlyberly;
import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangList;
import com.ericsson.otp.erlang.OtpErlangLong;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangRangeException;
import com.ericsson.otp.erlang.OtpErlangString;
import com.ericsson.otp.erlang.OtpErlangTuple;
import erlyberly.StackTraceView.ErlyberlyStackTraceElement;
import erlyberly.node.OtpUtil;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
public class StackTraceView extends ListView<ErlyberlyStackTraceElement> {
@SuppressWarnings({ "unchecked", "rawtypes" })
public StackTraceView() {
applyStackTraceCellFactory((ListView)this);
}
private void applyStackTraceCellFactory(ListView<ErlyberlyStackTraceElement> listview) {
listview.setCellFactory(new Callback<ListView<ErlyberlyStackTraceElement>, ListCell<ErlyberlyStackTraceElement>>() {
@Override
public ListCell<ErlyberlyStackTraceElement> call(ListView<ErlyberlyStackTraceElement> param) {
return new ListCell<ErlyberlyStackTraceElement>() {
private final Hyperlink functionLink = new Hyperlink();
{
setGraphic(functionLink);
functionLink.setOnAction((e) -> {
ErlyberlyStackTraceElement stackElement = getItem();
if(stackElement == null)
return;
if(stackElement.isError())
return;
ModFunc mf = stackElement.getModFunc();
try {
String source = ErlyBerly.nodeAPI().moduleFunctionSourceCode(
mf.getModuleName(), mf.getFuncName(), mf.getArity());
ErlyBerly.showPane(
"Crash Report Stack",
ErlyBerly.wrapInPane(new CodeView(source))
);
} catch (Exception e1) {
e1.printStackTrace();
}
});
}
@Override
public void updateItem(ErlyberlyStackTraceElement item, boolean empty) {
super.updateItem(item, empty);
if(item == null)
functionLink.setText("");
else
functionLink.setText(item.toString());
}
};
}
});
}
public void populateFromCrashReport(CrashReport crashReport) {
try {
getItems().addAll(crashReport.<ErlyberlyStackTraceElement>mapStackTraces((module, function, arity, file, line) -> {
try {
ModFunc modFunc = mfaToModFunc(module, function, arity);
return new ErlyberlyStackTraceElement(modFunc, file.toString(), line.longValue());
}
catch (Exception e) {
throw new RuntimeException(e);
}
}));
} catch (Exception e) {
e.printStackTrace();
}
}
private ModFunc mfaToModFunc(OtpErlangAtom module, OtpErlangAtom function, OtpErlangLong arity) {
boolean exported = false;
boolean synthetic = false;
try {
return new ModFunc(module.toString(), function.toString(), arity.intValue(), exported, synthetic);
}
catch (OtpErlangRangeException e) {
throw new RuntimeException(e);
}
}
public void populateFromMfaList(OtpErlangList stackTrace) {
try {
for (OtpErlangObject obj : stackTrace) {
if(obj instanceof OtpErlangString) {
OtpErlangString errorMessage = (OtpErlangString) obj;
getItems().add(new ErlyberlyStackTraceElement(errorMessage.stringValue()));
}
else {
OtpErlangTuple tuple = OtpUtil.toTuple(obj);
OtpErlangAtom module = (OtpErlangAtom) tuple.elementAt(0);
OtpErlangAtom function = (OtpErlangAtom) tuple.elementAt(1);
OtpErlangLong arity = (OtpErlangLong) tuple.elementAt(2);
ModFunc modFunc = mfaToModFunc(module, function, arity);
getItems().add(new ErlyberlyStackTraceElement(modFunc, "", 0L));
}
}
}
catch (Exception e) {
// try/catch so if there is a problem decoding the process_dump, it won't
// stop the trace log from being shown.
e.printStackTrace();
}
}
public boolean isStackTracesEmpty() {
return getItems().isEmpty();
}
/**
* Put erlyberly on the front of this because there is already a StaceTraceElement
* class in the standard library.
*/
static class ErlyberlyStackTraceElement {
private final ModFunc modFunc;
private final long line;
private final String file;
private final String error;
public ErlyberlyStackTraceElement(ModFunc modFunc, String file, long line) {
this.modFunc = modFunc;
this.file = file;
this.line = line;
this.error = null;
}
public ErlyberlyStackTraceElement(String anError) {
this.modFunc = null;
this.file = null;
this.line = -1L;
this.error = anError;
}
public ModFunc getModFunc() {
return modFunc;
}
public boolean isError() {
return error != null;
}
@Override
public String toString() {
if(isError()) {
return error;
}
String display = modFunc.toFullString();
if(file != null && !file.isEmpty()) {
display += " (" + file + ":" + line +")";
}
return display;
}
}
}