Skip to content
This repository was archived by the owner on Aug 30, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
ref stacktraces and exceptions
  • Loading branch information
Manoel Aranda Neto committed Oct 25, 2019
commit 69aea15b7b8aaeb82912c1d48b8cfad671270abf
15 changes: 2 additions & 13 deletions sentry-core/src/main/java/io/sentry/core/MainEventProcessor.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.sentry.core;

import io.sentry.core.exception.SentryExceptionReader;
import io.sentry.core.util.Objects;

public class MainEventProcessor implements EventProcessor {
Expand All @@ -15,20 +14,10 @@ public MainEventProcessor(SentryOptions options) {
public SentryEvent process(SentryEvent event) {
Throwable throwable = event.getThrowable();
if (throwable != null) {

// if (event.getMessage() == null) {
// event.setMessage(getMessage(throwable));
// }

event.setException(SentryExceptionReader.createSentryException(throwable));
SentryExceptionFactory sentryExceptionFactory = new SentryExceptionFactory();
event.setException(sentryExceptionFactory.getSentryExceptions(throwable));
}

return event;
}

// private Message getMessage(Throwable throwable) {
// Message message = new Message();
// message.setFormatted(throwable.getMessage());
// return message;
// }
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
package io.sentry.core.exception;
package io.sentry.core;

import io.sentry.core.exception.ExceptionMechanismThrowable;
import io.sentry.core.protocol.Mechanism;
import io.sentry.core.protocol.SentryException;
import io.sentry.core.protocol.SentryStackFrame;
import io.sentry.core.protocol.SentryStackTrace;
import io.sentry.core.util.VisibleForTesting;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public final class SentryExceptionReader {

/** class responsible for converting Java Throwable to SentryExceptions */
class SentryExceptionFactory {
/**
* Creates a new instance from the given {@code throwable}.
*
* @param throwable the {@link Throwable} to build this instance from
*/
public static List<SentryException> createSentryException(final Throwable throwable) {
return createSentryException(extractExceptionQueue(throwable));
List<SentryException> getSentryExceptions(final Throwable throwable) {
return getSentryExceptions(extractExceptionQueue(throwable));
}

/**
* Creates a new instance from the given {@code exceptions}.
*
* @param exceptions a {@link Deque} of {@link SentryException} to build this instance from
*/
private static List<SentryException> createSentryException(
final Deque<SentryException> exceptions) {
private List<SentryException> getSentryExceptions(final Deque<SentryException> exceptions) {
return new ArrayList<>(exceptions);
}

Expand All @@ -42,8 +42,8 @@ private static List<SentryException> createSentryException(
* @param exceptionMechanism The optional {@link Mechanism} of the {@code throwable}. Or null if
* none exist.
*/
private static SentryException createSentryException(
Throwable throwable, Mechanism exceptionMechanism) {
private SentryException getSentryException(
final Throwable throwable, final Mechanism exceptionMechanism) {

Package exceptionPackage = throwable.getClass().getPackage();
String fullClassName = throwable.getClass().getName();
Expand All @@ -61,7 +61,9 @@ private static SentryException createSentryException(

SentryStackTrace sentryStackTrace = new SentryStackTrace();

sentryStackTrace.setFrames(getStackFrames(throwable.getStackTrace()));
SentryStackTraceFactory sentryStackTraceFactory = new SentryStackTraceFactory();

sentryStackTrace.setFrames(sentryStackTraceFactory.getStackFrames(throwable.getStackTrace()));

exception.setStacktrace(sentryStackTrace);
exception.setType(exceptionClassName);
Expand All @@ -72,20 +74,6 @@ private static SentryException createSentryException(
return exception;
}

private static List<SentryStackFrame> getStackFrames(StackTraceElement[] elements) {
List<SentryStackFrame> sentryStackFrames = new ArrayList<>();

for (StackTraceElement item : elements) {
SentryStackFrame sentryStackFrame = new SentryStackFrame();
sentryStackFrame.setModule(item.getClassName());
sentryStackFrame.setFunction(item.getMethodName());
sentryStackFrame.setFilename(item.getFileName());
sentryStackFrame.setLineno(item.getLineNumber());
sentryStackFrames.add(sentryStackFrame);
}
return sentryStackFrames;
}

/**
* Transforms a {@link Throwable} into a Queue of {@link SentryException}.
*
Expand All @@ -94,26 +82,29 @@ private static List<SentryStackFrame> getStackFrames(StackTraceElement[] element
* @param throwable throwable to transform in a queue of exceptions.
* @return a queue of exception with StackTrace.
*/
private static Deque<SentryException> extractExceptionQueue(Throwable throwable) {
@VisibleForTesting
Deque<SentryException> extractExceptionQueue(final Throwable throwable) {
Deque<SentryException> exceptions = new ArrayDeque<>();
Set<Throwable> circularityDetector = new HashSet<>();
Mechanism exceptionMechanism;

Throwable currentThrowable = throwable;

// Stack the exceptions to send them in the reverse order
while (throwable != null && circularityDetector.add(throwable)) {
if (throwable instanceof ExceptionMechanismThrowable) {
while (currentThrowable != null && circularityDetector.add(currentThrowable)) {
if (currentThrowable instanceof ExceptionMechanismThrowable) {
// this is for ANR I believe
ExceptionMechanismThrowable exceptionMechanismThrowable =
(ExceptionMechanismThrowable) throwable;
(ExceptionMechanismThrowable) currentThrowable;
exceptionMechanism = exceptionMechanismThrowable.getExceptionMechanism();
throwable = exceptionMechanismThrowable.getThrowable();
currentThrowable = exceptionMechanismThrowable.getThrowable();
} else {
exceptionMechanism = null;
}

SentryException exception = createSentryException(throwable, exceptionMechanism);
SentryException exception = getSentryException(currentThrowable, exceptionMechanism);
exceptions.add(exception);
throwable = throwable.getCause();
currentThrowable = currentThrowable.getCause();
}

return exceptions;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.sentry.core;

import io.sentry.core.protocol.SentryStackFrame;
import io.sentry.core.util.Nullable;
import java.util.ArrayList;
import java.util.List;

/** class responsible for converting Java StackTraceElements to SentryStackFrames */
class SentryStackTraceFactory {

/**
* convert an Array of Java StackTraceElements to a list of SentryStackFrames
*
* @param elements Array of Java StackTraceElements
* @return list of SentryStackFrames
*/
List<SentryStackFrame> getStackFrames(@Nullable final StackTraceElement[] elements) {
List<SentryStackFrame> sentryStackFrames = new ArrayList<>();

if (elements != null) {
for (StackTraceElement item : elements) {
SentryStackFrame sentryStackFrame = new SentryStackFrame();
sentryStackFrame.setModule(item.getClassName());
sentryStackFrame.setFunction(item.getMethodName());
sentryStackFrame.setFilename(item.getFileName());
sentryStackFrame.setLineno(item.getLineNumber());
sentryStackFrame.setNative(item.isNativeMethod());

sentryStackFrames.add(sentryStackFrame);
}
}

return sentryStackFrames;
}
}
28 changes: 7 additions & 21 deletions sentry-core/src/main/java/io/sentry/core/SentryThreadFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ List<SentryThread> getCurrentThreads() {
return getCurrentThreads(null);
}

private List<SentryThread> getCurrentThreads(@Nullable Thread crashedThread) {
private List<SentryThread> getCurrentThreads(@Nullable final Thread crashedThread) {
Map<Thread, StackTraceElement[]> threads = Thread.getAllStackTraces();
List<SentryThread> result = new ArrayList<>();

Expand All @@ -33,10 +33,10 @@ private List<SentryThread> getCurrentThreads(@Nullable Thread crashedThread) {
}

private SentryThread getSentryThread(
@Nullable Thread crashedThread,
Thread currentThread,
StackTraceElement[] stackFramesElements,
Thread thread) {
@Nullable final Thread crashedThread,
final Thread currentThread,
final StackTraceElement[] stackFramesElements,
final Thread thread) {
SentryThread sentryThread = new SentryThread();

sentryThread.setName(thread.getName());
Expand All @@ -49,27 +49,13 @@ private SentryThread getSentryThread(
}
sentryThread.setCurrent(thread == currentThread);

List<SentryStackFrame> frames = new ArrayList<>();
for (StackTraceElement element : stackFramesElements) {
frames.add(getSentryStackFrame(element));
}
SentryStackTraceFactory sentryStackTraceFactory = new SentryStackTraceFactory();
List<SentryStackFrame> frames = sentryStackTraceFactory.getStackFrames(stackFramesElements);

if (frames.size() > 0) {
sentryThread.setStacktrace(new SentryStackTrace(frames));
}

return sentryThread;
}

private SentryStackFrame getSentryStackFrame(StackTraceElement element) {
SentryStackFrame sentryStackFrame = new SentryStackFrame();
sentryStackFrame.setModule(element.getClassName());
sentryStackFrame.setFilename(element.getFileName());
if (element.getLineNumber() >= 0) {
sentryStackFrame.setLineno(element.getLineNumber());
}
sentryStackFrame.setFunction(element.getMethodName());
sentryStackFrame.setNative(element.isNativeMethod());
return sentryStackFrame;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.sentry.core

import io.sentry.core.exception.ExceptionMechanismThrowable
import io.sentry.core.protocol.Mechanism
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class SentryExceptionFactoryTest {
private val sut = SentryExceptionFactory()

@Test
fun `when getSentryExceptions is called passing an Exception, not empty result`() {
val exception = Exception("Exception")
assertTrue(sut.getSentryExceptions(exception).size > 0)
}

@Test
fun `when getSentryExceptions is called passing null, empty result`() {
assertEquals(0, sut.getSentryExceptions(null).size)
}

@Test
fun `when getSentryExceptions is called passing an Exception, it should set its fields`() {
val exception = Exception("Exception")
val sentryExceptions = sut.getSentryExceptions(exception)
assertEquals("Exception", sentryExceptions[0].type)
assertEquals("Exception", sentryExceptions[0].value)
assertEquals("java.lang", sentryExceptions[0].module)
assertTrue(sentryExceptions[0].stacktrace.frames.size > 0)
}

@Test
fun `when getSentryExceptions is called passing a ExceptionMechanism, it should set its fields`() {
val mechanism = Mechanism()
mechanism.type = "anr"
mechanism.handled = false

val error = Exception("Exception")

val throwable = ExceptionMechanismThrowable(mechanism, error)

val sentryExceptions = sut.getSentryExceptions(throwable)
assertEquals("anr", sentryExceptions[0].mechanism.type)
assertEquals(false, sentryExceptions[0].mechanism.handled)
}

@Test
fun `when exception has a cause, ensure conversion queue keeps order`() {
val exception = Exception("message", Exception("cause"))
val queue = sut.extractExceptionQueue(exception)

assertEquals("message", queue.first.value)
assertEquals("cause", queue.last.value)
}

@Test
fun `when getSentryExceptions is called passing an Inner exception, not empty result`() {
val exception = InnerClassThrowable(InnerClassThrowable())
val queue = sut.extractExceptionQueue(exception)
assertEquals("SentryExceptionFactoryTest\$InnerClassThrowable", queue.first.type)
}

private inner class InnerClassThrowable constructor(cause: Throwable? = null) : Throwable(cause)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.sentry.core

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class SentryStackTraceFactoryTest {
private val sut = SentryStackTraceFactory()

@Test
fun `when getStackFrames is called passing a valid Array, not empty result`() {
val stacktraces = Thread.currentThread().stackTrace
assertTrue(sut.getStackFrames(stacktraces).count() > 0)
}

@Test
fun `when getStackFrames is called passing null, empty result`() {
assertEquals(0, sut.getStackFrames(null).count())
}

@Test
fun `when getStackFrames is called passing a valid array, fields should be set`() {
val element = StackTraceElement("class", "method", "fileName", -2)
val stacktraces = Array(1) { element }
val stackFrames = sut.getStackFrames(stacktraces)
assertEquals("class", stackFrames[0].module)
assertEquals("method", stackFrames[0].function)
assertEquals("fileName", stackFrames[0].filename)
assertEquals(-2, stackFrames[0].lineno)
assertEquals(true, stackFrames[0].isNative)
}
}