Skip to content

Commit

Permalink
add get fields on objects
Browse files Browse the repository at this point in the history
  • Loading branch information
ubamrein committed Jan 27, 2023
1 parent 66cf8d4 commit d843b6d
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 31 deletions.
3 changes: 3 additions & 0 deletions coeus-python/coeus_python.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class Debugger:
"""Get valid code indices from function"""
def get_breakpoints(self) -> list[VmBreakpoint]:
"""Get all currently set breakpoints"""
class VmInstance:
def to_string(self, debugger: Debugger) -> str:
"""Get a string representation of the object"""
class VmBreakpoint:
def location(self) -> str:
"""Get the location identifier for this breakpoint"""
Expand Down
55 changes: 41 additions & 14 deletions coeus-python/src/debugging.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
use std::convert::{TryFrom};
use std::convert::TryFrom;

// Copyright (c) 2023 Ubique Innovation AG <https://www.ubique.ch>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use coeus::{
coeus_debug::{
jdwp::JdwpClient,
models::{
Composite, Event, JdwpPacket, SlotValue,
StackFrame,
},
Runtime,
},
use coeus::coeus_debug::{
jdwp::JdwpClient,
models::{ClassInstance, Composite, Event, JdwpPacket, SlotValue, StackFrame},
Runtime,
};
use pyo3::{
exceptions::PyRuntimeError,
ffi::Py_None,
pyclass, pymethods,
types::{PyBool, PyFloat, PyLong, PyModule, PyString},
Py, PyAny, PyResult, Python, ToPyObject,
IntoPy, Py, PyAny, PyResult, Python, ToPyObject,
};

use crate::analysis::Method;
Expand All @@ -44,6 +40,35 @@ impl VmBreakpoint {
}
}

#[pyclass]
#[derive(Clone)]
pub struct VmInstance {
inner: ClassInstance,
}

#[pymethods]
impl VmInstance {
pub fn to_string(&self, py: Python, debugger: &mut Debugger) -> PyResult<String> {
let mut output = self.inner.signature.to_string();
output.push('\n');
for f in &self.inner.fields {
let value = if let Some(v) = &f.value {
let stack_val = StackValue { slot: v.clone() };
let s = stack_val.get_value(debugger, py)?;
if let Ok(val) = s.extract::<VmInstance>(py) {
format!("[{}@{}]", val.inner.signature, val.inner.object_id)
} else {
format!("{}", s)
}
} else {
"null".to_string()
};
output.push_str(&format!("\t{} : {} = {:?} \n", f.name, f.signature, value));
}
Ok(output)
}
}

#[pyclass]
/// A debugger struct, holding the jdwp_client for communication with the Debugger
/// and the runtime
Expand Down Expand Up @@ -151,7 +176,7 @@ impl StackValue {
pub fn get_value(&self, debugger: &mut Debugger, py: Python) -> PyResult<Py<PyAny>> {
match self.slot.value {
coeus::coeus_debug::models::Value::Object(o) => {
let s = match debugger.jdwp_client.get_object_signature(&debugger.rt, o) {
let s = match debugger.jdwp_client.get_object(&debugger.rt, o) {
Ok(s) => s,
Err(e) => {
return Err(PyRuntimeError::new_err(format!(
Expand All @@ -160,7 +185,7 @@ impl StackValue {
)))
}
};
Ok(s.to_object(py))
Ok(VmInstance { inner: s }.into_py(py))
}
coeus::coeus_debug::models::Value::Byte(b) => Ok(b.to_object(py)),
coeus::coeus_debug::models::Value::Short(s) => Ok(s.to_object(py)),
Expand All @@ -177,7 +202,8 @@ impl StackValue {
coeus::coeus_debug::models::Value::Double(d) => Ok(d.to_object(py)),
coeus::coeus_debug::models::Value::Boolean(b) => Ok((b == 1).to_object(py)),
coeus::coeus_debug::models::Value::Char(c) => Ok(c.to_object(py)),
coeus::coeus_debug::models::Value::Void => todo!(),
coeus::coeus_debug::models::Value::Void => Ok(None::<String>.to_object(py)),
coeus::coeus_debug::models::Value::Reference(_) => Ok(None::<String>.to_object(py)),
}
}
}
Expand Down Expand Up @@ -324,5 +350,6 @@ pub(crate) fn register(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<DebuggerStackFrame>()?;
m.add_class::<StackValue>()?;
m.add_class::<VmBreakpoint>()?;
m.add_class::<VmInstance>()?;
Ok(())
}
120 changes: 108 additions & 12 deletions coeus/coeus_debug/src/jdwp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use tokio::{

use crate::{
models::{
Class, JdwpCommandPacket, JdwpEventType, JdwpPacket, JdwpReplyPacket, Location, Method,
Slot, SlotValue, StackFrame, VariableTable, VmType,
Class, ClassInstance, Field, JdwpCommandPacket, JdwpEventType, JdwpPacket, JdwpReplyPacket,
Location, Method, Slot, SlotValue, StackFrame, VariableTable, VmType,
},
FromBytes, ToBytes,
};
Expand Down Expand Up @@ -162,6 +162,71 @@ impl JdwpClient {
let reference_id = reader.read_u64::<BigEndian>()?;
Ok(reference_id)
}
async fn get_field_ids_for_reference_type(
&mut self,
reference_id: u64,
) -> anyhow::Result<Vec<Field>> {
let cmd = JdwpCommandPacket::get_fields_for_reference_type(rand::random(), reference_id)?;
self.send_cmd(JdwpPacket::CommandPacket(cmd))?;
let reply = self
.wait_for_package()
.await
.ok_or_else(|| anyhow::Error::msg("No answer"))?;
let JdwpPacket::ReplyPacket(reply) = reply else {
panic!("Wrong packet");
};
let mut reader = Cursor::new(reply.get_data());
let num_of_fields = reader.read_u32::<BigEndian>()?;
let mut fields = vec![];
for _ in 0..num_of_fields {
fields.push(Field::from_bytes(&mut reader).context("Get field id failed")?);
}
Ok(fields)
}
async fn get_fields(
&mut self,
object_id: u64,
fields: Vec<Field>,
) -> anyhow::Result<Vec<Field>> {
let fields = fields
.into_iter()
.filter(|f| f.flags & 0x8 != 0x8)
.collect::<Vec<_>>();
if fields.is_empty() {
return Ok(vec![]);
}
let cmd = JdwpCommandPacket::get_fields(rand::random(), object_id, &fields)?;
self.send_cmd(JdwpPacket::CommandPacket(cmd))?;
let reply = self
.wait_for_package()
.await
.ok_or_else(|| anyhow::Error::msg("No answer"))?;
let JdwpPacket::ReplyPacket(reply) = reply else {
bail!("Wrong packet");
};
let mut reader = Cursor::new(reply.get_data());
let num_of_fields = reader.read_u32::<BigEndian>()?;
if reply.is_error() {
bail!("We got error code: {}", reply.get_error());
}
let mut fields = fields;
for i in 0..num_of_fields {
let slot_value = SlotValue::from_bytes(&mut reader).context("SlotValue failed")?;
fields[i as usize].value = Some(slot_value);
}
Ok(fields)
}

async fn get_object_signature(&mut self, reference_type: u64) -> anyhow::Result<String> {
let cmd = JdwpCommandPacket::get_object_signature(rand::random(), reference_type)?;
self.send_cmd(JdwpPacket::CommandPacket(cmd))?;
if let Some(JdwpPacket::ReplyPacket(reply)) = self.rx.recv().await {
log::debug!("{:?}", reply);
let mut data_cursor = Cursor::new(reply.get_data());
return String::from_bytes(&mut data_cursor);
}
bail!("Something went wrong");
}
}

impl JdwpClient {
Expand Down Expand Up @@ -409,21 +474,23 @@ impl JdwpClient {
stack_frame.set_value(self, runtime, slot_index, value)?;
Ok(())
}
pub fn get_object_signature(

pub fn get_object(
&mut self,
runtime: &Runtime,
object_ref: u64,
) -> anyhow::Result<String> {
) -> anyhow::Result<ClassInstance> {
runtime.block_on(async {
let ref_type = self.get_reference_type(object_ref).await?;
let cmd = JdwpCommandPacket::get_object_signature(rand::random(), ref_type)?;
self.send_cmd(JdwpPacket::CommandPacket(cmd))?;
if let Some(JdwpPacket::ReplyPacket(reply)) = self.rx.recv().await {
log::debug!("{:?}", reply);
let mut data_cursor = Cursor::new(reply.get_data());
return String::from_bytes(&mut data_cursor);
}
Ok(String::new())
let signature = self.get_object_signature(ref_type).await?;
let fields = self.get_field_ids_for_reference_type(ref_type).await?;
let fields = self.get_fields(object_ref, fields).await?;
Ok(ClassInstance {
object_id: object_ref,
reference_type: ref_type,
signature,
fields,
})
})
}
pub fn send_cmd(&mut self, cmd: JdwpPacket) -> anyhow::Result<()> {
Expand Down Expand Up @@ -665,6 +732,35 @@ impl JdwpCommandPacket {
data,
})
}
pub fn get_fields_for_reference_type(id: u32, reference_id: u64) -> anyhow::Result<Self> {
let mut data = vec![];
data.write_u64::<BigEndian>(reference_id)?;

Ok(Self {
length: 11 + data.len() as u32,
id,
flags: 0,
command_set: 2,
command: 4,
data,
})
}
pub fn get_fields(id: u32, object_id: u64, fields: &[Field]) -> anyhow::Result<Self> {
let mut data = vec![];
data.write_u64::<BigEndian>(object_id)?;
data.write_u32::<BigEndian>(fields.len() as u32)?;
for f in fields {
data.write_u64::<BigEndian>(f.field_id)?;
}
Ok(Self {
length: 11 + data.len() as u32,
id,
flags: 0,
command_set: 9,
command: 2,
data,
})
}

pub fn set_breakpoint(id: u32, location: &Location) -> anyhow::Result<Self> {
let mut data = vec![];
Expand Down
Loading

0 comments on commit d843b6d

Please sign in to comment.