printflags: improve protobuf decode error message

Include the file path and first bytes of file in the error message in
case aconfig fails to parse one of the protobuf files.

Example output:

  $ adb shell printflags
  Error: failed to parse /vendor/etc/aconfig_flags.pb ([0a], 1 byte(s))

  Caused by:
      Unexpected EOF

Bug: 304278614
Test: atest printflags.test
Change-Id: I18ff88bd25d72dd477c4b11a32505e75884906ee
This commit is contained in:
Mårten Kongstad
2023-10-10 10:12:29 +02:00
parent 7683671bcd
commit 6e61f84fd6

View File

@@ -18,7 +18,7 @@
use aconfig_protos::aconfig::Flag_state as State; use aconfig_protos::aconfig::Flag_state as State;
use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags; use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
use anyhow::{bail, Result}; use anyhow::{bail, Context, Result};
use regex::Regex; use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::process::Command; use std::process::Command;
@@ -39,6 +39,19 @@ fn parse_device_config(raw: &str) -> HashMap<String, String> {
flags flags
} }
fn xxd(bytes: &[u8]) -> String {
let n = 8.min(bytes.len());
let mut v = Vec::with_capacity(n);
for byte in bytes.iter().take(n) {
v.push(format!("{:02x}", byte));
}
let trailer = match bytes.len() {
0..=8 => "",
_ => " ..",
};
format!("[{}{}]", v.join(" "), trailer)
}
fn main() -> Result<()> { fn main() -> Result<()> {
// read device_config // read device_config
let output = Command::new("/system/bin/device_config").arg("list").output()?; let output = Command::new("/system/bin/device_config").arg("list").output()?;
@@ -60,7 +73,10 @@ fn main() -> Result<()> {
eprintln!("warning: failed to read {}", path); eprintln!("warning: failed to read {}", path);
continue; continue;
}; };
let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)?; let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)
.with_context(|| {
format!("failed to parse {} ({}, {} byte(s))", path, xxd(&bytes), bytes.len())
})?;
for flag in parsed_flags.parsed_flag { for flag in parsed_flags.parsed_flag {
let key = format!("{}/{}.{}", flag.namespace(), flag.package(), flag.name()); let key = format!("{}/{}.{}", flag.namespace(), flag.package(), flag.name());
let value = format!("{:?} + {:?} ({})", flag.permission(), flag.state(), partition); let value = format!("{:?} + {:?} ({})", flag.permission(), flag.state(), partition);
@@ -85,7 +101,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn test_foo() { fn test_parse_device_config() {
let input = r#" let input = r#"
namespace_one/com.foo.bar.flag_one=true namespace_one/com.foo.bar.flag_one=true
namespace_one/com.foo.bar.flag_two=false namespace_one/com.foo.bar.flag_two=false
@@ -107,4 +123,16 @@ namespace_two/android.flag_two=nonsense
let actual = parse_device_config(input); let actual = parse_device_config(input);
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }
#[test]
fn test_xxd() {
let input = [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9];
assert_eq!("[]", &xxd(&input[0..0]));
assert_eq!("[00]", &xxd(&input[0..1]));
assert_eq!("[00 01]", &xxd(&input[0..2]));
assert_eq!("[00 01 02 03 04 05 06]", &xxd(&input[0..7]));
assert_eq!("[00 01 02 03 04 05 06 07]", &xxd(&input[0..8]));
assert_eq!("[00 01 02 03 04 05 06 07 ..]", &xxd(&input[0..9]));
assert_eq!("[00 01 02 03 04 05 06 07 ..]", &xxd(&input));
}
} }