diff --git a/pom.xml b/pom.xml
index 60d34a0..f26dbfb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
andytill
erlyberly
- 0.6.2
+ 0.6.3
jar
erlyberly
diff --git a/src/main/java/erlyberly/CallGraphView.java b/src/main/java/erlyberly/CallGraphView.java
index 7b6f99a..1bd76d4 100644
--- a/src/main/java/erlyberly/CallGraphView.java
+++ b/src/main/java/erlyberly/CallGraphView.java
@@ -1,5 +1,8 @@
package erlyberly;
+import java.util.Arrays;
+import java.util.List;
+
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
@@ -14,6 +17,14 @@
public class CallGraphView extends TreeView {
+
+ /**
+ * A list of module names that will not be expanded in the call graph tree, since
+ * they have lots of sub-calls, cluttering up the tree and it is assumed most usage
+ * will be for application calls, not for the standard libs.
+ */
+ private static List UNEXPANDED_MODULES = Arrays.asList(
+ "erlang", "gen_server", "io", "io_lib", "lists", "rpc", "unicode");
private ModFuncContextMenu modFuncContextMenu;
@@ -70,7 +81,9 @@ private void populateCallGraph(TreeItem parentModFuncItem, OtpErlangTup
TreeItem modFuncItem;
modFuncItem = new TreeItem<>(modFunc);
- modFuncItem.setExpanded(true);
+ String atomString = module.atomValue();
+ boolean value = !UNEXPANDED_MODULES.contains(atomString);
+ modFuncItem.setExpanded(value);
parentModFuncItem.getChildren().add(modFuncItem);
@@ -82,17 +95,4 @@ private void populateCallGraph(TreeItem parentModFuncItem, OtpErlangTup
e.printStackTrace();
}
}
-
- class CGModFunc {
- private final ModFunc modFunc;
-
- public CGModFunc(ModFunc aModFunc) {
- modFunc = aModFunc;
- }
-
- @Override
- public String toString() {
- return modFunc.toFullString();
- }
- }
}
diff --git a/src/main/java/erlyberly/DbgView.java b/src/main/java/erlyberly/DbgView.java
index 9df9a46..bb58d88 100644
--- a/src/main/java/erlyberly/DbgView.java
+++ b/src/main/java/erlyberly/DbgView.java
@@ -25,13 +25,12 @@
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
+import javafx.scene.control.SelectionMode;
import javafx.scene.control.Separator;
import javafx.scene.control.SplitPane;
import javafx.scene.control.SplitPane.Divider;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
-import javafx.scene.input.KeyCode;
-import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
@@ -94,8 +93,15 @@ public class DbgView implements Initializable {
private ModFuncContextMenu modFuncContextMenu;
+ /**
+ * Set insertTracesAtTop=true in the .erlyberly file in your home directory to
+ * make traces be inserted at the top of the list.
+ */
+ private boolean insertTracesAtTop;
+
@Override
public void initialize(URL url, ResourceBundle r) {
+ insertTracesAtTop = PrefBind.getOrDefault("insertTracesAtTop", "false").equals("true");
modFuncContextMenu = new ModFuncContextMenu(dbgController);
modulesTree
@@ -122,7 +128,7 @@ public void initialize(URL url, ResourceBundle r) {
tracesBox.setCellFactory(new TraceLogListCellFactory());
modulesTree.setCellFactory(new ModFuncTreeCellFactory(dbgController));
- modulesTree.setOnKeyReleased(this::onKeyReleaseInModuleTree);
+ /*modulesTree.setOnKeyPressed(this::onKeyPressInModuleTree);*/
modulesTree.setContextMenu(modFuncContextMenu);
Bindings.bindContentBidirectional(tracesBox.getItems(), filteredTraces);
@@ -132,26 +138,14 @@ public void initialize(URL url, ResourceBundle r) {
addTraceLogFloatySearchControl();
dbgController.initialize(url, r);
- }
-
- /**
- * if ctrl+t
is released while focus is on the tree view, then check the
- * selected item is a function and toggle tracing for it.
- */
- private void onKeyReleaseInModuleTree(KeyEvent e) {
- TreeItem item = modulesTree.getSelectionModel().getSelectedItem();
+ TraceContextMenu traceContextMenu = new TraceContextMenu();
+ traceContextMenu.setItems(traceLogs);
+ traceContextMenu
+ .setSelectedItems(tracesBox.getSelectionModel().getSelectedItems());
- if(!e.isControlDown())
- return;
- if(e.getCode() != KeyCode.T)
- return;
- if(item == null || item.getValue() == null)
- return;
- if(item.getValue().isModule())
- return;
-
- toggleTraceModFunc(item.getValue());
+ tracesBox.setContextMenu(traceContextMenu);
+ tracesBox.selectionModelProperty().get().setSelectionMode(SelectionMode.MULTIPLE);
}
private FxmlLoadable addModulesFloatySearchControl() {
@@ -199,7 +193,10 @@ private FxmlLoadable addTraceLogFloatySearchControl() {
public void traceLogsChanged(ListChangeListener.Change extends TraceLog> e) {
while(e.next()) {
for (TraceLog trace : e.getAddedSubList()) {
- traceLogs.add(0, trace);
+ if(insertTracesAtTop)
+ traceLogs.add(0, trace);
+ else
+ traceLogs.add(trace);
}
}
}
@@ -318,7 +315,8 @@ private void showTraceTermView(final TraceLog traceLog) {
StringBuilder sb = new StringBuilder(traceLog.getPidString());
sb.append(" ");
- traceLog.appendFunctionToString(sb);
+ boolean appendArity = false;
+ traceLog.appendFunctionToString(sb, appendArity);
showWindow(splitPane, sb);
}
@@ -355,11 +353,6 @@ private boolean isMatchingModFunc(String searchText, TreeItem t) {
return true;
return t.getValue().toString().contains(searchText);
}
-
- private void toggleTraceModFunc(ModFunc function) {
- dbgController.toggleTraceModFunc(function);
- }
-
private void onConnected(Observable o) {
boolean connected = ErlyBerly.nodeAPI().connectedProperty().get();
diff --git a/src/main/java/erlyberly/ModFuncContextMenu.java b/src/main/java/erlyberly/ModFuncContextMenu.java
index 93a5052..0d2a6b2 100644
--- a/src/main/java/erlyberly/ModFuncContextMenu.java
+++ b/src/main/java/erlyberly/ModFuncContextMenu.java
@@ -13,6 +13,7 @@
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.control.TreeItem;
+import javafx.scene.input.KeyCombination;
import javafx.stage.Stage;
import com.ericsson.otp.erlang.OtpErlangException;
@@ -47,10 +48,15 @@ public ModFuncContextMenu(DbgController aDbgController) {
selectedItem = new SimpleObjectProperty<>();
selectedTreeItem = new SimpleObjectProperty<>();
- MenuItem seqTraceMenuItem, functionTraceMenuItem, moduleTraceMenuItem, callGraphMenuItem, moduleSourceCodeItem, moduleAbstCodeItem;
+ MenuItem functionTraceMenuItem, exportsTraceMenuItem, moduleTraceMenuItem, seqTraceMenuItem, callGraphMenuItem, moduleSourceCodeItem, moduleAbstCodeItem;
functionTraceMenuItem = new MenuItem("Function Trace");
functionTraceMenuItem.setOnAction(this::onFunctionTrace);
+ functionTraceMenuItem.setAccelerator(KeyCombination.keyCombination("shortcut+t"));
+
+ exportsTraceMenuItem = new MenuItem("Exported Function Trace");
+ exportsTraceMenuItem.setOnAction(this::onExportedFunctionTrace);
+ exportsTraceMenuItem.setAccelerator(KeyCombination.keyCombination("shortcut+e"));
moduleTraceMenuItem = new MenuItem("Recursive Trace");
moduleTraceMenuItem.setOnAction(this::onModuleTrace);
@@ -66,10 +72,9 @@ public ModFuncContextMenu(DbgController aDbgController) {
callGraphMenuItem = new MenuItem("View Call Graph");
callGraphMenuItem.setOnAction(this::onViewCallGraph);
-
getItems().addAll(
- functionTraceMenuItem, moduleTraceMenuItem, seqTraceMenuItem,
+ functionTraceMenuItem, exportsTraceMenuItem, moduleTraceMenuItem, seqTraceMenuItem,
new SeparatorMenuItem(),
callGraphMenuItem, moduleSourceCodeItem, moduleAbstCodeItem);
}
@@ -134,6 +139,28 @@ private void onModuleTrace(ActionEvent e) {
toggleTraceMod(funcs);
}
+
+ private void onExportedFunctionTrace(ActionEvent e) {
+ TreeItem selectedItem = selectedTreeItemProperty().get();
+
+ if(selectedItem == null)
+ return;
+
+ // get all the functions we may trace
+ HashSet funcs = new HashSet();
+ recurseModFuncItems(selectedItem, funcs);
+
+ // filter the exported ones
+ HashSet exported = new HashSet();
+ for (ModFunc modFunc : funcs) {
+ if(modFunc.isExported()) {
+ exported.add(modFunc);
+ }
+ }
+
+ // trace 'em
+ toggleTraceMod(exported);
+ }
private void recurseModFuncItems(TreeItem item, HashSet funcs) {
if(item == null)
diff --git a/src/main/java/erlyberly/PrefBind.java b/src/main/java/erlyberly/PrefBind.java
index ece68bd..869ada5 100644
--- a/src/main/java/erlyberly/PrefBind.java
+++ b/src/main/java/erlyberly/PrefBind.java
@@ -30,7 +30,7 @@ public class PrefBind {
private static File erlyberlyConfig;
private static boolean awaitingStore;
-
+
public static void bind(final String propName, StringProperty stringProp) {
if(props == null) {
return;
@@ -88,4 +88,12 @@ public static void setup() throws IOException {
props = properties;
}
+
+ public static Object get(Object key) {
+ return props.get(key);
+ }
+
+ public static Object getOrDefault(String key, Object theDefault) {
+ return props.getOrDefault(key, theDefault);
+ }
}
diff --git a/src/main/java/erlyberly/TopBarView.java b/src/main/java/erlyberly/TopBarView.java
index 4d2ae5d..de882e1 100644
--- a/src/main/java/erlyberly/TopBarView.java
+++ b/src/main/java/erlyberly/TopBarView.java
@@ -38,11 +38,11 @@
import floatyfield.FloatyFieldView;
public class TopBarView implements Initializable {
- private static final KeyCodeCombination TOGGLE_HIDE_PROCESSES_SHORTCUT = new KeyCodeCombination(KeyCode.P, KeyCombination.CONTROL_DOWN);
+ private static final KeyCodeCombination TOGGLE_HIDE_PROCESSES_SHORTCUT = new KeyCodeCombination(KeyCode.P, KeyCombination.SHORTCUT_DOWN);
- private static final KeyCodeCombination TOGGLE_HIDE_MODULES_SHORTCUT = new KeyCodeCombination(KeyCode.M, KeyCombination.CONTROL_DOWN);
+ private static final KeyCodeCombination TOGGLE_HIDE_MODULES_SHORTCUT = new KeyCodeCombination(KeyCode.M, KeyCombination.SHORTCUT_DOWN);
- private static final KeyCodeCombination REFRESH_MODULES_SHORTCUT = new KeyCodeCombination(KeyCode.R, KeyCombination.CONTROL_DOWN);
+ private static final KeyCodeCombination REFRESH_MODULES_SHORTCUT = new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN);
@FXML
private ToggleButton hideProcessesButton;
diff --git a/src/main/java/erlyberly/TraceContextMenu.java b/src/main/java/erlyberly/TraceContextMenu.java
new file mode 100644
index 0000000..24fdaee
--- /dev/null
+++ b/src/main/java/erlyberly/TraceContextMenu.java
@@ -0,0 +1,81 @@
+package erlyberly;
+
+
+import java.util.ArrayList;
+
+import javafx.collections.ObservableList;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.control.ContextMenu;
+import javafx.scene.control.MenuItem;
+import javafx.scene.input.Clipboard;
+import javafx.scene.input.ClipboardContent;
+import javafx.scene.input.KeyCombination;
+
+public class TraceContextMenu extends ContextMenu {
+
+ private ObservableList items, selectedItems;
+
+ public TraceContextMenu() {
+ getItems().add(menuItem("Copy All", "shortcut+c", this::onCopy));
+ getItems().add(menuItem("Copy Function Call", null, this::onCopyCalls));
+ getItems().add(menuItem("Delete", "delete", this::onDelete));
+ }
+
+ private MenuItem menuItem(String text, String accelerator, EventHandler e) {
+ MenuItem menuItem;
+
+ menuItem = new MenuItem(text);
+ menuItem.setOnAction(e);
+
+ if(accelerator != null)
+ menuItem.setAccelerator(KeyCombination.keyCombination(accelerator));
+
+ return menuItem;
+ }
+
+ private void onCopy(ActionEvent e) {
+ StringBuilder sbuilder = new StringBuilder();
+
+ for (TraceLog traceLog : selectedItems) {
+ sbuilder.append(traceLog.toString()).append("\n");
+ }
+
+ copyToClipboard(sbuilder);
+ }
+
+ private void onCopyCalls(ActionEvent e) {
+ StringBuilder sbuilder = new StringBuilder();
+
+ for (TraceLog traceLog : selectedItems) {
+ sbuilder.append(traceLog.toCallString()).append("\n");
+ }
+
+ copyToClipboard(sbuilder);
+ }
+
+ private void copyToClipboard(StringBuilder sbuilder) {
+ final Clipboard clipboard = Clipboard.getSystemClipboard();
+ final ClipboardContent content = new ClipboardContent();
+
+ content.putString(sbuilder.toString());
+ clipboard.setContent(content);
+ }
+
+ private void onDelete(ActionEvent e) {
+ ArrayList arrayList = new ArrayList(selectedItems);
+
+ for (TraceLog traceLog : arrayList) {
+ items.remove(traceLog);
+ }
+ }
+
+
+ public void setSelectedItems(ObservableList selectedItems2) {
+ selectedItems = selectedItems2;
+ }
+
+ public void setItems(ObservableList items2) {
+ items = items2;
+ }
+}
diff --git a/src/main/java/erlyberly/TraceLog.java b/src/main/java/erlyberly/TraceLog.java
index e860649..a017201 100644
--- a/src/main/java/erlyberly/TraceLog.java
+++ b/src/main/java/erlyberly/TraceLog.java
@@ -10,6 +10,7 @@
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.OtpErlangString;
import com.ericsson.otp.erlang.OtpErlangTuple;
@@ -23,6 +24,8 @@ public class TraceLog implements Comparable {
public static final OtpErlangAtom ATOM_PID = new OtpErlangAtom("pid");
public static final OtpErlangAtom ATOM_REG_NAME = new OtpErlangAtom("reg_name");
public static final OtpErlangAtom ATOM_UNDEFINED = new OtpErlangAtom("undefined");
+ private static final Object TIMESTAMP_CALL_ATOM = new OtpErlangAtom("timetamp_call_us");
+ private static final Object TIMESTAMP_RETURN_ATOM = new OtpErlangAtom("timetamp_return_us");
private static final AtomicLong instanceCounter = new AtomicLong();
@@ -34,6 +37,7 @@ public class TraceLog implements Comparable {
private final SimpleBooleanProperty complete = new SimpleBooleanProperty(false);
+ private String tracePropsToString;
public TraceLog(HashMap