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 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 map) { this.map = map; @@ -49,24 +53,16 @@ public SimpleStringProperty summaryProperty() { @Override public String toString() { - return tracePropsToString(); + if(tracePropsToString == null) + tracePropsToString = tracePropsToString(); + return tracePropsToString; } private String tracePropsToString() { StringBuilder sb = new StringBuilder(1024); - - OtpErlangAtom regName = (OtpErlangAtom) map.get(ATOM_REG_NAME); - - if(!ATOM_UNDEFINED.equals(regName)) { - sb.append(regName.atomValue()); - } - else { - OtpErlangString pidString = (OtpErlangString) map.get(ATOM_PID); - sb.append(pidString.stringValue()); - } - sb.append(" "); - - appendFunctionToString(sb); + + boolean appendArity = false; + toCallString(sb, appendArity); sb.append(" => "); @@ -85,6 +81,39 @@ else if(isExceptionThrower()) { return sb.toString(); } + private StringBuilder toCallString(StringBuilder sb, boolean appendArity) { + OtpErlangAtom regName = (OtpErlangAtom) map.get(ATOM_REG_NAME); + + if(!ATOM_UNDEFINED.equals(regName)) { + sb.append(regName.atomValue()); + } + else { + OtpErlangString pidString = (OtpErlangString) map.get(ATOM_PID); + sb.append(pidString.stringValue()); + } + sb.append(" "); + + appendTimeStampToString(sb); + + appendFunctionToString(sb, appendArity); + + return sb; + } + + private void appendTimeStampToString(StringBuilder sb) { + Object tsCall = map.get(TIMESTAMP_CALL_ATOM); + Object tsReturn = map.get(TIMESTAMP_RETURN_ATOM); + + if(tsCall == null|| tsReturn == null) + return; + + long us = ((OtpErlangLong)tsReturn).longValue() - ((OtpErlangLong)tsCall).longValue(); + + sb.append("+") + .append(us) + .append("us "); + } + private OtpErlangTuple getFunction() { return (OtpErlangTuple)map.get(new OtpErlangAtom("fn")); } @@ -93,7 +122,7 @@ public boolean isExceptionThrower() { return map.containsKey(EXCEPTION_FROM_ATOM); } - public void appendFunctionToString(StringBuilder sb) { + public void appendFunctionToString(StringBuilder sb, boolean appendArity) { OtpErlangTuple tuple = getFunction(); OtpErlangAtom mod = (OtpErlangAtom) tuple.elementAt(0); OtpErlangAtom func = (OtpErlangAtom) tuple.elementAt(1); @@ -105,19 +134,27 @@ public void appendFunctionToString(StringBuilder sb) { sb .append(mod.atomValue()) .append(":") - .append(func.atomValue()) - .append("("); - - OtpErlangObject[] elements = args.elements(); - if(elements.length > 0) - OtpUtil.otpObjectToString(elements[0], sb); + .append(func.atomValue()); - for(int i=1; i 0) + OtpUtil.otpObjectToString(elements[0], sb); + + for(int i=1; i resultMap) { + tracePropsToString = null; + Object e = resultMap.get(EXCEPTION_FROM_ATOM); Object r = resultMap.get(RESULT_ATOM); + Object ts = resultMap.get(TIMESTAMP_RETURN_ATOM); if(e != null) map.put(EXCEPTION_FROM_ATOM, e); if(r != null) map.put(RESULT_ATOM, r); + if(ts != null) + map.put(TIMESTAMP_RETURN_ATOM, ts); Platform.runLater(() -> { summary.set(toString()); complete.set(true); }); } @@ -168,4 +210,10 @@ public ReadOnlyBooleanProperty isCompleteProperty() { public boolean isComplete() { return complete.get(); } + + public String toCallString() { + StringBuilder sb = new StringBuilder(255); + boolean appendArity = true; + return toCallString(sb, appendArity).toString(); + } } diff --git a/src/main/java/erlyberly/node/NodeAPI.java b/src/main/java/erlyberly/node/NodeAPI.java index 98629f4..e01b068 100644 --- a/src/main/java/erlyberly/node/NodeAPI.java +++ b/src/main/java/erlyberly/node/NodeAPI.java @@ -153,7 +153,7 @@ public void run() { private void loadRemoteErlyberly() throws IOException, OtpErlangException { OtpErlangBinary otpErlangBinary = new OtpErlangBinary(loadBeamFile()); - System.out.println("binary size=" + otpErlangBinary.size()); + sendRPC("code", "load_binary", list( atom("erlyberly"), @@ -412,8 +412,6 @@ public synchronized OtpErlangObject callGraph(OtpErlangAtom module, OtpErlangAto OtpErlangObject result = (OtpErlangObject) receiveRPC(); - System.out.println(result); - return result; } @@ -423,6 +421,7 @@ public synchronized String moduleFunctionSourceCode(String module, String functi OtpErlangObject result = receiveRPC(); return returnCode(result, "Failed to get source code for " + module + ":" + function + "/" + arity.toString() + "."); } + public synchronized String moduleFunctionSourceCode(String module) throws IOException, OtpErlangException { sendRPC("erlyberly", "get_source_code", list( atom(module) )); OtpErlangObject result = receiveRPC(); diff --git a/src/main/java/erlyberly/node/TraceManager.java b/src/main/java/erlyberly/node/TraceManager.java index ee96c4b..3f18663 100644 --- a/src/main/java/erlyberly/node/TraceManager.java +++ b/src/main/java/erlyberly/node/TraceManager.java @@ -2,8 +2,7 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedList; -import java.util.Queue; +import java.util.Stack; import com.ericsson.otp.erlang.OtpErlangAtom; import com.ericsson.otp.erlang.OtpErlangList; @@ -21,7 +20,7 @@ public class TraceManager { private static final OtpErlangAtom CALL_ATOM = new OtpErlangAtom("call"); - private final HashMap> unfinishedCalls = new HashMap>(); + private final HashMap> unfinishedCalls = new HashMap>(); public ArrayList collateTraces(OtpErlangList traceLogs) { ArrayList traceList = new ArrayList(); @@ -33,14 +32,14 @@ public ArrayList collateTraces(OtpErlangList traceLogs) { if(CALL_ATOM.equals(traceType)) { TraceLog trace = proplistToTraceLog(tup); - Queue queue = unfinishedCalls.get(trace.getPidString()); + Stack stack = unfinishedCalls.get(trace.getPidString()); - if(queue == null) - queue = new LinkedList(); + if(stack == null) + stack = new Stack(); - queue.add(trace); + stack.add(trace); - unfinishedCalls.put(trace.getPidString(), queue); + unfinishedCalls.put(trace.getPidString(), stack); traceList.add(trace); } @@ -52,15 +51,19 @@ else if(RETURN_FROM_ATOM.equals(traceType) || EXCEPTION_FROM_ATOM.equals(traceTy if(object != null) { OtpErlangString pidString = (OtpErlangString) object; - Queue queue = unfinishedCalls.get(pidString.stringValue()); - TraceLog traceLog = queue.poll(); + Stack stack = unfinishedCalls.get(pidString.stringValue()); + if(stack == null) + continue; + + TraceLog traceLog = stack.pop(); traceLog.complete(map); - if(queue.isEmpty()) + if(stack.isEmpty()) unfinishedCalls.remove(pidString.stringValue()); } } + } return traceList; diff --git a/src/main/resources/erlyberly/beam/erlyberly.beam b/src/main/resources/erlyberly/beam/erlyberly.beam index b654637..78ff114 100644 Binary files a/src/main/resources/erlyberly/beam/erlyberly.beam and b/src/main/resources/erlyberly/beam/erlyberly.beam differ diff --git a/src/main/resources/erlyberly/beam/erlyberly.erl b/src/main/resources/erlyberly/beam/erlyberly.erl index e421e78..dd122de 100644 --- a/src/main/resources/erlyberly/beam/erlyberly.erl +++ b/src/main/resources/erlyberly/beam/erlyberly.erl @@ -193,55 +193,59 @@ tcollector_start_trace({start_trace, Mod, Func, Arity, IsExported}, #tcollector{ true -> dbg:tp(Mod, Func, Arity, cx); false -> dbg:tpl(Mod, Func, Arity, cx) end, - dbg:p(all, c), + dbg:p(all, [c, timestamp]), Trace_spec = {Mod, Func, Arity, IsExported}, TC#tcollector{ traces = [Trace_spec | Traces] }. + %% -collect_log({trace, _, return_from, {code, ensure_loaded, _}, _}, TC) -> +collect_log({trace_ts, _, return_from, {code, ensure_loaded, _}, _, _Timestamp}, TC) -> % ensure loaded can be called many times for one reload so just skip it TC; -collect_log({trace, _, return_from, {code, _, _}, {module, Loaded_module}}, TC) -> +collect_log({trace_ts, _, return_from, {code, _, _}, {module, Loaded_module}, _Timestamp}, TC) -> % if we trace that a module is reloaded then reapply traces to it ok = reapply_traces(Loaded_module, TC#tcollector.traces), TC; -collect_log({trace, _, _, {code, _, _}, _}, TC) -> +collect_log({trace_ts, _, _, {code, _, _}, _}, TC) -> TC; -collect_log({trace, _, _, {code, _, _}}, TC) -> +collect_log({trace_ts, _, _, {code, _, _}}, TC) -> TC; -collect_log(Trace, #tcollector{ logs = Logs } = TC) when element(1, Trace) == trace -> +collect_log(Trace, #tcollector{ logs = Logs } = TC) when element(1, Trace) == trace_ts -> Logs_1 = maybe_add_log(trace_to_props(Trace), Logs), TC#tcollector{ logs = Logs_1 }; collect_log(U, TC) -> - io:format("unknown trace ~p", [U]), + io:format("unknown trace ~p~n", [U]), TC. %% maybe_add_log(skip, Logs) -> Logs; maybe_add_log(Log, Logs) -> [Log | Logs]. %% -trace_to_props({trace, Pid, call, Func}) -> +% trace_to_props({trace_ts, Pid, call, Func}) -> +% {call, +% [ {pid, pid_to_list(Pid)}, +% {reg_name, get_registered_name(Pid)}, +% {fn, Func} ]}; +trace_to_props({trace_ts, Pid, call, Func, _, Timestamp}) -> {call, [ {pid, pid_to_list(Pid)}, {reg_name, get_registered_name(Pid)}, - {fn, Func} ]}; -trace_to_props({trace, Pid, call, Func, _}) -> - {call, - [ {pid, pid_to_list(Pid)}, - {reg_name, get_registered_name(Pid)}, - {fn, Func} ]}; -trace_to_props({trace, Pid, exception_from, Func, {Class, Value}}) -> + {fn, Func}, + {timetamp_call_us, timestamp_to_us(Timestamp)} ]}; +trace_to_props({trace_ts, Pid, exception_from, Func, {Class, Value}, Timestamp}) -> {exception_from, [ {pid, pid_to_list(Pid)}, {reg_name, get_registered_name(Pid)}, {fn, Func}, - {exception_from, {Class, Value}} ]}; -trace_to_props({trace, Pid, return_from, Func, Result}) -> + {exception_from, {Class, Value}}, + {timetamp_return_us, timestamp_to_us(Timestamp)} ]}; +trace_to_props({trace_ts, Pid, return_from, Func, Result, Timestamp}) -> {return_from, [ {pid, pid_to_list(Pid)}, {reg_name, get_registered_name(Pid)}, {fn, Func}, - {result, Result} ]}; + {result, Result}, + {timetamp_return_us, timestamp_to_us(Timestamp)} ]}; trace_to_props(U) -> - io:format("skipped trace ~p", [U]), + io:format("skipped trace_ts ~p~n", [U]), skip. @@ -278,6 +282,9 @@ get_registered_name(Pid) -> _ -> undefined end. +timestamp_to_us({Mega, Sec, Micros}) -> + (((Mega * 1000000) + Sec) * 1000000) + Micros. + %%% ============================================================================= %%% %%% seq_trace