Commit 5da82d47 authored by Sebastian Kaspari's avatar Sebastian Kaspari
Browse files

Issue #1705 - lib-crash: Add database storage for crashes.

parent b6245d84
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ plugins {
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
    compileSdkVersion config.compileSdkVersion
@@ -32,6 +33,12 @@ android {
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    kapt {
        arguments {
            arg("room.schemaLocation", "$projectDir/schemas".toString())
        }
    }

    buildTypes {
        release {
            minifyEnabled false
@@ -51,6 +58,9 @@ dependencies {
    implementation project(':support-ktx')
    implementation project(':support-utils')

    implementation Dependencies.androidx_room_runtime
    kapt Dependencies.androidx_room_compiler

    // We only compile against Sentry, GeckoView, and Glean. It's up to the app to add those dependencies if it wants to
    // send crash reports to Socorro (GV) or Sentry.
    compileOnly Dependencies.thirdparty_sentry
+84 −0
Original line number Diff line number Diff line
{
  "formatVersion": 1,
  "database": {
    "version": 1,
    "identityHash": "c82f6a8cf8fbd6509f73a7d7d5ff97cd",
    "entities": [
      {
        "tableName": "crashes",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `stacktrace` TEXT NOT NULL, `created_at` INTEGER NOT NULL, PRIMARY KEY(`uuid`))",
        "fields": [
          {
            "fieldPath": "uuid",
            "columnName": "uuid",
            "affinity": "TEXT",
            "notNull": true
          },
          {
            "fieldPath": "stacktrace",
            "columnName": "stacktrace",
            "affinity": "TEXT",
            "notNull": true
          },
          {
            "fieldPath": "createdAt",
            "columnName": "created_at",
            "affinity": "INTEGER",
            "notNull": true
          }
        ],
        "primaryKey": {
          "columnNames": [
            "uuid"
          ],
          "autoGenerate": false
        },
        "indices": [],
        "foreignKeys": []
      },
      {
        "tableName": "reports",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `crashUuid` TEXT NOT NULL, `service_id` TEXT NOT NULL, `report_id` TEXT NOT NULL)",
        "fields": [
          {
            "fieldPath": "id",
            "columnName": "id",
            "affinity": "INTEGER",
            "notNull": false
          },
          {
            "fieldPath": "crashUuid",
            "columnName": "crashUuid",
            "affinity": "TEXT",
            "notNull": true
          },
          {
            "fieldPath": "serviceId",
            "columnName": "service_id",
            "affinity": "TEXT",
            "notNull": true
          },
          {
            "fieldPath": "reportId",
            "columnName": "report_id",
            "affinity": "TEXT",
            "notNull": true
          }
        ],
        "primaryKey": {
          "columnNames": [
            "id"
          ],
          "autoGenerate": true
        },
        "indices": [],
        "foreignKeys": []
      }
    ],
    "views": [],
    "setupQueries": [
      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c82f6a8cf8fbd6509f73a7d7d5ff97cd')"
    ]
  }
}
 No newline at end of file
+36 −0
Original line number Diff line number Diff line
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.lib.crash.db

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction

/**
 * Dao for saving and accessing crash related information.
 */
@Dao
internal interface CrashDao {
    /**
     * Inserts a crash into the database.
     */
    @Insert
    fun insertCrash(crash: CrashEntity): Long

    /**
     * Inserts a report to the database.
     */
    @Insert
    fun insertReport(report: ReportEntity): Long

    /**
     * Returns saved crashes with their reports.
     */
    @Transaction
    @Query("SELECT * FROM crashes ORDER BY created_at DESC")
    fun getCrashesWithReports(): LiveData<List<CrashWithReports>>
}
+40 −0
Original line number Diff line number Diff line
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.lib.crash.db

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

/**
 * Internal database for storing collections and their tabs.
 */
@Database(
    entities = [CrashEntity::class, ReportEntity::class],
    version = 1
)
internal abstract class CrashDatabase : RoomDatabase() {
    abstract fun crashDao(): CrashDao

    companion object {
        @Volatile private var instance: CrashDatabase? = null

        @Synchronized
        fun get(context: Context): CrashDatabase {
            instance?.let { return it }

            return Room.databaseBuilder(context, CrashDatabase::class.java, "crashes")
                // We are allowing main thread queries here since we need to write to disk blocking
                // in a crash event before the process gets shutdown. At this point the app already
                // crashed and temporarily blocking the UI thread is not that problematic anymore.
                .allowMainThreadQueries()
                .build()
                .also {
                    instance = it
                }
        }
    }
}
+54 −0
Original line number Diff line number Diff line
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.lib.crash.db

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import mozilla.components.lib.crash.Crash
import mozilla.components.support.base.ext.getStacktraceAsString

/**
 * Database entity modeling a crash that has happened.
 */
@Entity(
    tableName = "crashes"
)
internal data class CrashEntity(
    /**
     * Generated UUID for this crash.
     */
    @PrimaryKey
    @ColumnInfo(name = "uuid")
    var uuid: String,

    /**
     * The stacktrace of the crash (if this crash was caused by an exception/throwable): otherwise
     * a string describing the type of crash.
     */
    @ColumnInfo(name = "stacktrace")
    var stacktrace: String,

    /**
     * Timestamp (in milliseconds) of when the crash happened.
     */
    @ColumnInfo(name = "created_at")
    var createdAt: Long
)

internal fun Crash.toEntity(): CrashEntity {
    return CrashEntity(
        uuid = uuid,
        stacktrace = getStacktrace(),
        createdAt = System.currentTimeMillis()
    )
}

internal fun Crash.getStacktrace(): String {
    return when (this) {
        is Crash.NativeCodeCrash -> "<native crash>"
        is Crash.UncaughtExceptionCrash -> throwable.getStacktraceAsString()
    }
}
Loading