py_spy/
dump.rs

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                // child_processes() returns the whole process tree, since we're recursing here
53                // though we could end up printing grandchild processes multiple times. Limit down
54                // to just once
55                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}