@Test
	public void testRefreshStack() throws Exception {
		try (JshellAndConnection conn = startAndConnectJshell()) {
			start(conn, "HelloWorld.class");

			RemoteMethod brkEnter = conn.getMethod("break_enter_container");
			RemoteMethod setClsFilt = conn.getMethod("set_class_filter");
			RemoteMethod resume = conn.getMethod("resume_thread");
			RemoteMethod stepInto = conn.getMethod("step_into");
			RemoteMethod refreshStack = conn.getMethod("refresh_stack");
			try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
				tb = new ToyDBTraceBuilder((Trace) mdo.get());
				txPut(conn, "Threads");
				TraceObject events = waitForObject("VMs[].Events");
				TraceObject thread = waitForObject("VMs[].Threads[main]");

				brkEnter.invoke(Map.of("container", events));
				TraceObject evtEnter = waitForObject("VMs[].Events[]");
				setClsFilt.invoke(Map.of(
					"event", evtEnter,
					"filter", "HelloWorld*",
					"exclude", false));

				resume.invoke(Map.of("thread", thread));
				waitTxDone();

				TraceObject stack = waitForObject("VMs[].Threads[main].Stack");
				refreshStack.invoke(Map.of("stack", stack));
				waitTxDone();
				waitForValuesPass("VMs[].Threads[main].Stack[]",
					frames -> assertEquals(1, frames.size()));

				// Because main is static, there is no preceding call to <init>
				// This class has no <clinit>, so we get main first!
				waitForLocation("HelloWorld", "main", 0);
				Address start = getPC();

				do2Steps(conn, stepInto, thread);
				stepInto.invoke(Map.of("thread", thread));
				waitTxDone();

				refreshStack.invoke(Map.of("stack", stack));
				waitTxDone();
				waitForValuesPass("VMs[].Threads[main].Stack[]", frames -> {
					assertEquals(2, frames.size());
				});
				waitForValuesPass("VMs[].Threads[main].Stack[].PC", pcs -> {
					assertEquals(2, pcs.size());
					assertEquals(start.add(5), pcs.get(1).getValue());
				});
			}
		}
	}
Because we control the target artifact, we know the steps precisely. NOTE: This must be the
same at least for step_into and step_over, otherwise, we can't know for sure step_over
actually behaves any differently than step_into. By matching with step_into, we can assure
ourselves that step_over actually encountered an invoke* bytecode instruction, because if it
didn't then step_into ought to fail.
Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaMethodsTest.java