* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This software is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
pub mod types {
pub mod xgpu_recorder {
pub mod v1 {
include!(concat!(env!("OUT_DIR"), "/xgpu.journal.v1.rs"));
pub const RECORD_FILE_MAGIC: u32 = 0x78475055;
pub const FOOTER_MAGIC: u32 = 0x464F4F54;
pub const RECORD_FILE_VERSION: RecorderVersion = RecorderVersion {
major: 1,
minor: 0,
patch: Some(0),
};
use rustix::system;
impl SystemContext {
pub fn new() -> Self {
Self {
hostname: system::uname().nodename().to_string_lossy().into_owned(),
process_id: std::process::id(),
process_name: std::env::current_exe()
.ok()
.and_then(|p| p.file_name().map(|n| n.to_string_lossy().to_string()))
.unwrap_or_else(|| "unknown".to_string()),
cuda_version: Some(String::from("12.2.140")),
driver_version: Some(String::from("535.104.05")),
toolkit_version: Some(String::from("12.2")),
}
}
}
impl RecorderHeader {
pub fn new() -> Self {
Self {
magic: RECORD_FILE_MAGIC,
version: Some(RECORD_FILE_VERSION),
timestamp_ns: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64,
context: Some(SystemContext::new()),
}
}
}
impl RecorderFooter {
pub fn new(total_requests: u64, sha256_digest: [u8; 32]) -> Self {
Self {
blocks: total_requests,
sha256_digest: sha256_digest.to_vec(),
magic: FOOTER_MAGIC,
}
}
}
}
}
}
mod format;
mod logger;
mod player;
use std::path::PathBuf;
use std::time::Instant;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum RecorderError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("serialization error: {0}")]
Serialization(String),
#[error("deserialization error: {0}")]
Deserialization(String),
#[error("file format error: {message}")]
FormatError { message: String },
#[error("buffer overflow: required {required}, available {available}")]
BufferOverflow { required: usize, available: usize },
#[error("invalid record at position {position}")]
InvalidRecord { position: u64 },
#[error("protobuf encoding error: {0}")]
ProtobufEncoding(#[from] prost::EncodeError),
#[error("protobuf decoding error: {0}")]
ProtobufDecoding(#[from] prost::DecodeError),
#[error("file corruption detected: {details}")]
Corruption { details: String },
}
#[derive(Debug, Clone)]
pub struct RecorderConfig {
pub output_path: PathBuf,
pub compression: bool,
pub block_size: usize,
pub max_file_size: u64,
pub sync_interval_secs: u64,
pub checksum_enabled: bool,
}
impl Default for RecorderConfig {
fn default() -> Self {
Self {
output_path: PathBuf::from("requests.rec"),
compression: false,
block_size: 128,
max_file_size: 1024 * 1024 * 1024,
sync_interval_secs: 1,
checksum_enabled: true,
}
}
}
#[derive(Debug, Default, Clone)]
pub struct RecorderStats {
pub total_requests: u64,
pub total_bytes: u64,
pub flush_count: u64,
pub last_flush: Option<Instant>,
}
pub use format::{RecordReader, RecordWriter};
pub use logger::RequestLogger;
pub use player::{RequestPlayer, RequestPlayerBuilder};
pub use crate::types::xgpu_recorder::v1::{
RecordBlock, RecordPayload, RecorderHeader, RecorderVersion, SystemContext, FOOTER_MAGIC,
RECORD_FILE_MAGIC,
};
#[cfg(test)]
mod tests;