Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions FptnVPN/Cpp/FptnVPN-Bridging-Header.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ Distributed under the MIT License (https://opensource.org/licenses/MIT)

#include <fptn_native_lib/src/https/SwiftApiClient.h>
#include <fptn_native_lib/src/websocket/WrapperWebsocketClientBridge.h>
#include "FptnTunnelDiagnostics.h"

#endif // FPTN_VPN_BRIDGING_HEADER
1 change: 1 addition & 0 deletions FptnVPNTunnel/Cpp/FptnVPNTunnel-Bridging-Header.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
#define FPTN_VPN_TUNNEL_BRIDGING_HEADER

#include <fptn_native_lib/src/websocket/WrapperWebsocketClientBridge.h>
#include "FptnTunnelDiagnostics.h"

#endif // FPTN_VPN_TUNNEL_BRIDGING_HEADER
303 changes: 205 additions & 98 deletions FptnVPNTunnel/PacketTunnelProvider.swift

Large diffs are not rendered by default.

110 changes: 110 additions & 0 deletions SharedTunnelRuntime/TunnelFlightRecorder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import Foundation

// PR3: process-lifetime identity. Created once per provider process.
final class ProviderProcessIdentity: @unchecked Sendable {
static let shared = ProviderProcessIdentity()

let pid: UInt64
let processToken: UInt64
let startedMachTime: UInt64
let processSequence: UInt64

private init() {
pid = UInt64(getpid())
processToken = Self.secureRandomUInt64()
startedMachTime = mach_continuous_time()
processSequence = UInt64(Date().timeIntervalSince1970 * 1_000_000)
}

private static func secureRandomUInt64() -> UInt64 {
var value: UInt64 = 0
_ = SecRandomCopyBytes(kSecRandomDefault, 8, &value)
return value
}
}

// PR3: flight event codes matching the C++ EventCode enum.
enum TunnelFlightEventCode: UInt16, Sendable {
case processStarted = 1
case startTunnel = 2
case tunnelConnected = 3
case stopTunnelEntered = 4
case tunnelStopped = 5
case bridgeCreated = 6
case bridgeStartRequested = 7
case bridgeConnected = 8
case bridgeStopRequested = 9
case bridgeTeardownCompleted = 10
case readerStarted = 11
case readerStopped = 12
case senderStarted = 13
case senderStopped = 14
case reconnectScheduled = 15
case reconnectStarted = 16
case transportDisconnected = 17
case pathChanged = 18
case memorySample = 19
case memoryWarning = 20
case memoryCritical = 21
case queueHighWater = 22
case invariantViolation = 23
case snapshotWriteFailed = 24
}

// PR3: RAII Swift wrapper over the C flight recorder.
// Allocation-free on the record path. Thread-safe (C mutex).
// C ABI uses void* handles; Swift stores UnsafeMutableRawPointer.
final class TunnelFlightRecorder: @unchecked Sendable {
private let handle: UnsafeMutableRawPointer
private let identity = ProviderProcessIdentity.shared

init?(path: String) {
guard let h = path.withCString({ fptn_flight_recorder_create($0) }) else {
return nil
}
handle = h
}

deinit {
fptn_flight_recorder_destroy(handle)
}

@inline(__always)
func record(
_ code: TunnelFlightEventCode,
generation: UInt32 = 0,
flags: UInt16 = 0,
value0: UInt64 = 0,
value1: UInt64 = 0,
value2: UInt64 = 0,
synchronize: Bool = false
) -> UInt64 {
fptn_flight_recorder_record(
handle, code.rawValue, flags, generation,
identity.processToken, 0, mach_continuous_time(),
value0, value1, value2, synchronize ? 1 : 0
)
}

@inline(__always)
func recordForSession(
_ code: TunnelFlightEventCode,
sessionToken: UInt64,
generation: UInt32 = 0,
flags: UInt16 = 0,
value0: UInt64 = 0,
value1: UInt64 = 0,
value2: UInt64 = 0,
synchronize: Bool = false
) -> UInt64 {
fptn_flight_recorder_record(
handle, code.rawValue, flags, generation,
identity.processToken, sessionToken, mach_continuous_time(),
value0, value1, value2, synchronize ? 1 : 0
)
}

func flush() -> Bool {
fptn_flight_recorder_flush(handle) != 0
}
}
74 changes: 74 additions & 0 deletions SharedTunnelRuntime/TunnelLifecycleSnapshotStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Foundation

// PR3: snapshot flags matching the C++ SnapshotFlags enum.
enum TunnelSnapshotFlag: UInt32 {
case shutdownRequested = 1
case reasserting = 2
case pathSatisfied = 4
case readLoopActive = 8
case packetReadPending = 16
case nativeStopCleanupCompleted = 32
case recorderError = 64
}

// PR3: RAII Swift wrapper over the C lifecycle snapshot store.
final class TunnelLifecycleSnapshotStore: @unchecked Sendable {
private let handle: UnsafeMutableRawPointer

init?(path: String) {
guard let h = path.withCString({ fptn_lifecycle_store_create($0) }) else {
return nil
}
handle = h
}

deinit {
fptn_lifecycle_store_destroy(handle)
}

func write(
pid: UInt32,
processToken: UInt64,
processSequence: UInt64,
processStartedMachTime: UInt64,
tunnelSessionToken: UInt64,
tunnelStartedMachTime: UInt64,
providerStopReason: UInt32 = 0,
localStopInitiator: UInt16 = 0,
nativeDisconnectCode: UInt16 = 0,
nativeStopOrigin: UInt16 = 0,
flags: UInt32 = 0,
lifecycleState: UInt32 = 0,
websocketGeneration: UInt32 = 0,
reconnectAttempt: UInt32 = 0,
physicalFootprintBytes: UInt64 = 0,
physicalFootprintPeakBytes: UInt64 = 0,
residentBytes: UInt64 = 0,
activeBridges: UInt32 = 0,
activeNativeClients: UInt32 = 0,
nativeActiveOperations: UInt32 = 0,
activeReaderCoroutines: UInt32 = 0,
activeSenderCoroutines: UInt32 = 0,
activeReadLoops: UInt32 = 0,
outboundQueuedBytes: UInt64 = 0,
outboundQueuedBytesPeak: UInt64 = 0,
latestEventSequence: UInt64 = 0,
synchronize: Bool = false
) -> Bool {
fptn_lifecycle_store_write(
handle,
pid, processToken, processSequence, processStartedMachTime,
tunnelSessionToken, tunnelStartedMachTime,
providerStopReason, localStopInitiator,
nativeDisconnectCode, nativeStopOrigin,
mach_continuous_time(),
UInt64(Date().timeIntervalSince1970 * 1_000_000_000),
flags, lifecycleState, websocketGeneration, reconnectAttempt,
physicalFootprintBytes, physicalFootprintPeakBytes, residentBytes,
activeBridges, activeNativeClients, nativeActiveOperations,
activeReaderCoroutines, activeSenderCoroutines, activeReadLoops,
outboundQueuedBytes, outboundQueuedBytesPeak, latestEventSequence,
synchronize ? 1 : 0
) != 0
}
}
158 changes: 158 additions & 0 deletions TunnelDiagnosticsCore/include/FptnTunnelDiagnostics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// FptnTunnelDiagnostics.h — C-compatible public ABI.
// This is the ONLY header visible to Swift.
// Implementation is C++23 with RAII; Swift sees opaque handles.

#ifndef FPTN_TUNNEL_DIAGNOSTICS_H
#define FPTN_TUNNEL_DIAGNOSTICS_H

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

// ── Flight Recorder ──────────────────────────────────────────────────



// Opens or creates a flight recorder ring file.
// Returns NULL on failure.
void *
fptn_flight_recorder_create(const char *path);

// Destroys the recorder and closes the file.
void
fptn_flight_recorder_destroy(void *handle);

// Flushes the ring file to disk.
int
fptn_flight_recorder_flush(void *handle);

// Records an event. Returns the assigned sequence (0 on failure).
uint64_t
fptn_flight_recorder_record(
void *handle,
uint16_t event_code,
uint16_t flags,
uint32_t generation,
uint64_t process_token,
uint64_t session_token,
uint64_t mach_continuous_time,
uint64_t value_0,
uint64_t value_1,
uint64_t value_2,
int synchronize);

// ── Lifecycle Snapshot Store ─────────────────────────────────────────



// Opens or creates a double-buffered snapshot file.
void *
fptn_lifecycle_store_create(const char *path);

void
fptn_lifecycle_store_destroy(void *handle);

// Writes a snapshot. If synchronize is true, fsyncs after the write.
int
fptn_lifecycle_store_write(
void *handle,
uint32_t pid,
uint64_t process_token,
uint64_t process_sequence,
uint64_t process_started_mach_time,
uint64_t tunnel_session_token,
uint64_t tunnel_started_mach_time,
uint32_t provider_stop_reason,
uint16_t local_stop_initiator,
uint16_t native_disconnect_code,
uint16_t native_stop_origin,
uint64_t snapshot_mach_continuous_time,
uint64_t snapshot_wall_time_ns,
uint32_t flags,
uint32_t lifecycle_state,
uint32_t websocket_generation,
uint32_t reconnect_attempt,
uint64_t physical_footprint_bytes,
uint64_t physical_footprint_peak_bytes,
uint64_t resident_bytes,
uint32_t active_bridges,
uint32_t active_native_clients,
uint32_t native_active_operations,
uint32_t active_reader_coroutines,
uint32_t active_sender_coroutines,
uint32_t active_read_loops,
uint64_t outbound_queued_bytes,
uint64_t outbound_queued_bytes_peak,
uint64_t latest_event_sequence,
int synchronize);

// ── Decoded snapshot (returned by reader) ────────────────────────────

typedef struct {
uint64_t write_sequence;
uint32_t pid;
uint64_t process_token;
uint64_t process_sequence;
uint64_t process_started_mach_time;
uint64_t tunnel_session_token;
uint64_t tunnel_started_mach_time;
uint32_t provider_stop_reason;
uint16_t local_stop_initiator;
uint16_t native_disconnect_code;
uint16_t native_stop_origin;
uint64_t snapshot_mach_continuous_time;
uint64_t snapshot_wall_time_ns;
uint32_t flags;
uint32_t lifecycle_state;
uint32_t websocket_generation;
uint32_t reconnect_attempt;
uint64_t physical_footprint_bytes;
uint64_t physical_footprint_peak_bytes;
uint64_t resident_bytes;
uint32_t active_bridges;
uint32_t active_native_clients;
uint32_t native_active_operations;
uint32_t active_reader_coroutines;
uint32_t active_sender_coroutines;
uint32_t active_read_loops;
uint64_t outbound_queued_bytes;
uint64_t outbound_queued_bytes_peak;
uint64_t latest_event_sequence;
} FptnDecodedLifecycleSnapshot;

// Reads the latest valid snapshot from a double-buffered file.
int
fptn_lifecycle_store_read_latest(
const char *path,
FptnDecodedLifecycleSnapshot *output);

// ── Decoded types and reader functions ───────────────────────────────

typedef struct {
uint64_t sequence;
uint64_t mach_continuous_time;
uint64_t process_token;
uint64_t tunnel_session_token;
uint16_t event_code;
uint16_t flags;
uint32_t websocket_generation;
uint64_t value_0;
uint64_t value_1;
uint64_t value_2;
} FptnDecodedFlightEvent;

size_t
fptn_flight_recorder_read_valid(
const char *path,
FptnDecodedFlightEvent *output,
size_t output_capacity);

#ifdef __cplusplus
}
#endif

#endif // FPTN_TUNNEL_DIAGNOSTICS_H
Loading
Loading