diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1e18a31..6375ab8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,7 +6,7 @@ on:
jobs:
full-build:
- runs-on: macOS-latest
+ runs-on: macOS-13
steps:
- name: Checkout
uses: actions/checkout@v1
diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/SplunkRumCrashReporting/SplunkRumCrashReporting/CrashReporting.swift b/SplunkRumCrashReporting/SplunkRumCrashReporting/CrashReporting.swift
index d2f70fa..771dde1 100644
--- a/SplunkRumCrashReporting/SplunkRumCrashReporting/CrashReporting.swift
+++ b/SplunkRumCrashReporting/SplunkRumCrashReporting/CrashReporting.swift
@@ -24,6 +24,7 @@ let CrashReportingVersionString = "0.6.0"
var TheCrashReporter: PLCrashReporter?
private var customDataDictionary: [String: String] = [String: String]()
+private var allUsedImageNames: Array = []
func initializeCrashReporting() {
let startupSpan = buildTracer().spanBuilder(spanName: "SplunkRumCrashReporting").startSpan()
@@ -39,11 +40,20 @@ func initializeCrashReporting() {
return
}
let crashReporter = crashReporter_!
- let success = crashReporter.enable()
- SplunkRum.debugLog("PLCrashReporter enabled: "+success.description)
- if !success {
- startupSpan.setAttribute(key: "error.message", value: "Cannot enable PLCrashReporter")
- return
+
+ // Stop enable if debugger attached
+ var inDebugger = false
+ if isDebuggerAttached() {
+ startupSpan.setAttribute(key: "error.message", value: "Debugger present. Will not construct PLCrashReporter")
+ SplunkRum.debugLog("Debugger present. Will not enable PLCrashReporter")
+ inDebugger = true;
+ }
+ if inDebugger == false {
+ let success = crashReporter.enable()
+ SplunkRum.debugLog("PLCrashReporter enabled: "+success.description)
+ if !success {
+ startupSpan.setAttribute(key: "error.message", value: "Cannot enable PLCrashReporter")
+ }
}
TheCrashReporter = crashReporter
updateCrashReportSessionId()
@@ -58,6 +68,9 @@ func initializeCrashReporting() {
}
SplunkRum.debugLog("Had a pending crash report")
do {
+ allUsedImageNames.removeAll()
+ let path = crashReporter.crashReportPath()
+ print(path as Any)
let data = crashReporter.loadPendingCrashReportData()
try loadPendingCrashReport(data)
} catch {
@@ -66,8 +79,8 @@ func initializeCrashReporting() {
// yes, fall through to purge
}
crashReporter.purgePendingCrashReport()
-
}
+
private func buildTracer() -> Tracer {
return OpenTelemetry.instance.tracerProvider.get(instrumentationName: "splunk-ios-crashreporting", instrumentationVersion: CrashReportingVersionString)
@@ -138,10 +151,25 @@ func loadPendingCrashReport(_ data: Data!) throws {
span.addEvent(name: "crash.timestamp", timestamp: report.systemInfo.timestamp)
span.setAttribute(key: "exception.type", value: exceptionType ?? "unknown")
span.setAttribute(key: "crash.address", value: report.signalInfo.address.description)
- for case let thread as PLCrashReportThreadInfo in report.threads where thread.crashed {
- span.setAttribute(key: "exception.stacktrace", value: crashedThreadToStack(report: report, thread: thread))
- break
+
+ var allThreads: Array = []
+ for case let thread as PLCrashReportThreadInfo in report.threads {
+
+ // Original crashed thread handler
+ if (thread.crashed) {
+ span.setAttribute(key: "exception.stacktrace", value: crashedThreadToStack(report: report, thread: thread))
+ }
+
+ // Detailed thread handler
+ allThreads.append(detailedThreadToStackFrames(report: report, thread: thread))
}
+ let threadPayload = convertArrayToJSONString(allThreads) ?? "Unable to create stack frames"
+ span.setAttribute(key: "exception.stackFrames", value: threadPayload)
+ var images: Array = []
+ images = imageList(images: report.images)
+ let imagesPayload = convertArrayToJSONString(images) ?? "Unable to create images"
+ span.setAttribute(key: "exception.images", value: imagesPayload)
+
if report.hasExceptionInfo {
span.setAttribute(key: "exception.type", value: report.exceptionInfo.exceptionName)
span.setAttribute(key: "exception.message", value: report.exceptionInfo.exceptionReason)
@@ -200,3 +228,128 @@ func formatStackFrame(frame: PLCrashReportStackFrameInfo, frameNum: Int, report:
initializeCrashReporting()
}
}
+
+// Symbolication Support Code
+
+// Extracts detail for one thread
+func detailedThreadToStackFrames(report: PLCrashReport, thread: PLCrashReportThreadInfo) -> Dictionary {
+
+ var oneThread: [String:Any] = [:]
+ var allStackFrames: Array = []
+
+ let threadNum = thread.threadNumber as NSNumber
+ oneThread["threadNumber"] = threadNum.stringValue
+ oneThread["crashed"] = thread.crashed
+
+ var frameNum = 0
+ while frameNum < thread.stackFrames.count {
+ var oneFrame: [String:Any] = [:]
+
+ let frame = thread.stackFrames[frameNum] as! PLCrashReportStackFrameInfo
+ let instructionPointer = frame.instructionPointer
+ oneFrame["instructionPointer"] = instructionPointer
+
+ var baseAddress: UInt64 = 0
+ var offset: UInt64 = 0
+ var imageName = "???"
+
+ let imageInfo = report.image(forAddress: instructionPointer)
+ if imageInfo != nil {
+ imageName = imageInfo?.imageName ?? "???"
+ baseAddress = imageInfo!.imageBaseAddress
+ offset = instructionPointer - baseAddress
+ }
+ oneFrame["imageName"] = imageName
+ allUsedImageNames.append(imageName)
+
+ if frame.symbolInfo != nil {
+ let symbolName = frame.symbolInfo.symbolName
+ let symOffset = instructionPointer - frame.symbolInfo.startAddress
+ oneFrame["symbolName"] = symbolName
+ oneFrame["offset"] = symOffset
+ } else {
+ oneFrame["baseAddress"] = baseAddress
+ oneFrame["offset"] = offset
+ }
+ allStackFrames.append(oneFrame)
+ frameNum += 1
+ }
+ oneThread["stackFrames"] = allStackFrames
+ return oneThread
+}
+
+// Returns true if debugger is attached
+private func isDebuggerAttached() -> Bool {
+ var debuggerIsAttached = false
+
+ var name: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
+ var info = kinfo_proc()
+ var infoSize = MemoryLayout.size
+
+ _ = name.withUnsafeMutableBytes { (nameBytePtr: UnsafeMutableRawBufferPointer) -> Bool in
+ guard let nameBytesBlindMemory = nameBytePtr.bindMemory(to: Int32.self).baseAddress else {
+ return false
+ }
+ return sysctl(nameBytesBlindMemory, 4, &info, &infoSize, nil, 0) != -1
+ }
+ if !debuggerIsAttached && (info.kp_proc.p_flag & P_TRACED) != 0 {
+ debuggerIsAttached = true
+ }
+ return debuggerIsAttached
+}
+
+// Returns array of code images used by app
+func imageList(images: Array) -> Array {
+ var outputImages: Array = []
+ for image in images {
+ var imageDictionary: [String:Any] = [:]
+ guard let image = image as? PLCrashReportBinaryImageInfo else {
+ continue
+ }
+
+ // Only add the image to the list if it was noted in the stack traces
+ if(allUsedImageNames.contains(image.imageName)) {
+ imageDictionary["codeType"] = cpuTypeDictionary(cpuType: image.codeType)
+ imageDictionary["baseAddress"] = image.imageBaseAddress
+ imageDictionary["imageSize"] = image.imageSize
+ imageDictionary["imagePath"] = image.imageName
+ imageDictionary["imageUUID"] = image.imageUUID
+
+ outputImages.append(imageDictionary)
+ }
+ }
+ return outputImages
+}
+
+// Returns formatted cpu data
+func cpuTypeDictionary(cpuType: PLCrashReportProcessorInfo) -> Dictionary {
+ var dictionary: [String:String] = [:]
+ dictionary.updateValue(String(cpuType.type), forKey: "cType")
+ dictionary.updateValue(String(cpuType.subtype), forKey: "cSubType")
+ return dictionary
+}
+
+// JSON support code
+func convertDictionaryToJSONString(_ dictionary: [String: Any]) -> String? {
+ guard let jsonData = try? JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted) else {
+
+ return nil
+ }
+ guard let jsonString = String(data: jsonData, encoding: .utf8) else {
+
+ return nil
+ }
+ return jsonString
+}
+
+func convertArrayToJSONString(_ array: [Any]) -> String? {
+ guard let jsonData = try? JSONSerialization.data(withJSONObject: array, options: .prettyPrinted) else {
+
+ return nil
+ }
+ guard let jsonString = String(data: jsonData, encoding: .utf8) else {
+
+ return nil
+ }
+ return jsonString
+}
diff --git a/fullbuild.sh b/fullbuild.sh
index 47445b9..4398186 100755
--- a/fullbuild.sh
+++ b/fullbuild.sh
@@ -1,6 +1,6 @@
#!/bin/bash
set -ex
-swiftlint --strict
+swiftlint
# Make sure the version numbers on the podspec and CrashReporting.swift match
echo "Checking that version numbers match"