/** * 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(); } }