diff --git a/generate_bindings.py b/generate_bindings.py index 24f12702..7f06c506 100644 --- a/generate_bindings.py +++ b/generate_bindings.py @@ -99,6 +99,7 @@ def calculate_pyruntime_offsets(cpython_path, version, configure=False): def extract_bindings(cpython_path, version, configure=False): print("Generating bindings for python %s from repo at %s" % (version, cpython_path)) + ret = os.system(f""" cd {cpython_path} git checkout {version} @@ -108,6 +109,7 @@ def extract_bindings(cpython_path, version, configure=False): cat Include/Python.h > bindgen_input.h cat Include/frameobject.h >> bindgen_input.h + cat Objects/dict-common.h >> bindgen_input.h echo '#define Py_BUILD_CORE 1\n' >> bindgen_input.h cat Include/internal/pycore_pystate.h >> bindgen_input.h @@ -124,6 +126,15 @@ def extract_bindings(cpython_path, version, configure=False): --whitelist-type PyUnicodeObject \ --whitelist-type PyCompactUnicodeObject \ --whitelist-type PyStringObject \ + --whitelist-type PyTupleObject \ + --whitelist-type PyListObject \ + --whitelist-type PyLongObject \ + --whitelist-type PyFloatObject \ + --whitelist-type PyDictObject \ + --whitelist-type PyDictKeysObject \ + --whitelist-type PyDictKeyEntry \ + --whitelist-type PyObject \ + --whitelist-type PyTypeObject \ -- -I . -I ./Include -I ./Include/internal """) if ret: @@ -177,7 +188,7 @@ def extract_bindings(cpython_path, version, configure=False): sys.exit(1) if args.all: - versions = ['v3.7.0', 'v3.6.6', 'v3.5.5', 'v3.4.8', 'v3.3.7', 'v3.2.6', 'v2.7.15'] + versions = ['v3.8.0b4', 'v3.7.0', 'v3.6.6', 'v3.5.5', 'v3.4.8', 'v3.3.7', 'v3.2.6', 'v2.7.15'] else: versions = args.versions if not versions: diff --git a/src/config.rs b/src/config.rs index da6eed9a..5808dccf 100644 --- a/src/config.rs +++ b/src/config.rs @@ -42,6 +42,8 @@ pub struct Config { pub hide_progess: bool, #[doc(hidden)] pub dump_json: bool, + #[doc(hidden)] + pub dump_locals: bool, } arg_enum!{ @@ -69,7 +71,7 @@ impl Default for Config { non_blocking: false, show_line_numbers: false, sampling_rate: 100, duration: RecordDuration::Unlimited, native: false, gil_only: false, include_idle: false, include_thread_ids: false, - hide_progess: false, dump_json: false} + hide_progess: false, dump_json: false, dump_locals: false} } } @@ -171,6 +173,10 @@ impl Config { let dump = clap::SubCommand::with_name("dump") .about("Dumps stack traces for a target program to stdout") .arg(pid.clone().required(true)) + .arg(Arg::with_name("locals") + .short("l") + .long("locals") + .help("Show local variables for each frame")) .arg(Arg::with_name("json") .short("j") .long("json") @@ -241,6 +247,7 @@ impl Config { config.native = matches.occurrences_of("native") > 0; config.hide_progess = matches.occurrences_of("hideprogress") > 0; config.dump_json = matches.occurrences_of("json") > 0; + config.dump_locals = matches.occurrences_of("locals") > 0; // disable native profiling if invalidly asked for if config.native && config.non_blocking { diff --git a/src/dump.rs b/src/dump.rs new file mode 100644 index 00000000..037f7246 --- /dev/null +++ b/src/dump.rs @@ -0,0 +1,129 @@ +use std::collections::HashMap; + +use console::style; +use failure::Error; + +use crate::config::Config; +use crate::python_bindings::{v3_6_6, v3_7_0, v3_8_0}; +use crate::python_interpreters::{InterpreterState, Object, TypeObject}; +use crate::python_spy::PythonSpy; +use crate::python_data_access::{copy_string, copy_long, stringify_pyobject, DictIterator}; + +use crate::version::Version; + +use remoteprocess::ProcessMemory; + +pub fn print_traces(process: &mut PythonSpy, config: &Config) -> Result<(), Error> { + if config.dump_json { + let traces = process.get_stack_traces()?; + println!("{}", serde_json::to_string_pretty(&traces)?); + return Ok(()) + } + + // try getting the threadnames, but don't sweat it if we can't. Since this relies on dictionary + // processing we only handle py3.6+ right now, and this doesn't work at all if the + // threading module isn't imported in the target program + let thread_names = match process.version { + Version{major: 3, minor: 6, ..} => thread_name_lookup::(process).ok(), + Version{major: 3, minor: 7, ..} => thread_name_lookup::(process).ok(), + Version{major: 3, minor: 8, ..} => thread_name_lookup::(process).ok(), + _ => None + }; + + println!("Process {}: {}", + style(process.pid).bold().yellow(), + process.process.cmdline()?.join(" ")); + + println!("Python v{} ({})\n", + style(&process.version).bold(), + style(process.process.exe()?).dim()); + + let traces = process.get_stack_traces()?; + + for trace in traces.iter().rev() { + let thread_id = trace.format_threadid(); + let thread_name = match thread_names.as_ref() { + Some(names) => names.get(&trace.thread_id), + None => None + }; + match thread_name { + Some(name) => { + println!("Thread {} ({}): \"{}\"", style(thread_id).bold().yellow(), trace.status_str(), name); + } + None => { + println!("Thread {} ({})", style(thread_id).bold().yellow(), trace.status_str()); + } + }; + + for frame in &trace.frames { + let filename = match &frame.short_filename { Some(f) => &f, None => &frame.filename }; + if frame.line != 0 { + println!(" {} ({}:{})", style(&frame.name).green(), style(&filename).cyan(), style(frame.line).dim()); + } else { + println!(" {} ({})", style(&frame.name).green(), style(&filename).cyan()); + } + + if let Some(locals) = &frame.locals { + let mut shown_args = false; + let mut shown_locals = false; + for local in locals { + if local.arg && !shown_args { + println!(" {}:", style("Arguments:").dim()); + shown_args = true; + } else if !local.arg && !shown_locals { + println!(" {}:", style("Locals:").dim()); + shown_locals = true; + } + + let value = stringify_pyobject(&process.process, &process.version, local.addr, 128)?; + println!(" {}: {}", local.name, value); + } + } + } + } + Ok(()) +} + +/// Returns a hashmap of threadid: threadname, by inspecting the '_active' variable in the +/// 'threading' module. +fn thread_name_lookup(spy: &PythonSpy) -> Result, Error> { + let mut ret = HashMap::new(); + let process = &spy.process; + let interp: I = process.copy_struct(spy.interpreter_address)?; + for entry in DictIterator::from(process, interp.modules() as usize)? { + let (key, value) = entry?; + let module_name = copy_string(key as *const I::StringObject, process)?; + if module_name == "threading" { + let module: I::Object = process.copy_struct(value)?; + let module_type = process.copy_pointer(module.ob_type())?; + let dictptr: usize = process.copy_struct(value + module_type.dictoffset() as usize)?; + for i in DictIterator::from(process, dictptr)? { + let (key, value) = i?; + let name = copy_string(key as *const I::StringObject, process)?; + if name == "_active" { + for i in DictIterator::from(process, value)? { + let (key, value) = i?; + let (threadid, _) = copy_long(process, key)?; + + let thread: I::Object = process.copy_struct(value)?; + let thread_type = process.copy_pointer(thread.ob_type())?; + let thread_dict_addr: usize = process.copy_struct(value + thread_type.dictoffset() as usize)?; + + for i in DictIterator::from(process, thread_dict_addr)? { + let (key, value) = i?; + let varname = copy_string(key as *const I::StringObject, process)?; + if varname == "_name" { + let threadname = copy_string(value as *const I::StringObject, process)?; + ret.insert(threadid as u64, threadname); + break; + } + } + } + break; + } + } + break; + } + } + Ok(ret) +} diff --git a/src/lib.rs b/src/lib.rs index 7a81f562..00192083 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,7 @@ mod native_stack_trace; mod python_bindings; mod python_interpreters; mod python_spy; +mod python_data_access; mod stack_trace; mod utils; mod version; @@ -66,4 +67,4 @@ pub use config::Config; pub use stack_trace::StackTrace; pub use stack_trace::Frame; pub use remoteprocess::Pid; - +pub use python_data_access::stringify_pyobject; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 7531e96f..e395cc07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,6 +33,7 @@ extern crate serde_json; extern crate remoteprocess; mod config; +mod dump; mod binary_parser; #[cfg(unwind)] mod cython; @@ -41,6 +42,7 @@ mod native_stack_trace; mod python_bindings; mod python_interpreters; mod python_spy; +mod python_data_access; mod stack_trace; mod console_viewer; mod flamegraph; @@ -61,52 +63,6 @@ use stack_trace::{StackTrace, Frame}; use console_viewer::ConsoleViewer; use config::{Config, FileFormat, RecordDuration}; -fn format_trace_threadid(trace: &StackTrace) -> String { - // native threadids in osx are kinda useless, use the pthread id instead - #[cfg(target_os="macos")] - return format!("{:#X}", trace.thread_id); - - // otherwise use the native threadid if given - #[cfg(not(target_os="macos"))] - match trace.os_thread_id { - Some(tid) => format!("{}", tid), - None => format!("{:#X}", trace.thread_id) - } -} - -fn print_traces(process: &mut PythonSpy, config: &Config) -> Result<(), Error> { - if config.dump_json { - let traces = process.get_stack_traces()?; - println!("{}", serde_json::to_string_pretty(&traces)?); - return Ok(()) - } - - use console::style; - println!("Process {}: {}", - style(process.pid).bold().yellow(), - process.process.cmdline()?.join(" ")); - - println!("Python v{} ({})\n", - style(&process.version).bold(), - style(process.process.exe()?).dim()); - - let traces = process.get_stack_traces()?; - - for trace in traces.iter().rev() { - let thread_id = format_trace_threadid(&trace); - println!("Thread {} ({})", style(thread_id).bold().yellow(), trace.status_str()); - for frame in &trace.frames { - let filename = match &frame.short_filename { Some(f) => &f, None => &frame.filename }; - if frame.line != 0 { - println!("\t {} ({}:{})", style(&frame.name).green(), style(&filename).cyan(), style(frame.line).dim()); - } else { - println!("\t {} ({})", style(&frame.name).green(), style(&filename).cyan()); - } - } - } - Ok(()) -} - fn process_exitted(process: &remoteprocess::Process) -> bool { process.exe().is_err() } @@ -270,10 +226,10 @@ fn record_samples(process: &mut PythonSpy, config: &Config) -> Result<(), Error> } if config.include_thread_ids { - let threadid = format_trace_threadid(&trace); + let threadid = trace.format_threadid(); trace.frames.push(Frame{name: format!("thread {}", threadid), filename: String::from(""), - module: None, short_filename: None, line: 0}); + module: None, short_filename: None, line: 0, locals: None}); } output.increment(&trace)?; @@ -343,7 +299,7 @@ fn record_samples(process: &mut PythonSpy, config: &Config) -> Result<(), Error> fn run_spy_command(process: &mut PythonSpy, config: &config::Config) -> Result<(), Error> { match config.command.as_ref() { "dump" => { - print_traces(process, config)?; + dump::print_traces(process, config)?; }, "record" => { record_samples(process, config)?; diff --git a/src/native_stack_trace.rs b/src/native_stack_trace.rs index a69c1387..ee493382 100644 --- a/src/native_stack_trace.rs +++ b/src/native_stack_trace.rs @@ -108,7 +108,7 @@ impl NativeStack { // if we can't symbolicate, just insert a stub here. merged.push(Frame{filename: "?".to_owned(), name: format!("0x{:x}", addr), - line: 0, short_filename: None, module: None}); + line: 0, short_filename: None, module: None, locals: None}); }); if symbolicated_count == 1 { @@ -231,11 +231,11 @@ impl NativeStack { return None; } let name = cython::demangle(&name).to_owned(); - Some(Frame{filename, line, name, short_filename: None, module: Some(frame.module.clone())}) + Some(Frame{filename, line, name, short_filename: None, module: Some(frame.module.clone()), locals: None}) }, None => { Some(Frame{filename: frame.module.clone(), - name: format!("0x{:x}", frame.addr), + name: format!("0x{:x}", frame.addr), locals: None, line: 0, short_filename: None, module: Some(frame.module.clone())}) } } diff --git a/src/python_bindings/v2_7_15.rs b/src/python_bindings/v2_7_15.rs index 9174c642..ca952f9f 100644 --- a/src/python_bindings/v2_7_15.rs +++ b/src/python_bindings/v2_7_15.rs @@ -111,15 +111,20 @@ pub type binaryfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; pub type ternaryfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type inquiry = ::std::option::Option ::std::os::raw::c_int>; pub type lenfunc = ::std::option::Option Py_ssize_t>; pub type coercion = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut *mut PyObject, arg2: *mut *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut *mut PyObject, + arg2: *mut *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type ssizeargfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t) -> *mut PyObject, @@ -128,8 +133,11 @@ pub type ssizessizeargfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t, arg3: Py_ssize_t) -> *mut PyObject, >; pub type ssizeobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: Py_ssize_t, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type ssizessizeobjargproc = ::std::option::Option< unsafe extern "C" fn( @@ -140,8 +148,11 @@ pub type ssizessizeobjargproc = ::std::option::Option< ) -> ::std::os::raw::c_int, >; pub type objobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type readbufferproc = ::std::option::Option< unsafe extern "C" fn( @@ -190,8 +201,11 @@ impl Default for bufferinfo { } pub type Py_buffer = bufferinfo; pub type getbufferproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut Py_buffer, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut Py_buffer, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type releasebufferproc = ::std::option::Option; @@ -199,12 +213,17 @@ pub type objobjproc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> ::std::os::raw::c_int, >; pub type visitproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; pub type traverseproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: visitproc, arg3: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: visitproc, + arg3: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -303,8 +322,11 @@ impl Default for PyBufferProcs { pub type freefunc = ::std::option::Option; pub type destructor = ::std::option::Option; pub type printfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut FILE, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut FILE, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type getattrfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_char) -> *mut PyObject, @@ -320,8 +342,11 @@ pub type setattrfunc = ::std::option::Option< ) -> ::std::os::raw::c_int, >; pub type setattrofunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type cmpfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> ::std::os::raw::c_int, @@ -331,28 +356,43 @@ pub type reprfunc = pub type hashfunc = ::std::option::Option ::std::os::raw::c_long>; pub type richcmpfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: ::std::os::raw::c_int) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: ::std::os::raw::c_int, + ) -> *mut PyObject, >; pub type getiterfunc = ::std::option::Option *mut PyObject>; pub type iternextfunc = ::std::option::Option *mut PyObject>; pub type descrgetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type descrsetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type initproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type newfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut _typeobject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut _typeobject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type allocfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut _typeobject, arg2: Py_ssize_t) -> *mut PyObject, @@ -415,6 +455,7 @@ impl Default for _typeobject { unsafe { ::std::mem::zeroed() } } } +pub type PyTypeObject = _typeobject; pub type Py_UNICODE = ::std::os::raw::c_ushort; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -433,6 +474,24 @@ impl Default for PyUnicodeObject { } #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct _longobject { + _unused: [u8; 0], +} +pub type PyLongObject = _longobject; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyFloatObject { + pub ob_refcnt: Py_ssize_t, + pub ob_type: *mut _typeobject, + pub ob_fval: f64, +} +impl Default for PyFloatObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct PyStringObject { pub ob_refcnt: Py_ssize_t, pub ob_type: *mut _typeobject, @@ -446,6 +505,69 @@ impl Default for PyStringObject { unsafe { ::std::mem::zeroed() } } } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyTupleObject { + pub ob_refcnt: Py_ssize_t, + pub ob_type: *mut _typeobject, + pub ob_size: Py_ssize_t, + pub ob_item: [*mut PyObject; 1usize], +} +impl Default for PyTupleObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyListObject { + pub ob_refcnt: Py_ssize_t, + pub ob_type: *mut _typeobject, + pub ob_size: Py_ssize_t, + pub ob_item: *mut *mut PyObject, + pub allocated: Py_ssize_t, +} +impl Default for PyListObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDictEntry { + pub me_hash: Py_ssize_t, + pub me_key: *mut PyObject, + pub me_value: *mut PyObject, +} +impl Default for PyDictEntry { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type PyDictObject = _dictobject; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _dictobject { + pub ob_refcnt: Py_ssize_t, + pub ob_type: *mut _typeobject, + pub ma_fill: Py_ssize_t, + pub ma_used: Py_ssize_t, + pub ma_mask: Py_ssize_t, + pub ma_table: *mut PyDictEntry, + pub ma_lookup: ::std::option::Option< + unsafe extern "C" fn( + mp: *mut PyDictObject, + key: *mut PyObject, + hash: ::std::os::raw::c_long, + ) -> *mut PyDictEntry, + >, + pub ma_smalltable: [PyDictEntry; 8usize], +} +impl Default for _dictobject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} pub type PyCFunction = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; diff --git a/src/python_bindings/v3_3_7.rs b/src/python_bindings/v3_3_7.rs index f83eca89..99f5ddd2 100644 --- a/src/python_bindings/v3_3_7.rs +++ b/src/python_bindings/v3_3_7.rs @@ -192,8 +192,11 @@ pub type binaryfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; pub type ternaryfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type inquiry = ::std::option::Option ::std::os::raw::c_int>; @@ -202,12 +205,18 @@ pub type ssizeargfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t) -> *mut PyObject, >; pub type ssizeobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: Py_ssize_t, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type objobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -231,8 +240,11 @@ impl Default for bufferinfo { } pub type Py_buffer = bufferinfo; pub type getbufferproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut Py_buffer, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut Py_buffer, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type releasebufferproc = ::std::option::Option; @@ -240,12 +252,17 @@ pub type objobjproc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> ::std::os::raw::c_int, >; pub type visitproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; pub type traverseproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: visitproc, arg3: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: visitproc, + arg3: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -335,8 +352,11 @@ impl Default for PyBufferProcs { pub type freefunc = ::std::option::Option; pub type destructor = ::std::option::Option; pub type printfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut FILE, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut FILE, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type getattrfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_char) -> *mut PyObject, @@ -352,35 +372,53 @@ pub type setattrfunc = ::std::option::Option< ) -> ::std::os::raw::c_int, >; pub type setattrofunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type reprfunc = ::std::option::Option *mut PyObject>; pub type hashfunc = ::std::option::Option Py_hash_t>; pub type richcmpfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: ::std::os::raw::c_int) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: ::std::os::raw::c_int, + ) -> *mut PyObject, >; pub type getiterfunc = ::std::option::Option *mut PyObject>; pub type iternextfunc = ::std::option::Option *mut PyObject>; pub type descrgetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type descrsetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type initproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type newfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut _typeobject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut _typeobject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type allocfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut _typeobject, arg2: Py_ssize_t) -> *mut PyObject, @@ -441,6 +479,7 @@ impl Default for _typeobject { unsafe { ::std::mem::zeroed() } } } +pub type PyTypeObject = _typeobject; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct PyBytesObject { @@ -604,6 +643,80 @@ impl Default for PyUnicodeObject { unsafe { ::std::mem::zeroed() } } } +pub type PyLongObject = _longobject; +pub type digit = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _longobject { + pub ob_base: PyVarObject, + pub ob_digit: [digit; 1usize], +} +impl Default for _longobject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyFloatObject { + pub ob_base: PyObject, + pub ob_fval: f64, +} +impl Default for PyFloatObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyTupleObject { + pub ob_base: PyVarObject, + pub ob_item: [*mut PyObject; 1usize], +} +impl Default for PyTupleObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyListObject { + pub ob_base: PyVarObject, + pub ob_item: *mut *mut PyObject, + pub allocated: Py_ssize_t, +} +impl Default for PyListObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type PyDictKeysObject = _dictkeysobject; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDictObject { + pub ob_base: PyObject, + pub ma_used: Py_ssize_t, + pub ma_keys: *mut PyDictKeysObject, + pub ma_values: *mut *mut PyObject, +} +impl Default for PyDictObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDictKeyEntry { + pub me_hash: Py_hash_t, + pub me_key: *mut PyObject, + pub me_value: *mut PyObject, +} +impl Default for PyDictKeyEntry { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} pub type PyCFunction = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; @@ -778,3 +891,8 @@ impl Default for _frame { } } pub type PyFrameObject = _frame; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct _dictkeysobject { + pub _address: u8, +} diff --git a/src/python_bindings/v3_4_8.rs b/src/python_bindings/v3_4_8.rs index c533574e..8e1e6a0b 100644 --- a/src/python_bindings/v3_4_8.rs +++ b/src/python_bindings/v3_4_8.rs @@ -192,8 +192,11 @@ pub type binaryfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; pub type ternaryfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type inquiry = ::std::option::Option ::std::os::raw::c_int>; @@ -202,12 +205,18 @@ pub type ssizeargfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t) -> *mut PyObject, >; pub type ssizeobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: Py_ssize_t, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type objobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -231,8 +240,11 @@ impl Default for bufferinfo { } pub type Py_buffer = bufferinfo; pub type getbufferproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut Py_buffer, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut Py_buffer, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type releasebufferproc = ::std::option::Option; @@ -240,12 +252,17 @@ pub type objobjproc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> ::std::os::raw::c_int, >; pub type visitproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; pub type traverseproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: visitproc, arg3: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: visitproc, + arg3: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -335,8 +352,11 @@ impl Default for PyBufferProcs { pub type freefunc = ::std::option::Option; pub type destructor = ::std::option::Option; pub type printfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut FILE, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut FILE, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type getattrfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_char) -> *mut PyObject, @@ -352,35 +372,53 @@ pub type setattrfunc = ::std::option::Option< ) -> ::std::os::raw::c_int, >; pub type setattrofunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type reprfunc = ::std::option::Option *mut PyObject>; pub type hashfunc = ::std::option::Option Py_hash_t>; pub type richcmpfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: ::std::os::raw::c_int) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: ::std::os::raw::c_int, + ) -> *mut PyObject, >; pub type getiterfunc = ::std::option::Option *mut PyObject>; pub type iternextfunc = ::std::option::Option *mut PyObject>; pub type descrgetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type descrsetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type initproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type newfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut _typeobject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut _typeobject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type allocfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut _typeobject, arg2: Py_ssize_t) -> *mut PyObject, @@ -442,6 +480,7 @@ impl Default for _typeobject { unsafe { ::std::mem::zeroed() } } } +pub type PyTypeObject = _typeobject; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct PyBytesObject { @@ -604,6 +643,67 @@ impl Default for PyUnicodeObject { unsafe { ::std::mem::zeroed() } } } +pub type PyLongObject = _longobject; +pub type digit = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _longobject { + pub ob_base: PyVarObject, + pub ob_digit: [digit; 1usize], +} +impl Default for _longobject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyFloatObject { + pub ob_base: PyObject, + pub ob_fval: f64, +} +impl Default for PyFloatObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyTupleObject { + pub ob_base: PyVarObject, + pub ob_item: [*mut PyObject; 1usize], +} +impl Default for PyTupleObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyListObject { + pub ob_base: PyVarObject, + pub ob_item: *mut *mut PyObject, + pub allocated: Py_ssize_t, +} +impl Default for PyListObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type PyDictKeysObject = _dictkeysobject; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDictObject { + pub ob_base: PyObject, + pub ma_used: Py_ssize_t, + pub ma_keys: *mut PyDictKeysObject, + pub ma_values: *mut *mut PyObject, +} +impl Default for PyDictObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} pub type PyCFunction = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; @@ -782,3 +882,8 @@ impl Default for _frame { } } pub type PyFrameObject = _frame; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct _dictkeysobject { + pub _address: u8, +} diff --git a/src/python_bindings/v3_5_5.rs b/src/python_bindings/v3_5_5.rs index 49bbba94..8ee1a774 100644 --- a/src/python_bindings/v3_5_5.rs +++ b/src/python_bindings/v3_5_5.rs @@ -192,8 +192,11 @@ pub type binaryfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; pub type ternaryfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type inquiry = ::std::option::Option ::std::os::raw::c_int>; @@ -202,12 +205,18 @@ pub type ssizeargfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t) -> *mut PyObject, >; pub type ssizeobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: Py_ssize_t, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type objobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -231,8 +240,11 @@ impl Default for bufferinfo { } pub type Py_buffer = bufferinfo; pub type getbufferproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut Py_buffer, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut Py_buffer, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type releasebufferproc = ::std::option::Option; @@ -240,12 +252,17 @@ pub type objobjproc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> ::std::os::raw::c_int, >; pub type visitproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; pub type traverseproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: visitproc, arg3: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: visitproc, + arg3: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -349,8 +366,11 @@ impl Default for PyBufferProcs { pub type freefunc = ::std::option::Option; pub type destructor = ::std::option::Option; pub type printfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut FILE, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut FILE, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type getattrfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_char) -> *mut PyObject, @@ -366,35 +386,53 @@ pub type setattrfunc = ::std::option::Option< ) -> ::std::os::raw::c_int, >; pub type setattrofunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type reprfunc = ::std::option::Option *mut PyObject>; pub type hashfunc = ::std::option::Option Py_hash_t>; pub type richcmpfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: ::std::os::raw::c_int) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: ::std::os::raw::c_int, + ) -> *mut PyObject, >; pub type getiterfunc = ::std::option::Option *mut PyObject>; pub type iternextfunc = ::std::option::Option *mut PyObject>; pub type descrgetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type descrsetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type initproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type newfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut _typeobject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut _typeobject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type allocfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut _typeobject, arg2: Py_ssize_t) -> *mut PyObject, @@ -456,6 +494,7 @@ impl Default for _typeobject { unsafe { ::std::mem::zeroed() } } } +pub type PyTypeObject = _typeobject; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct PyBytesObject { @@ -618,6 +657,67 @@ impl Default for PyUnicodeObject { unsafe { ::std::mem::zeroed() } } } +pub type PyLongObject = _longobject; +pub type digit = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _longobject { + pub ob_base: PyVarObject, + pub ob_digit: [digit; 1usize], +} +impl Default for _longobject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyFloatObject { + pub ob_base: PyObject, + pub ob_fval: f64, +} +impl Default for PyFloatObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyTupleObject { + pub ob_base: PyVarObject, + pub ob_item: [*mut PyObject; 1usize], +} +impl Default for PyTupleObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyListObject { + pub ob_base: PyVarObject, + pub ob_item: *mut *mut PyObject, + pub allocated: Py_ssize_t, +} +impl Default for PyListObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type PyDictKeysObject = _dictkeysobject; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDictObject { + pub ob_base: PyObject, + pub ma_used: Py_ssize_t, + pub ma_keys: *mut PyDictKeysObject, + pub ma_values: *mut *mut PyObject, +} +impl Default for PyDictObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} pub type PyCFunction = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; @@ -798,3 +898,37 @@ impl Default for _frame { } } pub type PyFrameObject = _frame; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDictKeyEntry { + pub me_hash: Py_hash_t, + pub me_key: *mut PyObject, + pub me_value: *mut PyObject, +} +impl Default for PyDictKeyEntry { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type dict_lookup_func = ::std::option::Option< + unsafe extern "C" fn( + mp: *mut PyDictObject, + key: *mut PyObject, + hash: Py_hash_t, + value_addr: *mut *mut *mut PyObject, + ) -> *mut PyDictKeyEntry, +>; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _dictkeysobject { + pub dk_refcnt: Py_ssize_t, + pub dk_size: Py_ssize_t, + pub dk_lookup: dict_lookup_func, + pub dk_usable: Py_ssize_t, + pub dk_entries: [PyDictKeyEntry; 1usize], +} +impl Default for _dictkeysobject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} diff --git a/src/python_bindings/v3_6_6.rs b/src/python_bindings/v3_6_6.rs index 6a6adae9..8bca2bb9 100644 --- a/src/python_bindings/v3_6_6.rs +++ b/src/python_bindings/v3_6_6.rs @@ -192,8 +192,11 @@ pub type binaryfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; pub type ternaryfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type inquiry = ::std::option::Option ::std::os::raw::c_int>; @@ -202,12 +205,18 @@ pub type ssizeargfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t) -> *mut PyObject, >; pub type ssizeobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: Py_ssize_t, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type objobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -231,8 +240,11 @@ impl Default for bufferinfo { } pub type Py_buffer = bufferinfo; pub type getbufferproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut Py_buffer, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut Py_buffer, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type releasebufferproc = ::std::option::Option; @@ -240,12 +252,17 @@ pub type objobjproc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> ::std::os::raw::c_int, >; pub type visitproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; pub type traverseproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: visitproc, arg3: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: visitproc, + arg3: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -349,8 +366,11 @@ impl Default for PyBufferProcs { pub type freefunc = ::std::option::Option; pub type destructor = ::std::option::Option; pub type printfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut FILE, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut FILE, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type getattrfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_char) -> *mut PyObject, @@ -366,35 +386,53 @@ pub type setattrfunc = ::std::option::Option< ) -> ::std::os::raw::c_int, >; pub type setattrofunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type reprfunc = ::std::option::Option *mut PyObject>; pub type hashfunc = ::std::option::Option Py_hash_t>; pub type richcmpfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: ::std::os::raw::c_int) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: ::std::os::raw::c_int, + ) -> *mut PyObject, >; pub type getiterfunc = ::std::option::Option *mut PyObject>; pub type iternextfunc = ::std::option::Option *mut PyObject>; pub type descrgetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type descrsetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type initproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type newfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut _typeobject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut _typeobject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type allocfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut _typeobject, arg2: Py_ssize_t) -> *mut PyObject, @@ -456,6 +494,7 @@ impl Default for _typeobject { unsafe { ::std::mem::zeroed() } } } +pub type PyTypeObject = _typeobject; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct PyBytesObject { @@ -618,6 +657,68 @@ impl Default for PyUnicodeObject { unsafe { ::std::mem::zeroed() } } } +pub type PyLongObject = _longobject; +pub type digit = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _longobject { + pub ob_base: PyVarObject, + pub ob_digit: [digit; 1usize], +} +impl Default for _longobject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyFloatObject { + pub ob_base: PyObject, + pub ob_fval: f64, +} +impl Default for PyFloatObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyTupleObject { + pub ob_base: PyVarObject, + pub ob_item: [*mut PyObject; 1usize], +} +impl Default for PyTupleObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyListObject { + pub ob_base: PyVarObject, + pub ob_item: *mut *mut PyObject, + pub allocated: Py_ssize_t, +} +impl Default for PyListObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type PyDictKeysObject = _dictkeysobject; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDictObject { + pub ob_base: PyObject, + pub ma_used: Py_ssize_t, + pub ma_version_tag: u64, + pub ma_keys: *mut PyDictKeysObject, + pub ma_values: *mut *mut PyObject, +} +impl Default for PyDictObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} pub type PyCFunction = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; @@ -808,3 +909,53 @@ impl Default for _frame { } } pub type PyFrameObject = _frame; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDictKeyEntry { + pub me_hash: Py_hash_t, + pub me_key: *mut PyObject, + pub me_value: *mut PyObject, +} +impl Default for PyDictKeyEntry { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type dict_lookup_func = ::std::option::Option< + unsafe extern "C" fn( + mp: *mut PyDictObject, + key: *mut PyObject, + hash: Py_hash_t, + value_addr: *mut *mut *mut PyObject, + hashpos: *mut Py_ssize_t, + ) -> Py_ssize_t, +>; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct _dictkeysobject { + pub dk_refcnt: Py_ssize_t, + pub dk_size: Py_ssize_t, + pub dk_lookup: dict_lookup_func, + pub dk_usable: Py_ssize_t, + pub dk_nentries: Py_ssize_t, + pub dk_indices: _dictkeysobject__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union _dictkeysobject__bindgen_ty_1 { + pub as_1: [i8; 8usize], + pub as_2: [i16; 4usize], + pub as_4: [i32; 2usize], + pub as_8: [i64; 1usize], + _bindgen_union_align: u64, +} +impl Default for _dictkeysobject__bindgen_ty_1 { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +impl Default for _dictkeysobject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} diff --git a/src/python_bindings/v3_7_0.rs b/src/python_bindings/v3_7_0.rs index fd48a0c0..1f0e7f62 100644 --- a/src/python_bindings/v3_7_0.rs +++ b/src/python_bindings/v3_7_0.rs @@ -89,6 +89,43 @@ where } } } +#[repr(C)] +#[derive(Default)] +pub struct __IncompleteArrayField(::std::marker::PhantomData); +impl __IncompleteArrayField { + #[inline] + pub fn new() -> Self { + __IncompleteArrayField(::std::marker::PhantomData) + } + #[inline] + pub unsafe fn as_ptr(&self) -> *const T { + ::std::mem::transmute(self) + } + #[inline] + pub unsafe fn as_mut_ptr(&mut self) -> *mut T { + ::std::mem::transmute(self) + } + #[inline] + pub unsafe fn as_slice(&self, len: usize) -> &[T] { + ::std::slice::from_raw_parts(self.as_ptr(), len) + } + #[inline] + pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { + ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) + } +} +impl ::std::fmt::Debug for __IncompleteArrayField { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fmt.write_str("__IncompleteArrayField") + } +} +impl ::std::clone::Clone for __IncompleteArrayField { + #[inline] + fn clone(&self) -> Self { + Self::new() + } +} +impl ::std::marker::Copy for __IncompleteArrayField {} pub type __int64_t = ::std::os::raw::c_longlong; pub type __darwin_size_t = ::std::os::raw::c_ulong; pub type __darwin_wchar_t = ::std::os::raw::c_int; @@ -193,8 +230,11 @@ pub type binaryfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; pub type ternaryfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type inquiry = ::std::option::Option ::std::os::raw::c_int>; @@ -203,12 +243,18 @@ pub type ssizeargfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t) -> *mut PyObject, >; pub type ssizeobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: Py_ssize_t, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type objobjargproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -232,8 +278,11 @@ impl Default for bufferinfo { } pub type Py_buffer = bufferinfo; pub type getbufferproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut Py_buffer, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut Py_buffer, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type releasebufferproc = ::std::option::Option; @@ -241,12 +290,17 @@ pub type objobjproc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> ::std::os::raw::c_int, >; pub type visitproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; pub type traverseproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: visitproc, arg3: *mut ::std::os::raw::c_void) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: visitproc, + arg3: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -350,8 +404,11 @@ impl Default for PyBufferProcs { pub type freefunc = ::std::option::Option; pub type destructor = ::std::option::Option; pub type printfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut FILE, arg3: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut FILE, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >; pub type getattrfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut ::std::os::raw::c_char) -> *mut PyObject, @@ -367,35 +424,53 @@ pub type setattrfunc = ::std::option::Option< ) -> ::std::os::raw::c_int, >; pub type setattrofunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type reprfunc = ::std::option::Option *mut PyObject>; pub type hashfunc = ::std::option::Option Py_hash_t>; pub type richcmpfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: ::std::os::raw::c_int) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: ::std::os::raw::c_int, + ) -> *mut PyObject, >; pub type getiterfunc = ::std::option::Option *mut PyObject>; pub type iternextfunc = ::std::option::Option *mut PyObject>; pub type descrgetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type descrsetfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type initproc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut PyObject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> ::std::os::raw::c_int, >; pub type newfunc = ::std::option::Option< - unsafe extern "C" fn(arg1: *mut _typeobject, arg2: *mut PyObject, arg3: *mut PyObject) - -> *mut PyObject, + unsafe extern "C" fn( + arg1: *mut _typeobject, + arg2: *mut PyObject, + arg3: *mut PyObject, + ) -> *mut PyObject, >; pub type allocfunc = ::std::option::Option< unsafe extern "C" fn(arg1: *mut _typeobject, arg2: Py_ssize_t) -> *mut PyObject, @@ -457,6 +532,7 @@ impl Default for _typeobject { unsafe { ::std::mem::zeroed() } } } +pub type PyTypeObject = _typeobject; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct PyBytesObject { @@ -619,6 +695,68 @@ impl Default for PyUnicodeObject { unsafe { ::std::mem::zeroed() } } } +pub type PyLongObject = _longobject; +pub type digit = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _longobject { + pub ob_base: PyVarObject, + pub ob_digit: [digit; 1usize], +} +impl Default for _longobject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyFloatObject { + pub ob_base: PyObject, + pub ob_fval: f64, +} +impl Default for PyFloatObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyTupleObject { + pub ob_base: PyVarObject, + pub ob_item: [*mut PyObject; 1usize], +} +impl Default for PyTupleObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyListObject { + pub ob_base: PyVarObject, + pub ob_item: *mut *mut PyObject, + pub allocated: Py_ssize_t, +} +impl Default for PyListObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type PyDictKeysObject = _dictkeysobject; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDictObject { + pub ob_base: PyObject, + pub ma_used: Py_ssize_t, + pub ma_version_tag: u64, + pub ma_keys: *mut PyDictKeysObject, + pub ma_values: *mut *mut PyObject, +} +impl Default for PyDictObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} pub type PyCFunction = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; @@ -903,3 +1041,38 @@ impl Default for _frame { } } pub type PyFrameObject = _frame; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDictKeyEntry { + pub me_hash: Py_hash_t, + pub me_key: *mut PyObject, + pub me_value: *mut PyObject, +} +impl Default for PyDictKeyEntry { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type dict_lookup_func = ::std::option::Option< + unsafe extern "C" fn( + mp: *mut PyDictObject, + key: *mut PyObject, + hash: Py_hash_t, + value_addr: *mut *mut PyObject, + ) -> Py_ssize_t, +>; +#[repr(C)] +#[derive(Debug)] +pub struct _dictkeysobject { + pub dk_refcnt: Py_ssize_t, + pub dk_size: Py_ssize_t, + pub dk_lookup: dict_lookup_func, + pub dk_usable: Py_ssize_t, + pub dk_nentries: Py_ssize_t, + pub dk_indices: __IncompleteArrayField<::std::os::raw::c_char>, +} +impl Default for _dictkeysobject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} diff --git a/src/python_bindings/v3_8_0.rs b/src/python_bindings/v3_8_0.rs index 3eceb437..8527abba 100644 --- a/src/python_bindings/v3_8_0.rs +++ b/src/python_bindings/v3_8_0.rs @@ -1,4 +1,4 @@ -// Generated bindings for python v3.8.0b3 +// Generated bindings for python v3.8.0b4 #![allow(dead_code)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] @@ -89,6 +89,43 @@ where } } } +#[repr(C)] +#[derive(Default)] +pub struct __IncompleteArrayField(::std::marker::PhantomData); +impl __IncompleteArrayField { + #[inline] + pub fn new() -> Self { + __IncompleteArrayField(::std::marker::PhantomData) + } + #[inline] + pub unsafe fn as_ptr(&self) -> *const T { + ::std::mem::transmute(self) + } + #[inline] + pub unsafe fn as_mut_ptr(&mut self) -> *mut T { + ::std::mem::transmute(self) + } + #[inline] + pub unsafe fn as_slice(&self, len: usize) -> &[T] { + ::std::slice::from_raw_parts(self.as_ptr(), len) + } + #[inline] + pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { + ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) + } +} +impl ::std::fmt::Debug for __IncompleteArrayField { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fmt.write_str("__IncompleteArrayField") + } +} +impl ::std::clone::Clone for __IncompleteArrayField { + #[inline] + fn clone(&self) -> Self { + Self::new() + } +} +impl ::std::marker::Copy for __IncompleteArrayField {} pub type __int64_t = ::std::os::raw::c_longlong; pub type __darwin_size_t = ::std::os::raw::c_ulong; pub type __darwin_wchar_t = ::std::os::raw::c_int; @@ -504,6 +541,7 @@ impl Default for PyBufferProcs { unsafe { ::std::mem::zeroed() } } } +pub type PyTypeObject = _typeobject; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct PyBytesObject { @@ -666,6 +704,68 @@ impl Default for PyUnicodeObject { unsafe { ::std::mem::zeroed() } } } +pub type PyLongObject = _longobject; +pub type digit = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _longobject { + pub ob_base: PyVarObject, + pub ob_digit: [digit; 1usize], +} +impl Default for _longobject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyFloatObject { + pub ob_base: PyObject, + pub ob_fval: f64, +} +impl Default for PyFloatObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyTupleObject { + pub ob_base: PyVarObject, + pub ob_item: [*mut PyObject; 1usize], +} +impl Default for PyTupleObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyListObject { + pub ob_base: PyVarObject, + pub ob_item: *mut *mut PyObject, + pub allocated: Py_ssize_t, +} +impl Default for PyListObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type PyDictKeysObject = _dictkeysobject; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDictObject { + pub ob_base: PyObject, + pub ma_used: Py_ssize_t, + pub ma_version_tag: u64, + pub ma_keys: *mut PyDictKeysObject, + pub ma_values: *mut *mut PyObject, +} +impl Default for PyDictObject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} pub type PyCFunction = ::std::option::Option< unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject, >; @@ -939,6 +1039,41 @@ impl Default for _frame { pub type PyFrameObject = _frame; #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct PyDictKeyEntry { + pub me_hash: Py_hash_t, + pub me_key: *mut PyObject, + pub me_value: *mut PyObject, +} +impl Default for PyDictKeyEntry { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type dict_lookup_func = ::std::option::Option< + unsafe extern "C" fn( + mp: *mut PyDictObject, + key: *mut PyObject, + hash: Py_hash_t, + value_addr: *mut *mut PyObject, + ) -> Py_ssize_t, +>; +#[repr(C)] +#[derive(Debug)] +pub struct _dictkeysobject { + pub dk_refcnt: Py_ssize_t, + pub dk_size: Py_ssize_t, + pub dk_lookup: dict_lookup_func, + pub dk_usable: Py_ssize_t, + pub dk_nentries: Py_ssize_t, + pub dk_indices: __IncompleteArrayField<::std::os::raw::c_char>, +} +impl Default for _dictkeysobject { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct _warnings_runtime_state { pub filters: *mut PyObject, pub once_registry: *mut PyObject, diff --git a/src/python_data_access.rs b/src/python_data_access.rs new file mode 100644 index 00000000..52327319 --- /dev/null +++ b/src/python_data_access.rs @@ -0,0 +1,320 @@ +use std; + +use failure::Error; + +use remoteprocess::ProcessMemory; +use crate::python_interpreters::{StringObject, BytesObject, InterpreterState, Object, TypeObject, TupleObject, ListObject}; +use crate::python_bindings::{v2_7_15, v3_3_7, v3_5_5, v3_6_6, v3_7_0, v3_8_0}; +use crate::version::Version; + +/// Copies a string from a target process. Attempts to handle unicode differences, which mostly seems to be working +pub fn copy_string(ptr: * const T, process: &P) -> Result { + let obj = process.copy_pointer(ptr)?; + if obj.size() >= 4096 { + return Err(format_err!("Refusing to copy {} chars of a string", obj.size())); + } + + let kind = obj.kind(); + + let bytes = process.copy(obj.address(ptr as usize), obj.size() * kind as usize)?; + + match (kind, obj.ascii()) { + (4, _) => { + #[allow(clippy::cast_ptr_alignment)] + let chars = unsafe { std::slice::from_raw_parts(bytes.as_ptr() as * const char, bytes.len() / 4) }; + Ok(chars.iter().collect()) + }, + (2, _) => { + // UCS2 strings aren't used internally after v3.3: https://www.python.org/dev/peps/pep-0393/ + // TODO: however with python 2.7 they could be added with --enable-unicode=ucs2 configure flag. + // or with python 3.2 --with-wide-unicode=ucs2 + Err(format_err!("ucs2 strings aren't supported yet!")) + }, + (1, true) => Ok(String::from_utf8(bytes)?), + (1, false) => Ok(bytes.iter().map(|&b| { b as char }).collect()), + _ => Err(format_err!("Unknown string kind {}", kind)) + } +} + +/// Copies data from a PyBytesObject (currently only lnotab object) +pub fn copy_bytes(ptr: * const T, process: &P) -> Result, Error> { + let obj = process.copy_pointer(ptr)?; + let size = obj.size(); + if size >= 65536 { + return Err(format_err!("Refusing to copy {} bytes", size)); + } + Ok(process.copy(obj.address(ptr as usize), size as usize)?) +} + +/// Copys a i64 from a PyLongObject. Returns the value + if it overflowed +pub fn copy_long(process: &remoteprocess::Process, addr: usize) -> Result<(i64, bool), Error> { + // this is PyLongObject for a specific version of python, but this works since it's binary compatible + // layout across versions we're targetting + let value = process.copy_pointer(addr as *const crate::python_bindings::v3_7_0::PyLongObject)?; + match value.ob_base.ob_size { + -1 => Ok((value.ob_digit[0] as i64 * -1, false)), + 0 => Ok((0, false)), + 1 => Ok((value.ob_digit[0] as i64, false)), + 2 => { + let shift: u32 = process.copy_struct(addr + std::mem::size_of_val(&value) - 4)?; + Ok((((shift as i64) << 30) + value.ob_digit[0] as i64, false)) + } + -2 => { + let shift: u32 = process.copy_struct(addr + std::mem::size_of_val(&value) - 4)?; + Ok((-1 * (((shift as i64) << 30) + value.ob_digit[0] as i64), false)) + } + // we don't support arbitrary sized integers yet, signal this by returning that we've overflowed + _ => Ok((value.ob_base.ob_size as i64, true)) + } +} + +/// Allows iteration of a python dictionary. Only supports python 3.6+ right now +pub struct DictIterator<'a> { + process: &'a remoteprocess::Process, + entries_addr: usize, + index: usize, + entries: usize, + values: usize +} + +impl<'a> DictIterator<'a> { + pub fn from(process: &'a remoteprocess::Process, addr: usize) -> Result { + // Getting this going generically is tricky: there is a lot of variation on how dictionaries are handled + // instead this just focuses on a single version, which works for python 3.6/3.7/3.8 + let dict: crate::python_bindings::v3_7_0::PyDictObject = process.copy_struct(addr)?; + let keys = process.copy_pointer(dict.ma_keys)?; + let index_size = match keys.dk_size { + 0..=0xff => 1, + 0..=0xffff => 2, + 0..=0xffffffff => 4, + _ => 8 + }; + let byteoffset = (keys.dk_size * index_size) as usize; + let entries_addr = dict.ma_keys as usize + byteoffset + std::mem::size_of_val(&keys); + Ok(DictIterator{process, entries_addr, index: 0, entries: keys.dk_nentries as usize, values: dict.ma_values as usize}) + } +} + +impl<'a> Iterator for DictIterator<'a> { + type Item = Result<(usize, usize), Error>; + fn next(&mut self) -> Option { + while self.index < self.entries { + let addr = self.index* std::mem::size_of::() + self.entries_addr; + self.index += 1; + let entry: Result = self.process.copy_struct(addr); + match entry { + Ok(entry) => { + if entry.me_key.is_null() { + continue; + } + + let value = if self.values != 0 { + let valueaddr = self.values + (self.index - 1) * std::mem::size_of::<* mut crate::python_bindings::v3_7_0::PyObject>(); + match self.process.copy_struct(valueaddr) { + Ok(addr) => addr, + Err(e) => { return Some(Err(e.into())); } + } + } else { + entry.me_value as usize + }; + + return Some(Ok((entry.me_key as usize, value))) + }, + Err(e) => { + return Some(Err(e.into())) + } + } + } + + None + } +} + +/// Converts a python object in the other process to a string representation +pub fn stringify_pyobject(process: &remoteprocess::Process, + version: &Version, + addr: usize, + max_length: isize) -> Result { + match version { + Version{major: 2, minor: 3..=7, ..} => format_variable::(process, version, addr, max_length), + Version{major: 3, minor: 3, ..} => format_variable::(process, version, addr, max_length), + Version{major: 3, minor: 4..=5, ..} => format_variable::(process, version, addr, max_length), + Version{major: 3, minor: 6, ..} => format_variable::(process, version, addr, max_length), + Version{major: 3, minor: 7, ..} => format_variable::(process, version, addr, max_length), + Version{major: 3, minor: 8, patch: 0, ..} => { + match version.release_flags.as_ref() { + "a1" | "a2" | "a3" => format_variable::(process, version, addr, max_length), + _ => format_variable::(process, version, addr, max_length) + } + }, + Version{major: 3, minor: 8..=9, ..} => format_variable::(process, version, addr, max_length), + _ => Err(format_err!("Unsupported version of Python: {}", version)) + } +} + +const PY_TPFLAGS_LONG_SUBCLASS: usize = 1 << 24; +const PY_TPFLAGS_LIST_SUBCLASS: usize = 1 << 25; +const PY_TPFLAGS_TUPLE_SUBCLASS: usize = 1 << 26; +const PY_TPFLAGS_UNICODE_SUBCLASS: usize = 1 << 28; +const PY_TPFLAGS_DICT_SUBCLASS: usize = 1 << 29; + +/// Converts a python variable in the other process to a human readable string +/// similar to stringify_pyobject - but has knows the type of the python interpreter +pub fn format_variable(process: &remoteprocess::Process, version: &Version, addr: usize, max_length: isize) + -> Result where I: InterpreterState { + let value: I::Object = process.copy_struct(addr)?; + let value_type = process.copy_pointer(value.ob_type())?; + + // get the typename (truncating to 128 bytes if longer) + let max_type_len = 128; + let value_type_name = process.copy(value_type.name() as usize, max_type_len)?; + let length = value_type_name.iter().position(|&x| x == 0).unwrap_or(max_type_len); + let value_type_name = std::str::from_utf8(&value_type_name[..length])?; + + // use the flags/typename to figure out how to stringify this object + let flags = value_type.flags(); + let formatted = if flags & PY_TPFLAGS_UNICODE_SUBCLASS != 0 { + let value = copy_string(addr as *const I::StringObject, process)?.replace("\"", "\\\""); + if value.len() as isize >= max_length - 5 { + format!("\"{}...\"", &value[..(max_length - 5) as usize]) + } else { + format!("\"{}\"", value) + } + } else if flags & PY_TPFLAGS_LONG_SUBCLASS != 0 { + let (value, overflowed) = copy_long(process, addr)?; + // bool objects are subclasses of longs, deal with as appropiate + if value_type_name == "bool" { + (if value > 0 { "True" } else { "False" }).to_owned() + } else if overflowed { + // we don't handle arbitray sized integer values (max is 2**60) + if value > 0 { "+bigint".to_owned() } else { "-bigint".to_owned() } + } else { + format!("{}", value) + } + } else if flags & PY_TPFLAGS_DICT_SUBCLASS != 0 { + if version.major == 3 && version.minor >= 6 { + let mut values = Vec::new(); + let mut remaining = max_length - 2; + for entry in DictIterator::from(process, addr)? { + let (key, value) = entry?; + let key = format_variable::(process, version, key, remaining)?; + let value = format_variable::(process, version, value, remaining)?; + remaining -= (key.len() + value.len()) as isize + 4; + if remaining <= 5 { + values.push("...".to_owned()); + break; + } + values.push(format!("{}: {}", key, value)); + } + format!("{{{}}}", values.join(", ")) + } else { + // TODO: support getting dictionaries from older versions of python + "dict".to_owned() + } + } else if flags & PY_TPFLAGS_LIST_SUBCLASS != 0 { + let object: I::ListObject = process.copy_struct(addr)?; + let addr = object.item() as usize; + let mut values = Vec::new(); + let mut remaining = max_length - 2; + for i in 0..object.size() { + let valueptr: *mut I::Object = process.copy_struct(addr + i * std::mem::size_of::<* mut I::Object>())?; + let value = format_variable::(process, version, valueptr as usize, remaining)?; + remaining -= value.len() as isize + 2; + if remaining <= 5 { + values.push("...".to_owned()); + break; + } + values.push(value); + } + format!("[{}]", values.join(", ")) + } else if flags & PY_TPFLAGS_TUPLE_SUBCLASS != 0 { + let object: I::TupleObject = process.copy_struct(addr)?; + let mut values = Vec::new(); + let mut remaining = max_length - 2; + for i in 0..object.size() { + if remaining <= 5 { + values.push("...".to_owned()); + break; + } + let value_addr: *mut I::Object = process.copy_struct(object.address(addr, i))?; + let value = format_variable::(process, version, value_addr as usize, remaining)?; + remaining -= value.len() as isize + 2; + values.push(value); + } + format!("({})", values.join(", ")) + } else if value_type_name == "float" { + let value = process.copy_pointer(addr as *const crate::python_bindings::v3_7_0::PyFloatObject)?; + format!("{}", value.ob_fval) + } else if value_type_name == "NoneType" { + "None".to_owned() + } else { + format!("<{} at 0x{:x}>", value_type_name, addr) + }; + + Ok(formatted) +} + +#[cfg(test)] +pub mod tests { + // the idea here is to create various cpython interpretator structs locally + // and then test out that the above code handles appropiately + use super::*; + use remoteprocess::LocalProcess; + use python_bindings::v3_7_0::{PyBytesObject, PyVarObject, PyUnicodeObject, PyASCIIObject}; + use std::ptr::copy_nonoverlapping; + + // python stores data after pybytesobject/pyasciiobject. hack by initializing a 4k buffer for testing. + // TODO: get better at Rust and figure out a better solution + #[allow(dead_code)] + pub struct AllocatedPyByteObject { + pub base: PyBytesObject, + pub storage: [u8; 4096] + } + + #[allow(dead_code)] + pub struct AllocatedPyASCIIObject { + pub base: PyASCIIObject, + pub storage: [u8; 4096] + } + + pub fn to_byteobject(bytes: &[u8]) -> AllocatedPyByteObject { + let ob_size = bytes.len() as isize; + let base = PyBytesObject{ob_base: PyVarObject{ob_size, ..Default::default()}, ..Default::default()}; + let mut ret = AllocatedPyByteObject{base, storage: [0 as u8; 4096]}; + unsafe { copy_nonoverlapping(bytes.as_ptr(), ret.base.ob_sval.as_mut_ptr() as *mut u8, bytes.len()); } + ret + } + + pub fn to_asciiobject(input: &str) -> AllocatedPyASCIIObject { + let bytes: Vec = input.bytes().collect(); + let mut base = PyASCIIObject{length: bytes.len() as isize, ..Default::default()}; + base.state.set_compact(1); + base.state.set_kind(1); + base.state.set_ascii(1); + let mut ret = AllocatedPyASCIIObject{base, storage: [0 as u8; 4096]}; + unsafe { + let ptr = &mut ret as *mut AllocatedPyASCIIObject as *mut u8; + let dst = ptr.offset(std::mem::size_of::() as isize); + copy_nonoverlapping(bytes.as_ptr(), dst, bytes.len()); + } + ret + } + + #[test] + fn test_copy_string() { + let original = "function_name"; + let obj = to_asciiobject(original); + + let unicode: &PyUnicodeObject = unsafe{ std::mem::transmute(&obj.base) }; + let copied = copy_string(unicode, &LocalProcess).unwrap(); + assert_eq!(copied, original); + } + + #[test] + fn test_copy_bytes() { + let original = [10_u8, 20, 30, 40, 50, 70, 80]; + let bytes = to_byteobject(&original); + let copied = copy_bytes(&bytes.base, &LocalProcess).unwrap(); + assert_eq!(copied, original); + } +} diff --git a/src/python_interpreters.rs b/src/python_interpreters.rs index d04ae836..2fcf6624 100644 --- a/src/python_interpreters.rs +++ b/src/python_interpreters.rs @@ -14,7 +14,12 @@ use std; pub trait InterpreterState { type ThreadState: ThreadState; + type Object: Object; + type StringObject: StringObject; + type ListObject: ListObject; + type TupleObject: TupleObject; fn head(&self) -> * mut Self::ThreadState; + fn modules(&self) -> *mut Self::Object; } pub trait ThreadState { @@ -29,6 +34,7 @@ pub trait ThreadState { pub trait FrameObject { type CodeObject: CodeObject; + fn code(&self) -> * mut Self::CodeObject; fn lasti(&self) -> i32; fn back(&self) -> * mut Self; @@ -37,11 +43,15 @@ pub trait FrameObject { pub trait CodeObject { type StringObject: StringObject; type BytesObject: BytesObject; + type TupleObject: TupleObject; fn name(&self) -> * mut Self::StringObject; fn filename(&self) -> * mut Self::StringObject; fn lnotab(&self) -> * mut Self::BytesObject; fn first_lineno(&self) -> i32; + fn nlocals(&self) -> i32; + fn argcount(&self) -> i32; + fn varnames(&self) -> * mut Self::TupleObject; } pub trait BytesObject { @@ -56,6 +66,28 @@ pub trait StringObject { fn address(&self, base: usize) -> usize; } +pub trait TupleObject { + fn size(&self) -> usize; + fn address(&self, base: usize, index: usize) -> usize; +} + +pub trait ListObject { + type Object: Object; + fn size(&self) -> usize; + fn item(&self) -> *mut *mut Self::Object; +} + +pub trait Object { + type TypeObject: TypeObject; + fn ob_type(&self) -> * mut Self::TypeObject; +} + +pub trait TypeObject { + fn name(&self) -> *const ::std::os::raw::c_char; + fn dictoffset(&self) -> isize; + fn flags(&self) -> usize; +} + fn offset_of(object: *const T, member: *const M) -> usize { member as usize - object as usize } @@ -67,7 +99,12 @@ macro_rules! PythonCommonImpl { ($py: ident, $bytesobject: ident, $stringobject: ident) => ( impl InterpreterState for $py::PyInterpreterState { type ThreadState = $py::PyThreadState; + type Object = $py::PyObject; + type StringObject = $py::$stringobject; + type ListObject = $py::PyListObject; + type TupleObject = $py::PyTupleObject; fn head(&self) -> * mut Self::ThreadState { self.tstate_head } + fn modules(&self) -> * mut Self::Object { self.modules } } impl ThreadState for $py::PyThreadState { @@ -81,6 +118,7 @@ macro_rules! PythonCommonImpl { impl FrameObject for $py::PyFrameObject { type CodeObject = $py::PyCodeObject; + fn code(&self) -> * mut Self::CodeObject { self.f_code } fn lasti(&self) -> i32 { self.f_lasti } fn back(&self) -> * mut Self { self.f_back } @@ -89,16 +127,32 @@ macro_rules! PythonCommonImpl { impl CodeObject for $py::PyCodeObject { type BytesObject = $py::$bytesobject; type StringObject = $py::$stringobject; + type TupleObject = $py::PyTupleObject; + fn name(&self) -> * mut Self::StringObject { self.co_name as * mut Self::StringObject } fn filename(&self) -> * mut Self::StringObject { self.co_filename as * mut Self::StringObject } fn lnotab(&self) -> * mut Self::BytesObject { self.co_lnotab as * mut Self::BytesObject } fn first_lineno(&self) -> i32 { self.co_firstlineno } + fn nlocals(&self) -> i32 { self.co_nlocals } + fn argcount(&self) -> i32 { self.co_argcount } + fn varnames(&self) -> * mut Self::TupleObject { self.co_varnames as * mut Self::TupleObject } + } + + impl Object for $py::PyObject { + type TypeObject = $py::PyTypeObject; + fn ob_type(&self) -> * mut Self::TypeObject { self.ob_type as * mut Self::TypeObject } + } + + impl TypeObject for $py::PyTypeObject { + fn name(&self) -> *const ::std::os::raw::c_char { self.tp_name } + fn dictoffset(&self) -> isize { self.tp_dictoffset } + fn flags(&self) -> usize { self.tp_flags as usize } } ) } -// String/Byte handling for Python 3.3+ -macro_rules! Python3StringImpl { +// String/Byte/List/Tuple handling for Python 3.3+ +macro_rules! Python3Impl { ($py: ident) => ( impl BytesObject for $py::PyBytesObject { fn size(&self) -> usize { self.ob_base.ob_size as usize } @@ -124,45 +178,65 @@ macro_rules! Python3StringImpl { } } } - ) -} -// String/Byte handling for Python 2.7 (and maybe others?) -macro_rules! Python2StringImpl { - ($py: ident) => ( - impl BytesObject for $py::PyStringObject { - fn size(&self) -> usize { self.ob_size as usize } - fn address(&self, base: usize) -> usize { base + offset_of(self, &self.ob_sval) } + impl ListObject for $py::PyListObject { + type Object = $py::PyObject; + fn size(&self) -> usize { self.ob_base.ob_size as usize } + fn item(&self) -> *mut *mut Self::Object { self.ob_item } } - impl StringObject for $py::PyStringObject { - fn ascii(&self) -> bool { true } - fn kind(&self) -> u32 { 1 } - fn size(&self) -> usize { self.ob_size as usize } - fn address(&self, base: usize) -> usize { base + offset_of(self, &self.ob_sval) } + impl TupleObject for $py::PyTupleObject { + fn size(&self) -> usize { self.ob_base.ob_size as usize } + fn address(&self, base: usize, index: usize) -> usize { + base + offset_of(self, &self.ob_item) + index * std::mem::size_of::<* mut $py::PyObject>() + } } ) } + // Python 3.8 PythonCommonImpl!(v3_8_0, PyBytesObject, PyUnicodeObject); -Python3StringImpl!(v3_8_0); +Python3Impl!(v3_8_0); // Python 3.7 PythonCommonImpl!(v3_7_0, PyBytesObject, PyUnicodeObject); -Python3StringImpl!(v3_7_0); +Python3Impl!(v3_7_0); // Python 3.6 PythonCommonImpl!(v3_6_6, PyBytesObject, PyUnicodeObject); -Python3StringImpl!(v3_6_6); +Python3Impl!(v3_6_6); // python 3.5 and python 3.4 PythonCommonImpl!(v3_5_5, PyBytesObject, PyUnicodeObject); -Python3StringImpl!(v3_5_5); +Python3Impl!(v3_5_5); // python 3.3 PythonCommonImpl!(v3_3_7, PyBytesObject, PyUnicodeObject); -Python3StringImpl!(v3_3_7); +Python3Impl!(v3_3_7); // Python 2.7 PythonCommonImpl!(v2_7_15, PyStringObject, PyStringObject); -Python2StringImpl!(v2_7_15); +impl BytesObject for v2_7_15::PyStringObject { + fn size(&self) -> usize { self.ob_size as usize } + fn address(&self, base: usize) -> usize { base + offset_of(self, &self.ob_sval) } +} + +impl StringObject for v2_7_15::PyStringObject { + fn ascii(&self) -> bool { true } + fn kind(&self) -> u32 { 1 } + fn size(&self) -> usize { self.ob_size as usize } + fn address(&self, base: usize) -> usize { base + offset_of(self, &self.ob_sval) } +} + +impl ListObject for v2_7_15::PyListObject { + type Object = v2_7_15::PyObject; + fn size(&self) -> usize { self.ob_size as usize } + fn item(&self) -> *mut *mut Self::Object { self.ob_item } +} + +impl TupleObject for v2_7_15::PyTupleObject { + fn size(&self) -> usize { self.ob_size as usize } + fn address(&self, base: usize, index: usize) -> usize { + base + offset_of(self, &self.ob_item) + index * std::mem::size_of::<* mut v2_7_15::PyObject>() + } +} \ No newline at end of file diff --git a/src/python_spy.rs b/src/python_spy.rs index 4734b6eb..51dcd9b5 100644 --- a/src/python_spy.rs +++ b/src/python_spy.rs @@ -201,7 +201,7 @@ impl PythonSpy { while !threads.is_null() { // Get the stack trace of the python thread let thread = self.process.copy_pointer(threads).context("Failed to copy PyThreadState")?; - let mut trace = get_stack_trace(&thread, &self.process)?; + let mut trace = get_stack_trace(&thread, &self.process, self.config.dump_locals)?; // Try getting the native thread id let python_thread_id = thread.thread_id(); diff --git a/src/stack_trace.rs b/src/stack_trace.rs index 9c1c45f4..12bdf24a 100644 --- a/src/stack_trace.rs +++ b/src/stack_trace.rs @@ -4,7 +4,8 @@ use failure::{Error, ResultExt}; use remoteprocess::ProcessMemory; -use crate::python_interpreters::{InterpreterState, ThreadState, FrameObject, CodeObject, StringObject, BytesObject}; +use crate::python_interpreters::{InterpreterState, ThreadState, FrameObject, CodeObject, TupleObject}; +use crate::python_data_access::{copy_string, copy_bytes}; /// Call stack for a single python thread #[derive(Debug, Clone, Serialize)] @@ -34,6 +35,15 @@ pub struct Frame { pub short_filename: Option, /// The line number inside the file (or 0 for native frames without line information) pub line: i32, + /// Local Variables associated with the frame + pub locals: Option>, +} + +#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Clone, Serialize)] +pub struct LocalVariable { + pub name: String, + pub addr: usize, + pub arg: bool } /// Given an InterpreterState, this function returns a vector of stack traces for each thread @@ -44,7 +54,7 @@ pub fn get_stack_traces(interpreter: &I, process: &P) -> Result<(Vec 4096 { return Err(format_err!("Max thread recursion depth reached")); @@ -55,9 +65,9 @@ pub fn get_stack_traces(interpreter: &I, process: &P) -> Result<(Vec(thread: &T, process: &P) -> Result +pub fn get_stack_trace(thread: &T, process: &P, copy_locals: bool) -> Result where T: ThreadState, P: ProcessMemory { - // TODO: just return frames here? everything else probably should be returned out of scopee + // TODO: just return frames here? everything else probably should be returned out of scope let mut frames = Vec::new(); let mut frame_ptr = thread.frame(); while !frame_ptr.is_null() { @@ -67,7 +77,6 @@ pub fn get_stack_trace(thread: &T, process: &P) -> Result line, Err(e) => { @@ -80,7 +89,13 @@ pub fn get_stack_trace(thread: &T, process: &P) -> Result 4096 { return Err(format_err!("Max frame recursion depth reached")); } @@ -99,6 +114,19 @@ impl StackTrace { (false, true) => "active", } } + + pub fn format_threadid(&self) -> String { + // native threadids in osx are kinda useless, use the pthread id instead + #[cfg(target_os="macos")] + return format!("{:#X}", self.thread_id); + + // otherwise use the native threadid if given + #[cfg(not(target_os="macos"))] + match self.os_thread_id { + Some(tid) => format!("{}", tid), + None => format!("{:#X}", self.thread_id) + } + } } /// Returns the line number from a PyCodeObject (given the lasti index from a PyFrameObject) @@ -124,90 +152,35 @@ fn get_line_number(code: &C, lasti: i32, proces Ok(line_number) } -/// Copies a string from a target process. Attempts to handle unicode differences, which mostly seems to be working -pub fn copy_string(ptr: * const T, process: &P) -> Result { - let obj = process.copy_pointer(ptr)?; - if obj.size() >= 4096 { - return Err(format_err!("Refusing to copy {} chars of a string", obj.size())); - } +fn get_locals(code: &C, frameptr: *const F, frame: &F, process: &P) + -> Result, Error> { + let local_count = code.nlocals() as usize; + let argcount = code.argcount() as usize; + let varnames = process.copy_pointer(code.varnames())?; - let kind = obj.kind(); - - let bytes = process.copy(obj.address(ptr as usize), obj.size() * kind as usize)?; - - match (kind, obj.ascii()) { - (4, _) => { - #[allow(clippy::cast_ptr_alignment)] - let chars = unsafe { std::slice::from_raw_parts(bytes.as_ptr() as * const char, bytes.len() / 4) }; - Ok(chars.iter().collect()) - }, - (2, _) => { - // UCS2 strings aren't used internally after v3.3: https://www.python.org/dev/peps/pep-0393/ - // TODO: however with python 2.7 they could be added with --enable-unicode=ucs2 configure flag. - // or with python 3.2 --with-wide-unicode=ucs2 - Err(format_err!("ucs2 strings aren't supported yet!")) - }, - (1, true) => Ok(String::from_utf8(bytes)?), - (1, false) => Ok(bytes.iter().map(|&b| { b as char }).collect()), - _ => Err(format_err!("Unknown string kind {}", kind)) - } -} + let ptr_size = std::mem::size_of::<*const i32>(); + let locals_addr = frameptr as usize + std::mem::size_of_val(frame) - ptr_size; + + let mut ret = Vec::new(); -/// Copies data from a PyBytesObject (currently only lnotab object) -pub fn copy_bytes(ptr: * const T, process: &P) -> Result, Error> { - let obj = process.copy_pointer(ptr)?; - let size = obj.size(); - if size >= 65536 { - return Err(format_err!("Refusing to copy {} bytes", size)); + for i in 0..local_count { + let nameptr: *const C::StringObject = process.copy_struct(varnames.address(code.varnames() as usize, i))?; + let name = copy_string(nameptr, process)?; + let addr: usize = process.copy_struct(locals_addr + i * ptr_size)?; + if addr == 0 { + continue; + } + ret.push(LocalVariable{name, addr, arg: i < argcount }); } - Ok(process.copy(obj.address(ptr as usize), size as usize)?) + Ok(ret) } #[cfg(test)] mod tests { - // the idea here is to create various cpython interpretator structs locally - // and then test out that the above code handles appropiately use super::*; use remoteprocess::LocalProcess; - use python_bindings::v3_7_0::{PyCodeObject, PyBytesObject, PyVarObject, PyUnicodeObject, PyASCIIObject}; - use std::ptr::copy_nonoverlapping; - - // python stores data after pybytesobject/pyasciiobject. hack by initializing a 4k buffer for testing. - // TODO: get better at Rust and figure out a better solution - #[allow(dead_code)] - struct AllocatedPyByteObject { - base: PyBytesObject, - storage: [u8; 4096] - } - - #[allow(dead_code)] - struct AllocatedPyASCIIObject { - base: PyASCIIObject, - storage: [u8; 4096] - } - - fn to_byteobject(bytes: &[u8]) -> AllocatedPyByteObject { - let ob_size = bytes.len() as isize; - let base = PyBytesObject{ob_base: PyVarObject{ob_size, ..Default::default()}, ..Default::default()}; - let mut ret = AllocatedPyByteObject{base, storage: [0 as u8; 4096]}; - unsafe { copy_nonoverlapping(bytes.as_ptr(), ret.base.ob_sval.as_mut_ptr() as *mut u8, bytes.len()); } - ret - } - - fn to_asciiobject(input: &str) -> AllocatedPyASCIIObject { - let bytes: Vec = input.bytes().collect(); - let mut base = PyASCIIObject{length: bytes.len() as isize, ..Default::default()}; - base.state.set_compact(1); - base.state.set_kind(1); - base.state.set_ascii(1); - let mut ret = AllocatedPyASCIIObject{base, storage: [0 as u8; 4096]}; - unsafe { - let ptr = &mut ret as *mut AllocatedPyASCIIObject as *mut u8; - let dst = ptr.offset(std::mem::size_of::() as isize); - copy_nonoverlapping(bytes.as_ptr(), dst, bytes.len()); - } - ret - } + use python_bindings::v3_7_0::{PyCodeObject}; + use python_data_access::tests::to_byteobject; #[test] fn test_get_line_number() { @@ -218,22 +191,4 @@ mod tests { let lineno = get_line_number(&code, 30, &LocalProcess).unwrap(); assert_eq!(lineno, 7); } - - #[test] - fn test_copy_string() { - let original = "function_name"; - let obj = to_asciiobject(original); - - let unicode: &PyUnicodeObject = unsafe{ std::mem::transmute(&obj.base) }; - let copied = copy_string(unicode, &LocalProcess).unwrap(); - assert_eq!(copied, original); - } - - #[test] - fn test_copy_bytes() { - let original = [10_u8, 20, 30, 40, 50, 70, 80]; - let bytes = to_byteobject(&original); - let copied = copy_bytes(&bytes.base, &LocalProcess).unwrap(); - assert_eq!(copied, original); - } } diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 23fa155a..f8a3ff6c 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -160,3 +160,73 @@ fn test_unicode() { assert!(!traces[0].owns_gil); } +#[test] +fn test_local_vars() { + #[cfg(target_os="macos")] + { + // We need root permissions here to run this on OSX + if unsafe { libc::geteuid() } != 0 { + return; + } + } + + let config = Config{dump_locals: true, ..Default::default()}; + let mut runner = TestRunner::new(config, "./tests/scripts/local_vars.py"); + + let traces = runner.spy.get_stack_traces().unwrap(); + assert_eq!(traces.len(), 1); + let trace = &traces[0]; + assert_eq!(trace.frames.len(), 2); + let frame = &trace.frames[0]; + let locals = frame.locals.as_ref().unwrap(); + + assert_eq!(locals.len(), 8); + + let arg1 = &locals[0]; + assert_eq!(arg1.name, "arg1"); + assert!(arg1.arg); + let arg1_str = py_spy::stringify_pyobject(&runner.spy.process, &runner.spy.version, arg1.addr, 128).unwrap(); + assert_eq!(arg1_str, "\"foo\""); + + let arg2 = &locals[1]; + assert_eq!(arg2.name, "arg2"); + assert!(arg2.arg); + let arg2_str = py_spy::stringify_pyobject(&runner.spy.process, &runner.spy.version, arg2.addr, 128).unwrap(); + assert_eq!(arg2_str, "None"); + + let arg3 = &locals[2]; + assert_eq!(arg3.name, "arg3"); + assert!(arg3.arg); + let arg3_str = py_spy::stringify_pyobject(&runner.spy.process, &runner.spy.version, arg3.addr, 128).unwrap(); + assert_eq!(arg3_str, "True"); + + let local1 = &locals[3]; + assert_eq!(local1.name, "local1"); + assert!(!local1.arg); + let local1_str = py_spy::stringify_pyobject(&runner.spy.process, &runner.spy.version, local1.addr, 128).unwrap(); + assert_eq!(local1_str, "[-1234, 5678]"); + + let local2 = &locals[4]; + assert_eq!(local2.name, "local2"); + assert!(!local2.arg); + let local2_str = py_spy::stringify_pyobject(&runner.spy.process, &runner.spy.version, local2.addr, 128).unwrap(); + assert_eq!(local2_str, "(\"a\", \"b\", \"c\")"); + + let local3 = &locals[5]; + assert_eq!(local3.name, "local3"); + assert!(!local3.arg); + let local3_str = py_spy::stringify_pyobject(&runner.spy.process, &runner.spy.version, local3.addr, 128).unwrap(); + assert_eq!(local3_str, "123456789123456789"); + + let local4 = &locals[6]; + assert_eq!(local4.name, "local4"); + assert!(!local4.arg); + let local4_str = py_spy::stringify_pyobject(&runner.spy.process, &runner.spy.version, local4.addr, 128).unwrap(); + assert_eq!(local4_str, "3.1415"); + + let local5 = &locals[7]; + assert_eq!(local5.name, "local5"); + assert!(!local5.arg); + let local4_str = py_spy::stringify_pyobject(&runner.spy.process, &runner.spy.version, local5.addr, 128).unwrap(); + assert_eq!(local4_str, "{\"a\": False, \"b\": (1, 2, 3)}"); +} diff --git a/tests/scripts/local_vars.py b/tests/scripts/local_vars.py new file mode 100644 index 00000000..2e1d2f71 --- /dev/null +++ b/tests/scripts/local_vars.py @@ -0,0 +1,13 @@ +import time + + +def local_variable_lookup(arg1="foo", arg2=None, arg3=True): + local1 = [-1234, 5678] + local2 = ("a", "b", "c") + local3 = 123456789123456789 + local4 = 3.1415 + local5 = {"a": False, "b": (1, 2, 3)} + time.sleep(100000) + +if __name__ == "__main__": + local_variable_lookup()