From e6fab5c27d82811222848050a22f8c279b14f19e Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Mon, 4 Dec 2023 16:47:42 -0800 Subject: [PATCH] log parsing --- Cargo.lock | 230 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/diag.rs | 78 ++++++++++++--- src/diag_device.rs | 73 ++++++++------ 4 files changed, 338 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d969ab..f3caa6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,27 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bitvec" version = "1.0.1" @@ -14,6 +35,12 @@ dependencies = [ "wyz", ] +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + [[package]] name = "byteorder" version = "1.5.0" @@ -35,6 +62,41 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "crc" version = "3.0.1" @@ -124,6 +186,7 @@ name = "diag" version = "0.1.0" dependencies = [ "bytes", + "chrono", "crc", "deku", "libc", @@ -155,6 +218,29 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -171,18 +257,42 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -311,6 +421,126 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "winnow" version = "0.5.19" diff --git a/Cargo.toml b/Cargo.toml index 8c4ef28..823290a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] bytes = "1.5.0" +chrono = "0.4.31" crc = "3.0.1" deku = "0.16.0" libc = "0.2.150" diff --git a/src/diag.rs b/src/diag.rs index b97904f..20387dd 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -1,5 +1,6 @@ //! Diag protocol serialization/deserialization +use chrono::{DateTime, Local, FixedOffset}; use deku::prelude::*; #[derive(Debug, Clone, DekuWrite)] @@ -43,31 +44,66 @@ pub enum DataType { } #[derive(Debug, Clone, DekuRead)] -pub struct ResponseContainer { +pub struct MessagesContainer { pub data_type: DataType, pub num_responses: u32, #[deku(count = "num_responses")] - pub responses: Vec, + pub messages: Vec, } #[derive(Debug, Clone, DekuRead)] -pub struct HdlcEncapsulatedResponse { +pub struct HdlcEncapsulatedMessage { pub len: u32, #[deku(count = "len")] pub data: Vec, } -// kinda unpleasant deku hackery here. deku expects an enum's variant to be -// right before its data, but in this case, a status value comes between the -// variants and the data. so we need to use deku's context (ctx) feature to pass -// those opcodes down to their respective parsers. #[derive(Debug, Clone, DekuRead)] -pub struct Response { - opcode: u32, - subopcode: u32, - pub status: u32, - #[deku(ctx = "*opcode, *subopcode")] - pub payload: ResponsePayload, +#[deku(type = "u8")] +pub enum Message { + #[deku(id = "16")] + Log { + pending_msgs: u8, + outer_length: u16, + inner_length: u16, + log_type: u16, + timestamp: Timestamp, + #[deku(count = "inner_length - 12")] + payload: Vec, + }, + + // kinda unpleasant deku hackery here. deku expects an enum's variant to be + // right before its data, but in this case, a status value comes between the + // variants and the data. so we need to use deku's context (ctx) feature to + // pass those opcodes down to their respective parsers. + #[deku(id_pat = "_")] + Response { + opcode: u32, + subopcode: u32, + status: u32, + #[deku(ctx = "*opcode, *subopcode")] + payload: ResponsePayload, + }, +} + +#[derive(Debug, Clone, DekuRead)] +#[deku(endian = "little")] +pub struct Timestamp { + pub ts: u64, +} + +impl Timestamp { + pub fn to_datetime(&self) -> DateTime { + // Upper 48 bits: epoch at 1980-01-06 00:00:00, incremented by 1 for 1/800s + // Lower 16 bits: time since last 1/800s tick in 1/32 chip units + let ts_upper = self.ts >> 16; + let ts_lower = self.ts & 0xffff; + let epoch = chrono::DateTime::parse_from_rfc3339("1980-01-06T00:00:00-00:00").unwrap(); + let mut delta_seconds = ts_upper as f64 * 1.25; + delta_seconds += ts_lower as f64 / 40960.0; + let ts_delta = chrono::Duration::milliseconds(delta_seconds as i64); + epoch + ts_delta + } } #[derive(Debug, Clone, DekuRead)] @@ -177,4 +213,20 @@ mod test { 1, 2, 3, 4, ]); } + + #[test] + fn test_message_parsing() { + let msg_bytes = vec![16, 0, 26, 0, 26, 0, 167, 24, 38, 161, 72, 107, 146, 30, 2, 1, 1, 1, 0, 0, 0, 0, 0, 140, 10, 0, 0, 220, 5, 0]; + match Message::from_bytes((msg_bytes.as_slice(), 0)) { + Ok((_, Message::Log { pending_msgs, outer_length, inner_length, log_type, timestamp, payload })) => { + assert_eq!(pending_msgs, 0); + assert_eq!(outer_length, 26); + assert_eq!(inner_length, 26); + assert_eq!(log_type, 6311); + assert_eq!(timestamp.to_datetime().date_naive(), chrono::NaiveDate::from_ymd_opt(2023, 12, 4).unwrap()); + assert_eq!(payload, vec![1, 1, 0, 0, 0, 0, 0, 140, 10, 0, 0, 220, 5, 0]); + }, + _ => panic!("failed to parse message"), + } + } } diff --git a/src/diag_device.rs b/src/diag_device.rs index de3dc35..aba508d 100644 --- a/src/diag_device.rs +++ b/src/diag_device.rs @@ -1,5 +1,5 @@ use crate::hdlc::{hdlc_encapsulate, hdlc_decapsulate, HdlcError}; -use crate::diag::{Response, ResponsePayload, Request, LogConfigRequest, LogConfigResponse, build_log_mask_request, RequestContainer, DataType, ResponseContainer}; +use crate::diag::{Message, ResponsePayload, Request, LogConfigRequest, LogConfigResponse, build_log_mask_request, RequestContainer, DataType, MessagesContainer}; use std::fs::File; use std::io::{Cursor, Read, Write}; @@ -72,32 +72,35 @@ impl DiagDevice { }) } - fn parse_response_container(&self, container: ResponseContainer) -> DiagResult> { + fn parse_response_container(&self, container: MessagesContainer) -> DiagResult> { let mut result = Vec::new(); - for msg in container.responses { - let data = hdlc_decapsulate(msg.data, &self.crc)?; - match Response::from_bytes((&data, 0)) { - Ok(((_, leftover_bytes), res)) => { - if leftover_bytes > 0 { - println!("warning: {} leftover bytes when Response", leftover_bytes); - } - result.push(res); - }, - Err(e) => { - println!("{:?}", data); - println!("error parsing response: {:?}", e); + for msg in container.messages { + match hdlc_decapsulate(msg.data, &self.crc) { + Ok(data) => match Message::from_bytes((&data, 0)) { + Ok(((_, leftover_bytes), res)) => { + if leftover_bytes > 0 { + println!("warning: {} leftover bytes when Response", leftover_bytes); + } + result.push(res); + }, + Err(e) => { + println!("error parsing response: {:?}", e); + }, }, + Err(err) => { + println!("error decapsulating response: {:?}", err); + } } } Ok(result) } - pub fn read_response(&mut self) -> DiagResult> { + pub fn read_response(&mut self) -> DiagResult> { let mut buf = vec![0; BUFFER_LEN]; loop { let _ = self.file.read(&mut buf)?; - let ((_, leftover_bytes), res_container) = ResponseContainer::from_bytes((&buf, 0))?; + let ((_, leftover_bytes), res_container) = MessagesContainer::from_bytes((&buf, 0))?; if leftover_bytes > 0 { println!("warning: {} leftover bytes when parsing ResponseContainer", leftover_bytes); } @@ -124,7 +127,7 @@ impl DiagDevice { let msg = format!("write failed with error code {}", ret); return Err(DiagDeviceError::DeviceReadFailed(msg)); } - println!("{}. wrote {} bytes to device", buf.len(), ret); + println!("wrote {} bytes to device", ret); } Ok(()) } @@ -133,15 +136,18 @@ impl DiagDevice { let req = Request::LogConfig(LogConfigRequest::RetrieveIdRanges); self.write_request(&req)?; - for res in self.read_response()? { - match res.payload { - ResponsePayload::LogConfig(LogConfigResponse::RetrieveIdRanges { log_mask_sizes }) => { - if res.status != 0 { - return Err(DiagDeviceError::RequestFailed(res.status, req)); - } - return Ok(log_mask_sizes); + for msg in self.read_response()? { + match msg { + Message::Log { .. } => println!("skipping log response..."), + Message::Response { payload, status, .. } => match payload { + ResponsePayload::LogConfig(LogConfigResponse::RetrieveIdRanges { log_mask_sizes }) => { + if status != 0 { + return Err(DiagDeviceError::RequestFailed(status, req)); + } + return Ok(log_mask_sizes); + }, + _ => println!("skipping non-LogConfigResponse response..."), }, - _ => println!("skipping non-LogConfigResponse response..."), } } @@ -153,12 +159,17 @@ impl DiagDevice { let req = build_log_mask_request(log_type, log_mask_bitsize); self.write_request(&req)?; - for res in self.read_response()? { - if let ResponsePayload::LogConfig(LogConfigResponse::SetMask) = res.payload { - if res.status != 0 { - return Err(DiagDeviceError::RequestFailed(res.status, req)); - } - return Ok(()); + for msg in self.read_response()? { + match msg { + Message::Log { .. } => println!("skipping log response..."), + Message::Response { payload, status, .. } => { + if let ResponsePayload::LogConfig(LogConfigResponse::SetMask) = payload { + if status != 0 { + return Err(DiagDeviceError::RequestFailed(status, req)); + } + return Ok(()); + } + }, } }