Skip to content

Commit

Permalink
Merge pull request #92 from swiftwasm/katei/drop-foundation
Browse files Browse the repository at this point in the history
Drop Foundation dependency
  • Loading branch information
kateinoigakukun committed May 1, 2024
2 parents e56f0fc + 396a16e commit 63aa2e5
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 93 deletions.
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ let package = Package(
),
.target(
name: "WasmParser",
dependencies: ["WasmTypes"]
dependencies: [
"WasmTypes",
.product(name: "SystemPackage", package: "swift-system"),
]
),
.target(
name: "WasmKitWASI",
Expand Down
39 changes: 20 additions & 19 deletions Sources/CLI/Run/Run.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import ArgumentParser
import Foundation
import SystemPackage
import WasmKitWASI
import WasmKit
Expand Down Expand Up @@ -43,7 +42,7 @@ struct Run: ParsableCommand {
log("Started parsing module", verbose: true)

let module: Module
if verbose {
if verbose, #available(macOS 13.0, *) {
let (parsedModule, parseTime) = try measure {
try parseWasm(filePath: FilePath(path))
}
Expand Down Expand Up @@ -71,23 +70,27 @@ struct Run: ParsableCommand {
invoke = entry
}

let (_, invokeTime) = try measure(execution: invoke)

log("Finished invoking function \"\(path)\": \(invokeTime)", verbose: true)
if #available(macOS 13.0, *) {
let (_, invokeTime) = try measure(execution: invoke)
log("Finished invoking function \"\(path)\": \(invokeTime)", verbose: true)
} else {
try invoke()
}
}

func deriveInterceptor() throws -> (interceptor: GuestTimeProfiler, finalize: () -> Void)? {
guard let outputPath = self.profileOutput else { return nil }
FileManager.default.createFile(atPath: outputPath, contents: nil)
let fileHandle = try FileHandle(forWritingTo: URL(fileURLWithPath: outputPath))
let fileHandle = try FileDescriptor.open(
FilePath(outputPath), .writeOnly, options: .create
)
let profiler = GuestTimeProfiler { data in
try? fileHandle.write(contentsOf: data)
var data = data
_ = data.withUTF8 { try! fileHandle.writeAll($0) }
}
return (
profiler,
{
profiler.finalize()
try! fileHandle.synchronize()
try! fileHandle.close()

print("\nProfile Completed: \(outputPath) can be viewed using https://ui.perfetto.dev/")
Expand Down Expand Up @@ -144,23 +147,21 @@ struct Run: ParsableCommand {
}
}

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
func measure<Result>(
execution: () throws -> Result
) rethrows -> (Result, String) {
let start = DispatchTime.now()
let result = try execution()
let end = DispatchTime.now()

let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
let nanoseconds = NSNumber(value: end.uptimeNanoseconds - start.uptimeNanoseconds)
let formattedTime = numberFormatter.string(from: nanoseconds)! + " ns"
return (result, formattedTime)
var result: Result!
let formattedTime = try ContinuousClock().measure {
result = try execution()
}

return (result, formattedTime.description)
}

@Sendable func log(_ message: String, verbose: Bool = false) {
if !verbose || self.verbose {
fputs(message + "\n", stderr)
try! FileDescriptor.standardError.writeAll((message + "\n").utf8)
}
}
}
1 change: 0 additions & 1 deletion Sources/WASI/FileSystem.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Foundation
import SystemPackage

struct FileAccessMode: OptionSet {
Expand Down
50 changes: 18 additions & 32 deletions Sources/WASI/Platform/File.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Foundation
import SystemPackage

protocol FdWASIEntry: WASIEntry {
Expand Down Expand Up @@ -31,48 +30,34 @@ extension FdWASIFile {
throw WASIAbi.Errno.EBADF
}
// TODO: Use `writev`
let handle = FileHandle(fileDescriptor: fd.rawValue)
var bytesWritten: UInt32 = 0
for iovec in buffer {
try iovec.withHostBufferPointer {
try handle.write(contentsOf: $0)
bytesWritten += try iovec.withHostBufferPointer {
UInt32(try fd.write(UnsafeRawBufferPointer($0)))
}
bytesWritten += iovec.length
}
return bytesWritten
}

@inlinable
func pwrite<Buffer: Sequence>(vectored buffer: Buffer, offset: WASIAbi.FileSize) throws -> WASIAbi.Size where Buffer.Element == WASIAbi.IOVec {
// TODO: Use `pwritev`
let handle = FileHandle(fileDescriptor: fd.rawValue)
let savedOffset = try handle.offset()
try handle.seek(toOffset: offset)
let nwritten = try write(vectored: buffer)
try handle.seek(toOffset: savedOffset)
return nwritten
var currentOffset: Int64 = Int64(offset)
for iovec in buffer {
currentOffset += try iovec.withHostBufferPointer {
Int64(try fd.writeAll(toAbsoluteOffset: currentOffset, $0))
}
}
let nwritten = WASIAbi.FileSize(currentOffset) - offset
return WASIAbi.Size(nwritten)
}

@inlinable
func read<Buffer: Sequence>(into buffer: Buffer) throws -> WASIAbi.Size where Buffer.Element == WASIAbi.IOVec {
// TODO: Use `readv`
let handle = FileHandle(fileDescriptor: fd.rawValue)
var nread: UInt32 = 0
for iovec in buffer {
try iovec.buffer.withHostPointer(count: Int(iovec.length)) { rawBufferStart in
var bufferStart = rawBufferStart.baseAddress!.bindMemory(
to: UInt8.self, capacity: Int(iovec.length)
)
let bufferEnd = bufferStart + Int(iovec.length)
while bufferStart < bufferEnd {
let remaining = bufferEnd - bufferStart
guard let bytes = try handle.read(upToCount: remaining) else {
break
}
bytes.copyBytes(to: bufferStart, count: bytes.count)
bufferStart += bytes.count
}
nread += iovec.length - UInt32(bufferEnd - bufferStart)
nread += try iovec.withHostBufferPointer {
try UInt32(fd.read(into: $0))
}
}
return nread
Expand All @@ -81,11 +66,12 @@ extension FdWASIFile {
@inlinable
func pread<Buffer: Sequence>(into buffer: Buffer, offset: WASIAbi.FileSize) throws -> WASIAbi.Size where Buffer.Element == WASIAbi.IOVec {
// TODO: Use `preadv`
let handle = FileHandle(fileDescriptor: fd.rawValue)
let savedOffset = try handle.offset()
try handle.seek(toOffset: offset)
let nread = try read(into: buffer)
try handle.seek(toOffset: savedOffset)
var nread: UInt32 = 0
for iovec in buffer {
nread += try iovec.withHostBufferPointer {
try UInt32(fd.read(fromAbsoluteOffset: Int64(offset + UInt64(nread)), into: $0))
}
}
return nread
}
}
Expand Down
6 changes: 4 additions & 2 deletions Sources/WASI/RandomBufferGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ extension RandomBufferGenerator where Self: RandomNumberGenerator {
withUnsafeBytes(of: random) { randomBytes in
let startOffset = i * 8
let destination = UnsafeMutableBufferPointer(rebasing: buffer[startOffset..<(startOffset + 8)])
randomBytes.copyBytes(to: destination)
UnsafeMutableRawBufferPointer(destination).copyMemory(from: randomBytes)
}
}

Expand All @@ -33,7 +33,9 @@ extension RandomBufferGenerator where Self: RandomNumberGenerator {
withUnsafeBytes(of: random) { randomBytes in
let startOffset = count * 8
let destination = UnsafeMutableBufferPointer(rebasing: buffer[startOffset..<(startOffset + remaining)])
randomBytes.copyBytes(to: destination)
UnsafeMutableRawBufferPointer(destination).copyMemory(
from: UnsafeRawBufferPointer(start: randomBytes.baseAddress, count: remaining)
)
}
}
}
Expand Down
34 changes: 22 additions & 12 deletions Sources/WASI/WASI.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import Foundation
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
import Darwin
#elseif os(Linux) || os(FreeBSD) || os(Android)
import Glibc
#elseif os(Windows)
import ucrt
#else
#error("Unsupported Platform")
#endif
import SystemExtras
import SystemPackage
import WasmTypes
Expand Down Expand Up @@ -376,9 +384,9 @@ enum WASIAbi {
let buffer: UnsafeGuestRawPointer
let length: WASIAbi.Size

func withHostBufferPointer<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
func withHostBufferPointer<R>(_ body: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R {
try buffer.withHostPointer(count: Int(length)) { hostPointer in
try body(UnsafeRawBufferPointer(hostPointer))
try body(hostPointer)
}
}

Expand Down Expand Up @@ -1382,7 +1390,7 @@ public class WASIBridgeToHost: WASI {
let fd = open(cHostPath, O_DIRECTORY)
if fd < 0 {
let errno = errno
throw POSIXError(POSIXErrorCode(rawValue: errno)!)
throw WASIError(description: "Failed to open preopen path '\(hostPath)': \(String(cString: strerror(errno)))")
}
return FileDescriptor(rawValue: fd)
}
Expand All @@ -1409,8 +1417,8 @@ public class WASIBridgeToHost: WASI {
offsets += 1
let count = arg.utf8CString.withUnsafeBytes { bytes in
let count = UInt32(bytes.count)
_ = buffer.raw.withHostPointer(count: bytes.count) { hostDestBuffer in
bytes.copyBytes(to: hostDestBuffer)
buffer.raw.withHostPointer(count: bytes.count) { hostDestBuffer in
hostDestBuffer.copyMemory(from: bytes)
}
return count
}
Expand All @@ -1434,8 +1442,8 @@ public class WASIBridgeToHost: WASI {
offsets += 1
let count = "\(key)=\(value)".utf8CString.withUnsafeBytes { bytes in
let count = UInt32(bytes.count)
_ = buffer.raw.withHostPointer(count: bytes.count) { hostDestBuffer in
bytes.copyBytes(to: hostDestBuffer)
buffer.raw.withHostPointer(count: bytes.count) { hostDestBuffer in
hostDestBuffer.copyMemory(from: bytes)
}
return count
}
Expand Down Expand Up @@ -1589,8 +1597,8 @@ public class WASIBridgeToHost: WASI {
guard bytes.count <= maxPathLength else {
throw WASIAbi.Errno.ENAMETOOLONG
}
_ = path.withHostPointer(count: Int(maxPathLength)) { buffer in
bytes.copyBytes(to: buffer)
path.withHostPointer(count: Int(maxPathLength)) { buffer in
UnsafeMutableRawBufferPointer(buffer).copyBytes(from: bytes)
}
}
}
Expand Down Expand Up @@ -1650,8 +1658,10 @@ public class WASIBridgeToHost: WASI {
let copyingBytes = min(entry.dirNameLen, totalBufferSize - bufferUsed)
let rangeStart = buffer.baseAddress.raw.advanced(by: bufferUsed)
name.withUTF8 { bytes in
_ = rangeStart.withHostPointer(count: Int(copyingBytes)) { hostBuffer in
bytes.copyBytes(to: hostBuffer, count: Int(copyingBytes))
rangeStart.withHostPointer(count: Int(copyingBytes)) { hostBuffer in
hostBuffer.copyMemory(
from: UnsafeRawBufferPointer(start: bytes.baseAddress, count: Int(copyingBytes))
)
}
}
bufferUsed += copyingBytes
Expand Down
59 changes: 45 additions & 14 deletions Sources/WasmKit/Execution/Runtime/Profiler.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Foundation
import SystemExtras
import SystemPackage

Expand All @@ -15,30 +14,29 @@ public class GuestTimeProfiler: RuntimeInterceptor {
let pid: Int
let name: String
let ts: Int

var jsonLine: String {
#"{"ph":"\#(ph.rawValue)","pid":\#(pid),"name":"\#(JSON.serialize(name))","ts":\#(ts)}"#
}
}

private var output: (Data) -> Void
private var output: (_ line: String) -> Void
private var hasFirstEvent: Bool = false
private let encoder = JSONEncoder()
private let startTime: UInt64

public init(output: @escaping (Data) -> Void) {
public init(output: @escaping (_ line: String) -> Void) {
self.output = output
self.startTime = Self.getTimestamp()
}

private func eventLine(_ event: Event) -> Data? {
return try? encoder.encode(event)
}

private func addEventLine(_ event: Event) {
guard let line = eventLine(event) else { return }
let line = event.jsonLine
if !hasFirstEvent {
self.output("[\n".data(using: .utf8)!)
self.output("[\n")
self.output(line)
hasFirstEvent = true
} else {
self.output(",\n".data(using: .utf8)!)
self.output(",\n")
self.output(line)
}
}
Expand All @@ -65,7 +63,7 @@ public class GuestTimeProfiler: RuntimeInterceptor {
let functionName = try? store.nameRegistry.lookup(address)
let event = Event(
ph: .begin, pid: 1,
name: functionName ?? "unknown function(\(String(format: "0x%x", address)))",
name: functionName ?? "unknown function(0x\(String(address, radix: 16)))",
ts: getDurationSinceStart()
)
addEventLine(event)
Expand All @@ -75,13 +73,46 @@ public class GuestTimeProfiler: RuntimeInterceptor {
let functionName = try? store.nameRegistry.lookup(address)
let event = Event(
ph: .end, pid: 1,
name: functionName ?? "unknown function(\(String(format: "0x%x", address)))",
name: functionName ?? "unknown function(0x\(String(address, radix: 16)))",
ts: getDurationSinceStart()
)
addEventLine(event)
}

public func finalize() {
output("\n]".data(using: .utf8)!)
output("\n]")
}
}

/// Foundation-less JSON serialization
private enum JSON {
static func serialize(_ value: String) -> String {
// https://www.ietf.org/rfc/rfc4627.txt
var output = "\""
for scalar in value.unicodeScalars {
switch scalar {
case "\"":
output += "\\\""
case "\\":
output += "\\\\"
case "\u{08}":
output += "\\b"
case "\u{0C}":
output += "\\f"
case "\n":
output += "\\n"
case "\r":
output += "\\r"
case "\t":
output += "\\t"
case "\u{20}"..."\u{21}", "\u{23}"..."\u{5B}", "\u{5D}"..."\u{10FFFF}":
output.unicodeScalars.append(scalar)
default:
var hex = String(scalar.value, radix: 16, uppercase: true)
hex = String(repeating: "0", count: 4 - hex.count) + hex
output += "\\u" + hex
}
}
return output
}
}
Loading

0 comments on commit 63aa2e5

Please sign in to comment.