```
├── .git-blame-ignore-revs
├── .github/
├── release.yml (100 tokens)
├── workflows/
├── main.yml (300 tokens)
├── pull_request.yml (400 tokens)
├── pull_request_label.yml (100 tokens)
├── .gitignore
├── .licenseignore (100 tokens)
├── .spi.yml
├── .swift-format (500 tokens)
├── .unacceptablelanguageignore
├── Benchmarks/
├── Benchmarks/
├── ConversionBenchmarks/
├── Benchmarks.swift (900 tokens)
├── SampleWorkload/
├── ArrayAppend.swift (300 tokens)
├── Package.swift (300 tokens)
├── FuzzTesting/
├── .gitignore
├── Package.swift (100 tokens)
├── Sources/
├── FuzzELF/
├── main.swift (600 tokens)
├── do_build.sh (200 tokens)
├── LICENSE.txt (2.3k tokens)
├── Misc/
├── Dockerfile (100 tokens)
├── Resources/
├── 20250927-linux-hummingbird-hello.png
├── 20250927-macos-hummingbird-hello.png
├── vendored-backtracing_root.version
├── vendored-core-symbolication.version
├── NOTICE.txt (400 tokens)
├── Package.swift (1600 tokens)
├── Protos/
├── generate-protos.sh (200 tokens)
├── profile.proto (2.1k tokens)
├── README.md (1600 tokens)
├── Scripts/
├── debug-llvm-symbolizer
├── smoke-in-docker.sh (100 tokens)
├── smoke-test.sh (200 tokens)
├── vendor-core-symbolication-symbolizer.sh (1000 tokens)
├── vendor-native-symbolizer.sh (1900 tokens)
├── Sources/
├── CProfileRecorderDarwin/
├── empty.c (100 tokens)
├── include/
├── CProfileRecorderDarwin.h (100 tokens)
├── swipr-core-symbolication.h (1800 tokens)
├── CProfileRecorderSampler/
├── asserts.h (200 tokens)
├── common.h (300 tokens)
├── fp_unwinder.c (800 tokens)
├── fp_unwinder.h (300 tokens)
├── include/
├── CSampler.h (200 tokens)
├── interface.h (300 tokens)
├── os_dep.h (500 tokens)
├── os_dep_darwin.c (2.7k tokens)
├── os_dep_darwin.h (200 tokens)
├── os_dep_dispatch.h (400 tokens)
├── os_dep_linux.c (2.2k tokens)
├── os_dep_linux.h (200 tokens)
├── os_dep_pthread.h (700 tokens)
├── sampler.c (2.5k tokens)
├── sampler.h (200 tokens)
├── CProfileRecorderSwiftELF/
├── empty.c (200 tokens)
├── include/
├── CProfileRecorderSwiftELF.h (300 tokens)
├── swipr-dwarf.h (7.3k tokens)
├── swipr-eh_frame_hdr.h (700 tokens)
├── swipr-elf.h (7.5k tokens)
├── ProfileRecorder/
├── Documentation.docc/
├── Documentation.md (300 tokens)
├── ProfileRecorderSampler.md (100 tokens)
├── ProfileRecorderSampler.swift (1500 tokens)
├── ProfileRecorderHelpers/
├── Helpers.swift (800 tokens)
├── ProfileRecorderPprofFormat/
├── profile.pb.swift (7.2k tokens)
├── ProfileRecorderSampleConversion/
├── Coders.swift (1100 tokens)
├── CoreSymbolication/
├── CoreSymbolication.swift (3k tokens)
├── CoreSymbolicationHelper.swift (600 tokens)
├── CoreSymbolicationSymboliser.swift (900 tokens)
├── FakeSymbolizer.swift (300 tokens)
├── HelperFunctions.swift (600 tokens)
├── NIOTimeAmount+PrettyPrint.swift (600 tokens)
├── NativeELFSymboliser/
├── ByteSwapping.swift (600 tokens)
├── Dwarf.swift (17.2k tokens)
├── Elf.swift (13.8k tokens)
├── Image.swift (1200 tokens)
├── ImageSource.swift (3.2k tokens)
├── MemoryReader.swift (800 tokens)
├── Registers.swift (2.2k tokens)
├── Utils.swift (1000 tokens)
├── OutputFormatRendering/
├── FlamegraphCollapsed.swift (400 tokens)
├── PerfScript.swift (600 tokens)
├── Pprof.swift (1400 tokens)
├── RendererTypes.swift (300 tokens)
├── ProfileRecorderAddtions+Types.swift (300 tokens)
├── ProfileRecorderSampleConverter.swift (2.9k tokens)
├── SampleAggregator.swift (600 tokens)
├── Sampler+Conversion.swift (1200 tokens)
├── Symboliser.swift (3.3k tokens)
├── Types.swift (200 tokens)
├── ProfileRecorderServer/
├── Documentation.docc/
├── Documentation.md (300 tokens)
├── Reference/
├── ProfileRecorderServer-ServerInfo.md
├── ProfileRecorderServer.md (500 tokens)
├── ProfileRecorderServerConfiguration.md (100 tokens)
├── Visualizing-Traces.md (400 tokens)
├── Server.swift (7.5k tokens)
├── swipr-mini-demo/
├── Main.swift (1700 tokens)
├── swipr-sample-conv/
├── LLVMJSONOutputParserHandler.swift (1100 tokens)
├── LLVMOutputParserHandler.swift (800 tokens)
├── LLVMStackFrameEncoderHandler.swift (400 tokens)
├── LLVMSymbolizer.swift (1600 tokens)
├── MainFile.swift (1500 tokens)
├── Tests/
├── ProfileRecorderSampleConversionTests/
├── CachedSymbolizerTests.swift (700 tokens)
├── FlamegraphCollapsedTests.swift (1200 tokens)
├── Helpers.swift (300 tokens)
├── PerfScriptTests.swift (1000 tokens)
├── PprofTests.swift (2.1k tokens)
├── SampleConversionTests.swift (900 tokens)
├── ProfileRecorderServerTests/
├── ProfileRecorderConfigurationTests.swift (1700 tokens)
├── ProfileRecorderServerTests.swift (1300 tokens)
├── ProfileRecorderTests/
├── ProfileRecorderTests.swift (2.5k tokens)
```
## /.git-blame-ignore-revs
```git-blame-ignore-revs path="/.git-blame-ignore-revs"
# format everything (#22)
a56ead018e57b7ef0ba8755dcf178f147745bfb2
```
## /.github/release.yml
```yml path="/.github/release.yml"
changelog:
categories:
- title: SemVer Major
labels:
- ⚠️ semver/major
- title: SemVer Minor
labels:
- 🆕 semver/minor
- title: SemVer Patch
labels:
- 🔨 semver/patch
- title: Other Changes
labels:
- semver/none
```
## /.github/workflows/main.yml
```yml path="/.github/workflows/main.yml"
name: Main
permissions:
contents: read
on:
push:
branches: [main]
schedule:
- cron: "0 8,20 * * *"
jobs:
unit-tests:
name: Unit tests
uses: apple/swift-nio/.github/workflows/unit_tests.yml@main
with:
linux_5_10_arguments_override: "--explicit-target-dependency-import-check error"
linux_6_0_arguments_override: "--explicit-target-dependency-import-check error"
linux_6_1_arguments_override: "--explicit-target-dependency-import-check error"
linux_6_2_arguments_override: "--explicit-target-dependency-import-check error"
linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error"
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error"
cxx-interop:
name: Cxx interop
uses: apple/swift-nio/.github/workflows/cxx_interop.yml@main
static-sdk:
name: Static SDK
uses: apple/swift-nio/.github/workflows/static_sdk.yml@main
macos-tests:
name: macOS tests
uses: apple/swift-nio/.github/workflows/macos_tests.yml@main
with:
runner_pool: nightly
build_scheme: swift-profile-recorder
release-builds:
name: Release builds
uses: apple/swift-nio/.github/workflows/release_builds.yml@main
```
## /.github/workflows/pull_request.yml
```yml path="/.github/workflows/pull_request.yml"
name: PR
permissions:
contents: read
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
soundness:
name: Soundness
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
with:
license_header_check_project_name: "Swift Profile Recorder"
python_lint_check_enabled: false
docs_check_enabled: false
format_check_enabled: true
swift-test:
name: Run tests
uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main
with:
enable_linux_static_sdk_build: true
linux_pre_build_command: "apt-get update && apt-get -yy install libc6-dbg && apt-get -yy install libc6-prof || true"
linux_exclude_swift_versions: "[{\"swift_version\": \"5.9\"}]]"
linux_os_versions: "[\"noble\", \"jammy\"]"
linux_env_vars: |
why_PROTOBUF_NO_PROTOC=we_do_not_need_protoc_and_macOS_builders_do_not_have_network
PROTOBUF_NO_PROTOC=true
DEBIAN_FRONTEND=noninteractive
why_LD_LIBRARY_PATH=old_Ubuntus_need_this_to_enable_frame_pointers,_all_fine_from_24.04
LD_LIBRARY_PATH=/lib/libc6-prof/x86_64-linux-gnu:/lib/libc6-prof/aarch64-linux-gnu
macos_env_vars: |
why_PROTOBUF_NO_PROTOC=we_do_not_need_protoc_and_macOS_builders_do_not_have_network
PROTOBUF_NO_PROTOC=true
enable_macos_checks: true
windows_swift_versions: "[]"
enable_windows_checks: false
enable_windows_docker: false
cxx-interop:
name: Cxx interop
uses: apple/swift-nio/.github/workflows/cxx_interop.yml@main
static-sdk:
name: Static SDK
# Workaround https://github.com/nektos/act/issues/1875
uses: apple/swift-nio/.github/workflows/static_sdk.yml@main
macos-tests:
name: macOS tests
uses: apple/swift-nio/.github/workflows/macos_tests.yml@main
with:
runner_pool: general
build_scheme: swift-profile-recorder-Package
env_vars: "{\"PROTOBUF_NO_PROTOC\": \"true\"}"
release-builds:
name: Release builds
uses: apple/swift-nio/.github/workflows/release_builds.yml@main
```
## /.github/workflows/pull_request_label.yml
```yml path="/.github/workflows/pull_request_label.yml"
name: PR label
permissions:
contents: read
on:
pull_request:
types: [labeled, unlabeled, opened, reopened, synchronize]
jobs:
semver-label-check:
name: Semantic version label check
runs-on: ubuntu-latest
timeout-minutes: 1
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Check for Semantic Version label
uses: apple/swift-nio/.github/actions/pull_request_semver_label_checker@main
```
## /.gitignore
```gitignore path="/.gitignore"
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.swiftpm
Package.resolved
```
## /.licenseignore
```licenseignore path="/.licenseignore"
.gitignore
**/.gitignore
.licenseignore
.gitattributes
.git-blame-ignore-revs
.mailfilter
.mailmap
.spi.yml
.swift-format
.editorconfig
.github/*
*.md
*.png
*.txt
*.yml
*.yaml
*.json
Package.swift
**/Package.swift
Package@swift-*.swift
**/Package@swift-*.swift
Package.resolved
**/Package.resolved
Makefile
*.modulemap
**/*.modulemap
**/*.docc/*
*.xcprivacy
**/*.xcprivacy
*.symlink
**/*.symlink
Dockerfile
**/Dockerfile
Snippets/*
Misc/*
Misc/Resources/*
Scripts/*
Sources/ProfileRecorderPprofFormat/profile.pb.swift
.unacceptablelanguageignore
```
## /.spi.yml
```yml path="/.spi.yml"
version: 1
builder:
configs:
- documentation_targets:
- ProfileRecorderServer
- Swift Profile Recorder
```
## /.swift-format
```swift-format path="/.swift-format"
{
"version" : 1,
"indentation" : {
"spaces" : 4
},
"tabWidth" : 4,
"fileScopedDeclarationPrivacy" : {
"accessLevel" : "private"
},
"spacesBeforeEndOfLineComments" : 1,
"spacesAroundRangeFormationOperators" : false,
"indentConditionalCompilationBlocks" : false,
"indentSwitchCaseLabels" : false,
"lineBreakAroundMultilineExpressionChainComponents" : false,
"lineBreakBeforeControlFlowKeywords" : false,
"lineBreakBeforeEachArgument" : true,
"lineBreakBeforeEachGenericRequirement" : true,
"lineLength" : 120,
"maximumBlankLines" : 1,
"respectsExistingLineBreaks" : true,
"prioritizeKeepingFunctionOutputTogether" : true,
"rules" : {
"AllPublicDeclarationsHaveDocumentation" : false,
"AlwaysUseLiteralForEmptyCollectionInit" : false,
"AlwaysUseLowerCamelCase" : false,
"AmbiguousTrailingClosureOverload" : true,
"BeginDocumentationCommentWithOneLineSummary" : false,
"DoNotUseSemicolons" : true,
"DontRepeatTypeInStaticProperties" : false,
"FileScopedDeclarationPrivacy" : true,
"FullyIndirectEnum" : true,
"GroupNumericLiterals" : true,
"IdentifiersMustBeASCII" : true,
"NeverForceUnwrap" : false,
"NeverUseForceTry" : false,
"NeverUseImplicitlyUnwrappedOptionals" : false,
"NoAccessLevelOnExtensionDeclaration" : true,
"NoAssignmentInExpressions" : true,
"NoBlockComments" : false,
"NoCasesWithOnlyFallthrough" : true,
"NoEmptyTrailingClosureParentheses" : true,
"NoLabelsInCasePatterns" : true,
"NoLeadingUnderscores" : false,
"NoParensAroundConditions" : true,
"NoVoidReturnOnFunctionSignature" : true,
"OmitExplicitReturns" : false,
"OneCasePerLine" : true,
"OneVariableDeclarationPerLine" : true,
"OnlyOneTrailingClosureArgument" : true,
"OrderedImports" : false,
"ReplaceForEachWithForLoop" : true,
"ReturnVoidInsteadOfEmptyTuple" : true,
"UseEarlyExits" : false,
"UseExplicitNilCheckInConditions" : false,
"UseLetInEveryBoundCaseVariable" : false,
"UseShorthandTypeNames" : true,
"UseSingleLinePropertyGetter" : false,
"UseSynthesizedInitializer" : false,
"UseTripleSlashForDocumentationComments" : true,
"UseWhereClausesInForLoops" : false,
"ValidateDocumentationComments" : false
}
}
```
## /.unacceptablelanguageignore
```unacceptablelanguageignore path="/.unacceptablelanguageignore"
Scripts/*
```
## /Benchmarks/Benchmarks/ConversionBenchmarks/Benchmarks.swift
```swift path="/Benchmarks/Benchmarks/ConversionBenchmarks/Benchmarks.swift"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Benchmark
import Foundation
import Logging
import NIO
import ProfileRecorder
import _ProfileRecorderSampleConversion
import SampleWorkload
let benchmarks = {
Benchmark.defaultConfiguration = .init(
warmupIterations: 1,
maxDuration: .seconds(3)
)
let outputDir = "./output/conversion"
let arrayAppendProfilePath = outputDir.appending("/array-append.swipr")
LoggingSystem.bootstrap { label in
var handler = StreamLogHandler.standardError(label: label)
handler.logLevel = .warning
return handler
}
let logger = Logger(label: "benchmark")
Benchmark.setup = {
try FileManager.default.createDirectory(
at: URL(fileURLWithPath: outputDir),
withIntermediateDirectories: true
)
// create once a sample
if !FileManager.default.fileExists(atPath: arrayAppendProfilePath) {
Task {
let array = ArrayAppend(blocking: true, threads: 20)
array.run()
}
try await ProfileRecorderSampler.sharedInstance.requestSamples(
outputFilePath: arrayAppendProfilePath,
failIfFileExists: false,
count: 1000,
timeBetweenSamples: .milliseconds(1)
)
}
}
Benchmark("ArrayAppend FakeSymbolizer (PerfScript)") { benchmark in
let converter = ProfileRecorderSampleConverter(
config: SymbolizerConfiguration.default,
threadPool: .singleton,
group: .singletonMultiThreadedEventLoopGroup,
renderer: PerfScriptOutputRenderer(),
symbolizer: _ProfileRecorderFakeSymbolizer()
)
for _ in benchmark.scaledIterations {
try await converter.convert(
inputRawProfileRecorderFormatPath: arrayAppendProfilePath,
outputPath: outputDir.appending("/array-append.fake.perf"),
format: .pprofSymbolized,
logger: logger
)
}
}
Benchmark("ArrayAppend convert to PerfScript") { benchmark in
let symbolizer = ProfileRecorderSampler._makeDefaultSymbolizer()
try await NIOThreadPool.singleton.runIfActive {
try symbolizer.start()
}
let converter = ProfileRecorderSampleConverter(
config: SymbolizerConfiguration.default,
threadPool: .singleton,
group: .singletonMultiThreadedEventLoopGroup,
renderer: PerfScriptOutputRenderer(),
symbolizer: symbolizer
)
for _ in benchmark.scaledIterations {
try await converter.convert(
inputRawProfileRecorderFormatPath: arrayAppendProfilePath,
outputPath: outputDir.appending("/array-append.perf"),
format: .pprofSymbolized,
logger: logger
)
}
try? await NIOThreadPool.singleton.runIfActive {
try symbolizer.shutdown()
}
}
Benchmark("ArrayAppend convert to pprof") { benchmark in
let symbolizer = ProfileRecorderSampler._makeDefaultSymbolizer()
try await NIOThreadPool.singleton.runIfActive {
try symbolizer.start()
}
let converter = ProfileRecorderSampleConverter(
config: SymbolizerConfiguration.default,
threadPool: .singleton,
group: .singletonMultiThreadedEventLoopGroup,
renderer: PprofOutputRenderer(),
symbolizer: symbolizer
)
for _ in benchmark.scaledIterations {
try await converter.convert(
inputRawProfileRecorderFormatPath: arrayAppendProfilePath,
outputPath: outputDir.appending("/array-append.pprof"),
format: .pprofSymbolized,
logger: logger
)
}
try? await NIOThreadPool.singleton.runIfActive {
try symbolizer.shutdown()
}
}
}
```
## /Benchmarks/Benchmarks/SampleWorkload/ArrayAppend.swift
```swift path="/Benchmarks/Benchmarks/SampleWorkload/ArrayAppend.swift"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Benchmark
import Dispatch
import Foundation
public struct ArrayAppend {
var iterations: Int
var blocking: Bool
var threads: Int
public init(
iterations: Int = 4_000_000,
blocking: Bool = false,
threads: Int = 1
) {
self.iterations = iterations
self.blocking = blocking
self.threads = threads
}
private func runIteration() {
var xs: [Int] = []
for x in 0..<iterations {
xs.append(x)
}
blackHole(xs)
if blocking {
Thread.sleep(forTimeInterval: 0.2)
}
}
public func run() {
if threads > 1 {
let g = DispatchGroup()
let queue = DispatchQueue.global()
for _ in 0..<threads {
queue.async(group: g) {
self.runIteration()
}
}
g.wait()
} else {
runIteration()
}
}
}
```
## /Benchmarks/Package.swift
```swift path="/Benchmarks/Package.swift"
// swift-tools-version:5.10
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import PackageDescription
let package = Package(
name: "benchmarks",
platforms: [
.macOS(.v14)
],
dependencies: [
.package(path: "../"),
.package(url: "https://github.com/ordo-one/package-benchmark.git", from: "1.29.0"),
],
targets: [
.target(
name: "SampleWorkload",
dependencies: [
.product(name: "Benchmark", package: "package-benchmark")
],
path: "Benchmarks/SampleWorkload",
),
.executableTarget(
name: "ConversionBenchmarks",
dependencies: [
"SampleWorkload",
.product(name: "Benchmark", package: "package-benchmark"),
.product(name: "ProfileRecorder", package: "swift-profile-recorder"),
.product(name: "_ProfileRecorderSampleConversion", package: "swift-profile-recorder"),
],
path: "Benchmarks/ConversionBenchmarks",
plugins: [
.plugin(name: "BenchmarkPlugin", package: "package-benchmark")
]
),
]
)
```
## /FuzzTesting/.gitignore
```gitignore path="/FuzzTesting/.gitignore"
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.swiftpm
Package.resolved
crash-*
```
## /FuzzTesting/Package.swift
```swift path="/FuzzTesting/Package.swift"
// swift-tools-version:5.3
import PackageDescription
let package = Package(
name: "FuzzTesting",
platforms: [
.macOS(.v11)
],
dependencies: [
.package(path: "..")
],
targets: [
.target(
name: "FuzzELF",
dependencies: [
.product(name: "_ProfileRecorderSampleConversion", package: "swift-profile-recorder")
]
)
]
)
```
## /FuzzTesting/Sources/FuzzELF/main.swift
```swift path="/FuzzTesting/Sources/FuzzELF/main.swift"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import _ProfileRecorderSampleConversion
@_cdecl("LLVMFuzzerTestOneInput")
public func fuzzELF(_ start: UnsafeRawPointer, _ count: Int) -> CInt {
let buffer = UnsafeRawBufferPointer(start: start, count: count)
let source = ImageSource(unowned: buffer, isMappedImage: false)
if let image = try? Elf32Image(source: source) {
exerciseElfImage(image)
}
if let image = try? Elf64Image(source: source) {
exerciseElfImage(image)
}
return 0
}
private func exerciseElfImage<Traits: ElfTraits>(_ image: ElfImage<Traits>) {
_ = image.uuid
_ = image.debugLinkCRC
_ = image.ehFrameInfo
_ = image.imageName
_ = image.getSection(".text")
_ = image.getSection(".symtab")
_ = image.getSection(".strtab")
_ = image.getSection(".debug_info")
_ = image.getSection(".debug_line")
_ = image.getDebugLink()
_ = image.getDebugAltLink()
_ = image.getSectionAsString(".comment")
for _ in image.notes {}
// Symbol table: construction and merge
if let symtab = ElfSymbolTable(image: image) {
if let symtab2 = ElfSymbolTable(image: image) {
_ = symtab.merged(with: symtab2)
}
}
// Collect addresses from the fuzzed image's own headers to exercise lookup
// paths with values that actually fall within the image's address ranges.
var addresses: [UInt64] = [0]
for phdr in image.programHeaders where phdr.p_type == .internal_SWIPR_PT_LOAD {
let vaddr = UInt64(phdr.p_vaddr)
addresses.append(vaddr)
addresses.append(vaddr &+ UInt64(phdr.p_memsz) / 2)
}
if let sections = image.sectionHeaders {
for shdr in sections where shdr.sh_addr != 0 {
addresses.append(UInt64(shdr.sh_addr))
}
}
let symtab = ElfSymbolTable(image: image)
for addr in addresses {
_ = symtab?.lookupSymbol(address: Traits.Address(truncatingIfNeeded: addr))
_ = image.lookupSymbol(address: Traits.Address(truncatingIfNeeded: addr))
_ = try? image.sourceLocation(for: Traits.Address(truncatingIfNeeded: addr))
_ = image.inlineCallSites(at: Traits.Address(truncatingIfNeeded: addr))
}
// DWARF section access
_ = image.getDwarfSection(.debugInfo)
_ = image.getDwarfSection(.debugLine)
_ = image.getDwarfSection(.debugAbbrev)
_ = image.getDwarfSection(.debugStr)
_ = image.getDwarfSection(.debugRanges)
_ = image.getDwarfSection(.debugRngLists)
_ = image.getDwarfSection(.debugAddr)
_ = image.getDwarfSection(.debugStrOffsets)
_ = image.getDwarfSection(.debugLineStr)
}
```
## /FuzzTesting/do_build.sh
```sh path="/FuzzTesting/do_build.sh"
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the Swift Profile Recorder open source project
##
## Copyright (c) 2026 Apple Inc. and the Swift Profile Recorder project authors
## Licensed under Apache License v2.0
##
## See LICENSE.txt for license information
## See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
##
## SPDX-License-Identifier: Apache-2.0
##
##===----------------------------------------------------------------------===##
set -euo pipefail
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$here"
config="${1:-release}"
echo "Building: ${config}"
swift build -Xswiftc "-sanitize=fuzzer,address" -Xswiftc -parse-as-library -Xswiftc -enable-testing -c "${config}" --product FuzzELF
echo "Binary: $here/.build/${config}/FuzzELF"
```
## /LICENSE.txt
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
## /Misc/Dockerfile
``` path="/Misc/Dockerfile"
FROM swift:5.10
RUN apt-get -y update \
&& apt-get -yy install llvm vim lsof valgrind linux-tools-generic siege curl strace \
&& rm -f /usr/local/bin/perf \
&& perf=$(find /usr/lib/linux-tools/*/perf | head -1) \
&& ln -s "$perf" /usr/local/bin/perf
```
## /Misc/Resources/20250927-linux-hummingbird-hello.png
Binary file available at https://raw.githubusercontent.com/apple/swift-profile-recorder/refs/heads/main/Misc/Resources/20250927-linux-hummingbird-hello.png
## /Misc/Resources/20250927-macos-hummingbird-hello.png
Binary file available at https://raw.githubusercontent.com/apple/swift-profile-recorder/refs/heads/main/Misc/Resources/20250927-macos-hummingbird-hello.png
## /Misc/vendored-backtracing_root.version
```version path="/Misc/vendored-backtracing_root.version"
swift-DEVELOPMENT-SNAPSHOT-2026-02-06-a-178-ge77a8ba8eca
```
## /Misc/vendored-core-symbolication.version
```version path="/Misc/vendored-core-symbolication.version"
swift-DEVELOPMENT-SNAPSHOT-2025-08-18-a-87-ga14dbd2fd06
```
## /NOTICE.txt
The Swift Profile Recorder Project
=================
Please visit the Swift Profile Recorder web site for more information:
* https://github.com/apple/swift-profile-recorder
Copyright 2021 The Swift Profile Recorder Project
The Swift Profile Recorder Project licenses this file to you under the Apache License,
version 2.0 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at:
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Also, please refer to each LICENSE.<component>.txt file, which is located in
the 'license' directory of the distribution file, for the license terms of the
components that this product depends on.
-------------------------------------------------------------------------------
This product contains LLVM's libunwind.
* LICENSE (Apache License v2.0, with LLVM Exception):
* https://github.com/llvm/llvm-project/blob/main/libunwind/LICENSE.TXT
* HOMEPAGE:
* https://github.com/llvm/llvm-project/blob/main/libunwind
---
This product contains the ELF/DWARF parsing library of Swift Backtrace.
* LICENSE (Apache License v2.0, with Runtime Library Exception to the Apache 2.0 License):
* https://github.com/swiftlang/swift/blob/main/LICENSE.txt
* HOMEPAGE:
* https://github.com/swiftlang/swift
---
This product contains the profile.proto from Google's pprof package.
* LICENSE (Apache License v2.0, with Runtime Library Exception to the Apache 2.0 License):
* https://github.com/google/pprof/blob/main/LICENSE
* HOMEPAGE:
* https://github.com/google/pprof
## /Package.swift
```swift path="/Package.swift"
// swift-tools-version:5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "swift-profile-recorder",
platforms: [
// supported
// Linux
.macOS(.v11),
// not supported, listed to make compilation work
.iOS(.v14),
.watchOS(.v7),
.tvOS(.v14),
],
products: [
.library(name: "ProfileRecorder", targets: ["ProfileRecorder"]),
.library(name: "ProfileRecorderServer", targets: ["ProfileRecorderServer"]),
.executable(name: "swipr-sample-conv", targets: ["swipr-sample-conv"]),
// _ProfileRecorderSampleConversion is not part of public API, internal benchmark use
.library(name: "_ProfileRecorderSampleConversion", targets: ["_ProfileRecorderSampleConversion"]),
],
dependencies: {
var packageDependencies: [Package.Dependency] = [
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.1"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.99.0"),
.package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.24.1"),
.package(url: "https://github.com/apple/swift-protobuf.git", from: "1.31.1"),
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.25.2"),
]
#if compiler(>=6.2)
packageDependencies.append(
.package(url: "https://github.com/apple/swift-configuration.git", from: "1.0.0")
)
#endif
return packageDependencies
}(),
targets: [
// MARK: - Executables
.executableTarget(
name: "swipr-mini-demo",
dependencies: [
"ProfileRecorder",
"ProfileRecorderServer",
"ProfileRecorderHelpers",
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "NIO", package: "swift-nio"),
.product(name: "Logging", package: "swift-log"),
]
),
.target(
name: "_ProfileRecorderSampleConversion",
dependencies: [
"ProfileRecorder",
"CProfileRecorderSwiftELF",
"CProfileRecorderDarwin",
"ProfileRecorderPprofFormat",
"ProfileRecorderHelpers",
.product(name: "NIO", package: "swift-nio"),
.product(name: "Logging", package: "swift-log"),
.product(name: "NIOExtras", package: "swift-nio-extras"),
],
path: "Sources/ProfileRecorderSampleConversion"
),
.executableTarget(
name: "swipr-sample-conv",
dependencies: [
"CProfileRecorderSwiftELF",
"_ProfileRecorderSampleConversion",
"ProfileRecorderHelpers",
"ProfileRecorder",
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "NIOFoundationCompat", package: "swift-nio"),
.product(name: "Logging", package: "swift-log"),
]
),
// MARK: - Library targets
.target(
name: "ProfileRecorder",
dependencies: [
"ProfileRecorderHelpers",
.targetItem(
name: "CProfileRecorderSampler",
// We currently only support Linux but we compile just fine on macOS too.
// Let's be a little conservative and allow-list macOS & Linux.
condition: .when(platforms: [.macOS, .linux])
),
.product(name: "NIO", package: "swift-nio"),
.product(name: "_NIOFileSystem", package: "swift-nio"),
]
),
.target(
name: "ProfileRecorderHelpers",
dependencies: [
.product(name: "NIO", package: "swift-nio"),
.product(name: "_NIOFileSystem", package: "swift-nio"),
]
),
.target(
name: "ProfileRecorderPprofFormat",
dependencies: [
"ProfileRecorderHelpers",
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
]
),
.target(
name: "ProfileRecorderServer",
dependencies: {
var profileRecorderServerTargetDeps: [Target.Dependency] = [
"ProfileRecorderHelpers",
.product(name: "NIO", package: "swift-nio"),
.product(name: "NIOHTTP1", package: "swift-nio"),
.product(name: "NIOFoundationEssentialsCompat", package: "swift-nio"),
.product(name: "_NIOFileSystem", package: "swift-nio"),
.product(name: "Logging", package: "swift-log"),
"ProfileRecorder",
"_ProfileRecorderSampleConversion",
"ProfileRecorderPprofFormat",
]
#if compiler(>=6.2)
profileRecorderServerTargetDeps.append(.product(name: "Configuration", package: "swift-configuration"))
#endif
return profileRecorderServerTargetDeps
}()
),
.target(
name: "CProfileRecorderSwiftELF",
dependencies: []
),
.target(
name: "CProfileRecorderDarwin",
dependencies: []
),
.target(
name: "CProfileRecorderSampler",
dependencies: []
),
// MARK: - Tests
.testTarget(
name: "ProfileRecorderTests",
dependencies: [
"ProfileRecorder",
"_ProfileRecorderSampleConversion",
"ProfileRecorderHelpers",
.product(name: "Atomics", package: "swift-atomics"),
.product(name: "NIO", package: "swift-nio"),
.product(name: "Logging", package: "swift-log"),
.product(name: "_NIOFileSystem", package: "swift-nio"),
]
),
.testTarget(
name: "ProfileRecorderServerTests",
dependencies: [
"ProfileRecorder",
"ProfileRecorderServer",
"_ProfileRecorderSampleConversion",
"ProfileRecorderHelpers",
.product(name: "Atomics", package: "swift-atomics"),
.product(name: "NIO", package: "swift-nio"),
.product(name: "Logging", package: "swift-log"),
.product(name: "_NIOFileSystem", package: "swift-nio"),
.product(name: "AsyncHTTPClient", package: "async-http-client"),
]
),
.testTarget(
name: "ProfileRecorderSampleConversionTests",
dependencies: [
"ProfileRecorder",
"_ProfileRecorderSampleConversion",
"ProfileRecorderPprofFormat",
"ProfileRecorderHelpers",
.product(name: "Atomics", package: "swift-atomics"),
.product(name: "NIO", package: "swift-nio"),
.product(name: "Logging", package: "swift-log"),
.product(name: "_NIOFileSystem", package: "swift-nio"),
]
),
],
cxxLanguageStandard: .cxx14
)
for target in package.targets {
var settings = target.swiftSettings ?? []
settings.append(.enableExperimentalFeature("StrictConcurrency=complete"))
target.swiftSettings = settings
}
```
## /Protos/generate-protos.sh
```sh path="/Protos/generate-protos.sh"
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the Swift Profile Recorder open source project
##
## Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
## Licensed under Apache License v2.0
##
## See LICENSE.txt for license information
## See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
##
## SPDX-License-Identifier: Apache-2.0
##
##===----------------------------------------------------------------------===##
set -eu
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
protoc \
--swift_opt=Visibility=Public \
--swift_out="$here/../Sources/ProfileRecorderPprofFormat" \
-I "$here" \
profile.proto
```
## /Protos/profile.proto
```proto path="/Protos/profile.proto"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2022-2025 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Profile is a common stacktrace profile format.
//
// Measurements represented with this format should follow the
// following conventions:
//
// - Consumers should treat unset optional fields as if they had been
// set with their default value.
//
// - When possible, measurements should be stored in "unsampled" form
// that is most useful to humans. There should be enough
// information present to determine the original sampled values.
//
// - On-disk, the serialized proto must be gzip-compressed.
//
// - The profile is represented as a set of samples, where each sample
// references a sequence of locations, and where each location belongs
// to a mapping.
// - There is a N->1 relationship from sample.location_id entries to
// locations. For every sample.location_id entry there must be a
// unique Location with that id.
// - There is an optional N->1 relationship from locations to
// mappings. For every nonzero Location.mapping_id there must be a
// unique Mapping with that id.
syntax = "proto3";
package perftools.profiles;
option java_package = "com.google.perftools.profiles";
option java_outer_classname = "ProfileProto";
message Profile {
// A description of the samples associated with each Sample.value.
// For a cpu profile this might be:
// [["cpu","nanoseconds"]] or [["wall","seconds"]] or [["syscall","count"]]
// For a heap profile, this might be:
// [["allocations","count"], ["space","bytes"]],
// If one of the values represents the number of events represented
// by the sample, by convention it should be at index 0 and use
// sample_type.unit == "count".
repeated ValueType sample_type = 1;
// The set of samples recorded in this profile.
repeated Sample sample = 2;
// Mapping from address ranges to the image/binary/library mapped
// into that address range. mapping[0] will be the main binary.
repeated Mapping mapping = 3;
// Locations referenced by samples.
repeated Location location = 4;
// Functions referenced by locations.
repeated Function function = 5;
// A common table for strings referenced by various messages.
// string_table[0] must always be "".
repeated string string_table = 6;
// frames with Function.function_name fully matching the following
// regexp will be dropped from the samples, along with their successors.
int64 drop_frames = 7; // Index into string table.
// frames with Function.function_name fully matching the following
// regexp will be kept, even if it matches drop_frames.
int64 keep_frames = 8; // Index into string table.
// The following fields are informational, do not affect
// interpretation of results.
// Time of collection (UTC) represented as nanoseconds past the epoch.
int64 time_nanos = 9;
// Duration of the profile, if a duration makes sense.
int64 duration_nanos = 10;
// The kind of events between sampled occurrences.
// e.g [ "cpu","cycles" ] or [ "heap","bytes" ]
ValueType period_type = 11;
// The number of events between sampled occurrences.
int64 period = 12;
// Free-form text associated with the profile. The text is displayed as is
// to the user by the tools that read profiles (e.g. by pprof). This field
// should not be used to store any machine-readable information, it is only
// for human-friendly content. The profile must stay functional if this field
// is cleaned.
repeated int64 comment = 13; // Indices into string table.
// Index into the string table of the type of the preferred sample
// value. If unset, clients should default to the last sample value.
int64 default_sample_type = 14;
// Documentation link for this profile. The URL must be absolute,
// e.g., http://pprof.example.com/cpu-profile.html
//
// The URL may be missing if the profile was generated by older code or code
// that did not bother to supply a link.
int64 doc_url = 15; // Index into string table.
}
// ValueType describes the semantics and measurement units of a value.
message ValueType {
int64 type = 1; // Index into string table.
int64 unit = 2; // Index into string table.
}
// Each Sample records values encountered in some program
// context. The program context is typically a stack trace, perhaps
// augmented with auxiliary information like the thread-id, some
// indicator of a higher level request being handled etc.
message Sample {
// The ids recorded here correspond to a Profile.location.id.
// The leaf is at location_id[0].
repeated uint64 location_id = 1;
// The type and unit of each value is defined by the corresponding
// entry in Profile.sample_type. All samples must have the same
// number of values, the same as the length of Profile.sample_type.
// When aggregating multiple samples into a single sample, the
// result has a list of values that is the element-wise sum of the
// lists of the originals.
repeated int64 value = 2;
// label includes additional context for this sample. It can include
// things like a thread id, allocation size, etc.
//
// NOTE: While possible, having multiple values for the same label key is
// strongly discouraged and should never be used. Most tools (e.g. pprof) do
// not have good (or any) support for multi-value labels. And an even more
// discouraged case is having a string label and a numeric label of the same
// name on a sample. Again, possible to express, but should not be used.
repeated Label label = 3;
}
message Label {
// Index into string table. An annotation for a sample (e.g.
// "allocation_size") with an associated value.
// Keys with "pprof::" prefix are reserved for internal use by pprof.
int64 key = 1;
// At most one of the following must be present
int64 str = 2; // Index into string table
int64 num = 3;
// Should only be present when num is present.
// Specifies the units of num.
// Use arbitrary string (for example, "requests") as a custom count unit.
// If no unit is specified, consumer may apply heuristic to deduce the unit.
// Consumers may also interpret units like "bytes" and "kilobytes" as memory
// units and units like "seconds" and "nanoseconds" as time units,
// and apply appropriate unit conversions to these.
int64 num_unit = 4; // Index into string table
}
message Mapping {
// Unique nonzero id for the mapping.
uint64 id = 1;
// Address at which the binary (or DLL) is loaded into memory.
uint64 memory_start = 2;
// The limit of the address range occupied by this mapping.
uint64 memory_limit = 3;
// Offset in the binary that corresponds to the first mapped address.
uint64 file_offset = 4;
// The object this entry is loaded from. This can be a filename on
// disk for the main binary and shared libraries, or virtual
// abstractions like "[vdso]".
int64 filename = 5; // Index into string table
// A string that uniquely identifies a particular program version
// with high probability. E.g., for binaries generated by GNU tools,
// it could be the contents of the .note.gnu.build-id field.
int64 build_id = 6; // Index into string table
// The following fields indicate the resolution of symbolic info.
bool has_functions = 7;
bool has_filenames = 8;
bool has_line_numbers = 9;
bool has_inline_frames = 10;
}
// Describes function and line table debug information.
message Location {
// Unique nonzero id for the location. A profile could use
// instruction addresses or any integer sequence as ids.
uint64 id = 1;
// The id of the corresponding profile.Mapping for this location.
// It can be unset if the mapping is unknown or not applicable for
// this profile type.
uint64 mapping_id = 2;
// The instruction address for this location, if available. It
// should be within [Mapping.memory_start...Mapping.memory_limit]
// for the corresponding mapping. A non-leaf address may be in the
// middle of a call instruction. It is up to display tools to find
// the beginning of the instruction if necessary.
uint64 address = 3;
// Multiple line indicates this location has inlined functions,
// where the last entry represents the caller into which the
// preceding entries were inlined.
//
// E.g., if memcpy() is inlined into printf:
// line[0].function_name == "memcpy"
// line[1].function_name == "printf"
repeated Line line = 4;
// Provides an indication that multiple symbols map to this location's
// address, for example due to identical code folding by the linker. In that
// case the line information above represents one of the multiple
// symbols. This field must be recomputed when the symbolization state of the
// profile changes.
bool is_folded = 5;
}
message Line {
// The id of the corresponding profile.Function for this line.
uint64 function_id = 1;
// Line number in source code.
int64 line = 2;
// Column number in source code.
int64 column = 3;
}
message Function {
// Unique nonzero id for the function.
uint64 id = 1;
// Name of the function, in human-readable form if available.
int64 name = 2; // Index into string table
// Name of the function, as identified by the system.
// For instance, it can be a C++ mangled name.
int64 system_name = 3; // Index into string table
// Source file containing the function.
int64 filename = 4; // Index into string table
// Line number in source file.
int64 start_line = 5;
}
```
## /README.md
# Swift Profile Recorder, an in-process sampling profiler
Want to profile your software in restricted Kubernetes or Docker containers or other environments where you don't have `CAP_SYS_PTRACE`? Look no further.
## What is this?
This is a sampling profiler (like `sample` on macOS) with the special twist that it runs _inside_ the process that gets sampled. This means that it doesn't need `CAP_SYS_PTRACE` or any other privileges to work.
You can pull it in as a fully self-contained Swift Package Manager dependency and then use it in your app.
Swift Profile Recorder is an on- and off-CPU profiler, which means that it records waiting threads (e.g., sleeps, locks, blocking system calls) as well as running (i.e., computing) threads.
### Supported OSes
At the moment, it only supports Linux and macOS.
It could also support other operating systems, but that's not implemented at this point in time.
## How can I use it?
### Via Swift Profile Recorder Server
The easiest way to use Swift Profile Recorder in your application is to run the Swift Profile Recorder Server.
This allows you to retrieve symbolicated samples with a single `curl` (or any other HTTP client) command.
#### Using the Sampling Server
##### One-off setup to get your application ready for sampling
- Add a `swift-profile-recorder` dependency: `.package(url: "https://github.com/apple/swift-profile-recorder.git", .upToNextMinor(from: "0.3.13"))`
- Make your main `executableTarget` depend on `ProfileRecorderServer`: `.product(name: "ProfileRecorderServer", package: "swift-profile-recorder"),`
- Add the following few lines at the very beginning of your main function (`static func main()` or `func run()`):
```swift
import ProfileRecorderServer
[...]
@main
struct YourApp {
func run() async throws {
// Run `ProfileRecorderServer` in the background if enabled via environment variable. Ignore failures.
// It will be automatically cancelled if this function returns.
//
// Example:
// PROFILE_RECORDER_SERVER_URL_PATTERN='unix:///tmp/my-app-samples-{PID}.sock' ./my-app
async let _ = ProfileRecorderServer(configuration: .parseFromEnvironment()).runIgnoringFailures(logger: logger)
[... your regular main function ...]
}
}
```
##### Using the profiling server
Once you added the profile recorder server to your app, you can enable it using an environment variable (assuming you passed `configuration: .parseFromEnvironment()`):
```bash
# Request the profile recording server to listen on a UNIX Domain Socket at path `/tmp/my-app-samples-{PID}.sock`.
# `{PID}` will automatically be replaced with your process's process ID.
PROFILE_RECORDER_SERVER_URL_PATTERN=unix:///tmp/my-app-samples-{PID}.sock .build/release/MyApp
```
After that, you're ready to request samples:
```bash
curl --unix-socket /tmp/my-app-samples-62012.sock -sd '{"numberOfSamples":10,"timeInterval":"100 ms"}' http://localhost/sample | swift demangle --compact > /tmp/samples.perf
```
Now, a file called `/tmp/samples.perf` should have been created. This file is in the standard Linux perf format.
#### Visualisation
Whilst `.perf` files are plain text files, they are most easily digested in a visual form such as FlameGraphs.
Below, some compatible visualisation tools:
- [Speedscope](https://speedscope.app) ([speedscope.app](https://speedscope.app)), simply drag a `.perf` file (such as `/tmp/samples.perf` in the example above) onto the Speedscope website.
- [Firefox Profiler](https://profiler.firefox.com) ([profiler.firefox.com](https://profiler.firefox.com)), simply drag a `.perf` file (such as `/tmp/samples.perf` in the example above) onto the Firefox Profiler website.
- The original [FlameGraph](https://github.com/brendangregg/Flamegraph) tooling. Try for example `./stackcollapse-perf.pl < /tmp/samples.perf | swift demangle --compact | ./flamegraph.pl > /tmp/samples.svg && open -a Safari /tmp/samples.svg`.
## Compatibility
### Formats
- The Linux perf script format (`.perf`, like what `perf record && perf script` would emit)
- The `pprof` format (`.pprof`, like what Golang's pprof emits)
- The "collapsed" format (like what FlameGraph's `stackcollapse*` scripts emit)
### Profile recording server URL endpoints
- pprof's [`/debug/pprof/profile` endpoint](https://pkg.go.dev/net/http/pprof)
- Swift Profile Recorder's own `/sample` endpoint
- `/health` endpoint for health checks (returns `200 OK`)
## Example profiles
- Hummingbird's [hello example](https://github.com/hummingbird-project/hummingbird-examples/tree/main/hello) load-tested by `wrk -T50s -c 20000 -t 200 http://127.0.0.1:8080` running on macOS
- Applied [a small diff](#swipr-diff-hummingbird-hello) to enable Swift Profile Recorder in Humminbird's hello example
- Server started with just one SwiftNIO thread for a cleaner profile: `NIO_SINGLETON_BLOCKING_POOL_THREAD_COUNT=1 NIO_SINGLETON_GROUP_LOOP_COUNT=1 PROFILE_RECORDER_SERVER_URL_PATTERN=unix:///tmp/swipr-{PID}.sock .build/release/App`
= Samples received using `curl -sd '{"numberOfSamples":1000,"timeInterval":"10ms"}' --unix-socket /tmp/swipr-SERVER_PID.sock http://unix | swift demangle --compact > /tmp/samples.perf`
- View [profile in Firefox Profiler](https://share.firefox.dev/4pJf8Sl)
- Screenshot of speedscope.app:

- Hummingbird's [hello example](https://github.com/hummingbird-project/hummingbird-examples/tree/main/hello) load-tested by `wrk -T50s -c 20000 -t 200 http://127.0.0.1:8080` running on Linux (Ubuntu 20.04, Swift 6.2, unprivileged container)
- Applied [a small diff](#swipr-diff-hummingbird-hello) to enable Swift Profile Recorder in Humminbird's hello example
- Server started with just one SwiftNIO thread for a cleaner profile: `NIO_SINGLETON_BLOCKING_POOL_THREAD_COUNT=1 NIO_SINGLETON_GROUP_LOOP_COUNT=1 PROFILE_RECORDER_SERVER_URL_PATTERN=unix:///tmp/swipr-{PID}.sock .build/release/App`
= Samples received using `curl -sd '{"numberOfSamples":1000,"timeInterval":"10ms"}' --unix-socket /tmp/swipr-SERVER_PID.sock http://unix | swift demangle --compact > /tmp/samples.perf`
- View [profile in Firefox Profiler](https://share.firefox.dev/42JY1Ge)
- Screenshot of speedscope.app:

### Example diffs
#### Add Swift Profile Recorder to hummingbird-examples/hello
<div id="swipr-diff-hummingbird-hello"></div>
<details>
<summary>
Expand here to see `git diff -U1` onto [commit `97a09f0664679f017616a82894848b267c5e7068`](https://github.com/hummingbird-project/hummingbird-examples/commit/97a09f0664679f017616a82894848b267c5e7068)
</summary>
```diff
diff --git a/hello/Package.swift b/hello/Package.swift
index ae0b6d2..33b24ed 100644
--- a/hello/Package.swift
+++ b/hello/Package.swift
@@ -11,2 +11,3 @@ let package = Package(
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.4.0"),
+ .package(url: "https://github.com/apple/swift-profile-recorder.git", .upToNextMinor(from: "0.3.13")),
],
@@ -18,2 +19,3 @@ let package = Package(
.product(name: "Hummingbird", package: "hummingbird"),
+ .product(name: "ProfileRecorderServer", package: "swift-profile-recorder"),
],
diff --git a/hello/Sources/App/app.swift b/hello/Sources/App/app.swift
index 13131d9..95b114a 100644
--- a/hello/Sources/App/app.swift
+++ b/hello/Sources/App/app.swift
@@ -1,2 +1,3 @@
import ArgumentParser
+import ProfileRecorderServer
@@ -17,2 +18,5 @@ struct HummingbirdArguments: AsyncParsableCommand {
)
+ async let _ = ProfileRecorderServer(configuration: .parseFromEnvironment()).runIgnoringFailures(
+ logger: app.logger
+ )
try await app.runService()
```
</summary>
## /Scripts/debug-llvm-symbolizer
``` path="/Scripts/debug-llvm-symbolizer"
#!/bin/bash
set -eu
tee /tmp/llvm-symbolizer-input | llvm-symbolizer "$@" | tee /tmp/llvm-symbolizer-output
```
## /Scripts/smoke-in-docker.sh
```sh path="/Scripts/smoke-in-docker.sh"
#!/bin/bash
set -eu
tmpdir=$(mktemp -d /tmp/swipr-smoke_XXXXXX)
swift package resolve # Package.resolved is necessary for 'ro' mount to work
docker run \
-it --rm \
-v "$PWD:$PWD:ro" \
-v "$tmpdir":/output \
-w "$PWD" \
--memory $(( 1024 * 1024 * 1024 )) \
swift:6.0-noble bash -c 'export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y curl && Scripts/smoke-test.sh /output'
echo "$tmpdir"
ls "$tmpdir"
```
## /Scripts/smoke-test.sh
```sh path="/Scripts/smoke-test.sh"
#!/bin/bash
set -eu
output=${1:?output directory}
tmpdir=$(mktemp -d /tmp/swipr_smoke_XXXXXX)
trap 'rm -rf "$tmpdir"' EXIT
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$here"
swift build -c release --scratch-path "$tmpdir"/build
mkdir -p "$output"
socket_path="$tmpdir/swipr.sock"
PROFILE_RECORDER_SERVER_URL=unix://"$socket_path" \
"$tmpdir"/build/release/swipr-mini-demo \
--blocking --burn-cpu --array-appends \
--tsp true \
--output "$output"/samples.swipr \
--iterations 1000 \
--profiling-server &
demo_pid=$!
sleep 3
set -x
curl -o "$output"/samples.perf \
-sd '{"timeInterval":"100 ms","numberOfSamples":100}' \
--unix-socket "$socket_path" \
http://unix/sample
curl -o "$output"/samples.pprof \
-s \
--unix-socket "$socket_path" \
http://unix/debug/pprof/profile?seconds=10
curl -o "$output"/samples-fakesym.pprof \
-s \
--unix-socket "$socket_path" \
http://unix/debug/pprof/symbolizer=fake/profile?seconds=10
kill "$demo_pid"
wait "$demo_pid" || true
```
## /Scripts/vendor-core-symbolication-symbolizer.sh
```sh path="/Scripts/vendor-core-symbolication-symbolizer.sh"
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the Swift Profile Recorder open source project
##
## Copyright (c) 2025 Apple Inc. and the Swift Profile Recorder project authors
## Licensed under Apache License v2.0
##
## See LICENSE.txt for license information
## See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
##
## SPDX-License-Identifier: Apache-2.0
##
##===----------------------------------------------------------------------===##
set -eu
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
swift_root=${1:-$here/../../swift}
backtracing_root="$swift_root"/stdlib/public/RuntimeModule
# (no prefixes just yet) prefix=${2:-SWIPR}
function die() {
echo "ERROR: $*"
exit 1
}
echo "Using backtracing_root from $backtracing_root"
test -d "$backtracing_root" || die "$backtracing_root: Not found"
target_c_src="$here/../Sources/CProfileRecorderDarwin"
target_swift_src="$here/../Sources/ProfileRecorderSampleConversion/CoreSymbolication"
desc=$(cd "$backtracing_root" && git describe --abbrev --dirty)
echo "$desc" > "$here/../Misc/vendored-core-symbolication.version"
rm -rf "$target_c_src"
rm -f "$target_swift_src/CoreSymbolication.swift"
mkdir -p "$target_c_src/include"
cat > "$target_c_src/empty.c" <<"EOF"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2025 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
EOF
cat > "$target_c_src/include/CProfileRecorderDarwin.h" <<"EOF"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2025 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#pragma once
#include "swipr-core-symbolication.h"
EOF
cp "$backtracing_root"/modules/OS/Darwin.h "$target_c_src/include/swipr-core-symbolication.h"
vendored_file_text='//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// This file has been adjusted for ProfileRecorder, originally of the Swift.org open source project
// swift-format-ignore-file
'
find . -name "swipr-core-symbolication.h" -exec gsed -ri \
-e "s/SWIFT_BACKTRACING_DARWIN_H/SWIPR_CORE_SYMBOLICATION_H/g" \
-e "/#include <mach\/mach_vm\.h>/d" \
-e "/#include <libproc\.h>/d" \
-e "0,/#include .*/{/#include .*/a\#include <stdbool.h>
}" \
-e "/enum CFStringBuiltInEncodings: CFStringEncoding \{/,/^};/d" \
-e "1s%^%$(echo "$vendored_file_text" | tr '\n' '%' | sed 's/%/\\n/g' | sed 's/\\n$//' )%" \
'{}' \;
cp "$backtracing_root/CoreSymbolication.swift" "$target_swift_src/CoreSymbolication.swift"
find . -name "CoreSymbolication.swift" -exec gsed -ri \
-e "s/private framework/TEMP_PLACEHOLDER/g" \
-e "s/private//g" \
-e "s/TEMP_PLACEHOLDER/private framework/g" \
-e "s/internal import .*//g" \
-e '/^import .*$/a\
import Darwin\
import Foundation\
import CProfileRecorderDarwin
' \
-e '/^typealias .*$/a\
typealias CSTypeRef = CProfileRecorderDarwin.CSTypeRef\
typealias CSBinaryImageInformation = CProfileRecorderDarwin.CSBinaryImageInformation\
typealias CSNotificationBlock = CProfileRecorderDarwin.CSNotificationBlock\
typealias CSSymbolicatorRef = CProfileRecorderDarwin.CSSymbolicatorRef\
typealias CFUUIDBytes = CProfileRecorderDarwin.CFUUIDBytes
' \
-e '/Crash Reporter support/,/Base functionality/{//!d;}' \
-e "1s%^%$(echo "$vendored_file_text" | tr '\n' '%' | sed 's/%/\\n/g' | sed 's/\\n$//' )%" \
'{}' \;
echo "Okay, all done."
```
## /Scripts/vendor-native-symbolizer.sh
```sh path="/Scripts/vendor-native-symbolizer.sh"
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the Swift Profile Recorder open source project
##
## Copyright (c) 2024 Apple Inc. and the Swift Profile Recorder project authors
## Licensed under Apache License v2.0
##
## See LICENSE.txt for license information
## See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
##
## SPDX-License-Identifier: Apache-2.0
##
##===----------------------------------------------------------------------===##
set -eu
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
swift_root=${1:-$here/../../swift}
backtracing_root="$swift_root"/stdlib/public/RuntimeModule
prefix=${2:-SWIPR}
function die() {
echo "ERROR: $*"
exit 1
}
echo "Using backtracing_root from $backtracing_root"
test -d "$backtracing_root" || die "$backtracing_root: Not found"
target_c_src="$here/../Sources/CProfileRecorderSwiftELF"
target_swift_src="$here/../Sources/ProfileRecorderSampleConversion/NativeELFSymboliser"
desc=$(cd "$backtracing_root" && git describe --abbrev --dirty)
rm -rf "$target_c_src" "$target_swift_src"
mkdir -p "$target_c_src/include"
mkdir -p "$target_swift_src"
cat > "$target_c_src/empty.c" <<"EOF"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
EOF
cp "$backtracing_root"/modules/ImageFormats/Elf/*.h "$target_c_src/include/"
cp "$backtracing_root"/modules/ImageFormats/Dwarf/*.h "$target_c_src/include/"
for f in "$target_c_src/include"/*.h; do
mv "$f" "$(dirname "$f")/swipr-$(basename "$f")"
done
cat > "$target_c_src/include/CProfileRecorderSwiftELF.h" <<"EOF"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef CProfileRecorderSWIFTELF_h
#define CProfileRecorderSWIFTELF_h
#include "swipr-elf.h"
#include "swipr-eh_frame_hdr.h"
#include "swipr-dwarf.h"
#endif
EOF
swift_files=(
ByteSwapping.swift
Dwarf.swift
Elf.swift
Image.swift
ImageSource.swift
MemoryReader.swift
Registers.swift
Utils.swift
)
for f in "${swift_files[@]}"; do
cp "$backtracing_root/$f" "$target_swift_src"
done
echo "$desc" > "$here/../Misc/vendored-backtracing_root.version"
vendored_file_text='//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// This file has been adjusted for ProfileRecorder, originally of the Swift.org open source project
// swift-format-ignore-file
'
find "$target_c_src" "$target_swift_src" \
-type f \
-exec gsed -ri \
-e "s/namespace ([A-Za-z0-9]+)/namespace ${prefix}\1/g" \
-e "s/\<elf_/${prefix}_elf_/g" \
-e "s/\<Elf_/${prefix}_Elf_/g" \
-e "s/\<Elk_/${prefix}_Elk_/g" \
-e "s/\<Elf64_/${prefix}_Elf64_/g" \
-e "s/\<Elf32_/${prefix}_Elf32_/g" \
-e "s/\<Dwarf_/${prefix}_Dwarf_/g" \
-e "s/\<Dwarf32_/${prefix}_Dwarf32_/g" \
-e "s/\<Dwarf64_/${prefix}_Dwarf64_/g" \
-e "s/\<DF_/internal_${prefix}_DF_/g" \
-e "s/\<DT_/internal_${prefix}_DT_/g" \
-e "s/\<DW_/internal_${prefix}_DW_/g" \
-e "s/\<DWARF_/internal_${prefix}_DWARF_/g" \
-e "s/\<EI_/internal_${prefix}_EI_/g" \
-e "s/\<ELFCOMPRESS_/internal_${prefix}_ELFCOMPRESS_/g" \
-e "s/\<ELFOSABI_/internal_${prefix}_ELFOSABI_/g" \
-e "s/\<EM_/internal_${prefix}_EM_/g" \
-e "s/\<ET_/internal_${prefix}_ET_/g" \
-e "s/\<EV_/internal_${prefix}_EV_/g" \
-e "s/\<GRP_/internal_${prefix}_GRP_/g" \
-e "s/\<NT_/internal_${prefix}_NT_/g" \
-e "s/\<PF_/internal_${prefix}_PF_/g" \
-e "s/\<PT_/internal_${prefix}_PT_/g" \
-e "s/\<SHF_/internal_${prefix}_SHF_/g" \
-e "s/\<SHN_/internal_${prefix}_SHN_/g" \
-e "s/\<SHT_/internal_${prefix}_SHT_/g" \
-e "s/\<STB_/internal_${prefix}_STB_/g" \
-e "s/\<STN_/internal_${prefix}_STN_/g" \
-e "s/\<STT_/internal_${prefix}_STT_/g" \
-e "s/\<STV_/internal_${prefix}_STV_/g" \
-e "s/@available(.*)//g" \
-e "s/import Swift//g" \
-e "s/^let ELF64_/nonisolated(unsafe) let ELF64_/g" \
-e "s/^let ELF32_/nonisolated(unsafe) let ELF32_/g" \
-e "s/^let elf_/nonisolated(unsafe) let elf_/g" \
-e "s/os\(linux\)/os(Linux)/g" \
-e "s/internal import BacktracingImpl.ImageFormats.Dwarf/import CProfileRecorderSwiftELF/g" \
-e "s/internal import BacktracingImpl.Runtime//g" \
-e "s/internal import BacktracingImpl.ImageFormats.Elf/import CProfileRecorderSwiftELF/g" \
-e "s/internal import BacktracingImpl.OS.Darwin//g" \
-e "s/swift\.runtime\./CProfileRecorderSwiftELF./g" \
-e "s/internal import/import/g" \
-e "s/_swift_open/open/g" \
-e "s/_swift_get_errno\(\)/errno/g" \
-e "s/mach_task_self\(\)/mach_task_self_/g" \
-e "s/typealias SourceLocation = SymbolicatedBacktrace.SourceLocation//g" \
-e "s/@_specialize\(kind: full, where R == RemoteMemoryReader\)//g" \
-e "s/@_specialize\(kind: full, where R == MemserverMemoryReader\)//g" \
-e "s/@_specialize\(kind: full, where R == RemoteMemoryReader, Traits == Elf32Traits\)//g" \
-e "s/@_specialize\(kind: full, where R == RemoteMemoryReader, Traits == Elf64Traits\)//g" \
-e "s/@_specialize\(kind: full, where R == MemserverMemoryReader, Traits == Elf32Traits\)//g" \
-e "s/@_specialize\(kind: full, where R == MemserverMemoryReader, Traits == Elf64Traits\)//g" \
-e "s/@_specialize\(kind: full, where R == UnsafeLocalMemoryReader\)//g" \
-e "s/@_specialize\(kind: full, where R == UnsafeLocalMemoryReader, Traits == Elf32Traits\)//g" \
-e "s/@_specialize\(kind: full, where R == UnsafeLocalMemoryReader, Traits == Elf64Traits\)//g" \
-e "s/^#if os\(macOS\) \|\| os\(iOS\) \|\| os\(tvOS\) \|\| os\(watchOS\)$/#if canImport(Darwin)/g" \
-e "1s%^%$(echo "$vendored_file_text" | tr '\n' '%' | sed 's/%/\\n/g')%" \
'{}' \;
# Comment out compression-related code in Elf.swift (unsupported features)
elf_swift="$target_swift_src/Elf.swift"
awk '
# Comment out .gnu_debugdata block
/if let debugData = getSection\("\.gnu_debugdata"\)/ {
print "/*"
in_debugdata = 1
brace_count = 0
}
in_debugdata {
print
brace_count += gsub(/{/, "{")
brace_count -= gsub(/}/, "}")
if (brace_count == 0) {
print "*/"
in_debugdata = 0
}
next
}
# Replace elfCompressedImageSource with () // compression unsupported
/return try ImageSource\(elfCompressedImageSource:/ {
print " () // compression unsupported"
getline # skip the traits: line
next
}
# Comment out zname == sname block
/if zname == sname \{/ {
print "/*"
in_zname = 1
zname_brace = 0
}
in_zname {
print
zname_brace += gsub(/{/, "{")
zname_brace -= gsub(/}/, "}")
if (zname_brace == 0) {
print "*/"
in_zname = 0
}
next
}
# Comment out CompressedImageSourceError catch blocks (keep the } before catch)
/\} catch let CompressedImageSourceError\.libraryNotFound/ {
print " }"
print "/*"
print " catch let CompressedImageSourceError.libraryNotFound(library) {"
in_catch = 1
next
}
in_catch {
print
if (/\} catch \{/) {
prev_was_catch_open = 1
} else if (/^[[:space:]]*\}[[:space:]]*$/ && prev_was_catch_open) {
print "*/"
in_catch = 0
prev_was_catch_open = 0
} else {
prev_was_catch_open = 0
}
next
}
{ print }
' "$elf_swift" > "$elf_swift.tmp" && mv "$elf_swift.tmp" "$elf_swift"
# Truncate MemoryReader.swift - remove new platform-specific memory readers
# Keep only up to the end of "extension MemoryReader { ... }"
memory_reader="$target_swift_src/MemoryReader.swift"
awk '
/^extension MemoryReader/ { in_extension = 1 }
in_extension && /^}$/ { print; exit }
{ print }
' "$memory_reader" > "$memory_reader.tmp" && mv "$memory_reader.tmp" "$memory_reader"
echo "Okay, all done."
```
## /Sources/CProfileRecorderDarwin/empty.c
```c path="/Sources/CProfileRecorderDarwin/empty.c"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2025 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
```
## /Sources/CProfileRecorderDarwin/include/CProfileRecorderDarwin.h
```h path="/Sources/CProfileRecorderDarwin/include/CProfileRecorderDarwin.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2025 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#pragma once
#include "swipr-core-symbolication.h"
```
## /Sources/CProfileRecorderDarwin/include/swipr-core-symbolication.h
```h path="/Sources/CProfileRecorderDarwin/include/swipr-core-symbolication.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// This file has been adjusted for ProfileRecorder, originally of the Swift.org open source project
// swift-format-ignore-file
//===--- Darwin.h - Darwin specifics ----------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Darwin specifics.
//
// WARNING: Some of the things in this file are SPI. If you use them in
// your own code, we will mercilessly break your program for you and hand
// you the pieces :-)
//
//===----------------------------------------------------------------------===//
#ifndef SWIPR_CORE_SYMBOLICATION_H
#define SWIPR_CORE_SYMBOLICATION_H
#ifdef __APPLE__
#include <mach/mach.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// .. Mach fixes ...............................................................
// Use an inline function for mach_task_self() or it won't import into Swift
#undef mach_task_self
static inline task_t mach_task_self() { return mach_task_self_; }
// .. Thread states ............................................................
/* We can't import these from the system header, because it uses all kinds of
macros and the Swift importer can't cope with that. So declare them here
in a form it can understand. */
#define ARM_THREAD_STATE64 6
struct darwin_arm64_thread_state {
uint64_t _x[29];
uint64_t fp;
uint64_t lr;
uint64_t sp;
uint64_t pc;
uint32_t cpsr;
uint32_t __pad;
};
struct darwin_arm64_exception_state {
uint64_t far;
uint32_t esr;
uint32_t exception;
};
struct darwin_arm64_mcontext {
struct darwin_arm64_exception_state es;
struct darwin_arm64_thread_state ss;
// followed by NEON state (which we don't care about)
};
#define X86_THREAD_STATE64 4
struct darwin_x86_64_thread_state {
uint64_t rax;
uint64_t rbx;
uint64_t rcx;
uint64_t rdx;
uint64_t rdi;
uint64_t rsi;
uint64_t rbp;
uint64_t rsp;
uint64_t r8;
uint64_t r9;
uint64_t r10;
uint64_t r11;
uint64_t r12;
uint64_t r13;
uint64_t r14;
uint64_t r15;
uint64_t rip;
uint64_t rflags;
uint64_t cs;
uint64_t fs;
uint64_t gs;
};
struct darwin_x86_64_exception_state {
uint16_t trapno;
uint16_t cpu;
uint32_t err;
uint64_t faultvaddr;
};
struct darwin_x86_64_mcontext {
struct darwin_x86_64_exception_state es;
struct darwin_x86_64_thread_state ss;
// followed by FP/AVX/AVX512 state (which we don't care about)
};
// .. libproc SPI ..............................................................
int proc_name(int pid, void * buffer, uint32_t buffersize);
// .. Mach SPI .................................................................
extern kern_return_t task_read_for_pid(task_t task, int pid, task_t *ptask);
// .. dyld SPI .................................................................
struct dyld_process_cache_info {
uuid_t cacheUUID;
uint64_t cacheBaseAddress;
bool noCache;
bool privateCache;
};
typedef struct dyld_process_cache_info dyld_process_cache_info;
typedef const struct dyld_process_info_base* dyld_process_info;
extern dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kernelError);
extern void _dyld_process_info_release(dyld_process_info info);
extern void _dyld_process_info_retain(dyld_process_info info);
extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo);
extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path));
extern void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName));
// .. Code Signing SPI .........................................................
#define CS_OPS_STATUS 0
#define CS_GET_TASK_ALLOW 0x00000004
#define CS_RUNTIME 0x00010000
#define CS_PLATFORM_BINARY 0x04000000
#define CS_PLATFORM_PATH 0x08000000
extern int csops(int, unsigned int, void *, size_t);
// .. CoreFoundation ...........................................................
// We can't include <CoreFoundation/CoreFoundation.h> because that will create
// a circular dependency. So declare some types directly.
typedef struct CFUUIDBytes {
uint8_t byte0;
uint8_t byte1;
uint8_t byte2;
uint8_t byte3;
uint8_t byte4;
uint8_t byte5;
uint8_t byte6;
uint8_t byte7;
uint8_t byte8;
uint8_t byte9;
uint8_t byte10;
uint8_t byte11;
uint8_t byte12;
uint8_t byte13;
uint8_t byte14;
uint8_t byte15;
} CFUUIDBytes;
#define CF_BRIDGED_TYPE(T) __attribute__((objc_bridge(T)))
typedef const struct CF_BRIDGED_TYPE(NSString) __CFString *CFStringRef;
typedef const struct CF_BRIDGED_TYPE(id) __CFAllocator *CFAllocatorRef;
#ifdef __LLP64__
typedef signed long long CFIndex;
#else
typedef signed long CFIndex;
#endif
typedef struct {
CFIndex location;
CFIndex length;
} CFRange;
typedef uint32_t CFStringEncoding;
typedef enum __attribute__((enum_extensibility(open)))
CFStringBuiltInEncodings : CFStringEncoding CFStringBuiltInEncodings;
// .. CoreSymbolication SPI ....................................................
typedef int32_t cpu_type_t;
typedef int32_t cpu_subtype_t;
struct _CSArchitecture {
cpu_type_t cpu_type;
cpu_subtype_t cpu_subtype;
};
typedef struct _CSArchitecture CSArchitecture;
static const CSArchitecture kCSArchitectureI386 = {
CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL
};
static const CSArchitecture kCSArchitectureX86_64 = {
CPU_TYPE_X86_64, CPU_SUBTYPE_I386_ALL
};
static const CSArchitecture kCSArchitectureArm64 = {
CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL
};
static const CSArchitecture kCSArchitectureArm64_32 = {
CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_ALL
};
static const CSArchitecture kCSArchitectureArmV7K = {
CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K
};
typedef struct _CSBinaryRelocationInformation {
vm_address_t base;
vm_address_t extent;
char name[17];
} CSBinaryRelocationInformation;
typedef struct _CSBinaryImageInformation {
vm_address_t base;
vm_address_t extent;
CFUUIDBytes uuid;
CSArchitecture arch;
const char *path;
CSBinaryRelocationInformation *relocations;
uint32_t relocationCount;
uint32_t flags;
} CSBinaryImageInformation;
typedef uint64_t CSMachineTime;
static const CSMachineTime kCSBeginningOfTime = 0;
static const CSMachineTime kCSEndOfTime = (1ull<<63) - 1;
static const CSMachineTime kCSNow = (1ull<<63);
static const CSMachineTime kCSAllTimes = (1ull<<63) + 1;
struct _CSTypeRef {
uintptr_t _opaque_1;
uintptr_t _opaque_2;
};
typedef struct _CSTypeRef CSTypeRef;
typedef CSTypeRef CSNullRef;
typedef CSTypeRef CSSymbolicatorRef;
typedef CSTypeRef CSSymbolOwnerRef;
typedef CSTypeRef CSSymbolRef;
typedef CSTypeRef CSSourceInfoRef;
static const CSNullRef kCSNull = { 0, 0 };
typedef void (^CSSymbolOwnerIterator)(CSSymbolOwnerRef owner);
typedef void (^CSStackFrameIterator)(CSSymbolRef symbol, CSSourceInfoRef info);
typedef struct _CSNotificationData {
CSSymbolicatorRef symbolicator;
union {
struct Ping {
uint32_t value;
} ping;
struct DyldLoad {
CSSymbolOwnerRef symbolOwner;
} dyldLoad;
struct DyldUnload {
CSSymbolOwnerRef symbolOwner;
} dyldUnload;
} u;
} CSNotificationData;
typedef void (^CSNotificationBlock)(uint32_t type, CSNotificationData data);
struct _CSRange {
vm_address_t location;
vm_size_t length;
};
typedef struct _CSRange CSRange;
enum {
kCRSanitizePathGlobLocalHomeDirectories = 1,
kCRSanitizePathGlobLocalVolumes = 2,
kCRSanitizePathGlobAllTypes = 0xff,
kCRSanitizePathNormalize = 0x100 << 0,
kCRSanitizePathKeepFile = 0x100 << 1,
};
#ifdef __cplusplus
} // extern "C"
#endif
#endif // __APPLE__
#endif // SWIPR_CORE_SYMBOLICATION_H
```
## /Sources/CProfileRecorderSampler/asserts.h
```h path="/Sources/CProfileRecorderSampler/asserts.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef asserts_h
#define asserts_h
#include <stdlib.h>
#define swipr_precondition(_x) do { \
if (!(_x)) { \
char buffer[128] = { 0 }; \
snprintf(buffer, sizeof(buffer), "ProfileRecorder precondition failed: %s:%d: " #_x "\n", __FILE__, __LINE__); \
write(STDERR_FILENO, buffer, strlen(buffer)); \
abort(); \
} \
} while(0)
#endif /* asserts_h */
```
## /Sources/CProfileRecorderSampler/common.h
```h path="/Sources/CProfileRecorderSampler/common.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef common_h
#define common_h
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#define SWIPR_MAX_MUTATOR_THREADS 1024
#define SWIPR_MAX_STACK_DEPTH 128
#define SWIPR_MAX_LIBS 16384
#define SWIPR_NSEC_PER_USEC 1000ULL
#define SWIPR_NSEC_PER_MSEC (1000ULL * SWIPR_NSEC_PER_USEC)
#define SWIPR_NSEC_PER_SEC (1000ULL * SWIPR_NSEC_PER_MSEC)
#if defined(SWIPR_ENABLE_UNSAFE_DEBUG)
# define UNSAFE_DEBUG(...) \
do { \
char buffer[512] = {0}; \
snprintf(buffer, sizeof(buffer), "ProfileRecorder: " __VA_ARGS__); \
write(STDERR_FILENO, buffer, strlen(buffer)); \
} while (0)
#else
# define UNSAFE_DEBUG(...) do { } while (0)
#endif
#define SWIPR_MIN(a, b) ((a) < (b) ? (a) : (b))
#endif /* common_h */
```
## /Sources/CProfileRecorderSampler/fp_unwinder.c
```c path="/Sources/CProfileRecorderSampler/fp_unwinder.c"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#include <stdbool.h>
#include "fp_unwinder.h"
#include "common.h"
void swipr_fp_unwinder_init(struct swipr_fp_unwinder_cursor *cursor, struct swipr_fp_unwinder_context *context) {
cursor->sfuc_fp = context->sfuctx_fp;
cursor->sfuc_ip = context->sfuctx_ip;
cursor->sfuc_original_sp = context->sfuctx_sp;
cursor->sfuc_frame_depth = 0;
}
int swipr_fp_unwinder_step(struct swipr_fp_unwinder_cursor *cursor) {
struct swipr_fp_unwinder_cursor old_cursor = *cursor;
cursor->sfuc_frame_depth++;
if (cursor->sfuc_frame_depth <= 2) {
// We return the original IP twice because we're stripping it once in the sample conv.
return 1; // >0 == continue
}
// FIXME: The layout (previous frame followed by return address), stack direction (down) & stack size (128k) are technically arch dependent
if (
cursor->sfuc_fp != 0 && // We're not at the end, ...
cursor->sfuc_fp >= cursor->sfuc_original_sp && // ... we're walking the right direction and ...
cursor->sfuc_fp - cursor->sfuc_original_sp <= 128*1024 // ... we no more than 128k away from the top of the stack.
) {
uintptr_t *fp = (uintptr_t *)cursor->sfuc_fp;
cursor->sfuc_fp = fp[0];
cursor->sfuc_ip = fp[1];
return 1; // >0 == continue
} else {
UNSAFE_DEBUG("ending stack unwind (fp!=0: %d, fp>sp: %d, fp-sp: %ld)",
cursor->sfuc_fp != 0,
cursor->sfuc_fp >= cursor->sfuc_original_sp,
cursor->sfuc_fp - cursor->sfuc_original_sp);
return 0; // 0 == stop
}
}
int swipr_fp_unwinder_get_reg(struct swipr_fp_unwinder_cursor *cursor,
enum swipr_fp_unwinder_register reg,
uintptr_t *output) {
switch (reg) {
case SWIPR_FP_UNWINDER_REG_IP:
*output = cursor->sfuc_ip;
break;
case SWIPR_FP_UNWINDER_REG_FP:
*output = cursor->sfuc_fp;
break;
default:
return 1;
}
return 0;
}
int swipr_fp_unwinder_getcontext(struct swipr_fp_unwinder_context *context, ucontext_t *uc) {
#if defined(__linux__) && defined(__x86_64__)
intptr_t reg_ip = uc->uc_mcontext.gregs[REG_RIP];
intptr_t reg_fp = uc->uc_mcontext.gregs[REG_RBP];
intptr_t reg_sp = uc->uc_mcontext.gregs[REG_RSP];
#elif defined(__linux__) && defined(__aarch64__)
intptr_t reg_ip = uc->uc_mcontext.pc;
intptr_t reg_fp = uc->uc_mcontext.regs[29];
intptr_t reg_sp = uc->uc_mcontext.sp;
#elif defined(__linux__) && defined(__arm__)
// ARM (32-bit) on Linux (e.g. ARMv6 and ARMv7)
intptr_t reg_ip = uc->uc_mcontext.arm_pc;
intptr_t reg_fp = uc->uc_mcontext.arm_fp; // r11 is the frame pointer
intptr_t reg_sp = uc->uc_mcontext.arm_sp; // r13 is the stack pointer
#elif defined(__APPLE__) && defined(__x86_64__)
intptr_t reg_ip = uc->uc_mcontext->__ss.__rip;
intptr_t reg_fp = uc->uc_mcontext->__ss.__rbp;
intptr_t reg_sp = uc->uc_mcontext->__ss.__rsp;
#elif defined(__APPLE__) && defined(__aarch64__)
intptr_t reg_ip = __darwin_arm_thread_state64_get_pc(uc->uc_mcontext->__ss);
intptr_t reg_fp = __darwin_arm_thread_state64_get_fp(uc->uc_mcontext->__ss);
intptr_t reg_sp = __darwin_arm_thread_state64_get_sp(uc->uc_mcontext->__ss);
#else
#warning unknown OS/arch combination
intptr_t reg_ip = 0;
intptr_t reg_fp = 0;
intptr_t reg_sp = 0;
#endif
context->sfuctx_fp = reg_fp;
context->sfuctx_ip = reg_ip;
context->sfuctx_sp = reg_sp;
return 0;
}
```
## /Sources/CProfileRecorderSampler/fp_unwinder.h
```h path="/Sources/CProfileRecorderSampler/fp_unwinder.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef FP_UNWINDER_H
#define FP_UNWINDER_H
#define _GNU_SOURCE
#include <signal.h>
#include <stdint.h>
struct swipr_fp_unwinder_cursor {
intptr_t sfuc_fp;
intptr_t sfuc_ip;
intptr_t sfuc_original_sp;
int sfuc_frame_depth;
};
struct swipr_fp_unwinder_context {
intptr_t sfuctx_ip;
intptr_t sfuctx_fp;
intptr_t sfuctx_sp;
};
enum swipr_fp_unwinder_register {
SWIPR_FP_UNWINDER_REG_IP = 1111,
SWIPR_FP_UNWINDER_REG_FP = 2222
};
void swipr_fp_unwinder_init(struct swipr_fp_unwinder_cursor *cursor, struct swipr_fp_unwinder_context *context);
int swipr_fp_unwinder_step(struct swipr_fp_unwinder_cursor *cursor);
int swipr_fp_unwinder_get_reg(struct swipr_fp_unwinder_cursor *cursor, enum swipr_fp_unwinder_register reg, uintptr_t *output);
int swipr_fp_unwinder_getcontext(struct swipr_fp_unwinder_context *context, ucontext_t *uc);
#endif
```
## /Sources/CProfileRecorderSampler/include/CSampler.h
```h path="/Sources/CProfileRecorderSampler/include/CSampler.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef CSampler_h
#define CSampler_h
#include <unistd.h>
#include <stdio.h>
int swipr_request_sample(FILE *output,
size_t sample_count,
useconds_t usecs_between_samples);
int swipr_initialize(void);
#endif /* CSampler_h */
```
## /Sources/CProfileRecorderSampler/interface.h
```h path="/Sources/CProfileRecorderSampler/interface.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef interface_h
#define interface_h
#include "common.h"
#include "os_dep.h"
#include "fp_unwinder.h"
enum swipr_c2ms_state {
swipr_c2m_idle = 0,
swipr_c2m_preparing = 1,
swipr_c2m_sampling = 2,
swipr_c2m_processing = 3,
};
struct collector_to_mutator {
swipr_os_dep_thread_id c2m_thread_id;
swipr_os_dep_sem c2m_proceed;
swipr_os_dep_sem m2c_proceed;
struct swipr_fp_unwinder_context c2m_tiny_context;
};
struct collector_to_mutators {
_Atomic enum swipr_c2ms_state c2ms_state;
struct collector_to_mutator c2ms_c2ms[SWIPR_MAX_MUTATOR_THREADS];
};
#if defined(__APPLE__)
struct os_dep_thread_info {
thread_t mach_thread; // send right to suspend/resume the mach thread
};
#else
struct os_dep_thread_info {
// empty for now
};
#endif
struct thread_info {
swipr_os_dep_thread_id ti_id; // ti_id == 0 means skip
char ti_name[32];
struct os_dep_thread_info ti_os_specific;
};
#endif /* interface_h */
```
## /Sources/CProfileRecorderSampler/os_dep.h
```h path="/Sources/CProfileRecorderSampler/os_dep.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef swipr_os_dep_h
#define swipr_os_dep_h
struct thread_info;
#if __APPLE__ && __has_include(<dispatch/dispatch.h>)
# include "os_dep_dispatch.h"
#elif __has_include(<pthread.h>)
# include "os_dep_pthread.h"
#else
# error "unsupported threading libs"
#endif
#if __linux__
# include "os_dep_linux.h"
#elif __APPLE__
# include "os_dep_darwin.h"
#else
# error "unsupported OS"
#endif
#include "common.h"
#include "sampler.h"
struct thread_info *swipr_os_dep_create_thread_list(size_t *all_threads_count);
int swipr_os_dep_destroy_thread_list(struct thread_info *thread_list);
struct swipr_dynamic_lib {
char dl_name[1024];
char dl_arch[16];
/// The slide (always positive), the offset between the in-the-process segment start address and the in-the-file virtual segment start address
uintptr_t dl_seg_slide;
/// The in-the-process segment start address (slide + in-the-file virtual segment start address)
uintptr_t dl_seg_start_addr;
/// The in-the-process segment end address (slide + in-the-file virtual segment end address)
uintptr_t dl_seg_end_addr; // dl_seg_start_addr + vmsize
};
int swipr_os_dep_list_all_dynamic_libs(struct swipr_dynamic_lib *all_libs,
size_t all_libs_capacity,
size_t *all_libs_count);
int swipr_os_dep_set_current_thread_name(const char *name);
int swipr_os_dep_get_current_thread_name(char *name, size_t len);
int swipr_os_dep_sample_prepare(size_t num_threads, struct thread_info *all_threads, struct swipr_minidump *minidumps);
void swipr_os_dep_suspend_threads(size_t num_threads, struct thread_info *all_threads);
int swipr_os_dep_sample_cleanup(size_t num_threads, struct thread_info *all_threads);
#endif /* swipr_os_dep_h */
```
## /Sources/CProfileRecorderSampler/os_dep_darwin.c
```c path="/Sources/CProfileRecorderSampler/os_dep_darwin.c"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#if __APPLE__
#include <pthread.h>
#include <mach/thread_info.h>
#include <mach/machine/thread_status.h>
#include <mach-o/dyld.h>
#include <string.h>
#include <stdio.h>
#include "os_dep.h"
#include "asserts.h"
struct thread_info *swipr_os_dep_create_thread_list(size_t *all_threads_count) {
thread_array_t threads = NULL;
mach_msg_type_number_t flavor = THREAD_IDENTIFIER_INFO_COUNT;
mach_msg_type_number_t threads_count = 0;
kern_return_t kret = task_threads(mach_task_self(),
&threads,
&threads_count);
if (kret) {
*all_threads_count = 0;
return NULL;
}
struct thread_info *all_threads = calloc(SWIPR_MAX_MUTATOR_THREADS, sizeof(struct thread_info));
if (!all_threads) {
return NULL;
}
memset(all_threads, 0, sizeof(*all_threads) * SWIPR_MAX_MUTATOR_THREADS);
for (int i = 0; i < threads_count; i++) {
pthread_t pthread = pthread_from_mach_thread_np(threads[i]);
if (pthread == NULL || threads[i] == mach_thread_self()) {
// skip controller and mach threads without corresponding pthreads
// set ti_id to 0 so they will be ignored
all_threads[i].ti_id = 0;
threads_count--;
continue;
}
char name[32] = {0};
int getname_ret = pthread_getname_np(pthread, name, sizeof(name));
// ignore threads for which we can't get mach info
thread_identifier_info_data_t tid_info = {0};
kern_return_t info_ret = thread_info(threads[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&tid_info, &flavor);
if (info_ret != KERN_SUCCESS) {
UNSAFE_DEBUG("failed to get thread_info in create thread list for mach port %llu | %llx\n", threads[i], threads[i]);
all_threads[i].ti_id = 0;
threads_count--;
continue;
}
all_threads[i].ti_id = tid_info.thread_id;
all_threads[i].ti_os_specific.mach_thread = threads[i];
if (name[0] == 0) {
strcpy(all_threads[i].ti_name, "<n/a>"); // threads may have empty names
} else {
_Static_assert(sizeof(all_threads[0].ti_name) >= sizeof(name), "destination too small for memcpy");
memcpy(all_threads[i].ti_name, name, sizeof(all_threads[i].ti_name));
}
}
kret = vm_deallocate(mach_task_self(),
(vm_address_t)threads,
threads_count * sizeof(thread_t));
swipr_precondition(kret == KERN_SUCCESS);
swipr_precondition(threads_count >= 0);
*all_threads_count = (size_t) threads_count;
return all_threads;
}
int swipr_os_dep_destroy_thread_list(struct thread_info *thread_list) {
swipr_precondition(thread_list);
int err = 0;
for (int i = 0; i < SWIPR_MAX_MUTATOR_THREADS; i++) {
if (thread_list[i].ti_os_specific.mach_thread == THREAD_NULL) {
// wasn't allocated a port right
continue;
} else {
kern_return_t kret = mach_port_deallocate(mach_task_self(), thread_list[i].ti_os_specific.mach_thread);
swipr_precondition(kret == KERN_SUCCESS);
}
}
free(thread_list);
return err;
}
int swipr_os_dep_list_all_dynamic_libs(struct swipr_dynamic_lib *all_libs,
size_t all_libs_capacity,
size_t *all_libs_count) {
int img_count = _dyld_image_count();
int libs_count = 0;
uint arch_size = sizeof(all_libs[0].dl_arch);
for (int image_index = 0; image_index < img_count; image_index++) {
const struct mach_header *header = _dyld_get_image_header(image_index);
intptr_t slide = _dyld_get_image_vmaddr_slide(image_index);
const char *name = _dyld_get_image_name(image_index);
// Move ptr over mach header, offset depends on header size
bool is64 = (header->magic == MH_MAGIC_64 || header->magic == MH_CIGAM_64);
const uint8_t *ld_cmd_ptr = (const uint8_t *)header
+ (is64 ? sizeof(struct mach_header_64)
: sizeof(struct mach_header));
cpu_type_t type = header->cputype;
cpu_subtype_t subtype = header->cpusubtype & ~CPU_SUBTYPE_MASK;
// we iterate over load commands and finds __TEXT segments.
// Note: vmaddr is the intended load address of the Mach-O binary,
// it is adjusted by the ASLR slide to give the runtime address
for (int load_command_index = 0; load_command_index < header->ncmds; load_command_index++) {
const struct load_command *ld_cmd = (const struct load_command *)ld_cmd_ptr;
if (ld_cmd->cmd == LC_SEGMENT) {
const struct segment_command *seg = (const struct segment_command *)ld_cmd;
if (strcmp(seg->segname, "__TEXT") == 0) {
uintptr_t start = seg->vmaddr + slide;
uintptr_t end = start + seg->vmsize;
memcpy(all_libs[libs_count].dl_name, name, sizeof(all_libs[libs_count].dl_name));
all_libs[libs_count].dl_name[sizeof(all_libs[libs_count].dl_name) - 1] = 0; //truncates string
all_libs[libs_count].dl_seg_slide = slide;
all_libs[libs_count].dl_seg_start_addr = start;
all_libs[libs_count].dl_seg_end_addr = end;
if (type == CPU_TYPE_I386) {
strlcpy(all_libs[libs_count].dl_arch, "i386", arch_size);
} else if (type == CPU_TYPE_ARM) {
strlcpy(all_libs[libs_count].dl_arch, "arm", arch_size);
} else {
strlcpy(all_libs[libs_count].dl_arch, "unknown", arch_size);
}
libs_count++;
}
} else if (ld_cmd->cmd == LC_SEGMENT_64) {
const struct segment_command_64 *seg = (const struct segment_command_64 *)ld_cmd;
if (strcmp(seg->segname, "__TEXT") == 0) {
uintptr_t start = seg->vmaddr + slide;
uintptr_t end = start + seg->vmsize;
memcpy(all_libs[libs_count].dl_name, name, sizeof(all_libs[libs_count].dl_name));
all_libs[libs_count].dl_name[sizeof(all_libs[libs_count].dl_name) - 1] = 0; //truncates string
all_libs[libs_count].dl_seg_slide = slide;
all_libs[libs_count].dl_seg_start_addr = start;
all_libs[libs_count].dl_seg_end_addr = end;
if (type == CPU_TYPE_X86_64) {
strlcpy(all_libs[libs_count].dl_arch, "x86_64", arch_size);
} else if (type == CPU_TYPE_ARM64_32) {
strlcpy(all_libs[libs_count].dl_arch, "arm64_32", arch_size);
} else if (type == CPU_TYPE_ARM64) {
if (subtype == CPU_SUBTYPE_ARM64E) {
strlcpy(all_libs[libs_count].dl_arch, "arm64e", arch_size);
} else {
strlcpy(all_libs[libs_count].dl_arch, "arm64", arch_size);
}
} else {
strlcpy(all_libs[libs_count].dl_arch, "unknown", arch_size);
}
libs_count++;
}
}
ld_cmd_ptr += ld_cmd->cmdsize;
}
}
// libs above capacity are ignored
*all_libs_count = SWIPR_MIN(all_libs_capacity, libs_count);
return 0;
}
int swipr_os_dep_set_current_thread_name(const char *name) {
if (pthread_setname_np(name)){
return -1;
}
return 0;
}
int swipr_os_dep_get_current_thread_name(char *name, size_t len) {
if (pthread_getname_np(pthread_self(), name, len)){
return -1;
}
return 0;
}
static int swipr_wait_for_thread_suspend(thread_act_t thread) {
#if TARGET_OS_OSX || TARGET_OS_IOS
kern_return_t kr;
thread_basic_info_data_t info;
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
struct timespec start_time = swipr_sampler_get_current_time();
useconds_t sleep_time = 1;
float sleep_mult = 1.3;
while (true) {
count = THREAD_BASIC_INFO_COUNT;
kr = thread_info(thread,
THREAD_BASIC_INFO,
(thread_info_t)&info,
&count);
if (kr != KERN_SUCCESS) {
return 1;
}
if (info.run_state == TH_STATE_WAITING) {
return 0;
}
// abort incase thread is uninterruptable
if (info.run_state == TH_STATE_UNINTERRUPTIBLE) {
thread_abort(thread);
continue;
}
usleep(sleep_time);
struct timespec current_time = swipr_sampler_get_current_time();
float duration = (current_time.tv_sec - start_time.tv_sec) +
(current_time.tv_nsec - start_time.tv_nsec) / 1e9f;
if (duration > SWIPR_NSEC_PER_SEC) {
// abandon thread
UNSAFE_DEBUG("Thread timed out during suspension \n");
return 1;
}
// update sleep_time
sleep_time = (useconds_t) sleep_time * sleep_mult;
}
return 0;
#else
return 1;
#endif
}
int swipr_os_dep_sample_prepare(size_t num_threads, struct thread_info *all_threads, struct swipr_minidump *minidumps) {
for (int i=0; i<num_threads; i++) {
minidumps[i] = (typeof(minidumps[i])){ 0 };
}
return 0;
}
void swipr_os_dep_suspend_threads(size_t num_threads, struct thread_info *all_threads) {
#if TARGET_OS_OSX || TARGET_OS_IOS
// For Darwin - controller thread suspends and resumes each mutator
// We ignore thread iff ti_id == 0
for (mach_msg_type_number_t i=0; i<num_threads; i++) {
// ignore and mark unwanted threads
if (all_threads[i].ti_id == 0) {
continue;
}
g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id = all_threads[i].ti_id;
kern_return_t kret = thread_suspend(all_threads[i].ti_os_specific.mach_thread);
if (kret != KERN_SUCCESS) {
// if thread is dead then ignore error and mark ignore
all_threads[i].ti_id = 0;
continue;
}
// skip thread if it died during wait
int has_suspended = swipr_wait_for_thread_suspend(all_threads[i].ti_os_specific.mach_thread);
if (has_suspended != 0) {
all_threads[i].ti_id = 0;
continue;
}
#if defined(__x86_64__)
x86_thread_state64_t state;
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
thread_state_flavor_t flavor = x86_THREAD_STATE64;
kret = thread_get_state(all_threads[i].ti_os_specific.mach_thread, flavor, (thread_state_t)&state, &count);
swipr_precondition(kret == KERN_SUCCESS);
g_swipr_c2ms.c2ms_c2ms[i].c2m_tiny_context.sfuctx_fp = state.__rbp;
g_swipr_c2ms.c2ms_c2ms[i].c2m_tiny_context.sfuctx_sp = state.__rsp;
g_swipr_c2ms.c2ms_c2ms[i].c2m_tiny_context.sfuctx_ip = state.__rip;
#elif defined(__aarch64__)
arm_thread_state64_t state;
mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT;
thread_state_flavor_t flavor = ARM_THREAD_STATE64;
kret = thread_get_state(all_threads[i].ti_os_specific.mach_thread, flavor, (thread_state_t)&state, &count);
swipr_precondition(kret == KERN_SUCCESS);
g_swipr_c2ms.c2ms_c2ms[i].c2m_tiny_context.sfuctx_fp = arm_thread_state64_get_fp(state);
g_swipr_c2ms.c2ms_c2ms[i].c2m_tiny_context.sfuctx_sp = arm_thread_state64_get_sp(state);
g_swipr_c2ms.c2ms_c2ms[i].c2m_tiny_context.sfuctx_ip = arm_thread_state64_get_pc(state);
#else
#warning unknown OS/arch combination
g_swipr_c2ms.c2ms_c2ms[i].c2m_tiny_context.sfuctx_fp = 0;
g_swipr_c2ms.c2ms_c2ms[i].c2m_tiny_context.sfuctx_sp = 0;
g_swipr_c2ms.c2ms_c2ms[i].c2m_tiny_context.sfuctx_ip = 0;
#endif
}
#else
return 1; // unsupported OS
#endif
}
int swipr_os_dep_sample_cleanup(size_t num_threads, struct thread_info *all_threads) {
#if TARGET_OS_OSX || TARGET_OS_IOS
for (mach_msg_type_number_t i=0; i<num_threads; i++) {
if (all_threads[i].ti_id == 0) {
continue;
}
kern_return_t kret = thread_resume(all_threads[i].ti_os_specific.mach_thread);
swipr_precondition(kret == KERN_SUCCESS);
}
int err = swipr_os_dep_destroy_thread_list(all_threads);
return err;
#else
return 1; // unsupported OS
#endif
}
#endif
```
## /Sources/CProfileRecorderSampler/os_dep_darwin.h
```h path="/Sources/CProfileRecorderSampler/os_dep_darwin.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef swipr_os_dep_dawin_h
#define swipr_os_dep_dawin_h
#import <pthread.h>
#include <mach/mach.h>
typedef intptr_t swipr_os_dep_thread_id;
static inline int swipr_os_dep_kill(swipr_os_dep_thread_id thread_id, int signal) {
return pthread_kill((pthread_t)thread_id, signal);
}
#endif /* swipr_os_dep_dawin_h */
```
## /Sources/CProfileRecorderSampler/os_dep_dispatch.h
```h path="/Sources/CProfileRecorderSampler/os_dep_dispatch.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef swipr_os_dep_macos_h
#define swipr_os_dep_macos_h
#ifndef __APPLE__
# error "This is only meant to be included on Darwin."
#endif
#include <dispatch/dispatch.h>
#include "os_dep_darwin.h"
#include "asserts.h"
typedef dispatch_semaphore_t swipr_os_dep_sem;
#define swipr_os_dep_sem_create dispatch_semaphore_create
#define swipr_os_dep_sem_free dispatch_release
#define swipr_os_dep_sem_signal dispatch_semaphore_signal
#define swipr_os_dep_deadline dispatch_time_t
static inline swipr_os_dep_deadline
swipr_os_dep_create_deadline(uint64_t nsecs) {
return dispatch_time(DISPATCH_TIME_NOW, nsecs);
}
static inline void
swipr_os_dep_sem_wait(swipr_os_dep_sem sem) {
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}
static inline int
swipr_os_dep_sem_wait_with_deadline(swipr_os_dep_sem sem, swipr_os_dep_deadline deadline) {
return (int)dispatch_semaphore_wait(sem, deadline);
}
static inline swipr_os_dep_thread_id
swipr_os_dep_get_thread_id(void) {
thread_identifier_info_data_t tid_info;
mach_msg_type_number_t flavor = THREAD_IDENTIFIER_INFO_COUNT;
kern_return_t kret = thread_info(mach_thread_self(),
THREAD_IDENTIFIER_INFO,
(thread_info_t)&tid_info,
&flavor);
swipr_precondition(kret == KERN_SUCCESS);
return tid_info.thread_id;
}
#endif /* swipr_os_dep_macos_h */
```
## /Sources/CProfileRecorderSampler/os_dep_linux.c
```c path="/Sources/CProfileRecorderSampler/os_dep_linux.c"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#if __linux__
#define _GNU_SOURCE
#include <dirent.h>
#include <sys/errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <link.h>
#include <string.h>
#include <fcntl.h>
#include "os_dep.h"
#include "interface.h"
#include "common.h"
// Check if a specific signal is blocked for a given thread.
// Returns: 1 if signal is blocked, 0 if not blocked, -1 on error.
static int swipr_is_signal_blocked(swipr_os_dep_thread_id tid, int signum) {
if (signum < 1 || signum > 64) {
return -1;
}
char path[64];
snprintf(path, sizeof(path), "/proc/self/task/%d/status", tid);
int fd = open(path, O_RDONLY);
if (fd < 0) {
return -1;
}
char buffer[4096];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
int close_result = close(fd);
if (bytes_read <= 0 || (close_result != 0 && !(close_result == -1 && errno == EINTR))) {
return -1;
}
buffer[bytes_read] = '\0';
// Find the SigBlk line
const char *sigblk_line = strstr(buffer, "\nSigBlk:");
if (!sigblk_line) {
sigblk_line = strstr(buffer, "SigBlk:");
if (!sigblk_line) {
return -1;
}
} else {
sigblk_line++; // Skip the newline
}
// Skip "SigBlk:" and whitespace
sigblk_line += 7;
while (*sigblk_line == ' ' || *sigblk_line == '\t') {
sigblk_line++;
}
// Parse the hexadecimal mask
unsigned long long mask = strtoull(sigblk_line, NULL, 16);
// Check if the signal bit is set (signals are 1-indexed)
int bit_position = signum - 1;
return (mask & (1ULL << bit_position)) ? 1 : 0;
}
struct thread_info *swipr_os_dep_create_thread_list(size_t *all_threads_count) {
struct thread_info *all_threads = calloc(sizeof(struct thread_info), SWIPR_MAX_MUTATOR_THREADS);
if (!all_threads) {
return NULL;
}
DIR *dir = opendir("/proc/self/task");
if (!dir) {
goto error;
}
int next_index = 0;
pid_t my_tid = swipr_os_dep_get_thread_id();
struct dirent *ent = NULL;
while (true) {
errno = 0;
ent = readdir(dir);
if (ent == NULL) {
switch (errno) {
case 0:
goto out;
case EINTR:
continue;
default:
goto error;
}
if (errno == EINTR) {
continue;
}
}
pid_t tid = atol(ent->d_name);
if (tid != 0 && tid != my_tid) {
// Check if SIGPROF is blocked for this thread
int sigprof_blocked = swipr_is_signal_blocked(tid, SIGPROF);
if (sigprof_blocked > 0) {
// Skip threads that have SIGPROF blocked
continue;
}
int idx = next_index++;
all_threads[idx].ti_id = tid;
char file_path[128] = {0};
snprintf(file_path, 128, "/proc/self/task/%d/comm", tid);
int fd = open(file_path, O_RDONLY);
if (fd >= 0) {
int how_much = read(fd, all_threads[idx].ti_name, 32);
all_threads[idx].ti_name[how_much > 0 ? how_much-1 : 0] = 0; // get rid of the \n
int ret = close(fd);
// continue if close successful or failed due to interrupt
if (!(ret == 0 || (ret == -1 && errno == EINTR))) {
goto error;
}
}
}
}
out:
closedir(dir);
*all_threads_count = next_index;
return all_threads;
error:
closedir(dir);
swipr_os_dep_destroy_thread_list(all_threads);
*all_threads_count = 0;
return NULL;
}
int swipr_os_dep_destroy_thread_list(struct thread_info *thread_list) {
swipr_precondition(thread_list);
free(thread_list);
return 0;
}
struct dl_iterate_phdr_data {
struct swipr_dynamic_lib *dli_all_libs;
size_t dli_all_libs_capacity;
size_t dli_all_libs_count;
bool dli_first;
};
static int
dl_iterate_phdr_cb(struct dl_phdr_info *info, size_t size, void *v_data) {
struct dl_iterate_phdr_data *data = (typeof(data))v_data;
for (int i=0; i<info->dlpi_phnum; i++) {
const ElfW(Phdr) *phdr = &info->dlpi_phdr[i];
if (phdr->p_type != PT_LOAD) {
continue;
}
struct swipr_dynamic_lib *my_lib = &data->dli_all_libs[data->dli_all_libs_count++];
if (data->dli_first) {
readlink("/proc/self/exe", my_lib->dl_name, sizeof(my_lib->dl_name));
} else {
strncpy(my_lib->dl_name, info->dlpi_name, sizeof(my_lib->dl_name));
}
my_lib->dl_name[sizeof(my_lib->dl_name) - 1] = 0;
my_lib->dl_seg_slide = info->dlpi_addr;
my_lib->dl_seg_start_addr = info->dlpi_addr + phdr->p_vaddr;
my_lib->dl_seg_end_addr = info->dlpi_addr + phdr->p_vaddr + phdr->p_memsz;
}
data->dli_first = false;
return 0;
}
int swipr_os_dep_list_all_dynamic_libs(struct swipr_dynamic_lib *all_libs,
size_t all_libs_capacity,
size_t *all_libs_count) {
struct dl_iterate_phdr_data data = {
.dli_all_libs = all_libs,
.dli_all_libs_capacity = all_libs_capacity,
.dli_all_libs_count = 0,
.dli_first = true,
};
size_t next = 0;
int err = dl_iterate_phdr(dl_iterate_phdr_cb, &data);
*all_libs_count = data.dli_all_libs_count;
return err;
}
int swipr_os_dep_set_current_thread_name(const char *name) {
return pthread_setname_np(pthread_self(), name);
}
int swipr_os_dep_get_current_thread_name(char *name, size_t len) {
#ifdef __ANDROID__
if (len < 16) {
return -1;
}
return TEMP_FAILURE_RETRY(prctl(PR_GET_NAME, name)) == -1 ? -1 : 0;
#else
return pthread_getname_np(pthread_self(), name, len);
#endif
}
int swipr_os_dep_sample_prepare(size_t num_threads, struct thread_info *all_threads, struct swipr_minidump *minidumps) {
for (int i=0; i<num_threads; i++) {
swipr_precondition(all_threads[i].ti_id != 0);
g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id = all_threads[i].ti_id;
swipr_precondition(g_swipr_c2ms.c2ms_c2ms[i].c2m_proceed == NULL);
g_swipr_c2ms.c2ms_c2ms[i].c2m_proceed = swipr_os_dep_sem_create(0);
swipr_precondition(g_swipr_c2ms.c2ms_c2ms[i].m2c_proceed == NULL);
g_swipr_c2ms.c2ms_c2ms[i].m2c_proceed = swipr_os_dep_sem_create(0);
}
// checks threads, if out of memory then free all
for (int i=0; i<num_threads; i++) {
if (g_swipr_c2ms.c2ms_c2ms[i].c2m_proceed == NULL || g_swipr_c2ms.c2ms_c2ms[i].m2c_proceed == NULL) {
// out of memory.
for (int j=0; j<num_threads; j++) {
if (g_swipr_c2ms.c2ms_c2ms[j].c2m_proceed) {
swipr_os_dep_sem_free(g_swipr_c2ms.c2ms_c2ms[j].c2m_proceed);
g_swipr_c2ms.c2ms_c2ms[j].c2m_proceed = NULL;
}
if (g_swipr_c2ms.c2ms_c2ms[j].m2c_proceed) {
swipr_os_dep_sem_free(g_swipr_c2ms.c2ms_c2ms[j].m2c_proceed);
g_swipr_c2ms.c2ms_c2ms[j].m2c_proceed = NULL;
}
}
return 1;
}
}
for (int i=0; i<num_threads; i++) {
minidumps[i] = (typeof(minidumps[i])){ 0 };
}
return 0;
}
void swipr_os_dep_suspend_threads(size_t num_threads, struct thread_info *all_threads) {
int err;
for (int i=0; i<num_threads; i++) {
swipr_precondition(all_threads[i].ti_id != 0);
UNSAFE_DEBUG("signalling thread %lu\n", (uintptr_t)g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id);
err = swipr_os_dep_kill(all_threads[i].ti_id, SIGPROF);
if (err != 0) {
UNSAFE_DEBUG("couldn't signal thread %lu\n", (uintptr_t)g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id);
// thread dead, let's not wait for it later.
g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id = 0;
}
}
swipr_os_dep_deadline deadline = swipr_os_dep_create_deadline(SWIPR_NSEC_PER_SEC);
for (int i=0; i<num_threads; i++) {
swipr_os_dep_thread_id thread_id = g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id;
if (thread_id > 0) {
err = swipr_os_dep_sem_wait_with_deadline(g_swipr_c2ms.c2ms_c2ms[i].m2c_proceed, deadline);
if (err) {
if (swipr_os_dep_kill(thread_id, 0) == -1 && errno == ESRCH) {
UNSAFE_DEBUG("thread %d/%ld died, that's probably okay\n",
i, (long)thread_id);
g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id = 0;
} else {
UNSAFE_DEBUG("OUCH, timeout, thread still alive but no response %d/%d of %zu\n",
i, thread_id, num_threads);
// FIXME: We can't just continue here...
g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id = 0;
}
}
}
}
}
int swipr_os_dep_sample_cleanup(size_t num_threads, struct thread_info *all_threads) {
for (int i=0; i<num_threads; i++) {
if (g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id > 0) {
swipr_os_dep_sem_signal(g_swipr_c2ms.c2ms_c2ms[i].c2m_proceed);
}
}
int err;
for (int i=0; i<num_threads; i++) {
swipr_os_dep_thread_id thread_id = g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id;
if (thread_id > 0) {
swipr_os_dep_deadline deadline = swipr_os_dep_create_deadline(100 * SWIPR_NSEC_PER_MSEC);
err = swipr_os_dep_sem_wait_with_deadline(g_swipr_c2ms.c2ms_c2ms[i].m2c_proceed, deadline);
if (err) {
UNSAFE_DEBUG("OUTCH, timeout (B), thread %d/%ld of %zu\n",
i, (long)thread_id, num_threads);
// FIXME: Continuing here is unsafe, the tests might still exist, and below, we'll free the semaphore.
// Probably best to leak it or so.
}
}
g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id = 0;
swipr_os_dep_sem_free(g_swipr_c2ms.c2ms_c2ms[i].c2m_proceed);
swipr_os_dep_sem_free(g_swipr_c2ms.c2ms_c2ms[i].m2c_proceed);
g_swipr_c2ms.c2ms_c2ms[i].c2m_proceed = NULL;
g_swipr_c2ms.c2ms_c2ms[i].m2c_proceed = NULL;
}
err = swipr_os_dep_destroy_thread_list(all_threads);
return err;
}
#endif
```
## /Sources/CProfileRecorderSampler/os_dep_linux.h
```h path="/Sources/CProfileRecorderSampler/os_dep_linux.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef swipr_os_dep_linux_h
#define swipr_os_dep_linux_h
#define _GNU_SOURCE
#include <unistd.h>
#include <signal.h> /* Definition of SIG* constants */
#include <sys/syscall.h> /* Definition of SYS_* constants */
#define swipr_os_dep_thread_id pid_t
static inline pid_t
swipr_os_dep_get_thread_id(void) {
return syscall(SYS_gettid);
}
static inline int swipr_os_dep_kill(swipr_os_dep_thread_id tid, int sig) {
return syscall(SYS_tgkill, getpid(), tid, sig);
}
#endif /* swipr_os_dep_linux_h */
```
## /Sources/CProfileRecorderSampler/os_dep_pthread.h
```h path="/Sources/CProfileRecorderSampler/os_dep_pthread.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef swipr_os_dep_pthread_h
#define swipr_os_dep_pthread_h
#include <pthread.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/errno.h>
#include "asserts.h"
#include "os_dep_linux.h"
#include "common.h"
struct swipr_pthread_sem {
pthread_mutex_t mutex;
pthread_cond_t cond;
int value;
};
typedef struct swipr_pthread_sem *swipr_os_dep_sem;
static inline swipr_os_dep_sem
swipr_os_dep_sem_create(int value) {
struct swipr_pthread_sem *sem = malloc(sizeof(*sem));
if (!sem) {
return NULL;
}
*sem = (typeof(*sem)){ 0 };
sem->value = 0;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_init(&sem->mutex, &attr);
pthread_cond_init(&sem->cond, NULL);
return sem;
}
static inline void
swipr_os_dep_sem_free(swipr_os_dep_sem sem) {
int err = pthread_cond_destroy(&sem->cond);
swipr_precondition(err == 0);
err = pthread_mutex_destroy(&sem->mutex);
swipr_precondition(err == 0);
free(sem);
}
static inline void
swipr_os_dep_sem_signal(swipr_os_dep_sem sem) {
int err = pthread_mutex_lock(&sem->mutex);
swipr_precondition(err == 0);
swipr_precondition(sem->value >= 0);
sem->value++;
err = pthread_mutex_unlock(&sem->mutex);
swipr_precondition(err == 0);
err = pthread_cond_signal(&sem->cond);
swipr_precondition(err == 0);
}
#define swipr_os_dep_deadline struct timespec
static inline swipr_os_dep_deadline
swipr_os_dep_create_deadline(uint64_t nsecs) {
struct timeval cur_time = { 0 };
gettimeofday(&cur_time, NULL);
uint64_t all_nsecs = nsecs + ((uint64_t)cur_time.tv_usec * 1000ULL);
struct timespec timeout_abs = {
.tv_sec = cur_time.tv_sec + (all_nsecs / SWIPR_NSEC_PER_SEC),
.tv_nsec = all_nsecs % SWIPR_NSEC_PER_SEC
};
return timeout_abs;
}
static inline void
swipr_os_dep_sem_wait(swipr_os_dep_sem sem) {
int err = pthread_mutex_lock(&sem->mutex);
swipr_precondition(err == 0);
swipr_precondition(sem->value >= 0);
while (sem->value <= 0) {
err = pthread_cond_wait(&sem->cond, &sem->mutex);
swipr_precondition(err == 0);
}
sem->value--;
swipr_precondition(sem->value >= 0);
err = pthread_mutex_unlock(&sem->mutex);
swipr_precondition(err == 0);
}
static inline int
swipr_os_dep_sem_wait_with_deadline(swipr_os_dep_sem sem, swipr_os_dep_deadline deadline) {
int err = pthread_mutex_lock(&sem->mutex);
swipr_precondition(err == 0);
while (sem->value <= 0) {
err = pthread_cond_timedwait(&sem->cond, &sem->mutex, &deadline);
if (err == ETIMEDOUT) {
err = pthread_mutex_unlock(&sem->mutex);
swipr_precondition(err == 0);
return ETIMEDOUT;
}
swipr_precondition(err == 0);
}
sem->value--;
swipr_precondition(sem->value >= 0);
err = pthread_mutex_unlock(&sem->mutex);
swipr_precondition(err == 0);
return 0;
}
#endif /* swipr_os_dep_pthread_h */
```
## /Sources/CProfileRecorderSampler/sampler.c
```c path="/Sources/CProfileRecorderSampler/sampler.c"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#define _GNU_SOURCE
#include <stdatomic.h>
#include <signal.h>
#include <stdio.h>
#include <pthread.h>
#include <stdbool.h>
#include <string.h>
#include <sys/errno.h>
#include "os_dep.h"
#include "interface.h"
#include "asserts.h"
#include "common.h"
struct collector_to_mutators g_swipr_c2ms = {0};
static inline void
swipr_state_start_preparing(void) {
enum swipr_c2ms_state expected = swipr_c2m_idle;
bool success = atomic_compare_exchange_strong_explicit(&g_swipr_c2ms.c2ms_state,
&expected,
swipr_c2m_preparing,
memory_order_relaxed,
memory_order_relaxed);
swipr_precondition(success);
}
static inline void
swipr_state_start_sampling(void) {
enum swipr_c2ms_state expected = swipr_c2m_preparing;
bool success = atomic_compare_exchange_strong_explicit(&g_swipr_c2ms.c2ms_state,
&expected,
swipr_c2m_sampling,
memory_order_seq_cst,
memory_order_seq_cst);
swipr_precondition(success);
}
static inline void
swipr_state_start_processing(void) {
enum swipr_c2ms_state expected = swipr_c2m_sampling;
bool success = atomic_compare_exchange_strong_explicit(&g_swipr_c2ms.c2ms_state,
&expected,
swipr_c2m_processing,
memory_order_acquire,
memory_order_acquire);
swipr_precondition(success);
}
static inline void
swipr_state_finish_processing(void) {
enum swipr_c2ms_state expected = swipr_c2m_processing;
bool success = atomic_compare_exchange_strong_explicit(&g_swipr_c2ms.c2ms_state,
&expected,
swipr_c2m_idle,
memory_order_relaxed,
memory_order_relaxed);
swipr_precondition(success);
}
static inline void
swipr_state_abort_preparing(void) {
enum swipr_c2ms_state expected = swipr_c2m_preparing;
bool success = atomic_compare_exchange_strong_explicit(&g_swipr_c2ms.c2ms_state,
&expected,
swipr_c2m_idle,
memory_order_seq_cst,
memory_order_seq_cst);
swipr_precondition(success);
}
static int
swipr_dump_shared_objs(FILE *output) {
size_t all_libs_count = 0;
struct swipr_dynamic_lib *all_libs = calloc(SWIPR_MAX_LIBS, sizeof(*all_libs));
if (!all_libs) {
return 1;
}
swipr_os_dep_list_all_dynamic_libs(all_libs, SWIPR_MAX_LIBS, &all_libs_count);
fprintf(output, "[SWIPR] VERS { \"version\": 1}\n");
for (size_t i=0; i < all_libs_count; i++) {
fprintf(output,
"[SWIPR] VMAP {"
"\"path\": \"%s\", "
"\"architecture\": \"%s\", "
"\"segmentSlide\": \"0x%lx\", "
"\"segmentStartAddress\": \"0x%lx\", "
"\"segmentEndAddress\": \"0x%lx\""
"}\n",
all_libs[i].dl_name, all_libs[i].dl_arch, all_libs[i].dl_seg_slide,
all_libs[i].dl_seg_start_addr, all_libs[i].dl_seg_end_addr);
}
UNSAFE_DEBUG("Number of libraries mapped: %zu \n", all_libs_count);
free(all_libs);
return 0;
}
static int
swipr_initialise_c2ms(FILE *output) {
for (int i=0; i<SWIPR_MAX_MUTATOR_THREADS; i++) {
g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id = 0;
g_swipr_c2ms.c2ms_c2ms[i].c2m_proceed = NULL;
g_swipr_c2ms.c2ms_c2ms[i].m2c_proceed = NULL;
}
return swipr_dump_shared_objs(output);
}
static int
swipr_make_sample(struct swipr_minidump *minidumps,
size_t minidumps_capacity,
size_t *minidumps_count_ptr) {
swipr_state_start_preparing();
size_t num_threads = 0;
int err;
struct thread_info *all_threads = swipr_os_dep_create_thread_list(&num_threads);
if (all_threads == NULL) {
swipr_state_abort_preparing();
return 1;
}
*minidumps_count_ptr = num_threads;
UNSAFE_DEBUG("sampling %lu threads (controller is %lu)\n", num_threads, (uintptr_t)swipr_os_dep_get_thread_id());
err = swipr_os_dep_sample_prepare(num_threads, all_threads, minidumps);
if (err != 0) {
swipr_os_dep_destroy_thread_list(all_threads);
swipr_state_abort_preparing();
return 1;
}
swipr_state_start_sampling();
struct timespec start_time = swipr_sampler_get_current_time();
swipr_os_dep_suspend_threads(num_threads, all_threads);
swipr_state_start_processing();
for (int i=0; i<num_threads; i++) {
if (g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id == 0 || all_threads[i].ti_id == 0) {
continue;
}
struct swipr_fp_unwinder_cursor cursor = { 0 };
swipr_fp_unwinder_init(&cursor, &g_swipr_c2ms.c2ms_c2ms[i].c2m_tiny_context);
UNSAFE_DEBUG("[%d: %lu] starting unwind\n",
i,
(uintptr_t)g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id);
int ret = -1;
size_t next_stack_frame_idx = 0;
while ((ret = swipr_fp_unwinder_step(&cursor)) > 0 && next_stack_frame_idx < SWIPR_MAX_STACK_DEPTH) {
struct swipr_stackframe *stack_frame = &minidumps[i].md_stack[next_stack_frame_idx++];
swipr_fp_unwinder_get_reg(&cursor, SWIPR_FP_UNWINDER_REG_IP, &stack_frame->sf_ip);
swipr_fp_unwinder_get_reg(&cursor, SWIPR_FP_UNWINDER_REG_FP, &stack_frame->sf_sp);
UNSAFE_DEBUG("[%d: %lu] ip=%lx, sp=%lx, ret=%d\n",
i,
(uintptr_t)g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id,
stack_frame->sf_ip,
stack_frame->sf_sp,
ret);
}
UNSAFE_DEBUG("[%d: %lu] unwind done, ret=%d\n",
i,
(uintptr_t)g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id,
ret);
minidumps[i].md_stack_depth = next_stack_frame_idx;
minidumps[i].md_time = start_time;
minidumps[i].md_pid = getpid();
minidumps[i].md_tid = g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id;
strcpy(minidumps[i].md_thread_name, all_threads[i].ti_name);
}
swipr_state_finish_processing();
err = swipr_os_dep_sample_cleanup(num_threads, all_threads);
return err;
}
int
swipr_request_sample(FILE *output,
size_t sample_count,
useconds_t usecs_between_samples) {
size_t num_minidumps = 0;
char old_thread_name[128] = {0};
swipr_os_dep_get_current_thread_name(old_thread_name, sizeof(old_thread_name));
struct timespec current_time = swipr_sampler_get_current_time();
struct swipr_minidump *minidumps = NULL;
#if !defined(__linux__) && !defined(__APPLE__)
fprintf(output,
"[SWIPR] MESG { \"message\": \"Unsupported OS, cannot generate samples yet.\", \"exit\": 1 }\n");
return 1;
#endif
minidumps = calloc(SWIPR_MAX_MUTATOR_THREADS, sizeof(*minidumps));
if (!minidumps) {
fprintf(output,
"[SWIPR] MESG { \"message\": \"ProfileRecorder could not allocate memory to collect minidumps.\", \"exit\": 1 }\n");
return 1;
}
int err = swipr_initialise_c2ms(output);
if (err) {
fprintf(output,
"[SWIPR] MESG { \"message\": \"ProfileRecorder initialisation failed, error: %d.\" }\n",
err);
free(minidumps);
minidumps = NULL;
return err;
}
fprintf(output,
"[SWIPR] CONF { "
"\"sampleCount\": %llu, "
"\"microSecondsBetweenSamples\": %llu, "
"\"currentTimeSeconds\": %llu, "
"\"currentTimeNanoseconds\": %llu, "
"}\n",
(unsigned long long)sample_count,
(unsigned long long)usecs_between_samples,
(unsigned long long)current_time.tv_sec,
(unsigned long long)current_time.tv_nsec);
swipr_os_dep_set_current_thread_name("swipr-sampling");
for (size_t sample_no=0; sample_no<sample_count; sample_no++) {
err = swipr_make_sample(minidumps, SWIPR_MAX_MUTATOR_THREADS, &num_minidumps);
if (err) {
fprintf(output,
"[SWIPR] MESG { \"message\": \"Sample %lu failed, error: %d.\" }\n",
sample_no, err);
continue;
}
for (size_t t=0; t<num_minidumps; t++) {
struct swipr_minidump *minidump = &minidumps[t];
fprintf(output,
"[SWIPR] SMPL {"
"\"pid\": %d, "
"\"tid\": %lu, "
"\"name\": \"%s\", "
"\"timeSec\": %ld, "
"\"timeNSec\": %ld"
"}\n",
minidump->md_pid,
(uintptr_t)minidump->md_tid,
minidump->md_thread_name,
minidump->md_time.tv_sec,
minidump->md_time.tv_nsec
);
for (size_t s=0; s<minidump->md_stack_depth; s++) {
fprintf(output,
"[SWIPR] STCK {"
"\"ip\": \"0x%lx\", "
"\"sp\": \"0x%lx\""
"}\n",
minidump->md_stack[s].sf_ip,
minidump->md_stack[s].sf_sp
);
}
fprintf(output, "[SWIPR] DONE\n");
}
UNSAFE_DEBUG("done sample %lu\n", sample_no);
usleep(usecs_between_samples);
}
swipr_os_dep_set_current_thread_name(old_thread_name);
free(minidumps);
minidumps = NULL;
return 0;
}
static void
profiling_handler(int signo, siginfo_t *info, void *ucontext_untyped)
{
enum swipr_c2ms_state state = atomic_load_explicit(&g_swipr_c2ms.c2ms_state, memory_order_acquire);
swipr_precondition(state == swipr_c2m_sampling);
int my_idx = -1;
const swipr_os_dep_thread_id my_thread_id = swipr_os_dep_get_thread_id();
UNSAFE_DEBUG("thread %lu: collecting context\n", (uintptr_t)my_thread_id);
for (int i=0; i<SWIPR_MAX_MUTATOR_THREADS; i++) {
if (g_swipr_c2ms.c2ms_c2ms[i].c2m_thread_id == my_thread_id) {
my_idx = i;
break;
}
}
swipr_precondition(my_idx >= 0);
ucontext_t *uc = (ucontext_t *)ucontext_untyped;
int err = swipr_fp_unwinder_getcontext(&g_swipr_c2ms.c2ms_c2ms[my_idx].c2m_tiny_context, uc);
swipr_precondition(err == 0);
UNSAFE_DEBUG("thread %lu: done collecting context\n", (uintptr_t)my_thread_id);
swipr_os_dep_sem_signal(g_swipr_c2ms.c2ms_c2ms[my_idx].m2c_proceed);
UNSAFE_DEBUG("thread %lu: waiting for collector\n", (uintptr_t)my_thread_id);
swipr_os_dep_sem_wait(g_swipr_c2ms.c2ms_c2ms[my_idx].c2m_proceed);
UNSAFE_DEBUG("thread %lu: continuing execution\n", (uintptr_t)my_thread_id);
swipr_os_dep_sem_signal(g_swipr_c2ms.c2ms_c2ms[my_idx].m2c_proceed);
}
int swipr_initialize(void) {
struct sigaction act = { 0 };
act.sa_flags = SA_NODEFER | SA_SIGINFO;
act.sa_sigaction = &profiling_handler;
int err = sigaction(SIGPROF, &act, NULL);
swipr_precondition(err == 0);
return 0;
}
```
## /Sources/CProfileRecorderSampler/sampler.h
```h path="/Sources/CProfileRecorderSampler/sampler.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2022-2025 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#pragma once
#include "interface.h"
#include <sys/time.h>
extern struct collector_to_mutators g_swipr_c2ms;
struct swipr_stackframe {
uintptr_t sf_ip;
uintptr_t sf_sp;
};
struct swipr_minidump {
pid_t md_pid;
swipr_os_dep_thread_id md_tid;
struct timespec md_time;
size_t md_stack_depth;
char md_thread_name[32];
struct swipr_stackframe md_stack[SWIPR_MAX_STACK_DEPTH];
};
static struct timespec
swipr_sampler_get_current_time(void) {
struct timeval tv;
struct timespec ts;
gettimeofday(&tv, NULL);
ts.tv_sec = tv.tv_sec + 0;
ts.tv_nsec = ((typeof(ts.tv_nsec))tv.tv_usec) * SWIPR_NSEC_PER_USEC;
return ts;
}
```
## /Sources/CProfileRecorderSwiftELF/empty.c
```c path="/Sources/CProfileRecorderSwiftELF/empty.c"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// This file has been adjusted for ProfileRecorder, originally of the Swift.org open source project
// swift-format-ignore-file
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
```
## /Sources/CProfileRecorderSwiftELF/include/CProfileRecorderSwiftELF.h
```h path="/Sources/CProfileRecorderSwiftELF/include/CProfileRecorderSwiftELF.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// This file has been adjusted for ProfileRecorder, originally of the Swift.org open source project
// swift-format-ignore-file
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef CProfileRecorderSWIFTELF_h
#define CProfileRecorderSWIFTELF_h
#include "swipr-elf.h"
#include "swipr-eh_frame_hdr.h"
#include "swipr-dwarf.h"
#endif
```
## /Sources/CProfileRecorderSwiftELF/include/swipr-dwarf.h
```h path="/Sources/CProfileRecorderSwiftELF/include/swipr-dwarf.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// This file has been adjusted for ProfileRecorder, originally of the Swift.org open source project
// swift-format-ignore-file
//===--- dwarf.h - Definitions of DWARF structures for import into Swift --===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Definitions of DWARF structures for import into Swift code
//
// The types here are taken from the DWARF 5 specification, from
// <https://dwarfstd.org>
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_DWARF_H
#define SWIFT_DWARF_H
#include <inttypes.h>
#ifdef __cplusplus
namespace SWIPRswift {
namespace SWIPRruntime {
#endif
#pragma pack(push, 1)
/* .. Useful macros ......................................................... */
#define internal_SWIPR_DWARF_EXTENSIBLE_ENUM __attribute__((enum_extensibility(open)))
#define internal_SWIPR_DWARF_ENUM(t,n) \
enum internal_SWIPR_DWARF_EXTENSIBLE_ENUM n: t
#define internal_SWIPR_DWARF_BYTECODE \
internal_SWIPR_DWARF_EXTENSIBLE_ENUM : SWIPR_Dwarf_Byte
/* .. Data Representation ................................................... */
// Common sizes (these don't change between 32-bit and 64-bit)
typedef uint8_t SWIPR_Dwarf_Byte;
typedef uint16_t SWIPR_Dwarf_Half;
typedef uint32_t SWIPR_Dwarf_Word;
typedef uint64_t SWIPR_Dwarf_Xword;
typedef int8_t SWIPR_Dwarf_Sbyte;
typedef int32_t SWIPR_Dwarf_Sword;
typedef int64_t SWIPR_Dwarf_Sxword;
// 32-bit sizes
typedef uint32_t SWIPR_Dwarf32_Offset;
typedef uint32_t SWIPR_Dwarf32_Size;
typedef uint32_t SWIPR_Dwarf32_Length; // 0xfffffff0 and above are reserved
// 64-bit sizes
typedef uint64_t SWIPR_Dwarf64_Offset;
typedef uint64_t SWIPR_Dwarf64_Size;
typedef struct {
uint32_t magic; // 0xffffffff means we're using 64-bit
uint64_t length;
} SWIPR_Dwarf64_Length;
/* .. Unit Types ............................................................ */
// Table 7.2
typedef internal_SWIPR_DWARF_ENUM(SWIPR_Dwarf_Byte, SWIPR_Dwarf_UnitType) {
internal_SWIPR_DW_UT_unknown = 0x00,
internal_SWIPR_DW_UT_compile = 0x01,
internal_SWIPR_DW_UT_type = 0x02,
internal_SWIPR_DW_UT_partial = 0x03,
internal_SWIPR_DW_UT_skeleton = 0x04,
internal_SWIPR_DW_UT_split_compile = 0x05,
internal_SWIPR_DW_UT_split_type = 0x06,
internal_SWIPR_DW_UT_lo_user = 0x80,
internal_SWIPR_DW_UT_hi_user = 0xff
} SWIPR_Dwarf_UnitType;
/* .. Tags .................................................................. */
// Table 7.3
typedef internal_SWIPR_DWARF_ENUM(SWIPR_Dwarf_Xword, SWIPR_Dwarf_Tag) {
internal_SWIPR_DW_TAG_array_type = 0x01,
internal_SWIPR_DW_TAG_class_type = 0x02,
internal_SWIPR_DW_TAG_entry_point = 0x03,
internal_SWIPR_DW_TAG_enumeration_type = 0x04,
internal_SWIPR_DW_TAG_formal_parmeter = 0x05,
// Reserved = 0x06,
// Reserved = 0x07,
internal_SWIPR_DW_TAG_imported_declaration = 0x08,
// Reserved = 0x09,
internal_SWIPR_DW_TAG_label = 0x0a,
internal_SWIPR_DW_TAG_lexical_block = 0x0b,
// Reserved = 0x0c,
internal_SWIPR_DW_TAG_member = 0x0d,
// Reserved = 0x0e,
internal_SWIPR_DW_TAG_pointer_type = 0x0f,
internal_SWIPR_DW_TAG_reference_type = 0x10,
internal_SWIPR_DW_TAG_compile_unit = 0x11,
internal_SWIPR_DW_TAG_string_type = 0x12,
internal_SWIPR_DW_TAG_structure_type = 0x13,
// Reserved = 0x14,
internal_SWIPR_DW_TAG_subroutine_type = 0x15,
internal_SWIPR_DW_TAG_typedef = 0x16,
internal_SWIPR_DW_TAG_union_type = 0x17,
internal_SWIPR_DW_TAG_unspecified_parameters = 0x18,
internal_SWIPR_DW_TAG_variant = 0x19,
internal_SWIPR_DW_TAG_common_block = 0x1a,
internal_SWIPR_DW_TAG_common_inclusion = 0x1b,
internal_SWIPR_DW_TAG_inheritance = 0x1c,
internal_SWIPR_DW_TAG_inlined_subroutine = 0x1d,
internal_SWIPR_DW_TAG_module = 0x1e,
internal_SWIPR_DW_TAG_ptr_to_member_type = 0x1f,
internal_SWIPR_DW_TAG_set_type = 0x20,
internal_SWIPR_DW_TAG_subrange_type = 0x21,
internal_SWIPR_DW_TAG_with_stmt = 0x22,
internal_SWIPR_DW_TAG_access_declaration = 0x23,
internal_SWIPR_DW_TAG_base_type = 0x24,
internal_SWIPR_DW_TAG_catch_block = 0x25,
internal_SWIPR_DW_TAG_const_type = 0x26,
internal_SWIPR_DW_TAG_constant = 0x27,
internal_SWIPR_DW_TAG_enumerator = 0x28,
internal_SWIPR_DW_TAG_file_type = 0x29,
internal_SWIPR_DW_TAG_friend = 0x2a,
internal_SWIPR_DW_TAG_namelist = 0x2b,
internal_SWIPR_DW_TAG_namelist_item = 0x2c,
internal_SWIPR_DW_TAG_packed_type = 0x2d,
internal_SWIPR_DW_TAG_subprogram = 0x2e,
internal_SWIPR_DW_TAG_template_type_parameter = 0x2f,
internal_SWIPR_DW_TAG_template_value_parameter = 0x30,
internal_SWIPR_DW_TAG_thrown_type = 0x31,
internal_SWIPR_DW_TAG_try_block = 0x32,
internal_SWIPR_DW_TAG_variant_part = 0x33,
internal_SWIPR_DW_TAG_variable = 0x34,
internal_SWIPR_DW_TAG_volatile_type = 0x35,
internal_SWIPR_DW_TAG_dwarf_procedure = 0x36,
internal_SWIPR_DW_TAG_restrict_type = 0x37,
internal_SWIPR_DW_TAG_interface_type = 0x38,
internal_SWIPR_DW_TAG_namespace = 0x39,
internal_SWIPR_DW_TAG_imported_module = 0x3a,
internal_SWIPR_DW_TAG_unspecified_type = 0x3b,
internal_SWIPR_DW_TAG_partial_unit = 0x3c,
internal_SWIPR_DW_TAG_imported_unit = 0x3d,
// Reserved = 0x3e,
internal_SWIPR_DW_TAG_condition = 0x3f,
internal_SWIPR_DW_TAG_shared_type = 0x40,
internal_SWIPR_DW_TAG_type_unit = 0x41,
internal_SWIPR_DW_TAG_rvalue_reference_type = 0x42,
internal_SWIPR_DW_TAG_template_alias = 0x43,
internal_SWIPR_DW_TAG_coarray_type = 0x44,
internal_SWIPR_DW_TAG_generic_subrange = 0x45,
internal_SWIPR_DW_TAG_dynamic_type = 0x46,
internal_SWIPR_DW_TAG_atomic_type = 0x47,
internal_SWIPR_DW_TAG_call_site = 0x48,
internal_SWIPR_DW_TAG_call_site_parameter = 0x49,
internal_SWIPR_DW_TAG_skeleton_unit = 0x4a,
internal_SWIPR_DW_TAG_immutable_type = 0x4b,
internal_SWIPR_DW_TAG_lo_user = 0x4080,
internal_SWIPR_DW_TAG_hi_user = 0xffff,
} SWIPR_Dwarf_Tag;
/* .. Child Determination Encodings ......................................... */
typedef internal_SWIPR_DWARF_ENUM(SWIPR_Dwarf_Byte, SWIPR_Dwarf_ChildDetermination) {
internal_SWIPR_DW_CHILDREN_no = 0x00,
internal_SWIPR_DW_CHILDREN_yes = 0x01,
} SWIPR_Dwarf_ChildDetermination;
/* .. Attribute Encodings ................................................... */
// Table 7.5
typedef internal_SWIPR_DWARF_ENUM(SWIPR_Dwarf_Word, SWIPR_Dwarf_Attribute) {
internal_SWIPR_DW_AT_sibling = 0x01, // reference
internal_SWIPR_DW_AT_location = 0x02, // exprloc, loclist
internal_SWIPR_DW_AT_name = 0x03, // string
// Reserved = 0x04, // not applicable
// Reserved = 0x05, // not applicable
// Reserved = 0x06, // not applicable
// Reserved = 0x07, // not applicable
// Reserved = 0x08, // not applicable
internal_SWIPR_DW_AT_ordering = 0x09, // constant
// Reserved = 0x0a, // not applicable
internal_SWIPR_DW_AT_byte_size = 0x0b, // constant, exprloc, reference
// Reserved = 0x0c2, // constant, exprloc, reference
internal_SWIPR_DW_AT_bit_size = 0x0d, // constant, exprloc, reference
// Reserved = 0x0e, // not applicable
// Reserved = 0x0f, // not applicable
internal_SWIPR_DW_AT_stmt_list = 0x10, // lineptr
internal_SWIPR_DW_AT_low_pc = 0x11, // address
internal_SWIPR_DW_AT_high_pc = 0x12, // address, constant
internal_SWIPR_DW_AT_language = 0x13, // constant
// Reserved = 0x14, // not applicable
internal_SWIPR_DW_AT_discr = 0x15, // reference
internal_SWIPR_DW_AT_discr_value = 0x16, // constant
internal_SWIPR_DW_AT_visibility = 0x17, // constant
internal_SWIPR_DW_AT_import = 0x18, // reference
internal_SWIPR_DW_AT_string_length = 0x19, // exprloc, loclist, reference
internal_SWIPR_DW_AT_common_reference = 0x1a, // reference
internal_SWIPR_DW_AT_comp_dir = 0x1b, // string
internal_SWIPR_DW_AT_const_value = 0x1c, // block, constant, string
internal_SWIPR_DW_AT_containing_type = 0x1d, // reference
internal_SWIPR_DW_AT_default_value = 0x1e, // constant, reference, flag
// Reserved = 0x1f, // not applicable
internal_SWIPR_DW_AT_inline = 0x20, // constant
internal_SWIPR_DW_AT_is_optional = 0x21, // flag
internal_SWIPR_DW_AT_lower_bound = 0x22, // constant, exprloc, reference
// Reserved = 0x23, // not applicable
// Reserved = 0x24, // not applicable
internal_SWIPR_DW_AT_producer = 0x25, // string
// Reserved = 0x26, // not applicable
internal_SWIPR_DW_AT_prototyped = 0x27, // flag
// Reserved = 0x28, // not applicable
// Reserved = 0x29, // not applicable
internal_SWIPR_DW_AT_return_addr = 0x2a, // exprloc, loclist
// Reserved = 0x2b, // not applicable
internal_SWIPR_DW_AT_start_scope = 0x2c, // constant, rnglist
// Reserved = 0x2d, // not applicable
internal_SWIPR_DW_AT_bit_stride = 0x2e, // constant, exprloc, reference
internal_SWIPR_DW_AT_upper_bound = 0x2f, // constant, exprloc, reference
// Reserved = 0x30, // not applicable
internal_SWIPR_DW_AT_abstract_origin = 0x31, // reference
internal_SWIPR_DW_AT_accessibility = 0x32, // constant
internal_SWIPR_DW_AT_address_class = 0x33, // constant
internal_SWIPR_DW_AT_artificial = 0x34, // flag
internal_SWIPR_DW_AT_base_types = 0x35, // reference
internal_SWIPR_DW_AT_calling_convention = 0x36, // constant
internal_SWIPR_DW_AT_count = 0x37, // constant, exprloc, reference
internal_SWIPR_DW_AT_data_member_location = 0x38, // constant, exprloc, loclist
internal_SWIPR_DW_AT_decl_column = 0x39, // constant
internal_SWIPR_DW_AT_decl_file = 0x3a, // constant
internal_SWIPR_DW_AT_decl_line = 0x3b, // constant
internal_SWIPR_DW_AT_declaration = 0x3c, // flag
internal_SWIPR_DW_AT_discr_list = 0x3d, // block
internal_SWIPR_DW_AT_encoding = 0x3e, // constant
internal_SWIPR_DW_AT_external = 0x3f, // flag
internal_SWIPR_DW_AT_frame_base = 0x40, // exprloc, loclist
internal_SWIPR_DW_AT_friend = 0x41, // reference
internal_SWIPR_DW_AT_identifier_case = 0x42, // constant
// Reserved = 0x43, // macptr
internal_SWIPR_DW_AT_namelist_item = 0x44, // reference
internal_SWIPR_DW_AT_priority = 0x45, // reference
internal_SWIPR_DW_AT_segment = 0x46, // exprloc, loclist
internal_SWIPR_DW_AT_specification = 0x47, // reference
internal_SWIPR_DW_AT_static_link = 0x48, // exprloc, loclist
internal_SWIPR_DW_AT_type = 0x49, // reference
internal_SWIPR_DW_AT_use_location = 0x4a, // exprloc, loclist
internal_SWIPR_DW_AT_variable_parameter = 0x4b, // flag
internal_SWIPR_DW_AT_virtuality = 0x4c, // constant
internal_SWIPR_DW_AT_vtable_elem_location = 0x4d, // exprloc, loclist
internal_SWIPR_DW_AT_allocated = 0x4e, // constant, exprloc, reference
internal_SWIPR_DW_AT_associated = 0x4f, // constant, exprloc, reference
internal_SWIPR_DW_AT_data_location = 0x50, // exprloc
internal_SWIPR_DW_AT_byte_stride = 0x51, // constant, exprloc, reference
internal_SWIPR_DW_AT_entry_pc = 0x52, // address, constant
internal_SWIPR_DW_AT_use_UTF8 = 0x53, // flag
internal_SWIPR_DW_AT_extension = 0x54, // reference
internal_SWIPR_DW_AT_ranges = 0x55, // rnglist
internal_SWIPR_DW_AT_trampoline = 0x56, // address, flag, reference, string
internal_SWIPR_DW_AT_call_column = 0x57, // constant
internal_SWIPR_DW_AT_call_file = 0x58, // constant
internal_SWIPR_DW_AT_call_line = 0x59, // constant
internal_SWIPR_DW_AT_description = 0x5a, // string
internal_SWIPR_DW_AT_binary_scale = 0x5b, // constant
internal_SWIPR_DW_AT_decimal_scale = 0x5c, // constant
internal_SWIPR_DW_AT_small = 0x5d, // reference
internal_SWIPR_DW_AT_decimal_sign = 0x5e, // constant
internal_SWIPR_DW_AT_digit_count = 0x5f, // constant
internal_SWIPR_DW_AT_picture_string = 0x60, // string
internal_SWIPR_DW_AT_mutable = 0x61, // flag
internal_SWIPR_DW_AT_threads_scaled = 0x62, // flag
internal_SWIPR_DW_AT_explicit = 0x63, // flag
internal_SWIPR_DW_AT_object_pointer = 0x64, // reference
internal_SWIPR_DW_AT_endianity = 0x65, // constant
internal_SWIPR_DW_AT_elemental = 0x66, // flag
internal_SWIPR_DW_AT_pure = 0x67, // flag
internal_SWIPR_DW_AT_recursive = 0x68, // flag
internal_SWIPR_DW_AT_signature = 0x69, // reference
internal_SWIPR_DW_AT_main_subprogram = 0x6a, // flag
internal_SWIPR_DW_AT_data_bit_offset = 0x6b, // constant
internal_SWIPR_DW_AT_const_expr = 0x6c, // flag
internal_SWIPR_DW_AT_enum_class = 0x6d, // flag
internal_SWIPR_DW_AT_linkage_name = 0x6e, // string
internal_SWIPR_DW_AT_string_length_bit_size = 0x6f, // constant
internal_SWIPR_DW_AT_string_length_byte_size = 0x70, // constant
internal_SWIPR_DW_AT_rank = 0x71, // constant, exprloc
internal_SWIPR_DW_AT_str_offsets_base = 0x72, // stroffsetsptr
internal_SWIPR_DW_AT_addr_base = 0x73, // addrptr
internal_SWIPR_DW_AT_rnglists_base = 0x74, // rnglistsptr
// Reserved = 0x75, // Unused
internal_SWIPR_DW_AT_dwo_name = 0x76, // string
internal_SWIPR_DW_AT_reference = 0x77, // flag
internal_SWIPR_DW_AT_rvalue_reference = 0x78, // flag
internal_SWIPR_DW_AT_macros = 0x79, // macptr
internal_SWIPR_DW_AT_call_all_calls = 0x7a, // flag
internal_SWIPR_DW_AT_call_all_source_calls = 0x7b, // flag
internal_SWIPR_DW_AT_call_all_tail_calls = 0x7c, // flag
internal_SWIPR_DW_AT_call_return_pc = 0x7d, // address
internal_SWIPR_DW_AT_call_value = 0x7e, // exprloc
internal_SWIPR_DW_AT_call_origin = 0x7f, // exprloc
internal_SWIPR_DW_AT_call_parameter = 0x80, // reference
internal_SWIPR_DW_AT_call_pc = 0x81, // address
internal_SWIPR_DW_AT_call_tail_call = 0x82, // flag
internal_SWIPR_DW_AT_call_target = 0x83, // exprloc
internal_SWIPR_DW_AT_call_target_clobbered = 0x84, // exprloc
internal_SWIPR_DW_AT_call_data_location = 0x85, // exprloc
internal_SWIPR_DW_AT_call_data_value = 0x86, // exprloc
internal_SWIPR_DW_AT_noreturn = 0x87, // flag
internal_SWIPR_DW_AT_alignment = 0x88, // constant
internal_SWIPR_DW_AT_export_symbols = 0x89, // flag
internal_SWIPR_DW_AT_deleted = 0x8a, // flag
internal_SWIPR_DW_AT_defaulted = 0x8b, // constant
internal_SWIPR_DW_AT_loclists_base = 0x8c, // loclistsptr
internal_SWIPR_DW_AT_lo_user = 0x2000, // —
internal_SWIPR_DW_AT_hi_user = 0x3fff, // —
} SWIPR_Dwarf_Attribute;
/* .. Form Encodings ........................................................ */
// Table 7.6
typedef internal_SWIPR_DWARF_ENUM(SWIPR_Dwarf_Byte, SWIPR_Dwarf_Form) {
internal_SWIPR_DW_FORM_addr = 0x01,
// Reserved = 0x02,
internal_SWIPR_DW_FORM_block2 = 0x03,
internal_SWIPR_DW_FORM_block4 = 0x04,
internal_SWIPR_DW_FORM_data2 = 0x05,
internal_SWIPR_DW_FORM_data4 = 0x06,
internal_SWIPR_DW_FORM_data8 = 0x07,
internal_SWIPR_DW_FORM_string = 0x08,
internal_SWIPR_DW_FORM_block = 0x09,
internal_SWIPR_DW_FORM_block1 = 0x0a,
internal_SWIPR_DW_FORM_data1 = 0x0b,
internal_SWIPR_DW_FORM_flag = 0x0c,
internal_SWIPR_DW_FORM_sdata = 0x0d,
internal_SWIPR_DW_FORM_strp = 0x0e,
internal_SWIPR_DW_FORM_udata = 0x0f,
internal_SWIPR_DW_FORM_ref_addr = 0x10,
internal_SWIPR_DW_FORM_ref1 = 0x11,
internal_SWIPR_DW_FORM_ref2 = 0x12,
internal_SWIPR_DW_FORM_ref4 = 0x13,
internal_SWIPR_DW_FORM_ref8 = 0x14,
internal_SWIPR_DW_FORM_ref_udata = 0x15,
internal_SWIPR_DW_FORM_indirect = 0x16,
internal_SWIPR_DW_FORM_sec_offset = 0x17,
internal_SWIPR_DW_FORM_exprloc = 0x18,
internal_SWIPR_DW_FORM_flag_present = 0x19,
internal_SWIPR_DW_FORM_strx = 0x1a,
internal_SWIPR_DW_FORM_addrx = 0x1b,
internal_SWIPR_DW_FORM_ref_sup4 = 0x1c,
internal_SWIPR_DW_FORM_strp_sup = 0x1d,
internal_SWIPR_DW_FORM_data16 = 0x1e,
internal_SWIPR_DW_FORM_line_strp = 0x1f,
internal_SWIPR_DW_FORM_ref_sig8 = 0x20,
internal_SWIPR_DW_FORM_implicit_const = 0x21,
internal_SWIPR_DW_FORM_loclistx = 0x22,
internal_SWIPR_DW_FORM_rnglistx = 0x23,
internal_SWIPR_DW_FORM_ref_sup8 = 0x24,
internal_SWIPR_DW_FORM_strx1 = 0x25,
internal_SWIPR_DW_FORM_strx2 = 0x26,
internal_SWIPR_DW_FORM_strx3 = 0x27,
internal_SWIPR_DW_FORM_strx4 = 0x28,
internal_SWIPR_DW_FORM_addrx1 = 0x29,
internal_SWIPR_DW_FORM_addrx2 = 0x2a,
internal_SWIPR_DW_FORM_addrx3 = 0x2b,
internal_SWIPR_DW_FORM_addrx4 = 0x2c,
} SWIPR_Dwarf_Form;
/* .. DWARF Expressions ..................................................... */
// Table 7.9
enum internal_SWIPR_DWARF_BYTECODE {
// Reserved = 0x01,
// Reserved = 0x02,
internal_SWIPR_DW_OP_addr = 0x03,
// Reserved = 0x04,
// Reserved = 0x05,
internal_SWIPR_DW_OP_deref = 0x06,
// Reserved = 0x07,
internal_SWIPR_DW_OP_const1u = 0x08,
internal_SWIPR_DW_OP_const1s = 0x09,
internal_SWIPR_DW_OP_const2u = 0x0a,
internal_SWIPR_DW_OP_const2s = 0x0b,
internal_SWIPR_DW_OP_const4u = 0x0c,
internal_SWIPR_DW_OP_const4s = 0x0d,
internal_SWIPR_DW_OP_const8u = 0x0e,
internal_SWIPR_DW_OP_const8s = 0x0f,
internal_SWIPR_DW_OP_constu = 0x10,
internal_SWIPR_DW_OP_consts = 0x11,
internal_SWIPR_DW_OP_dup = 0x12,
internal_SWIPR_DW_OP_drop = 0x13,
internal_SWIPR_DW_OP_over = 0x14,
internal_SWIPR_DW_OP_pick = 0x15,
internal_SWIPR_DW_OP_swap = 0x16,
internal_SWIPR_DW_OP_rot = 0x17,
internal_SWIPR_DW_OP_xderef = 0x18,
internal_SWIPR_DW_OP_abs = 0x19,
internal_SWIPR_DW_OP_and = 0x1a,
internal_SWIPR_DW_OP_div = 0x1b,
internal_SWIPR_DW_OP_minus = 0x1c,
internal_SWIPR_DW_OP_mod = 0x1d,
internal_SWIPR_DW_OP_mul = 0x1e,
internal_SWIPR_DW_OP_neg = 0x1f,
internal_SWIPR_DW_OP_not = 0x20,
internal_SWIPR_DW_OP_or = 0x21,
internal_SWIPR_DW_OP_plus = 0x22,
internal_SWIPR_DW_OP_plus_uconst = 0x23,
internal_SWIPR_DW_OP_shl = 0x24,
internal_SWIPR_DW_OP_shr = 0x25,
internal_SWIPR_DW_OP_shra = 0x26,
internal_SWIPR_DW_OP_xor = 0x27,
internal_SWIPR_DW_OP_bra = 0x28,
internal_SWIPR_DW_OP_eq = 0x29,
internal_SWIPR_DW_OP_ge = 0x2a,
internal_SWIPR_DW_OP_gt = 0x2b,
internal_SWIPR_DW_OP_le = 0x2c,
internal_SWIPR_DW_OP_lt = 0x2d,
internal_SWIPR_DW_OP_ne = 0x2e,
internal_SWIPR_DW_OP_skip = 0x2f,
internal_SWIPR_DW_OP_lit0 = 0x30,
internal_SWIPR_DW_OP_lit1 = 0x31,
internal_SWIPR_DW_OP_lit2 = 0x32,
internal_SWIPR_DW_OP_lit3 = 0x33,
internal_SWIPR_DW_OP_lit4 = 0x34,
internal_SWIPR_DW_OP_lit5 = 0x35,
internal_SWIPR_DW_OP_lit6 = 0x36,
internal_SWIPR_DW_OP_lit7 = 0x37,
internal_SWIPR_DW_OP_lit8 = 0x38,
internal_SWIPR_DW_OP_lit9 = 0x39,
internal_SWIPR_DW_OP_lit10 = 0x3a,
internal_SWIPR_DW_OP_lit11 = 0x3b,
internal_SWIPR_DW_OP_lit12 = 0x3c,
internal_SWIPR_DW_OP_lit13 = 0x3d,
internal_SWIPR_DW_OP_lit14 = 0x3e,
internal_SWIPR_DW_OP_lit15 = 0x3f,
internal_SWIPR_DW_OP_lit16 = 0x40,
internal_SWIPR_DW_OP_lit17 = 0x41,
internal_SWIPR_DW_OP_lit18 = 0x42,
internal_SWIPR_DW_OP_lit19 = 0x43,
internal_SWIPR_DW_OP_lit20 = 0x44,
internal_SWIPR_DW_OP_lit21 = 0x45,
internal_SWIPR_DW_OP_lit22 = 0x46,
internal_SWIPR_DW_OP_lit23 = 0x47,
internal_SWIPR_DW_OP_lit24 = 0x48,
internal_SWIPR_DW_OP_lit25 = 0x49,
internal_SWIPR_DW_OP_lit26 = 0x4a,
internal_SWIPR_DW_OP_lit27 = 0x4b,
internal_SWIPR_DW_OP_lit28 = 0x4c,
internal_SWIPR_DW_OP_lit29 = 0x4d,
internal_SWIPR_DW_OP_lit30 = 0x4e,
internal_SWIPR_DW_OP_lit31 = 0x4f,
internal_SWIPR_DW_OP_reg0 = 0x50,
internal_SWIPR_DW_OP_reg1 = 0x51,
internal_SWIPR_DW_OP_reg2 = 0x52,
internal_SWIPR_DW_OP_reg3 = 0x53,
internal_SWIPR_DW_OP_reg4 = 0x54,
internal_SWIPR_DW_OP_reg5 = 0x55,
internal_SWIPR_DW_OP_reg6 = 0x56,
internal_SWIPR_DW_OP_reg7 = 0x57,
internal_SWIPR_DW_OP_reg8 = 0x58,
internal_SWIPR_DW_OP_reg9 = 0x59,
internal_SWIPR_DW_OP_reg10 = 0x5a,
internal_SWIPR_DW_OP_reg11 = 0x5b,
internal_SWIPR_DW_OP_reg12 = 0x5c,
internal_SWIPR_DW_OP_reg13 = 0x5d,
internal_SWIPR_DW_OP_reg14 = 0x5e,
internal_SWIPR_DW_OP_reg15 = 0x5f,
internal_SWIPR_DW_OP_reg16 = 0x60,
internal_SWIPR_DW_OP_reg17 = 0x61,
internal_SWIPR_DW_OP_reg18 = 0x62,
internal_SWIPR_DW_OP_reg19 = 0x63,
internal_SWIPR_DW_OP_reg20 = 0x64,
internal_SWIPR_DW_OP_reg21 = 0x65,
internal_SWIPR_DW_OP_reg22 = 0x66,
internal_SWIPR_DW_OP_reg23 = 0x67,
internal_SWIPR_DW_OP_reg24 = 0x68,
internal_SWIPR_DW_OP_reg25 = 0x69,
internal_SWIPR_DW_OP_reg26 = 0x6a,
internal_SWIPR_DW_OP_reg27 = 0x6b,
internal_SWIPR_DW_OP_reg28 = 0x6c,
internal_SWIPR_DW_OP_reg29 = 0x6d,
internal_SWIPR_DW_OP_reg30 = 0x6e,
internal_SWIPR_DW_OP_reg31 = 0x6f,
internal_SWIPR_DW_OP_breg0 = 0x70,
internal_SWIPR_DW_OP_breg1 = 0x71,
internal_SWIPR_DW_OP_breg2 = 0x72,
internal_SWIPR_DW_OP_breg3 = 0x73,
internal_SWIPR_DW_OP_breg4 = 0x74,
internal_SWIPR_DW_OP_breg5 = 0x75,
internal_SWIPR_DW_OP_breg6 = 0x76,
internal_SWIPR_DW_OP_breg7 = 0x77,
internal_SWIPR_DW_OP_breg8 = 0x78,
internal_SWIPR_DW_OP_breg9 = 0x79,
internal_SWIPR_DW_OP_breg10 = 0x7a,
internal_SWIPR_DW_OP_breg11 = 0x7b,
internal_SWIPR_DW_OP_breg12 = 0x7c,
internal_SWIPR_DW_OP_breg13 = 0x7d,
internal_SWIPR_DW_OP_breg14 = 0x7e,
internal_SWIPR_DW_OP_breg15 = 0x7f,
internal_SWIPR_DW_OP_breg16 = 0x80,
internal_SWIPR_DW_OP_breg17 = 0x81,
internal_SWIPR_DW_OP_breg18 = 0x82,
internal_SWIPR_DW_OP_breg19 = 0x83,
internal_SWIPR_DW_OP_breg20 = 0x84,
internal_SWIPR_DW_OP_breg21 = 0x85,
internal_SWIPR_DW_OP_breg22 = 0x86,
internal_SWIPR_DW_OP_breg23 = 0x87,
internal_SWIPR_DW_OP_breg24 = 0x88,
internal_SWIPR_DW_OP_breg25 = 0x89,
internal_SWIPR_DW_OP_breg26 = 0x8a,
internal_SWIPR_DW_OP_breg27 = 0x8b,
internal_SWIPR_DW_OP_breg28 = 0x8c,
internal_SWIPR_DW_OP_breg29 = 0x8d,
internal_SWIPR_DW_OP_breg30 = 0x8e,
internal_SWIPR_DW_OP_breg31 = 0x8f,
internal_SWIPR_DW_OP_regx = 0x90,
internal_SWIPR_DW_OP_fbreg = 0x91,
internal_SWIPR_DW_OP_bregx = 0x92,
internal_SWIPR_DW_OP_piece = 0x93,
internal_SWIPR_DW_OP_deref_size = 0x94,
internal_SWIPR_DW_OP_xderef_size = 0x95,
internal_SWIPR_DW_OP_nop = 0x96,
internal_SWIPR_DW_OP_push_object_address = 0x97,
internal_SWIPR_DW_OP_call2 = 0x98,
internal_SWIPR_DW_OP_call4 = 0x99,
internal_SWIPR_DW_OP_call_ref = 0x9a,
internal_SWIPR_DW_OP_form_tls_address = 0x9b,
internal_SWIPR_DW_OP_call_frame_cfa = 0x9c,
internal_SWIPR_DW_OP_bit_piece = 0x9d,
internal_SWIPR_DW_OP_implicit_value = 0x9e,
internal_SWIPR_DW_OP_stack_value = 0x9f,
internal_SWIPR_DW_OP_implicit_pointer = 0xa0,
internal_SWIPR_DW_OP_addrx = 0xa1,
internal_SWIPR_DW_OP_constx = 0xa2,
internal_SWIPR_DW_OP_entry_value = 0xa3,
internal_SWIPR_DW_OP_const_type = 0xa4,
internal_SWIPR_DW_OP_regval_type = 0xa5,
internal_SWIPR_DW_OP_deref_type = 0xa6,
internal_SWIPR_DW_OP_xderef_type = 0xa7,
internal_SWIPR_DW_OP_convert = 0xa8,
internal_SWIPR_DW_OP_reinterpret = 0xa9,
internal_SWIPR_DW_OP_lo_user = 0xe0,
internal_SWIPR_DW_OP_hi_user = 0xff,
};
/* .. Line Number Information ............................................... */
// Table 7.25
typedef internal_SWIPR_DWARF_ENUM(SWIPR_Dwarf_Byte, SWIPR_Dwarf_LNS_Opcode) {
internal_SWIPR_DW_LNS_extended = 0x00,
internal_SWIPR_DW_LNS_copy = 0x01,
internal_SWIPR_DW_LNS_advance_pc = 0x02,
internal_SWIPR_DW_LNS_advance_line = 0x03,
internal_SWIPR_DW_LNS_set_file = 0x04,
internal_SWIPR_DW_LNS_set_column = 0x05,
internal_SWIPR_DW_LNS_negate_stmt = 0x06,
internal_SWIPR_DW_LNS_set_basic_block = 0x07,
internal_SWIPR_DW_LNS_const_add_pc = 0x08,
internal_SWIPR_DW_LNS_fixed_advance_pc = 0x09,
internal_SWIPR_DW_LNS_set_prologue_end = 0x0a,
internal_SWIPR_DW_LNS_set_epilogue_begin = 0x0b,
internal_SWIPR_DW_LNS_set_isa = 0x0c,
} SWIPR_Dwarf_LNS_Opcode;
// Table 7.26
typedef internal_SWIPR_DWARF_ENUM(SWIPR_Dwarf_Byte, SWIPR_Dwarf_LNE_Opcode) {
internal_SWIPR_DW_LNE_end_sequence = 0x01,
internal_SWIPR_DW_LNE_set_address = 0x02,
internal_SWIPR_DW_LNE_define_file = 0x03,
internal_SWIPR_DW_LNE_set_discriminator = 0x04,
internal_SWIPR_DW_LNE_lo_user = 0x80,
internal_SWIPR_DW_LNE_hi_user = 0xff,
} SWIPR_Dwarf_LNE_Opcode;
// Table 7.27
typedef internal_SWIPR_DWARF_ENUM(SWIPR_Dwarf_Half, SWIPR_Dwarf_Lhdr_Format) {
internal_SWIPR_DW_LNCT_path = 0x0001,
internal_SWIPR_DW_LNCT_directory_index = 0x0002,
internal_SWIPR_DW_LNCT_timestamp = 0x0003,
internal_SWIPR_DW_LNCT_size = 0x0004,
internal_SWIPR_DW_LNCT_MD5 = 0x0005,
internal_SWIPR_DW_LNCT_lo_user = 0x2000,
internal_SWIPR_DW_LNCT_hi_user = 0x3fff,
} SWIPR_Dwarf_Lhdr_Format;
// 6.2.4 The Line Number Program Header
typedef struct {
SWIPR_Dwarf32_Length length;
SWIPR_Dwarf_Half version;
SWIPR_Dwarf_Byte address_size;
SWIPR_Dwarf_Byte segment_selector_size;
SWIPR_Dwarf32_Size header_length;
SWIPR_Dwarf_Byte minimum_instruction_length;
SWIPR_Dwarf_Byte maximum_operations_per_instruction;
SWIPR_Dwarf_Byte default_is_stmt;
SWIPR_Dwarf_Sbyte line_base;
SWIPR_Dwarf_Byte line_range;
SWIPR_Dwarf_Byte opcode_base;
// Variable length fields:
// SWIPR_Dwarf_Byte standard_opcode_lengths[];
// SWIPR_Dwarf_Byte directory_entry_format_count;
// SWIPR_Dwarf_ULEB128 directory_entry_format[];
// SWIPR_Dwarf_ULEB128 directories_count;
// encoded directories[];
// SWIPR_Dwarf_Byte file_name_entry_format_count;
// SWIPR_Dwarf_ULEB128 file_name_entry_format;
// SWIPR_Dwarf_ULEB128 file_names_count;
// encoded file_names[];
} DWARF32_Lhdr;
typedef struct {
SWIPR_Dwarf64_Length length;
SWIPR_Dwarf_Half version;
SWIPR_Dwarf_Byte address_size;
SWIPR_Dwarf_Byte segment_selector_size;
SWIPR_Dwarf64_Size header_length;
SWIPR_Dwarf_Byte minimum_instruction_length;
SWIPR_Dwarf_Byte maximum_operations_per_instruction;
SWIPR_Dwarf_Byte default_is_stmt;
SWIPR_Dwarf_Sbyte line_base;
SWIPR_Dwarf_Byte line_range;
SWIPR_Dwarf_Byte opcode_base;
// Variable length fields:
// SWIPR_Dwarf_Byte standard_opcode_lengths[];
// SWIPR_Dwarf_Byte directory_entry_format_count;
// SWIPR_Dwarf_ULEB128 directory_entry_format[];
// SWIPR_Dwarf_ULEB128 directories_count;
// encoded directories[];
// SWIPR_Dwarf_Byte file_name_entry_format_count;
// SWIPR_Dwarf_ULEB128 file_name_entry_format;
// SWIPR_Dwarf_ULEB128 file_names_count;
// encoded file_names[];
} DWARF64_Lhdr;
/* .. Call frame instruction encodings ...................................... */
// Table 7.29
enum internal_SWIPR_DWARF_BYTECODE {
internal_SWIPR_DW_CFA_advance_loc = 0x40,
internal_SWIPR_DW_CFA_offset = 0x80,
internal_SWIPR_DW_CFA_restore = 0xc0,
internal_SWIPR_DW_CFA_nop = 0x00,
internal_SWIPR_DW_CFA_set_loc = 0x01,
internal_SWIPR_DW_CFA_advance_loc1 = 0x02,
internal_SWIPR_DW_CFA_advance_loc2 = 0x03,
internal_SWIPR_DW_CFA_advance_loc4 = 0x04,
internal_SWIPR_DW_CFA_offset_extended = 0x05,
internal_SWIPR_DW_CFA_restore_extended = 0x06,
internal_SWIPR_DW_CFA_undefined = 0x07,
internal_SWIPR_DW_CFA_same_value = 0x08,
internal_SWIPR_DW_CFA_register = 0x09,
internal_SWIPR_DW_CFA_remember_state = 0x0a,
internal_SWIPR_DW_CFA_restore_state = 0x0b,
internal_SWIPR_DW_CFA_def_cfa = 0x0c,
internal_SWIPR_DW_CFA_def_cfa_register = 0x0d,
internal_SWIPR_DW_CFA_def_cfa_offset = 0x0e,
internal_SWIPR_DW_CFA_def_cfa_expression = 0x0f,
internal_SWIPR_DW_CFA_expression = 0x10,
internal_SWIPR_DW_CFA_offset_extended_sf = 0x11,
internal_SWIPR_DW_CFA_def_cfa_sf = 0x12,
internal_SWIPR_DW_CFA_def_cfa_offset_sf = 0x13,
internal_SWIPR_DW_CFA_val_offset = 0x14,
internal_SWIPR_DW_CFA_val_offset_sf = 0x15,
internal_SWIPR_DW_CFA_val_expression = 0x16,
internal_SWIPR_DW_CFA_lo_user = 0x1c,
internal_SWIPR_DW_CFA_hi_user = 0x3f
};
/* .. Range list encodings .................................................. */
// Table 7.30
typedef internal_SWIPR_DWARF_ENUM(SWIPR_Dwarf_Byte, SWIPR_Dwarf_RLE_Entry) {
internal_SWIPR_DW_RLE_end_of_list = 0x00,
internal_SWIPR_DW_RLE_base_addressx = 0x01,
internal_SWIPR_DW_RLE_startx_endx = 0x02,
internal_SWIPR_DW_RLE_startx_length = 0x03,
internal_SWIPR_DW_RLE_offset_pair = 0x04,
internal_SWIPR_DW_RLE_base_address = 0x05,
internal_SWIPR_DW_RLE_start_end = 0x06,
internal_SWIPR_DW_RLE_start_length = 0x07
} SWIPR_Dwarf_RLE_Entry;
/* .. Common Information Entry .............................................. */
// Table 4.5: Common Information Entry (CIE)
typedef struct {
SWIPR_Dwarf32_Length length;
SWIPR_Dwarf32_Offset CIE_id;
SWIPR_Dwarf_Byte version;
// Followed by variable length fields as follows:
//
// SWIPR_Dwarf_Byte augmentation[]; // NUL terminated string
// SWIPR_Dwarf_Byte address_size;
// SWIPR_Dwarf_Byte segment_selector_size;
// SWIPR_Dwarf_ULEB128 code_alignment_factor;
// SWIPR_Dwarf_SLEB128 data_alignment_factor;
// SWIPR_Dwarf_ULEB128 return_address_register;
// SWIPR_Dwarf_Byte initial_instructions[];
// SWIPR_Dwarf_Byte padding[]
} SWIPR_Dwarf32_CIEHdr;
typedef struct {
SWIPR_Dwarf64_Length length;
SWIPR_Dwarf64_Offset CIE_id;
SWIPR_Dwarf_Byte version;
// Followed by variable length fields as follows:
//
// SWIPR_Dwarf_Byte augmentation[]; // NUL terminated string
// SWIPR_Dwarf_Byte address_size;
// SWIPR_Dwarf_Byte segment_selector_size;
// SWIPR_Dwarf_ULEB128 code_alignment_factor;
// SWIPR_Dwarf_SLEB128 data_alignment_factor;
// SWIPR_Dwarf_ULEB128 return_address_register;
// SWIPR_Dwarf_Byte initial_instructions[];
// SWIPR_Dwarf_Byte padding[]
} SWIPR_Dwarf64_CIEHdr;
/* .. Frame Descriptor Entry ................................................ */
// Table 4.7: Frame Descriptor Entry (FDE)
typedef struct {
SWIPR_Dwarf32_Length length;
SWIPR_Dwarf32_Offset CIE_pointer; // Offset into the section that points at CIE
// Followed by variable fields as follows:
//
// SWIPR_Dwarf_FarAddr initial_location; // May include segment selector and address
// SWIPR_Dwarf_Addr address_range; // Number of bytes of instructions
// SWIPR_Dwarf_Byte instructions[];
// SWIPR_Dwarf_Byte padding[];
} SWIPR_Dwarf32_FDEHdr;
#pragma pack(pop)
#ifdef __cplusplus
} // namespace SWIPRruntime
} // namespace SWIPRswift
#endif
#endif // SWIFT_DWARF_H
```
## /Sources/CProfileRecorderSwiftELF/include/swipr-eh_frame_hdr.h
```h path="/Sources/CProfileRecorderSwiftELF/include/swipr-eh_frame_hdr.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// This file has been adjusted for ProfileRecorder, originally of the Swift.org open source project
// swift-format-ignore-file
//===--- eh_frame_hdr.h - DWARF EH frame header definitions ---------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Defines the format of the .eh_frame_hdr section, which isn't part of the
// DWARF specification (it's a GNU extension).
//
// https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_EH_FRAME_HDR_H
#define SWIFT_EH_FRAME_HDR_H
#include <inttypes.h>
#ifdef __cplusplus
namespace SWIPRswift {
namespace SWIPRruntime {
#endif
/* .. Useful macros ......................................................... */
#define EH_FRAME_OPTIONS(t,n) \
t n; \
enum __attribute__((flag_enum,enum_extensibility(open))): t
/* .. .eh_frame_hdr header .................................................. */
struct EHFrameHdr {
uint8_t version;
uint8_t eh_frame_ptr_enc;
uint8_t fde_count_enc;
uint8_t table_enc;
// encoded eh_frame_ptr;
// encoded fde_count;
// encoded binary_search_table[fde_count];
};
/* .. Constants ............................................................. */
typedef EH_FRAME_OPTIONS(uint8_t, EHFrameEncoding) {
internal_SWIPR_DW_EH_PE_omit = 0xff, // No value is present
internal_SWIPR_DW_EH_PE_uleb128 = 0x01, // Unsigned value encoded using LEB128
internal_SWIPR_DW_EH_PE_udata2 = 0x02, // A 2-byte unsigned value
internal_SWIPR_DW_EH_PE_udata4 = 0x03, // A 4-byte unsigned value
internal_SWIPR_DW_EH_PE_udata8 = 0x04, // An 8-byte unsigned value
internal_SWIPR_DW_EH_PE_sleb128 = 0x09, // Signed value encoded using LEB128
internal_SWIPR_DW_EH_PE_sdata2 = 0x0a, // A 2-byte signed value
internal_SWIPR_DW_EH_PE_sdata4 = 0x0b, // A 4-byte signed value
internal_SWIPR_DW_EH_PE_sdata8 = 0x0c, // An 8-byte signed value
internal_SWIPR_DW_EH_PE_absptr = 0x00, // Absolute, used with no modification
internal_SWIPR_DW_EH_PE_pcrel = 0x10, // Relative to the current program counter
internal_SWIPR_DW_EH_PE_datarel = 0x30, // Relative to the beginning of the .eh_frame_hdr
};
#ifdef __cplusplus
} // namespace SWIPRruntime
} // namespace SWIPRswift
#endif
#endif // SWIFT_EH_FRAME_HDR_H
```
## /Sources/CProfileRecorderSwiftELF/include/swipr-elf.h
```h path="/Sources/CProfileRecorderSwiftELF/include/swipr-elf.h"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// This file has been adjusted for ProfileRecorder, originally of the Swift.org open source project
// swift-format-ignore-file
//===--- elf.h - Definitions of ELF structures for import into Swift ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Definitions of ELF structures for import into Swift code
//
// The types here are taken from the System V ABI update, here:
// <http://www.sco.com/developers/gabi/2012-12-31/contents.html>
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_ELF_H
#define SWIFT_ELF_H
#include <inttypes.h>
#ifdef __cplusplus
namespace SWIPRswift {
namespace SWIPRruntime {
#endif
/* .. Useful macros ......................................................... */
#define ELF_ENUM(t,n) \
enum __attribute__((enum_extensibility(open))) n: t
#define ELF_OPTIONS(t,n) \
t n; \
enum __attribute__((flag_enum,enum_extensibility(open))): t
/* .. Data Representation ................................................... */
// Common sizes (these don't change between 32-bit and 64-bit)
typedef uint8_t SWIPR_Elf_Byte;
typedef uint16_t SWIPR_Elf_Half;
typedef uint32_t SWIPR_Elf_Word;
typedef uint64_t SWIPR_Elf_Xword;
typedef int32_t SWIPR_Elf_Sword;
typedef int64_t SWIPR_Elf_Sxword;
// 32-bit sizes (includes some aliases of the above, for compatibility)
typedef SWIPR_Elf_Byte SWIPR_Elf32_Byte;
typedef uint32_t SWIPR_Elf32_Addr;
typedef SWIPR_Elf_Half SWIPR_Elf32_Half;
typedef uint32_t SWIPR_Elf32_Off;
typedef SWIPR_Elf_Sword SWIPR_Elf32_Sword;
typedef SWIPR_Elf_Word SWIPR_Elf32_Word;
// 64-bit sizes (includes some aliases of the above, for compatibility)
typedef SWIPR_Elf_Byte SWIPR_Elf64_Byte;
typedef uint64_t SWIPR_Elf64_Addr;
typedef uint64_t SWIPR_Elf64_Off;
typedef SWIPR_Elf_Half SWIPR_Elf64_Half;
typedef SWIPR_Elf_Word SWIPR_Elf64_Word;
typedef SWIPR_Elf_Sword SWIPR_Elf64_Sword;
typedef SWIPR_Elf_Xword SWIPR_Elf64_Xword;
typedef SWIPR_Elf_Sxword SWIPR_Elf64_Sxword;
/* .. Constants ............................................................. */
// e_type values
typedef ELF_ENUM(SWIPR_Elf_Half, SWIPR_Elf_Ehdr_Type) {
internal_SWIPR_ET_NONE = 0, // No file type
internal_SWIPR_ET_REL = 1, // Relocatable file
internal_SWIPR_ET_EXEC = 2, // Executable file
internal_SWIPR_ET_DYN = 3, // Shared object file
internal_SWIPR_ET_CORE = 4, // Core file
internal_SWIPR_ET_LOOS = 0xfe00, // Operating system specific
internal_SWIPR_ET_HIOS = 0xfeff, // Operating system specific
internal_SWIPR_ET_LOPROC = 0xff00, // Processor specific
internal_SWIPR_ET_HIPROC = 0xffff, // Processor specific
} SWIPR_Elf_Ehdr_Type;
// e_machine values
typedef ELF_ENUM(SWIPR_Elf_Half, SWIPR_Elf_Ehdr_Machine) {
internal_SWIPR_EM_NONE = 0, // No machine
internal_SWIPR_EM_M32 = 1, // AT&T WE 32100
internal_SWIPR_EM_SPARC = 2, // SPARC
internal_SWIPR_EM_386 = 3, // Intel 80386
internal_SWIPR_EM_68K = 4, // Motorola 68000
internal_SWIPR_EM_88K = 5, // Motorola 88000
internal_SWIPR_EM_860 = 7, // Intel 80860
internal_SWIPR_EM_MIPS = 8, // MIPS I Architecture
internal_SWIPR_EM_S370 = 9, // IBM System/370 Processor
internal_SWIPR_EM_MIPS_RS3_LE = 10, // MIPS RS3000 Little-endian
internal_SWIPR_EM_PARISC = 15, // Hewlett-Packard PA-RISC
internal_SWIPR_EM_VPP500 = 17, // Fujitsu VPP500
internal_SWIPR_EM_SPARC32PLUS = 18, // Enhanced instruction set SPARC
internal_SWIPR_EM_960 = 19, // Intel 80960
internal_SWIPR_EM_PPC = 20, // PowerPC
internal_SWIPR_EM_PPC64 = 21, // 64-bit PowerPC
internal_SWIPR_EM_S390 = 22, // IBM System/390 Processor
internal_SWIPR_EM_SPU = 23, // IBM SPU/SPC
internal_SWIPR_EM_V800 = 36, // NEC V800
internal_SWIPR_EM_FR20 = 37, // Fujitsu FR20
internal_SWIPR_EM_RH32 = 38, // TRW RH-32
internal_SWIPR_EM_RCE = 39, // Motorola RCE
internal_SWIPR_EM_ARM = 40, // ARM 32-bit architecture (AARCH32)
internal_SWIPR_EM_ALPHA = 41, // Digital Alpha
internal_SWIPR_EM_SH = 42, // Hitachi SH
internal_SWIPR_EM_SPARCV9 = 43, // SPARC Version 9
internal_SWIPR_EM_TRICORE = 44, // Siemens TriCore embedded processor
internal_SWIPR_EM_ARC = 45, // Argonaut RISC Core, Argonaut Technologies Inc.
internal_SWIPR_EM_H8_300 = 46, // Hitachi H8/300
internal_SWIPR_EM_H8_300H = 47, // Hitachi H8/300H
internal_SWIPR_EM_H8S = 48, // Hitachi H8S
internal_SWIPR_EM_H8_500 = 49, // Hitachi H8/500
internal_SWIPR_EM_IA_64 = 50, // Intel IA-64 processor architecture
internal_SWIPR_EM_MIPS_X = 51, // Stanford MIPS-X
internal_SWIPR_EM_COLDFIRE = 52, // Motorola ColdFire
internal_SWIPR_EM_68HC12 = 53, // Motorola M68HC12
internal_SWIPR_EM_MMA = 54, // Fujitsu MMA Multimedia Accelerator
internal_SWIPR_EM_PCP = 55, // Siemens PCP
internal_SWIPR_EM_NCPU = 56, // Sony nCPU embedded RISC processor
internal_SWIPR_EM_NDR1 = 57, // Denso NDR1 microprocessor
internal_SWIPR_EM_STARCORE = 58, // Motorola Star*Core processor
internal_SWIPR_EM_ME16 = 59, // Toyota ME16 processor
internal_SWIPR_EM_ST100 = 60, // STMicroelectronics ST100 processor
internal_SWIPR_EM_TINYJ = 61, // Advanced Logic Corp. TinyJ embedded processor family
internal_SWIPR_EM_X86_64 = 62, // AMD x86-64 architecture
internal_SWIPR_EM_PDSP = 63, // Sony DSP Processor
internal_SWIPR_EM_PDP10 = 64, // Digital Equipment Corp. PDP-10
internal_SWIPR_EM_PDP11 = 65, // Digital Equipment Corp. PDP-11
internal_SWIPR_EM_FX66 = 66, // Siemens FX66 microcontroller
internal_SWIPR_EM_ST9PLUS = 67, // STMicroelectronics ST9+ 8/16 bit microcontroller
internal_SWIPR_EM_ST7 = 68, // STMicroelectronics ST7 8-bit microcontroller
internal_SWIPR_EM_68HC16 = 69, // Motorola MC68HC16 Microcontroller
internal_SWIPR_EM_68HC11 = 70, // Motorola MC68HC11 Microcontroller
internal_SWIPR_EM_68HC08 = 71, // Motorola MC68HC08 Microcontroller
internal_SWIPR_EM_68HC05 = 72, // Motorola MC68HC05 Microcontroller
internal_SWIPR_EM_SVX = 73, // Silicon Graphics SVx
internal_SWIPR_EM_ST19 = 74, // STMicroelectronics ST19 8-bit microcontroller
internal_SWIPR_EM_VAX = 75, // Digital VAX
internal_SWIPR_EM_CRIS = 76, // Axis Communications 32-bit embedded processor
internal_SWIPR_EM_JAVELIN = 77, // Infineon Technologies 32-bit embedded processor
internal_SWIPR_EM_FIREPATH = 78, // Element 14 64-bit DSP Processor
internal_SWIPR_EM_ZSP = 79, // LSI Logic 16-bit DSP Processor
internal_SWIPR_EM_MMIX = 80, // Donald Knuth's educational 64-bit processor
internal_SWIPR_EM_HUANY = 81, // Harvard University machine-independent object files
internal_SWIPR_EM_PRISM = 82, // SiTera Prism
internal_SWIPR_EM_AVR = 83, // Atmel AVR 8-bit microcontroller
internal_SWIPR_EM_FR30 = 84, // Fujitsu FR30
internal_SWIPR_EM_D10V = 85, // Mitsubishi D10V
internal_SWIPR_EM_D30V = 86, // Mitsubishi D30V
internal_SWIPR_EM_V850 = 87, // NEC v850
internal_SWIPR_EM_M32R = 88, // Mitsubishi M32R
internal_SWIPR_EM_MN10300 = 89, // Matsushita MN10300
internal_SWIPR_EM_MN10200 = 90, // Matsushita MN10200
internal_SWIPR_EM_PJ = 91, // picoJava
internal_SWIPR_EM_OPENRISC = 92, // OpenRISC 32-bit embedded processor
internal_SWIPR_EM_ARC_COMPACT = 93, // ARC International ARCompact processor (old spelling/synonym: internal_SWIPR_EM_ARC_A5)
internal_SWIPR_EM_XTENSA = 94, // Tensilica Xtensa Architecture
internal_SWIPR_EM_VIDEOCORE = 95, // Alphamosaic VideoCore processor
internal_SWIPR_EM_TMM_GPP = 96, // Thompson Multimedia General Purpose Processor
internal_SWIPR_EM_NS32K = 97, // National Semiconductor 32000 series
internal_SWIPR_EM_TPC = 98, // Tenor Network TPC processor
internal_SWIPR_EM_SNP1K = 99, // Trebia SNP 1000 processor
internal_SWIPR_EM_ST200 = 100, // STMicroelectronics (www.st.com) ST200 microcontroller
internal_SWIPR_EM_IP2K = 101, // Ubicom IP2xxx microcontroller family
internal_SWIPR_EM_MAX = 102, // MAX Processor
internal_SWIPR_EM_CR = 103, // National Semiconductor CompactRISC microprocessor
internal_SWIPR_EM_F2MC16 = 104, // Fujitsu F2MC16
internal_SWIPR_EM_MSP430 = 105, // Texas Instruments embedded microcontroller msp430
internal_SWIPR_EM_BLACKFIN = 106, // Analog Devices Blackfin (DSP) processor
internal_SWIPR_EM_SE_C33 = 107, // S1C33 Family of Seiko Epson processors
internal_SWIPR_EM_SEP = 108, // Sharp embedded microprocessor
internal_SWIPR_EM_ARCA = 109, // Arca RISC Microprocessor
internal_SWIPR_EM_UNICORE = 110, // Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University
internal_SWIPR_EM_EXCESS = 111, // eXcess: 16/32/64-bit configurable embedded CPU
internal_SWIPR_EM_DXP = 112, // Icera Semiconductor Inc. Deep Execution Processor
internal_SWIPR_EM_ALTERA_NIOS2 = 113, // Altera Nios II soft-core processor
internal_SWIPR_EM_CRX = 114, // National Semiconductor CompactRISC CRX microprocessor
internal_SWIPR_EM_XGATE = 115, // Motorola XGATE embedded processor
internal_SWIPR_EM_C166 = 116, // Infineon C16x/XC16x processor
internal_SWIPR_EM_M16C = 117, // Renesas M16C series microprocessors
internal_SWIPR_EM_DSPIC30F = 118, // Microchip Technology dsPIC30F Digital Signal Controller
internal_SWIPR_EM_CE = 119, // Freescale Communication Engine RISC core
internal_SWIPR_EM_M32C = 120, // Renesas M32C series microprocessors
internal_SWIPR_EM_TSK3000 = 131, // Altium TSK3000 core
internal_SWIPR_EM_RS08 = 132, // Freescale RS08 embedded processor
internal_SWIPR_EM_SHARC = 133, // Analog Devices SHARC family of 32-bit DSP processors
internal_SWIPR_EM_ECOG2 = 134, // Cyan Technology eCOG2 microprocessor
internal_SWIPR_EM_SCORE7 = 135, // Sunplus S+core7 RISC processor
internal_SWIPR_EM_DSP24 = 136, // New Japan Radio (NJR) 24-bit DSP Processor
internal_SWIPR_EM_VIDEOCORE3 = 137, // Broadcom VideoCore III processor
internal_SWIPR_EM_LATTICEMICO32 = 138, // RISC processor for Lattice FPGA architecture
internal_SWIPR_EM_SE_C17 = 139, // Seiko Epson C17 family
internal_SWIPR_EM_TI_C6000 = 140, // The Texas Instruments TMS320C6000 DSP family
internal_SWIPR_EM_TI_C2000 = 141, // The Texas Instruments TMS320C2000 DSP family
internal_SWIPR_EM_TI_C5500 = 142, // The Texas Instruments TMS320C55x DSP family
internal_SWIPR_EM_MMDSP_PLUS = 160, // STMicroelectronics 64bit VLIW Data Signal Processor
internal_SWIPR_EM_CYPRESS_M8C = 161, // Cypress M8C microprocessor
internal_SWIPR_EM_R32C = 162, // Renesas R32C series microprocessors
internal_SWIPR_EM_TRIMEDIA = 163, // NXP Semiconductors TriMedia architecture family
internal_SWIPR_EM_QDSP6 = 164, // QUALCOMM DSP6 Processor
internal_SWIPR_EM_8051 = 165, // Intel 8051 and variants
internal_SWIPR_EM_STXP7X = 166, // STMicroelectronics STxP7x family of configurable and extensible RISC processors
internal_SWIPR_EM_NDS32 = 167, // Andes Technology compact code size embedded RISC processor family
internal_SWIPR_EM_ECOG1 = 168, // Cyan Technology eCOG1X family
internal_SWIPR_EM_ECOG1X = 168, // Cyan Technology eCOG1X family
internal_SWIPR_EM_MAXQ30 = 169, // Dallas Semiconductor MAXQ30 Core Micro-controllers
internal_SWIPR_EM_XIMO16 = 170, // New Japan Radio (NJR) 16-bit DSP Processor
internal_SWIPR_EM_MANIK = 171, // M2000 Reconfigurable RISC Microprocessor
internal_SWIPR_EM_CRAYNV2 = 172, // Cray Inc. NV2 vector architecture
internal_SWIPR_EM_RX = 173, // Renesas RX family
internal_SWIPR_EM_METAG = 174, // Imagination Technologies META processor architecture
internal_SWIPR_EM_MCST_ELBRUS = 175, // MCST Elbrus general purpose hardware architecture
internal_SWIPR_EM_ECOG16 = 176, // Cyan Technology eCOG16 family
internal_SWIPR_EM_CR16 = 177, // National Semiconductor CompactRISC CR16 16-bit microprocessor
internal_SWIPR_EM_ETPU = 178, // Freescale Extended Time Processing Unit
internal_SWIPR_EM_SLE9X = 179, // Infineon Technologies SLE9X core
internal_SWIPR_EM_L10M = 180, // Intel L10M
internal_SWIPR_EM_K10M = 181, // Intel K10M
internal_SWIPR_EM_AARCH64 = 183, // ARM 64-bit architecture (AARCH64)
internal_SWIPR_EM_AVR32 = 185, // Atmel Corporation 32-bit microprocessor family
internal_SWIPR_EM_STM8 = 186, // STMicroeletronics STM8 8-bit microcontroller
internal_SWIPR_EM_TILE64 = 187, // Tilera TILE64 multicore architecture family
internal_SWIPR_EM_TILEPRO = 188, // Tilera TILEPro multicore architecture family
internal_SWIPR_EM_MICROBLAZE = 189, // Xilinx MicroBlaze 32-bit RISC soft processor core
internal_SWIPR_EM_CUDA = 190, // NVIDIA CUDA architecture
internal_SWIPR_EM_TILEGX = 191, // Tilera TILE-Gx multicore architecture family
internal_SWIPR_EM_CLOUDSHIELD = 192, // CloudShield architecture family
internal_SWIPR_EM_COREA_1ST = 193, // KIPO-KAIST Core-A 1st generation processor family
internal_SWIPR_EM_COREA_2ND = 194, // KIPO-KAIST Core-A 2nd generation processor family
internal_SWIPR_EM_ARC_COMPACT2 = 195, // Synopsys ARCompact V2
internal_SWIPR_EM_OPEN8 = 196, // Open8 8-bit RISC soft processor core
internal_SWIPR_EM_RL78 = 197, // Renesas RL78 family
internal_SWIPR_EM_VIDEOCORE5 = 198, // Broadcom VideoCore V processor
internal_SWIPR_EM_78KOR = 199, // Renesas 78KOR family
internal_SWIPR_EM_56800EX = 200, // Freescale 56800EX Digital Signal Controller (DSC)
internal_SWIPR_EM_BA1 = 201, // Beyond BA1 CPU architecture
internal_SWIPR_EM_BA2 = 202, // Beyond BA2 CPU architecture
internal_SWIPR_EM_XCORE = 203, // XMOS xCORE processor family
internal_SWIPR_EM_MCHP_PIC = 204, // Microchip 8-bit PIC(r) family
} SWIPR_Elf_Ehdr_Machine;
// e_version values
typedef ELF_ENUM(SWIPR_Elf_Word, SWIPR_Elf_Ehdr_Version) {
internal_SWIPR_EV_NONE = 0, // Invalid version
internal_SWIPR_EV_CURRENT = 1, // Current version
} SWIPR_Elf_Ehdr_Version;
// e_ident[] identification indices
enum {
internal_SWIPR_EI_MAG0 = 0, // File identification = 0x7f
internal_SWIPR_EI_MAG1 = 1, // File identification = 'E' 0x45
internal_SWIPR_EI_MAG2 = 2, // File identification = 'L' 0x4c
internal_SWIPR_EI_MAG3 = 3, // File identification = 'F' 0x46
internal_SWIPR_EI_CLASS = 4, // File class
internal_SWIPR_EI_DATA = 5, // Data encoding
internal_SWIPR_EI_VERSION = 6, // File version
internal_SWIPR_EI_OSABI = 7, // Operating system/ABI identification
internal_SWIPR_EI_ABIVERSION = 8, // ABI version
internal_SWIPR_EI_PAD = 9, // Start of padding bytes
};
// Magic number
enum : uint8_t {
ELFMAG0 = 0x7f,
ELFMAG1 = 'E',
ELFMAG2 = 'L',
ELFMAG3 = 'F',
};
// File class
typedef ELF_ENUM(SWIPR_Elf_Byte, SWIPR_Elf_Ehdr_Class) {
ELFCLASSNONE = 0, // Invalid class
ELFCLASS32 = 1, // 32-bit objects
ELFCLASS64 = 2, // 64-bit objects
} SWIPR_Elf_Ehdr_Class;
// Data encoding
typedef ELF_ENUM(SWIPR_Elf_Byte, SWIPR_Elf_Ehdr_Data) {
ELFDATANONE = 0, // Invalid data encoding
ELFDATA2LSB = 1, // 2's complement Little Endian
ELFDATA2MSB = 2, // 2's complement Big Endian
} SWIPR_Elk_Ehdr_Data;
// OS/ABI identification
typedef ELF_ENUM(SWIPR_Elf_Byte, SWIPR_Elf_Ehdr_OsAbi) {
internal_SWIPR_ELFOSABI_NONE = 0, // No extensions or unspecified
internal_SWIPR_ELFOSABI_HPUX = 1, // Hewlett-Packard HP-UX
internal_SWIPR_ELFOSABI_NETBSD = 2, // NetBSD
internal_SWIPR_ELFOSABI_GNU = 3, // GNU
internal_SWIPR_ELFOSABI_LINUX = 3, // Linux (historical - alias for internal_SWIPR_ELFOSABI_GNU)
internal_SWIPR_ELFOSABI_SOLARIS = 6, // Sun Solaris
internal_SWIPR_ELFOSABI_AIX = 7, // AIX
internal_SWIPR_ELFOSABI_IRIX = 8, // IRIX
internal_SWIPR_ELFOSABI_FREEBSD = 9, // FreeBSD
internal_SWIPR_ELFOSABI_TRU64 = 10, // Compaq TRU64 UNIX
internal_SWIPR_ELFOSABI_MODESTO = 11, // Novell Modesto
internal_SWIPR_ELFOSABI_OPENBSD = 12, // Open BSD
internal_SWIPR_ELFOSABI_OPENVMS = 13, // Open VMS
internal_SWIPR_ELFOSABI_NSK = 14, // Hewlett-Packard Non-Stop Kernel
internal_SWIPR_ELFOSABI_AROS = 15, // Amiga Research OS
internal_SWIPR_ELFOSABI_FENIXOS = 16, // The FenixOS highly scalable multi-core OS
} SWIPR_Elf_Ehdr_OsAbi;
// Special Section Indices
enum {
internal_SWIPR_SHN_UNDEF = 0, // Undefined, missing, irrelevant or meaningless
internal_SWIPR_SHN_LORESERVE = 0xff00, // Lower bound of reserved indices
internal_SWIPR_SHN_LOPROC = 0xff00, // Processor specific
internal_SWIPR_SHN_HIPROC = 0xff1f,
internal_SWIPR_SHN_LOOS = 0xff20, // OS specific
internal_SWIPR_SHN_HIOS = 0xff3f,
internal_SWIPR_SHN_ABS = 0xfff1, // Absolute (symbols are not relocated)
internal_SWIPR_SHN_COMMON = 0xfff2, // Common
internal_SWIPR_SHN_XINDEX = 0xffff, // Indicates section header index is elsewhere
internal_SWIPR_SHN_HIRESERVE = 0xffff,
};
// Section types
typedef ELF_ENUM(SWIPR_Elf_Word, SWIPR_Elf_Shdr_Type) {
internal_SWIPR_SHT_NULL = 0, // Inactive
internal_SWIPR_SHT_PROGBITS = 1, // Program-defined information
internal_SWIPR_SHT_SYMTAB = 2, // Symbol table
internal_SWIPR_SHT_STRTAB = 3, // String table
internal_SWIPR_SHT_RELA = 4, // Relocation entries with explicit addends
internal_SWIPR_SHT_HASH = 5, // Symbol hash table
internal_SWIPR_SHT_DYNAMIC = 6, // Information for dynamic linking
internal_SWIPR_SHT_NOTE = 7, // Notes
internal_SWIPR_SHT_NOBITS = 8, // Program-defined empty space (bss)
internal_SWIPR_SHT_REL = 9, // Relocation entries without explicit addents
internal_SWIPR_SHT_SHLIB = 10, // Reserved
internal_SWIPR_SHT_DYNSYM = 11,
internal_SWIPR_SHT_INIT_ARRAY = 14, // Pointers to initialization functions
internal_SWIPR_SHT_FINI_ARRAY = 15, // Pointers to termination functions
internal_SWIPR_SHT_PREINIT_ARRAY = 16, // Pointers to pre-initialization functions
internal_SWIPR_SHT_GROUP = 17, // Defines a section group
internal_SWIPR_SHT_SYMTAB_SHNDX = 18, // Section header indices for symtab
internal_SWIPR_SHT_LOOS = 0x60000000, // OS specific
internal_SWIPR_SHT_GNU_ATTRIBUTES = 0x6ffffff5, // Object attributes
internal_SWIPR_SHT_GNU_HASH = 0x6ffffff6, // GNU-style hash table
internal_SWIPR_SHT_GNU_LIBLIST = 0x6ffffff7, // Prelink library list
internal_SWIPR_SHT_CHECKSUM = 0x6ffffff8, // Checksum for DSK content
internal_SWIPR_SHT_LOSUNW = 0x6ffffffa, // Sun-specific
internal_SWIPR_SHT_SUNW_move = 0x6ffffffa,
internal_SWIPR_SHT_SUNW_COMDAT = 0x6ffffffb,
internal_SWIPR_SHT_SUNW_syminfo = 0x6ffffffc,
internal_SWIPR_SHT_GNU_verdef = 0x6ffffffd,
internal_SWIPR_SHT_GNU_verneed = 0x6ffffffe,
internal_SWIPR_SHT_GNU_versym = 0x6fffffff,
internal_SWIPR_SHT_HISUNW = 0x6fffffff,
internal_SWIPR_SHT_HIOS = 0x6fffffff,
internal_SWIPR_SHT_LOPROC = 0x70000000, // Processor specific
internal_SWIPR_SHT_HIPROC = 0x7fffffff,
internal_SWIPR_SHT_LOUSER = 0x80000000, // Application specific
internal_SWIPR_SHT_HIUSER = 0xffffffff,
} SWIPR_Elf_Shdr_Type;
// Section attribute flags (we can't have a type for these because the
// 64-bit section header defines them as 64-bit)
enum {
internal_SWIPR_SHF_WRITE = 0x1, // Writable
internal_SWIPR_SHF_ALLOC = 0x2, // Mapped
internal_SWIPR_SHF_EXECINSTR = 0x4, // Executable instructions
internal_SWIPR_SHF_MERGE = 0x10, // Mergeable elements
internal_SWIPR_SHF_STRINGS = 0x20, // NUL-terminated strings
internal_SWIPR_SHF_INFO_LINK = 0x40, // Section header table index
internal_SWIPR_SHF_LINK_ORDER = 0x80, // Special ordering requirement
internal_SWIPR_SHF_OS_NONCONFORMING = 0x100, // OS-specific processing
internal_SWIPR_SHF_GROUP = 0x200, // Section group member
internal_SWIPR_SHF_TLS = 0x400, // Thread Local Storage
internal_SWIPR_SHF_COMPRESSED = 0x800, // Compressed
internal_SWIPR_SHF_MASKOS = 0x0ff00000, // Operating system specific flags
internal_SWIPR_SHF_MASKPROC = 0xf0000000, // Processor specific flags
};
// Section group flags
enum : SWIPR_Elf_Word {
internal_SWIPR_GRP_COMDAT = 0x1, // COMDAT group
internal_SWIPR_GRP_MASKOS = 0x0ff00000, // Operating system specific flags
internal_SWIPR_GRP_MASKPROC = 0xf0000000, // Processof specific flags
};
// Compression type
typedef ELF_ENUM(SWIPR_Elf_Word, SWIPR_Elf_Chdr_Type) {
internal_SWIPR_ELFCOMPRESS_ZLIB = 1, // DEFLATE algorithm
internal_SWIPR_ELFCOMPRESS_ZSTD = 2, // zstd algorithm
internal_SWIPR_ELFCOMPRESS_LOOS = 0x60000000, // Operating system specific
internal_SWIPR_ELFCOMPRESS_HIOS = 0x6fffffff,
internal_SWIPR_ELFCOMPRESS_LOPROC = 0x70000000, // Processor specific
internal_SWIPR_ELFCOMPRESS_HIPROC = 0x7fffffff
} SWIPR_Elf_Chdr_Type;
// Symbol table entry
enum : SWIPR_Elf_Word {
internal_SWIPR_STN_UNDEF = 0
};
typedef ELF_ENUM(SWIPR_Elf_Byte, SWIPR_Elf_Sym_Binding) {
internal_SWIPR_STB_LOCAL = 0,
internal_SWIPR_STB_GLOBAL = 1,
internal_SWIPR_STB_WEAK = 2,
internal_SWIPR_STB_LOOS = 10, // Operating system specific
internal_SWIPR_STB_HIOS = 12,
internal_SWIPR_STB_LOPROC = 13, // Processor specific
internal_SWIPR_STB_HIPROC = 15
} SWIPR_Elf_Sym_Binding;
typedef ELF_ENUM(SWIPR_Elf_Byte, SWIPR_Elf_Sym_Type) {
internal_SWIPR_STT_NOTYPE = 0, // Unspecified
internal_SWIPR_STT_OBJECT = 1, // Data object (variable, array, &c)
internal_SWIPR_STT_FUNC = 2, // Function or other executable code
internal_SWIPR_STT_SECTION = 3, // A section
internal_SWIPR_STT_FILE = 4, // Source file name
internal_SWIPR_STT_COMMON = 5, // Uninitialized common block
internal_SWIPR_STT_TLS = 6, // Thread Local Storage
internal_SWIPR_STT_LOOS = 10, // Operating system specific
internal_SWIPR_STT_HIOS = 12,
internal_SWIPR_STT_LOPROC = 13, // Processor specific
internal_SWIPR_STT_HIPROC = 15,
} SWIPR_Elf_Sym_Type;
typedef ELF_ENUM(SWIPR_Elf_Byte, SWIPR_Elf_Sym_Visibility) {
internal_SWIPR_STV_DEFAULT = 0,
internal_SWIPR_STV_INTERNAL = 1, // Processor specific but like hidden
internal_SWIPR_STV_HIDDEN = 2, // Not visible from other components
internal_SWIPR_STV_PROTECTED = 3, // Visible but cannot be preempted
} SWIPR_Elf_Sym_Visibility;
// Program header types
typedef ELF_ENUM(SWIPR_Elf_Word, SWIPR_Elf_Phdr_Type) {
internal_SWIPR_PT_NULL = 0, // Element unused
internal_SWIPR_PT_LOAD = 1, // Loadable segment
internal_SWIPR_PT_DYNAMIC = 2, // Dynamic linking information
internal_SWIPR_PT_INTERP = 3, // Interpreter
internal_SWIPR_PT_NOTE = 4, // Auxiliary information
internal_SWIPR_PT_SHLIB = 5, // Reserved
internal_SWIPR_PT_PHDR = 6, // Program header table
internal_SWIPR_PT_TLS = 7, // Thread Local Storage
internal_SWIPR_PT_LOOS = 0x60000000, // Operating system specific
internal_SWIPR_PT_GNU_EH_FRAME = 0x6474e550, // GNU .eh_frame_hdr segment
internal_SWIPR_PT_GNU_STACK = 0x6474e551, // Indicates stack executability
internal_SWIPR_PT_GNU_RELRO = 0x6474e552, // Read-only after relocation
internal_SWIPR_PT_LOSUNW = 0x6ffffffa,
internal_SWIPR_PT_SUNWBSS = 0x6ffffffa,
internal_SWIPR_PT_SUNWSTACK = 0x6ffffffb,
internal_SWIPR_PT_HISUNW = 0x6fffffff,
internal_SWIPR_PT_HIOS = 0x6fffffff,
internal_SWIPR_PT_LOPROC = 0x70000000, // Processor specific
internal_SWIPR_PT_HIPROC = 0x7fffffff,
} SWIPR_Elf_Phdr_Type;
// Program header flags
typedef ELF_OPTIONS(SWIPR_Elf_Word, SWIPR_Elf_Phdr_Flags) {
internal_SWIPR_PF_X = 0x1, // Execute
internal_SWIPR_PF_W = 0x2, // Write
internal_SWIPR_PF_R = 0x4, // Read,
internal_SWIPR_PF_MASKOS = 0x0ff00000, // Operating system specific
internal_SWIPR_PF_MASKPROC = 0xf0000000, // Processor specific
};
// Dynamic linking tags
enum {
internal_SWIPR_DT_NULL = 0, // Marks the end of the _DYNAMIC array
internal_SWIPR_DT_NEEDED = 1, // String table offset of name of needed library
internal_SWIPR_DT_PLTRELSZ = 2, // Total size of relocation entries for PLT
internal_SWIPR_DT_PLTGOT = 3, // Address of PLT/GOT
internal_SWIPR_DT_HASH = 4, // Address of symbol hash table
internal_SWIPR_DT_STRTAB = 5, // Address of string table
internal_SWIPR_DT_SYMTAB = 6, // Address of symbol table
internal_SWIPR_DT_RELA = 7, // Address of internal_SWIPR_DT_RELA relocation table
internal_SWIPR_DT_RELASZ = 8, // Size of internal_SWIPR_DT_RELA table
internal_SWIPR_DT_RELAENT = 9, // Size of internal_SWIPR_DT_RELA entry
internal_SWIPR_DT_STRSZ = 10, // Size of string table
internal_SWIPR_DT_SYMENT = 11, // Size of symbol table entry
internal_SWIPR_DT_INIT = 12, // Address of initialization function
internal_SWIPR_DT_FINI = 13, // Address of termination function
internal_SWIPR_DT_SONAME = 14, // String table offset of name of shared object
internal_SWIPR_DT_RPATH = 15, // String table offset of search path
internal_SWIPR_DT_SYMBOLIC = 16, // Means to search from shared object first
internal_SWIPR_DT_REL = 17, // Address of internal_SWIPR_DT_REL relocation table
internal_SWIPR_DT_RELSZ = 18, // Size of internal_SWIPR_DT_REL table
internal_SWIPR_DT_RELENT = 19, // Size of internal_SWIPR_DT_REL entry
internal_SWIPR_DT_PLTREL = 20, // Type of PLT relocation entry (internal_SWIPR_DT_REL/internal_SWIPR_DT_RELA)
internal_SWIPR_DT_DEBUG = 21, // Used for debugging
internal_SWIPR_DT_TEXTREL = 22, // Means relocations might write to read-only segment
internal_SWIPR_DT_JMPREL = 23, // Address of relocation entries for PLT
internal_SWIPR_DT_BIND_NOW = 24, // Means linker should not lazily bind
internal_SWIPR_DT_INIT_ARRAY = 25, // Address of pointers to initialization functions
internal_SWIPR_DT_FINI_ARRAY = 26, // Address of pointers to termination functions
internal_SWIPR_DT_INIT_ARRAYSZ = 27, // Size in bytes of initialization function array
internal_SWIPR_DT_FINI_ARRAYSZ = 28, // Size in bytes of termination function array
internal_SWIPR_DT_RUNPATH = 29, // String table offset of search path
internal_SWIPR_DT_FLAGS = 30, // Flags
internal_SWIPR_DT_ENCODING = 32, // Tags equal to or above this follow encoding rules
internal_SWIPR_DT_PREINIT_ARRAY = 32, // Address of pre-initialization function array
internal_SWIPR_DT_PREINIT_ARRAYSZ = 33, // Size in bytes of pre-initialization fn array
internal_SWIPR_DT_LOOS = 0x6000000D, // Operating system specific
internal_SWIPR_DT_HIOS = 0x6ffff000,
internal_SWIPR_DT_LOPROC = 0x70000000, // Processor specific
internal_SWIPR_DT_HIPROC = 0x7fffffff,
};
// Dynamic linking flags
enum {
internal_SWIPR_DF_ORIGIN = 0x1, // Uses $ORIGIN substitution string
internal_SWIPR_DF_SYMBOLIC = 0x2, // Search shared object first before usual search
internal_SWIPR_DF_TEXTREL = 0x4, // Relocations may modify read-only segments
internal_SWIPR_DF_BIND_NOW = 0x8, // Linker should not lazily bind
internal_SWIPR_DF_STATIC_TLS = 0x10, // Uses static TLS - must not be dynamically loaded
};
// GNU note types
enum {
internal_SWIPR_NT_GNU_ABI_TAG = 1, // ABI information
internal_SWIPR_NT_GNU_HWCAP = 2, // Synthetic hwcap information
internal_SWIPR_NT_GNU_BUILD_ID = 3, // Build ID
internal_SWIPR_NT_GNU_GOLD_VERSION = 4, // Generated by GNU gold
internal_SWIPR_NT_GNU_PROPERTY_TYPE_0 = 5, // Program property
};
/* .. ELF Header ............................................................ */
#define internal_SWIPR_EI_NIDENT 16
typedef struct {
SWIPR_Elf32_Byte e_ident[internal_SWIPR_EI_NIDENT];
SWIPR_Elf_Ehdr_Type e_type;
SWIPR_Elf_Ehdr_Machine e_machine;
SWIPR_Elf_Ehdr_Version e_version;
SWIPR_Elf32_Addr e_entry;
SWIPR_Elf32_Off e_phoff;
SWIPR_Elf32_Off e_shoff;
SWIPR_Elf32_Word e_flags;
SWIPR_Elf32_Half e_ehsize;
SWIPR_Elf32_Half e_phentsize;
SWIPR_Elf32_Half e_phnum;
SWIPR_Elf32_Half e_shentsize;
SWIPR_Elf32_Half e_shnum;
SWIPR_Elf32_Half e_shstrndx;
} SWIPR_Elf32_Ehdr;
typedef struct {
SWIPR_Elf64_Byte e_ident[internal_SWIPR_EI_NIDENT];
SWIPR_Elf_Ehdr_Type e_type;
SWIPR_Elf_Ehdr_Machine e_machine;
SWIPR_Elf_Ehdr_Version e_version;
SWIPR_Elf64_Addr e_entry;
SWIPR_Elf64_Off e_phoff;
SWIPR_Elf64_Off e_shoff;
SWIPR_Elf64_Word e_flags;
SWIPR_Elf64_Half e_ehsize;
SWIPR_Elf64_Half e_phentsize;
SWIPR_Elf64_Half e_phnum;
SWIPR_Elf64_Half e_shentsize;
SWIPR_Elf64_Half e_shnum;
SWIPR_Elf64_Half e_shstrndx;
} SWIPR_Elf64_Ehdr;
/* .. Section Header ........................................................ */
typedef struct {
SWIPR_Elf32_Word sh_name;
SWIPR_Elf_Shdr_Type sh_type;
SWIPR_Elf32_Word sh_flags;
SWIPR_Elf32_Addr sh_addr;
SWIPR_Elf32_Off sh_offset;
SWIPR_Elf32_Word sh_size;
SWIPR_Elf32_Word sh_link;
SWIPR_Elf32_Word sh_info;
SWIPR_Elf32_Word sh_addralign;
SWIPR_Elf32_Word sh_entsize;
} SWIPR_Elf32_Shdr;
typedef struct {
SWIPR_Elf64_Word sh_name;
SWIPR_Elf_Shdr_Type sh_type;
SWIPR_Elf64_Xword sh_flags;
SWIPR_Elf64_Addr sh_addr;
SWIPR_Elf64_Off sh_offset;
SWIPR_Elf64_Xword sh_size;
SWIPR_Elf64_Word sh_link;
SWIPR_Elf64_Word sh_info;
SWIPR_Elf64_Xword sh_addralign;
SWIPR_Elf64_Xword sh_entsize;
} SWIPR_Elf64_Shdr;
/* .. Compression Header .................................................... */
typedef struct {
SWIPR_Elf_Chdr_Type ch_type;
SWIPR_Elf32_Word ch_size;
SWIPR_Elf32_Word ch_addralign;
} SWIPR_Elf32_Chdr;
typedef struct {
SWIPR_Elf_Chdr_Type ch_type;
SWIPR_Elf64_Word ch_reserved;
SWIPR_Elf64_Xword ch_size;
SWIPR_Elf64_Xword ch_addralign;
} SWIPR_Elf64_Chdr;
/* .. Symbol Table .......................................................... */
typedef struct {
SWIPR_Elf32_Word st_name;
SWIPR_Elf32_Addr st_value;
SWIPR_Elf32_Word st_size;
SWIPR_Elf32_Byte st_info;
SWIPR_Elf32_Byte st_other;
SWIPR_Elf32_Half st_shndx;
} SWIPR_Elf32_Sym;
typedef struct {
SWIPR_Elf64_Word st_name;
SWIPR_Elf64_Byte st_info;
SWIPR_Elf64_Byte st_other;
SWIPR_Elf64_Half st_shndx;
SWIPR_Elf64_Addr st_value;
SWIPR_Elf64_Xword st_size;
} SWIPR_Elf64_Sym;
static inline SWIPR_Elf_Sym_Binding ELF32_ST_BIND(SWIPR_Elf_Byte i) {
return (SWIPR_Elf_Sym_Binding)(i >> 4);
}
static inline SWIPR_Elf_Sym_Type ELF32_ST_TYPE(SWIPR_Elf_Byte i) {
return (SWIPR_Elf_Sym_Type)(i & 0xf);
}
static inline SWIPR_Elf_Byte ELF32_ST_INFO(SWIPR_Elf_Sym_Binding b, SWIPR_Elf_Sym_Type t) {
return (SWIPR_Elf_Byte)((b << 4) | (t & 0xf));
}
static inline SWIPR_Elf_Sym_Binding ELF64_ST_BIND(SWIPR_Elf_Byte i) {
return (SWIPR_Elf_Sym_Binding)(i >> 4);
}
static inline SWIPR_Elf_Sym_Type ELF64_ST_TYPE(SWIPR_Elf_Byte i) {
return (SWIPR_Elf_Sym_Type)(i & 0xf);
}
static inline SWIPR_Elf_Byte ELF64_ST_INFO(SWIPR_Elf_Sym_Binding b, SWIPR_Elf_Sym_Type t) {
return (SWIPR_Elf_Byte)((b << 4) | (t & 0xf));
}
static inline SWIPR_Elf_Sym_Visibility ELF32_ST_VISIBILITY(SWIPR_Elf_Byte o) {
return (SWIPR_Elf_Sym_Visibility)(o & 3);
}
static inline SWIPR_Elf_Sym_Visibility ELF64_ST_VISIBILITY(SWIPR_Elf_Byte o) {
return (SWIPR_Elf_Sym_Visibility)(o & 3);
}
/* .. Relocation ............................................................ */
typedef struct {
SWIPR_Elf32_Addr r_offset;
SWIPR_Elf32_Word r_info;
} SWIPR_Elf32_Rel;
typedef struct {
SWIPR_Elf32_Addr r_offset;
SWIPR_Elf32_Word r_info;
SWIPR_Elf32_Sword r_addend;
} SWIPR_Elf32_Rela;
typedef struct {
SWIPR_Elf64_Addr r_offset;
SWIPR_Elf64_Xword r_info;
} SWIPR_Elf64_Rel;
typedef struct {
SWIPR_Elf64_Addr r_offset;
SWIPR_Elf64_Xword r_info;
SWIPR_Elf64_Sxword r_addend;
} SWIPR_Elf64_Rela;
static inline SWIPR_Elf32_Byte ELF32_R_SYM(SWIPR_Elf32_Word i) { return i >> 8; }
static inline SWIPR_Elf32_Byte ELF32_R_TYPE(SWIPR_Elf32_Word i) { return i & 0xff; }
static inline SWIPR_Elf32_Word ELF32_R_INFO(SWIPR_Elf32_Byte s, SWIPR_Elf32_Byte t) {
return (s << 8) | t;
}
static inline SWIPR_Elf64_Word ELF64_R_SYM(SWIPR_Elf64_Xword i) { return i >> 32; }
static inline SWIPR_Elf64_Word ELF64_R_TYPE(SWIPR_Elf64_Xword i) { return i & 0xffffffff; }
static inline SWIPR_Elf64_Xword ELF64_R_INFO(SWIPR_Elf64_Word s, SWIPR_Elf64_Word t) {
return (((SWIPR_Elf64_Xword)s) << 32) | t;
}
/* .. Program Header ........................................................ */
typedef struct {
SWIPR_Elf_Phdr_Type p_type;
SWIPR_Elf32_Off p_offset;
SWIPR_Elf32_Addr p_vaddr;
SWIPR_Elf32_Addr p_paddr;
SWIPR_Elf32_Word p_filesz;
SWIPR_Elf32_Word p_memsz;
SWIPR_Elf_Phdr_Flags p_flags;
SWIPR_Elf32_Word p_align;
} SWIPR_Elf32_Phdr;
typedef struct {
SWIPR_Elf_Phdr_Type p_type;
SWIPR_Elf_Phdr_Flags p_flags;
SWIPR_Elf64_Off p_offset;
SWIPR_Elf64_Addr p_vaddr;
SWIPR_Elf64_Addr p_paddr;
SWIPR_Elf64_Xword p_filesz;
SWIPR_Elf64_Xword p_memsz;
SWIPR_Elf64_Xword p_align;
} SWIPR_Elf64_Phdr;
/* .. Note Header ........................................................... */
typedef struct {
SWIPR_Elf32_Word n_namesz;
SWIPR_Elf32_Word n_descsz;
SWIPR_Elf32_Word n_type;
} SWIPR_Elf32_Nhdr;
typedef struct {
SWIPR_Elf64_Word n_namesz;
SWIPR_Elf64_Word n_descsz;
SWIPR_Elf64_Word n_type;
} SWIPR_Elf64_Nhdr;
/* .. Dynamic Linking ....................................................... */
typedef struct {
SWIPR_Elf32_Sword d_tag;
union {
SWIPR_Elf32_Word d_val;
SWIPR_Elf32_Addr d_ptr;
} d_un;
} SWIPR_Elf32_Dyn;
typedef struct {
SWIPR_Elf64_Sxword d_tag;
union {
SWIPR_Elf64_Xword d_val;
SWIPR_Elf64_Addr d_ptr;
} d_un;
} SWIPR_Elf64_Dyn;
/* .. Hash Table ............................................................ */
typedef struct {
SWIPR_Elf32_Word h_nbucket;
SWIPR_Elf32_Word h_nchain;
} SWIPR_Elf32_Hash;
typedef struct {
SWIPR_Elf64_Word h_nbucket;
SWIPR_Elf64_Word h_nchain;
} SWIPR_Elf64_Hash;
static inline unsigned long
SWIPR_elf_hash(const unsigned char *name)
{
unsigned long h = 0, g;
while (*name) {
h = (h << 4) + *name++;
if ((g = h & 0xf0000000))
h ^= g >> 24;
h &= ~g;
}
return h;
}
#ifdef __cplusplus
} // namespace SWIPRruntime
} // namespace SWIPRswift
#endif
#endif // ELF_H
```
## /Sources/ProfileRecorder/Documentation.docc/Documentation.md
# ``ProfileRecorder``
An in-process sampler to capture performance traces for your app.
## Overview
To retrieve samples for you app, have your app invoke:
```swift
ProfileRecorderSampler.sharedInstance.requestSamples(
outputFilePath: "/tmp/samples",
count: 1_000,
timeBetweenSamples: .milliseconds(10),
eventLoop: loop
)
```
The example above creates 1000 samples with 10 ms between each sample. With this configuration,
the profile recording server will capture samples over about 10 seconds.
After that, you'll need to (on the machine you're running it) convert the samples into a regular format and symbolicate it.
Optionally, you can have `llvm-symbolizer` symbolise your samples.
You can request this by passing `--use-native-symbolizer false` to `swipr-sample-conv`.
By default (as of Swift Profile Recorder 0.2.6), it will use ProfileRecorder's native symboliser.
That can be done using the command:
```bash
swipr-sample-conv < /tmp/samples | swift demangle --simplified > /tmp/stacks.perf
```
> Note: `swipr-sample-conv` _must_ be run on a system that has access to the very same files in the same versions as the system where the samples were obtained.
Usually this means that you need to run them in a Docker container with the exact same image as what you were running in production.
It can be another container but it needs to have the same files at the same paths to be able to symbolicate the traces.
## Topics
### In-process sampler
- ``ProfileRecorderSampler``
## /Sources/ProfileRecorder/Documentation.docc/ProfileRecorderSampler.md
# ``ProfileRecorder/ProfileRecorderSampler``
## Topics
### Accessing the sampler
- ``sharedInstance``
### Requesting samples
- ``requestSamples(outputFilePath:failIfFileExists:count:timeBetweenSamples:eventLoop:)``
- ``requestSamples(outputFilePath:failIfFileExists:count:timeBetweenSamples:queue:_:)``
### Inspecting the sampler
- ``isSupportedPlatform``
## /Sources/ProfileRecorder/ProfileRecorderSampler.swift
```swift path="/Sources/ProfileRecorder/ProfileRecorderSampler.swift"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#if canImport(Glibc)
@preconcurrency import Glibc // Sendability of stdout/stderr/..., needs to be at the top of the file
#elseif canImport(Musl)
@preconcurrency import Musl
#endif
import NIO
import _NIOFileSystem
import Dispatch
#if canImport(CProfileRecorderSampler) // only on macOS & Linux
@_implementationOnly import CProfileRecorderSampler
#endif
private let globalProfileRecorder: ProfileRecorderSampler = {
#if canImport(CProfileRecorderSampler) // only on macOS & Linux
swipr_initialize()
#endif
return ProfileRecorderSampler()
}()
struct CouldNotOpenFileError: Error {
var path: String
}
struct ProfileRecorderSamplerError: Error {
var code: CInt
}
public final class ProfileRecorderSampler: Sendable {
private let threadPool: NIOThreadPool
internal struct UnsupportedOperation: Error {}
/// A shared instance of a global in-process sampler.
public static var sharedInstance: ProfileRecorderSampler {
return globalProfileRecorder
}
/// A Boolean value that indicates whether this is a supported platform.
public static var isSupportedPlatform: Bool {
#if os(Linux)
return true
#elseif os(macOS)
return true
#else
return false
#endif
}
fileprivate init() {
self.threadPool = NIOThreadPool(numberOfThreads: 1)
self.threadPool.start()
}
private func requestSamples(
output: CFilePointer,
count: Int,
timeBetweenSamples: TimeAmount,
eventLoop: EventLoop
) -> EventLoopFuture<Void> {
#if canImport(CProfileRecorderSampler) // only on macOS & Linux
return self.threadPool.runIfActive(eventLoop: eventLoop) {
let ret = swipr_request_sample(output.handle, .init(count), .init(timeBetweenSamples.nanoseconds / 1000))
fflush(output.handle)
guard ret == 0 else {
throw ProfileRecorderSamplerError(code: ret)
}
}
#else
return eventLoop.makeFailedFuture(UnsupportedOperation())
#endif
}
/// Request and write the _raw_ samples to the output path you provide.
///
/// - note: The samples will need to be symbolicated with `swipr-sample-conv`, alternatively use `ProfileRecorderServer` for automatically
/// getting the samples in a symbolicated standard format.
///
/// - Parameters:
/// - outputFilePath: The output path for the raw samples.
/// - failIfFileExists: A Boolean value that indicates whether the function should fail if the output path file you provided already exists.
/// - count: The number of samples to capture.
/// - timeBetweenSamples: The time between samples.
/// - queue: The dispatch queue on which to run the sampler.
/// - handler: A closure the library calls when the samples are ready, providing the results.
public func requestSamples(
outputFilePath: String,
failIfFileExists: Bool = true,
count: Int,
timeBetweenSamples: TimeAmount,
queue: DispatchQueue,
_ handler: @Sendable @escaping (Result<String, Error>) -> Void
) {
self.requestSamples(
outputFilePath: outputFilePath,
failIfFileExists: failIfFileExists,
count: count,
timeBetweenSamples: timeBetweenSamples,
eventLoop: MultiThreadedEventLoopGroup.singleton.any()
).whenComplete { result in
queue.async {
handler(result.map { outputFilePath })
}
}
}
/// Request and write the _raw_ samples to the output path you provide.
///
/// - note: The samples will need to be symbolicated with `swipr-sample-conv`, alternatively use `ProfileRecorderServer` for automatically
/// getting the samples in a symbolicated standard format.
///
/// - Parameters:
/// - outputFilePath: The output path for the raw samples.
/// - failIfFileExists: A Boolean value that indicates whether the function should fail if the output path file you provided already exists.
/// - count: The number of samples to capture.
/// - timeBetweenSamples: The time between samples.
/// - eventLoop: The event loop on which the sampler runs.
public func requestSamples(
outputFilePath: String,
failIfFileExists: Bool = true,
count: Int,
timeBetweenSamples: TimeAmount,
eventLoop: EventLoop
) -> EventLoopFuture<Void> {
if outputFilePath == "-" {
return self.requestSamples(
output: CFilePointer(stderr),
count: count,
timeBetweenSamples: timeBetweenSamples,
eventLoop: eventLoop
)
} else {
guard let outputRaw = fopen(outputFilePath, "w\(failIfFileExists ? "x" : "")") else {
return eventLoop.makeFailedFuture(CouldNotOpenFileError(path: outputFilePath))
}
let output = CFilePointer(outputRaw)
return self.requestSamples(
output: output,
count: count,
timeBetweenSamples: timeBetweenSamples,
eventLoop: eventLoop
).always { _ in
fclose(output.handle)
}
}
}
/// Request and write the _raw_ samples to the output path you provide.
///
/// - note: The samples will need to be symbolicated with `swipr-sample-conv`, alternatively use `ProfileRecorderServer` for automatically
/// getting the samples in a symbolicated standard format.
///
/// - Parameters:
/// - outputFilePath: The output path for the raw samples.
/// - failIfFileExists: A Boolean value that indicates whether the function should fail if the output path file you provided already exists.
/// - count: The number of samples to capture.
/// - timeBetweenSamples: The time between samples.
/// - eventLoop: The event loop on which the sampler runs.
public func requestSamples(
outputFilePath: String,
failIfFileExists: Bool = true,
count: Int,
timeBetweenSamples: TimeAmount
) async throws {
return try await self.requestSamples(
outputFilePath: outputFilePath,
failIfFileExists: failIfFileExists,
count: count,
timeBetweenSamples: timeBetweenSamples,
eventLoop: .singletonMultiThreadedEventLoopGroup.any()
).get()
}
}
struct CFilePointer: @unchecked Sendable {
let handle: UnsafeMutablePointer<FILE>
init(_ handle: UnsafeMutablePointer<FILE>) {
self.handle = handle
}
}
```
## /Sources/ProfileRecorderHelpers/Helpers.swift
```swift path="/Sources/ProfileRecorderHelpers/Helpers.swift"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2023-2025 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// swift-format-ignore
// Note: Whitespace changes are used to workaround compiler bug
// https://github.com/swiftlang/swift/issues/79285
/// Runs `body` and when body returns or throws, run `finally`.
///
/// This is useful as an async version of this code pattern
///
/// \`\`\`swift
/// let resource = try createResource()
/// defer {
/// try? destroyResource()
/// }
/// return try useResource()
/// \`\`\`
///
/// - note: Even if the task that `asyncDo` is running is is cancelled, the `finally` code will run in a task that
/// is _not_ cancelled. This is crucial to not require all cleanup code to work in an already-cancelled task.
#if compiler(>=6.0)
@inlinable
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
public func asyncDo<R>(
isolation: isolated (any Actor)? = #isolation,
// DO NOT FIX THE WHITESPACE IN THE NEXT LINE UNTIL 5.10 IS UNSUPPORTED
// https://github.com/swiftlang/swift/issues/79285
_ body: () async throws -> sending R, finally: sending @escaping ((any Error)?) async throws -> Void) async throws -> sending R {
let result: R
do {
result = try await body()
} catch {
// `body` failed, we need to invoke `finally` with the `error`.
// This _looks_ unstructured but isn't really because we unconditionally always await the return.
// We need to have an uncancelled task here to assure this is actually running in case we hit a
// cancellation error.
try await Task {
try await finally(error)
}.value
throw error
}
// `body` succeeded, we need to invoke `finally` with `nil` (no error).
// This _looks_ unstructured but isn't really because we unconditionally always await the return.
// We need to have an uncancelled task here to assure this is actually running in case we hit a
// cancellation error.
try await Task {
try await finally(nil)
}.value
return result
}
#else
@inlinable
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
public func asyncDo<R: Sendable>(
_ body: () async throws -> R,
finally: @escaping @Sendable ((any Error)?) async throws -> Void
) async throws -> R {
let result: R
do {
result = try await body()
} catch {
// `body` failed, we need to invoke `finally` with the `error`.
// This _looks_ unstructured but isn't really because we unconditionally always await the return.
// We need to have an uncancelled task here to assure this is actually running in case we hit a
// cancellation error.
try await Task {
try await finally(error)
}.value
throw error
}
// `body` succeeded, we need to invoke `finally` with `nil` (no error).
// This _looks_ unstructured but isn't really because we unconditionally always await the return.
// We need to have an uncancelled task here to assure this is actually running in case we hit a
// cancellation error.
try await Task {
try await finally(nil)
}.value
return result
}
#endif
public enum ProfileRecorderSystemInformation {
public static var defaultArchitecture: String {
#if arch(x86_64)
return "x86_64"
#elseif arch(arm64)
return "arm64"
#elseif arch(arm64e)
return "arm64e"
#elseif arch(arm64_32)
return "arm64_32"
#elseif arch(arm)
return "arm"
#else
#warning("unknown architecture")
return "unknown"
#endif
}
}
```
## /Sources/ProfileRecorderSampleConversion/Coders.swift
```swift path="/Sources/ProfileRecorderSampleConversion/Coders.swift"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Profile Recorder open source project
//
// Copyright (c) 2021-2024 Apple Inc. and the Swift Profile Recorder project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Profile Recorder project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif
public struct Version: Decodable {
public var version: Int
}
public struct Message: Decodable {
public var message: String
public var exit: CInt?
}
public struct SampleConfig: Codable & Hashable & Sendable {
public init(
currentTimeSeconds: Int,
currentTimeNanoseconds: Int,
microSecondsBetweenSamples: Int,
sampleCount: Int
) {
self.currentTimeSeconds = currentTimeSeconds
self.currentTimeNanoseconds = currentTimeNanoseconds
self.microSecondsBetweenSamples = microSecondsBetweenSamples
self.sampleCount = sampleCount
}
public var currentTimeSeconds: Int
public var currentTimeNanoseconds: Int
public var microSecondsBetweenSamples: Int
public var sampleCount: Int
}
public struct DynamicLibMapping: Decodable & Sendable & CustomStringConvertible & Hashable & Comparable {
enum CodingKeys: CodingKey {
case path
case architecture
case segmentSlide
case segmentStartAddress
case segmentEndAddress
}
public var path: String
public var architecture: String
public var segmentSlide: UInt
public var segmentStartAddress: UInt
public var segmentEndAddress: UInt
public init(
path: String,
architecture: String,
segmentSlide: UInt,
segmentStartAddress: UInt,
segmentEndAddress: UInt
) {
self.path = path
self.architecture = architecture
self.segmentSlide = segmentSlide
self.segmentStartAddress = segmentStartAddress
self.segmentEndAddress = segmentEndAddress
}
public init(from decoder: Decoder) throws {
struct FailedToDecodeAddressError: Error {}
let container = try decoder.container(keyedBy: CodingKeys.self)
self.path = try container.decode(String.self, forKey: .path)
self.architecture = try container.decode(String.self, forKey: .architecture)
if let segmentSlide = UInt(try container.decode(String.self, forKey: .segmentSlide).dropFirst(2), radix: 16) {
self.segmentSlide = segmentSlide
} else {
throw FailedToDecodeAddressError()
}
if let startAddress = UInt(
try container.decode(String.self, forKey: .segmentStartAddress).dropFirst(2),
radix: 16
) {
self.segmentStartAddress = startAddress
} else {
throw FailedToDecodeAddressError()
}
if let endAddress = UInt(try container.decode(String.self, forKey: .segmentEndAddress).dropFirst(2), radix: 16)
{
self.segmentEndAddress = endAddress
} else {
throw FailedToDecodeAddressError()
}
}
public static func < (lhs: DynamicLibMapping, rhs: DynamicLibMapping) -> Bool {
return lhs.segmentStartAddress < rhs.segmentStartAddress
}
public var description: String {
return """
DynamicLibMapping {\
path: '\(self.path)' (\(self.architecture)),\
segmentSlide: 0x\(String(self.segmentSlide, radix: 16)),\
segmentStart: 0x\(String(self.segmentStartAddress, radix: 16)),\
segmentEnd: 0x\(String(self.segmentEndAddress, radix: 16))\
}
"""
}
}
public struct SampleHeader: Codable {
public init(
pid: Int,
tid: Int,
name: String,
timeSec: Int,
timeNSec: Int
) {
self.pid = pid
self.tid = tid
self.name = name
self.timeSec = timeSec
self.timeNSec = timeNSec
}
var pid: Int
var tid: Int
var name: String
var timeSec: Int
var timeNSec: Int
}
public struct StackFrame: Codable & Sendable & CustomStringConvertible & Hashable {
public enum CodingKeys: String, CodingKey {
case instructionPointer = "ip"
case stackPointer = "sp"
}
public var instructionPointer: UInt
public var stackPointer: UInt
public init(instructionPointer: UInt, stackPointer: UInt) {
self.instructionPointer = instructionPointer
self.stackPointer = stackPointer
}
public init(from decoder: Decoder) throws {
struct FailedToDecodeAddressError: Error {}
let container = try decoder.container(keyedBy: CodingKeys.self)
if let ip = UInt(try container.decode(String.self, forKey: .instructionPointer).dropFirst(2), radix: 16) {
self.instructionPointer = ip
} else {
throw FailedToDecodeAddressError()
}
if let sp = UInt(try container.decode(String.self, forKey: .stackPointer).dropFirst(2), radix: 16) {
self.stackPointer = sp
} else {
throw FailedToDecodeAddressError()
}
}
public var description: String {
return """
StackFrame {\
ip: 0x\(String(self.instructionPointer, radix: 16)),\
sp: 0x\(String(self.stackPointer, radix: 16))\
}
"""
}
}
```
## /Sources/ProfileRecorderServer/Documentation.docc/Reference/ProfileRecorderServer-ServerInfo.md
# ``ProfileRecorderServer/ProfileRecorderServer/ServerInfo``
## Topics
### Inspecting the state of the profile recording server
- ``startResult``
- ``ServerStartResult``
The content has been capped at 50000 tokens. The user could consider applying other filters to refine the result. The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.