Skip to content

Commit b921766

Browse files
authored
feat(anr): Map Android thread states to Java states (#45585)
The depending on the Android os version, the sentry android sdk will either send the Java thread [states](https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.State.html) or the Android thread states (as a part of the ANRv2 implementation). Mapping the android states back to the six java ones based on [this](https://cs.android.com/android/platform/superproject/+/master:art/runtime/thread_state.h;l=28). closes #45693
1 parent bed30ee commit b921766

File tree

6 files changed

+104
-22
lines changed

6 files changed

+104
-22
lines changed

static/app/components/events/interfaces/threads/index.spec.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,5 +152,25 @@ describe('Threads', () => {
152152
userEvent.click(screen.getByText('ViewController.captureNSException (RUNNABLE)'));
153153
expect(screen.getByText('State')).toBeInTheDocument();
154154
});
155+
156+
it('maps android vm states to java vm states', () => {
157+
const threadsEntry = entries[1];
158+
threadsEntry.data.values[0].state = 'kWaitingPerformingGc';
159+
render(
160+
<Threads
161+
data={threadsEntry.data}
162+
projectSlug="project-id"
163+
event={event}
164+
hasHierarchicalGrouping={false}
165+
/>
166+
);
167+
168+
// kWaitingPerformingGc maps to WAITING
169+
expect(
170+
screen.getByText('ViewController.captureNSException (WAITING)')
171+
).toBeInTheDocument();
172+
userEvent.click(screen.getByText('ViewController.captureNSException (WAITING)'));
173+
expect(screen.getByText('State')).toBeInTheDocument();
174+
});
155175
});
156176
});

static/app/components/events/interfaces/threads/threadSelector/filterThreadInfo.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import {trimPackage} from 'sentry/components/events/interfaces/frame/utils';
2+
import {
3+
getMappedThreadState,
4+
ThreadStates,
5+
} from 'sentry/components/events/interfaces/threads/threadSelector/threadStates';
26
import {
37
EntryData,
48
Event,
@@ -17,7 +21,7 @@ type ThreadInfo = {
1721
crashedInfo?: EntryData;
1822
filename?: string;
1923
label?: string;
20-
state?: Thread['state'];
24+
state?: ThreadStates;
2125
};
2226

2327
function filterThreadInfo(
@@ -26,7 +30,7 @@ function filterThreadInfo(
2630
exception?: Required<ExceptionType>
2731
): ThreadInfo {
2832
const threadInfo: ThreadInfo = {};
29-
threadInfo.state = thread.state;
33+
threadInfo.state = getMappedThreadState(thread.state);
3034

3135
let stacktrace: StacktraceType | undefined = getThreadStacktrace(false, thread);
3236

static/app/components/events/interfaces/threads/threadSelector/index.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import partition from 'lodash/partition';
44

55
import DropdownAutoComplete from 'sentry/components/dropdownAutoComplete';
66
import DropdownButton from 'sentry/components/dropdownButton';
7+
import {getMappedThreadState} from 'sentry/components/events/interfaces/threads/threadSelector/threadStates';
78
import {t} from 'sentry/locale';
89
import {Event, ExceptionType, Thread} from 'sentry/types';
910
import {defined} from 'sentry/utils';
@@ -33,13 +34,19 @@ const ThreadSelector = ({
3334
onChange,
3435
fullWidth = false,
3536
}: Props) => {
36-
const hasThreadStates = threads.some(thread => defined(thread.state));
37+
const hasThreadStates = threads.some(thread =>
38+
defined(getMappedThreadState(thread.state))
39+
);
3740

3841
const getDropDownItem = (thread: Thread) => {
39-
const {label, filename, crashedInfo} = filterThreadInfo(event, thread, exception);
40-
const threadInfo = {label, filename, state: thread.state};
42+
const {label, filename, crashedInfo, state} = filterThreadInfo(
43+
event,
44+
thread,
45+
exception
46+
);
47+
const threadInfo = {label, filename, state};
4148
return {
42-
value: `#${thread.id}: ${thread.name} ${label} ${filename} (${thread.state})`,
49+
value: `#${thread.id}: ${thread.name} ${label} ${filename}`,
4350
threadInfo,
4451
thread,
4552
label: (
@@ -49,7 +56,6 @@ const ThreadSelector = ({
4956
name={thread.name}
5057
crashed={thread.crashed}
5158
crashedInfo={crashedInfo}
52-
state={thread.state}
5359
hasThreadStates={hasThreadStates}
5460
/>
5561
),

static/app/components/events/interfaces/threads/threadSelector/option.tsx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import styled from '@emotion/styled';
22

3+
import {ThreadStates} from 'sentry/components/events/interfaces/threads/threadSelector/threadStates';
34
import TextOverflow from 'sentry/components/textOverflow';
45
import {Tooltip} from 'sentry/components/tooltip';
56
import {IconFire} from 'sentry/icons';
67
import {t, tct} from 'sentry/locale';
7-
import {EntryData, Thread} from 'sentry/types';
8+
import {EntryData} from 'sentry/types';
89
import {ColorOrAlias} from 'sentry/utils/theme';
910

1011
import {Grid, GridCell} from './styles';
@@ -16,23 +17,15 @@ type Props = {
1617
crashed?: boolean;
1718
crashedInfo?: EntryData;
1819
name?: string | null;
19-
state?: Thread['state'];
2020
};
2121

2222
type ThreadInfo = {
2323
filename?: string;
2424
label?: string;
25+
state?: ThreadStates;
2526
};
2627

27-
const Option = ({
28-
id,
29-
details,
30-
name,
31-
crashed,
32-
crashedInfo,
33-
state,
34-
hasThreadStates,
35-
}: Props) => {
28+
const Option = ({id, details, name, crashed, crashedInfo, hasThreadStates}: Props) => {
3629
const label = details.label ?? `<${t('unknown')}>`;
3730
const optionName = name || `<${t('unknown')}>`;
3831

@@ -82,8 +75,8 @@ const Option = ({
8275
{hasThreadStates && (
8376
<GridCell>
8477
<InnerCell>
85-
<Tooltip title={state} position="top">
86-
<TextOverflow>{state}</TextOverflow>
78+
<Tooltip title={details.state} position="top">
79+
<TextOverflow>{details.state}</TextOverflow>
8780
</Tooltip>
8881
</InnerCell>
8982
</GridCell>

static/app/components/events/interfaces/threads/threadSelector/selectedOption.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import styled from '@emotion/styled';
22

3+
import {ThreadStates} from 'sentry/components/events/interfaces/threads/threadSelector/threadStates';
34
import TextOverflow from 'sentry/components/textOverflow';
45
import {t, tct} from 'sentry/locale';
56
import {space} from 'sentry/styles/space';
6-
import {Thread} from 'sentry/types';
77

88
type Props = {
99
details: ThreadInfo;
@@ -13,7 +13,7 @@ type Props = {
1313
type ThreadInfo = {
1414
filename?: string;
1515
label?: string;
16-
state?: Thread['state'];
16+
state?: ThreadStates;
1717
};
1818

1919
function getThreadLabel(details: ThreadInfo) {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import {defined} from 'sentry/utils';
2+
3+
export enum ThreadStates {
4+
RUNNABLE = 'RUNNABLE',
5+
TIMED_WAITING = 'TIMED_WAITING',
6+
BLOCKED = 'BLOCKED',
7+
WAITING = 'WAITING',
8+
NEW = 'NEW',
9+
TERMINATED = 'TERMINATED',
10+
}
11+
12+
type ThreadStatesMap = Record<string, ThreadStates>;
13+
14+
export const javaThreadStatesMap: ThreadStatesMap = {
15+
RUNNABLE: ThreadStates.RUNNABLE,
16+
TIMED_WAITING: ThreadStates.TIMED_WAITING,
17+
BLOCKED: ThreadStates.BLOCKED,
18+
WAITING: ThreadStates.WAITING,
19+
NEW: ThreadStates.NEW,
20+
TERMINATED: ThreadStates.TERMINATED,
21+
// Android VM thread states https://cs.android.com/android/platform/superproject/+/master:art/runtime/thread_state.h
22+
kTerminated: ThreadStates.TERMINATED, // Thread.run has returned, but Thread* still around
23+
kRunnable: ThreadStates.RUNNABLE, // runnable
24+
kTimedWaiting: ThreadStates.TIMED_WAITING, // in Object.wait() with a timeout
25+
kSleeping: ThreadStates.TIMED_WAITING, // in Thread.sleep()
26+
kBlocked: ThreadStates.BLOCKED, // blocked on a monitor
27+
kWaiting: ThreadStates.WAITING, // in Object.wait()
28+
kWaitingForLockInflation: ThreadStates.WAITING, // blocked inflating a thin-lock
29+
kWaitingForTaskProcessor: ThreadStates.WAITING, // blocked waiting for taskProcessor
30+
kWaitingForGcToComplete: ThreadStates.WAITING, // blocked waiting for GC
31+
kWaitingForCheckPointsToRun: ThreadStates.WAITING, // GC waiting for checkpoints to run
32+
kWaitingPerformingGc: ThreadStates.WAITING, // performing GC
33+
kWaitingForDebuggerSend: ThreadStates.WAITING, // blocked waiting for events to be sent
34+
kWaitingForDebuggerToAttach: ThreadStates.WAITING, // blocked waiting for debugger to attach
35+
kWaitingInMainDebuggerLoop: ThreadStates.WAITING, // blocking/reading/processing debugger events
36+
kWaitingForDebuggerSuspension: ThreadStates.WAITING, // waiting for debugger suspend all
37+
kWaitingForJniOnLoad: ThreadStates.WAITING, // waiting for execution of dlopen and JNI on load code
38+
kWaitingForSignalCatcherOutput: ThreadStates.WAITING, // waiting for signal catcher IO to complete
39+
kWaitingInMainSignalCatcherLoop: ThreadStates.WAITING, // blocking/reading/processing signals
40+
kWaitingForDeoptimization: ThreadStates.WAITING, // waiting for deoptimization suspend all
41+
kWaitingForMethodTracingStart: ThreadStates.WAITING, // waiting for method tracing to start
42+
kWaitingForVisitObjects: ThreadStates.WAITING, // waiting for visiting objects
43+
kWaitingForGetObjectsAllocated: ThreadStates.WAITING, // waiting for getting the number of allocated objects
44+
kWaitingWeakGcRootRead: ThreadStates.WAITING, // waiting on the GC to read a weak root
45+
kWaitingForGcThreadFlip: ThreadStates.WAITING, // waiting on the GC thread flip (CC collector) to finish
46+
kNativeForAbort: ThreadStates.WAITING, // checking other threads are not run on abort.
47+
kStarting: ThreadStates.NEW, // native thread started, not yet ready to run managed code
48+
kNative: ThreadStates.RUNNABLE, // running in a JNI native method
49+
kSuspended: ThreadStates.RUNNABLE, // suspended by GC or debugger
50+
};
51+
52+
export function getMappedThreadState(
53+
state: string | undefined | null
54+
): ThreadStates | undefined {
55+
if (defined(state)) {
56+
return javaThreadStatesMap[state];
57+
}
58+
return undefined;
59+
}

0 commit comments

Comments
 (0)