Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify symbolicator API #71

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 0 additions & 31 deletions ETTrace/ETTraceRunner/ResponseModels/ResponseModel.swift

This file was deleted.

60 changes: 5 additions & 55 deletions ETTrace/ETTraceRunner/RunnerHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import Foundation
import Peertalk
import CommunicationFrame
import Swifter
import JSONWrapper
import ETModels
import Symbolicator

class RunnerHelper {
Expand Down Expand Up @@ -75,42 +73,15 @@ class RunnerHelper {

print("Stopped recording, symbolicating...")

let responseData = try JSONDecoder().decode(ResponseModel.self, from: receivedData)

let isSimulator = responseData.isSimulator
var arch = responseData.cpuType.lowercased()
if arch == "arm64e" {
arch = " arm64e"
} else {
arch = ""
}
var osBuild = responseData.osBuild
osBuild.removeAll(where: { !$0.isLetter && !$0.isNumber })

let threadIds = responseData.threads.keys
let threads = threadIds.map { responseData.threads[$0]!.stacks }
let symbolicator = StackSymbolicator(isSimulator: isSimulator, dSymsDir: dsyms, osBuild: osBuild, osVersion: responseData.osVersion, arch: arch, verbose: verbose)
let flamegraphs = FlamegraphGenerator.generate(
events: responseData.events,
threads: threads,
loadedLibraries: responseData.libraryInfo.loadedLibraries,
symbolicator: symbolicator)
let flamegraphs = try FlamegraphGenerator.generate(data: receivedData, dSymsDir: dsyms, verbose: verbose)
let outputUrl = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)

var mainThreadData: Data?
for (threadId, symbolicationResult) in zip(threadIds, flamegraphs) {
let thread = responseData.threads[threadId]!
let flamegraph = createFlamegraphForThread(symbolicationResult.0, symbolicationResult.1, thread, responseData)

let outJsonData = JSONWrapper.toData(flamegraph)!

if thread.name == "Main Thread" {
if verbose {
try symbolicationResult.2.write(toFile: "output.folded", atomically: true, encoding: .utf8)
}
mainThreadData = outJsonData
for (name, threadId, data) in flamegraphs {
if name == "Main Thread" {
mainThreadData = data
}
try saveFlamegraph(outJsonData, outputUrl, threadId)
try saveFlamegraph(data, outputUrl, threadId)
}

guard let mainThreadData else {
Expand All @@ -128,27 +99,6 @@ class RunnerHelper {
print("Results saved to \(outputUrl)")
}

private func createFlamegraphForThread(_ flamegraphNodes: FlameNode, _ eventTimes: [Double], _ thread: Thread, _ responseData: ResponseModel) -> Flamegraph {
let threadNode = ThreadNode(nodes: flamegraphNodes, threadName: thread.name)

let events = zip(responseData.events, eventTimes).map { (event, t) in
return FlamegraphEvent(name: event.span,
type: event.type.rawValue,
time: t)
}

let libraries = responseData.libraryInfo.loadedLibraries.reduce(into: [String:UInt64]()) { partialResult, library in
partialResult[library.path] = library.loadAddress
}

return Flamegraph(osBuild: responseData.osBuild,
device: responseData.device,
isSimulator: responseData.isSimulator,
libraries: libraries,
events: events,
threadNodes: [threadNode])
}

func startLocalServer(_ data: Data) throws {
server = HttpServer()

Expand Down
61 changes: 57 additions & 4 deletions ETTrace/Symbolicator/FlamegraphGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,68 @@

import Foundation
import ETModels
import JSONWrapper

public enum FlamegraphGenerator {

public static func generate(events: [Event], threads: [[Stack]], loadedLibraries: [LoadedLibrary], symbolicator: StackSymbolicator) -> [(FlameNode, [Double], String)] {
let syms = symbolicator.symbolicate(threads.flatMap { $0 }, loadedLibraries)
return threads.map { generateFlamegraphs(events: events, stacks: $0, syms: syms) }
public static func generate(
data: Data,
dSymsDir: String?,
verbose: Bool) throws -> [(name: String, threadId: String, flamegraph: Data)]
{
let responseData = try JSONDecoder().decode(ResponseModel.self, from: data)
let isSimulator = responseData.isSimulator
var arch = responseData.cpuType.lowercased()
if arch == "arm64e" {
arch = " arm64e"
} else {
arch = ""
}
var osBuild = responseData.osBuild
osBuild.removeAll(where: { !$0.isLetter && !$0.isNumber })

let threadIds = responseData.threads.keys
let threads = threadIds.map { responseData.threads[$0]!.stacks }
let symbolicator = StackSymbolicator(isSimulator: isSimulator, dSymsDir: dSymsDir, osBuild: osBuild, osVersion: responseData.osVersion, arch: arch, verbose: verbose)

let syms = symbolicator.symbolicate(threads.flatMap { $0 }, responseData.libraryInfo.loadedLibraries)
let flamegraphs = threads.map { generateFlameNode(events: responseData.events, stacks: $0, syms: syms) }
var result = [(String, String, Data)]()
for (threadId, symbolicationResult) in zip(threadIds, flamegraphs) {
let thread = responseData.threads[threadId]!
let flamegraph = createFlamegraphForThread(symbolicationResult.0, symbolicationResult.1, thread, responseData)
if verbose && thread.name == "Main Thread" {
try symbolicationResult.2.write(toFile: "output.folded", atomically: true, encoding: .utf8)
}

let outJsonData = JSONWrapper.toData(flamegraph)!
result.append((thread.name, threadId, outJsonData))
}
return result
}

private static func generateFlamegraphs(
private static func createFlamegraphForThread(_ flamegraphNodes: FlameNode, _ eventTimes: [Double], _ thread: Thread, _ responseData: ResponseModel) -> Flamegraph {
let threadNode = ThreadNode(nodes: flamegraphNodes, threadName: thread.name)

let events = zip(responseData.events, eventTimes).map { (event, t) in
return FlamegraphEvent(name: event.span,
type: event.type.rawValue,
time: t)
}

let libraries = responseData.libraryInfo.loadedLibraries.reduce(into: [String:UInt64]()) { partialResult, library in
partialResult[library.path] = library.loadAddress
}

return Flamegraph(osBuild: responseData.osBuild,
device: responseData.device,
isSimulator: responseData.isSimulator,
libraries: libraries,
events: events,
threadNodes: [threadNode])
}

private static func generateFlameNode(
events: [Event],
stacks: [Stack],
syms: SymbolicationResult) -> (FlameNode, [Double], String)
Expand Down
42 changes: 32 additions & 10 deletions ETTrace/Symbolicator/Models.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,46 @@

import Foundation

public struct Event: Decodable {
public let span: String
public let type: EventType
public let time: Double
struct ResponseModel: Decodable {
let osBuild: String
let osVersion: String?
let isSimulator: Bool
let libraryInfo: LibraryInfo
let cpuType: String
let device: String
let events: [Event]
let threads: [String: Thread]
}

public enum EventType: String, Decodable {
struct LibraryInfo: Decodable {
let relativeTime: Double
let mainThreadId: Int
let loadedLibraries: [LoadedLibrary]
}

struct Thread: Decodable {
let name: String
let stacks: [Stack]
}

struct Event: Decodable {
let span: String
let type: EventType
let time: Double
}

enum EventType: String, Decodable {
case start
case stop
}

public struct LoadedLibrary: Decodable, Equatable, Hashable {
public let path: String
public let loadAddress: UInt64
public let uuid: String
struct LoadedLibrary: Decodable, Equatable, Hashable {
let path: String
let loadAddress: UInt64
let uuid: String
}

public struct Stack: Decodable {
struct Stack: Decodable {
let stack: [UInt64]
let time: Double
}
4 changes: 2 additions & 2 deletions ETTrace/Symbolicator/Symbolicator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct Address {

typealias SymbolicationResult = [UInt64: (String, String, Bool)]

public class StackSymbolicator {
class StackSymbolicator {
var formatSymbolCache: [String: String] = [:]

let isSimulator: Bool
Expand All @@ -26,7 +26,7 @@ public class StackSymbolicator {
let arch: String
let verbose: Bool

public init(isSimulator: Bool, dSymsDir: String?, osBuild: String, osVersion: String?, arch: String, verbose: Bool) {
init(isSimulator: Bool, dSymsDir: String?, osBuild: String, osVersion: String?, arch: String, verbose: Bool) {
self.isSimulator = isSimulator
self.dSymsDir = dSymsDir
self.osBuild = osBuild
Expand Down
4 changes: 1 addition & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ let package = Package(
path: "ETTrace/Tracer",
publicHeadersPath: "Public"
),
.target(name: "Symbolicator", dependencies: ["ETModels"], path: "ETTrace/Symbolicator"),
.target(name: "Symbolicator", dependencies: ["ETModels", "JSONWrapper",], path: "ETTrace/Symbolicator"),
.target(
name: "CommunicationFrame",
path: "ETTrace/CommunicationFrame",
Expand All @@ -63,8 +63,6 @@ let package = Package(
name: "ETTraceRunner",
dependencies: [
"CommunicationFrame",
"JSONWrapper",
"ETModels",
"Symbolicator",
.product(name: "Peertalk", package: "peertalk"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
Expand Down
Loading