123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- import {render, screen} from 'sentry-test/reactTestingLibrary';
- import {textWithMarkupMatcher} from 'sentry-test/utils';
- import {
- analyzeFrameForRootCause,
- analyzeFramesForRootCause,
- } from 'sentry/components/events/interfaces/analyzeFrames';
- import type {Event, Frame} from 'sentry/types/event';
- import {EntryType, EventOrGroupType, LockType} from 'sentry/types/event';
- const makeEventWithFrames = (frames: Frame[]): Event => {
- const event: Event = {
- id: '020eb33f6ce64ed6adc60f8993535816',
- groupID: '68',
- eventID: '020eb33f6ce64ed6adc60f8993535816',
- projectID: '2',
- size: 3481,
- entries: [
- {
- data: {
- values: [
- {
- type: 'ZeroDivisionError',
- value: 'divided by 0',
- mechanism: null,
- threadId: null,
- module: '',
- stacktrace: {
- frames: [
- {
- filename: 'puma (3.12.6) lib/puma/thread_pool.rb',
- absPath: 'puma (3.12.6) lib/puma/thread_pool.rb',
- module: null,
- package: null,
- platform: null,
- instructionAddr: null,
- symbolAddr: null,
- function: 'block in spawn_thread',
- rawFunction: null,
- symbol: null,
- context: [],
- lineNo: 135,
- colNo: null,
- inApp: false,
- trust: null,
- vars: null,
- },
- ...frames,
- ],
- framesOmitted: null,
- registers: null,
- hasSystemFrames: true,
- },
- rawStacktrace: null,
- frames: null,
- },
- ],
- hasSystemFrames: true,
- excOmitted: null,
- },
- type: EntryType.EXCEPTION,
- },
- {
- data: {
- values: [
- {
- id: 13920,
- current: true,
- crashed: true,
- name: 'puma 002',
- stacktrace: null,
- rawStacktrace: null,
- state: 'WAITING',
- },
- ],
- },
- type: EntryType.THREADS,
- },
- ],
- dist: null,
- message: '',
- title: 'ZeroDivisionError: divided by 0',
- location: 'sentry/controllers/welcome_controller.rb',
- user: null,
- contexts: {},
- sdk: null,
- context: {},
- packages: {},
- type: EventOrGroupType.ERROR,
- metadata: {
- display_title_with_tree_label: false,
- filename: 'sentry/controllers/welcome_controller.rb',
- finest_tree_label: [
- {filebase: 'welcome_controller.rb', function: '/'},
- {filebase: 'welcome_controller.rb', function: 'index'},
- ],
- function: '/',
- type: 'ZeroDivisionError',
- value: 'divided by 0',
- },
- tags: [{key: 'level', value: 'error'}],
- platform: 'other',
- dateReceived: '2021-10-28T12:28:22.318469Z',
- errors: [],
- crashFile: null,
- culprit: 'sentry/controllers/welcome_controller.rb in /',
- dateCreated: '2021-10-28T12:28:22.318469Z',
- fingerprints: ['58f1f47bea5239ea25397888dc9253d1'],
- groupingConfig: {
- enhancements: 'eJybzDRxY25-UmZOqpWRgZGhroGJroHRBABbUQb_',
- id: 'mobile:2021-02-12',
- },
- release: null,
- userReport: null,
- sdkUpdates: [],
- nextEventID: null,
- previousEventID: null,
- occurrence: null,
- };
- return event;
- };
- describe('analyzeAnrFrames', function () {
- it('detects anr root cause', function () {
- const event = makeEventWithFrames([
- {
- filename: 'SharedPreferencesImpl.java',
- absPath: 'SharedPreferencesImpl.java',
- module: 'android.app.SharedPreferencesImpl$EditorImpl$1',
- package: null,
- platform: null,
- instructionAddr: null,
- symbolAddr: null,
- function: 'run',
- rawFunction: null,
- symbol: null,
- context: [],
- lineNo: 366,
- colNo: null,
- inApp: false,
- trust: null,
- vars: null,
- },
- ]);
- const rootCause = analyzeFramesForRootCause(event);
- expect(rootCause?.resources).toEqual(
- 'SharedPreferences.apply will save data on background thread only if it happens before the activity/service finishes. Switch to SharedPreferences.commit and move commit to a background thread.'
- );
- expect(rootCause?.culprit).toEqual(
- '/^android\\.app\\.SharedPreferencesImpl\\$EditorImpl\\$[0-9]/'
- );
- });
- it('regex matches function names', function () {
- const event = makeEventWithFrames([
- {
- filename: 'sqlite.SQLiteConnection',
- absPath: 'sqlite.SQLiteConnection',
- module: 'android.database.sqlite.SQLiteConnection',
- package: null,
- platform: null,
- instructionAddr: null,
- symbolAddr: null,
- function: 'nativeBindArgs',
- rawFunction: null,
- symbol: null,
- context: [],
- lineNo: 366,
- colNo: null,
- inApp: false,
- trust: null,
- vars: null,
- },
- ]);
- const rootCause = analyzeFramesForRootCause(event);
- expect(rootCause?.resources).toEqual(
- 'Database operations, such as querying, inserting, updating, or deleting data, can involve disk I/O, processing, and potentially long-running operations. Move database operations off the main thread to avoid this ANR.'
- );
- expect(rootCause?.culprit).toEqual('android.database.sqlite.SQLiteConnection');
- });
- it('picks anr root cause of the topmost frame', function () {
- const event = makeEventWithFrames([
- {
- filename: 'Instrumentation.java',
- absPath: 'Instrumentation.java',
- module: 'android.app.Instrumentation',
- package: null,
- platform: null,
- instructionAddr: null,
- symbolAddr: null,
- function: 'callApplicationOnCreate',
- rawFunction: null,
- symbol: null,
- context: [],
- lineNo: 1176,
- colNo: null,
- inApp: false,
- trust: null,
- vars: null,
- },
- {
- filename: 'SharedPreferencesImpl.java',
- absPath: 'SharedPreferencesImpl.java',
- module: 'android.app.SharedPreferencesImpl$EditorImpl$1',
- package: null,
- platform: null,
- instructionAddr: null,
- symbolAddr: null,
- function: 'run',
- rawFunction: null,
- symbol: null,
- context: [],
- lineNo: 366,
- colNo: null,
- inApp: false,
- trust: null,
- vars: null,
- },
- ]);
- const rootCause = analyzeFramesForRootCause(event);
- expect(rootCause?.resources).toEqual(
- 'SharedPreferences.apply will save data on background thread only if it happens before the activity/service finishes. Switch to SharedPreferences.commit and move commit to a background thread.'
- );
- expect(rootCause?.culprit).toEqual(
- '/^android\\.app\\.SharedPreferencesImpl\\$EditorImpl\\$[0-9]/'
- );
- });
- it('given lock address returns frame with matching lock address', function () {
- const frame1 = {
- filename: 'Instrumentation.java',
- absPath: 'Instrumentation.java',
- module: 'android.app.Instrumentation',
- package: null,
- platform: null,
- instructionAddr: null,
- symbolAddr: null,
- function: 'call',
- rawFunction: null,
- symbol: null,
- context: [],
- lineNo: 1176,
- colNo: null,
- inApp: false,
- trust: null,
- vars: null,
- lock: {
- type: LockType.BLOCKED,
- address: '0x08a8651c',
- package_name: 'io.sentry.samples',
- class_name: 'Monitor',
- thread_id: 12,
- },
- };
- const frame2 = {
- filename: 'MainActivity.java',
- absPath: 'MainActivity.java',
- module: 'com.example.MainActivity',
- package: null,
- platform: null,
- instructionAddr: null,
- symbolAddr: null,
- function: 'onCreate',
- rawFunction: null,
- symbol: null,
- context: [],
- lineNo: 128,
- colNo: null,
- inApp: false,
- trust: null,
- vars: null,
- lock: {
- type: LockType.BLOCKED,
- address: '0x07d7437b',
- package_name: 'java.lang',
- class_name: 'Object',
- thread_id: 7,
- },
- };
- const rootCause1 = analyzeFrameForRootCause(frame1, undefined, '<address>');
- expect(rootCause1).toBeNull();
- const rootCause2 = analyzeFrameForRootCause(frame2, undefined, '0x07d7437b');
- render(<div>{rootCause2?.resources}</div>);
- expect(
- screen.getByText(
- textWithMarkupMatcher(
- 'The main thread is blocked/waiting, trying to acquire lock 0x07d7437b (java.lang.Object) held by the suspect frame of this thread.'
- )
- )
- ).toBeInTheDocument();
- });
- it('when thread id is not provided, does not append "held by"', function () {
- const frame = {
- filename: 'MainActivity.java',
- absPath: 'MainActivity.java',
- module: 'com.example.MainActivity',
- package: null,
- platform: null,
- instructionAddr: null,
- symbolAddr: null,
- function: 'onCreate',
- rawFunction: null,
- symbol: null,
- context: [],
- lineNo: 128,
- colNo: null,
- inApp: false,
- trust: null,
- vars: null,
- lock: {
- type: LockType.BLOCKED,
- address: '0x07d7437b',
- package_name: 'java.lang',
- class_name: 'Object',
- },
- };
- const rootCause2 = analyzeFrameForRootCause(frame, undefined, '0x07d7437b');
- render(<div>{rootCause2?.resources}</div>);
- expect(
- screen.getByText(
- textWithMarkupMatcher(
- 'The main thread is blocked/waiting, trying to acquire lock 0x07d7437b (java.lang.Object) .'
- )
- )
- ).toBeInTheDocument();
- });
- it('given main thread is locked returns it as root cause', function () {
- const frame = {
- filename: 'MainActivity.java',
- absPath: 'MainActivity.java',
- module: 'com.example.MainActivity',
- package: null,
- platform: null,
- instructionAddr: null,
- symbolAddr: null,
- function: 'onCreate',
- rawFunction: null,
- symbol: null,
- context: [],
- lineNo: 128,
- colNo: null,
- inApp: false,
- trust: null,
- vars: null,
- lock: {
- type: LockType.BLOCKED,
- address: '0x08a1321b',
- package_name: 'java.lang',
- class_name: 'Object',
- thread_id: 7,
- },
- };
- const thread = {
- id: 13920,
- current: true,
- crashed: true,
- name: 'puma 002',
- stacktrace: null,
- rawStacktrace: null,
- state: 'BLOCKED',
- };
- const rootCause = analyzeFrameForRootCause(frame, thread);
- render(<div>{rootCause?.resources}</div>);
- expect(
- screen.getByText(
- textWithMarkupMatcher(
- 'The main thread is blocked/waiting, trying to acquire lock 0x08a1321b (java.lang.Object) held by the suspect frame of this thread.'
- )
- )
- ).toBeInTheDocument();
- });
- });
|