Skip to content

Commit

Permalink
Add a configuration option to limit the number of threads captured
Browse files Browse the repository at this point in the history
  • Loading branch information
kstenerud committed Feb 25, 2022
1 parent 3808c41 commit 8250bbe
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ internal class ConfigInternal(
var maxBreadcrumbs: Int = DEFAULT_MAX_BREADCRUMBS
var maxPersistedEvents: Int = DEFAULT_MAX_PERSISTED_EVENTS
var maxPersistedSessions: Int = DEFAULT_MAX_PERSISTED_SESSIONS
var maxReportedThreads: Int = DEFAULT_MAX_REPORTED_THREADS
var context: String? = null

var redactedKeys: Set<String>
Expand Down Expand Up @@ -99,6 +100,7 @@ internal class ConfigInternal(
private const val DEFAULT_MAX_BREADCRUMBS = 50
private const val DEFAULT_MAX_PERSISTED_SESSIONS = 128
private const val DEFAULT_MAX_PERSISTED_EVENTS = 32
private const val DEFAULT_MAX_REPORTED_THREADS = 200
private const val DEFAULT_LAUNCH_CRASH_THRESHOLD_MS: Long = 5000

@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,32 @@ public void setMaxPersistedEvents(int maxPersistedEvents) {
}
}

/**
* Gets the maximum number of threads that will be reported with an event. Once the threshold is
* reached, all remaining threads will be omitted.
*
* By default, up to 200 threads are reported.
*/
public int getMaxReportedThreads() {
return impl.getMaxReportedThreads();
}

/**
* Sets the maximum number of threads that will be reported with an event. Once the threshold is
* reached, all remaining threads will be omitted.
*
* By default, up to 200 threads are reported.
*/
public void setMaxReportedThreads(int maxReportedThreads) {
if (maxReportedThreads >= 0) {
impl.setMaxReportedThreads(maxReportedThreads);
} else {
getLogger().e("Invalid configuration value detected. "
+ "Option maxReportedThreads should be a positive integer."
+ "Supplied value is " + maxReportedThreads);
}
}

/**
* Sets the maximum number of persisted sessions which will be stored. Once the threshold is
* reached, the oldest session will be deleted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,20 @@ import java.io.IOException
/**
* Capture and serialize the state of all threads at the time of an exception.
*/
internal class ThreadState @Suppress("LongParameterList") @JvmOverloads constructor(
internal class ThreadState @Suppress("LongParameterList") constructor(
exc: Throwable?,
isUnhandled: Boolean,
maxThreads: Int,
sendThreads: ThreadSendPolicy,
projectPackages: Collection<String>,
logger: Logger,
currentThread: java.lang.Thread? = null,
stackTraces: MutableMap<java.lang.Thread, Array<StackTraceElement>>? = null
) : JsonStream.Streamable {

internal constructor(
exc: Throwable?,
isUnhandled: Boolean,
config: ImmutableConfig
) : this(exc, isUnhandled, config.sendThreads, config.projectPackages, config.logger)
) : this(exc, isUnhandled, config.maxReportedThreads, config.sendThreads, config.projectPackages, config.logger)

val threads: MutableList<Thread>

Expand All @@ -29,49 +28,55 @@ internal class ThreadState @Suppress("LongParameterList") @JvmOverloads construc
(sendThreads == ThreadSendPolicy.UNHANDLED_ONLY && isUnhandled)

threads = when {
recordThreads -> captureThreadTrace(
stackTraces ?: java.lang.Thread.getAllStackTraces(),
currentThread ?: java.lang.Thread.currentThread(),
exc,
isUnhandled,
projectPackages,
logger
)
recordThreads -> captureThreadTrace(exc, isUnhandled, maxThreads, projectPackages, logger)
else -> mutableListOf()
}
}

private fun captureThreadTrace(
stackTraces: MutableMap<java.lang.Thread, Array<StackTraceElement>>,
currentThread: java.lang.Thread,
exc: Throwable?,
isUnhandled: Boolean,
maxThreadCount: Int,
projectPackages: Collection<String>,
logger: Logger
): MutableList<Thread> {
// API 24/25 don't record the currentThread, add it in manually
// https://issuetracker.google.com/issues/64122757
if (!stackTraces.containsKey(currentThread)) {
stackTraces[currentThread] = currentThread.stackTrace
}
if (exc != null && isUnhandled) { // unhandled errors use the exception trace for thread traces
stackTraces[currentThread] = exc.stackTrace
}
val currentThread = java.lang.Thread.currentThread()
val threadCount = java.lang.Thread.activeCount()
val wantedThreadCount = kotlin.math.min(threadCount, maxThreadCount)
val threads: Array<java.lang.Thread?> = arrayOfNulls(wantedThreadCount)
val recordedThreadCount = java.lang.Thread.enumerate(threads)

val currentThreadId = currentThread.id
return stackTraces.keys
.sortedBy { it.id }
.mapNotNull { thread ->
val trace = stackTraces[thread]

if (trace != null) {
val stacktrace = Stacktrace(trace, projectPackages, logger)
val errorThread = thread.id == currentThreadId
Thread(thread.id, thread.name, ThreadType.ANDROID, errorThread, Thread.State.forThread(thread), stacktrace, logger)
fun newThread(thread: java.lang.Thread): Thread {
val isErrorThread = thread.id == currentThread.id
// API 24/25 don't record the currentThread, add it in manually
// https://issuetracker.google.com/issues/64122757
val stackTrace = Stacktrace(if (isErrorThread) {
if (exc != null && isUnhandled) { // unhandled errors use the exception trace for thread traces
exc.stackTrace
} else {
null
currentThread.stackTrace
}
}.toMutableList()
} else {
thread.stackTrace
}, projectPackages, logger)

return Thread(thread.id, thread.name, ThreadType.ANDROID, isErrorThread, Thread.State.forThread(thread), stackTrace, logger)
}

val list = ArrayList<Thread>(recordedThreadCount)
for (i in 0..recordedThreadCount-1) {
list.add(newThread(threads[i]!!))
}
if (threadCount > maxThreadCount) {
list.add(Thread(-1,
"[${threadCount-recordedThreadCount} threads omitted as the maxReportedThreads limit (${maxThreadCount}) was exceeded]",
ThreadType.EMPTY,
false,
Thread.State.UNKNOWN,
Stacktrace(arrayOf(StackTraceElement("", "", "-", 0)), projectPackages, logger),
logger))
}
return list
}

@Throws(IOException::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ package com.bugsnag.android
*/
enum class ThreadType(internal val desc: String) {

/**
* A thread captured from Android's JVM layer
*/
EMPTY(""),

/**
* A thread captured from Android's JVM layer
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ data class ImmutableConfig(
val maxBreadcrumbs: Int,
val maxPersistedEvents: Int,
val maxPersistedSessions: Int,
val maxReportedThreads: Int,
val persistenceDirectory: Lazy<File>,
val sendLaunchCrashesSynchronously: Boolean,

Expand Down Expand Up @@ -159,6 +160,7 @@ internal fun convertToImmutableConfig(
maxBreadcrumbs = config.maxBreadcrumbs,
maxPersistedEvents = config.maxPersistedEvents,
maxPersistedSessions = config.maxPersistedSessions,
maxReportedThreads = config.maxReportedThreads,
enabledBreadcrumbTypes = config.enabledBreadcrumbTypes?.toSet(),
persistenceDirectory = persistenceDir,
sendLaunchCrashesSynchronously = config.sendLaunchCrashesSynchronously,
Expand Down

0 comments on commit 8250bbe

Please sign in to comment.