GP-0: from review

This commit is contained in:
d-millar 2025-01-10 20:50:09 +00:00
parent 4fd092bb73
commit fe71611803
6 changed files with 119 additions and 119 deletions

View file

@ -973,106 +973,111 @@ def ghidra_trace_put_environment():
put_environment()
def put_regions():
nproc = util.selected_process()
if nproc is None:
return
# Detect whether this is supported before defining the command
if hasattr(drgn, 'RelocatableModule'):
def put_regions():
nproc = util.selected_process()
if nproc is None:
return
try:
regions = prog.loaded_modules()
except Exception as e:
regions = []
#if len(regions) == 0:
# regions = util.full_mem()
try:
regions = prog.loaded_modules()
except Exception as e:
regions = []
#if len(regions) == 0:
# regions = util.full_mem()
mapper = STATE.trace.memory_mapper
keys = []
# r : MEMORY_BASIC_INFORMATION64
for r in regions:
start = r[0].address_range[0]
end = r[0].address_range[1]
size = end - start + 1
rpath = REGION_PATTERN.format(procnum=nproc, start=start)
keys.append(REGION_KEY_PATTERN.format(start=start))
regobj = STATE.trace.create_object(rpath)
(start_base, start_addr) = map_address(start)
regobj.set_value('Range', start_addr.extend(size))
regobj.set_value('Name', r[0].name)
regobj.set_value('Object File', r[0].loaded_file_path)
regobj.set_value('_readable', True)
regobj.set_value('_writable', True)
regobj.set_value('_executable', True)
regobj.set_value('_display', '{:x} {}'.format(start, r[0].name))
regobj.insert()
STATE.trace.proxy_object_path(
MEMORY_PATTERN.format(procnum=nproc)).retain_values(keys)
mapper = STATE.trace.memory_mapper
keys = []
# r : MEMORY_BASIC_INFORMATION64
for r in regions:
start = r[0].address_range[0]
end = r[0].address_range[1]
size = end - start + 1
rpath = REGION_PATTERN.format(procnum=nproc, start=start)
keys.append(REGION_KEY_PATTERN.format(start=start))
regobj = STATE.trace.create_object(rpath)
(start_base, start_addr) = map_address(start)
regobj.set_value('Range', start_addr.extend(size))
regobj.set_value('Name', r[0].name)
regobj.set_value('Object File', r[0].loaded_file_path)
regobj.set_value('_readable', True)
regobj.set_value('_writable', True)
regobj.set_value('_executable', True)
regobj.set_value('_display', '{:x} {}'.format(start, r[0].name))
regobj.insert()
STATE.trace.proxy_object_path(
MEMORY_PATTERN.format(procnum=nproc)).retain_values(keys)
def ghidra_trace_put_regions():
"""
Read the memory map, if applicable, and write to the trace's Regions
"""
def ghidra_trace_put_regions():
"""
Read the memory map, if applicable, and write to the trace's Regions
"""
STATE.require_tx()
with STATE.client.batch() as b:
put_regions()
STATE.require_tx()
with STATE.client.batch() as b:
put_regions()
def put_modules():
nproc = util.selected_process()
if nproc is None:
return
# Detect whether this is supported before defining the command
if hasattr(drgn, 'RelocatableModule'):
def put_modules():
nproc = util.selected_process()
if nproc is None:
return
try:
modules = prog.modules()
except Exception as e:
return
try:
modules = prog.modules()
except Exception as e:
return
mapper = STATE.trace.memory_mapper
mod_keys = []
for m in modules:
name = m.name
# m[1] : _DEBUG_MODULE_PARAMETERS
base = m.address_range[0]
hbase = hex(base)
size = m.address_range[1] - base
mpath = MODULE_PATTERN.format(procnum=nproc, modpath=hbase)
modobj = STATE.trace.create_object(mpath)
mod_keys.append(MODULE_KEY_PATTERN.format(modpath=hbase))
base_base, base_addr = mapper.map(nproc, base)
if base_base != base_addr.space:
STATE.trace.create_overlay_space(base_base, base_addr.space)
modobj.set_value('Range', base_addr.extend(size))
modobj.set_value('Name', name)
modobj.set_value('_display', '{:x} {}'.format(base, name))
modobj.insert()
attrobj = STATE.trace.create_object(mpath+".Attributes")
attrobj.set_value('BuildId', m.build_id)
attrobj.set_value('DebugBias', m.debug_file_bias)
attrobj.set_value('DebugPath', m.debug_file_path)
attrobj.set_value('DebugStatus', str(m.debug_file_status))
attrobj.set_value('LoadBias', m.loaded_file_bias)
attrobj.set_value('LoadPath', m.loaded_file_path)
attrobj.set_value('LoadStatus', str(m.loaded_file_status))
attrobj.insert()
if type(m) == drgn.RelocatableModule:
secobj = STATE.trace.create_object(mpath+".Sections")
secobj.insert()
STATE.trace.proxy_object_path(MODULES_PATTERN.format(
procnum=nproc)).retain_values(mod_keys)
mapper = STATE.trace.memory_mapper
mod_keys = []
for m in modules:
name = m.name
# m[1] : _DEBUG_MODULE_PARAMETERS
base = m.address_range[0]
hbase = hex(base)
size = m.address_range[1] - base
mpath = MODULE_PATTERN.format(procnum=nproc, modpath=hbase)
modobj = STATE.trace.create_object(mpath)
mod_keys.append(MODULE_KEY_PATTERN.format(modpath=hbase))
base_base, base_addr = mapper.map(nproc, base)
if base_base != base_addr.space:
STATE.trace.create_overlay_space(base_base, base_addr.space)
modobj.set_value('Range', base_addr.extend(size))
modobj.set_value('Name', name)
modobj.set_value('_display', '{:x} {}'.format(base, name))
modobj.insert()
attrobj = STATE.trace.create_object(mpath+".Attributes")
attrobj.set_value('BuildId', m.build_id)
attrobj.set_value('DebugBias', m.debug_file_bias)
attrobj.set_value('DebugPath', m.debug_file_path)
attrobj.set_value('DebugStatus', str(m.debug_file_status))
attrobj.set_value('LoadBias', m.loaded_file_bias)
attrobj.set_value('LoadPath', m.loaded_file_path)
attrobj.set_value('LoadStatus', str(m.loaded_file_status))
attrobj.insert()
if type(m) == drgn.RelocatableModule:
secobj = STATE.trace.create_object(mpath+".Sections")
secobj.insert()
STATE.trace.proxy_object_path(MODULES_PATTERN.format(
procnum=nproc)).retain_values(mod_keys)
def ghidra_trace_put_modules():
"""
Gather object files, if applicable, and write to the trace's Modules
"""
def ghidra_trace_put_modules():
"""
Gather object files, if applicable, and write to the trace's Modules
"""
STATE.require_tx()
with STATE.client.batch() as b:
put_modules()
STATE.require_tx()
with STATE.client.batch() as b:
put_modules()
# Detect whether this is supported before defining the command
if hasattr(drgn, 'RelocatableModule'):
def put_sections(m : drgn.RelocatableModule):
nproc = util.selected_process()

View file

@ -263,20 +263,22 @@ def refresh_locals(node: sch.Schema('LocalsContainer')):
commands.ghidra_trace_put_locals()
@REGISTRY.method(action='refresh', display='Refresh Memory')
def refresh_mappings(node: sch.Schema('Memory')):
"""Refresh the list of memory regions for the process."""
with commands.open_tracked_tx('Refresh Memory Regions'):
commands.ghidra_trace_put_regions()
if hasattr(drgn, 'RelocatableModule'):
@REGISTRY.method(action='refresh', display='Refresh Memory')
def refresh_mappings(node: sch.Schema('Memory')):
"""Refresh the list of memory regions for the process."""
with commands.open_tracked_tx('Refresh Memory Regions'):
commands.ghidra_trace_put_regions()
@REGISTRY.method(action='refresh', display='Refresh Modules')
def refresh_modules(node: sch.Schema('ModuleContainer')):
"""
Refresh the modules list for the process.
"""
with commands.open_tracked_tx('Refresh Modules'):
commands.ghidra_trace_put_modules()
if hasattr(drgn, 'RelocatableModule'):
@REGISTRY.method(action='refresh', display='Refresh Modules')
def refresh_modules(node: sch.Schema('ModuleContainer')):
"""
Refresh the modules list for the process.
"""
with commands.open_tracked_tx('Refresh Modules'):
commands.ghidra_trace_put_modules()
@REGISTRY.method(action='activate')

View file

@ -230,10 +230,6 @@ public abstract class AbstractDrgnTraceRmiTest extends AbstractGhidraHeadedDebug
protected record PythonAndConnection(ExecInDrgn exec, TraceRmiConnection connection)
implements AutoCloseable {
protected boolean hasMethod(String name) {
return connection.getMethods().get(name) != null;
}
protected RemoteMethod getMethod(String name) {
return Objects.requireNonNull(connection.getMethods().get(name));
}

View file

@ -17,6 +17,7 @@ package agent.drgn.rmi;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import java.util.*;
@ -200,9 +201,7 @@ public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest {
txCreate(conn, path);
String out = conn.executeCapture("print(hasattr(drgn, 'RelocatableModule'))").strip();
if (out.equals("False")) {
return;
}
assumeFalse(out.equals("False"));
RemoteMethod refreshMappings = conn.getMethod("refresh_mappings");
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
@ -227,9 +226,7 @@ public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest {
txCreate(conn, path);
String out = conn.executeCapture("print(hasattr(drgn, 'RelocatableModule'))").strip();
if (out.equals("False")) {
return;
}
assumeFalse(out.equals("False"));
RemoteMethod refreshModules = conn.getMethod("refresh_modules");
try (ManagedDomainObject mdo = openDomainObject(MDO)) {

View file

@ -132,7 +132,7 @@
<h3>Unit tests</h3>
<p>The hardest part of writing unit tests is almost always getting the first test to run, and the easiest unit tests, as with the Python files, are those for <code>commands.py</code>. For <strong>drgn</strong>, as before, were using <strong>dbgeng</strong> as the pattern, but several elements had to be changed. Because the launchers execute a script, we need to amend the <code>runThrowError</code> logic (and, more specifically, the <code>execInPython</code> logic) in <a href="../../../Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/AbstractDrgnTraceRmiTest.java"><code>AbstractDrgnTraceRmiTest</code></a> with a <code>ProcessBuilder</code> call that takes a script, rather than writing the script to stdin. While there, we can also trim out the unnecessary helper logic around items like breakpoints, watchpoints, etc. from all of the test classes.</p>
<p>JUnits for <code>methods.py</code> follow a similar pattern, but, again, getting the first one to run is often the most difficult. For <strong>drgn</strong>, weve had to override the timeouts in <code>waitForPass</code> and <code>waitForCondition</code>. After starting with hardcoded paths for the test target, we also had to add logic to re-write the <code>PREAMBLE</code> on-the-fly in <code>execInDrgn</code>. Obviously, with no real <code>hooks.py</code> logic, theres no need for <code>DrgnHooksTest</code>.</p>
<p>Of note, weve used the gdb <code>gcore</code> command to create a core dump for the tests. Both user- and kernel-mode require privileges to run the debugger, and, for testing, thats not ideal. <a href="../../../Ghidra/Test/DebuggerIntegrationTest/build.gradle"><code>build.gradle</code></a> will also need to be modified to include the new debugger package.</p>
<p>Of note, weve used the gdb <code>gcore</code> command to create a core dump for the tests. Both user- and kernel-mode require privileges to run the debugger, and, for testing, thats not ideal. <a href="../../../Ghidra/Test/DebuggerIntegrationTest/build.gradle"><code>build.gradle</code></a> for IntegrationTest projext will also need to be modified to include the new debugger package.</p>
</section>
<section id="documentation" class="level3">
<h3>Documentation</h3>

View file

@ -211,7 +211,7 @@ Obviously, with no real `hooks.py` logic, there's no need for `DrgnHooksTest`.
Of note, we've used the gdb `gcore` command to create a core dump for the tests.
Both user- and kernel-mode require privileges to run the debugger, and, for testing, that's not ideal.
[`build.gradle`](../../../Ghidra/Test/DebuggerIntegrationTest/build.gradle) will also need to be modified to include the new debugger package.
[`build.gradle`](../../../Ghidra/Test/DebuggerIntegrationTest/build.gradle) for IntegrationTest projext will also need to be modified to include the new debugger package.
### Documentation