Skip to content

Commit

Permalink
v5.14.0 (#1392)
Browse files Browse the repository at this point in the history
* docs: correct changelog entry

* test: remove flaky test case

* Capture and report `Thread.state` for Android Runtime threads (#1372)

* feat(threads): capture and report `Thread.state` for Android Runtime threads

* fix(thread state): added nullability annotation to `Thread.State.getDescriptor`

* feat(unity): allow Client to be initialised with the Notifier details for reporting (#1386)

* Capture ndk thread states (#1390)

* feat(threads): add `bsg_thread` to the NDK plugin (#1371)

* migrate ndk reports for the new threads property (#1376)

* feat(threads): add `bsg_thread` to the NDK plugin

* feat(thread state): migrate v5 ndk reports to the new v6

* Capture native thread states (#1384)

* feat(thread state): capture and report native thread states in NDK crashes

* fix(native threads): native threads obey `Configuration.sendThreads`

* test(native threads): test scenarios for native thread reporting

* fix(native threads): map native thread-status codes to a descriptive string

* fix: updated CHANGELOG for native thread state reporting

* v5.14.0

* v5.14.0 - Changelog update

Co-authored-by: fractalwrench <[email protected]>
  • Loading branch information
lemnik and fractalwrench authored Sep 29, 2021
1 parent 644a944 commit 0125a41
Show file tree
Hide file tree
Showing 50 changed files with 753 additions and 70 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 5.14.0 (2021-09-29)

### Enhancements

* Capture and report thread state (running, sleeping, etc.) for Android Runtime and Native threads
[#1367](https://github.com/bugsnag/bugsnag-android/pull/1367)
[#1390](https://github.com/bugsnag/bugsnag-android/pull/1390)

## 5.13.0 (2021-09-22)

* Capture breadcrumbs for OkHttp network requests
Expand All @@ -18,7 +26,7 @@
[#1375](https://github.com/bugsnag/bugsnag-android/pull/1375)

* Use SystemClock.elapsedRealtime to track `app.durationInForeground`
[#1375](https://github.com/bugsnag/bugsnag-android/pull/1375)
[#1378](https://github.com/bugsnag/bugsnag-android/pull/1378)

## 5.12.0 (2021-08-26)

Expand Down
4 changes: 3 additions & 1 deletion bugsnag-android-core/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
<ID>LongParameterList:DeviceWithState.kt$DeviceWithState$( buildInfo: DeviceBuildInfo, jailbroken: Boolean?, id: String?, locale: String?, totalMemory: Long?, runtimeVersions: MutableMap&lt;String, Any>, /** * The number of free bytes of storage available on the device */ var freeDisk: Long?, /** * The number of free bytes of memory available on the device */ var freeMemory: Long?, /** * The orientation of the device when the event occurred: either portrait or landscape */ var orientation: String?, /** * The timestamp on the device when the event occurred */ var time: Date? )</ID>
<ID>LongParameterList:EventFilenameInfo.kt$EventFilenameInfo.Companion$( obj: Any, uuid: String = UUID.randomUUID().toString(), apiKey: String?, timestamp: Long = System.currentTimeMillis(), config: ImmutableConfig, isLaunching: Boolean? = null )</ID>
<ID>LongParameterList:EventStorageModule.kt$EventStorageModule$( contextModule: ContextModule, configModule: ConfigModule, dataCollectionModule: DataCollectionModule, bgTaskService: BackgroundTaskService, trackerModule: TrackerModule, systemServiceModule: SystemServiceModule, notifier: Notifier )</ID>
<ID>LongParameterList:StateEvent.kt$StateEvent.Install$( @JvmField val apiKey: String, @JvmField val autoDetectNdkCrashes: Boolean, @JvmField val appVersion: String?, @JvmField val buildUuid: String?, @JvmField val releaseStage: String?, @JvmField val lastRunInfoPath: String, @JvmField val consecutiveLaunchCrashes: Int )</ID>
<ID>LongParameterList:StateEvent.kt$StateEvent.Install$( @JvmField val apiKey: String, @JvmField val autoDetectNdkCrashes: Boolean, @JvmField val appVersion: String?, @JvmField val buildUuid: String?, @JvmField val releaseStage: String?, @JvmField val lastRunInfoPath: String, @JvmField val consecutiveLaunchCrashes: Int, @JvmField val sendThreads: ThreadSendPolicy )</ID>
<ID>LongParameterList:ThreadState.kt$ThreadState$( stackTraces: MutableMap&lt;java.lang.Thread, Array&lt;StackTraceElement>>, currentThread: java.lang.Thread, exc: Throwable?, isUnhandled: Boolean, projectPackages: Collection&lt;String>, logger: Logger )</ID>
<ID>MagicNumber:DefaultDelivery.kt$DefaultDelivery$299</ID>
<ID>MagicNumber:DefaultDelivery.kt$DefaultDelivery$429</ID>
<ID>MagicNumber:DefaultDelivery.kt$DefaultDelivery$499</ID>
<ID>MagicNumber:LastRunInfoStore.kt$LastRunInfoStore$3</ID>
<ID>MaxLineLength:EventSerializationTest.kt$EventSerializationTest.Companion$it.threads.add(Thread(5, "main", ThreadType.ANDROID, true, Thread.State.RUNNABLE, stacktrace, NoopLogger))</ID>
<ID>MaxLineLength:LastRunInfo.kt$LastRunInfo$return "LastRunInfo(consecutiveLaunchCrashes=$consecutiveLaunchCrashes, crashed=$crashed, crashedDuringLaunch=$crashedDuringLaunch)"</ID>
<ID>MaxLineLength:ThreadState.kt$ThreadState$Thread(thread.id, thread.name, ThreadType.ANDROID, errorThread, Thread.State.forThread(thread), stacktrace, logger)</ID>
<ID>ProtectedMemberInFinalClass:ConfigInternal.kt$ConfigInternal$protected val plugins = HashSet&lt;Plugin>()</ID>
<ID>ProtectedMemberInFinalClass:EventInternal.kt$EventInternal$protected fun isAnr(event: Event): Boolean</ID>
<ID>ProtectedMemberInFinalClass:EventInternal.kt$EventInternal$protected fun shouldDiscardClass(): Boolean</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
final ClientObservable clientObservable;
PluginClient pluginClient;

final Notifier notifier = new Notifier();
final Notifier notifier;

@Nullable
final LastRunInfo lastRunInfo;
Expand Down Expand Up @@ -117,6 +117,8 @@ public Client(@NonNull Context androidContext, @NonNull final Configuration conf
ContextModule contextModule = new ContextModule(androidContext);
appContext = contextModule.getCtx();

notifier = configuration.getNotifier();

connectivity = new ConnectivityCompat(appContext, new Function2<Boolean, String, Unit>() {
@Override
public Unit invoke(Boolean hasConnection, String networkState) {
Expand Down Expand Up @@ -233,7 +235,8 @@ public Unit invoke(Boolean hasConnection, String networkState) {
DeliveryDelegate deliveryDelegate,
LastRunInfoStore lastRunInfoStore,
LaunchCrashTracker launchCrashTracker,
ExceptionHandler exceptionHandler
ExceptionHandler exceptionHandler,
Notifier notifier
) {
this.immutableConfig = immutableConfig;
this.metadataState = metadataState;
Expand All @@ -255,6 +258,7 @@ public Unit invoke(Boolean hasConnection, String networkState) {
this.launchCrashTracker = launchCrashTracker;
this.lastRunInfo = null;
this.exceptionHandler = exceptionHandler;
this.notifier = notifier;
}

void registerLifecycleCallbacks() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ internal class ClientObservable : BaseObservable() {
conf.buildUuid,
conf.releaseStage,
lastRunInfoPath,
consecutiveLaunchCrashes
consecutiveLaunchCrashes,
conf.sendThreads
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ internal class ConfigInternal(var apiKey: String) : CallbackAware, MetadataAware
var projectPackages: Set<String> = emptySet()
var persistenceDirectory: File? = null

val notifier: Notifier = Notifier()

protected val plugins = HashSet<Plugin>()

override fun addOnError(onError: OnErrorCallback) = callbackState.addOnError(onError)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -981,4 +981,8 @@ public void addPlugin(@NonNull Plugin plugin) {
Set<Plugin> getPlugins() {
return impl.getPlugins();
}

Notifier getNotifier() {
return impl.getNotifier();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import java.io.IOException
*/
class Notifier @JvmOverloads constructor(
var name: String = "Android Bugsnag Notifier",
var version: String = "5.13.0",
var version: String = "5.14.0",
var url: String = "https://bugsnag.com"
) : JsonStream.Streamable {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ sealed class StateEvent { // JvmField allows direct field access optimizations
@JvmField val buildUuid: String?,
@JvmField val releaseStage: String?,
@JvmField val lastRunInfoPath: String,
@JvmField val consecutiveLaunchCrashes: Int
@JvmField val consecutiveLaunchCrashes: Int,
@JvmField val sendThreads: ThreadSendPolicy
) : StateEvent()

object DeliverPending : StateEvent()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.bugsnag.android;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.IOException;
import java.util.List;
Expand All @@ -19,9 +20,10 @@ public class Thread implements JsonStream.Streamable {
@NonNull String name,
@NonNull ThreadType type,
boolean errorReportingThread,
@NonNull Thread.State state,
@NonNull Stacktrace stacktrace,
@NonNull Logger logger) {
this.impl = new ThreadInternal(id, name, type, errorReportingThread, stacktrace);
this.impl = new ThreadInternal(id, name, type, errorReportingThread, state, stacktrace);
this.logger = logger;
}

Expand Down Expand Up @@ -81,6 +83,25 @@ public ThreadType getType() {
return impl.getType();
}

/**
* Sets the state of thread (from {@link java.lang.Thread})
*/
public void setState(@NonNull Thread.State threadState) {
if (threadState != null) {
impl.setState(threadState);
} else {
logNull("state");
}
}

/**
* Gets the state of the thread (from {@link java.lang.Thread})
*/
@NonNull
public Thread.State getState() {
return impl.getState();
}

/**
* Gets whether the thread was the thread that caused the event
*/
Expand Down Expand Up @@ -111,4 +132,79 @@ public List<Stackframe> getStacktrace() {
public void toStream(@NonNull JsonStream stream) throws IOException {
impl.toStream(stream);
}

/**
* The state of a reported {@link Thread}. These states correspond directly to
* {@link java.lang.Thread.State}, except for {@code UNKNOWN} which indicates that
* a state could not be captured or mapped.
*/
public enum State {
NEW("NEW"),
BLOCKED("BLOCKED"),
RUNNABLE("RUNNABLE"),
TERMINATED("TERMINATED"),
TIMED_WAITING("TIMED_WAITING"),
WAITING("WAITING"),
UNKNOWN("UNKNOWN");

private final String descriptor;

State(String descriptor) {
this.descriptor = descriptor;
}

@NonNull
public String getDescriptor() {
return descriptor;
}

@NonNull
public static State forThread(@NonNull java.lang.Thread thread) {
java.lang.Thread.State state = thread.getState();
return getState(state);
}

/**
* Lookup the {@code State} for a given {@link #getDescriptor() descriptor} code. Unlike
* {@link #valueOf(String) valueOf}, this method will return {@link #UNKNOWN} is no
* matching {@code State} constant can be found.
*
* @param descriptor a consistent descriptor of the state constant to lookup
* @return the requested {@link State} or {@link #UNKNOWN}
*/
@NonNull
public static State byDescriptor(@Nullable String descriptor) {
if (descriptor == null) {
return UNKNOWN;
}

for (State state : values()) {
if (state.getDescriptor().equals(descriptor)) {
return state;
}
}

return UNKNOWN;
}

@NonNull
private static State getState(java.lang.Thread.State state) {
switch (state) {
case NEW:
return NEW;
case BLOCKED:
return BLOCKED;
case RUNNABLE:
return RUNNABLE;
case TERMINATED:
return TERMINATED;
case TIMED_WAITING:
return TIMED_WAITING;
case WAITING:
return WAITING;
default:
return UNKNOWN;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class ThreadInternal internal constructor(
var name: String,
var type: ThreadType,
val isErrorReportingThread: Boolean,
var state: Thread.State,
stacktrace: Stacktrace
) : JsonStream.Streamable {

Expand All @@ -18,6 +19,7 @@ class ThreadInternal internal constructor(
writer.name("id").value(id)
writer.name("name").value(name)
writer.name("type").value(type.desc)
writer.name("state").value(state.descriptor)

writer.name("stacktrace")
writer.beginArray()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ internal class ThreadState @Suppress("LongParameterList") @JvmOverloads construc
if (trace != null) {
val stacktrace = Stacktrace(trace, projectPackages, logger)
val errorThread = thread.id == currentThreadId
Thread(thread.id, thread.name, ThreadType.ANDROID, errorThread, stacktrace, logger)
Thread(thread.id, thread.name, ThreadType.ANDROID, errorThread, Thread.State.forThread(thread), stacktrace, logger)
} else {
null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ public class ClientFacadeTest {
@Mock
ExceptionHandler exceptionHandler;

@Mock
Notifier notifier;

private Client client;
private InterceptingLogger logger;

Expand Down Expand Up @@ -118,7 +121,8 @@ public void setUp() {
deliveryDelegate,
lastRunInfoStore,
launchCrashTracker,
exceptionHandler
exceptionHandler,
notifier
);

// required fields for generating an event
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ internal class EventSerializationTest {
createEvent {
val stacktrace = Stacktrace(arrayOf(), emptySet(), NoopLogger)
it.threads.clear()
it.threads.add(Thread(5, "main", ThreadType.ANDROID, true, stacktrace, NoopLogger))
it.threads.add(Thread(5, "main", ThreadType.ANDROID, true, Thread.State.RUNNABLE, stacktrace, NoopLogger))
},

// threads included
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,15 @@ public void setUp() {
logger = new InterceptingLogger();
List<Stackframe> frames = Collections.emptyList();
stacktrace = new Stacktrace(frames);
thread = new Thread(1, "thread-2", ThreadType.ANDROID, false, stacktrace, logger);
thread = new Thread(
1,
"thread-2",
ThreadType.ANDROID,
false,
Thread.State.RUNNABLE,
stacktrace,
logger
);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
true,
Thread.State.RUNNABLE,
Stacktrace(
stacktrace,
emptySet(),
Expand All @@ -43,6 +44,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
false,
Thread.State.RUNNABLE,
Stacktrace(
stacktrace1,
emptySet(),
Expand Down Expand Up @@ -76,6 +78,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
true,
Thread.State.RUNNABLE,
trace,
NoopLogger
)
Expand All @@ -98,6 +101,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
false,
Thread.State.RUNNABLE,
trace,
NoopLogger
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"id": 5,
"name": "main",
"type": "android",
"state": "RUNNABLE",
"stacktrace": [],
"errorReportingThread": true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
"state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
"state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
"state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
"state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
Expand Down
Loading

0 comments on commit 0125a41

Please sign in to comment.