/**
* erlyberly, erlang trace debugger
* Copyright (C) 2016 Andy Till
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package erlyberly;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangList;
import com.ericsson.otp.erlang.OtpErlangLong;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangPid;
import com.ericsson.otp.erlang.OtpErlangString;
import com.ericsson.otp.erlang.OtpErlangTuple;
import erlyberly.node.OtpUtil;
public class CrashReport {
private static final OtpErlangAtom ATOM_INITIAL_CALL = OtpUtil.atom("initial_call");
private static final OtpErlangAtom ATOM_PID = OtpUtil.atom("pid");
private static final OtpErlangAtom ATOM_REGISTERED_NAME = OtpUtil.atom("registered_name");
private static final OtpErlangAtom ATOM_ERROR_INFO = OtpUtil.atom("error_info");
private static final OtpErlangAtom ATOM_FILE = OtpUtil.atom("file");
private static final OtpErlangAtom ATOM_LINE = OtpUtil.atom("line");
private final Map<Object, Object> crashProps;
private final String registeredName;
private final OtpErlangObject terms;
private final OtpErlangTuple errorInfo;
private final LocalDateTime date = LocalDateTime.now();
public CrashReport(OtpErlangObject obj) {
this.terms = obj;
crashProps = OtpUtil.propsToMap((OtpErlangList) obj);
errorInfo = (OtpErlangTuple) crashProps.get(ATOM_ERROR_INFO);
// pull out the register name of the crashing process
Object aRegName = crashProps.get(ATOM_REGISTERED_NAME);
if(OtpUtil.list().equals(aRegName)) {
registeredName = "";
}
else {
registeredName = aRegName.toString();
}
}
<T> List<T> mapStackTraces(StackTraceFn<T> fn) {
OtpErlangList stackTrace = (OtpErlangList) errorInfo.elementAt(2);
ArrayList<T> result = new ArrayList<T>();
for (OtpErlangObject obj : stackTrace) {
OtpErlangTuple tuple = (OtpErlangTuple) obj;
OtpErlangAtom module = (OtpErlangAtom) tuple.elementAt(0);
OtpErlangAtom function = (OtpErlangAtom) tuple.elementAt(1);
OtpErlangObject argsOrArity = tuple.elementAt(2);
OtpErlangLong arity;
if(argsOrArity instanceof OtpErlangLong) {
arity = (OtpErlangLong) argsOrArity;
}
else {
OtpErlangList list = (OtpErlangList) argsOrArity;
arity = new OtpErlangLong(list.arity());
}
Map<Object, Object> fileLineProps = OtpUtil.propsToMap((OtpErlangList) tuple.elementAt(3));
OtpErlangString file = (OtpErlangString) fileLineProps.get(ATOM_FILE);
OtpErlangLong line = (OtpErlangLong) fileLineProps.get(ATOM_LINE);
String fileString = "";
if(file != null) {
fileString = file.stringValue();
}
result.add(
fn.invoke(module, function, arity, fileString, line)
);
}
return result;
}
public interface StackTraceFn<T> {
T invoke(OtpErlangAtom module, OtpErlangAtom function, OtpErlangLong arity, String file, OtpErlangLong line);
}
public OtpErlangPid getPid() {
return (OtpErlangPid) crashProps.get(ATOM_PID);
}
public String getRegisteredName() {
return registeredName;
}
public Object getProcessInitialCall() {
return crashProps.get(ATOM_INITIAL_CALL);
}
public OtpErlangObject getProps() {
return terms;
}
public OtpErlangAtom getErrorClass() {
return (OtpErlangAtom) errorInfo.elementAt(0);
}
public OtpErlangObject getErrorReason() {
return errorInfo.elementAt(1);
}
public LocalDateTime getDateTime() {
return date;
}
public Optional<OtpErlangList> getCallArgs() {
OtpErlangTuple staceTrace1 = (OtpErlangTuple) ((OtpErlangList) errorInfo.elementAt(2)).getHead();
OtpErlangObject argsOrArity = staceTrace1.elementAt(2);
if(argsOrArity instanceof OtpErlangList)
return Optional.of((OtpErlangList)argsOrArity);
else
return Optional.empty();
}
}