|
100 | 100 | //! # etc... |
101 | 101 | //! ``` |
102 | 102 |
|
103 | | -use anyhow::{Result, anyhow}; |
| 103 | +use anyhow::{Result, bail}; |
104 | 104 | use chrono::DateTime; |
105 | 105 | use clap::Parser; |
106 | 106 |
|
107 | 107 | use humility::{ |
108 | 108 | core::Core, |
109 | 109 | hubris::HubrisArchive, |
110 | 110 | log::{Logger, info, warn}, |
111 | | - reflect, |
112 | | - reflect::Load, |
| 111 | + reflect::{self, Load, Value}, |
113 | 112 | }; |
114 | 113 | use humility_cli::{ExecutionContext, humility_cmd}; |
115 | 114 | use humility_doppel as doppel; |
@@ -154,9 +153,24 @@ static SEPARATE_HOST_BOOT_FAIL_NAME: &str = "LAST_HOST_BOOT_FAIL"; |
154 | 153 |
|
155 | 154 | static SEPARATE_LAST_HOST_PANIC_NAME: &str = "LAST_HOST_PANIC"; |
156 | 155 |
|
157 | | -static HOST_STATE_BUF_NAME: &str = |
| 156 | +// Pre-2544 |
| 157 | +static HOST_STATE_BUF_NAME_1: &str = |
158 | 158 | "task_host_sp_comms::ServerImpl::claim_static_resources::BUFS"; |
159 | 159 |
|
| 160 | +// Post-2544 |
| 161 | +static HOST_STATE_BUF_NAME_2: &str = |
| 162 | + "<task_host_sp_comms::ServerImpl>::claim_static_resources::BUFS"; |
| 163 | + |
| 164 | +static PACKRAT_BUF_NAME: &str = "task_packrat::main::BUFS"; |
| 165 | + |
| 166 | +// packrat field names |
| 167 | +const PACKRAT_LAST_PANIC_PAYLOAD: &str = |
| 168 | + "cell.value.host_info.host_panic_payload"; |
| 169 | +const PACKRAT_LAST_PANIC_STATE: &str = "cell.value.host_info.host_panic_state"; |
| 170 | +const PACKRAT_BOOT_FAIL_PAYLOAD: &str = |
| 171 | + "cell.value.host_info.host_panic_payload"; |
| 172 | +const PACKRAT_BOOT_FAIL_STATE: &str = "cell.value.host_info.host_panic_state"; |
| 173 | + |
160 | 174 | /// Mirror type of the internal buf struct in `host_sp_comms`. Must be kept in |
161 | 175 | /// (partial) sync with that structure (fields that are present need to match, |
162 | 176 | /// other fields can be ignored). |
@@ -229,48 +243,147 @@ fn print_escaped_ascii(mut bytes: &[u8]) { |
229 | 243 | println!("{buf}"); |
230 | 244 | } |
231 | 245 |
|
| 246 | +/// Try getting last panic/boot fail from packrat, where it moved to in |
| 247 | +/// https://github.com/oxidecomputer/hubris/pull/2518. |
| 248 | +fn host_bootinfo_packrat( |
| 249 | + hubris: &HubrisArchive, |
| 250 | + core: &mut dyn Core, |
| 251 | + payload: &str, |
| 252 | + state: &str, |
| 253 | +) -> Result<Option<Vec<u8>>> { |
| 254 | + // If this variable doesn't exist, we're probably on a REALLY old version |
| 255 | + // of hubris, but don't return the error here, as it means we'll still want |
| 256 | + // to check the host-sp-comms's vars. |
| 257 | + let lookup = hubris.lookup_qualified_variable(PACKRAT_BUF_NAME); |
| 258 | + let Ok(buf_ty) = lookup else { |
| 259 | + return Ok(None); |
| 260 | + }; |
| 261 | + |
| 262 | + // We do ? the error here, because errors while loading indicate some |
| 263 | + // kind of transport error. |
| 264 | + let buf: Value = humility::reflect::read_variable(hubris, core, buf_ty)?; |
| 265 | + |
| 266 | + // Again, it's possible the image DOES have the packrat buf, but NOT this |
| 267 | + // field (either it is older than #2518, or it is a hostless SP), so treat |
| 268 | + // errors here as "no data". |
| 269 | + let res_payload: Result<Vec<u8>> = buf.field(payload); |
| 270 | + let res_state: Result<Option<Value>> = buf.field(state); |
| 271 | + |
| 272 | + let (Ok(mut payload), Ok(state)) = (res_payload, res_state) else { |
| 273 | + return Ok(None); |
| 274 | + }; |
| 275 | + |
| 276 | + // Cool, cool, we have the fields! Now check if the state is in a place |
| 277 | + // where there is something reasonable to extract. If the variables DO |
| 278 | + // exist, but DON'T have data, return an empty vec. |
| 279 | + let Some(state) = state else { |
| 280 | + return Ok(Some(vec![])); |
| 281 | + }; |
| 282 | + |
| 283 | + let total: u32 = state.field("total_length")?; |
| 284 | + payload.truncate(total as usize); |
| 285 | + |
| 286 | + Ok(Some(payload)) |
| 287 | +} |
| 288 | + |
| 289 | +fn host_boot_fail_spcomms_old( |
| 290 | + hubris: &HubrisArchive, |
| 291 | + core: &mut dyn Core, |
| 292 | +) -> Result<Option<Vec<u8>>> { |
| 293 | + read_uqvar(hubris, core, SEPARATE_HOST_BOOT_FAIL_NAME) |
| 294 | +} |
| 295 | + |
| 296 | +fn host_boot_fail_spcomms_new( |
| 297 | + hubris: &HubrisArchive, |
| 298 | + core: &mut dyn Core, |
| 299 | + base_buf: &str, |
| 300 | +) -> Result<Option<Vec<u8>>> { |
| 301 | + let buf = read_qualified_state_buf(hubris, core, base_buf)?; |
| 302 | + let maybe_bf = buf.map(|b| b.last_boot_fail); |
| 303 | + Ok(maybe_bf) |
| 304 | +} |
| 305 | + |
| 306 | +/// Try getting boot fail from packrat, where it moved to in |
| 307 | +/// https://github.com/oxidecomputer/hubris/pull/2518. |
232 | 308 | fn host_boot_fail(hubris: &HubrisArchive, core: &mut dyn Core) -> Result<()> { |
233 | | - // Try old name: |
234 | | - let d = read_uqvar(hubris, core, SEPARATE_HOST_BOOT_FAIL_NAME)?; |
235 | | - if let Some(d) = d { |
236 | | - print_escaped_ascii(&d); |
237 | | - return Ok(()); |
238 | | - } |
239 | | - // Try new name |
240 | | - let buf = read_qualified_state_buf(hubris, core, HOST_STATE_BUF_NAME)? |
241 | | - .ok_or_else(|| { |
242 | | - anyhow!( |
243 | | - "Could not find host boot variables under any known name; \ |
244 | | - is this a Gimlet image?" |
| 309 | + // Work through the different places "boot fail" info could be hiding |
| 310 | + let sources: [fn(&HubrisArchive, &mut dyn Core) -> _; _] = [ |
| 311 | + |h, c| { |
| 312 | + host_bootinfo_packrat( |
| 313 | + h, |
| 314 | + c, |
| 315 | + PACKRAT_BOOT_FAIL_PAYLOAD, |
| 316 | + PACKRAT_BOOT_FAIL_STATE, |
245 | 317 | ) |
246 | | - })?; |
| 318 | + }, |
| 319 | + host_boot_fail_spcomms_old, |
| 320 | + |h, c| host_boot_fail_spcomms_new(h, c, HOST_STATE_BUF_NAME_1), |
| 321 | + |h, c| host_boot_fail_spcomms_new(h, c, HOST_STATE_BUF_NAME_2), |
| 322 | + ]; |
| 323 | + |
| 324 | + for source in sources { |
| 325 | + if let Some(bootfail) = source(hubris, core)? { |
| 326 | + print_escaped_ascii(&bootfail); |
| 327 | + return Ok(()); |
| 328 | + } |
| 329 | + } |
247 | 330 |
|
248 | | - print_escaped_ascii(&buf.last_boot_fail[..]); |
| 331 | + bail!( |
| 332 | + "Could not find host boot variables under any known name; is this a \ |
| 333 | + Gimlet image?" |
| 334 | + ) |
| 335 | +} |
249 | 336 |
|
250 | | - Ok(()) |
| 337 | +/// In host-sp-comms, with the legacy-ish name |
| 338 | +fn host_last_panic_spcomms_old( |
| 339 | + hubris: &HubrisArchive, |
| 340 | + core: &mut dyn Core, |
| 341 | +) -> Result<Option<Vec<u8>>> { |
| 342 | + read_uqvar(hubris, core, SEPARATE_LAST_HOST_PANIC_NAME) |
| 343 | +} |
| 344 | + |
| 345 | +/// In host-sp-comms, with the modern-ish name |
| 346 | +fn host_last_panic_spcomms_new( |
| 347 | + hubris: &HubrisArchive, |
| 348 | + core: &mut dyn Core, |
| 349 | + base_buf: &str, |
| 350 | +) -> Result<Option<Vec<u8>>> { |
| 351 | + // Try new name: |
| 352 | + let buf = read_qualified_state_buf(hubris, core, base_buf)?; |
| 353 | + let maybe_panic = buf.map(|b| b.last_panic); |
| 354 | + Ok(maybe_panic) |
251 | 355 | } |
252 | 356 |
|
253 | 357 | fn host_last_panic( |
254 | 358 | hubris: &HubrisArchive, |
255 | 359 | core: &mut dyn Core, |
256 | 360 | log: &Logger, |
257 | 361 | ) -> Result<()> { |
258 | | - // Try original name: |
259 | | - let d = read_uqvar(hubris, core, SEPARATE_LAST_HOST_PANIC_NAME)?; |
260 | | - if let Some(d) = d { |
261 | | - return print_panic(d, log); |
262 | | - } |
263 | | - |
264 | | - // Try new name: |
265 | | - let buf = read_qualified_state_buf(hubris, core, HOST_STATE_BUF_NAME)? |
266 | | - .ok_or_else(|| { |
267 | | - anyhow!( |
268 | | - "Could not find host boot variables under any known name; \ |
269 | | - is this a Gimlet image?" |
| 362 | + // Work through the different places "last panic" info could be hiding |
| 363 | + let sources: [fn(&HubrisArchive, &mut dyn Core) -> _; _] = [ |
| 364 | + |h, c| { |
| 365 | + host_bootinfo_packrat( |
| 366 | + h, |
| 367 | + c, |
| 368 | + PACKRAT_LAST_PANIC_PAYLOAD, |
| 369 | + PACKRAT_LAST_PANIC_STATE, |
270 | 370 | ) |
271 | | - })?; |
| 371 | + }, |
| 372 | + host_last_panic_spcomms_old, |
| 373 | + |h, c| host_last_panic_spcomms_new(h, c, HOST_STATE_BUF_NAME_1), |
| 374 | + |h, c| host_last_panic_spcomms_new(h, c, HOST_STATE_BUF_NAME_2), |
| 375 | + ]; |
| 376 | + |
| 377 | + for source in sources { |
| 378 | + if let Some(panic) = source(hubris, core)? { |
| 379 | + return print_panic(panic, log); |
| 380 | + } |
| 381 | + } |
272 | 382 |
|
273 | | - print_panic(buf.last_panic, log) |
| 383 | + bail!( |
| 384 | + "Could not find host boot variables under any known name; is this a \ |
| 385 | + Gimlet image?" |
| 386 | + ) |
274 | 387 | } |
275 | 388 |
|
276 | 389 | fn print_panic(d: Vec<u8>, log: &Logger) -> Result<()> { |
|
0 commit comments