use super::task::Task;
use crate::debugger::address::RelocatedAddress;
use crate::debugger::r#async::context::TokioAnalyzeContext;
use crate::debugger::r#async::{AsyncError, TaskBacktrace};
use crate::debugger::utils::PopIf;
use crate::debugger::variable::dqe::{Dqe, Selector};
use crate::debugger::variable::value::Value;
use crate::debugger::{Error, ThreadSnapshot, Tracee};
#[derive(Debug, Clone)]
pub struct BlockThread {
pub thread: Tracee,
pub bt: TaskBacktrace,
pub in_focus: bool,
}
pub fn try_as_park_thread(
context: &mut TokioAnalyzeContext,
thread: &ThreadSnapshot,
) -> Result<Option<BlockThread>, Error> {
let backtrace = thread
.bt
.as_ref()
.ok_or(AsyncError::BacktraceShouldExist(thread.thread.pid))?;
let Some(block_on_frame_num) = backtrace.iter().position(|frame| {
let Some(fn_name) = frame.func_name.as_ref() else {
return false;
};
fn_name.contains("CachedParkThread::block_on")
&& !fn_name.contains("CachedParkThread::block_on::")
}) else {
return Ok(None);
};
let debugger = context.debugger_mut();
debugger.ecx_switch_thread(thread.thread.pid)?;
debugger.set_frame_into_focus(block_on_frame_num as u32)?;
let future = debugger
.read_variable(Dqe::Variable(Selector::by_name("f", true)))?
.pop_if_single_el()
.ok_or(AsyncError::IncorrectAssumption(
"it looks like it's a park thread, but variable `f` not found at `block_on` fn",
))?;
let Some(Value::RustEnum(fut)) = future.value else {
return Err(Error::Async(AsyncError::IncorrectAssumption(
"it looks like it's a park thread, but variable `f` not a future",
)));
};
let task = Task::from_enum_repr(
RelocatedAddress::from(fut.raw_address.unwrap_or_default()),
0,
fut,
);
Ok(Some(BlockThread {
thread: thread.thread.clone(),
in_focus: thread.in_focus,
bt: task.backtrace()?,
}))
}