forked from shadowfacts/Tusker
Fix wrong logs getting sent with crash reports
This commit is contained in:
parent
71a2029752
commit
67718d8fe4
|
@ -2483,7 +2483,7 @@
|
||||||
repositoryURL = "https://github.com/microsoft/plcrashreporter";
|
repositoryURL = "https://github.com/microsoft/plcrashreporter";
|
||||||
requirement = {
|
requirement = {
|
||||||
kind = upToNextMinorVersion;
|
kind = upToNextMinorVersion;
|
||||||
minimumVersion = 1.8.0;
|
minimumVersion = 1.11.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
|
@ -55,6 +55,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
private func setupCrashReporter() {
|
private func setupCrashReporter() {
|
||||||
let config = PLCrashReporterConfig(signalHandlerType: .BSD, symbolicationStrategy: .all)
|
let config = PLCrashReporterConfig(signalHandlerType: .BSD, symbolicationStrategy: .all)
|
||||||
AppDelegate.crashReporter = PLCrashReporter(configuration: config)
|
AppDelegate.crashReporter = PLCrashReporter(configuration: config)
|
||||||
|
let callbacksPtr = UnsafeMutablePointer<PLCrashReporterCallbacks>.allocate(capacity: 1)
|
||||||
|
callbacksPtr.pointee = PLCrashReporterCallbacks(version: 0, context: nil, handleSignal: crashCallback)
|
||||||
|
// setCrashCallbacks, it's poorly imported into Swift
|
||||||
|
AppDelegate.crashReporter.setCrash(callbacksPtr)
|
||||||
|
|
||||||
if AppDelegate.crashReporter.hasPendingCrashReport(),
|
if AppDelegate.crashReporter.hasPendingCrashReport(),
|
||||||
let data = try? AppDelegate.crashReporter.loadPendingCrashReportDataAndReturnError(),
|
let data = try? AppDelegate.crashReporter.loadPendingCrashReportDataAndReturnError(),
|
||||||
|
@ -114,3 +118,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
UIApplication.shared.requestSceneSessionDestruction(scene.session, options: nil)
|
UIApplication.shared.requestSceneSessionDestruction(scene.session, options: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func crashCallback(info: UnsafeMutablePointer<siginfo_t>!, uap: UnsafeMutablePointer<ucontext_t>!, context: UnsafeMutableRawPointer?) {
|
||||||
|
// read logs on the main queue in order to block further user interaction while we're crashing
|
||||||
|
// since getting the log data can take upwards of a second, even for very few entries
|
||||||
|
// synchronously dispatch because the callback can't return until we're ready to abort
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
Logging.writeDataForCrash()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,12 +17,13 @@ struct Logging {
|
||||||
static func getLogData() -> Data? {
|
static func getLogData() -> Data? {
|
||||||
do {
|
do {
|
||||||
let store = try OSLogStore(scope: .currentProcessIdentifier)
|
let store = try OSLogStore(scope: .currentProcessIdentifier)
|
||||||
// past hour
|
// do the filtering ourself, passing position/predicate into getEntries is far slower (priority inversion, I think)
|
||||||
let position = store.position(date: Date().addingTimeInterval(-60 * 60))
|
let entries = try store.getEntries()
|
||||||
let entries = try store.getEntries(at: position, matching: NSPredicate(format: "subsystem = %@", Bundle.main.bundleIdentifier!))
|
|
||||||
var data = Data()
|
var data = Data()
|
||||||
|
let subsystem = Bundle.main.bundleIdentifier!
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
guard let entry = entry as? OSLogEntryLog else {
|
guard let entry = entry as? OSLogEntryLog,
|
||||||
|
entry.subsystem == subsystem else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
data.append(contentsOf: entry.date.formatted(.iso8601).utf8)
|
data.append(contentsOf: entry.date.formatted(.iso8601).utf8)
|
||||||
|
@ -39,4 +40,23 @@ struct Logging {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func writeDataForCrash() {
|
||||||
|
guard let data = getLogData(),
|
||||||
|
let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let timestamp = ISO8601DateFormatter().string(from: Date())
|
||||||
|
let url = cacheDir.appendingPathComponent("Tusker-\(timestamp).log", isDirectory: false)
|
||||||
|
do {
|
||||||
|
try data.write(to: url)
|
||||||
|
UserDefaults.standard.set(url, forKey: "lastCrashLog")
|
||||||
|
} catch {
|
||||||
|
// if we can't write the data, oh well, we just won't have logs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func logURLForLastCrash() -> URL? {
|
||||||
|
return UserDefaults.standard.url(forKey: "lastCrashLog")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import CrashReporter
|
||||||
class CrashReporterViewController: IssueReporterViewController {
|
class CrashReporterViewController: IssueReporterViewController {
|
||||||
|
|
||||||
private let report: PLCrashReport
|
private let report: PLCrashReport
|
||||||
|
private var logURL: URL?
|
||||||
|
|
||||||
override var preamble: String {
|
override var preamble: String {
|
||||||
"Tusker has detected that it crashed the last time it was running. You can email the report to the developer or skip sending and continue to the app. You may review the report below before sending.\n\nIf you choose to send the report, please include any additional details about what you were doing prior to the crash that may be pertinent."
|
"Tusker has detected that it crashed the last time it was running. You can email the report to the developer or skip sending and continue to the app. You may review the report below before sending.\n\nIf you choose to send the report, please include any additional details about what you were doing prior to the crash that may be pertinent."
|
||||||
|
@ -27,6 +28,7 @@ class CrashReporterViewController: IssueReporterViewController {
|
||||||
|
|
||||||
private init(report: PLCrashReport, dismiss: @escaping () -> Void) {
|
private init(report: PLCrashReport, dismiss: @escaping () -> Void) {
|
||||||
self.report = report
|
self.report = report
|
||||||
|
self.logURL = Logging.logURLForLastCrash()
|
||||||
let reportText = PLCrashReportTextFormatter.stringValue(for: report, with: PLCrashReportTextFormatiOS)!
|
let reportText = PLCrashReportTextFormatter.stringValue(for: report, with: PLCrashReportTextFormatiOS)!
|
||||||
let timestamp = ISO8601DateFormatter().string(from: report.systemInfo.timestamp)
|
let timestamp = ISO8601DateFormatter().string(from: report.systemInfo.timestamp)
|
||||||
let reportFilename = "Tusker-crash-\(timestamp).crash"
|
let reportFilename = "Tusker-crash-\(timestamp).crash"
|
||||||
|
@ -44,4 +46,19 @@ class CrashReporterViewController: IssueReporterViewController {
|
||||||
navigationItem.title = NSLocalizedString("Crash Detected", comment: "crash reporter title")
|
navigationItem.title = NSLocalizedString("Crash Detected", comment: "crash reporter title")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func getLogData() async -> (Data, String)? {
|
||||||
|
guard let logURL,
|
||||||
|
let data = try? Data(contentsOf: logURL) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return (data, logURL.lastPathComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func finishedReport() {
|
||||||
|
super.finishedReport()
|
||||||
|
if let logURL {
|
||||||
|
try? FileManager.default.removeItem(at: logURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ class IssueReporterViewController: UIViewController {
|
||||||
|
|
||||||
let reportText: String
|
let reportText: String
|
||||||
let reportFilename: String
|
let reportFilename: String
|
||||||
private let dismiss: () -> Void
|
private let doDismiss: () -> Void
|
||||||
|
|
||||||
var preamble: String {
|
var preamble: String {
|
||||||
"Tusker has encountered an error. You can email a report to the developer. You may review the report below before sending.\n\nIf you choose to send the report, please include any additional details about what you were doing prior that may be pertinent."
|
"Tusker has encountered an error. You can email a report to the developer. You may review the report below before sending.\n\nIf you choose to send the report, please include any additional details about what you were doing prior that may be pertinent."
|
||||||
|
@ -43,7 +43,7 @@ class IssueReporterViewController: UIViewController {
|
||||||
init(reportText: String, reportFilename: String, dismiss: @escaping () -> Void) {
|
init(reportText: String, reportFilename: String, dismiss: @escaping () -> Void) {
|
||||||
self.reportText = reportText
|
self.reportText = reportText
|
||||||
self.reportFilename = reportFilename
|
self.reportFilename = reportFilename
|
||||||
self.dismiss = dismiss
|
self.doDismiss = dismiss
|
||||||
|
|
||||||
self.logDataTask = Task(priority: .userInitiated) {
|
self.logDataTask = Task(priority: .userInitiated) {
|
||||||
return await withCheckedContinuation({ continuation in
|
return await withCheckedContinuation({ continuation in
|
||||||
|
@ -118,6 +118,7 @@ class IssueReporterViewController: UIViewController {
|
||||||
|
|
||||||
@IBAction func sendReportTouchUpInside(_ sender: Any) {
|
@IBAction func sendReportTouchUpInside(_ sender: Any) {
|
||||||
updateSendReportButtonColor(lightened: false, animate: true)
|
updateSendReportButtonColor(lightened: false, animate: true)
|
||||||
|
sendReportButton.isEnabled = false
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
let composeVC = MFMailComposeViewController()
|
let composeVC = MFMailComposeViewController()
|
||||||
|
@ -128,12 +129,13 @@ class IssueReporterViewController: UIViewController {
|
||||||
let data = reportText.data(using: .utf8)!
|
let data = reportText.data(using: .utf8)!
|
||||||
composeVC.addAttachmentData(data, mimeType: "text/plain", fileName: reportFilename)
|
composeVC.addAttachmentData(data, mimeType: "text/plain", fileName: reportFilename)
|
||||||
|
|
||||||
if let logData = await logDataTask.value {
|
if let (logData, name) = await getLogData() {
|
||||||
let timestamp = ISO8601DateFormatter().string(from: Date())
|
composeVC.addAttachmentData(logData, mimeType: "text/plain", fileName: name)
|
||||||
composeVC.addAttachmentData(logData, mimeType: "text/plain", fileName: "Tusker-\(timestamp).log")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.present(composeVC, animated: true)
|
self.present(composeVC, animated: true)
|
||||||
|
|
||||||
|
sendReportButton.isEnabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +149,19 @@ class IssueReporterViewController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func cancelPressed(_ sender: Any) {
|
@IBAction func cancelPressed(_ sender: Any) {
|
||||||
dismiss()
|
self.finishedReport()
|
||||||
|
doDismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLogData() async -> (Data, String)? {
|
||||||
|
guard let data = await logDataTask.value else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let timestamp = ISO8601DateFormatter().string(from: Date())
|
||||||
|
return (data, "Tusker-\(timestamp).log")
|
||||||
|
}
|
||||||
|
|
||||||
|
func finishedReport() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -158,7 +172,8 @@ extension IssueReporterViewController: MFMailComposeViewControllerDelegate {
|
||||||
if result == .cancelled {
|
if result == .cancelled {
|
||||||
// don't dismiss ourself, to allowe the user to send the report a different way
|
// don't dismiss ourself, to allowe the user to send the report a different way
|
||||||
} else {
|
} else {
|
||||||
self.dismiss()
|
self.finishedReport()
|
||||||
|
self.doDismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue