1use anyhow::Error;
2use console::{style, Term};
3
4use crate::config::Config;
5use crate::python_spy::PythonSpy;
6use crate::stack_trace::StackTrace;
7
8use remoteprocess::Pid;
9
10pub fn print_traces(pid: Pid, config: &Config, parent: Option<Pid>) -> Result<(), Error> {
11 let mut process = PythonSpy::new(pid, config)?;
12 if config.dump_json {
13 let traces = process.get_stack_traces()?;
14 println!("{}", serde_json::to_string_pretty(&traces)?);
15 return Ok(());
16 }
17
18 println!(
19 "Process {}: {}",
20 style(process.pid).bold().yellow(),
21 process.process.cmdline()?.join(" ")
22 );
23
24 println!(
25 "Python v{} ({})",
26 style(&process.version).bold(),
27 style(process.process.exe()?).dim()
28 );
29
30 if let Some(parentpid) = parent {
31 let parentprocess = remoteprocess::Process::new(parentpid)?;
32 println!(
33 "Parent Process {}: {}",
34 style(parentpid).bold().yellow(),
35 parentprocess.cmdline()?.join(" ")
36 );
37 }
38 println!();
39 let traces = process.get_stack_traces()?;
40 for trace in traces.iter().rev() {
41 print_trace(trace, true);
42 if config.subprocesses {
43 for (childpid, parentpid) in process
44 .process
45 .child_processes()
46 .expect("failed to get subprocesses")
47 {
48 let term = Term::stdout();
49 let (_, width) = term.size();
50
51 println!("\n{}", &style("-".repeat(width as usize)).dim());
52 if parentpid == pid {
56 print_traces(childpid, config, Some(parentpid))?;
57 }
58 }
59 }
60 }
61 Ok(())
62}
63
64pub fn print_trace(trace: &StackTrace, include_activity: bool) {
65 let thread_id = trace.format_threadid();
66
67 let status = if include_activity {
68 format!(" ({})", trace.status_str())
69 } else if trace.owns_gil {
70 " (gil)".to_owned()
71 } else {
72 "".to_owned()
73 };
74
75 match trace.thread_name.as_ref() {
76 Some(name) => {
77 println!(
78 "Thread {}{}: \"{}\"",
79 style(thread_id).bold().yellow(),
80 status,
81 name
82 );
83 }
84 None => {
85 println!("Thread {}{}", style(thread_id).bold().yellow(), status);
86 }
87 };
88
89 for frame in &trace.frames {
90 let filename = match &frame.short_filename {
91 Some(f) => f,
92 None => &frame.filename,
93 };
94 if frame.line != 0 {
95 println!(
96 " {} ({}:{})",
97 style(&frame.name).green(),
98 style(&filename).cyan(),
99 style(frame.line).dim()
100 );
101 } else {
102 println!(
103 " {} ({})",
104 style(&frame.name).green(),
105 style(&filename).cyan()
106 );
107 }
108
109 if let Some(locals) = &frame.locals {
110 let mut shown_args = false;
111 let mut shown_locals = false;
112 for local in locals {
113 if local.arg && !shown_args {
114 println!(" {}", style("Arguments:").dim());
115 shown_args = true;
116 } else if !local.arg && !shown_locals {
117 println!(" {}", style("Locals:").dim());
118 shown_locals = true;
119 }
120
121 let repr = local.repr.as_deref().unwrap_or("?");
122 println!(" {}: {}", local.name, repr);
123 }
124 }
125 }
126}