blob: 879f806f78c4df17fca2624b929e5cfdb8eb28fa [file] [edit]
// Copyright 2025 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
use std::path::Path;
use anyhow::{Context, Result};
use pw_gdb_protocol::Client;
use pw_kernel_annotations::ImageInfo;
use tokio::net::TcpStream;
use tokio_util::compat::TokioAsyncReadCompatExt;
pub async fn run(path: &Path, gdb_addr: &str) -> Result<()> {
let info = ImageInfo::new(path)?;
// Connect to GDB server
let stream = TcpStream::connect(gdb_addr)
.await
.context(format!("Failed to connect to GDB server at {}", gdb_addr))?;
let compat_stream = stream.compat();
let mut client = Client::new(compat_stream);
println!(
"{:<30} {:<12} {:<10} {:<10} {:<10}",
"Name", "Address", "Size", "Used", "Usage %"
);
println!(
"{:-<30} {:-<12} {:-<10} {:-<10} {:-<10}",
"", "", "", "", ""
);
for stack in info.stacks {
let memory = client
.read_memory(stack.stack_addr, stack.stack_size)
.await
.context(format!("Failed to read stack memory for {}", stack.name))?;
// Stacks are initialized with the repeating pattern 0xdecafbad by the
// kernel. If this pattern is unmodified at a given, there is high
// probability that that part of the stack has not been used. From this
// a high water mark can be detected.
let pattern = magic_values::UNUSED_STACK_PATTERN;
let pattern_bytes = match info.endian {
object::Endianness::Little => pattern.to_le_bytes(),
object::Endianness::Big => pattern.to_be_bytes(),
};
let unused_bytes = memory
.chunks(4)
.take_while(|chunk| chunk == &pattern_bytes)
.count()
* 4;
let used_bytes = stack.stack_size - unused_bytes as u64;
// Precisions loss will not effect percentage display.
#[allow(clippy::cast_precision_loss)]
let usage_percent = (used_bytes as f64 / stack.stack_size as f64) * 100.0;
println!(
"{:<30} 0x{:08x} {:<10} {:<10} {:.1}%",
stack.name, stack.stack_addr, stack.stack_size, used_bytes, usage_percent
);
}
Ok(())
}