blob: 4f109d47d199af28f6d77ec8b17d03742c3c598b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2015 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.internal.debug.tests;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.eclipse.tcf.core.Command;
import org.eclipse.tcf.internal.debug.model.TCFMemoryRegion;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IErrorReport;
import org.eclipse.tcf.protocol.IToken;
import org.eclipse.tcf.protocol.JSON;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.services.IBreakpoints;
import org.eclipse.tcf.services.IContextQuery;
import org.eclipse.tcf.services.IDiagnostics;
import org.eclipse.tcf.services.IDisassembly;
import org.eclipse.tcf.services.ILineNumbers;
import org.eclipse.tcf.services.IMemory;
import org.eclipse.tcf.services.IMemoryMap;
import org.eclipse.tcf.services.IPathMap;
import org.eclipse.tcf.services.IRegisters;
import org.eclipse.tcf.services.IRunControl;
import org.eclipse.tcf.services.ISymbols;
import org.eclipse.tcf.services.IDiagnostics.ISymbol;
import org.eclipse.tcf.services.IDisassembly.IDisassemblyLine;
import org.eclipse.tcf.services.ILineNumbers.CodeArea;
import org.eclipse.tcf.services.IMemory.MemoryContext;
import org.eclipse.tcf.services.IMemory.MemoryError;
import org.eclipse.tcf.services.IMemoryMap.MemoryRegion;
import org.eclipse.tcf.services.IPathMap.PathMapRule;
import org.eclipse.tcf.services.IRegisters.RegistersContext;
import org.eclipse.tcf.services.IRunControl.RunControlContext;
import org.eclipse.tcf.services.ISymbols.Symbol;
class TestRCBP1 implements ITCFTest, RunControl.DiagnosticTestDone, IRunControl.RunControlListener {
private final TCFTestSuite test_suite;
private final RunControl test_rc;
private final int channel_id;
private final List<PathMapRule> path_map;
private final Map<String,ArrayList<MemoryRegion>> mem_map;
private final IDiagnostics srv_diag;
private final ISymbols srv_syms;
private final IMemory srv_memory;
private final IRunControl srv_run_ctrl;
private final IRegisters srv_registers;
private final IBreakpoints srv_breakpoints;
private final ILineNumbers srv_line_numbers;
private final IDisassembly srv_disassembly;
private final IPathMap srv_path_map;
private final IMemoryMap srv_memory_map;
private final IContextQuery srv_context_query;
private final Map<String,IRunControl.RunControlContext> threads = new HashMap<String,IRunControl.RunControlContext>();
private final Map<String,SuspendedContext> suspended = new HashMap<String,SuspendedContext>();
private final Map<String,SuspendedContext> suspended_prev = new HashMap<String,SuspendedContext>();
private final Map<String,IDisassemblyLine[]> disassembly_lines = new HashMap<String,IDisassemblyLine[]>();
private final Map<String,Map<String,Object>[]> disassembly_capabilities = new HashMap<String,Map<String,Object>[]>();
private final Set<String> running = new HashSet<String>();
private final Set<IToken> get_state_cmds = new HashSet<IToken>();
private final Set<IToken> bp_change_cmds = new HashSet<IToken>();
private final Map<String,Map<String,IRegisters.RegistersContext>> regs =
new HashMap<String,Map<String,IRegisters.RegistersContext>>();
private final Map<String,Map<String,Object>> bp_list = new HashMap<String,Map<String,Object>>();
private final Map<String,IDiagnostics.ISymbol> sym_list = new HashMap<String,IDiagnostics.ISymbol>();
private final Random rnd = new Random();
private String[] test_list;
private String test_id;
private boolean path_map_done;
private boolean mem_map_done;
private String test_ctx_id; // Test context ID
private IRunControl.RunControlContext test_context;
private String main_thread_id;
private Map<String,Object> bp_capabilities;
private Runnable pending_cancel;
private int bp_cnt;
private boolean done_get_state;
private boolean done_disassembly;
private int resume_cnt = 0;
private IToken cancel_test_cmd;
private boolean bp_set_done;
private boolean bp_change_done;
private boolean bp_reset_done;
private boolean bp_sync_done;
private boolean data_bp_area_done;
private ILineNumbers.CodeArea data_bp_area;
private String data_bp_id;
private String temp_bp_id;
private String inv_bp_id;
private boolean temp_bp_removed;
private int temp_bp_cnt;
private int data_bp_cnt;
private boolean mem_map_test_running;
private boolean mem_map_test_done;
private boolean all_setup_done;
private static int mem_map_region_id = 0;
private static int test_cnt;
private static class SuspendedContext {
final String id;
final String pc;
final String reason;
final Map<String,Object> params;
boolean get_state_pending;
boolean ok_to_resume;
SuspendedContext(String id, String pc, String reason, Map<String,Object> params) {
this.id = id;
this.pc = pc;
this.reason = reason;
this.params = params;
}
}
private final IBreakpoints.BreakpointsListener bp_listener = new IBreakpoints.BreakpointsListener() {
@SuppressWarnings("unchecked")
public void breakpointStatusChanged(String id, Map<String,Object> status) {
if (bp_list.get(id) != null && test_context != null && bp_cnt < 40) {
if (bp_change_cmds.size() == 0) {
Map<String,Object> m = bp_list.get(id);
Collection<String> prop_ids = (Collection<String>)m.get(IBreakpoints.PROP_CONTEXT_IDS);
if (prop_ids != null) {
Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)
status.get(IBreakpoints.STATUS_INSTANCES);
if (list != null) {
for (Map<String,Object> map : list) {
String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT);
if (prop_ids.contains(ctx)) continue;
boolean ok = false;
for (String s : prop_ids) {
IRunControl.RunControlContext x = test_rc.getContext(s);
if (x != null && ctx.equals(x.getBPGroup())) ok = true;
}
if (!ok) {
exit(new Error("Breakpoint " + id + ": invalid planting context " + ctx));
return;
}
}
}
}
}
String prs = test_context.getProcessID();
if (prs != null) {
for (ITCFTest test : test_suite.getActiveTests()) {
if (test instanceof TestRCBP1) {
TestRCBP1 rcbp = (TestRCBP1)test;
if (!rcbp.mem_map_test_running) continue;
if (prs.equals(rcbp.test_context.getProcessID())) return;
}
}
}
String err = (String)status.get(IBreakpoints.STATUS_ERROR);
if (err == null) {
Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)
status.get(IBreakpoints.STATUS_INSTANCES);
if (list != null) {
for (Map<String,Object> map : list) {
String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT);
if (test_context.getProcessID().equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null)
err = (String)map.get(IBreakpoints.INSTANCE_ERROR);
}
}
}
if (err != null) {
if (bp_cnt == 0 && id.equals(data_bp_id)) return;
if (id.equals(inv_bp_id)) return;
exit(new Exception("Invalid BP status: " + err));
}
}
}
public void contextAdded(Map<String,Object>[] bps) {
for (Map<String,Object> m0 : bps) {
String id = (String)m0.get(IBreakpoints.PROP_ID);
Map<String,Object> m1 = bp_list.get(id);
if (!checkBPData(m0, m1)) return;
}
}
public void contextChanged(Map<String,Object>[] bps) {
if (bp_change_cmds.size() > 1) return;
for (Map<String,Object> m0 : bps) {
String id = (String)m0.get(IBreakpoints.PROP_ID);
Map<String,Object> m1 = bp_list.get(id);
if (!checkBPData(m0, m1)) return;
}
}
public void contextRemoved(String[] ids) {
if (!bp_change_done) return;
for (String id : ids) {
if (bp_list.get(id) != null) {
if (id.equals(temp_bp_id)) {
if (bp_cnt != 3 || temp_bp_removed) {
exit(new Exception("Invalid Breakpoints.contextRemoved event for temporary breakpoint"));
return;
}
temp_bp_removed = true;
}
else {
exit(new Exception("Invalid Breakpoints.contextRemoved event"));
return;
}
}
}
}
private boolean checkBPData(Map<String,Object> m0, Map<String,Object> m1) {
if (m1 == null) return true;
m0 = new HashMap<String,Object>(m0);
if (m0.get(IBreakpoints.PROP_ENABLED) == null) m0.put(IBreakpoints.PROP_ENABLED, false);
if (m1.get(IBreakpoints.PROP_ENABLED) == null) m1.put(IBreakpoints.PROP_ENABLED, false);
if (!m1.equals(m0)) {
exit(new Exception("Invalid data in Breakpoints event: " + m0 + " != " + m1));
return false;
}
return true;
}
};
TestRCBP1(TCFTestSuite test_suite, RunControl test_rc, IChannel channel, int channel_id,
List<PathMapRule> path_map, Map<String,ArrayList<MemoryRegion>> mem_map) {
this.test_suite = test_suite;
this.test_rc = test_rc;
this.channel_id = channel_id;
this.path_map = path_map;
this.mem_map = mem_map;
srv_diag = channel.getRemoteService(IDiagnostics.class);
srv_syms = channel.getRemoteService(ISymbols.class);
srv_memory = channel.getRemoteService(IMemory.class);
srv_run_ctrl = channel.getRemoteService(IRunControl.class);
srv_registers = channel.getRemoteService(IRegisters.class);
srv_breakpoints = channel.getRemoteService(IBreakpoints.class);
srv_line_numbers = channel.getRemoteService(ILineNumbers.class);
srv_disassembly = channel.getRemoteService(IDisassembly.class);
srv_path_map = channel.getRemoteService(IPathMap.class);
srv_memory_map = channel.getRemoteService(IMemoryMap.class);
srv_context_query = channel.getRemoteService(IContextQuery.class);
}
public void start() {
if (srv_run_ctrl == null) {
test_suite.done(this, null);
}
else {
if (srv_breakpoints != null) srv_breakpoints.addListener(bp_listener);
runTest();
}
}
private void runTest() {
if (!test_suite.isActive(this)) return;
if (!path_map_done) {
setPathMap();
return;
}
if (!mem_map_done) {
setMemMap();
return;
}
if (test_list == null) {
getTestList();
return;
}
if (test_id != null) {
if (test_ctx_id == null) {
startTestContext();
return;
}
if (test_context == null) {
getTestContext();
return;
}
if (sym_list.isEmpty()) {
getSymbols();
return;
}
if (!data_bp_area_done) {
getDataBPFile();
return;
}
if (bp_capabilities == null) {
getBreakpointCapabilities();
return;
}
if (!bp_set_done) {
iniBreakpoints();
return;
}
}
if (!done_get_state) {
assert get_state_cmds.isEmpty();
assert threads.isEmpty();
assert running.isEmpty();
assert suspended.isEmpty();
getContextState(test_ctx_id);
return;
}
if (srv_disassembly != null && !done_disassembly) {
assert get_state_cmds.isEmpty();
assert disassembly_lines.isEmpty();
getDisassemlyLines();
return;
}
if (test_id != null) {
if (!bp_change_done) {
changeBreakpoint5();
return;
}
if (!mem_map_test_done) {
runMemoryMapTest();
return;
}
assert resume_cnt == 0;
assert !all_setup_done;
all_setup_done = true;
for (SuspendedContext s : suspended.values()) resume(s.id);
return;
}
if (suspended.size() > 0) {
final int test_cnt = suspended.size();
Runnable done = new Runnable() {
int done_cnt;
public void run() {
done_cnt++;
if (done_cnt == test_cnt) {
exit(null);
}
}
};
for (SuspendedContext sc : suspended.values()) runRegistersTest(sc, done);
return;
}
if (!bp_reset_done) {
resetBreakpoints();
return;
}
exit(null);
}
private void getTestList() {
if (srv_diag == null) {
test_list = new String[0];
runTest();
return;
}
srv_diag.getTestList(new IDiagnostics.DoneGetTestList() {
public void doneGetTestList(IToken token, Throwable error, String[] list) {
if (error != null) {
exit(error);
}
else {
if (list == null) list = new String[0];
if (list.length > 0) test_id = list[rnd.nextInt(list.length)];
test_list = list;
runTest();
}
}
});
}
private void setPathMap() {
if (srv_path_map == null || path_map == null) {
path_map_done = true;
runTest();
return;
}
srv_path_map.set(path_map.toArray(new PathMapRule[path_map.size()]), new IPathMap.DoneSet() {
public void doneSet(IToken token, Exception error) {
if (error != null) {
exit(error);
}
else {
path_map_done = true;
runTest();
}
}
});
}
private void setMemMap() {
if (mem_map == null || mem_map.size() == 0) {
mem_map_done = true;
runTest();
return;
}
final Set<IToken> cmds = new HashSet<IToken>();
for (String id : mem_map.keySet()) {
ArrayList<MemoryRegion> l = mem_map.get(id);
cmds.add(srv_memory_map.set(id, l.toArray(new MemoryRegion[l.size()]), new IMemoryMap.DoneSet() {
public void doneSet(IToken token, Exception error) {
cmds.remove(token);
if (error instanceof IErrorReport) {
IErrorReport e = (IErrorReport)error;
if (e.getErrorCode() == IErrorReport.TCF_ERROR_INV_CONTEXT) error = null;
}
if (error != null) {
exit(error);
}
else if (cmds.size() == 0) {
mem_map_done = true;
runTest();
}
}
}));
}
assert cmds.size() > 0;
}
private void resetBreakpoints() {
if (srv_breakpoints == null) {
bp_reset_done = true;
runTest();
return;
}
srv_breakpoints.set(null, new IBreakpoints.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
if (error != null) {
exit(error);
return;
}
bp_reset_done = true;
runTest();
}
});
}
private void getBreakpointCapabilities() {
if (srv_breakpoints == null) {
bp_capabilities = new HashMap<String,Object>();
runTest();
return;
}
srv_breakpoints.getCapabilities(test_ctx_id, new IBreakpoints.DoneGetCapabilities() {
public void doneGetCapabilities(IToken token, Exception error, Map<String,Object> capabilities) {
if (!test_suite.isActive(TestRCBP1.this)) return;
if (error != null) {
exit(error);
return;
}
Boolean l = (Boolean)capabilities.get(IBreakpoints.CAPABILITY_LOCATION);
Boolean c = (Boolean)capabilities.get(IBreakpoints.CAPABILITY_CONDITION);
if (l == null || !l) {
exit(new Exception("Breakpoints service does not support \"Location\" attribute"));
return;
}
if (c == null || !c) {
exit(new Exception("Breakpoints service does not support \"Condition\" attribute"));
return;
}
bp_capabilities = capabilities;
runTest();
}
});
}
private void startTestContext() {
srv_diag.runTest(test_id, new IDiagnostics.DoneRunTest() {
public void doneRunTest(IToken token, Throwable error, final String context_id) {
if (error != null) {
exit(error);
}
else {
// sync() is needed to resolve ambiguity if target re-uses test context IDs
Protocol.sync(new Runnable() {
@Override
public void run() {
if (!test_suite.isActive(TestRCBP1.this)) return;
assert test_ctx_id == null;
test_ctx_id = context_id;
if (pending_cancel != null) {
exit(null);
}
else {
runTest();
}
}
});
}
}
});
}
private void getTestContext() {
srv_run_ctrl.getContext(test_ctx_id, new IRunControl.DoneGetContext() {
public void doneGetContext(IToken token, Exception error, RunControlContext context) {
if (test_suite.cancel) return;
if (error != null) {
exit(error);
return;
}
test_context = context;
assert test_ctx_id.equals(context.getID());
srv_run_ctrl.addListener(TestRCBP1.this);
runTest();
}
});
}
private void getSymbols() {
final HashMap<IToken,String> cmds = new HashMap<IToken,String>();
IDiagnostics.DoneGetSymbol done = new IDiagnostics.DoneGetSymbol() {
public void doneGetSymbol(IToken token, Throwable error, ISymbol symbol) {
String name = cmds.remove(token);
if (error != null) {
exit(error);
return;
}
if (!test_suite.isActive(TestRCBP1.this)) return;
assert test_ctx_id != null;
if (symbol == null) {
exit(new Exception("Symbol must not be NULL: " + name));
}
else if (!symbol.isAbs()) {
exit(new Exception("Symbol must be absolute: " + name));
}
else if (symbol.getValue() == null || symbol.getValue().longValue() == 0) {
exit(new Exception("Symbol value must not be NULL: " + name));
}
else {
sym_list.put(name, symbol);
if (cmds.isEmpty()) runTest();
}
}
};
String[] syms = {
"tcf_test_func0",
"tcf_test_func1",
"tcf_test_func2",
"tcf_test_func3",
"tcf_test_array"
};
String prs = test_context.getProcessID();
for (String name : syms) cmds.put(srv_diag.getSymbol(prs, name, done), name);
}
private void getDataBPFile() {
ISymbol sym = sym_list.get("tcf_test_func3");
if (sym == null || srv_line_numbers == null) {
data_bp_area_done = true;
runTest();
return;
}
srv_line_numbers.mapToSource(test_ctx_id, sym.getValue(), sym.getValue().longValue() + 1, new ILineNumbers.DoneMapToSource() {
public void doneMapToSource(IToken token, Exception error, CodeArea[] areas) {
if (error != null) {
exit(error);
}
else {
if (areas != null && areas.length > 0) data_bp_area = areas[0];
data_bp_area_done = true;
runTest();
}
}
});
}
@SuppressWarnings("unchecked")
private void iniBreakpoints() {
assert !bp_set_done;
assert bp_list.isEmpty();
Boolean bp_type_ok = (Boolean)bp_capabilities.get(IBreakpoints.CAPABILITY_BREAKPOINT_TYPE);
Map<String,Object> m[] = new Map[10];
for (int i = 0; i < m.length; i++) {
m[i] = new HashMap<String,Object>();
m[i].put(IBreakpoints.PROP_ID, "TcfTestBP" + i + "" + channel_id);
m[i].put(IBreakpoints.PROP_ENABLED, true);
switch (i) {
case 0:
m[i].put(IBreakpoints.PROP_LOCATION, sym_list.get("tcf_test_func0").getValue().toString());
// Condition is always true
m[i].put(IBreakpoints.PROP_CONDITION, "$thread!=\"\"");
break;
case 1:
m[i].put(IBreakpoints.PROP_LOCATION, sym_list.get("tcf_test_func0").getValue().toString());
// Condition is always false
m[i].put(IBreakpoints.PROP_CONDITION, "$thread==\"\"");
break;
case 2:
// Second breakpoint at same address
m[i].put(IBreakpoints.PROP_LOCATION, "tcf_test_func0");
break;
case 3:
// Location is an expression
m[i].put(IBreakpoints.PROP_LOCATION, "(31+1)/16+tcf_test_func1-2");
// Condition is always true
m[i].put(IBreakpoints.PROP_CONDITION, "tcf_test_func0!=tcf_test_func1");
if (bp_type_ok != null && bp_type_ok.booleanValue()) {
switch (rnd.nextInt(4)) {
case 0: m[i].put(IBreakpoints.PROP_TYPE, IBreakpoints.TYPE_SOFTWARE); break;
case 1: m[i].put(IBreakpoints.PROP_TYPE, IBreakpoints.TYPE_AUTO); break;
}
}
break;
case 4:
// Disabled breakpoint
m[i].put(IBreakpoints.PROP_LOCATION, "tcf_test_func2");
m[i].put(IBreakpoints.PROP_ENABLED, false);
break;
case 5:
// Breakpoint that will be enabled with "enable" command
m[i].put(IBreakpoints.PROP_LOCATION, "tcf_test_func2");
m[i].put(IBreakpoints.PROP_ENABLED, false);
break;
case 6:
m[i].put(IBreakpoints.PROP_LOCATION, "tcf_test_func3");
if (bp_type_ok != null && bp_type_ok.booleanValue()) {
switch (rnd.nextInt(4)) {
case 0: m[i].put(IBreakpoints.PROP_TYPE, IBreakpoints.TYPE_SOFTWARE); break;
case 1: m[i].put(IBreakpoints.PROP_TYPE, IBreakpoints.TYPE_AUTO); break;
}
}
break;
case 7:
// Data breakpoint
m[i].put(IBreakpoints.PROP_LOCATION, "&tcf_test_char");
m[i].put(IBreakpoints.PROP_ACCESS_MODE, IBreakpoints.ACCESSMODE_WRITE);
Number ca = (Number)bp_capabilities.get(IBreakpoints.CAPABILITY_ACCESS_MODE);
if (data_bp_area != null && ca != null && (ca.intValue() & IBreakpoints.ACCESSMODE_WRITE) != 0) {
m[i].put(IBreakpoints.PROP_FILE, data_bp_area.file);
m[i].put(IBreakpoints.PROP_LINE, data_bp_area.start_line);
data_bp_id = (String)m[i].get(IBreakpoints.PROP_ID);
}
else {
m[i].put(IBreakpoints.PROP_ENABLED, false);
}
break;
case 8:
// Temporary breakpoint
// It will be enabled when we get main thread ID
m[i].put(IBreakpoints.PROP_LOCATION, "tcf_test_func3");
m[i].put(IBreakpoints.PROP_ENABLED, false);
Boolean ct = (Boolean)bp_capabilities.get(IBreakpoints.CAPABILITY_TEMPORARY);
if (ct != null && ct.booleanValue()) {
m[i].put(IBreakpoints.PROP_TEMPORARY, true);
temp_bp_id = (String)m[i].get(IBreakpoints.PROP_ID);
}
break;
case 9:
// Invalid breakpoint that should not be planted
m[i].put("Unknown property", "Unknown value");
inv_bp_id = (String)m[i].get(IBreakpoints.PROP_ID);
break;
}
bp_list.put((String)m[i].get(IBreakpoints.PROP_ID), m[i]);
}
srv_breakpoints.set(m, new IBreakpoints.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
assert !bp_set_done;
bp_set_done = true;
if (error != null) {
exit(error);
return;
}
runTest();
}
});
}
private void getContextState(final String id) {
get_state_cmds.add(srv_run_ctrl.getChildren(id, new IRunControl.DoneGetChildren() {
public void doneGetChildren(IToken token, Exception error, String[] contexts) {
get_state_cmds.remove(token);
if (test_suite.cancel) return;
if (error != null) {
exit(error);
return;
}
for (String s : contexts) getContextState(s);
if (get_state_cmds.isEmpty()) doneContextState();
}
}));
if (id == null) return;
get_state_cmds.add(srv_run_ctrl.getContext(id, new IRunControl.DoneGetContext() {
public void doneGetContext(IToken token, Exception error, RunControlContext ctx) {
get_state_cmds.remove(token);
if (test_suite.cancel) return;
if (error != null) {
exit(error);
return;
}
if (test_id != null) {
assert test_ctx_id != null;
assert isMyContext(ctx);
for (ITCFTest t : test_suite.getActiveTests()) {
if (t != TestRCBP1.this && t instanceof TestRCBP1 && ((TestRCBP1)t).threads.get(id) != null) {
exit(new Exception("Invalid or missing 'CreatorID' context attribute.\nContext: " + ctx));
return;
}
}
}
if (ctx.hasState()) {
threads.put(id, ctx);
get_state_cmds.add(ctx.getState(new IRunControl.DoneGetState() {
public void doneGetState(IToken token, Exception error,
boolean susp, String pc, String reason,
Map<String, Object> params) {
get_state_cmds.remove(token);
if (test_suite.cancel) return;
if (error != null) {
exit(new Exception("Cannot get context state", error));
return;
}
if (!susp) {
if (suspended.get(id) != null) {
exit(new Exception("Invalid result of getState command"));
return;
}
running.add(id);
}
else {
assert threads.get(id) != null;
if (running.contains(id)) {
exit(new Exception("Invalid result of getState command"));
return;
}
SuspendedContext sc = suspended.get(id);
if (sc != null && sc.pc != null && !sc.pc.equals(pc)) {
exit(new Exception("Invalid result of getState command: invalid PC. Context: " + id));
return;
}
if (sc != null && sc.reason != null && !sc.reason.equals(reason)) {
exit(new Exception("Invalid result of getState command: invalid suspend reason. Context: " + id));
return;
}
if (test_id != null && "Breakpoint".equals(reason)) {
exit(new Exception("Invalid suspend reason of main thread " +
id + " after test start: " + reason + " " + pc));
return;
}
assert !done_get_state;
suspended.put(id, new SuspendedContext(id, pc, reason, params));
}
if (get_state_cmds.isEmpty()) doneContextState();
}
}));
}
if (get_state_cmds.isEmpty()) doneContextState();
}
}));
}
private void doneContextState() {
assert !done_get_state;
assert get_state_cmds.isEmpty();
assert resume_cnt == 0;
assert threads.size() == suspended.size() + running.size();
done_get_state = true;
runTest();
}
private void getDisassemlyLines() {
for (final String id : suspended.keySet()) {
SuspendedContext sc = suspended.get(id);
get_state_cmds.add(srv_disassembly.getCapabilities(id, new IDisassembly.DoneGetCapabilities() {
public void doneGetCapabilities(IToken token, Throwable error, Map<String,Object>[] arr) {
get_state_cmds.remove(token);
if (error != null) {
exit(error);
}
else {
disassembly_capabilities.put(id, arr);
if (get_state_cmds.isEmpty()) doneDisassembly();
}
}
}));
if (sc.pc == null) {
disassembly_lines.put(id, new IDisassemblyLine[0]);
continue;
}
BigInteger pc = new BigInteger(sc.pc);
get_state_cmds.add(srv_disassembly.disassemble(id, pc, 1, null, new IDisassembly.DoneDisassemble() {
public void doneDisassemble(IToken token, Throwable error, IDisassemblyLine[] arr) {
get_state_cmds.remove(token);
if (error != null) {
exit(error);
}
else {
disassembly_lines.put(id, arr);
if (get_state_cmds.isEmpty()) doneDisassembly();
}
}
}));
}
if (get_state_cmds.isEmpty()) doneDisassembly();
}
private void doneDisassembly() {
assert !done_disassembly;
assert get_state_cmds.isEmpty();
if (!test_suite.isActive(TestRCBP1.this)) return;
assert suspended.size() == disassembly_lines.size();
done_disassembly = true;
runTest();
}
private void changeBreakpoint5() {
assert !bp_change_done;
final String bp_id = "TcfTestBP5" + channel_id;
final Map<String,Object> m = bp_list.get(bp_id);
ArrayList<String> l = new ArrayList<String>();
l.add(test_context.getProcessID());
Boolean ci = (Boolean)bp_capabilities.get(IBreakpoints.CAPABILITY_CONTEXT_IDS);
if (ci != null && ci) m.put(IBreakpoints.PROP_CONTEXT_IDS, l);
Boolean sg = (Boolean)bp_capabilities.get(IBreakpoints.CAPABILITY_STOP_GROUP);
if (sg != null && sg) m.put(IBreakpoints.PROP_STOP_GROUP, l);
StringBuffer bf = new StringBuffer();
for (String id : threads.keySet()) {
if (bf.length() > 0) bf.append(" || ");
bf.append("$thread==\"");
bf.append(id);
bf.append('"');
}
m.put(IBreakpoints.PROP_CONDITION, bf.toString());
bp_change_cmds.add(srv_breakpoints.change(m, new IBreakpoints.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
bp_change_cmds.remove(token);
bp_change_done = true;
if (error != null) exit(error);
}
}));
srv_breakpoints.getIDs(new IBreakpoints.DoneGetIDs() {
public void doneGetIDs(IToken token, Exception error, String[] ids) {
if (error != null) {
exit(error);
return;
}
if (!bp_change_done) {
exit(new Exception("Invalid responce order"));
return;
}
HashSet<String> s = new HashSet<String>();
for (String id : ids) s.add(id);
if (ids.length != s.size()) {
exit(new Exception("Invalis BP list: " + Arrays.toString(ids)));
return;
}
for (String id : bp_list.keySet()) {
if (!s.contains(id)) {
exit(new Exception("BP is not listed by Breakpoints.getIDs: " + id));
return;
}
}
}
});
for (final String id : bp_list.keySet()) {
srv_breakpoints.getProperties(id, new IBreakpoints.DoneGetProperties() {
public void doneGetProperties(IToken token, Exception error, Map<String,Object> properties) {
if (error != null) {
exit(error);
return;
}
HashMap<String,Object> m0 = new HashMap<String,Object>(properties);
HashMap<String,Object> m1 = (HashMap<String,Object>)bp_list.get(id);
if (m0.get(IBreakpoints.PROP_ENABLED) == null) m0.put(IBreakpoints.PROP_ENABLED, false);
if (m1.get(IBreakpoints.PROP_ENABLED) == null) m1.put(IBreakpoints.PROP_ENABLED, false);
if (!m1.equals(m0)) {
exit(new Exception("Invalid data returned by Breakpoints.getProperties: " + m0 + " != " + m1));
return;
}
}
});
srv_breakpoints.getStatus(id, new IBreakpoints.DoneGetStatus() {
public void doneGetStatus(IToken token, Exception error, Map<String,Object> status) {
if (error != null) {
exit(error);
return;
}
}
});
}
Protocol.sync(new Runnable() {
public void run() {
if (!test_suite.isActive(TestRCBP1.this)) return;
if (!bp_change_done) {
exit(new Exception("Protocol.sync() test failed"));
return;
}
m.put(IBreakpoints.PROP_ENABLED, true);
srv_breakpoints.enable(new String[]{ bp_id }, new IBreakpoints.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
if (error != null) exit(error);
}
});
bp_sync_done = true;
runTest();
}
});
}
private void changeBreakpoint6() {
assert bp_change_done;
if (threads.size() < 5) return;
final String bp_id = "TcfTestBP6" + channel_id;
final Map<String,Object> m = bp_list.get(bp_id);
ArrayList<String> l = new ArrayList<String>();
for (String id : threads.keySet()) l.add(id);
m.remove(IBreakpoints.PROP_CONTEXT_IDS);
m.remove(IBreakpoints.PROP_STOP_GROUP);
m.remove(IBreakpoints.PROP_CONTEXT_QUERY);
m.remove(IBreakpoints.PROP_CONDITION);
if (rnd.nextBoolean()) {
Boolean b = (Boolean)bp_capabilities.get(IBreakpoints.CAPABILITY_CONTEXT_IDS);
if (b != null && b) m.put(IBreakpoints.PROP_CONTEXT_IDS, l);
}
if (rnd.nextBoolean()) {
Boolean b = (Boolean)bp_capabilities.get(IBreakpoints.CAPABILITY_STOP_GROUP);
if (b != null && b) m.put(IBreakpoints.PROP_STOP_GROUP, l);
}
if (rnd.nextBoolean()) {
Boolean b = (Boolean)bp_capabilities.get(IBreakpoints.CAPABILITY_CONTEXT_QUERY);
if (b != null && b) {
String q = "ID=\"" + test_context.getProcessID() + "\"/HasState=true";
m.put(IBreakpoints.PROP_CONTEXT_QUERY, q);
}
}
if (rnd.nextBoolean()) {
StringBuffer bf = new StringBuffer();
for (String id : threads.keySet()) {
if (bf.length() > 0) bf.append(" || ");
bf.append("$thread==\"");
bf.append(id);
bf.append('"');
}
m.put(IBreakpoints.PROP_CONDITION, bf.toString());
}
bp_change_cmds.add(srv_breakpoints.change(m, new IBreakpoints.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
bp_change_cmds.remove(token);
if (error != null) exit(error);
}
}));
}
public void containerResumed(String[] context_ids) {
for (String id : context_ids) contextResumed(id);
}
public void containerSuspended(String context, String pc,
String reason, Map<String, Object> params,
String[] suspended_ids) {
for (String id : suspended_ids) {
if (id.equals(context)) continue;
contextSuspended(id, null, null, null);
}
contextSuspended(context, pc, reason, params);
}
public void contextAdded(RunControlContext[] contexts) {
for (RunControlContext ctx : contexts) {
final String id = ctx.getID();
if (threads.get(id) != null) {
exit(new Exception("Invalid contextAdded event:\nContext: " + ctx));
return;
}
if (isMyContext(ctx)) {
for (ITCFTest t : test_suite.getActiveTests()) {
if (t instanceof TestRCBP1 && ((TestRCBP1)t).threads.get(id) != null) {
exit(new Exception("Invalid or missing 'CreatorID' context attribute.\nContext: " + ctx));
return;
}
}
if (threads.size() > 0 && !all_setup_done) {
assert !canResume(id);
exit(new Exception("Unexpected contextAdded event\nContext: " + ctx));
return;
}
if (srv_context_query != null) {
srv_context_query.getAttrNames(new IContextQuery.DoneGetAttrNames() {
public void doneGetAttrNames(IToken token, Exception error, String[] names) {
if (error instanceof IErrorReport &&
((IErrorReport)error).getErrorCode() == IErrorReport.TCF_ERROR_INV_COMMAND) {
// Ignore
}
else if (error != null) {
exit(error);
}
else {
StringBuffer bf = new StringBuffer();
bf.append('"');
int l = id.length();
for (int i = 0; i < l; i++) {
char ch = id.charAt(i);
if (ch == '"' || ch == '\\') bf.append('\\');
bf.append(ch);
}
bf.append('"');
String v = bf.toString();
for (String nm : names) {
if (nm.equals("ID")) {
String s = null;
switch (rnd.nextInt(4)) {
case 0: s = "ID=" + v; break;
case 1: s = "**/ID=" + v; break;
case 2: s = "/**/ID=" + v; break;
case 3: s = "ID=" + v + ",id=" + v; break;
}
srv_context_query.query(s, new IContextQuery.DoneQuery() {
public void doneQuery(IToken token, Exception error, String[] contexts) {
if (error != null) {
exit(error);
}
else if (contexts == null || contexts.length != 1) {
exit(new Exception("Invalid result length of ContextQuery.query command"));
}
else if (!id.equals(contexts[0])) {
exit(new Exception("Invalid ID returned by ContextQuery.query command"));
}
}
});
switch (rnd.nextInt(2)) {
case 0: s = "ID=ID="; break;
case 1: s = "ID=ID=ID"; break;
}
srv_context_query.query(s, new IContextQuery.DoneQuery() {
public void doneQuery(IToken token, Exception error, String[] contexts) {
if (error == null) {
exit(new Exception("Invalid ContextQuery.query responce: error expected"));
}
}
});
return;
}
}
exit(new Exception("ContextQuery.getAttrNames result must include 'ID'"));
}
}
});
}
if (ctx.hasState()) {
threads.put(id, ctx);
if (!done_get_state) {
getContextState(id);
}
else {
running.add(id);
}
}
}
}
}
public void contextChanged(RunControlContext[] contexts) {
for (RunControlContext ctx : contexts) {
String id = ctx.getID();
if (id.equals(test_ctx_id)) test_context = ctx;
if (threads.get(id) != null) {
assert isMyContext(ctx);
threads.put(id, ctx);
}
}
}
public void contextException(String id, String msg) {
RunControlContext ctx = threads.get(id);
if (ctx != null) {
assert isMyContext(ctx);
exit(new Exception("Context exception: " + msg));
}
}
public void contextRemoved(String[] contexts) {
for (String id : contexts) {
if (suspended.get(id) != null) {
exit(new Exception("Invalid contextRemoved event"));
return;
}
running.remove(id);
testDone(id);
}
}
public void contextResumed(String id) {
IRunControl.RunControlContext ctx = threads.get(id);
if (ctx == null) return;
assert isMyContext(ctx);
if (!ctx.hasState()) {
exit(new Exception("Resumed event for context that HasState = false"));
return;
}
SuspendedContext sc = suspended.remove(id);
if (!done_get_state || sc == null || !sc.ok_to_resume || sc.get_state_pending && ctx.getRCGroup() == null) {
assert !canResume(id);
exit(new Exception("Unexpected contextResumed event: " + id));
return;
}
if (isMyBreakpoint(sc)) suspended_prev.put(id, sc);
running.add(id);
}
@Override
public void testDone(String id) {
if (threads.remove(id) != null && threads.isEmpty()) {
if (bp_cnt != 40) {
exit(new Exception("Test main thread breakpoint count = " + bp_cnt + ", expected 40"));
}
if (data_bp_id != null && data_bp_cnt != 10) {
exit(new Exception("Test main thread data breakpoint count = " + data_bp_cnt + ", expected 10"));
}
if (temp_bp_id != null && temp_bp_cnt != 1) {
exit(new Exception("Temporary breakpoint count = " + temp_bp_cnt + ", expected 1"));
}
srv_run_ctrl.removeListener(this);
// Reset breakpoint list
bp_list.clear();
srv_breakpoints.set(null, new IBreakpoints.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
exit(error);
}
});
}
}
private long getSymAddr(String sym) {
return sym_list.get(sym).getValue().longValue();
}
private String toSymName(long addr) {
for (String name : sym_list.keySet()) {
if (getSymAddr(name) == addr) return name;
}
return "0x" + Long.toHexString(addr);
}
private void checkSuspendedContext(SuspendedContext sc, String sym) {
long pc = Long.parseLong(sc.pc);
long ss = getSymAddr(sym);
if (pc != ss || !"Breakpoint".equals(sc.reason)) {
exit(new Exception("Invalid contextSuspended event: " +
sc.id + " '" + toSymName(pc) + "' " + sc.pc + " " + sc.reason +
", expected breakpoint at '" + sym + "' " + ss));
}
String bp_id = null;
if (sc.params != null) {
Object ids = sc.params.get(IRunControl.STATE_BREAKPOINT_IDS);
if (ids != null) {
@SuppressWarnings("unchecked")
Collection<String> c = (Collection<String>)ids;
HashSet<String> set = new HashSet<String>();
for (String id : c) {
if (!set.add(id)) {
exit(new Exception("Invalid value of 'BPs' attribute: duplicate items"));
return;
}
if (bp_list.get(id) != null) {
bp_id = id;
break;
}
}
if (bp_id == null) {
exit(new Exception("Invalid value of 'BPs' attribute in a context state: " + ids));
}
}
}
}
private void checkSuspendedContext(final SuspendedContext sc) {
if (sc.params != null) {
Object pc_err = sc.params.get(IRunControl.STATE_PC_ERROR);
if (pc_err != null) {
String msg = Command.toErrorString(pc_err);
exit(new Exception("PC error: " + msg));
return;
}
Object step_err = sc.params.get(IRunControl.STATE_STEP_ERROR);
if (step_err != null) {
String msg = Command.toErrorString(step_err);
exit(new Exception("Step error: " + msg));
return;
}
}
boolean my_breakpoint = isMyBreakpoint(sc);
if (main_thread_id == null && my_breakpoint) {
// Process main thread should be the first to hit a breakpoint in the test
if (!done_get_state) {
exit(new Exception("Unexpeceted breakpoint hit"));
return;
}
main_thread_id = sc.id;
if (temp_bp_id != null) {
final Map<String,Object> m = bp_list.get(temp_bp_id);
ArrayList<String> l = new ArrayList<String>();
l.add(main_thread_id);
m.put(IBreakpoints.PROP_CONTEXT_IDS, l);
m.put(IBreakpoints.PROP_ENABLED, true);
bp_change_cmds.add(srv_breakpoints.change(m, new IBreakpoints.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
bp_change_cmds.remove(token);
if (error != null) exit(error);
}
}));
}
}
if (main_thread_id == null) {
if (all_setup_done) resume(sc.id);
return;
}
if (my_breakpoint) {
if (sc.id.equals(main_thread_id)) bp_cnt++;
SuspendedContext sp = suspended_prev.get(sc.id);
String sp_sym = sp == null ? null : toSymName(Long.parseLong(sp.pc));
if (sp == null) {
checkSuspendedContext(sc, "tcf_test_func0");
}
else if ("tcf_test_func0".equals(sp_sym)) {
checkSuspendedContext(sc, "tcf_test_func1");
}
else if ("tcf_test_func1".equals(sp_sym)) {
if (sc.id.equals(main_thread_id)) {
checkSuspendedContext(sc, "tcf_test_func2");
}
else {
checkSuspendedContext(sc, "tcf_test_func3");
}
}
else if ("tcf_test_func2".equals(sp_sym)) {
checkSuspendedContext(sc, "tcf_test_func3");
}
else if ("tcf_test_func3".equals(sp_sym)) {
checkSuspendedContext(sc, "tcf_test_func0");
}
if (isMyTempBreakpoint(sc)) temp_bp_cnt++;
}
else if (isMyDataBreakpoint(sc)) {
if (sc.id.equals(main_thread_id)) data_bp_cnt++;
}
if (!all_setup_done) return;
if (!test_suite.isActive(this)) return;
Runnable done = new Runnable() {
public void run() {
if (suspended.get(sc.id) == sc) resume(sc.id);
}
};
if (my_breakpoint) {
switch (rnd.nextInt(6)) {
case 0:
runMemoryTest(sc, done);
break;
case 1:
runRegistersTest(sc, done);
break;
case 2:
runLineNumbersTest(sc, done);
break;
case 3:
runSymbolsTest(sc, done);
break;
case 4:
changeBreakpoint6();
done.run();
break;
default:
done.run();
break;
}
}
else {
done.run();
}
}
private boolean isMyContext(IRunControl.RunControlContext ctx) {
// Check if the context was created by this test
if (test_ctx_id == null) return false;
return test_ctx_id.equals(ctx.getID()) ||
test_ctx_id.equals(ctx.getParentID()) ||
test_ctx_id.equals(ctx.getCreatorID());
}
private boolean isMyBreakpoint(SuspendedContext sc) {
// Check if the context is suspended by one of our breakpoints
if (!"Breakpoint".equals(sc.reason)) return false;
long pc = Long.parseLong(sc.pc);
for (IDiagnostics.ISymbol sym : sym_list.values()) {
if (pc == sym.getValue().longValue()) return true;
}
return false;
}
private boolean isMyDataBreakpoint(SuspendedContext sc) {
// Check if the context is suspended by our data breakpoints
if (data_bp_id == null) return false;
if (!"Breakpoint".equals(sc.reason)) return false;
if (sc.params == null) return false;
Object ids = sc.params.get(IRunControl.STATE_BREAKPOINT_IDS);
if (ids != null) {
@SuppressWarnings("unchecked")
Collection<String> c = (Collection<String>)ids;
if (c.contains(data_bp_id)) return true;
}
return false;
}
private boolean isMyTempBreakpoint(SuspendedContext sc) {
// Check if the context is suspended by our temporary breakpoint
if (temp_bp_id == null) return false;
if (!"Breakpoint".equals(sc.reason)) return false;
if (sc.params == null) return false;
Object ids = sc.params.get(IRunControl.STATE_BREAKPOINT_IDS);
if (ids != null) {
@SuppressWarnings("unchecked")
Collection<String> c = (Collection<String>)ids;
if (c.contains(temp_bp_id)) return true;
}
return false;
}
public void contextSuspended(final String id, String pc, String reason, Map<String, Object> params) {
IRunControl.RunControlContext ctx = threads.get(id);
if (ctx == null) return;
assert isMyContext(ctx);
if (!ctx.hasState()) {
exit(new Exception("Suspended event for context that HasState = false"));
return;
}
running.remove(id);
SuspendedContext sc = suspended.get(id);
if (sc != null) {
if (done_get_state || pc != null && !sc.pc.equals(pc) || reason != null && !sc.reason.equals(reason)) {
exit(new Exception("Invalid contextSuspended event"));
return;
}
}
else {
sc = new SuspendedContext(id, pc, reason, params);
assert !done_get_state || done_disassembly || srv_disassembly == null;
suspended.put(id, sc);
}
if (!all_setup_done) return;
assert get_state_cmds.size() == 0;
assert suspended.get(id) == sc;
assert !sc.get_state_pending;
sc.get_state_pending = true;
final SuspendedContext sc0 = sc;
testSymbolsFlushEvents();
ctx.getState(new IRunControl.DoneGetState() {
public void doneGetState(IToken token, Exception error, boolean susp,
String pc, String reason, Map<String,Object> params) {
if (error != null) {
exit(new Exception("Cannot get context state", error));
}
else if (suspended.get(id) != sc0) {
exit(new Exception("Context resumed before RunControl.getState result"));
}
else if (!susp) {
exit(new Exception("Invalid RunControl.getState result"));
}
else if (pc == null || pc.length() == 0) {
exit(new Exception("Invalid PC returned by RunControl.getState"));
}
else if (test_suite.isActive(TestRCBP1.this)) {
SuspendedContext sc = suspended.get(id);
assert sc.get_state_pending;
sc.get_state_pending = false;
if (sc.pc == null || sc.reason == null) {
sc = new SuspendedContext(id, pc, reason, params);
assert !done_get_state || done_disassembly || srv_disassembly == null;
suspended.put(id, sc);
}
else if (!sc.pc.equals(pc) || !sc.reason.equals(reason)) {
exit(new Exception("Invalid RunControl.getState result"));
return;
}
checkSuspendedContext(sc);
}
}
});
}
public boolean canResume(String id) {
if (test_ctx_id != null && threads.size() == 0)
// Don't know yet neither my thread IDs nor my RC group.
return false;
IRunControl.RunControlContext ctx = test_rc.getContext(id);
if (ctx == null) return false;
if (isMyContext(ctx) && (!all_setup_done || threads.get(id) == null))
// My threads should stay suspended until all_setup_done
return false;
String grp = ctx.getRCGroup();
for (IRunControl.RunControlContext x : threads.values()) {
if (x.getID().equals(id) || grp != null && grp.equals(x.getRCGroup())) {
SuspendedContext sc = suspended.get(x.getID());
if (sc == null) return false;
if (!sc.ok_to_resume) return false;
}
}
return true;
}
private void resume(String id) {
assert done_get_state || resume_cnt == 0;
assert bp_sync_done;
assert mem_map_test_done;
resume_cnt++;
SuspendedContext sc = suspended.get(id);
IRunControl.RunControlContext ctx = threads.get(id);
if (ctx != null && sc != null) {
assert !sc.get_state_pending;
assert !sc.ok_to_resume;
sc.ok_to_resume = true;
int cnt = 1;
int rm = IRunControl.RM_RESUME;
if (isMyBreakpoint(sc)) {
rm = rnd.nextInt(6);
if (!ctx.canResume(rm)) rm = IRunControl.RM_RESUME;
if (rm != IRunControl.RM_RESUME) cnt = rnd.nextInt(5) + 1;
}
test_rc.resume(id, rm, cnt);
}
}
private void runMemoryTest(final SuspendedContext sc, final Runnable done) {
if (srv_memory == null || test_suite.target_lock) {
Protocol.invokeLater(done);
return;
}
test_suite.target_lock = true;
srv_memory.getContext(test_context.getProcessID(), new IMemory.DoneGetContext() {
public void doneGetContext(IToken token, Exception error, final MemoryContext mem_ctx) {
if (suspended.get(sc.id) != sc) {
test_suite.target_lock = false;
return;
}
if (error != null) {
exit(error);
return;
}
if (!test_context.getProcessID().equals(mem_ctx.getID())) {
exit(new Exception("Bad memory context data: invalid ID"));
}
final boolean big_endian = mem_ctx.isBigEndian();
final int addr_size = mem_ctx.getAddressSize();
final byte[] buf = new byte[0x1000];
mem_ctx.get(sym_list.get("tcf_test_array").getValue(), 1, buf, 0, addr_size, 0, new IMemory.DoneMemory() {
public void doneMemory(IToken token, MemoryError error) {
if (suspended.get(sc.id) != sc) {
test_suite.target_lock = false;
return;
}
if (error != null) {
exit(error);
return;
}
byte[] tmp = new byte[addr_size + 1];
tmp[0] = 0; // Extra byte to avoid sign extension by BigInteger
if (big_endian) {
System.arraycopy(buf, 0, tmp, 1, addr_size);
}
else {
for (int i = 0; i < addr_size; i++) {
tmp[i + 1] = buf[addr_size - i - 1];
}
}
Number mem_address = new BigInteger(tmp);
if (mem_address.longValue() == 0) {
exit(new Exception("Bad value of 'tcf_test_array': " + mem_address));
}
testSetMemoryCommand(sc, mem_ctx, mem_address, buf, done);
}
});
}
});
}
private void testSetMemoryCommand(final SuspendedContext sc,
final IMemory.MemoryContext mem_ctx,
final Number addr, final byte[] buf,
final Runnable done) {
final byte[] data = new byte[buf.length];
rnd.nextBytes(data);
mem_ctx.set(addr, 1, data, 0, data.length, 0, new IMemory.DoneMemory() {
public void doneMemory(IToken token, MemoryError error) {
if (suspended.get(sc.id) != sc) {
test_suite.target_lock = false;
return;
}
if (error != null) {
exit(error);
return;
}
mem_ctx.get(addr, 1, buf, 0, buf.length, 0, new IMemory.DoneMemory() {
public void doneMemory(IToken token, MemoryError error) {
if (suspended.get(sc.id) != sc) {
test_suite.target_lock = false;
return;
}
if (error != null) {
exit(error);
return;
}
for (int i = 0; i < data.length; i++) {
if (data[i] != buf[i]) {
exit(new Exception(
"Invalid Memory.get responce: wrong data at offset " + i +
", expected " + data[i] + ", actual " + buf[i]));
return;
}
}
testFillMemoryCommand(sc, mem_ctx, addr, buf, done);
}
});
}
});
}
private void testFillMemoryCommand(final SuspendedContext sc,
final IMemory.MemoryContext mem_ctx,
final Number addr, final byte[] buf,
final Runnable done) {
final byte[] data = new byte[buf.length / 7];
rnd.nextBytes(data);
mem_ctx.fill(addr, 1, data, buf.length, 0, new IMemory.DoneMemory() {
public void doneMemory(IToken token, MemoryError error) {
if (suspended.get(sc.id) != sc) {
test_suite.target_lock = false;
return;
}
if (error != null) {
exit(error);
return;
}
mem_ctx.get(addr, 1, buf, 0, buf.length, 0, new IMemory.DoneMemory() {
public void doneMemory(IToken token, MemoryError error) {
if (suspended.get(sc.id) != sc) {
test_suite.target_lock = false;
return;
}
if (error != null) {
exit(error);
return;
}
for (int i = 0; i < data.length; i++) {
if (data[i % data.length] != buf[i]) {
exit(new Exception(
"Invalid Memory.get responce: wrong data at offset " + i +
", expected " + data[i % data.length] + ", actual " + buf[i]));
return;
}
}
test_suite.target_lock = false;
done.run();
}
});
}
});
}
private void runRegistersTest(final SuspendedContext sc, final Runnable done) {
if (srv_registers == null) {
Protocol.invokeLater(done);
return;
}
if (regs.get(sc.id) == null) {
final Map<String,IRegisters.RegistersContext> reg_map =
new HashMap<String,IRegisters.RegistersContext>();
regs.put(sc.id, reg_map);
final Set<IToken> cmds = new HashSet<IToken>();
cmds.add(srv_registers.getChildren(sc.id, new IRegisters.DoneGetChildren() {
public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
cmds.remove(token);
if (suspended.get(sc.id) != sc) {
regs.remove(sc.id);
return;
}
if (error != null) {
exit(error);
return;
}
for (final String id : context_ids) {
cmds.add(srv_registers.getChildren(id, this));
cmds.add(srv_registers.getContext(id, new IRegisters.DoneGetContext() {
public void doneGetContext(IToken token, Exception error, RegistersContext context) {
cmds.remove(token);
if (suspended.get(sc.id) != sc) {
regs.remove(sc.id);
return;
}
if (error != null) {
exit(error);
return;
}
reg_map.put(id, context);
if (cmds.isEmpty()) {
testGetSetRegisterCommands(sc, done);
}
}
}));
}
}
}));
}
else {
testGetSetRegisterCommands(sc, done);
}
}
private void testGetSetRegisterCommands(final SuspendedContext sc, final Runnable done) {
Map<String,IRegisters.RegistersContext> reg_map = regs.get(sc.id);
final Set<IToken> cmds = new HashSet<IToken>();
if (reg_map.size() > 0) {
String[] ids = reg_map.keySet().toArray(new String[reg_map.size()]);
for (int n = 0; n < 10000 && cmds.size() < 100; n++) {
if (rnd.nextBoolean()) {
String id = ids[rnd.nextInt(ids.length)];
final IRegisters.RegistersContext ctx = reg_map.get(id);
if (!ctx.isReadable()) continue;
if (ctx.isReadOnce()) continue;
if (ctx.getSize() == 0) continue;
cmds.add(ctx.get(new IRegisters.DoneGet() {
public void doneGet(IToken token, Exception error, final byte[] value) {
cmds.remove(token);
if (suspended.get(sc.id) != sc) return;
if (error != null) {
exit(error);
return;
}
if (ctx.getSize() != value.length) {
exit(new Exception("Invalid register value size"));
return;
}
if (ctx.isWriteable() && !ctx.isWriteOnce() && ctx.getMemoryContext() == null) {
cmds.add(ctx.set(value, new IRegisters.DoneSet() {
public void doneSet(IToken token, Exception error) {
cmds.remove(token);
if (suspended.get(sc.id) != sc) return;
if (error != null) {
exit(error);
return;
}
cmds.add(ctx.get(new IRegisters.DoneGet() {
public void doneGet(IToken token, Exception error, byte[] value1) {
cmds.remove(token);
if (suspended.get(sc.id) != sc) return;
if (error != null) {
exit(error);
return;
}
for (int i = 0; i < value.length; i++) {
if (value[i] != value1[i]) {
exit(new Exception("Invalid register value"));
return;
}
}
if (cmds.isEmpty()) {
done.run();
}
}
}));
}
}));
}
if (cmds.isEmpty()) {
done.run();
}
}
}));
}
else {
int data_size = 0;
int l = rnd.nextInt(32);
List<IRegisters.Location> locs = new ArrayList<IRegisters.Location>();
for (int i = 0; i < l; i++) {
String id = ids[rnd.nextInt(ids.length)];
IRegisters.RegistersContext ctx = reg_map.get(id);
if (!ctx.isReadable()) continue;
if (!ctx.isWriteable()) continue;
if (ctx.isReadOnce()) continue;
if (ctx.isWriteOnce()) continue;
if (ctx.getSize() == 0) continue;
if (ctx.getMemoryContext() != null) continue;
int offs = rnd.nextInt(ctx.getSize());
int size = rnd.nextInt(ctx.getSize() - offs) + 1;
locs.add(new IRegisters.Location(id, offs, size));
data_size += size;
}
final int total_size = data_size;
final IRegisters.Location[] loc_arr = locs.toArray(new IRegisters.Location[locs.size()]);
cmds.add(srv_registers.getm(loc_arr, new IRegisters.DoneGet() {
public void doneGet(IToken token, Exception error, byte[] value) {
cmds.remove(token);
if (suspended.get(sc.id) != sc) return;
if (error == null && value.length != total_size) {
error = new Exception("Invalid data size in Registers.getm reply");
}
if (error != null) {
exit(error);
return;
}
cmds.add(srv_registers.setm(loc_arr, value, new IRegisters.DoneSet() {
public void doneSet(IToken token, Exception error) {
cmds.remove(token);
if (suspended.get(sc.id) != sc) return;
if (error != null) {
exit(error);
return;
}
if (cmds.isEmpty()) {
done.run();
}
}
}));
}
}));
}
}
}
if (cmds.isEmpty()) {
done.run();
}
}
private void runLineNumbersTest(SuspendedContext sc, final Runnable done) {
if (srv_line_numbers != null && sc.pc != null) {
BigInteger x = new BigInteger(sc.pc);
BigInteger y = x.add(BigInteger.valueOf(1));
srv_line_numbers.mapToSource(sc.id, x, y, new ILineNumbers.DoneMapToSource() {
public void doneMapToSource(IToken token, Exception error, CodeArea[] areas) {
if (error != null) {
exit(error);
return;
}
done.run();
}
});
}
else {
done.run();
}
}
private void runSymbolsTest(final SuspendedContext sc, final Runnable done) {
if (srv_syms != null && sc.pc != null) {
final BigInteger x = new BigInteger(sc.pc);
srv_syms.findByAddr(sc.id, x, new ISymbols.DoneFind() {
public void doneFind(IToken token, Exception error, String symbol_id) {
if (error != null) {
int code = IErrorReport.TCF_ERROR_OTHER;
if (error instanceof IErrorReport) code = ((IErrorReport)error).getErrorCode();
switch (code) {
case IErrorReport.TCF_ERROR_INV_COMMAND:
case IErrorReport.TCF_ERROR_SYM_NOT_FOUND:
done.run();
return;
default:
exit(error);
return;
}
}
srv_syms.getContext(symbol_id, new ISymbols.DoneGetContext() {
public void doneGetContext(IToken token, Exception error, Symbol context) {
if (error != null) {
exit(error);
return;
}
Number addr = context.getAddress();
int size = context.getSize();
if (addr == null) {
exit(new Exception("Missing symbol address attribute"));
return;
}
if (size <= 0) {
exit(new Exception("Invalid symbol size attribute"));
return;
}
BigInteger y = JSON.toBigInteger(addr);
BigInteger z = y.add(BigInteger.valueOf(size));
if (x.compareTo(y) < 0 || x.compareTo(z) >= 0) {
exit(new Exception("Invalid symbol address attribute"));
return;
}
String name = context.getName();
if (name == null) {
done.run();
return;
}
srv_syms.find(sc.id, 0, name, new ISymbols.DoneFind() {
public void doneFind(IToken token, Exception error, String symbol_id) {
if (error != null) {
exit(error);
return;
}
done.run();
}
});
}
});
}
});
}
else {
done.run();
}
}
private String getRandomString() {
int l = rnd.nextInt(512) + 1;
StringBuffer bf = new StringBuffer(l);
for (int i = 0; i < l; i++) {
bf.append((char)(rnd.nextInt(0xffff) + 1));
}
return bf.toString();
}
private void runMemoryMapTest() {
assert !mem_map_test_running;
if (srv_memory_map == null || test_context == null || test_context.getProcessID() == null) {
mem_map_test_done = true;
runTest();
return;
}
mem_map_test_running = true;
final String prs_id = test_context.getProcessID();
srv_memory_map.get(prs_id, new IMemoryMap.DoneGet() {
public void doneGet(IToken token, Exception error, MemoryRegion[] map) {
if (error != null) {
exit(error);
return;
}
final Map<String,Object> props = new HashMap<String,Object>();
final String test_id = "TestRCBP1." + mem_map_region_id++ + "." + prs_id;
props.put(IMemoryMap.PROP_ID, test_id);
if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_ADDRESS, rnd.nextInt(0x10000000));
if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_SIZE, rnd.nextInt(0x10000000));
if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_FLAGS, rnd.nextInt(0x7));
if (rnd.nextBoolean()) {
props.put(IMemoryMap.PROP_FILE_NAME, getRandomString());
if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_SECTION_NAME, getRandomString());
else if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_OFFSET, rnd.nextInt(0x10000000));
if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_BSS, true);
}
List<MemoryRegion> list = new ArrayList<MemoryRegion>();
for (MemoryRegion r : map) {
String id = (String)r.getProperties().get(IMemoryMap.PROP_ID);
if (id != null) list.add(r);
}
final List<MemoryRegion> org_list = new ArrayList<MemoryRegion>(list);
list.add(new TCFMemoryRegion(props));
srv_memory_map.set(prs_id, list.toArray(new MemoryRegion[list.size()]), new IMemoryMap.DoneSet() {
public void doneSet(IToken token, Exception error) {
if (error != null) {
exit(error);
return;
}
srv_memory_map.get(prs_id, new IMemoryMap.DoneGet() {
public void doneGet(IToken token, Exception error, MemoryRegion[] map) {
if (error != null) {
exit(error);
return;
}
int cnt = 0;
for (MemoryRegion r : map) {
String id = (String)r.getProperties().get(IMemoryMap.PROP_ID);
if (!test_id.equals(id)) continue;
for (String p : props.keySet()) {
if (!props.get(p).equals(r.getProperties().get(p))) {
exit(new Error("Invalid value returned for Memory Map region property " + p));
return;
}
}
cnt++;
}
if (cnt != 1) {
exit(new Error("Error adding memory map entry with MemoryMap.set command"));
return;
}
srv_memory_map.set(prs_id, org_list.toArray(new MemoryRegion[org_list.size()]), new IMemoryMap.DoneSet() {
public void doneSet(IToken token, Exception error) {
if (error != null) {
exit(error);
return;
}
mem_map_test_running = false;
mem_map_test_done = true;
runTest();
}
});
}
});
}
});
}
});
}
private void testSymbolsFlushEvents() {
// Use MemoryMap service to generate "memory map changed" event.
// The event invalidates any cached symbols info.
if (srv_memory_map == null) return;
if (rnd.nextInt(101) != 0) return;
ArrayList<MemoryRegion> l = null;
String process_id = test_context.getProcessID();
if (mem_map != null) l = mem_map.get(process_id);
if (l == null) l = new ArrayList<MemoryRegion>();
else l = new ArrayList<MemoryRegion>(l);
Map<String,Object> props = new HashMap<String,Object>();
props.put("TestRCBP1", test_cnt++);
l.add(new TCFMemoryRegion(props));
srv_memory_map.set(process_id, l.toArray(new MemoryRegion[l.size()]), new IMemoryMap.DoneSet() {
public void doneSet(IToken token, Exception error) {
if (error instanceof IErrorReport) {
IErrorReport e = (IErrorReport)error;
if (e.getErrorCode() == IErrorReport.TCF_ERROR_INV_CONTEXT) error = null;
}
if (error != null) {
exit(error);
}
}
});
}
void cancel(final Runnable done) {
if (srv_run_ctrl != null) srv_run_ctrl.removeListener(this);
if (test_ctx_id == null) {
if (pending_cancel != null) {
exit(null);
}
else {
pending_cancel = done;
}
}
else if (cancel_test_cmd == null) {
cancel_test_cmd = srv_diag.cancelTest(test_ctx_id, new IDiagnostics.DoneCancelTest() {
public void doneCancelTest(IToken token, Throwable error) {
cancel_test_cmd = null;
exit(error);
done.run();
}
});
}
else {
exit(new Exception("Cannot terminate remote test process"));
done.run();
}
}
private void exit(Throwable x) {
if (!test_suite.isActive(this)) return;
if (pending_cancel != null) {
Protocol.invokeLater(pending_cancel);
pending_cancel = null;
}
else {
if (srv_run_ctrl != null) srv_run_ctrl.removeListener(this);
}
if (srv_breakpoints != null) srv_breakpoints.removeListener(bp_listener);
test_suite.done(this, x);
}
}