Skip to content

Commit

Permalink
Use a table for displaying traces.
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy Till committed Aug 11, 2015
1 parent 0c34dee commit 24a55c1
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 40 deletions.
108 changes: 95 additions & 13 deletions src/main/java/erlyberly/DbgTraceView.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
package erlyberly;

import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.OverrunStyle;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.Separator;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
Expand All @@ -42,7 +47,7 @@ public class DbgTraceView extends VBox {

private final FilteredList<TraceLog> filteredTraces = new FilteredList<TraceLog>(sortedTtraces);

private ListView<TraceLog> tracesBox;
private final TableView<TraceLog> tracesBox;

/**
* Set insertTracesAtTop=true in the .erlyberly file in your home directory to
Expand All @@ -53,15 +58,18 @@ public class DbgTraceView extends VBox {
public DbgTraceView(DbgController dbgController) {
setSpacing(5d);
setStyle("-fx-background-insets: 5;");
setMaxHeight(Double.MAX_VALUE);

insertTracesAtTop = PrefBind.getOrDefault("insertTracesAtTop", "false").equals("true");

dbgController.getTraceLogs().addListener(this::traceLogsChanged);

tracesBox = new ListView<TraceLog>();
tracesBox = new TableView<TraceLog>();
tracesBox.setOnMouseClicked(this::onTraceClicked);
tracesBox.setCellFactory(new TraceLogListCellFactory());
tracesBox.setMaxHeight(Double.MAX_VALUE);
VBox.setVgrow(tracesBox, Priority.ALWAYS);

putTableColumns();

Bindings.bindContentBidirectional(tracesBox.getItems(), filteredTraces);
HBox.setHgrow(tracesBox, Priority.ALWAYS);

putTraceContextMenu();

Expand All @@ -81,13 +89,87 @@ public DbgTraceView(DbgController dbgController) {
hBox.getChildren().add(clearTraceLogsButton);

getChildren().addAll(hBox, tracesBox);

dbgController.getTraceLogs().addListener(this::traceLogsChanged);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
private void putTableColumns() {
TableColumn<TraceLog,String> pidColumn;
pidColumn = new TableColumn<TraceLog,String>("Pid");
pidColumn.setCellValueFactory(new PropertyValueFactory("pid"));

TableColumn<TraceLog,String> regNameColumn;
regNameColumn = new TableColumn<TraceLog,String>("Reg. Name");
regNameColumn.setCellValueFactory(new PropertyValueFactory("regName"));

TableColumn<TraceLog,String> durationNameColumn;
durationNameColumn = new TableColumn<TraceLog,String>("Duration");
durationNameColumn.setCellValueFactory(new PropertyValueFactory("duration"));

TableColumn<TraceLog,String> functionnNameColumn;
functionnNameColumn = new TableColumn<TraceLog,String>("Function");
functionnNameColumn.setCellValueFactory(new PropertyValueFactory("function"));

TableColumn<TraceLog,String> argsColumn;
argsColumn = new TableColumn<TraceLog,String>("Args");
argsColumn.setCellValueFactory(new PropertyValueFactory("args"));

TableColumn<TraceLog,String> resultColumn;
resultColumn = new TableColumn<TraceLog,String>("Result");
resultColumn.setCellValueFactory(new PropertyValueFactory("result"));

tracesBox.getColumns().setAll(
pidColumn, regNameColumn, durationNameColumn, functionnNameColumn, argsColumn, resultColumn
);

// based on http://stackoverflow.com/questions/27015961/tableview-row-style
PseudoClass exceptionClass = PseudoClass.getPseudoClass("exception");
PseudoClass notCompletedClass = PseudoClass.getPseudoClass("not-completed");
tracesBox.setRowFactory(tv -> {
TableRow<TraceLog> row = new TableRow<>();
row.itemProperty().addListener((obs, oldTl, tl) -> {
if (tl != null) {
row.pseudoClassStateChanged(exceptionClass, tl.isExceptionThrower());
row.pseudoClassStateChanged(notCompletedClass, !tl.isComplete());
}
else {
row.pseudoClassStateChanged(exceptionClass, false);
row.pseudoClassStateChanged(notCompletedClass, false);
}
});
return row ;
});

tracesBox.setRowFactory(tv -> {
TableRow<TraceLog> row = new TableRow<>();
ChangeListener<Boolean> completeListener = (obs, oldComplete, newComplete) -> {
row.pseudoClassStateChanged(exceptionClass, row.getItem().isExceptionThrower());
row.pseudoClassStateChanged(notCompletedClass, !row.getItem().isComplete());
};
row.itemProperty().addListener((obs, oldTl, tl) -> {
if (oldTl != null) {
oldTl.isCompleteProperty().removeListener(completeListener);
}
if (tl != null) {
tl.isCompleteProperty().addListener(completeListener);
row.pseudoClassStateChanged(exceptionClass, tl.isExceptionThrower());
row.pseudoClassStateChanged(notCompletedClass, !tl.isComplete());
}
else {
row.pseudoClassStateChanged(exceptionClass, false);
row.pseudoClassStateChanged(notCompletedClass, false);
}
});
return row ;
});
}

private void putTraceContextMenu() {
TraceContextMenu traceContextMenu = new TraceContextMenu();
traceContextMenu.setItems(traceLogs);
traceContextMenu
.setSelectedItems(tracesBox.getSelectionModel().getSelectedItems());
.setSelectedItems(tracesBox.getItems());

tracesBox.setContextMenu(traceContextMenu);
tracesBox.selectionModelProperty().get().setSelectionMode(SelectionMode.MULTIPLE);
Expand All @@ -103,7 +185,7 @@ private void onTraceClicked(MouseEvent me) {
if(me.getClickCount() == 2) {
TraceLog selectedItem = tracesBox.getSelectionModel().getSelectedItem();

if(selectedItem != null) {
if(selectedItem != null && selectedItem != null) {
showTraceTermView(selectedItem);
}
}
Expand Down Expand Up @@ -135,20 +217,20 @@ private TermTreeView newTermTreeView() {


private void showTraceTermView(final TraceLog traceLog) {
OtpErlangObject args = traceLog.getArgs();
OtpErlangObject result = traceLog.getResult();
OtpErlangObject args = traceLog.getArgsList();
OtpErlangObject result = traceLog.getResultFromMap();

TermTreeView resultTermsTreeView, argTermsTreeView;

resultTermsTreeView = newTermTreeView();

if(result != null) {
resultTermsTreeView.populateFromTerm(traceLog.getResult());
resultTermsTreeView.populateFromTerm(traceLog.getResultFromMap());
}
else {
WeakChangeListener<Boolean> listener = new WeakChangeListener<Boolean>((o, oldV, newV) -> {
if(newV)
resultTermsTreeView.populateFromTerm(traceLog.getResult());
resultTermsTreeView.populateFromTerm(traceLog.getResultFromMap());
});

traceLog.isCompleteProperty().addListener(listener);
Expand Down
97 changes: 80 additions & 17 deletions src/main/java/erlyberly/TraceLog.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,31 @@ public class TraceLog implements Comparable<TraceLog> {

private final SimpleStringProperty summary = new SimpleStringProperty("");

private final SimpleStringProperty duration = new SimpleStringProperty("");

private final SimpleStringProperty result = new SimpleStringProperty("");

private final SimpleBooleanProperty complete = new SimpleBooleanProperty(false);

private String tracePropsToString;


private final String pid, registeredName, function, argsString;

public TraceLog(HashMap<Object, Object> map) {
this.map = map;
instanceNum = instanceCounter.incrementAndGet();
pid = getPidString();
registeredName = regNameString().intern();
function = appendFunctionToString(new StringBuilder(), true).toString().intern();
argsString = appendArgsToString(new StringBuilder(), getArgsList().elements()).toString();
}

private String regNameString() {
Object object = map.get(ATOM_REG_NAME);
if(ATOM_UNDEFINED.equals(object))
return "";
return object.toString();
}

public SimpleStringProperty summaryProperty() {
if(summary.get().isEmpty()) {
Expand Down Expand Up @@ -100,30 +117,31 @@ private StringBuilder toCallString(StringBuilder sb, boolean appendArity) {
return sb;
}

private void appendTimeStampToString(StringBuilder sb) {
private StringBuilder appendTimeStampToString(StringBuilder sb) {
Object tsCall = map.get(TIMESTAMP_CALL_ATOM);
Object tsReturn = map.get(TIMESTAMP_RETURN_ATOM);

if(tsCall == null|| tsReturn == null)
return;
return sb;

long us = ((OtpErlangLong)tsReturn).longValue() - ((OtpErlangLong)tsCall).longValue();

sb.append("+")
.append(us)
.append("us ");
return sb;
}

private OtpErlangTuple getFunction() {
private OtpErlangTuple getFunctionFromMap() {
return (OtpErlangTuple)map.get(new OtpErlangAtom("fn"));
}

public boolean isExceptionThrower() {
return map.containsKey(EXCEPTION_FROM_ATOM);
}

public void appendFunctionToString(StringBuilder sb, boolean appendArity) {
OtpErlangTuple tuple = getFunction();
public StringBuilder appendFunctionToString(StringBuilder sb, boolean appendArity) {
OtpErlangTuple tuple = getFunctionFromMap();
OtpErlangAtom mod = (OtpErlangAtom) tuple.elementAt(0);
OtpErlangAtom func = (OtpErlangAtom) tuple.elementAt(1);

Expand All @@ -145,20 +163,26 @@ public void appendFunctionToString(StringBuilder sb, boolean appendArity) {
sb.append("(");

OtpErlangObject[] elements = args.elements();
if(elements.length > 0)
OtpUtil.otpObjectToString(elements[0], sb);

for(int i=1; i<elements.length; i++) {
sb.append(", ");
OtpUtil.otpObjectToString(elements[i], sb);
}
appendArgsToString(sb, elements);

sb.append(")");
}
return sb;
}

public OtpErlangObject getArgs() {
OtpErlangTuple tuple = getFunction();
private StringBuilder appendArgsToString(StringBuilder sb, OtpErlangObject[] elements) {
if(elements.length > 0)
OtpUtil.otpObjectToString(elements[0], sb);

for(int i=1; i<elements.length; i++) {
sb.append(", ");
OtpUtil.otpObjectToString(elements[i], sb);
}
return sb;
}

public OtpErlangList getArgsList() {
OtpErlangTuple tuple = getFunctionFromMap();
OtpErlangList args = OtpUtil.toOtpList(tuple.elementAt(2));
return args;
}
Expand All @@ -169,7 +193,7 @@ public String getPidString() {
return s.stringValue();
}

public OtpErlangObject getResult() {
public OtpErlangObject getResultFromMap() {
Object object = map.get(RESULT_ATOM);
if(object == null) {
OtpErlangTuple exception = (OtpErlangTuple) map.get(EXCEPTION_FROM_ATOM);
Expand All @@ -195,11 +219,15 @@ public void complete(HashMap<Object, Object> resultMap) {

if(e != null)
map.put(EXCEPTION_FROM_ATOM, e);
if(r != null)
if(r != null) {
map.put(RESULT_ATOM, r);
result.set(OtpUtil.otpObjectToString((OtpErlangObject) r, new StringBuilder()).toString());
}
if(ts != null)
map.put(TIMESTAMP_RETURN_ATOM, ts);

duration.set(appendTimeStampToString(new StringBuilder()).toString());

Platform.runLater(() -> { summary.set(toString()); complete.set(true); });
}

Expand All @@ -211,9 +239,44 @@ public boolean isComplete() {
return complete.get();
}

/**
* A call string is the pid and function with arity.
*/
public String toCallString() {
StringBuilder sb = new StringBuilder(255);
boolean appendArity = true;
return toCallString(sb, appendArity).toString();
}

public String getPid() {
return pid;
}

public String getRegName() {
return registeredName;
}

public String getDuration() {
return duration.get();
}

public SimpleStringProperty durationProperty() {
return duration;
}

public String getFunction() {
return function;
}

public String getArgs() {
return argsString;
}

public String getResult() {
return result.get();
}

public SimpleStringProperty resultProperty() {
return result;
}
}
3 changes: 2 additions & 1 deletion src/main/java/erlyberly/node/OtpUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public static HashMap<Object, Object> propsToMap(OtpErlangList pinfo) {
return map;
}

public static void otpObjectToString(OtpErlangObject obj, StringBuilder sb) {
public static StringBuilder otpObjectToString(OtpErlangObject obj, StringBuilder sb) {
if(obj instanceof OtpErlangBinary) {
sb.append(binaryToString((OtpErlangBinary) obj));
}
Expand All @@ -117,6 +117,7 @@ else if(obj instanceof OtpErlangString) {
else {
sb.append(obj.toString());
}
return sb;
}

public static String bracketsForTerm(OtpErlangObject obj) {
Expand Down
Loading

0 comments on commit 24a55c1

Please sign in to comment.