├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .travis.yml ├── Erlyberly.xml ├── LICENSE ├── README.md ├── _config.yml ├── checkstyle.xml ├── doc ├── call-graph.png ├── crash-report-button.png ├── erlyberly.png ├── exceptions.png ├── heap-pie.png ├── process-state.png ├── seq-trace.png ├── termview.png ├── view-abstract-source.png └── view-source.png ├── mvnw ├── pom.xml └── src ├── etc └── license_header ├── main ├── java │ ├── com │ │ └── ericsson │ │ │ └── otp │ │ │ └── erlang │ │ │ ├── OtpConn.java │ │ │ └── OtpSelfNode.java │ ├── erlyberly │ │ ├── BasicSearch.java │ │ ├── CallGraphView.java │ │ ├── CodeView.java │ │ ├── ConnectionView.java │ │ ├── CrashReport.java │ │ ├── CrashReportGraphic.java │ │ ├── CrashReportView.java │ │ ├── DbgController.java │ │ ├── DbgTraceView.java │ │ ├── DbgView.java │ │ ├── ErlyBerly.java │ │ ├── FilterFocusManager.java │ │ ├── FxmlLoadable.java │ │ ├── ModFunc.java │ │ ├── ModFuncContextMenu.java │ │ ├── ModFuncGraphic.java │ │ ├── ModFuncTreeCellFactory.java │ │ ├── PrefBind.java │ │ ├── PreferencesView.java │ │ ├── ProcController.java │ │ ├── ProcInfo.java │ │ ├── ProcSort.java │ │ ├── ProcView.java │ │ ├── SeqTraceLog.java │ │ ├── SeqTraceView.java │ │ ├── StackTraceView.java │ │ ├── TermTreeItem.java │ │ ├── TermTreeView.java │ │ ├── TopBarView.java │ │ ├── TraceContextMenu.java │ │ ├── TraceLog.java │ │ ├── format │ │ │ ├── ElixirFormatter.java │ │ │ ├── ErlangFormatter.java │ │ │ ├── Formatting.java │ │ │ ├── LFEFormatter.java │ │ │ └── TermFormatter.java │ │ └── node │ │ │ ├── AppProcs.java │ │ │ ├── NodeAPI.java │ │ │ ├── NodeRPC.java │ │ │ ├── OtpUtil.java │ │ │ ├── RecordManager.java │ │ │ ├── RpcCallback.java │ │ │ └── TraceManager.java │ ├── hextstar │ │ ├── DataRow.java │ │ └── HexstarView.java │ └── ui │ │ ├── CellController.java │ │ ├── CloseWindowOnEscape.java │ │ ├── FAIcon.java │ │ ├── FXTreeCell.java │ │ └── TabPaneDetacher.java └── resources │ └── erlyberly │ ├── SourceCodePro-Medium.ttf │ ├── beam │ └── erlyberly.erl │ ├── dbg.fxml │ ├── entop.fxml │ ├── erlyberly.css │ ├── preferences.fxml │ └── topbar.fxml └── test └── java └── erlyberly ├── BasicSearchTest.java └── node └── OtpUtilTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | target 14 | jinterface 15 | floaty-field 16 | src/main/resources/erlyberly/beam/erlyberly.beam 17 | 18 | # maven release stuff 19 | pom.xml.releaseBackup 20 | release.properties 21 | 22 | # intellij 23 | .idea/ 24 | erlyberly.iml 25 | 26 | # eclipse 27 | .settings/* 28 | .classpath 29 | .project 30 | _config.yml 31 | .checkstyle 32 | 33 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | - oraclejdk9 5 | after_success: 6 | - ./mvnw assembly:single license:check 7 | -------------------------------------------------------------------------------- /Erlyberly.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /doc/call-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/doc/call-graph.png -------------------------------------------------------------------------------- /doc/crash-report-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/doc/crash-report-button.png -------------------------------------------------------------------------------- /doc/erlyberly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/doc/erlyberly.png -------------------------------------------------------------------------------- /doc/exceptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/doc/exceptions.png -------------------------------------------------------------------------------- /doc/heap-pie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/doc/heap-pie.png -------------------------------------------------------------------------------- /doc/process-state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/doc/process-state.png -------------------------------------------------------------------------------- /doc/seq-trace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/doc/seq-trace.png -------------------------------------------------------------------------------- /doc/termview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/doc/termview.png -------------------------------------------------------------------------------- /doc/view-abstract-source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/doc/view-abstract-source.png -------------------------------------------------------------------------------- /doc/view-source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/doc/view-source.png -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # For Cygwin, switch paths to Windows format before running java 188 | if $cygwin; then 189 | [ -n "$M2_HOME" ] && 190 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 191 | [ -n "$JAVA_HOME" ] && 192 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 193 | [ -n "$CLASSPATH" ] && 194 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 195 | fi 196 | 197 | # traverses directory structure from process work directory to filesystem root 198 | # first directory with .mvn subdirectory is considered project base directory 199 | find_maven_basedir() { 200 | local basedir=$(pwd) 201 | local wdir=$(pwd) 202 | while [ "$wdir" != '/' ] ; do 203 | if [ -d "$wdir"/.mvn ] ; then 204 | basedir=$wdir 205 | break 206 | fi 207 | wdir=$(cd "$wdir/.."; pwd) 208 | done 209 | echo "${basedir}" 210 | } 211 | 212 | # concatenates all lines of a file 213 | concat_lines() { 214 | if [ -f "$1" ]; then 215 | echo "$(tr -s '\n' ' ' < "$1")" 216 | fi 217 | } 218 | 219 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 220 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 221 | 222 | # Provide a "standardized" way to retrieve the CLI args that will 223 | # work with both Windows and non-Windows executions. 224 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 225 | export MAVEN_CMD_LINE_ARGS 226 | 227 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 228 | 229 | exec "$JAVACMD" \ 230 | $MAVEN_OPTS \ 231 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 232 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 233 | ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS 234 | 235 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | andytill 5 | erlyberly 6 | 0.7.1-SNAPSHOT 7 | jar 8 | 9 | erlyberly 10 | http://maven.apache.org 11 | 12 | 13 | 14 | jitpack.io 15 | https://jitpack.io 16 | 17 | 18 | 19 | 20 | scm:git:git@github.com:andytill/erlyberly.git 21 | scm:git:git@github.com:andytill/erlyberly.git 22 | scm:git:git@github.com:andytill/erlyberly.git 23 | HEAD 24 | 25 | 26 | 27 | UTF-8 28 | 1.8 29 | 1.8 30 | 31 | 32 | 33 | 34 | com.github.andytill 35 | jinterface 36 | 1.6.0 37 | 38 | 39 | junit 40 | junit 41 | 4.11 42 | test 43 | 44 | 45 | de.jensd 46 | fontawesomefx 47 | 8.0.10 48 | 49 | 50 | com.github.andytill 51 | floaty-field 52 | 1.2.0 53 | 54 | 55 | com.github.andytill 56 | toml4j 57 | 0.6.1 58 | 59 | 60 | 61 | 62 | 63 | 64 | maven-assembly-plugin 65 | 2.4.1 66 | 67 | 68 | jar-with-dependencies 69 | 70 | false 71 | erlyberly-${project.version}-runnable 72 | 73 | 74 | erlyberly.ErlyBerly 75 | 76 | 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-checkstyle-plugin 83 | 2.17 84 | 85 | 86 | validate 87 | validate 88 | 89 | checkstyle.xml 90 | UTF-8 91 | true 92 | true 93 | false 94 | 95 | 96 | check 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | exec-maven-plugin 106 | org.codehaus.mojo 107 | 1.6.0 108 | 109 | 110 | 111 | Version Calculation 112 | compile 113 | 114 | exec 115 | 116 | 117 | erlc 118 | 119 | +debug_info 120 | -o 121 | src/main/resources/erlyberly/beam/ 122 | src/main/resources/erlyberly/beam/erlyberly.erl 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /src/etc/license_header: -------------------------------------------------------------------------------- 1 | erlyberly, erlang trace debugger 2 | Copyright (C) ${year} ${owner} 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . -------------------------------------------------------------------------------- /src/main/java/com/ericsson/otp/erlang/OtpConn.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.ericsson.otp.erlang; 19 | 20 | import java.io.IOException; 21 | 22 | public class OtpConn extends AbstractConnection { 23 | 24 | private final OtpSelfNode self2; 25 | 26 | protected OtpConn(OtpSelfNode self, OtpPeer other) 27 | throws IOException, OtpAuthException 28 | { 29 | super(self, other); 30 | 31 | self2 = self; 32 | 33 | // thread start 34 | start(); 35 | } 36 | 37 | @Override 38 | public void deliver(final Exception e) { 39 | 40 | } 41 | 42 | @Override 43 | public void deliver(OtpMsg msg) { 44 | self2.deliver(msg); 45 | } 46 | 47 | public void send(OtpErlangPid pid, OtpErlangPid dest, OtpErlangObject msg) 48 | throws IOException 49 | { 50 | // encode and send the message 51 | super.sendBuf(pid, dest, new OtpOutputStream(msg)); 52 | } 53 | 54 | public void send(OtpErlangPid pid, final String dest, final OtpErlangObject msg) 55 | throws IOException 56 | { 57 | // encode and send the message 58 | super.sendBuf(pid, dest, new OtpOutputStream(msg)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/ericsson/otp/erlang/OtpSelfNode.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.ericsson.otp.erlang; 19 | 20 | import java.io.IOException; 21 | 22 | public class OtpSelfNode extends OtpNode { 23 | 24 | public OtpSelfNode(String node, String cookie) throws IOException { 25 | super(node, cookie); 26 | } 27 | 28 | public OtpSelfNode(String node, String cookie, int port) throws IOException { 29 | super(node, cookie, port); 30 | } 31 | 32 | public OtpSelfNode(String node) throws IOException { 33 | super(node); 34 | } 35 | 36 | public OtpConn connect(OtpPeer aPeer) throws OtpAuthException, IOException { 37 | return new OtpConn(this, aPeer); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/BasicSearch.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | 24 | public class BasicSearch { 25 | 26 | interface SearchMatcher { 27 | IsMatch isMatch(String source); 28 | } 29 | 30 | enum IsMatch { MATCH, NO_MATCH, FILTERED }; 31 | 32 | private final List matchers = new ArrayList(); 33 | 34 | public BasicSearch(String searchText) { 35 | List searches = new ArrayList(Arrays.asList(searchText.split("\\|"))); 36 | 37 | // remove the empties 38 | while(searches.remove("")); 39 | 40 | for (String string : searches) { 41 | SearchMatcher sm; 42 | 43 | if(string.charAt(0) == '!') { 44 | if(string.length() == 1) 45 | continue; 46 | String string2 = string.substring(1); 47 | 48 | // this is a NOT filter, if we match then filter out, otherwise let it though 49 | sm = (s) -> { return s.contains(string2) ? IsMatch.FILTERED : IsMatch.MATCH; }; 50 | } 51 | else { 52 | sm = (s) -> { return s.contains(string) ? IsMatch.MATCH : IsMatch.NO_MATCH; }; 53 | } 54 | matchers.add(sm); 55 | } 56 | } 57 | 58 | public boolean matches(String... sourceStrings) { 59 | if(matchers.isEmpty()) 60 | return true; 61 | 62 | boolean match = false; 63 | 64 | for (String string : sourceStrings) { 65 | 66 | if(sourceStrings == null) 67 | continue; 68 | 69 | for (SearchMatcher matcher : matchers) { 70 | switch(matcher.isMatch(string)) { 71 | case FILTERED: 72 | // if this is filtered out, return false immediately 73 | return false; 74 | case MATCH: 75 | // we may match but could filter out later on 76 | match = true; 77 | case NO_MATCH: 78 | // keep going until we get something more substantive 79 | break; 80 | } 81 | } 82 | } 83 | return match; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/CallGraphView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.util.Arrays; 21 | import java.util.List; 22 | 23 | import com.ericsson.otp.erlang.OtpErlangAtom; 24 | import com.ericsson.otp.erlang.OtpErlangList; 25 | import com.ericsson.otp.erlang.OtpErlangLong; 26 | import com.ericsson.otp.erlang.OtpErlangObject; 27 | import com.ericsson.otp.erlang.OtpErlangRangeException; 28 | import com.ericsson.otp.erlang.OtpErlangTuple; 29 | 30 | import erlyberly.node.OtpUtil; 31 | import javafx.scene.control.TreeItem; 32 | import javafx.scene.control.TreeView; 33 | 34 | 35 | public class CallGraphView extends TreeView { 36 | 37 | /** 38 | * A list of module names that will not be expanded in the call graph tree, since 39 | * they have lots of sub-calls, cluttering up the tree and it is assumed most usage 40 | * will be for application calls, not for the standard libs. 41 | */ 42 | private static final List UNEXPANDED_MODULES = Arrays.asList( 43 | "erlang", "gen_server", "io", "io_lib", "lists", "rpc", "unicode"); 44 | 45 | private ModFuncContextMenu modFuncContextMenu; 46 | 47 | public CallGraphView(DbgController aDbgController) { 48 | assert aDbgController != null; 49 | 50 | modFuncContextMenu = new ModFuncContextMenu(aDbgController); 51 | 52 | getSelectionModel() 53 | .selectedItemProperty() 54 | .addListener((o, old, newItem) -> { 55 | modFuncContextMenu.selectedTreeItemProperty().set(newItem); 56 | if(newItem != null) 57 | modFuncContextMenu.selectedItemProperty().set(newItem.getValue()); 58 | }); 59 | 60 | 61 | ModFuncTreeCellFactory modFuncTreeCellFactory; 62 | 63 | modFuncTreeCellFactory = new ModFuncTreeCellFactory(aDbgController); 64 | modFuncTreeCellFactory.setShowModuleName(true); 65 | 66 | setRoot(new TreeItem<>()); 67 | setShowRoot(false); 68 | setContextMenu(modFuncContextMenu); 69 | setCellFactory(modFuncTreeCellFactory); 70 | } 71 | 72 | public void callGraph(OtpErlangTuple callStack) { 73 | assert callStack != null; 74 | populateCallGraph(getRoot(), callStack); 75 | } 76 | 77 | /** 78 | * Parses erlang terms in the format, into a JavaFX tree. 79 | * 80 | * {{M::atom(), F::atom(), A::integer()}, [{M,F,A}]} 81 | * 82 | */ 83 | private void populateCallGraph(TreeItem parentModFuncItem, OtpErlangTuple callGraph) { 84 | 85 | assert callGraph != null; 86 | System.out.println(callGraph); 87 | OtpErlangTuple mfaTuple = (OtpErlangTuple) OtpUtil.tupleElement(0, callGraph); 88 | OtpErlangList calls = (OtpErlangList) OtpUtil.tupleElement(1, callGraph); 89 | 90 | OtpErlangAtom module = (OtpErlangAtom) OtpUtil.tupleElement(0, mfaTuple); 91 | OtpErlangAtom function = (OtpErlangAtom) OtpUtil.tupleElement(1, mfaTuple); 92 | OtpErlangLong arity = (OtpErlangLong) OtpUtil.tupleElement(2, mfaTuple); 93 | 94 | try { 95 | // just put something in, this isn't used 96 | boolean exported = false; 97 | boolean synthetic = false; 98 | 99 | ModFunc modFunc = new ModFunc(module.atomValue(), function.atomValue(), arity.intValue(), exported, synthetic); 100 | 101 | TreeItem modFuncItem; 102 | 103 | modFuncItem = new TreeItem<>(modFunc); 104 | String atomString = module.atomValue(); 105 | boolean value = !UNEXPANDED_MODULES.contains(atomString); 106 | modFuncItem.setExpanded(value); 107 | 108 | parentModFuncItem.getChildren().add(modFuncItem); 109 | 110 | for (OtpErlangObject e : OtpUtil.iterableElements(calls)) { 111 | populateCallGraph(modFuncItem, (OtpErlangTuple) e); 112 | } 113 | } 114 | catch (OtpErlangRangeException e) { 115 | e.printStackTrace(); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/CodeView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import javafx.scene.control.TextArea; 21 | 22 | public class CodeView extends TextArea { 23 | 24 | { 25 | getStyleClass().add("erlyberly-code"); 26 | setEditable(false); 27 | } 28 | 29 | public CodeView() { 30 | super(); 31 | } 32 | 33 | public CodeView(String text) { 34 | super(text); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/CrashReport.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.time.LocalDateTime; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Optional; 25 | 26 | import com.ericsson.otp.erlang.OtpErlangAtom; 27 | import com.ericsson.otp.erlang.OtpErlangList; 28 | import com.ericsson.otp.erlang.OtpErlangLong; 29 | import com.ericsson.otp.erlang.OtpErlangObject; 30 | import com.ericsson.otp.erlang.OtpErlangPid; 31 | import com.ericsson.otp.erlang.OtpErlangString; 32 | import com.ericsson.otp.erlang.OtpErlangTuple; 33 | 34 | import erlyberly.node.OtpUtil; 35 | 36 | public class CrashReport { 37 | 38 | private static final OtpErlangAtom ATOM_INITIAL_CALL = OtpUtil.atom("initial_call"); 39 | 40 | private static final OtpErlangAtom ATOM_PID = OtpUtil.atom("pid"); 41 | 42 | private static final OtpErlangAtom ATOM_REGISTERED_NAME = OtpUtil.atom("registered_name"); 43 | 44 | private static final OtpErlangAtom ATOM_ERROR_INFO = OtpUtil.atom("error_info"); 45 | 46 | private static final OtpErlangAtom ATOM_FILE = OtpUtil.atom("file"); 47 | 48 | private static final OtpErlangAtom ATOM_LINE = OtpUtil.atom("line"); 49 | 50 | private final Map crashProps; 51 | 52 | private final String registeredName; 53 | 54 | private final OtpErlangObject terms; 55 | 56 | private final OtpErlangTuple errorInfo; 57 | 58 | private final LocalDateTime date = LocalDateTime.now(); 59 | 60 | public CrashReport(OtpErlangObject obj) { 61 | this.terms = obj; 62 | crashProps = OtpUtil.propsToMap((OtpErlangList) obj); 63 | errorInfo = (OtpErlangTuple) crashProps.get(ATOM_ERROR_INFO); 64 | 65 | // pull out the register name of the crashing process 66 | Object aRegName = crashProps.get(ATOM_REGISTERED_NAME); 67 | if(OtpUtil.list().equals(aRegName)) { 68 | registeredName = ""; 69 | } 70 | else { 71 | registeredName = aRegName.toString(); 72 | } 73 | } 74 | 75 | List mapStackTraces(StackTraceFn fn) { 76 | OtpErlangList stackTrace = (OtpErlangList) errorInfo.elementAt(2); 77 | ArrayList result = new ArrayList(); 78 | 79 | for (OtpErlangObject obj : stackTrace) { 80 | OtpErlangTuple tuple = (OtpErlangTuple) obj; 81 | OtpErlangAtom module = (OtpErlangAtom) tuple.elementAt(0); 82 | OtpErlangAtom function = (OtpErlangAtom) tuple.elementAt(1); 83 | OtpErlangObject argsOrArity = tuple.elementAt(2); 84 | OtpErlangLong arity; 85 | if(argsOrArity instanceof OtpErlangLong) { 86 | arity = (OtpErlangLong) argsOrArity; 87 | } 88 | else { 89 | OtpErlangList list = (OtpErlangList) argsOrArity; 90 | arity = new OtpErlangLong(list.arity()); 91 | } 92 | Map fileLineProps = OtpUtil.propsToMap((OtpErlangList) tuple.elementAt(3)); 93 | OtpErlangString file = (OtpErlangString) fileLineProps.get(ATOM_FILE); 94 | OtpErlangLong line = (OtpErlangLong) fileLineProps.get(ATOM_LINE); 95 | 96 | String fileString = ""; 97 | if(file != null) { 98 | fileString = file.stringValue(); 99 | } 100 | result.add( 101 | fn.invoke(module, function, arity, fileString, line) 102 | ); 103 | } 104 | return result; 105 | } 106 | 107 | public interface StackTraceFn { 108 | T invoke(OtpErlangAtom module, OtpErlangAtom function, OtpErlangLong arity, String file, OtpErlangLong line); 109 | } 110 | 111 | public OtpErlangPid getPid() { 112 | return (OtpErlangPid) crashProps.get(ATOM_PID); 113 | } 114 | 115 | public String getRegisteredName() { 116 | return registeredName; 117 | } 118 | 119 | public Object getProcessInitialCall() { 120 | return crashProps.get(ATOM_INITIAL_CALL); 121 | } 122 | 123 | public OtpErlangObject getProps() { 124 | return terms; 125 | } 126 | 127 | public OtpErlangAtom getErrorClass() { 128 | return (OtpErlangAtom) errorInfo.elementAt(0); 129 | } 130 | 131 | public OtpErlangObject getErrorReason() { 132 | return errorInfo.elementAt(1); 133 | } 134 | 135 | public LocalDateTime getDateTime() { 136 | return date; 137 | } 138 | 139 | public Optional getCallArgs() { 140 | OtpErlangTuple staceTrace1 = (OtpErlangTuple) ((OtpErlangList) errorInfo.elementAt(2)).getHead(); 141 | OtpErlangObject argsOrArity = staceTrace1.elementAt(2); 142 | if(argsOrArity instanceof OtpErlangList) 143 | return Optional.of((OtpErlangList)argsOrArity); 144 | else 145 | return Optional.empty(); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/CrashReportGraphic.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.time.format.DateTimeFormatter; 21 | 22 | import javafx.scene.control.Label; 23 | import javafx.scene.layout.VBox; 24 | 25 | /** 26 | * A control summarising a {@link CrashReport} for button graphics. 27 | */ 28 | public class CrashReportGraphic extends VBox { 29 | public CrashReportGraphic(CrashReport report) { 30 | getChildren().addAll( 31 | dateLabel(report), 32 | descriptionLabel(report) 33 | ); 34 | } 35 | 36 | private Label dateLabel(CrashReport report) { 37 | String dateString = report.getDateTime().format(DateTimeFormatter.ISO_TIME); 38 | Label label; 39 | label = new Label(dateString); 40 | // the date label is grey bold text that is slightly smaller than the main label 41 | label.setStyle("-fx-font-size: 10; -fx-font-weight: bold; -fx-text-fill: #b2b2b2;"); 42 | return label; 43 | } 44 | 45 | private Label descriptionLabel(CrashReport report) { 46 | String processString = report.getRegisteredName(); 47 | if("".equals(processString)) { 48 | processString = report.getPid().toString(); 49 | } 50 | processString += " " + report.getErrorClass() + ":" + report.getErrorReason(); 51 | 52 | Label label; 53 | label = new Label(processString); 54 | // #75 make sure that the label does not have unbounded width 55 | label.setMaxWidth(500d); 56 | label.setStyle("-fx-font-size: 11; -fx-padding: 2 0 0 0;"); 57 | return label; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/CrashReportView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import com.ericsson.otp.erlang.OtpErlangTuple; 21 | 22 | import javafx.beans.property.SimpleObjectProperty; 23 | import javafx.beans.value.ObservableValue; 24 | import javafx.scene.control.Label; 25 | import javafx.scene.control.Tab; 26 | import javafx.scene.control.TabPane; 27 | import javafx.scene.control.TableColumn; 28 | import javafx.scene.control.TableColumn.CellDataFeatures; 29 | import javafx.scene.control.TableView; 30 | import javafx.scene.layout.Priority; 31 | import javafx.scene.layout.VBox; 32 | import javafx.util.Callback; 33 | 34 | public class CrashReportView extends TabPane { 35 | 36 | private final TermTreeView termTreeView = new TermTreeView(); 37 | private final TermTreeView argsTreeView = new TermTreeView(); 38 | private final StackTraceView stackTraceView = new StackTraceView(); 39 | private final TableView crashInfoTable = new TableView<>(); 40 | 41 | public CrashReportView() { 42 | VBox.setVgrow(termTreeView, Priority.ALWAYS); 43 | 44 | Label label; 45 | label = new Label("Stack Trace"); 46 | label.setStyle("-fx-padding: 5; -fx-font-size: 14;"); 47 | 48 | Tab stackTraceTab, argsTermsTab, termsTab; 49 | termsTab = new Tab("Crash Report Terms"); 50 | termsTab.setContent(termTreeView); 51 | argsTermsTab = new Tab("Call Args"); 52 | argsTermsTab.setContent(argsTreeView); 53 | stackTraceTab = new Tab("Stack Trace"); 54 | stackTraceTab.setContent(new VBox(crashInfoTable, label, stackTraceView)); 55 | 56 | getTabs().addAll(stackTraceTab, argsTermsTab, termsTab); 57 | } 58 | 59 | public void setCrashReport(CrashReport crashReport) { 60 | stackTraceView.populateFromCrashReport(crashReport); 61 | termTreeView.populateFromTerm(crashReport.getProps()); 62 | 63 | Object[][] crashProps = { 64 | {"Pid", crashReport.getPid() }, 65 | {"Reg. Name", crashReport.getRegisteredName() }, 66 | {"Error", ErlyBerly.getTermFormatter().exceptionToString(crashReport.getErrorClass(), crashReport.getErrorReason()) }, 67 | {"Initial Call", ErlyBerly.getTermFormatter().modFuncArgsToString((OtpErlangTuple) crashReport.getProcessInitialCall()) }}; 68 | 69 | TableColumn keyColumn = new TableColumn<>("Key"); 70 | TableColumn valueColumn = new TableColumn<>("Value"); 71 | 72 | keyColumn.setCellValueFactory(new Callback, ObservableValue>() { 73 | @Override 74 | public ObservableValue call(CellDataFeatures p) { 75 | return new SimpleObjectProperty<>((p.getValue()[0])); 76 | } 77 | }); 78 | 79 | valueColumn.setCellValueFactory(new Callback, ObservableValue>() { 80 | @Override 81 | public ObservableValue call(CellDataFeatures p) { 82 | return new SimpleObjectProperty<>((p.getValue()[1])); 83 | } 84 | }); 85 | 86 | crashInfoTable.getColumns().add(keyColumn); 87 | crashInfoTable.getColumns().add(valueColumn); 88 | 89 | crashInfoTable.getItems().addAll(crashProps); 90 | 91 | crashReport.getCallArgs().ifPresent((callArgs) -> { 92 | argsTreeView.populateFromListContents(callArgs); 93 | }); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/DbgController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.io.IOException; 21 | import java.net.URL; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.ResourceBundle; 25 | 26 | import com.ericsson.otp.erlang.OtpErlangException; 27 | import com.ericsson.otp.erlang.OtpErlangList; 28 | import com.ericsson.otp.erlang.OtpErlangTuple; 29 | 30 | import erlyberly.node.OtpUtil; 31 | import erlyberly.node.RpcCallback; 32 | import javafx.application.Platform; 33 | import javafx.beans.InvalidationListener; 34 | import javafx.collections.FXCollections; 35 | import javafx.collections.ObservableList; 36 | import javafx.event.ActionEvent; 37 | import javafx.fxml.Initializable; 38 | 39 | 40 | public class DbgController implements Initializable { 41 | 42 | /** 43 | * The maximum trace logs that erlyberly can show in the table before it must 44 | * load shed them e.g. start throwing them away so that the UI does not attempt 45 | * to take an unlimited amount of memory, and fail! 46 | * 47 | * This is only checked at start up, so if it is changed it requires a restart. 48 | */ 49 | private static final int MAX_TRACE_LOGS; 50 | 51 | static { 52 | Number maxTraceLogs = (Number) PrefBind.getOrDefault("maxTraceLogs", 1000); 53 | MAX_TRACE_LOGS = maxTraceLogs.intValue(); 54 | } 55 | 56 | private final ObservableList traceLogs = FXCollections.observableArrayList(); 57 | 58 | private final ObservableList traces = FXCollections.observableArrayList(); 59 | 60 | private final ObservableList seqTraces = FXCollections.observableArrayList(); 61 | 62 | private volatile boolean collectingSeqTraces; 63 | 64 | private TraceLog loadSheddingLog; 65 | 66 | @Override 67 | public void initialize(URL url, ResourceBundle r) { 68 | ErlyBerly.nodeAPI().setTraceLogCallback((traceLog) -> { 69 | if(traceLogs.size() > MAX_TRACE_LOGS) { 70 | if(loadSheddingLog == null) { 71 | loadSheddingLog = TraceLog.newLoadShedding(); 72 | traceLogs.add(loadSheddingLog); 73 | } 74 | return; 75 | } 76 | if(loadSheddingLog != null) { 77 | traceLogs.remove(loadSheddingLog); 78 | loadSheddingLog = null; 79 | } 80 | traceLogs.add(traceLog); 81 | }); 82 | ErlyBerly.nodeAPI().suspendedProperty().addListener((o, oldv, suspended) -> { 83 | // an un-suspended is similar to a node coming back online, reapply our known traces 84 | if(!suspended && ErlyBerly.nodeAPI().isConnected()) { 85 | reapplyTraces(); 86 | } 87 | }); 88 | ErlyBerly.nodeAPI().connectedProperty().addListener( 89 | (o, oldV, newV) -> { 90 | if(oldV && !newV) { 91 | traceLogs.add(TraceLog.newNodeDown()); 92 | } 93 | }); 94 | new SeqTraceCollectorThread((seqs) -> { seqTraces.addAll(seqs); }).start(); 95 | } 96 | 97 | public ObservableList getTraceLogs() { 98 | return traceLogs; 99 | } 100 | 101 | public ObservableList getSeqTraceLogs() { 102 | return seqTraces; 103 | } 104 | 105 | public boolean isTraced(ModFunc function) { 106 | return traces.contains(function); 107 | } 108 | 109 | public void toggleTraceModFunc(ModFunc function) { 110 | // if tracing is suspended, we can't apply a new trace because that will 111 | // leave us in a state where some traces are active and others are not 112 | if(ErlyBerly.nodeAPI().isSuspended()) 113 | return; 114 | if(traces.contains(function)) 115 | onRemoveTracer(null, function); 116 | else 117 | traceModFunc(function); 118 | } 119 | 120 | private void traceModFunc(ModFunc function) { 121 | ErlyBerly.runIO(() -> { 122 | try { 123 | ErlyBerly.nodeAPI().startTrace(function, PrefBind.getMaxTraceQueueLengthConfig()); 124 | 125 | Platform.runLater(() -> { traces.add(function); }); 126 | 127 | ErlyBerly.nodeAPI().loadModuleRecords(OtpUtil.atom(function.getModuleName())); 128 | } 129 | catch (Exception ex) { 130 | ex.printStackTrace(); 131 | } 132 | }); 133 | } 134 | 135 | private void onRemoveTracer(ActionEvent e, ModFunc function) { 136 | ErlyBerly.runIO(() -> { 137 | try { 138 | ErlyBerly.nodeAPI().stopTrace(function); 139 | 140 | Platform.runLater(() -> { traces.remove(function); }); 141 | } 142 | catch (Exception ex) { 143 | ex.printStackTrace(); 144 | } 145 | }); 146 | } 147 | 148 | public void reapplyTraces() { 149 | final int maxTraceQueueLengthConfig = PrefBind.getMaxTraceQueueLengthConfig(); 150 | final ArrayList tracesCopy = new ArrayList(traces); 151 | ErlyBerly.runIO(() -> { 152 | for (ModFunc function : tracesCopy) { 153 | try { 154 | ErlyBerly.nodeAPI().startTrace(function, maxTraceQueueLengthConfig); 155 | } 156 | catch (Exception e) { 157 | e.printStackTrace(); 158 | Platform.runLater(() -> { traces.remove(function); }); 159 | } 160 | } 161 | }); 162 | } 163 | 164 | public void addTraceListener(InvalidationListener listener) { 165 | traces.addListener(listener); 166 | } 167 | 168 | public void requestModFuncs(RpcCallback rpcCallback) { 169 | new GetModulesThread(rpcCallback).start(); 170 | } 171 | 172 | public void seqTrace(ModFunc value) { 173 | try { 174 | ErlyBerly.nodeAPI().seqTrace(value); 175 | collectingSeqTraces = true; 176 | } 177 | catch (OtpErlangException | IOException e) { 178 | e.printStackTrace(); 179 | } 180 | } 181 | 182 | class SeqTraceCollectorThread extends Thread { 183 | private final RpcCallback callback; 184 | 185 | public SeqTraceCollectorThread(RpcCallback aCallback) { 186 | callback = aCallback; 187 | 188 | setDaemon(true); 189 | setName("Erlyberly Collect Seq Traces"); 190 | } 191 | 192 | @Override 193 | public void run() { 194 | while (true) { 195 | if(collectingSeqTraces && ErlyBerly.nodeAPI().isConnected()) { 196 | try { 197 | final List seqTraceLogs = ErlyBerly.nodeAPI().collectSeqTraceLogs(); 198 | 199 | for (SeqTraceLog seqTraceLog : seqTraceLogs) { 200 | Platform.runLater(() -> { callback.callback(seqTraceLog); }); 201 | } 202 | } catch (Exception e) { 203 | e.printStackTrace(); 204 | } 205 | } 206 | 207 | try { 208 | Thread.sleep(100); 209 | } catch (InterruptedException e) { 210 | e.printStackTrace(); 211 | } 212 | } 213 | } 214 | } 215 | 216 | class GetModulesThread extends Thread { 217 | private final RpcCallback rpcCallback; 218 | 219 | public GetModulesThread(RpcCallback anRpcCallback) { 220 | rpcCallback = anRpcCallback; 221 | 222 | setDaemon(true); 223 | setName("Erlyberly Get Modules"); 224 | } 225 | 226 | @Override 227 | public void run() { 228 | try { 229 | OtpErlangList functions = ErlyBerly.nodeAPI().requestFunctions(); 230 | Platform.runLater(() -> { rpcCallback.callback(functions); }); 231 | } 232 | catch (Exception e) { 233 | e.printStackTrace(); 234 | } 235 | } 236 | } 237 | 238 | public String moduleFunctionSourceCode(String module, String function, Integer arity) throws IOException, OtpErlangException { 239 | return ErlyBerly.nodeAPI().moduleFunctionSourceCode(module, function, arity); 240 | } 241 | public String moduleFunctionSourceCode(String module) throws IOException, OtpErlangException { 242 | return ErlyBerly.nodeAPI().moduleFunctionSourceCode(module); 243 | } 244 | 245 | public String moduleFunctionAbstCode(String module) throws IOException, OtpErlangException { 246 | String moduleCode = ErlyBerly.nodeAPI().moduleFunctionAbstractCode(module); 247 | return moduleCode; 248 | } 249 | public String moduleFunctionAbstCode(String module, String function, Integer arity) throws IOException, OtpErlangException { 250 | String moduleCode = ErlyBerly.nodeAPI().moduleFunctionAbstractCode(module, function, arity); 251 | return moduleCode; 252 | } 253 | 254 | public void setModuleLoadedCallback(RpcCallback moduleLoadedCallback) { 255 | ErlyBerly.nodeAPI().setModuleLoadedCallback(moduleLoadedCallback); 256 | } 257 | 258 | } 259 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/FilterFocusManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.util.ArrayList; 21 | 22 | import javafx.application.Platform; 23 | import javafx.scene.Scene; 24 | import javafx.scene.control.Control; 25 | import javafx.scene.input.KeyCode; 26 | import javafx.scene.input.KeyCodeCombination; 27 | import javafx.scene.input.KeyCombination; 28 | 29 | public class FilterFocusManager { 30 | 31 | private static final KeyCodeCombination REFRESH_MODULES_SHORTCUT = new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN); 32 | 33 | private static final ArrayList FILTERS = new ArrayList<>(); 34 | 35 | private static int lastFocusedIndex = -1; 36 | 37 | private FilterFocusManager() {} 38 | 39 | public static void init(Scene scene) { 40 | FILTERS.add(null); 41 | FILTERS.add(null); 42 | FILTERS.add(null); 43 | 44 | Platform.runLater(() -> { 45 | scene.getAccelerators().put(REFRESH_MODULES_SHORTCUT, () -> { nextFilter(); }); 46 | }); 47 | } 48 | 49 | private static void nextFilter() { 50 | if(FILTERS.isEmpty()) 51 | return; 52 | 53 | Control control = findNextFilter(); 54 | 55 | requestFocus(control); 56 | } 57 | 58 | private static Control findNextFilter() { 59 | // if we know about a filter control that was focused last but does not currently 60 | // have focus then return to it. The filter must be visible and attached to the scene. 61 | if(lastFocusedIndex != -1) { 62 | Control lastFocused = FILTERS.get(lastFocusedIndex); 63 | if(!lastFocused.isFocused() && lastFocused.isVisible() && lastFocused.getScene() != null) { 64 | return lastFocused; 65 | } 66 | } 67 | 68 | int focused = findCurrentFilter(); 69 | 70 | if(focused == -1) { 71 | focused = 0; 72 | } 73 | // iterate the filters until we find one that is part of the scene or we run out of filters 74 | int iterations = 0; 75 | while(iterations < FILTERS.size()) { 76 | iterations++; 77 | focused++; 78 | if(focused >= FILTERS.size()) { 79 | focused = 0; 80 | } 81 | boolean isFocusable = FILTERS.get(focused).getScene() != null; 82 | if(isFocusable) 83 | break; 84 | } 85 | return FILTERS.get(focused); 86 | } 87 | 88 | private static int findCurrentFilter() { 89 | int focused = -1; 90 | for (int i = 0; i < FILTERS.size(); i++) { 91 | Control control = FILTERS.get(i); 92 | if(control != null && control.isFocused()) { 93 | focused = i; 94 | break; 95 | } 96 | } 97 | return focused; 98 | } 99 | 100 | private static void requestFocus(Control control) { 101 | if(control != null) 102 | Platform.runLater(() -> { control.requestFocus(); }); 103 | } 104 | 105 | public static void addFilter(Control control, int order) { 106 | assert control != null; 107 | assert order >= 0; 108 | assert Platform.isFxApplicationThread(); 109 | 110 | FILTERS.set(order, control); 111 | 112 | control.focusedProperty().addListener((o, oldFocus, newFocus) -> { 113 | lastFocusedIndex = order; 114 | }); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/FxmlLoadable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.io.IOException; 21 | import java.net.URL; 22 | 23 | import javafx.fxml.FXMLLoader; 24 | import javafx.scene.Parent; 25 | 26 | class FxmlLoadable { 27 | final String resource; 28 | Parent fxmlNode; 29 | Object controller; 30 | 31 | public FxmlLoadable(String aResource) { 32 | resource = aResource; 33 | } 34 | 35 | public Parent load() { 36 | if(fxmlNode != null) 37 | return fxmlNode; 38 | 39 | URL location = getClass().getResource(resource); 40 | FXMLLoader fxmlLoader = new FXMLLoader(location); 41 | 42 | try { 43 | fxmlNode = (Parent) fxmlLoader.load(); 44 | controller = fxmlLoader.getController(); 45 | } catch (IOException e) { 46 | throw new RuntimeException("Cannot load FXML", e); 47 | } 48 | return fxmlNode; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/ModFunc.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import com.ericsson.otp.erlang.OtpErlangAtom; 21 | import com.ericsson.otp.erlang.OtpErlangLong; 22 | import com.ericsson.otp.erlang.OtpErlangObject; 23 | import com.ericsson.otp.erlang.OtpErlangTuple; 24 | 25 | public class ModFunc implements Comparable { 26 | 27 | private final String moduleName; 28 | 29 | private final String funcName; 30 | 31 | private final int arity; 32 | 33 | private final boolean exported; 34 | 35 | private final boolean synthetic; 36 | 37 | public ModFunc(String moduleName, String funcName, int arity, boolean exported, boolean synthetic) { 38 | this.moduleName = (moduleName != null) ? moduleName.replace("'", "") : null; 39 | this.funcName = (funcName != null) ? funcName.replace("'", "") : null; 40 | this.arity = arity; 41 | this.exported = exported; 42 | this.synthetic = synthetic; 43 | } 44 | 45 | public String getModuleName() { 46 | return moduleName; 47 | } 48 | 49 | public String getFuncName() { 50 | return funcName; 51 | } 52 | 53 | public int getArity() { 54 | return arity; 55 | } 56 | 57 | public boolean isExported() { 58 | return exported; 59 | } 60 | 61 | public boolean isSynthetic() { 62 | return synthetic; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | if(funcName == null) { 68 | return ErlyBerly.getTermFormatter().moduleNameToString(moduleName); 69 | } 70 | return funcName + "/" + arity; 71 | } 72 | 73 | public String toFullString() { 74 | if(funcName == null) { 75 | return ErlyBerly.getTermFormatter().moduleNameToString(moduleName); 76 | } 77 | return ErlyBerly.getTermFormatter().modFuncArityToString(moduleName, funcName, arity); 78 | } 79 | 80 | public static ModFunc toFunc(OtpErlangAtom moduleName, OtpErlangObject e, boolean exported) { 81 | OtpErlangAtom funcNameAtom = (OtpErlangAtom) ((OtpErlangTuple) e).elementAt(0); 82 | OtpErlangLong arity = (OtpErlangLong) ((OtpErlangTuple) e).elementAt(1); 83 | 84 | String funcName = funcNameAtom.atomValue(); 85 | 86 | return new ModFunc( 87 | moduleName.atomValue(), 88 | funcName, 89 | (int)arity.longValue(), 90 | exported, 91 | funcName.startsWith("-") 92 | ); 93 | } 94 | 95 | public static ModFunc toModule(OtpErlangAtom moduleName) { 96 | return new ModFunc( 97 | moduleName.atomValue(), 98 | null, 99 | 0, 100 | false, 101 | false 102 | ); 103 | } 104 | 105 | @Override 106 | public int compareTo(ModFunc o) { 107 | if(funcName == null) { 108 | return moduleName.compareTo(o.moduleName); 109 | } 110 | 111 | int comp = funcName.compareTo(o.funcName); 112 | if(comp == 0) { 113 | comp = Integer.compare(arity, o.arity); 114 | } 115 | return comp; 116 | } 117 | 118 | public boolean isModule() { 119 | return (funcName == null); 120 | } 121 | 122 | @Override 123 | public int hashCode() { 124 | final int prime = 31; 125 | int result = 1; 126 | result = prime * result + arity; 127 | result = prime * result 128 | + ((funcName == null) ? 0 : funcName.hashCode()); 129 | result = prime * result 130 | + ((moduleName == null) ? 0 : moduleName.hashCode()); 131 | return result; 132 | } 133 | 134 | @Override 135 | public boolean equals(Object obj) { 136 | if (this == obj) 137 | return true; 138 | if (obj == null) 139 | return false; 140 | if (getClass() != obj.getClass()) 141 | return false; 142 | ModFunc other = (ModFunc) obj; 143 | if (arity != other.arity) 144 | return false; 145 | if (funcName == null) { 146 | if (other.funcName != null) 147 | return false; 148 | } else if (!funcName.equals(other.funcName)) 149 | return false; 150 | if (moduleName == null) { 151 | if (other.moduleName != null) 152 | return false; 153 | } else if (!moduleName.equals(other.moduleName)) 154 | return false; 155 | return true; 156 | } 157 | 158 | public boolean isModuleInfo() { 159 | return "module_info".equals(funcName) && (arity == 0 || arity == 1); 160 | } 161 | 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/ModFuncGraphic.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import de.jensd.fx.fontawesome.AwesomeIcon; 21 | import javafx.beans.property.SimpleBooleanProperty; 22 | import javafx.beans.property.SimpleStringProperty; 23 | import javafx.scene.control.Label; 24 | import javafx.scene.control.Tooltip; 25 | import javafx.scene.layout.HBox; 26 | import ui.CellController; 27 | import ui.FAIcon; 28 | 29 | class ModFuncGraphic extends HBox implements CellController { 30 | 31 | public interface TraceFn { 32 | void trace(ModFunc modFunc); 33 | } 34 | 35 | public interface IsTracedFn { 36 | boolean isTraced(ModFunc mf); 37 | } 38 | 39 | private static final String ICON_STYLE = "-fx-font-family: FontAwesome; -fx-font-size: 1em;"; 40 | 41 | private final SimpleStringProperty text = new SimpleStringProperty(); 42 | 43 | private final SimpleStringProperty exportIconText = new SimpleStringProperty(); 44 | 45 | private final SimpleStringProperty exportToolTipText = new SimpleStringProperty(); 46 | 47 | private final SimpleStringProperty tracedIconText = new SimpleStringProperty(AwesomeIcon.STAR_ALT.toString()); 48 | 49 | private final SimpleBooleanProperty tracable = new SimpleBooleanProperty(); 50 | 51 | private final TraceFn traceFn; 52 | 53 | private final IsTracedFn isTracedFn; 54 | 55 | private boolean showModuleName; 56 | 57 | private ModFunc modFunc; 58 | 59 | public ModFuncGraphic(TraceFn aTraceFn, IsTracedFn isTracedFn) { 60 | traceFn = aTraceFn; 61 | this.isTracedFn = isTracedFn; 62 | 63 | getStyleClass().add("mod-func-graphic"); 64 | 65 | getChildren().addAll( 66 | exportIconGraphic(), 67 | traceIcon(), 68 | functionLabel() 69 | ); 70 | } 71 | 72 | private FAIcon exportIconGraphic() { 73 | Tooltip tooltip; 74 | 75 | tooltip = new Tooltip(); 76 | tooltip.textProperty().bind(exportToolTipText); 77 | 78 | FAIcon treeIcon; 79 | 80 | treeIcon = treeIcon(AwesomeIcon.SQUARE); 81 | treeIcon.textProperty().bind(exportIconText); 82 | treeIcon.setTooltip(tooltip); 83 | return treeIcon; 84 | } 85 | 86 | private FAIcon traceIcon() { 87 | FAIcon traceIcon; 88 | 89 | traceIcon = FAIcon.create().style(ICON_STYLE); 90 | traceIcon.textProperty().bind(tracedIconText); 91 | traceIcon.visibleProperty().bind(tracable); 92 | traceIcon.setTooltip(new Tooltip("Toggle tracing, double click on this star or ctrl+t when selected")); 93 | traceIcon.getStyleClass().add("erlyberly-icon-button"); 94 | traceIcon.setOnMouseClicked((e) -> { 95 | if(e.getClickCount() == 2) 96 | traceFn.trace(modFunc); 97 | }); 98 | return traceIcon; 99 | } 100 | 101 | private Label functionLabel() { 102 | Label label; 103 | 104 | label = new Label(); 105 | label.textProperty().bind(text); 106 | 107 | return label; 108 | } 109 | 110 | private FAIcon treeIcon(AwesomeIcon treeIcon) { 111 | return FAIcon.create().icon(treeIcon).style(ICON_STYLE); 112 | } 113 | 114 | @Override 115 | public void updateItem(ModFunc item, boolean empty) { 116 | if (item == null || empty) { 117 | text.set(null); 118 | } 119 | else { 120 | if(isShowModuleName()) 121 | text.set(item.toFullString()); 122 | else 123 | text.set(item.toString()); 124 | 125 | updateExportIcon(item); 126 | 127 | // no tracing of the whole module for now! 128 | tracable.set(!item.isModule()); 129 | } 130 | modFunc = item; 131 | 132 | onTracesChange(); 133 | } 134 | 135 | private void updateExportIcon(ModFunc item) { 136 | AwesomeIcon icon; 137 | String tooltipText; 138 | 139 | if(item.isModule()) { 140 | tooltipText = "Module"; 141 | icon = AwesomeIcon.CUBE; 142 | } 143 | else if(item.isExported()) { 144 | tooltipText = "Exported function"; 145 | icon = AwesomeIcon.UNLOCK_ALT; 146 | } 147 | else { 148 | tooltipText = "Unexported function"; 149 | icon = AwesomeIcon.LOCK; 150 | } 151 | 152 | exportToolTipText.set(tooltipText); 153 | exportIconText.set(icon.toString()); 154 | } 155 | 156 | public void onTracesChange() { 157 | if(modFunc != null && isTracedFn.isTraced(modFunc)) 158 | tracedIconText.set(AwesomeIcon.CHECK_SQUARE_ALT.toString()); 159 | else 160 | tracedIconText.set(AwesomeIcon.SQUARE_ALT.toString()); 161 | } 162 | 163 | public boolean isShowModuleName() { 164 | return showModuleName; 165 | } 166 | 167 | public void setShowModuleName(boolean showModuleName) { 168 | this.showModuleName = showModuleName; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/ModFuncTreeCellFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import javafx.beans.Observable; 21 | import javafx.scene.control.TreeCell; 22 | import javafx.scene.control.TreeView; 23 | import javafx.util.Callback; 24 | import ui.FXTreeCell; 25 | 26 | class ModFuncTreeCellFactory implements Callback, TreeCell> { 27 | 28 | private final DbgController dbgController; 29 | 30 | private boolean showModuleName; 31 | 32 | public ModFuncTreeCellFactory(DbgController aDbgController) { 33 | dbgController = aDbgController; 34 | } 35 | 36 | @Override 37 | public TreeCell call(TreeView tree) { 38 | ModFuncGraphic mfg; 39 | 40 | mfg = new ModFuncGraphic( 41 | dbgController::toggleTraceModFunc, 42 | dbgController::isTraced 43 | ); 44 | mfg.setShowModuleName(isShowModuleName()); 45 | 46 | dbgController.addTraceListener((Observable o) -> { 47 | mfg.onTracesChange(); 48 | }); 49 | 50 | return new FXTreeCell(mfg, mfg); 51 | } 52 | 53 | public boolean isShowModuleName() { 54 | return showModuleName; 55 | } 56 | 57 | public void setShowModuleName(boolean showModuleName) { 58 | this.showModuleName = showModuleName; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/PrefBind.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.io.File; 21 | import java.io.FileInputStream; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | import java.util.Map; 28 | import java.util.Timer; 29 | import java.util.TimerTask; 30 | 31 | import com.moandjiezana.toml.Toml; 32 | import com.moandjiezana.toml.TomlWriter; 33 | 34 | import erlyberly.ConnectionView.KnownNode; 35 | import javafx.beans.property.BooleanProperty; 36 | import javafx.beans.property.StringProperty; 37 | import javafx.beans.value.ObservableValue; 38 | 39 | /** 40 | * Load dot Properties files, bind them to JavaFX properties and auto-store 41 | * them when the JavaFX properties change. 42 | *

43 | * Call {@link PrefBind#setup()} before hand then {@link PrefBind#bind(String, StringProperty)} away. 44 | *

45 | * All methods must be called on the JavaFX thread. 46 | */ 47 | public class PrefBind { 48 | 49 | private static final long WRITE_TO_DISK_DELAY = 500L; 50 | 51 | private static final boolean IS_DAEMON = true; 52 | private static Timer timer = new Timer(IS_DAEMON); 53 | 54 | static { 55 | timer.scheduleAtFixedRate(new TimerTask() { 56 | @Override 57 | public void run() { 58 | synchronized (AWAIT_STORE_LOCK) { 59 | if(!awaitingStore) 60 | return; 61 | awaitingStore = false; 62 | } 63 | store(); 64 | }}, WRITE_TO_DISK_DELAY, WRITE_TO_DISK_DELAY); 65 | } 66 | 67 | private static Map props; 68 | 69 | private static File erlyberlyConfig; 70 | 71 | private static final Object AWAIT_STORE_LOCK = new Object(); 72 | private static boolean awaitingStore; 73 | 74 | private PrefBind() {} 75 | 76 | public static void bind(final String propName, StringProperty stringProp) { 77 | if(props == null) { 78 | return; 79 | } 80 | String storedValue = (String) props.get(propName); 81 | if(storedValue != null) { 82 | stringProp.set(storedValue); 83 | } 84 | stringProp.addListener((ObservableValue o, String oldValue, String newValue) -> { 85 | set(propName, newValue); 86 | }); 87 | } 88 | 89 | public static void bindBoolean(final String propName, BooleanProperty boolProp){ 90 | if(props == null) { 91 | return; 92 | } 93 | Boolean storedValue = (Boolean) props.get(propName); 94 | if(storedValue != null) { 95 | boolProp.set(storedValue); 96 | } 97 | boolProp.addListener((ObservableValue observable, Boolean oldValue, Boolean newValue) -> { 98 | set(propName, newValue); 99 | }); 100 | } 101 | 102 | static void store() { 103 | try { 104 | //props.store(new FileOutputStream(erlyberlyConfig), " erlyberly at https://github.com/andytill/erlyberly"); 105 | new TomlWriter().write(props, new FileOutputStream(erlyberlyConfig)); 106 | } 107 | catch (IOException | NoClassDefFoundError e) { 108 | e.printStackTrace(); 109 | } 110 | } 111 | 112 | public static void setup() throws IOException { 113 | String home = System.getProperty("user.home"); 114 | 115 | File homeDir = new File(home); 116 | 117 | File dotConfigDir = new File(homeDir, ".config"); 118 | 119 | if(dotConfigDir.exists()) { 120 | homeDir = dotConfigDir; 121 | } 122 | 123 | erlyberlyConfig = new File(homeDir, ".erlyberly2"); 124 | erlyberlyConfig.createNewFile(); 125 | 126 | Toml toml; 127 | toml = new Toml(); 128 | toml.read(new FileInputStream(erlyberlyConfig)); 129 | props = toml.getKeyValues(); 130 | } 131 | 132 | public static Object get(Object key) { 133 | return props.get(key); 134 | } 135 | 136 | public static Object getOrDefault(String key, Object theDefault) { 137 | return props.getOrDefault(key, theDefault); 138 | } 139 | 140 | public static double getOrDefaultDouble(String key, Double theDefault) { 141 | return Double.parseDouble(PrefBind.getOrDefault(key, theDefault).toString()); 142 | } 143 | 144 | public static boolean getOrDefaultBoolean(String key, boolean theDefault) { 145 | return Boolean.parseBoolean(PrefBind.getOrDefault(key, theDefault).toString()); 146 | } 147 | 148 | public static void set(String propName, Object newValue) { 149 | props.put(propName, newValue); 150 | synchronized (AWAIT_STORE_LOCK) { 151 | awaitingStore = true; 152 | } 153 | } 154 | 155 | @SuppressWarnings("unchecked") 156 | public static List> getKnownNodes() { 157 | return (List>) props.getOrDefault("knownNodes", new ArrayList<>()); 158 | } 159 | 160 | public static void storeKnownNode(KnownNode knownNode) { 161 | List> knownNodes = getKnownNodes(); 162 | knownNodes.add(Arrays.asList(knownNode.getNodeName(), knownNode.getCookie())); 163 | props.put("knownNodes", knownNodes); 164 | store(); 165 | } 166 | 167 | public static void removeKnownNode(KnownNode knownNode) { 168 | List nodeAsList = Arrays.asList(knownNode.getNodeName(), knownNode.getCookie()); 169 | List> knownNodes = getKnownNodes(); 170 | knownNodes.remove(nodeAsList); 171 | props.put("knownNodes", knownNodes); 172 | store(); 173 | } 174 | 175 | /** 176 | * The maximum number of traces that can be queued in the trace 177 | * proccesses queue before tracing is suspended on the node. 178 | */ 179 | public static int getMaxTraceQueueLengthConfig() { 180 | Number maxTraceQueueLength = (Number)getOrDefault("maxTraceQueueLength", 1000); 181 | return maxTraceQueueLength.intValue(); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/PreferencesView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.net.URL; 21 | import java.util.ResourceBundle; 22 | 23 | import erlyberly.format.ErlangFormatter; 24 | import erlyberly.format.ElixirFormatter; 25 | import erlyberly.format.LFEFormatter; 26 | import javafx.beans.Observable; 27 | import javafx.fxml.FXML; 28 | import javafx.fxml.Initializable; 29 | import javafx.scene.control.CheckBox; 30 | import javafx.scene.control.RadioButton; 31 | import javafx.scene.control.TextField; 32 | import javafx.scene.control.ToggleGroup; 33 | 34 | public class PreferencesView implements Initializable { 35 | 36 | @FXML 37 | private TextField nodeNameField; 38 | @FXML 39 | private TextField cookieField; 40 | @FXML 41 | private CheckBox autoConnectField; 42 | @FXML 43 | private CheckBox showSourceInSystemEditorBox; 44 | @FXML 45 | private CheckBox hideProcesses; 46 | @FXML 47 | private CheckBox hideModules; 48 | @FXML 49 | private RadioButton erlangTermsButton; 50 | @FXML 51 | private RadioButton elixirTermsButton; 52 | @FXML 53 | private RadioButton lfeTermsButton; 54 | 55 | @Override 56 | public void initialize(URL url, ResourceBundle r) { 57 | selectFormattingButton(); 58 | 59 | final ToggleGroup group; 60 | group = new ToggleGroup(); 61 | group.selectedToggleProperty().addListener((Observable o) -> { 62 | storeFormattingPreferenceChange(); 63 | }); 64 | erlangTermsButton.setToggleGroup(group); 65 | elixirTermsButton.setToggleGroup(group); 66 | lfeTermsButton.setToggleGroup(group); 67 | 68 | PrefBind.bind("targetNodeName", nodeNameField.textProperty()); 69 | PrefBind.bind("cookieName", cookieField.textProperty()); 70 | PrefBind.bindBoolean("autoConnect", autoConnectField.selectedProperty()); 71 | PrefBind.bindBoolean("hideProcesses", hideProcesses.selectedProperty()); 72 | PrefBind.bindBoolean("hideModules", hideModules.selectedProperty()); 73 | PrefBind.bindBoolean("showSourceInSystemEditor", showSourceInSystemEditorBox.selectedProperty()); 74 | } 75 | 76 | private void storeFormattingPreferenceChange() { 77 | if(erlangTermsButton.isSelected()) { 78 | PrefBind.set("termFormatting", "erlang"); 79 | ErlyBerly.setTermFormatter(new ErlangFormatter()); 80 | } 81 | else if(elixirTermsButton.isSelected()) { 82 | PrefBind.set("termFormatting", "elixir"); 83 | ErlyBerly.setTermFormatter(new ElixirFormatter()); 84 | } 85 | else if(lfeTermsButton.isSelected()) { 86 | PrefBind.set("termFormatting", "lfe"); 87 | ErlyBerly.setTermFormatter(new LFEFormatter()); 88 | } 89 | selectFormattingButton(); 90 | } 91 | 92 | private void selectFormattingButton() { 93 | String formattingPref = PrefBind.getOrDefault("termFormatting", "erlang").toString(); 94 | if("erlang".equals(formattingPref)) { 95 | erlangTermsButton.setSelected(true); 96 | } 97 | else if("elixir".equals(formattingPref)) { 98 | elixirTermsButton.setSelected(true); 99 | } 100 | else if("lfe".equals(formattingPref)) { 101 | lfeTermsButton.setSelected(true); 102 | } 103 | else 104 | throw new RuntimeException("Invalid configuration for property 'termFormatting' it must be 'erlang' or 'lfe' but was " + formattingPref); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/ProcController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.io.IOException; 21 | import java.util.ArrayList; 22 | import java.util.Collections; 23 | import java.util.Comparator; 24 | 25 | import com.ericsson.otp.erlang.OtpErlangException; 26 | import com.ericsson.otp.erlang.OtpErlangObject; 27 | 28 | import erlyberly.node.RpcCallback; 29 | import javafx.application.Platform; 30 | import javafx.beans.property.SimpleBooleanProperty; 31 | import javafx.beans.property.SimpleObjectProperty; 32 | import javafx.beans.property.SimpleStringProperty; 33 | import javafx.beans.value.ObservableValue; 34 | import javafx.collections.FXCollections; 35 | import javafx.collections.ObservableList; 36 | import javafx.collections.transformation.FilteredList; 37 | import javafx.collections.transformation.SortedList; 38 | import javafx.scene.control.TableColumn.SortType; 39 | 40 | /** 41 | * Logic and processing for the entop control. 42 | */ 43 | public class ProcController { 44 | 45 | private final SimpleBooleanProperty polling; 46 | 47 | private final SimpleObjectProperty procSortProperty; 48 | 49 | private ProcPollerThread procPollerThread; 50 | 51 | private final Object waiter; 52 | 53 | private int timeout = -1; 54 | 55 | private final SimpleStringProperty filter = new SimpleStringProperty(); 56 | 57 | private final ObservableList processes = FXCollections.observableArrayList(); 58 | 59 | private final FilteredList filteredProcesses = new FilteredList(processes); 60 | 61 | private final SortedList sortedProcesses = new SortedList(filteredProcesses); 62 | 63 | private volatile boolean temporarilySuspendPolling; 64 | 65 | public ProcController() { 66 | polling = new SimpleBooleanProperty(); 67 | 68 | procSortProperty = new SimpleObjectProperty<>(new ProcSort("reduc", SortType.DESCENDING)); 69 | 70 | procPollerThread = new ProcPollerThread(); 71 | 72 | waiter = new Object(); 73 | 74 | Platform.runLater(() -> { 75 | ErlyBerly.nodeAPI().connectedProperty().addListener((o) -> { startPollingThread(); } ); 76 | }); 77 | 78 | filter.addListener((o, ov, nv) -> { updateProcFilter(nv); }); 79 | } 80 | 81 | public void setListComparator(ObservableValue> tableComparator) { 82 | sortedProcesses.comparatorProperty().bind(tableComparator); 83 | } 84 | 85 | private void updateProcFilter(String filterText) { 86 | BasicSearch basicSearch = new BasicSearch(filterText); 87 | filteredProcesses.setPredicate((proc) -> { return isMatchingProcess(basicSearch, proc); }); 88 | } 89 | 90 | private boolean isMatchingProcess(BasicSearch basicSearch, ProcInfo proc) { 91 | return 92 | basicSearch.matches(proc.getPid(), proc.getProcessName()); 93 | } 94 | 95 | private void startPollingThread() { 96 | if(ErlyBerly.nodeAPI().connectedProperty().get() && !procPollerThread.isAlive()) { 97 | procPollerThread.start(); 98 | } 99 | } 100 | 101 | public SimpleStringProperty filterProperty() { 102 | return filter; 103 | } 104 | 105 | public void refreshOnce() { 106 | synchronized (waiter) { 107 | waiter.notify(); 108 | } 109 | } 110 | 111 | public void togglePolling() { 112 | if(timeout == -1) { 113 | timeout = 1000; 114 | 115 | refreshOnce(); 116 | } 117 | else { 118 | timeout = -1; 119 | } 120 | polling.set(timeout > 0); 121 | } 122 | 123 | public void clearProcesses(){ 124 | processes.clear(); 125 | } 126 | 127 | public ObservableList getProcs() { 128 | return sortedProcesses; 129 | } 130 | 131 | public SimpleBooleanProperty pollingProperty() { 132 | return polling; 133 | } 134 | 135 | public SimpleObjectProperty procSortProperty() { 136 | return procSortProperty; 137 | } 138 | 139 | public SimpleBooleanProperty connectedProperty() { 140 | return ErlyBerly.nodeAPI().connectedProperty(); 141 | } 142 | 143 | public boolean isTemporarilySuspendPolling() { 144 | return temporarilySuspendPolling; 145 | } 146 | 147 | public void setTemporarilySuspendPolling(boolean temporarilySuspendPolling) { 148 | this.temporarilySuspendPolling = temporarilySuspendPolling; 149 | } 150 | 151 | private final class ProcPollerThread extends Thread { 152 | public ProcPollerThread() { 153 | // make sure we don't hang the VM on close because of this thread 154 | setDaemon(true); 155 | setName("Process Info Poller"); 156 | } 157 | 158 | @Override 159 | public void run() { 160 | 161 | while(true) { 162 | 163 | boolean connected = ErlyBerly.nodeAPI().isConnected(); 164 | if(!connected) 165 | continue; 166 | 167 | final ArrayList processList = new ArrayList<>(); 168 | 169 | if (!temporarilySuspendPolling) { 170 | try { 171 | ErlyBerly.nodeAPI().retrieveProcessInfo(processList); 172 | 173 | updateProcessList(processList); 174 | } 175 | catch (Exception e) { 176 | e.printStackTrace(); 177 | } 178 | } 179 | 180 | try { 181 | synchronized (waiter) { 182 | if(timeout > 0) 183 | waiter.wait(timeout); 184 | else 185 | waiter.wait(); 186 | } 187 | } 188 | catch (InterruptedException e1) { 189 | e1.printStackTrace(); 190 | } 191 | } 192 | } 193 | 194 | private void updateProcessList(final ArrayList processList) { 195 | Platform.runLater(new Runnable() { 196 | @Override 197 | public void run() { 198 | ProcSort procSort = procSortProperty.get(); 199 | if(procSort != null) { 200 | Comparator comparator = null; 201 | 202 | if("proc".equals(procSort.getSortField())) { 203 | comparator = new Comparator() { 204 | @Override 205 | public int compare(ProcInfo o1, ProcInfo o2) { 206 | return o1.getProcessName().compareTo(o2.getProcessName()); 207 | }}; 208 | } 209 | else if("reduc".equals(procSort.getSortField())) { 210 | comparator = new Comparator() { 211 | @Override 212 | public int compare(ProcInfo o1, ProcInfo o2) { 213 | return Long.compare(o1.getReductions(), o2.getReductions()); 214 | }}; 215 | } 216 | 217 | if(comparator != null) { 218 | if(procSort.getSortType() == SortType.DESCENDING) { 219 | comparator = Collections.reverseOrder(comparator); 220 | } 221 | Collections.sort(processList, comparator); 222 | } 223 | } 224 | processes.clear(); 225 | processes.addAll(processList); 226 | }}); 227 | } 228 | } 229 | 230 | public void processState(ProcInfo proc, RpcCallback callback) { 231 | new ProcessStateThread(proc.getPid(), callback).start(); 232 | } 233 | 234 | class ProcessStateThread extends Thread { 235 | 236 | private final String pidString; 237 | private final RpcCallback callback; 238 | 239 | public ProcessStateThread(String aPidString, RpcCallback aCallback) { 240 | pidString = aPidString; 241 | callback = aCallback; 242 | 243 | setDaemon(true); 244 | setName("Erlyberly Get Process State"); 245 | } 246 | 247 | @Override 248 | public void run() { 249 | try { 250 | OtpErlangObject processState = ErlyBerly.nodeAPI().getProcessState(pidString); 251 | 252 | Platform.runLater(() -> { callback.callback(processState); }); 253 | } 254 | catch (OtpErlangException | IOException e) { 255 | e.printStackTrace(); 256 | } 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/ProcInfo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.util.Map; 21 | import java.util.Objects; 22 | 23 | import com.ericsson.otp.erlang.OtpErlangAtom; 24 | import com.ericsson.otp.erlang.OtpErlangList; 25 | import com.ericsson.otp.erlang.OtpErlangLong; 26 | import com.ericsson.otp.erlang.OtpErlangString; 27 | 28 | import javafx.beans.property.LongProperty; 29 | import javafx.beans.property.SimpleLongProperty; 30 | import javafx.beans.property.SimpleStringProperty; 31 | import javafx.beans.property.StringProperty; 32 | 33 | /** 34 | * Domain object for an erlang process. 35 | */ 36 | public class ProcInfo implements Comparable { 37 | 38 | private static final OtpErlangAtom TOTAL_HEAP_SIZE_ATOM = new OtpErlangAtom("total_heap_size"); 39 | 40 | private static final OtpErlangAtom STACK_SIZE_ATOM = new OtpErlangAtom("stack_size"); 41 | 42 | private static final OtpErlangAtom HEAP_SIZE_ATOM = new OtpErlangAtom("heap_size"); 43 | 44 | private static final OtpErlangAtom MSG_QUEUE_LEN_ATOM = new OtpErlangAtom("message_queue_len"); 45 | 46 | private static final OtpErlangList EMPTY_LIST = new OtpErlangList(); 47 | 48 | private static final OtpErlangAtom REGISTERED_NAME_ATOM = new OtpErlangAtom("registered_name"); 49 | 50 | private static final OtpErlangAtom GLOBAL_NAME_ATOM = new OtpErlangAtom("global_name"); 51 | 52 | private static final OtpErlangAtom PID_ATOM = new OtpErlangAtom("pid"); 53 | 54 | private static final OtpErlangAtom REDUCTIONS_ATOM = new OtpErlangAtom("reductions"); 55 | 56 | private StringProperty pid; 57 | 58 | private StringProperty processName; 59 | 60 | private LongProperty reductions; 61 | 62 | private LongProperty msgQueueLen; 63 | 64 | private LongProperty heapSize; 65 | 66 | private LongProperty stackSize; 67 | 68 | private LongProperty totalHeapSize; 69 | 70 | public void setTotalHeapSize(long value) { 71 | totalHeapSizeProperty().set(value); 72 | } 73 | 74 | public long getTotalHeapSize() { 75 | return totalHeapSizeProperty().get(); 76 | } 77 | 78 | public LongProperty totalHeapSizeProperty() { 79 | if (totalHeapSize == null) 80 | totalHeapSize = new SimpleLongProperty(this, "totalHeapSize"); 81 | return totalHeapSize; 82 | } 83 | 84 | public void setStackSize(long value) { 85 | stackSizeProperty().set(value); 86 | } 87 | 88 | public long getStackSize() { 89 | return stackSizeProperty().get(); 90 | } 91 | 92 | public LongProperty stackSizeProperty() { 93 | if (stackSize == null) 94 | stackSize = new SimpleLongProperty(this, "stackSize"); 95 | return stackSize; 96 | } 97 | 98 | public void setHeapSize(long value) { 99 | heapSizeProperty().set(value); 100 | } 101 | 102 | public long getHeapSize() { 103 | return heapSizeProperty().get(); 104 | } 105 | 106 | public LongProperty heapSizeProperty() { 107 | if (heapSize == null) 108 | heapSize = new SimpleLongProperty(this, "msgQueueLen"); 109 | return heapSize; 110 | } 111 | 112 | public void setMsgQueueLen(long value) { 113 | msgQueueLenProperty().set(value); 114 | } 115 | 116 | public long getMsgQueueLen() { 117 | return msgQueueLenProperty().get(); 118 | } 119 | 120 | public LongProperty msgQueueLenProperty() { 121 | if (msgQueueLen == null) 122 | msgQueueLen = new SimpleLongProperty(this, "msgQueueLen"); 123 | return msgQueueLen; 124 | } 125 | 126 | public void setPid(String value) { 127 | pidProperty().set(value); 128 | } 129 | 130 | public String getPid() { 131 | return pidProperty().get(); 132 | } 133 | 134 | public StringProperty pidProperty() { 135 | if (pid == null) 136 | pid = new SimpleStringProperty(this, "pid"); 137 | return pid; 138 | } 139 | 140 | public void setProcessName(String value) { 141 | processNameProperty().set(value); 142 | } 143 | 144 | public String getProcessName() { 145 | return processNameProperty().get(); 146 | } 147 | 148 | public StringProperty processNameProperty() { 149 | if (processName == null) 150 | processName = new SimpleStringProperty(this, "processName"); 151 | return processName; 152 | } 153 | 154 | public void setReductions(long value) { 155 | reductionsProperty().set(value); 156 | } 157 | 158 | public long getReductions() { 159 | return reductionsProperty().get(); 160 | } 161 | 162 | public LongProperty reductionsProperty() { 163 | if (reductions == null) 164 | reductions = new SimpleLongProperty(this, "reductions"); 165 | return reductions; 166 | } 167 | 168 | public static ProcInfo toProcessInfo(Map propList) { 169 | Object processName = propList.get(REGISTERED_NAME_ATOM); 170 | Object pid = ((OtpErlangString) propList.get(PID_ATOM)).stringValue(); 171 | 172 | if(EMPTY_LIST.equals(processName)) { 173 | processName = ""; 174 | } 175 | 176 | ProcInfo processInfo; 177 | 178 | processInfo = new ProcInfo(); 179 | String localName = Objects.toString(processName, ""); 180 | String globalName = Objects.toString(propList.get(GLOBAL_NAME_ATOM), ""); 181 | processInfo.setProcessName(localName + ("".equals(localName) ? "" : " ") + ("".equals(globalName) ? "" : "(" + globalName + ")")); 182 | processInfo.setPid(Objects.toString(pid, "")); 183 | processInfo.setReductions(toLong(propList.get(REDUCTIONS_ATOM))); 184 | processInfo.setMsgQueueLen(toLong(propList.get(MSG_QUEUE_LEN_ATOM))); 185 | processInfo.setHeapSize(toLong(propList.get(HEAP_SIZE_ATOM))); 186 | processInfo.setStackSize(toLong(propList.get(STACK_SIZE_ATOM))); 187 | processInfo.setTotalHeapSize(toLong(propList.get(TOTAL_HEAP_SIZE_ATOM))); 188 | 189 | return processInfo; 190 | } 191 | 192 | private static long toLong(Object object) { 193 | if (object instanceof OtpErlangLong) { 194 | return ((OtpErlangLong) object).longValue(); 195 | } 196 | return 0; 197 | } 198 | 199 | /** 200 | * We shouldn't need to implement this but sometimes {@link TableView} 201 | * attempts to sort the {@link ProcInfo} objects itself and tries to cast 202 | * {@link ProcInfo} to {@link Comparable}. 203 | *

204 | * To avoid this exception we're just implementing comparable even if it 205 | * gives the wrong sort to what the user expected. 206 | */ 207 | @Override 208 | public int compareTo(ProcInfo o) { 209 | return getProcessName().compareTo(o.getProcessName()); 210 | } 211 | 212 | public String getShortName() { 213 | String processName2 = getProcessName(); 214 | if(processName2 != null && !"".equals(getProcessName())) 215 | return getProcessName(); 216 | return getPid(); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/ProcSort.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import javafx.scene.control.TableColumn.SortType; 21 | 22 | /** 23 | * Description of how to sort {@link ProcInfo} objects. 24 | */ 25 | public class ProcSort { 26 | private final String sortField; 27 | 28 | private final SortType sortType; 29 | 30 | public ProcSort(String sortField, SortType sortType) { 31 | this.sortField = sortField; 32 | this.sortType = sortType; 33 | } 34 | 35 | public String getSortField() { 36 | return sortField; 37 | } 38 | 39 | public SortType getSortType() { 40 | return sortType; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "ProcSort [sortField=" + sortField + ", sortType=" + sortType 46 | + "]"; 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | final int prime = 31; 52 | int result = 1; 53 | result = prime * result 54 | + ((sortField == null) ? 0 : sortField.hashCode()); 55 | result = prime * result 56 | + ((sortType == null) ? 0 : sortType.hashCode()); 57 | return result; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object obj) { 62 | if (this == obj) 63 | return true; 64 | if (obj == null) 65 | return false; 66 | if (getClass() != obj.getClass()) 67 | return false; 68 | ProcSort other = (ProcSort) obj; 69 | if (sortField == null) { 70 | if (other.sortField != null) 71 | return false; 72 | } else if (!sortField.equals(other.sortField)) 73 | return false; 74 | if (sortType != other.sortType) 75 | return false; 76 | return true; 77 | } 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/SeqTraceLog.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import java.util.Map; 21 | 22 | import com.ericsson.otp.erlang.OtpErlangObject; 23 | import com.ericsson.otp.erlang.OtpErlangString; 24 | 25 | import erlyberly.node.OtpUtil; 26 | 27 | public class SeqTraceLog { 28 | 29 | private final Object msgType; 30 | private final Object serial; 31 | private final String from; 32 | private final String to; 33 | private final Object message; 34 | private final Object timestamp; 35 | 36 | public SeqTraceLog(Object msgType, Object serial, Object from, Object to, Object message, Object timestamp) { 37 | this.msgType = msgType; 38 | this.serial = serial; 39 | this.from = stringValue(from); 40 | this.to = stringValue(to); 41 | this.message = message; 42 | this.timestamp = timestamp; 43 | } 44 | 45 | public static SeqTraceLog build(Map props) { 46 | Object msgType = props.get(OtpUtil.atom("msg_type")); 47 | Object serial = props.get(OtpUtil.atom("serial")); 48 | Object from = props.get(OtpUtil.atom("from")); 49 | Object to = props.get(OtpUtil.atom("to")); 50 | Object message = props.get(OtpUtil.atom("message")); 51 | Object timestamp = props.get(OtpUtil.atom("timestamp")); 52 | return new SeqTraceLog(msgType, serial, from, to, message, timestamp); 53 | } 54 | 55 | private String stringValue(Object obj) { 56 | if(obj instanceof OtpErlangString) 57 | return ((OtpErlangString) obj).stringValue(); 58 | return obj.toString(); 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return "SeqTraceLog [msg_type=" + msgType + ", serial=" + serial 64 | + ", from=" + from + ", to=" + to + ", message=" + message 65 | + "]"; 66 | } 67 | 68 | public OtpErlangObject getMessage() { 69 | return (OtpErlangObject) message; 70 | } 71 | 72 | public Object getMsgType() { 73 | return msgType; 74 | } 75 | 76 | public Object getSerial() { 77 | return serial; 78 | } 79 | 80 | public Object getFrom() { 81 | return from; 82 | } 83 | 84 | public Object getTo() { 85 | return to; 86 | } 87 | 88 | public Object getTimestamp() { 89 | return timestamp; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/SeqTraceView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import javafx.collections.ListChangeListener; 21 | import javafx.collections.ObservableList; 22 | import javafx.scene.control.TableColumn; 23 | import javafx.scene.control.TableView; 24 | import javafx.scene.control.cell.PropertyValueFactory; 25 | import javafx.scene.input.MouseButton; 26 | import javafx.scene.input.MouseEvent; 27 | import javafx.scene.layout.Priority; 28 | import javafx.scene.layout.VBox; 29 | 30 | public class SeqTraceView extends VBox { 31 | 32 | private final TableView table; 33 | 34 | public SeqTraceView(ObservableList seqTraceLogs) { 35 | table = new TableView(); 36 | table.setOnMouseClicked(this::onTraceClicked); 37 | table.setMaxHeight(Integer.MAX_VALUE); 38 | VBox.setVgrow(table, Priority.ALWAYS); 39 | 40 | setupTableColumns(); 41 | 42 | getChildren().add(table); 43 | 44 | seqTraceLogs.addListener(this::traceLogsChanged); 45 | } 46 | 47 | @SuppressWarnings("unchecked") 48 | private void setupTableColumns() { 49 | TableColumn msgType, serial, from, to, message, timestamp; 50 | 51 | msgType = new TableColumn<>("Msg Type"); 52 | msgType.setCellValueFactory(new PropertyValueFactory<>("msgType")); 53 | 54 | timestamp = new TableColumn<>("Timestamp"); 55 | timestamp.setCellValueFactory(new PropertyValueFactory<>("timestamp")); 56 | timestamp.setPrefWidth(150d); 57 | 58 | serial = new TableColumn<>("Serial"); 59 | serial.setCellValueFactory(new PropertyValueFactory<>("serial")); 60 | 61 | from = new TableColumn<>("From"); 62 | from.setCellValueFactory(new PropertyValueFactory<>("from")); 63 | 64 | to = new TableColumn<>("To"); 65 | to.setCellValueFactory(new PropertyValueFactory<>("to")); 66 | 67 | // the message column should take the remaining space 68 | // TODO http://stackoverflow.com/questions/30288558/make-the-last-column-in-a-javafx-tableview-take-the-remaining-space 69 | message = new TableColumn<>("Message"); 70 | message.setCellValueFactory(new PropertyValueFactory<>("message")); 71 | message.setPrefWidth(700d); 72 | 73 | table.getColumns().addAll(msgType, timestamp, serial, from, to, message); 74 | } 75 | 76 | private void traceLogsChanged(ListChangeListener.Change e) { 77 | while(e.next()) { 78 | for (SeqTraceLog trace : e.getAddedSubList()) { 79 | table.getItems().add(trace); 80 | } 81 | } 82 | } 83 | 84 | private void onTraceClicked(MouseEvent me) { 85 | if(me.getButton().equals(MouseButton.PRIMARY)) { 86 | if(me.getClickCount() == 2) { 87 | SeqTraceLog selectedItem = table.getSelectionModel().getSelectedItem(); 88 | 89 | if(selectedItem != null) { 90 | showTraceTermView(selectedItem); 91 | } 92 | } 93 | } 94 | } 95 | 96 | private void showTraceTermView(final SeqTraceLog seqTraceLog) { 97 | TermTreeView argTermsTreeView; 98 | 99 | argTermsTreeView = newTermTreeView(); 100 | argTermsTreeView.populateFromTerm(seqTraceLog.getMessage()); 101 | 102 | ErlyBerly.showPane("Seq Trace", ErlyBerly.wrapInPane(argTermsTreeView)); 103 | } 104 | 105 | private TermTreeView newTermTreeView() { 106 | TermTreeView termTreeView; 107 | 108 | termTreeView = new TermTreeView(); 109 | termTreeView.setMaxHeight(Integer.MAX_VALUE); 110 | VBox.setVgrow(termTreeView, Priority.ALWAYS); 111 | 112 | return termTreeView; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/StackTraceView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import com.ericsson.otp.erlang.OtpErlangAtom; 21 | import com.ericsson.otp.erlang.OtpErlangList; 22 | import com.ericsson.otp.erlang.OtpErlangLong; 23 | import com.ericsson.otp.erlang.OtpErlangObject; 24 | import com.ericsson.otp.erlang.OtpErlangRangeException; 25 | import com.ericsson.otp.erlang.OtpErlangString; 26 | import com.ericsson.otp.erlang.OtpErlangTuple; 27 | 28 | import erlyberly.StackTraceView.ErlyberlyStackTraceElement; 29 | import erlyberly.node.OtpUtil; 30 | import javafx.application.Platform; 31 | import javafx.scene.control.Hyperlink; 32 | import javafx.scene.control.ListCell; 33 | import javafx.scene.control.ListView; 34 | import javafx.util.Callback; 35 | 36 | public class StackTraceView extends ListView { 37 | @SuppressWarnings({ "unchecked", "rawtypes" }) 38 | public StackTraceView() { 39 | applyStackTraceCellFactory((ListView)this); 40 | } 41 | 42 | private void applyStackTraceCellFactory(ListView listview) { 43 | listview.setCellFactory(new Callback, ListCell>() { 44 | @Override 45 | public ListCell call(ListView param) { 46 | return new ListCell() { 47 | private final Hyperlink functionLink = new Hyperlink(); 48 | { 49 | setGraphic(functionLink); 50 | functionLink.setOnAction((e) -> { 51 | ErlyberlyStackTraceElement stackElement = getItem(); 52 | if(stackElement == null) 53 | return; 54 | if(stackElement.isError()) 55 | return; 56 | ModFunc mf = stackElement.getModFunc(); 57 | ErlyBerly.runIO(() -> { 58 | try { 59 | String source = ErlyBerly.nodeAPI().moduleFunctionSourceCode( 60 | mf.getModuleName(), mf.getFuncName(), mf.getArity()); 61 | Platform.runLater(() -> { 62 | ErlyBerly.showPane( 63 | "Crash Report Stack", 64 | ErlyBerly.wrapInPane(new CodeView(source)) 65 | ); 66 | }); 67 | } catch (Exception e1) { 68 | e1.printStackTrace(); 69 | } 70 | }); 71 | }); 72 | } 73 | 74 | @Override 75 | public void updateItem(ErlyberlyStackTraceElement item, boolean empty) { 76 | super.updateItem(item, empty); 77 | if(item == null) 78 | functionLink.setText(""); 79 | else 80 | functionLink.setText(item.toString()); 81 | } 82 | }; 83 | } 84 | }); 85 | } 86 | 87 | public void populateFromCrashReport(CrashReport crashReport) { 88 | try { 89 | getItems().addAll(crashReport.mapStackTraces((module, function, arity, file, line) -> { 90 | try { 91 | ModFunc modFunc = mfaToModFunc(module, function, arity); 92 | return new ErlyberlyStackTraceElement(modFunc, file.toString(), line.longValue()); 93 | } 94 | catch (Exception e) { 95 | throw new RuntimeException(e); 96 | } 97 | })); 98 | } catch (Exception e) { 99 | e.printStackTrace(); 100 | } 101 | } 102 | 103 | private ModFunc mfaToModFunc(OtpErlangAtom module, OtpErlangAtom function, OtpErlangLong arity) { 104 | boolean exported = false; 105 | boolean synthetic = false; 106 | try { 107 | return new ModFunc(module.toString(), function.toString(), arity.intValue(), exported, synthetic); 108 | } 109 | catch (OtpErlangRangeException e) { 110 | throw new RuntimeException(e); 111 | } 112 | } 113 | 114 | public void populateFromMfaList(OtpErlangList stackTrace) { 115 | try { 116 | for (OtpErlangObject obj : stackTrace) { 117 | if(obj instanceof OtpErlangString) { 118 | OtpErlangString errorMessage = (OtpErlangString) obj; 119 | getItems().add(new ErlyberlyStackTraceElement(errorMessage.stringValue())); 120 | } 121 | else { 122 | OtpErlangTuple tuple = OtpUtil.toTuple(obj); 123 | OtpErlangAtom module = (OtpErlangAtom) tuple.elementAt(0); 124 | OtpErlangAtom function = (OtpErlangAtom) tuple.elementAt(1); 125 | OtpErlangLong arity = (OtpErlangLong) tuple.elementAt(2); 126 | ModFunc modFunc = mfaToModFunc(module, function, arity); 127 | getItems().add(new ErlyberlyStackTraceElement(modFunc, "", 0L)); 128 | } 129 | } 130 | } 131 | catch (Exception e) { 132 | // try/catch so if there is a problem decoding the process_dump, it won't 133 | // stop the trace log from being shown. 134 | e.printStackTrace(); 135 | } 136 | } 137 | 138 | public boolean isStackTracesEmpty() { 139 | return getItems().isEmpty(); 140 | } 141 | 142 | /** 143 | * Put erlyberly on the front of this because there is already a StaceTraceElement 144 | * class in the standard library. 145 | */ 146 | static class ErlyberlyStackTraceElement { 147 | private final ModFunc modFunc; 148 | private final long line; 149 | private final String file; 150 | private final String error; 151 | 152 | public ErlyberlyStackTraceElement(ModFunc modFunc, String file, long line) { 153 | this.modFunc = modFunc; 154 | this.file = file; 155 | this.line = line; 156 | this.error = null; 157 | } 158 | 159 | public ErlyberlyStackTraceElement(String anError) { 160 | this.modFunc = null; 161 | this.file = null; 162 | this.line = -1L; 163 | this.error = anError; 164 | } 165 | 166 | public ModFunc getModFunc() { 167 | return modFunc; 168 | } 169 | 170 | public boolean isError() { 171 | return error != null; 172 | } 173 | 174 | @Override 175 | public String toString() { 176 | if(isError()) { 177 | return error; 178 | } 179 | String display = modFunc.toFullString(); 180 | if(file != null && !file.isEmpty()) { 181 | display += " (" + file + ":" + line +")"; 182 | } 183 | return display; 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/TermTreeItem.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import com.ericsson.otp.erlang.OtpErlangObject; 21 | 22 | /** 23 | * A wrapper class around a {@link TermTreeView} tree item, with a erlang term 24 | * and the string which gets shown in the tree. The string is not necessarily 25 | * derivable from the term, and depends on its position in the tree so much be 26 | * separated. 27 | */ 28 | class TermTreeItem { 29 | private final String toString; 30 | private final OtpErlangObject object; 31 | public TermTreeItem(OtpErlangObject object, String toString) { 32 | this.object = object; 33 | this.toString = toString; 34 | } 35 | public String getToString() { 36 | return toString; 37 | } 38 | public OtpErlangObject getObject() { 39 | return object; 40 | } 41 | @Override 42 | public String toString() { 43 | return toString; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/TraceContextMenu.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | 21 | import java.util.ArrayList; 22 | 23 | import javafx.collections.ObservableList; 24 | import javafx.event.ActionEvent; 25 | import javafx.event.EventHandler; 26 | import javafx.scene.control.ContextMenu; 27 | import javafx.scene.control.MenuItem; 28 | import javafx.scene.control.SeparatorMenuItem; 29 | import javafx.scene.input.Clipboard; 30 | import javafx.scene.input.ClipboardContent; 31 | import javafx.scene.input.KeyCombination; 32 | 33 | public class TraceContextMenu extends ContextMenu { 34 | 35 | private final DbgController dbgController; 36 | 37 | private ObservableList items, selectedItems; 38 | 39 | public TraceContextMenu(DbgController aDbgContoller) { 40 | dbgController = aDbgContoller; 41 | getItems().add(menuItem("Copy All", "shortcut+c", this::onCopy)); 42 | getItems().add(menuItem("Copy Function Call", null, this::onCopyCalls)); 43 | getItems().add(new SeparatorMenuItem()); 44 | getItems().add(menuItem("Delete", "delete", this::onDelete)); 45 | getItems().add(menuItem("Delete All", "shortcut+n", this::onDeleteAll)); 46 | getItems().add(menuItem("Add Breaker", "shortcut+b", this::onAddBreaker)); 47 | getItems().add(menuItem("Toggle Trace", null, this::onTraceToggle)); 48 | } 49 | 50 | private MenuItem menuItem(String text, String accelerator, EventHandler e) { 51 | MenuItem menuItem; 52 | 53 | menuItem = new MenuItem(text); 54 | menuItem.setOnAction(e); 55 | 56 | if(accelerator != null) 57 | menuItem.setAccelerator(KeyCombination.keyCombination(accelerator)); 58 | 59 | return menuItem; 60 | } 61 | 62 | private void onCopy(ActionEvent e) { 63 | StringBuilder sbuilder = new StringBuilder(); 64 | 65 | for (TraceLog traceLog : selectedItems) { 66 | sbuilder.append(traceLog.toString()).append("\n"); 67 | } 68 | 69 | copyToClipboard(sbuilder); 70 | } 71 | 72 | private void onCopyCalls(ActionEvent e) { 73 | StringBuilder sbuilder = new StringBuilder(); 74 | 75 | for (TraceLog traceLog : selectedItems) { 76 | sbuilder.append(traceLog.toCallString()).append("\n"); 77 | } 78 | 79 | copyToClipboard(sbuilder); 80 | } 81 | 82 | private void copyToClipboard(StringBuilder sbuilder) { 83 | final Clipboard clipboard = Clipboard.getSystemClipboard(); 84 | final ClipboardContent content = new ClipboardContent(); 85 | 86 | content.putString(sbuilder.toString()); 87 | clipboard.setContent(content); 88 | } 89 | 90 | private void onTraceToggle(ActionEvent e) { 91 | for (TraceLog log : selectedItems) { 92 | dbgController.toggleTraceModFunc(log.getModFunc()); 93 | } 94 | } 95 | 96 | private void onDelete(ActionEvent e) { 97 | ArrayList arrayList = new ArrayList(selectedItems); 98 | items.removeAll(arrayList); 99 | } 100 | 101 | private void onDeleteAll(ActionEvent e) { 102 | items.clear(); 103 | } 104 | 105 | public void setSelectedItems(ObservableList selectedItems2) { 106 | selectedItems = selectedItems2; 107 | } 108 | 109 | public void setItems(ObservableList items2) { 110 | items = items2; 111 | } 112 | 113 | private void onAddBreaker(ActionEvent e) { 114 | items.add(TraceLog.newBreakLog()); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/format/ElixirFormatter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly.format; 19 | 20 | import java.nio.charset.StandardCharsets; 21 | import java.util.ArrayList; 22 | import java.util.Iterator; 23 | import java.util.Map; 24 | 25 | import com.ericsson.otp.erlang.OtpErlangAtom; 26 | import com.ericsson.otp.erlang.OtpErlangBinary; 27 | import com.ericsson.otp.erlang.OtpErlangBitstr; 28 | import com.ericsson.otp.erlang.OtpErlangList; 29 | import com.ericsson.otp.erlang.OtpErlangMap; 30 | import com.ericsson.otp.erlang.OtpErlangObject; 31 | import com.ericsson.otp.erlang.OtpErlangPid; 32 | import com.ericsson.otp.erlang.OtpErlangString; 33 | import com.ericsson.otp.erlang.OtpErlangTuple; 34 | 35 | import erlyberly.node.OtpUtil; 36 | 37 | public class ElixirFormatter implements TermFormatter { 38 | 39 | private static final String STRUCT_KEY = "__struct__"; 40 | 41 | private String stripElixirPrefix(String str) { 42 | if (str.startsWith("\'Elixir.")) { 43 | return stripQutes(str).substring(7); 44 | } 45 | if (str.startsWith("Elixir.")) { 46 | return str.substring(7); 47 | } 48 | return str; 49 | } 50 | 51 | private String stripQutes(String str) { 52 | return str.substring(1, str.length() - 1); 53 | } 54 | 55 | @Override 56 | public StringBuilder appendToString(OtpErlangObject obj, StringBuilder sb) { 57 | if(obj instanceof OtpErlangBinary) { 58 | OtpErlangBinary bin = (OtpErlangBinary) obj; 59 | if (Formatting.isDisplayableString(bin)) { 60 | sb.append("\""); 61 | sb.append(new String(bin.binaryValue(), StandardCharsets.UTF_8)); 62 | sb.append("\""); 63 | } else { 64 | sb.append("<<"); 65 | Formatting.binaryToString(bin, ", ", sb); 66 | sb.append(">>"); 67 | } 68 | } 69 | else if(obj instanceof OtpErlangBitstr) { 70 | sb.append("<<"); 71 | Formatting.bitstringToString((OtpErlangBitstr) obj, ", ", "%d::size(%d)", sb); 72 | sb.append(">>"); 73 | } 74 | else if(obj instanceof OtpErlangPid) { 75 | sb.append(pidToString((OtpErlangPid) obj)); 76 | } 77 | else if(OtpUtil.isErlyberlyRecord(obj)) { 78 | OtpErlangTuple record = (OtpErlangTuple) obj; 79 | OtpErlangAtom recordName = (OtpErlangAtom) record.elementAt(1); 80 | OtpErlangList fields = (OtpErlangList) record.elementAt(2); 81 | sb.append("{").append(recordName).append(", "); 82 | for(int i=0; i < fields.arity(); i++) { 83 | if(i != 0) { 84 | sb.append(", "); 85 | } 86 | appendToString(fields.elementAt(i), sb); 87 | } 88 | sb.append("}"); 89 | } 90 | else if(OtpUtil.isErlyberlyRecordField(obj)) { 91 | OtpErlangObject fieldObj = ((OtpErlangTuple)obj).elementAt(2); 92 | appendToString(fieldObj, sb); 93 | } 94 | else if(obj instanceof OtpErlangTuple || obj instanceof OtpErlangList) { 95 | String brackets = bracketsForTerm(obj); 96 | OtpErlangObject[] elements = OtpUtil.elementsForTerm(obj); 97 | 98 | sb.append(brackets.charAt(0)); 99 | 100 | for(int i=0; i < elements.length; i++) { 101 | if(i != 0) { 102 | sb.append(", "); 103 | } 104 | appendToString(elements[i], sb); 105 | } 106 | 107 | if(obj instanceof OtpErlangList && !((OtpErlangList)obj).isProper()) { 108 | sb.append(cons()); 109 | appendToString(((OtpErlangList)obj).getLastTail(), sb); 110 | } 111 | 112 | sb.append(brackets.charAt(1)); 113 | } 114 | else if(obj instanceof OtpErlangString) { 115 | Formatting.appendString((OtpErlangString) obj, this, "\'", sb); 116 | } 117 | else if(obj instanceof OtpErlangAtom){ 118 | String str = obj.toString(); 119 | if (str.startsWith("\'") && str.endsWith("\'")) { 120 | String innerStr = str.substring(1, str.length() - 1); 121 | if(innerStr.startsWith("Elixir.")) { 122 | // Treat modules differently 123 | str = innerStr.substring(7); 124 | } 125 | else { 126 | // Convert Erlang style escaped atoms to Elixir style 127 | str = ":\"" + innerStr +"\""; 128 | } 129 | 130 | } 131 | else { 132 | str = ":" + str; 133 | } 134 | sb.append(str); 135 | } 136 | else if(obj instanceof OtpErlangMap) { 137 | OtpErlangMap map = (OtpErlangMap) obj; 138 | sb.append(mapLeft(obj)); 139 | Iterator> elemIt = map.entrySet().iterator(); 140 | while(elemIt.hasNext()) { 141 | Map.Entry elem = elemIt.next(); 142 | if (elem.getKey() instanceof OtpErlangAtom) { 143 | if (isHiddenField(elem.getKey())) { 144 | continue; 145 | } 146 | sb.append(elem.getKey().toString()); 147 | sb.append(": "); 148 | } 149 | else { 150 | appendToString(elem.getKey(), sb); 151 | sb.append(" => "); 152 | } 153 | appendToString(elem.getValue(), sb); 154 | if (elemIt.hasNext()) { 155 | sb.append(", "); 156 | } 157 | } 158 | sb.append("}"); 159 | } 160 | else { 161 | sb.append(obj.toString()); 162 | } 163 | return sb; 164 | } 165 | 166 | @Override 167 | public String mapKeyToString(OtpErlangObject obj) { 168 | if (obj instanceof OtpErlangAtom) { 169 | return obj.toString() + ": "; 170 | } 171 | else { 172 | return appendToString(obj, new StringBuilder()).toString() + " => "; 173 | } 174 | } 175 | 176 | private static String pidToString(OtpErlangPid pid) { 177 | return pid.toString(); 178 | } 179 | 180 | public String bracketsForTerm(OtpErlangObject obj) { 181 | assert obj != null; 182 | 183 | if(obj instanceof OtpErlangTuple) 184 | return "{}"; 185 | else if(obj instanceof OtpErlangList) 186 | return "[]"; 187 | else 188 | throw new RuntimeException("No brackets for type " + obj.getClass()); 189 | } 190 | 191 | /** 192 | * Convert an MFA tuple to a string, where the MFA must have the type: 193 | * 194 | * {Module::atom(), Function::atom(), Args::[any()]}. 195 | */ 196 | @Override 197 | public String modFuncArgsToString(OtpErlangTuple mfa) { 198 | StringBuilder sb = new StringBuilder(); 199 | sb.append(moduleNameToString((OtpErlangAtom) mfa.elementAt(0))) 200 | .append(".") 201 | .append(funToStringNoQuotes((OtpErlangAtom) mfa.elementAt(1))) 202 | .append("("); 203 | OtpErlangList args = (OtpErlangList) mfa.elementAt(2); 204 | ArrayList stringArgs = new ArrayList<>(); 205 | for (OtpErlangObject arg : args) { 206 | stringArgs.add(toString(arg)); 207 | } 208 | sb.append(String.join(", ", stringArgs)); 209 | sb.append(")"); 210 | return sb.toString(); 211 | } 212 | 213 | private String moduleNameToString(OtpErlangAtom mod) { 214 | String str = mod.atomValue(); 215 | return moduleNameToString(str); 216 | } 217 | 218 | @Override 219 | public String moduleNameToString(String moduleName) { 220 | if(moduleName.startsWith("Elixir.")) 221 | return moduleName.substring(7); 222 | else 223 | return ":" + moduleName; 224 | } 225 | 226 | private String funToStringNoQuotes(OtpErlangAtom atom) { 227 | return atom.atomValue(); 228 | } 229 | 230 | @Override 231 | public String modFuncArityToString(OtpErlangTuple mfa) { 232 | StringBuilder sb = new StringBuilder(); 233 | OtpErlangList argsList = OtpUtil.toErlangList(mfa.elementAt(2)); 234 | sb.append(moduleNameToString((OtpErlangAtom) mfa.elementAt(0))) 235 | .append(".") 236 | .append(funToStringNoQuotes((OtpErlangAtom) mfa.elementAt(1))) 237 | .append("/").append(argsList.arity()); 238 | return sb.toString(); 239 | } 240 | 241 | @Override 242 | public String modFuncArityToString(String mod, String func, int arity) { 243 | return moduleNameToString(mod) + "." + func + "/" + arity; 244 | } 245 | 246 | @Override 247 | public String exceptionToString(OtpErlangAtom errorClass, OtpErlangObject errorReason) { 248 | return errorClass + ":" + toString(errorReason); 249 | } 250 | 251 | @Override 252 | public String emptyTupleString() { 253 | return "{ }"; 254 | } 255 | 256 | @Override 257 | public String tupleLeftParen() { 258 | return "{"; 259 | } 260 | 261 | @Override 262 | public String tupleRightParen() { 263 | return "}"; 264 | } 265 | 266 | @Override 267 | public String emptyListString() { 268 | return "[ ]"; 269 | } 270 | 271 | @Override 272 | public String listLeftParen() { 273 | return "["; 274 | } 275 | 276 | @Override 277 | public String listRightParen() { 278 | return "]"; 279 | } 280 | 281 | @Override 282 | public String mapLeft(OtpErlangObject obj) 283 | { 284 | OtpErlangMap map = (OtpErlangMap) obj; 285 | OtpErlangAtom structNameKey = new OtpErlangAtom(STRUCT_KEY); 286 | if (map.get(structNameKey) != null) { 287 | String structName = stripElixirPrefix(map.get(structNameKey).toString()); 288 | return "%" + structName + "{"; 289 | } 290 | return "%{"; 291 | } 292 | 293 | @Override 294 | public Boolean isHiddenField(OtpErlangObject key) { 295 | OtpErlangAtom structNameKey = new OtpErlangAtom(STRUCT_KEY); 296 | return (key instanceof OtpErlangAtom) && key.equals(structNameKey); 297 | } 298 | 299 | @Override 300 | public String mapRight() { 301 | return "}"; 302 | } 303 | 304 | @Override 305 | public String cons() { 306 | return "|"; 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/format/ErlangFormatter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly.format; 19 | 20 | import java.util.ArrayList; 21 | 22 | import com.ericsson.otp.erlang.OtpErlangAtom; 23 | import com.ericsson.otp.erlang.OtpErlangBinary; 24 | import com.ericsson.otp.erlang.OtpErlangBitstr; 25 | import com.ericsson.otp.erlang.OtpErlangList; 26 | import com.ericsson.otp.erlang.OtpErlangObject; 27 | import com.ericsson.otp.erlang.OtpErlangPid; 28 | import com.ericsson.otp.erlang.OtpErlangString; 29 | import com.ericsson.otp.erlang.OtpErlangTuple; 30 | 31 | import erlyberly.node.OtpUtil; 32 | 33 | public class ErlangFormatter implements TermFormatter { 34 | 35 | @Override 36 | public StringBuilder appendToString(OtpErlangObject obj, StringBuilder sb) { 37 | if(obj instanceof OtpErlangBinary) { 38 | sb.append("<<"); 39 | Formatting.binaryToString((OtpErlangBinary) obj, ", ", sb); 40 | sb.append(">>"); 41 | } 42 | else if(obj instanceof OtpErlangBitstr) { 43 | sb.append("<<"); 44 | Formatting.bitstringToString((OtpErlangBitstr) obj, ", ", "%d:%d", sb); 45 | sb.append(">>"); 46 | } 47 | else if(obj instanceof OtpErlangPid) { 48 | sb.append(pidToString((OtpErlangPid) obj)); 49 | } 50 | else if(OtpUtil.isErlyberlyRecord(obj)) { 51 | OtpErlangTuple record = (OtpErlangTuple) obj; 52 | OtpErlangAtom recordName = (OtpErlangAtom) record.elementAt(1); 53 | OtpErlangList fields = (OtpErlangList) record.elementAt(2); 54 | sb.append("{").append(recordName).append(", "); 55 | for(int i=0; i < fields.arity(); i++) { 56 | if(i != 0) { 57 | sb.append(", "); 58 | } 59 | appendToString(fields.elementAt(i), sb); 60 | } 61 | sb.append("}"); 62 | } 63 | else if(OtpUtil.isErlyberlyRecordField(obj)) { 64 | OtpErlangObject fieldObj = ((OtpErlangTuple)obj).elementAt(2); 65 | appendToString(fieldObj, sb); 66 | } 67 | else if(obj instanceof OtpErlangTuple || obj instanceof OtpErlangList) { 68 | String brackets = bracketsForTerm(obj); 69 | OtpErlangObject[] elements = OtpUtil.elementsForTerm(obj); 70 | 71 | sb.append(brackets.charAt(0)); 72 | 73 | for(int i=0; i < elements.length; i++) { 74 | if(i != 0) { 75 | sb.append(", "); 76 | } 77 | appendToString(elements[i], sb); 78 | } 79 | 80 | if(obj instanceof OtpErlangList && !((OtpErlangList)obj).isProper()) { 81 | sb.append(cons()); 82 | appendToString(((OtpErlangList)obj).getLastTail(), sb); 83 | } 84 | 85 | sb.append(brackets.charAt(1)); 86 | } 87 | else if(obj instanceof OtpErlangString) { 88 | Formatting.appendString((OtpErlangString) obj, this, "\"", sb); 89 | } 90 | else { 91 | sb.append(obj.toString()); 92 | } 93 | return sb; 94 | } 95 | 96 | private static String pidToString(OtpErlangPid pid) { 97 | return pid.toString(); 98 | } 99 | 100 | public String bracketsForTerm(OtpErlangObject obj) { 101 | assert obj != null; 102 | 103 | if(obj instanceof OtpErlangTuple) 104 | return "{}"; 105 | else if(obj instanceof OtpErlangList) 106 | return "[]"; 107 | else 108 | throw new RuntimeException("No brackets for type " + obj.getClass()); 109 | } 110 | 111 | /** 112 | * Convert an MFA tuple to a string, where the MFA must have the type: 113 | * 114 | * {Module::atom(), Function::atom(), Args::[any()]}. 115 | */ 116 | @Override 117 | public String modFuncArgsToString(OtpErlangTuple mfa) { 118 | StringBuilder sb = new StringBuilder(); 119 | sb.append(atomToStringNoQuotes((OtpErlangAtom) mfa.elementAt(0))) 120 | .append(":") 121 | .append(atomToStringNoQuotes((OtpErlangAtom) mfa.elementAt(1))) 122 | .append("("); 123 | OtpErlangList args = (OtpErlangList) mfa.elementAt(2); 124 | ArrayList stringArgs = new ArrayList<>(); 125 | for (OtpErlangObject arg : args) { 126 | stringArgs.add(toString(arg)); 127 | } 128 | sb.append(String.join(", ", stringArgs)); 129 | sb.append(")"); 130 | return sb.toString(); 131 | } 132 | 133 | private String atomToStringNoQuotes(OtpErlangAtom atom) { 134 | return atom.atomValue(); 135 | } 136 | 137 | @Override 138 | public String modFuncArityToString(OtpErlangTuple mfa) { 139 | StringBuilder sb = new StringBuilder(); 140 | OtpErlangList argsList = OtpUtil.toErlangList(mfa.elementAt(2)); 141 | sb.append(atomToStringNoQuotes((OtpErlangAtom) mfa.elementAt(0))) 142 | .append(":") 143 | .append(atomToStringNoQuotes((OtpErlangAtom) mfa.elementAt(1))) 144 | .append("/").append(argsList.arity()); 145 | return sb.toString(); 146 | } 147 | 148 | @Override 149 | public String exceptionToString(OtpErlangAtom errorClass, OtpErlangObject errorReason) { 150 | return errorClass + ":" + toString(errorReason); 151 | } 152 | 153 | @Override 154 | public String emptyTupleString() { 155 | return "{ }"; 156 | } 157 | 158 | @Override 159 | public String tupleLeftParen() { 160 | return "{"; 161 | } 162 | 163 | @Override 164 | public String tupleRightParen() { 165 | return "}"; 166 | } 167 | 168 | @Override 169 | public String emptyListString() { 170 | return "[ ]"; 171 | } 172 | 173 | @Override 174 | public String listLeftParen() { 175 | return "["; 176 | } 177 | 178 | @Override 179 | public String listRightParen() { 180 | return "]"; 181 | } 182 | 183 | @Override 184 | public String mapLeft(OtpErlangObject obj) { 185 | return "#{"; 186 | } 187 | 188 | @Override 189 | public Boolean isHiddenField(OtpErlangObject key) {return false;} 190 | 191 | @Override 192 | public String mapRight() { 193 | return "}"; 194 | } 195 | @Override 196 | public String cons() { 197 | return "|"; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/format/Formatting.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly.format; 19 | 20 | import com.ericsson.otp.erlang.OtpErlangBinary; 21 | import com.ericsson.otp.erlang.OtpErlangBitstr; 22 | import com.ericsson.otp.erlang.OtpErlangString; 23 | 24 | /** 25 | * Utility methods for formatting. 26 | */ 27 | class Formatting { 28 | 29 | private Formatting() {} 30 | 31 | public static void appendString(OtpErlangString aString, TermFormatter formatter, String quote, StringBuilder sb) { 32 | String stringValue = aString.stringValue(); 33 | // sometimes a list of integers can be mis-typed by jinterface as a string 34 | // in this case we format it ourselves as a list of ints 35 | boolean isString = isString(stringValue); 36 | if(isString) { 37 | sb.append(quote).append(stringValue.replace("\n", "\\n")).append(quote); 38 | } 39 | else { 40 | appendListOfInts(stringValue, formatter, sb); 41 | } 42 | } 43 | 44 | private static void appendListOfInts(String stringValue, TermFormatter formatter, StringBuilder sb) { 45 | sb.append(formatter.listLeftParen()); 46 | for (int i = 0; i < stringValue.length(); i++) { 47 | int c = stringValue.charAt(i); 48 | sb.append(c); 49 | if(i < (stringValue.length() - 1)) { 50 | sb.append(","); 51 | } 52 | } 53 | sb.append(formatter.listRightParen()); 54 | } 55 | 56 | private static boolean isString(String stringValue) { 57 | int length = stringValue.length(); 58 | for (int i = 0; i < length; i++) { 59 | char c = stringValue.charAt(i); 60 | if(c < 10 || c > 127) { 61 | return false; 62 | } 63 | } 64 | return true; 65 | } 66 | 67 | /** 68 | * Append the binary term bytes to a string builder. It attempts to display character data 69 | * as strings. 70 | */ 71 | public static void binaryToString(OtpErlangBinary bin, String sep, StringBuilder sb) { 72 | int length = sb.length(); 73 | boolean inString = false; 74 | 75 | for (int b : bin.binaryValue()) { 76 | if(isDisplayableChar(b)) { 77 | if(!inString) { 78 | if((sb.length() - length) > 0) { 79 | sb.append(sep); 80 | } 81 | 82 | sb.append("\""); 83 | } 84 | inString = true; 85 | sb.append((char)b); 86 | } 87 | else { 88 | if(inString) { 89 | sb.append("\""); 90 | inString = false; 91 | } 92 | 93 | if((sb.length() - length) > 0) { 94 | sb.append(sep); 95 | } 96 | 97 | if(b < 0) { 98 | b = 256 + b; 99 | } 100 | sb.append(Integer.toString(b)); 101 | } 102 | } 103 | 104 | if(inString) { 105 | sb.append("\""); 106 | } 107 | } 108 | 109 | public static void bitstringToString(OtpErlangBitstr bits, String sep, 110 | String bitsFormat, StringBuilder sb) { 111 | byte[] binValue = bits.binaryValue(); 112 | for (int i=0; i < bits.size(); i++) { 113 | int b = binValue[i]>=0 ? binValue[i] : binValue[i]+256; 114 | sb.append(String.format("%d%s", b, sep)); 115 | } 116 | int b = binValue[bits.size()]; 117 | b = (b>=0 ? b : b+256) >> bits.pad_bits(); 118 | sb.append(String.format(bitsFormat, b, 8-bits.pad_bits())); 119 | } 120 | 121 | private static boolean isDisplayableChar(int b) { 122 | return b > 31 && b < 127; 123 | } 124 | 125 | /** 126 | * Make a guess if the given binary is a UTF-8 string or not. 127 | */ 128 | public static boolean isDisplayableString(OtpErlangBinary bin) { 129 | int expected; 130 | byte[] bytes = bin.binaryValue(); 131 | for (int i = 0; i < bytes.length; i++) { 132 | int ch = bytes[i]; 133 | if (isDisplayableChar(ch)) continue; 134 | else if ((ch & 0b11100000) == 0b11000000) expected = 1; 135 | else if ((ch & 0b11110000) == 0b11100000) expected = 2; 136 | else if ((ch & 0b11111000) == 0b11100000) expected = 3; 137 | else return false; 138 | 139 | while (expected > 0) { 140 | if (i + expected >= bytes.length) return false; 141 | if ((bytes[i + 1] & 0b11000000) != 0b10000000) return false; 142 | expected--; 143 | i++; 144 | } 145 | } 146 | return true; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/format/LFEFormatter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly.format; 19 | 20 | import java.util.ArrayList; 21 | 22 | import com.ericsson.otp.erlang.OtpErlangAtom; 23 | import com.ericsson.otp.erlang.OtpErlangBinary; 24 | import com.ericsson.otp.erlang.OtpErlangBitstr; 25 | import com.ericsson.otp.erlang.OtpErlangList; 26 | import com.ericsson.otp.erlang.OtpErlangObject; 27 | import com.ericsson.otp.erlang.OtpErlangString; 28 | import com.ericsson.otp.erlang.OtpErlangTuple; 29 | 30 | import erlyberly.node.OtpUtil; 31 | 32 | public class LFEFormatter implements TermFormatter { 33 | 34 | /** 35 | * Convert an MFA tuple to a string, where the MFA must have the type: 36 | * 37 | * {Module::atom(), Function::atom(), Args::[any()]}. 38 | */ 39 | @Override 40 | public String modFuncArgsToString(OtpErlangTuple mfa) { 41 | StringBuilder sb = new StringBuilder(); 42 | sb.append("(") 43 | .append(mfa.elementAt(0)) 44 | .append(":") 45 | .append(mfa.elementAt(1)); 46 | OtpErlangList args = (OtpErlangList) mfa.elementAt(2); 47 | ArrayList stringArgs = new ArrayList<>(); 48 | for (OtpErlangObject arg : args) { 49 | stringArgs.add(toString(arg)); 50 | } 51 | sb.append(String.join(", ", stringArgs)) 52 | .append(")"); 53 | return sb.toString(); 54 | } 55 | 56 | @Override 57 | public String modFuncArityToString(OtpErlangTuple mfa) { 58 | StringBuilder sb = new StringBuilder(); 59 | OtpErlangList argsList = (OtpErlangList) mfa.elementAt(2); 60 | sb.append("(") 61 | .append(mfa.elementAt(0)) 62 | .append(":") 63 | .append(mfa.elementAt(1)) 64 | .append("/").append(argsList.arity()) 65 | .append(")"); 66 | return sb.toString(); 67 | } 68 | 69 | @Override 70 | public String exceptionToString(OtpErlangAtom errorClass, OtpErlangObject errorReason) { 71 | return toString(errorClass) + ": " + toString(errorReason); 72 | } 73 | 74 | @Override 75 | public StringBuilder appendToString(OtpErlangObject obj, StringBuilder sb) { 76 | if(obj instanceof OtpErlangAtom) { 77 | sb.append("'").append(obj.toString()); 78 | } 79 | else if(obj instanceof OtpErlangBinary) { 80 | 81 | sb.append("#B("); 82 | Formatting.binaryToString((OtpErlangBinary) obj, " ", sb); 83 | sb.append(")"); 84 | } 85 | else if(obj instanceof OtpErlangBitstr) { 86 | sb.append("#B("); 87 | Formatting.bitstringToString((OtpErlangBitstr) obj, " ", "(%d (size %d))", sb); 88 | sb.append(")"); 89 | } 90 | else if(OtpUtil.isErlyberlyRecordField(obj)) { 91 | OtpErlangObject fieldObj = ((OtpErlangTuple)obj).elementAt(2); 92 | appendToString(fieldObj, sb); 93 | } 94 | else if(obj instanceof OtpErlangTuple) { 95 | sb.append("#("); 96 | elementsToString(sb, ((OtpErlangTuple) obj).elements()); 97 | sb.append(")"); 98 | } 99 | else if(obj instanceof OtpErlangList) { 100 | sb.append("("); 101 | elementsToString(sb, ((OtpErlangList) obj).elements()); 102 | if(!((OtpErlangList) obj).isProper()) { 103 | sb.append(cons()); 104 | appendToString(((OtpErlangList) obj).getLastTail(), sb); 105 | } 106 | sb.append(")"); 107 | } 108 | else if(obj instanceof OtpErlangString) { 109 | Formatting.appendString((OtpErlangString) obj, this, "\"", sb); 110 | } 111 | else { 112 | sb.append(obj.toString()); 113 | } 114 | return sb; 115 | } 116 | 117 | private void elementsToString(StringBuilder sb, OtpErlangObject[] elements) { 118 | for(int i=0; i < elements.length; i++) { 119 | if(i != 0) { 120 | sb.append(", "); 121 | } 122 | appendToString(elements[i], sb); 123 | } 124 | } 125 | 126 | @Override 127 | public String emptyTupleString() { 128 | return "#( )"; 129 | } 130 | 131 | @Override 132 | public String tupleLeftParen() { 133 | return "#("; 134 | } 135 | 136 | @Override 137 | public String tupleRightParen() { 138 | return ")"; 139 | } 140 | 141 | @Override 142 | public String emptyListString() { 143 | return "( )"; 144 | } 145 | 146 | @Override 147 | public String listLeftParen() { 148 | return "("; 149 | } 150 | 151 | @Override 152 | public String listRightParen() { 153 | return ")"; 154 | } 155 | 156 | @Override 157 | public String mapLeft(OtpErlangObject obj) { 158 | return "#M("; 159 | } 160 | 161 | @Override 162 | public String mapRight() { 163 | return ")"; 164 | } 165 | 166 | @Override 167 | public Boolean isHiddenField(OtpErlangObject key) {return false;} 168 | 169 | @Override 170 | public String cons() { 171 | return "."; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/format/TermFormatter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly.format; 19 | 20 | import com.ericsson.otp.erlang.OtpErlangAtom; 21 | import com.ericsson.otp.erlang.OtpErlangObject; 22 | import com.ericsson.otp.erlang.OtpErlangTuple; 23 | 24 | /** 25 | * Formatter API for erlang terms, functions, exceptions, and everything 26 | * else which differs per language. 27 | */ 28 | public interface TermFormatter { 29 | 30 | String modFuncArgsToString(OtpErlangTuple mfa); 31 | 32 | String modFuncArityToString(OtpErlangTuple mfa); 33 | 34 | default String modFuncArityToString(String mod, String func, int arity) { return mod + ":" + func + "/" + arity; } 35 | 36 | default String moduleNameToString(String modName) { return modName; } 37 | 38 | String exceptionToString(OtpErlangAtom errorClass, OtpErlangObject errorReason); 39 | 40 | StringBuilder appendToString(OtpErlangObject obj, StringBuilder sb); 41 | 42 | default String toString(OtpErlangObject obj) { 43 | return appendToString(obj, new StringBuilder()).toString(); 44 | } 45 | 46 | default String mapKeyToString(OtpErlangObject obj) { 47 | return toString(obj) + " => "; 48 | } 49 | 50 | String emptyTupleString(); 51 | 52 | String tupleLeftParen(); 53 | 54 | String tupleRightParen(); 55 | 56 | String emptyListString(); 57 | 58 | String listLeftParen(); 59 | 60 | String listRightParen(); 61 | 62 | String mapLeft(OtpErlangObject obj); 63 | 64 | String mapRight(); 65 | 66 | // True if given key in map should be hidden (Elixir __struct__) 67 | Boolean isHiddenField(OtpErlangObject key); 68 | 69 | String cons(); 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/node/AppProcs.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly.node; 19 | 20 | import java.time.LocalDateTime; 21 | 22 | public class AppProcs { 23 | private final int procCount; 24 | 25 | private final LocalDateTime dateTime; 26 | 27 | public AppProcs(int theProcCount, LocalDateTime theDateTime) { 28 | procCount = theProcCount; 29 | dateTime = theDateTime; 30 | } 31 | 32 | public int getProcCount() { 33 | return procCount; 34 | } 35 | 36 | public LocalDateTime getDateTime() { 37 | return dateTime; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/node/NodeRPC.java: -------------------------------------------------------------------------------- 1 | package erlyberly.node; 2 | 3 | import static erlyberly.node.OtpUtil.atom; 4 | import static erlyberly.node.OtpUtil.tuple; 5 | 6 | import java.io.IOException; 7 | 8 | import com.ericsson.otp.erlang.OtpConn; 9 | import com.ericsson.otp.erlang.OtpErlangAtom; 10 | import com.ericsson.otp.erlang.OtpErlangDecodeException; 11 | import com.ericsson.otp.erlang.OtpErlangException; 12 | import com.ericsson.otp.erlang.OtpErlangExit; 13 | import com.ericsson.otp.erlang.OtpErlangList; 14 | import com.ericsson.otp.erlang.OtpErlangObject; 15 | import com.ericsson.otp.erlang.OtpErlangTuple; 16 | import com.ericsson.otp.erlang.OtpMbox; 17 | import com.ericsson.otp.erlang.OtpSelfNode; 18 | 19 | public class NodeRPC { 20 | 21 | private static final OtpErlangAtom CALL_ATOM = atom("call"); 22 | 23 | private static final OtpErlangAtom USER_ATOM = atom("user"); 24 | 25 | private final OtpConn connection; 26 | 27 | private final OtpSelfNode self; 28 | 29 | private final int timeout; 30 | 31 | public NodeRPC(OtpSelfNode self, OtpConn connection) { 32 | this.connection = connection; 33 | this.self = self; 34 | this. timeout = 5000; 35 | } 36 | 37 | public NodeRPC(OtpSelfNode self, OtpConn connection, int timeoutMillis) { 38 | this.connection = connection; 39 | this.self = self; 40 | this.timeout = timeoutMillis; 41 | } 42 | 43 | public OtpErlangObject blockingRPC(OtpErlangAtom mod, OtpErlangAtom fun, OtpErlangList args) throws IOException, OtpErlangException { 44 | OtpMbox mbox = self.createMbox(); 45 | try { 46 | sendRPC(mbox, mod, fun, args); 47 | return receiveResult(mbox); 48 | } 49 | finally { 50 | mbox.close(); 51 | } 52 | } 53 | 54 | private OtpErlangObject receiveResult(OtpMbox mbox) throws OtpErlangExit, OtpErlangDecodeException { 55 | OtpErlangObject message = mbox.receive(timeout); 56 | if(message == null) 57 | return null; 58 | if(!(message instanceof OtpErlangTuple)) 59 | throw new RuntimeException("RPC response expected tuple but got " + message); 60 | OtpErlangTuple tupleMessage = (OtpErlangTuple) message; 61 | OtpErlangObject result = tupleMessage.elementAt(1); 62 | return result; 63 | } 64 | 65 | private void sendRPC(OtpMbox m, OtpErlangAtom mod, OtpErlangAtom fun, OtpErlangList args) throws IOException { 66 | OtpErlangTuple rpcMessage = tuple(m.self(), tuple(CALL_ATOM, mod, fun, args, USER_ATOM)); 67 | 68 | connection.send(m.self(), "rex", rpcMessage); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/node/RecordManager.java: -------------------------------------------------------------------------------- 1 | package erlyberly.node; 2 | 3 | import java.util.List; 4 | import java.util.Map.Entry; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | import com.ericsson.otp.erlang.OtpErlangAtom; 8 | 9 | public class RecordManager { 10 | private final ConcurrentHashMap> records = new ConcurrentHashMap<>(); 11 | 12 | public List get(Object key) { 13 | return records.get(key); 14 | } 15 | 16 | public List put(RecordKey key, List value) { 17 | return records.put(key, value); 18 | } 19 | 20 | public static class RecordKey { 21 | private final OtpErlangAtom module, recordName; 22 | 23 | public RecordKey(OtpErlangAtom module, OtpErlangAtom recordName) { 24 | this.module = module; 25 | this.recordName = recordName; 26 | } 27 | 28 | @Override 29 | public int hashCode() { 30 | final int prime = 31; 31 | int result = 1; 32 | result = prime * result + ((module == null) ? 0 : module.hashCode()); 33 | result = prime * result + ((recordName == null) ? 0 : recordName.hashCode()); 34 | return result; 35 | } 36 | 37 | @Override 38 | public boolean equals(Object obj) { 39 | if (this == obj) 40 | return true; 41 | if (obj == null) 42 | return false; 43 | if (getClass() != obj.getClass()) 44 | return false; 45 | RecordKey other = (RecordKey) obj; 46 | if (module == null) { 47 | if (other.module != null) 48 | return false; 49 | } else if (!module.equals(other.module)) 50 | return false; 51 | if (recordName == null) { 52 | if (other.recordName != null) 53 | return false; 54 | } else if (!recordName.equals(other.recordName)) 55 | return false; 56 | return true; 57 | } 58 | } 59 | 60 | public boolean isModuleManaged(OtpErlangAtom moduleName) { 61 | for (Entry> entry : records.entrySet()) { 62 | if(moduleName.equals(entry.getKey().module)) 63 | return true; 64 | } 65 | return false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/node/RpcCallback.java: -------------------------------------------------------------------------------- 1 | package erlyberly.node; 2 | 3 | public interface RpcCallback { 4 | void callback(T result); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/erlyberly/node/TraceManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly.node; 19 | 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Stack; 25 | 26 | import com.ericsson.otp.erlang.OtpErlangAtom; 27 | import com.ericsson.otp.erlang.OtpErlangList; 28 | import com.ericsson.otp.erlang.OtpErlangObject; 29 | import com.ericsson.otp.erlang.OtpErlangString; 30 | import com.ericsson.otp.erlang.OtpErlangTuple; 31 | 32 | import erlyberly.TraceLog; 33 | 34 | public class TraceManager { 35 | 36 | private static final OtpErlangAtom RETURN_FROM_ATOM = new OtpErlangAtom("return_from"); 37 | 38 | private static final OtpErlangAtom EXCEPTION_FROM_ATOM = new OtpErlangAtom("exception_from"); 39 | 40 | private static final OtpErlangAtom CALL_ATOM = new OtpErlangAtom("call"); 41 | 42 | private final HashMap> unfinishedCalls = new HashMap>(); 43 | 44 | 45 | public List collateTraces(OtpErlangList traceLogs) { 46 | final ArrayList traceList = new ArrayList(); 47 | 48 | for (OtpErlangObject obj : traceLogs) { 49 | decodeTraceLog(obj, traceList); 50 | } 51 | 52 | return traceList; 53 | } 54 | 55 | public List collateTraceSingle(OtpErlangTuple traceLog) { 56 | final ArrayList traceList = new ArrayList(); 57 | decodeTraceLog(traceLog, traceList); 58 | return traceList; 59 | } 60 | 61 | private void decodeTraceLog(OtpErlangObject otpErlangObject, ArrayList traceList) { 62 | OtpErlangTuple tup = (OtpErlangTuple) otpErlangObject; 63 | OtpErlangAtom traceType = (OtpErlangAtom) tup.elementAt(0); 64 | 65 | if(CALL_ATOM.equals(traceType)) { 66 | TraceLog trace = proplistToTraceLog(tup); 67 | 68 | Stack stack = unfinishedCalls.get(trace.getPidString()); 69 | 70 | if(stack == null) 71 | stack = new Stack(); 72 | 73 | stack.add(trace); 74 | 75 | unfinishedCalls.put(trace.getPidString(), stack); 76 | 77 | traceList.add(trace); 78 | } 79 | else if(RETURN_FROM_ATOM.equals(traceType) || EXCEPTION_FROM_ATOM.equals(traceType)) { 80 | Map map = propsFromTrace(tup); 81 | 82 | Object object = map.get(TraceLog.ATOM_PID); 83 | 84 | if(object != null) { 85 | OtpErlangString pidString = (OtpErlangString) object; 86 | 87 | Stack stack = unfinishedCalls.get(pidString.stringValue()); 88 | if(stack == null) 89 | return; 90 | 91 | TraceLog traceLog = stack.pop(); 92 | traceLog.complete(map); 93 | 94 | if(stack.isEmpty()) 95 | unfinishedCalls.remove(pidString.stringValue()); 96 | } 97 | 98 | } 99 | } 100 | 101 | private TraceLog proplistToTraceLog(OtpErlangTuple tup) { 102 | Map map = propsFromTrace(tup); 103 | 104 | TraceLog trace = new TraceLog(map); 105 | 106 | return trace; 107 | } 108 | 109 | private Map propsFromTrace(OtpErlangTuple tup) { 110 | return OtpUtil.propsToMap((OtpErlangList) tup.elementAt(1)); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/hextstar/DataRow.java: -------------------------------------------------------------------------------- 1 | package hextstar; 2 | 3 | import javafx.beans.property.SimpleStringProperty; 4 | import javafx.beans.property.StringProperty; 5 | 6 | /** 7 | * Created by Bart on 9/26/2015. 8 | * Originally from https://github.com/Velocity-/Hexstar 9 | */ 10 | public class DataRow { 11 | private StringProperty address, text; 12 | private StringProperty[] data = new StringProperty[16]; 13 | 14 | public DataRow(int addr, byte[] b, int bytesRead) { 15 | address = new SimpleStringProperty(String.format("%08X", addr)); 16 | text = new SimpleStringProperty(); 17 | StringBuilder sb = new StringBuilder(bytesRead); 18 | for (int i = 0; i < b.length; i++) { 19 | String stringByteValue = ""; 20 | // display the byte as an unsigned number, like erlang binaries 21 | if(i < bytesRead) 22 | stringByteValue = Integer.toString((b[i]& 0xFF)); 23 | data[i] = new SimpleStringProperty(stringByteValue); 24 | if(b[i] == 10) 25 | sb.append("\\n"); 26 | else if(b[i] >= 32 && b[i] < 127) 27 | sb.append((char)b[i]); 28 | else 29 | sb.append("."); 30 | } 31 | text.set(sb.toString()); 32 | } 33 | 34 | public StringProperty addressProperty() { 35 | return address; 36 | } 37 | 38 | public StringProperty textProperty() { 39 | return text; 40 | } 41 | 42 | public StringProperty b0Property() { 43 | return data[0]; 44 | } 45 | 46 | public StringProperty b1Property() { 47 | return data[1]; 48 | } 49 | 50 | public StringProperty b2Property() { 51 | return data[2]; 52 | } 53 | 54 | public StringProperty b3Property() { 55 | return data[3]; 56 | } 57 | 58 | public StringProperty b4Property() { 59 | return data[4]; 60 | } 61 | 62 | public StringProperty b5Property() { 63 | return data[5]; 64 | } 65 | 66 | public StringProperty b6Property() { 67 | return data[6]; 68 | } 69 | 70 | public StringProperty b7Property() { 71 | return data[7]; 72 | } 73 | 74 | public StringProperty b8Property() { 75 | return data[8]; 76 | } 77 | 78 | public StringProperty b9Property() { 79 | return data[9]; 80 | } 81 | 82 | public StringProperty bAProperty() { 83 | return data[0xA]; 84 | } 85 | 86 | public StringProperty bBProperty() { 87 | return data[0xB]; 88 | } 89 | 90 | public StringProperty bCProperty() { 91 | return data[0xC]; 92 | } 93 | 94 | public StringProperty bDProperty() { 95 | return data[0xD]; 96 | } 97 | 98 | public StringProperty bEProperty() { 99 | return data[0xE]; 100 | } 101 | 102 | public StringProperty bFProperty() { 103 | return data[0xF]; 104 | } 105 | } 106 | 107 | -------------------------------------------------------------------------------- /src/main/java/hextstar/HexstarView.java: -------------------------------------------------------------------------------- 1 | package hextstar; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.IOException; 5 | import java.util.Arrays; 6 | 7 | import com.ericsson.otp.erlang.OtpErlangBinary; 8 | 9 | import javafx.scene.control.SelectionMode; 10 | import javafx.scene.control.TableColumn; 11 | import javafx.scene.control.TableView; 12 | import javafx.scene.control.cell.PropertyValueFactory; 13 | 14 | /** 15 | * Created by Bart on 9/26/2015. 16 | * Originally from https://github.com/Velocity-/Hexstar 17 | */ 18 | public class HexstarView extends TableView { 19 | 20 | public HexstarView() { 21 | //setContextMenu(new ContextMenu(new MenuItem("Copy"), new MenuItem("Cut"), new MenuItem("Delete"))); 22 | getSelectionModel().setCellSelectionEnabled(true); 23 | getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); 24 | 25 | column("Address", "address"); 26 | column("0", "b0"); 27 | column("1", "b1"); 28 | column("2", "b2"); 29 | column("3", "b3"); 30 | column("4", "b4"); 31 | column("5", "b5"); 32 | column("6", "b6"); 33 | column("7", "b7"); 34 | column("8", "b8"); 35 | column("9", "b9"); 36 | column("A", "bA"); 37 | column("B", "bB"); 38 | column("C", "bC"); 39 | column("D", "bD"); 40 | column("E", "bE"); 41 | column("F", "bF"); 42 | column("Text", "text"); 43 | 44 | getColumns().forEach(c -> { 45 | c.getStyleClass().add("hex-column"); 46 | c.setSortable(false); 47 | }); 48 | getColumns().get(0).getStyleClass().add("hex-address-column"); 49 | } 50 | 51 | public void setBinary(OtpErlangBinary binary) { 52 | ByteArrayInputStream inputStream = new ByteArrayInputStream(binary.binaryValue()); 53 | byte[] input = new byte[16]; 54 | int addr = 0, bytesRead = 0; 55 | try { 56 | while ((bytesRead = inputStream.read(input)) > 0) { 57 | getItems().add(new DataRow(addr, input, bytesRead)); 58 | addr += 16; 59 | Arrays.fill(input, (byte)0); 60 | } 61 | } 62 | catch (IOException e) { 63 | throw new RuntimeException(e); 64 | } 65 | } 66 | 67 | private void column(String colName, String property) { 68 | TableColumn col; 69 | col = new TableColumn(colName); 70 | col.setCellValueFactory(new PropertyValueFactory(property)); 71 | getColumns().add(col); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/ui/CellController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package ui; 19 | 20 | public interface CellController { 21 | 22 | void updateItem(T item, boolean empty); 23 | 24 | // 25 | // cell editing, this is optional for implementers 26 | // 27 | 28 | default boolean isEditable() { 29 | return false; 30 | } 31 | 32 | default void startEdit() { } 33 | 34 | default void cancelEdit() { } 35 | 36 | default void commitEdit(T newValue) { } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/ui/CloseWindowOnEscape.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package ui; 19 | 20 | import javafx.event.EventHandler; 21 | import javafx.scene.Scene; 22 | import javafx.scene.input.KeyCode; 23 | import javafx.scene.input.KeyEvent; 24 | import javafx.stage.Stage; 25 | import javafx.stage.WindowEvent; 26 | 27 | public class CloseWindowOnEscape implements EventHandler { 28 | 29 | private final Stage stage; 30 | 31 | public CloseWindowOnEscape(Stage aTermsStage) { 32 | stage = aTermsStage; 33 | } 34 | 35 | @Override 36 | public void handle(KeyEvent evt) { 37 | if (evt.getCode().equals(KeyCode.ESCAPE)) { 38 | EventHandler onCloseRequest = stage.getOnCloseRequest(); 39 | if(onCloseRequest != null) { 40 | onCloseRequest.handle(null); 41 | } 42 | stage.close(); 43 | } 44 | } 45 | 46 | public static void apply(Scene scene, Stage stage) { 47 | scene.addEventFilter(KeyEvent.KEY_PRESSED, new CloseWindowOnEscape(stage)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/ui/FAIcon.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package ui; 19 | 20 | import static de.jensd.fx.fontawesome.AwesomeDude.FONT_AWESOME_TTF_PATH; 21 | 22 | import de.jensd.fx.fontawesome.AwesomeDude; 23 | import de.jensd.fx.fontawesome.AwesomeIcon; 24 | import javafx.beans.NamedArg; 25 | import javafx.fxml.FXML; 26 | import javafx.scene.control.Label; 27 | import javafx.scene.text.Font; 28 | 29 | /** 30 | * This is mostly a copy/pasta of the Icon class from fontawesomefx but checks that style 31 | * is not null before appending it to the style string. Otherwise the css parser throws 32 | * an error because the string 'null 'is appended to the css. 33 | * 34 | * erlyberly has not upgraded to the latest fontawesomefx because the Icon class now 35 | * inherits from Text and not Label so tooltips cannot be set. 36 | */ 37 | public class FAIcon extends Label { 38 | 39 | @FXML 40 | void initialize() { 41 | } 42 | 43 | static { 44 | Font.loadFont(AwesomeDude.class.getResource(FONT_AWESOME_TTF_PATH).toExternalForm(), 10.0); 45 | } 46 | 47 | public FAIcon(AwesomeIcon icon, String size, String style, String styleClass) { 48 | setText(icon.toString()); 49 | // add least add "awesome"-class 50 | getStyleClass().add("awesome"); 51 | if (styleClass != null && !styleClass.isEmpty()) { 52 | getStyleClass().add(styleClass); 53 | } 54 | size = (size == null || size.isEmpty()) ? "2em" : size; 55 | // make sure FontAwesome is assigned with appropriate size: 56 | String stylePrefix = String.format("-fx-font-family: FontAwesome; -fx-font-size: %s;", size); 57 | if(style != null) { 58 | stylePrefix += style; 59 | } 60 | setStyle(stylePrefix); 61 | } 62 | 63 | public FAIcon(@NamedArg("awesomeIcon") String awesomeIcon, @NamedArg("size") String size, @NamedArg("style") String style, @NamedArg("styleClass") String styleClass) { 64 | this(AwesomeIcon.valueOf(awesomeIcon), size, style, styleClass); 65 | } 66 | 67 | public FAIcon(@NamedArg("awesomeIcon") String awesomeIcon, @NamedArg("size") String size, @NamedArg("styleClass") String styleClass) { 68 | this(AwesomeIcon.valueOf(awesomeIcon), size, null, styleClass); 69 | } 70 | 71 | public FAIcon(@NamedArg("awesomeIcon") String awesomeIcon, @NamedArg("size") String size) { 72 | this(AwesomeIcon.valueOf(awesomeIcon), size, null, null); 73 | } 74 | 75 | public FAIcon(@NamedArg("awesomeIcon") String awesomeIcon) { 76 | this(AwesomeIcon.valueOf(awesomeIcon), "2em", null, null); 77 | } 78 | 79 | private FAIcon() { 80 | this(AwesomeIcon.STAR, "2em", null, null); 81 | } 82 | 83 | public static FAIcon create() { 84 | return new FAIcon(); 85 | } 86 | 87 | public FAIcon icon(AwesomeIcon icon) { 88 | setText(icon.toString()); 89 | return this; 90 | } 91 | 92 | public FAIcon size(String iconSize) { 93 | setStyle("-fx-font-size: " + iconSize + ";"); 94 | return this; 95 | } 96 | 97 | public FAIcon style(String style) { 98 | setStyle(getStyle() + style); 99 | return this; 100 | } 101 | 102 | public FAIcon styleClass(String styleClass) { 103 | getStyleClass().add(styleClass); 104 | return this; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/ui/FXTreeCell.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package ui; 19 | 20 | import javafx.scene.Parent; 21 | import javafx.scene.control.TreeCell; 22 | 23 | public class FXTreeCell extends TreeCell { 24 | 25 | private final CellController controller; 26 | 27 | private Parent aControl; 28 | 29 | public FXTreeCell(CellController aController, Parent aControl) { 30 | controller = aController; 31 | this.aControl = aControl; 32 | 33 | } 34 | 35 | @Override 36 | public void cancelEdit() { 37 | super.cancelEdit(); 38 | 39 | if(controller != null) { 40 | controller.cancelEdit(); 41 | } 42 | } 43 | 44 | @Override 45 | public void commitEdit(T newValue) { 46 | super.commitEdit(newValue); 47 | 48 | if(controller != null) { 49 | controller.commitEdit(newValue); 50 | } 51 | } 52 | 53 | @Override 54 | public void startEdit() { 55 | super.startEdit(); 56 | 57 | if(controller != null) { 58 | controller.startEdit(); 59 | } 60 | } 61 | 62 | @Override 63 | protected void updateItem(T item, boolean empty) { 64 | super.updateItem(item, empty); 65 | 66 | if(item == null || empty) { 67 | setGraphic(null); 68 | setText(null); 69 | } 70 | else { 71 | setGraphic(aControl); 72 | setText(null); 73 | } 74 | 75 | if(controller != null) { 76 | controller.updateItem(item, empty); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/ui/TabPaneDetacher.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package ui; 19 | 20 | /* 21 | * Copyright 2014 Jens Deters. 22 | * 23 | * Licensed under the Apache License, Version 2.0 (the "License"); 24 | * you may not use this file except in compliance with the License. 25 | * You may obtain a copy of the License at 26 | * 27 | * http://www.apache.org/licenses/LICENSE-2.0 28 | * 29 | * Unless required by applicable law or agreed to in writing, software 30 | * distributed under the License is distributed on an "AS IS" BASIS, 31 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 | * See the License for the specific language governing permissions and 33 | * limitations under the License. 34 | */ 35 | 36 | import java.util.ArrayList; 37 | import java.util.HashMap; 38 | import java.util.List; 39 | import java.util.Map; 40 | import java.util.SortedSet; 41 | import java.util.TreeSet; 42 | 43 | import javafx.beans.Observable; 44 | import javafx.beans.property.BooleanProperty; 45 | import javafx.beans.property.SimpleBooleanProperty; 46 | import javafx.scene.Cursor; 47 | import javafx.scene.Scene; 48 | import javafx.scene.SnapshotParameters; 49 | import javafx.scene.control.Tab; 50 | import javafx.scene.control.TabPane; 51 | import javafx.scene.image.WritableImage; 52 | import javafx.scene.input.ClipboardContent; 53 | import javafx.scene.input.DataFormat; 54 | import javafx.scene.input.DragEvent; 55 | import javafx.scene.input.Dragboard; 56 | import javafx.scene.input.MouseEvent; 57 | import javafx.scene.input.TransferMode; 58 | import javafx.scene.layout.Pane; 59 | import javafx.scene.transform.Transform; 60 | import javafx.stage.Stage; 61 | import javafx.stage.WindowEvent; 62 | 63 | /** 64 | * A simple Utility to make all {@link Tab}s of a {@link TabPane} detachable. 65 | *
66 | *

Usage

67 | * To get a {@link TabPane} in charge of control: 68 | *
69 | * Hint: Only already added {@link Tab}s are going to be in charge of control! 70 | *
 71 |  * {@code
 72 |  * TabPaneDude.create().makeTabsDetachable(myTapPane);
 73 |  * }
 74 |  * 
Tabs can then be detached simply by dragging a tab title to the desired window position. 75 | * 76 | /** 77 | * 78 | * @author Jens Deters (www.jensd.de) 79 | * @version 1.0.0 80 | * @since 14-10-2014 81 | */ 82 | public class TabPaneDetacher { 83 | 84 | private TabPane tabPane; 85 | private Tab currentTab; 86 | private final List originalTabs; 87 | private final Map tapTransferMap; 88 | private String[] stylesheets; 89 | private final BooleanProperty alwaysOnTop; 90 | 91 | private TabPaneDetacher() { 92 | originalTabs = new ArrayList<>(); 93 | stylesheets = new String[]{}; 94 | tapTransferMap = new HashMap<>(); 95 | alwaysOnTop = new SimpleBooleanProperty(); 96 | } 97 | 98 | /** 99 | * Creates a new instance of the TabPaneDetacher 100 | * 101 | * @return The new instance of the TabPaneDetacher. 102 | */ 103 | public static TabPaneDetacher create() { 104 | return new TabPaneDetacher(); 105 | } 106 | 107 | public BooleanProperty alwaysOnTopProperty() { 108 | return alwaysOnTop; 109 | } 110 | 111 | public Boolean isAlwaysOnTop() { 112 | return alwaysOnTop.get(); 113 | } 114 | 115 | /** 116 | * 117 | * Sets whether detached Tabs should be always on top. 118 | * 119 | * @param alwaysOnTop The state to be set. 120 | * @return The current TabPaneDetacher instance. 121 | */ 122 | public TabPaneDetacher alwaysOnTop(boolean alwaysOnTop){ 123 | alwaysOnTopProperty().set(alwaysOnTop); 124 | return this; 125 | } 126 | 127 | /** 128 | * Sets the stylesheets that should be assigend to the new created {@link Stage}. 129 | * 130 | * @param stylesheets The stylesheets to be set. 131 | * @return The current TabPaneDetacher instance. 132 | */ 133 | public TabPaneDetacher stylesheets(String... stylesheets) { 134 | this.stylesheets = stylesheets; 135 | return this; 136 | } 137 | 138 | /** 139 | * Make all added {@link Tab}s of the given {@link TabPane} detachable. 140 | * 141 | * @param tabPane The {@link TabPane} to take over. 142 | * @return The current TabPaneDetacher instance. 143 | */ 144 | public TabPaneDetacher makeTabsDetachable(TabPane tabPane) { 145 | this.tabPane = tabPane; 146 | originalTabs.addAll(tabPane.getTabs()); 147 | for (int i = 0; i < tabPane.getTabs().size(); i++) { 148 | tapTransferMap.put(i, tabPane.getTabs().get(i)); 149 | } 150 | tabPane.getTabs().stream().forEach(t -> { 151 | t.setClosable(false); 152 | }); 153 | tabPane.getTabs().addListener((Observable o) -> { 154 | for (Tab tabX : tabPane.getTabs()) { 155 | if(!(tabX.getContent() instanceof Pane)) { 156 | throw new RuntimeException("Tab added where the content node was not a subclass of Pane, this means it cannot be dragged by TabPaneDetacher."); 157 | } 158 | } 159 | }); 160 | tabPane.setOnDragDetected( 161 | (MouseEvent event) -> { 162 | if (event.getSource() instanceof TabPane) { 163 | Pane rootPane = (Pane) tabPane.getScene().getRoot(); 164 | rootPane.setOnDragOver((DragEvent event1) -> { 165 | event1.acceptTransferModes(TransferMode.ANY); 166 | event1.consume(); 167 | }); 168 | currentTab = tabPane.getSelectionModel().getSelectedItem(); 169 | SnapshotParameters snapshotParams = new SnapshotParameters(); 170 | snapshotParams.setTransform(Transform.scale(0.4, 0.4)); 171 | WritableImage snapshot = currentTab.getContent().snapshot(snapshotParams, null); 172 | Dragboard db = tabPane.startDragAndDrop(TransferMode.MOVE); 173 | ClipboardContent clipboardContent = new ClipboardContent(); 174 | clipboardContent.put(DataFormat.PLAIN_TEXT, "detach"); 175 | db.setDragView(snapshot, 40, 40); 176 | db.setContent(clipboardContent); 177 | } 178 | event.consume(); 179 | } 180 | ); 181 | tabPane.setOnDragDone( 182 | (DragEvent event) -> { 183 | openTabInStage(currentTab); 184 | tabPane.setCursor(Cursor.DEFAULT); 185 | event.consume(); 186 | } 187 | ); 188 | return this; 189 | } 190 | 191 | /** 192 | * Opens the content of the given {@link Tab} in a separate Stage. While the content is removed from the {@link Tab} it is 193 | * added to the root of a new {@link Stage}. The Window title is set to the name of the {@link Tab}; 194 | * 195 | * @param tab The {@link Tab} to get the content from. 196 | */ 197 | public void openTabInStage(final Tab tab) { 198 | if(tab == null){ 199 | return; 200 | } 201 | int originalTab = originalTabs.indexOf(tab); 202 | tapTransferMap.remove(originalTab); 203 | Pane content = (Pane) tab.getContent(); 204 | if (content == null) { 205 | throw new IllegalArgumentException("Can not detach Tab '" + tab.getText() + "': content is empty (null)."); 206 | } 207 | tab.setContent(null); 208 | final Scene scene = new Scene(content, content.getPrefWidth(), content.getPrefHeight()); 209 | scene.getStylesheets().addAll(stylesheets); 210 | Stage stage = new Stage(); 211 | stage.setScene(scene); 212 | stage.setTitle(tab.getText()); 213 | stage.setAlwaysOnTop(isAlwaysOnTop()); 214 | stage.setOnCloseRequest((WindowEvent t) -> { 215 | stage.close(); 216 | tab.setContent(content); 217 | int originalTabIndex = originalTabs.indexOf(tab); 218 | tapTransferMap.put(originalTabIndex, tab); 219 | int index = 0; 220 | SortedSet keys = new TreeSet<>(tapTransferMap.keySet()); 221 | for (Integer key : keys) { 222 | Tab value = tapTransferMap.get(key); 223 | if(!tabPane.getTabs().contains(value)){ 224 | tabPane.getTabs().add(index, value); 225 | } 226 | index++; 227 | } 228 | tabPane.getSelectionModel().select(tab); 229 | }); 230 | stage.setOnShown((WindowEvent t) -> { 231 | tab.getTabPane().getTabs().remove(tab); 232 | }); 233 | CloseWindowOnEscape.apply(scene, stage); 234 | stage.show(); 235 | } 236 | 237 | } 238 | -------------------------------------------------------------------------------- /src/main/resources/erlyberly/SourceCodePro-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andytill/erlyberly/625f9fda370d8218467d60421ee93bfa23850725/src/main/resources/erlyberly/SourceCodePro-Medium.ttf -------------------------------------------------------------------------------- /src/main/resources/erlyberly/dbg.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/resources/erlyberly/entop.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 25 | 30 | 35 | 40 | 45 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/test/java/erlyberly/BasicSearchTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import org.junit.Test; 23 | 24 | public class BasicSearchTest { 25 | 26 | @Test 27 | public void test01() { 28 | assertMatching("wert", "qwerty"); 29 | } 30 | 31 | @Test 32 | public void test02() { 33 | assertMatching("derp|wert", "qwerty"); 34 | } 35 | 36 | @Test 37 | public void test03() { 38 | assertMatching("wert|derp", "qwerty"); 39 | } 40 | 41 | @Test 42 | public void test04() { 43 | assertMatching("wert||derp", "qwerty"); 44 | } 45 | 46 | @Test 47 | public void test05() { 48 | assertNotMatching("wert|derp", "turp"); 49 | } 50 | 51 | @Test 52 | public void test06() { 53 | assertNotMatching("wert||derp", "turp"); 54 | } 55 | 56 | @Test 57 | public void test07() { 58 | assertMatching("", "trolololol"); 59 | } 60 | 61 | @Test 62 | public void test08() { 63 | // even match null when there is no search! 64 | assertMatching("", null); 65 | } 66 | 67 | @Test 68 | public void test10() { 69 | assertNotMatching("!the", "all the things"); 70 | } 71 | 72 | @Test 73 | public void test11() { 74 | assertNotMatching("the|!the", "all the things"); 75 | } 76 | 77 | @Test 78 | public void test13() { 79 | assertMatching("!the", "banjo"); 80 | } 81 | 82 | private void assertMatching(String searchText, String sourceText) { 83 | BasicSearch basicSearch = new BasicSearch(searchText); 84 | 85 | assertTrue(basicSearch.matches(sourceText)); 86 | } 87 | 88 | private void assertNotMatching(String searchText, String sourceText) { 89 | BasicSearch basicSearch = new BasicSearch(searchText); 90 | 91 | assertFalse(basicSearch.matches(sourceText)); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/erlyberly/node/OtpUtilTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * erlyberly, erlang trace debugger 3 | * Copyright (C) 2016 Andy Till 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package erlyberly.node; 19 | 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | 23 | import com.ericsson.otp.erlang.OtpErlangAtom; 24 | import com.ericsson.otp.erlang.OtpErlangBinary; 25 | import com.ericsson.otp.erlang.OtpErlangBitstr; 26 | import com.ericsson.otp.erlang.OtpErlangException; 27 | import com.ericsson.otp.erlang.OtpErlangList; 28 | import com.ericsson.otp.erlang.OtpErlangObject; 29 | 30 | import erlyberly.format.ErlangFormatter; 31 | import erlyberly.format.LFEFormatter; 32 | 33 | 34 | public class OtpUtilTest { 35 | 36 | byte[] bytes; 37 | 38 | @Test 39 | public void binaryToString1() { 40 | bytes = "hello".getBytes(); 41 | 42 | Assert.assertEquals("<<\"hello\">>", bin()); 43 | } 44 | 45 | @Test 46 | public void binaryToString2() { 47 | bytes = "\0hello".getBytes(); 48 | 49 | Assert.assertEquals("<<0, \"hello\">>", bin()); 50 | } 51 | 52 | @Test 53 | public void binaryToString3() { 54 | bytes = "\0hello\0".getBytes(); 55 | 56 | Assert.assertEquals("<<0, \"hello\", 0>>", bin()); 57 | } 58 | /* 59 | @Test 60 | public void binaryToString4() { 61 | bytes = "_hello\0".getBytes(); 62 | bytes[0] = 31; 63 | Assert.assertEquals("<<31, \"hello\", 0>>", bin()); 64 | } 65 | 66 | @Test 67 | public void otpObjectToStringList0() { 68 | OtpErlangList list = OtpUtil.list(); 69 | StringBuilder sb = new StringBuilder();; 70 | OtpUtil.otpObjectToString(list, sb ); 71 | Assert.assertEquals("[]", sb.toString()); 72 | } 73 | 74 | @Test 75 | public void otpObjectToStringList1() { 76 | OtpErlangList list = OtpUtil.list(1); 77 | StringBuilder sb = new StringBuilder();; 78 | OtpUtil.otpObjectToString(list, sb ); 79 | Assert.assertEquals("[1]", sb.toString()); 80 | } 81 | 82 | @Test 83 | public void otpObjectToStringList2() { 84 | OtpErlangList list = OtpUtil.list(1,2); 85 | StringBuilder sb = new StringBuilder();; 86 | OtpUtil.otpObjectToString(list, sb ); 87 | Assert.assertEquals("[1, 2]", sb.toString()); 88 | } 89 | 90 | @Test 91 | public void otpObjectToStringTuple0() { 92 | OtpErlangTuple tuple = OtpUtil.tuple(); 93 | StringBuilder sb = new StringBuilder();; 94 | OtpUtil.otpObjectToString(tuple, sb ); 95 | Assert.assertEquals("{}", sb.toString()); 96 | } 97 | 98 | @Test 99 | public void otpObjectToStringTuple1() { 100 | OtpErlangTuple tuple = OtpUtil.tuple(1); 101 | StringBuilder sb = new StringBuilder();; 102 | OtpUtil.otpObjectToString(tuple, sb ); 103 | Assert.assertEquals("{1}", sb.toString()); 104 | } 105 | 106 | @Test 107 | public void otpObjectToStringTuple2() { 108 | OtpErlangTuple tuple = OtpUtil.tuple(1,2); 109 | StringBuilder sb = new StringBuilder();; 110 | OtpUtil.otpObjectToString(tuple, sb ); 111 | Assert.assertEquals("{1, 2}", sb.toString()); 112 | } 113 | 114 | @Test 115 | public void otpObjectToStringTuple3() { 116 | OtpErlangTuple tuple = OtpUtil.tuple(1,2,3); 117 | StringBuilder sb = new StringBuilder();; 118 | OtpUtil.otpObjectToString(tuple, sb ); 119 | Assert.assertEquals("{1, 2, 3}", sb.toString()); 120 | } 121 | 122 | @Test 123 | public void otpObjectToStringNestedTuples() { 124 | OtpErlangTuple tuple = OtpUtil.tuple(1,2, OtpUtil.tuple(3,4)); 125 | StringBuilder sb = new StringBuilder();; 126 | OtpUtil.otpObjectToString(tuple, sb ); 127 | Assert.assertEquals("{1, 2, {3, 4}}", sb.toString()); 128 | } 129 | */ 130 | 131 | @Test 132 | public void improperListErlang() throws OtpErlangException { 133 | OtpErlangList improper = 134 | new OtpErlangList( 135 | new OtpErlangObject[] { 136 | new OtpErlangAtom("hello"), 137 | new OtpErlangBinary("x".getBytes()), 138 | }, 139 | new OtpErlangAtom("world")); 140 | Assert.assertEquals( 141 | "[hello, <<\"x\">>|world]", 142 | new ErlangFormatter().toString(improper)); 143 | } 144 | 145 | @Test 146 | public void improperListLFE() throws OtpErlangException { 147 | OtpErlangList improper = 148 | new OtpErlangList( 149 | new OtpErlangObject[] { 150 | new OtpErlangAtom("hello"), 151 | new OtpErlangBinary("x".getBytes()), 152 | }, 153 | new OtpErlangAtom("world")); 154 | Assert.assertEquals( 155 | "('hello, #B(\"x\").'world)", 156 | new LFEFormatter().toString(improper)); 157 | } 158 | 159 | @Test 160 | public void bitstringErlang() throws OtpErlangException { 161 | OtpErlangBitstr bitstr = new OtpErlangBitstr(new byte[]{42, -100, 3 << 5}, 5); 162 | Assert.assertEquals( 163 | "<<42, 156, 3:3>>", 164 | new ErlangFormatter().toString(bitstr)); 165 | } 166 | 167 | @Test 168 | public void bitstringLFE() throws OtpErlangException { 169 | OtpErlangBitstr bitstr = new OtpErlangBitstr(new byte[]{42, -100, 3 << 5}, 5); 170 | Assert.assertEquals( 171 | "#B(42 156 (3 (size 3)))", 172 | new LFEFormatter().toString(bitstr)); 173 | } 174 | 175 | private String bin() { 176 | OtpErlangBinary binary = new OtpErlangBinary(bytes); 177 | 178 | return new ErlangFormatter().toString(binary); 179 | } 180 | } 181 | --------------------------------------------------------------------------------