/*******************************************************************************
* Copyright (c) 2008, 2014 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.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.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.IDPrintf;
import org.eclipse.tcf.services.IDiagnostics;
import org.eclipse.tcf.services.IExpressions;
import org.eclipse.tcf.services.IMemoryMap;
import org.eclipse.tcf.services.IRunControl;
import org.eclipse.tcf.services.IStackTrace;
import org.eclipse.tcf.services.IStreams;
import org.eclipse.tcf.services.ISymbols;
import org.eclipse.tcf.services.IExpressions.Value;
import org.eclipse.tcf.services.IMemoryMap.MemoryRegion;
import org.eclipse.tcf.util.TCFMemoryRegion;
class TestExpressions implements ITCFTest, RunControl.DiagnosticTestDone,
IRunControl.RunControlListener, IExpressions.ExpressionsListener, IBreakpoints.BreakpointsListener {
private final TCFTestSuite test_suite;
private final RunControl test_rc;
private final IDiagnostics srv_diag;
private final IExpressions srv_expr;
private final ISymbols srv_syms;
private final IStackTrace srv_stk;
private final IRunControl srv_rc;
private final IBreakpoints srv_bp;
private final IDPrintf srv_dprintf;
private final IStreams srv_streams;
private final IMemoryMap srv_memory_map;
private final Random rnd = new Random();
private final Map<String,ArrayList<MemoryRegion>> mem_map;
private String test_id;
private String bp_id;
private boolean bp_ok;
private IDiagnostics.ISymbol sym_func3;
private String test_ctx_id;
private String process_id;
private String thread_id;
private boolean run_to_bp_done;
private boolean dprintf_done;
private boolean test_done;
private boolean cancel_test_sent;
private IRunControl.RunControlContext test_ctx;
private IRunControl.RunControlContext thread_ctx;
private String suspended_pc;
private boolean waiting_suspend;
private String[] stack_trace;
private String[] stack_range;
private IStackTrace.StackTraceContext[] stack_frames;
private String[] local_var_expr_ids;
private final Set<IToken> cmds = new HashSet<IToken>();
private final Map<String,String> global_var_ids = new HashMap<String,String>();
private final Map<String,String> local_var_ids = new HashMap<String,String>();
private final Map<String,SymbolLocation> global_var_location = new HashMap<String,SymbolLocation>();
private final Map<String,SymbolLocation> local_var_location = new HashMap<String,SymbolLocation>();
private final Map<String,IExpressions.Expression> expr_ctx = new HashMap<String,IExpressions.Expression>();
private final Map<String,IExpressions.Value> expr_val = new HashMap<String,IExpressions.Value>();
private final Map<String,ISymbols.Symbol> expr_sym = new HashMap<String,ISymbols.Symbol>();
private final Map<String,String[]> expr_chld = new HashMap<String,String[]>();
private final Set<String> expr_to_dispose = new HashSet<String>();
private int timer = 0;
private static int test_cnt;
private static String[] global_var_names = {
"tcf_test_char",
"tcf_test_short",
"tcf_test_long",
"tcf_test_str",
"tcf_cpp_test_bool",
"tcf_cpp_test_int_ref",
"tcf_cpp_test_class_extension_var",
"tcf_cpp_test_class_extension_ptr",
"tcf_cpp_test_class_extension_ref",
"tcf_cpp_test_class_extension_member_ptr",
"tcf_cpp_test_anonymous_union_var",
"tcf_test_array_field"
};
private static final String[] test_expressions = {
"func2_local1",
"func2_local2",
"func2_local3",
"func2_local1 == func2_local1",
"func2_local1 != func2_local2",
"1.34 == 1.34",
"1.34 != 1.35",
"1 ? 1 : 0",
"!func2_local1 ? 0 : 1",
"(0 || 0) == 0",
"(0 || func2_local1) == 1",
"(func2_local1 || 0) == 1",
"(func2_local1 || func2_local1) == 1",
"(0 && 0) == 0",
"(0 && func2_local1) == 0",
"(func2_local1 && 0) == 0",
"(func2_local1 && func2_local1) == 1",
"(func2_local1 | func2_local2) == 3",
"(func2_local1 & func2_local2) == 0",
"(func2_local1 ^ func2_local2) == 3",
"(func2_local1 < func2_local2)",
"(func2_local1 <= func2_local2)",
"!(func2_local1 > func2_local2)",
"!(func2_local1 >= func2_local2)",
"(func2_local1 < 1.1)",
"(func2_local1 <= 1.1)",
"!(func2_local1 > 1.1)",
"!(func2_local1 >= 1.1)",
"(func2_local2 << 2) == 8",
"(func2_local2 >> 1) == 1",
"+func2_local2 == 2",
"-func2_local2 == -2",
"::tcf_test_func2",
"tcf_test_func2::func2_local2 == 2",
"\"tcf_test_func2\"::func2_local2 == 2",
"L\"tcf_test_func2\"::func2_local2 == 2",
"$\"tcf_test_func2\"::func2_local2 == 2",
"::tcf_test_func2::func2_local2 == 2",
"(short)(int)(long)((char *)func2_local2 + 1) == 3",
"((func2_local1 + func2_local2) * 2 - 2) / 2 == 2",
"func2_local3.f_struct->f_struct->f_struct == &func2_local3",
"(char *)func2_local3.f_struct",
"(char[4])func2_local3.f_struct",
"&((test_struct *)0)->f_float",
"&((struct test_struct *)0)->f_float",
"tcf_test_func3",
"&tcf_test_func3",
"tcf_test_array + 10",
"*(tcf_test_array + 10) | 1",
"&*(char *)(int *)0 == 0",
"(bool)0 == false",
"(bool)1 == true",
"sizeof(bool) == sizeof true",
"tcf_cpp_test_class::s_int == 1",
"sizeof tcf_cpp_test_class::s_int == sizeof signed",
"tcf_cpp_test_class::tcf_cpp_test_class_nested::s_int == 2",
"tcf_cpp_test_class_extension::tcf_cpp_test_class_nested::s_int == 2",
"enum_val1 == 1 && enum_val2 == 2 && enum_val3 == 3",
"tcf_cpp_test_anonymous_union_var.f1 == 234",
"tcf_cpp_test_anonymous_union_var.f2 == 235",
"tcf_cpp_test_anonymous_union_var.f3 == 235",
"tcf_cpp_test_class_extension_var.f_int == 345",
"tcf_cpp_test_class_extension_ref.f_int == 345",
"tcf_cpp_test_class_extension_ptr == &tcf_cpp_test_class_extension_var",
"tcf_cpp_test_class_extension_var.*tcf_cpp_test_class_extension_member_ptr == tcf_cpp_test_class_extension_var.f_int",
"tcf_cpp_test_class_extension_ref.*tcf_cpp_test_class_extension_member_ptr == tcf_cpp_test_class_extension_var.f_int",
"tcf_cpp_test_class_extension_ptr->*tcf_cpp_test_class_extension_member_ptr == tcf_cpp_test_class_extension_var.f_int",
"sizeof(tcf_test_array_field.buf) == 15",
"sizeof(tcf_test_array_field.buf[0]) == 5",
"sizeof(tcf_test_array_field.buf[0][0]) == 1",
"sizeof(tcf_test_array_field.buf[2][4]) == 1",
"tcf_test_array_field.buf[1][3] == 8",
"tcf_cpp_test_int_ref == 1",
"&tcf_cpp_test_int_ref == &tcf_cpp_test_class::s_int",
"sizeof(tcf_cpp_test_int_ref) == sizeof(int)",
"sizeof(tcf_cpp_test_class_extension_ref) == sizeof(tcf_cpp_test_class_extension_var)",
};
private static final String[] test_dprintfs = {
"$printf", null,
"$printf(", null,
"$printf()", null,
"$printf(1)", null,
"$printf(\"abc\")", "abc",
"$printf(\"%s\",\"abc\")", "abc",
"$printf(\"%d\",1)", "1",
"$printf(\"%d\",enum_val2)", "2",
"$printf(\"%u\",func2_local3.f_enum)", "3",
"$printf(\"%g\",func2_local3.f_float)", "3.14",
"$printf(\"%g\",func2_local3.f_double)", "2.71",
"0 && $printf(\"fail\")", "",
"1 && $printf(\"OK\")", "OK",
"$printf(\"aa%%dd %s\",tcf_test_str)", "aa%dd abc",
"$printf(\"%s %s\",tcf_test_str,func2_local_str)", "abc bcd",
};
@SuppressWarnings("unused")
private static class SymbolLocation {
Exception error;
Map<String,Object> props;
}
TestExpressions(TCFTestSuite test_suite, RunControl test_rc,
IChannel channel, Map<String,ArrayList<MemoryRegion>> mem_map) {
this.test_suite = test_suite;
this.test_rc = test_rc;
this.mem_map = mem_map;
srv_diag = channel.getRemoteService(IDiagnostics.class);
srv_expr = channel.getRemoteService(IExpressions.class);
srv_syms = channel.getRemoteService(ISymbols.class);
srv_stk = channel.getRemoteService(IStackTrace.class);
srv_rc = channel.getRemoteService(IRunControl.class);
srv_bp = channel.getRemoteService(IBreakpoints.class);
srv_dprintf = channel.getRemoteService(IDPrintf.class);
srv_streams = channel.getRemoteService(IStreams.class);
srv_memory_map = channel.getRemoteService(IMemoryMap.class);
}
public void start() {
if (srv_diag == null || srv_expr == null || srv_stk == null || srv_rc == null || srv_bp == null) {
test_suite.done(this, null);
}
else {
srv_expr.addListener(this);
srv_rc.addListener(this);
srv_bp.addListener(this);
srv_diag.getTestList(new IDiagnostics.DoneGetTestList() {
public void doneGetTestList(IToken token, Throwable error, String[] list) {
if (!test_suite.isActive(TestExpressions.this)) return;
if (error != null) {
exit(error);
}
else {
if (list.length > 0) {
test_id = list[rnd.nextInt(list.length)];
runTest();
Protocol.invokeLater(100, new Runnable() {
public void run() {
if (!test_suite.isActive(TestExpressions.this)) return;
timer++;
if (test_suite.cancel) {
exit(null);
}
else if (timer < 600) {
if (test_done && !cancel_test_sent) {
test_rc.cancel(test_ctx_id);
cancel_test_sent = true;
}
Protocol.invokeLater(100, this);
}
else if (test_ctx_id == null) {
exit(new Error("Timeout waiting for reply of Diagnostics.runTest command"));
}
else {
exit(new Error("Missing 'contextRemoved' event for " + test_ctx_id));
}
}
});
return;
}
exit(null);
}
}
});
}
}
public boolean canResume(String id) {
if (test_ctx_id != null && thread_ctx == null) return false;
if (thread_ctx != null && !test_done) {
assert thread_ctx.getID().equals(thread_id);
IRunControl.RunControlContext ctx = test_rc.getContext(id);
if (ctx == null) return false;
String grp = ctx.getRCGroup();
if (id.equals(thread_id) || grp != null && grp.equals(thread_ctx.getRCGroup())) {
if (run_to_bp_done) return false;
if (sym_func3 == null) return false;
if (suspended_pc == null) return false;
BigInteger pc0 = JSON.toBigInteger(sym_func3.getValue());
BigInteger pc1 = new BigInteger(suspended_pc);
if (pc0.equals(pc1)) return false;
}
}
return true;
}
@SuppressWarnings("unchecked")
private void runTest() {
timer = 0;
if (cmds.size() > 0) return;
if (bp_id == null) {
srv_bp.set(null, new IBreakpoints.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
if (error != null) {
exit(error);
}
else {
bp_id = "TestExpressionsBP";
runTest();
}
}
});
return;
}
if (!bp_ok) {
Map<String,Object> m = new HashMap<String,Object>();
m.put(IBreakpoints.PROP_ID, bp_id);
m.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE);
m.put(IBreakpoints.PROP_LOCATION, "tcf_test_func3");
srv_bp.set(new Map[]{ m }, new IBreakpoints.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
if (error != null) {
exit(error);
}
else {
bp_ok = true;
runTest();
}
}
});
return;
}
if (test_ctx_id == null) {
srv_diag.runTest(test_id, new IDiagnostics.DoneRunTest() {
public void doneRunTest(IToken token, Throwable error, String id) {
if (error != null) {
exit(error);
}
else if (id == null) {
exit(new Exception("Test context ID must not be null"));
}
else if (test_rc.getContext(id) == null) {
exit(new Exception("Missing context added event"));
}
else {
test_ctx_id = id;
runTest();
}
}
});
return;
}
if (test_ctx == null) {
srv_rc.getContext(test_ctx_id, new IRunControl.DoneGetContext() {
public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext ctx) {
if (error != null) {
exit(error);
}
else if (ctx == null) {
exit(new Exception("Invalid test execution context"));
}
else {
test_ctx = ctx;
process_id = test_ctx.getProcessID();
if (test_ctx.hasState()) thread_id = test_ctx_id;
runTest();
}
}
});
return;
}
if (sym_func3 == null) {
srv_diag.getSymbol(process_id, "tcf_test_func3", new IDiagnostics.DoneGetSymbol() {
public void doneGetSymbol(IToken token, Throwable error, IDiagnostics.ISymbol symbol) {
if (error != null) {
exit(error);
}
else if (symbol == null) {
exit(new Exception("Symbol must not be null: tcf_test_func3"));
}
else {
sym_func3 = symbol;
runTest();
}
}
});
return;
}
if (thread_id == null) {
srv_rc.getChildren(process_id, new IRunControl.DoneGetChildren() {
public void doneGetChildren(IToken token, Exception error, String[] ids) {
if (error != null) {
exit(error);
}
else if (ids == null || ids.length == 0) {
exit(new Exception("Test process has no threads"));
}
else {
thread_id = ids[0];
thread_ctx = test_rc.getContext(thread_id);
runTest();
}
}
});
return;
}
if (suspended_pc == null) {
thread_ctx.getState(new IRunControl.DoneGetState() {
public void doneGetState(IToken token, Exception error,
boolean suspended, String pc, String reason,
Map<String,Object> params) {
if (error != null) {
exit(new Exception("Cannot get context state", error));
}
else if (!suspended) {
waiting_suspend = true;
}
else if (pc == null || pc.length() == 0) {
exit(new Exception("Invalid context PC"));
}
else {
suspended_pc = pc;
runTest();
}
}
});
return;
}
if (!run_to_bp_done) {
BigInteger pc0 = JSON.toBigInteger(sym_func3.getValue());
BigInteger pc1 = new BigInteger(suspended_pc);
if (!pc0.equals(pc1)) {
waiting_suspend = true;
return;
}
run_to_bp_done = true;
}
assert test_done || !canResume(thread_id);
if (stack_trace == null) {
srv_stk.getChildren(thread_id, new IStackTrace.DoneGetChildren() {
public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
if (error != null) {
exit(error);
}
else if (context_ids == null || context_ids.length < 2) {
exit(new Exception("Invalid stack trace"));
}
else {
stack_trace = context_ids;
runTest();
}
}
});
return;
}
if (stack_range == null) {
srv_stk.getChildrenRange(thread_id, 1, 2, new IStackTrace.DoneGetChildren() {
public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
if (error instanceof IErrorReport && ((IErrorReport)error).getErrorCode() == IErrorReport.TCF_ERROR_INV_COMMAND) {
/* Older agent, the command not available */
stack_range = new String[0];
runTest();
}
else if (error != null) {
exit(error);
}
else if (context_ids == null) {
exit(new Exception("Invalid stack trace"));
}
else {
for (int i = 0; i < 2; i++) {
int j = stack_trace.length - i - 2;
if (i >= context_ids.length) {
if (j >= 0) {
exit(new Exception("Invalid result of doneGetChildren command: too short"));
}
}
else {
if (j < 0) {
exit(new Exception("Invalid result of doneGetChildren command: too long"));
}
if (context_ids[i]== null) {
exit(new Exception("Invalid result of doneGetChildren command: ID is null"));
}
if (!context_ids[i].equals(stack_trace[j])) {
exit(new Exception("Invalid result of doneGetChildren command: wrong ID"));
}
}
}
stack_range = context_ids;
runTest();
}
}
});
return;
}
if (stack_frames == null) {
srv_stk.getContext(stack_trace, new IStackTrace.DoneGetContext() {
public void doneGetContext(IToken token, Exception error, IStackTrace.StackTraceContext[] frames) {
if (error != null) {
exit(error);
}
else if (frames == null || frames.length != stack_trace.length) {
exit(new Exception("Invalid stack trace"));
}
else {
stack_frames = frames;
runTest();
}
}
});
return;
}
if (local_var_expr_ids == null) {
testSymbolsFlushEvents();
srv_expr.getChildren(stack_trace[stack_trace.length - 2], new IExpressions.DoneGetChildren() {
public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
if (error != null || context_ids == null) {
// Need to continue tests even if local variables info is not available.
// TODO: need to distinguish absence of debug info from other errors.
local_var_expr_ids = new String[0];
runTest();
}
else {
local_var_expr_ids = context_ids;
runTest();
}
}
});
return;
}
for (final String id : local_var_expr_ids) {
if (expr_ctx.get(id) == null) {
testSymbolsFlushEvents();
cmds.add(srv_expr.getContext(id, new IExpressions.DoneGetContext() {
public void doneGetContext(IToken token, Exception error, IExpressions.Expression ctx) {
cmds.remove(token);
if (error != null) {
exit(error);
}
else {
expr_ctx.put(id, ctx);
local_var_ids.put(id, ctx.getSymbolID());
runTest();
}
}
}));
if (rnd.nextInt(16) == 0) return;
}
}
if (srv_syms != null && local_var_expr_ids.length > 0) {
for (final String nm : global_var_names) {
if (!global_var_ids.containsKey(nm)) {
cmds.add(srv_syms.find(process_id, new BigInteger(suspended_pc), nm, new ISymbols.DoneFind() {
public void doneFind(IToken token, Exception error, String symbol_id) {
cmds.remove(token);
if (error != null) {
if (error instanceof IErrorReport &&
((IErrorReport)error).getErrorCode() == IErrorReport.TCF_ERROR_SYM_NOT_FOUND) {
if (nm.startsWith("tcf_cpp_") || nm.equals("tcf_test_array_field")) {
global_var_ids.put(nm, null);
runTest();
return;
}
}
exit(error);
}
else if (symbol_id == null) {
exit(new Exception("Invalid symbol ID"));
}
else {
global_var_ids.put(nm, symbol_id);
runTest();
}
}
}));
if (rnd.nextInt(16) == 0) return;
}
}
}
if (srv_syms != null) {
for (final String id : global_var_ids.values()) {
if (id != null && global_var_location.get(id) == null) {
cmds.add(srv_syms.getLocationInfo(id, new ISymbols.DoneGetLocationInfo() {
public void doneGetLocationInfo(IToken token, Exception error, Map<String, Object> props) {
cmds.remove(token);
SymbolLocation l = new SymbolLocation();
l.error = error;
l.props = props;
global_var_location.put(id, l);
if (error != null) {
if (error instanceof IErrorReport &&
((IErrorReport)error).getErrorCode() == IErrorReport.TCF_ERROR_INV_COMMAND) {
runTest();
return;
}
exit(error);
}
else if (props == null) {
exit(new Exception("Invalid symbol location info: props = null"));
}
else {
List<Object> cmds = (List<Object>)props.get(ISymbols.LOC_VALUE_CMDS);
if (cmds == null || cmds.size() == 0) {
exit(new Exception("Invalid symbol location info: ValueCmds = null"));
}
else {
runTest();
}
}
}
}));
if (rnd.nextInt(16) == 0) return;
}
}
for (final String id : local_var_ids.values()) {
if (id != null && local_var_location.get(id) == null) {
cmds.add(srv_syms.getLocationInfo(id, new ISymbols.DoneGetLocationInfo() {
public void doneGetLocationInfo(IToken token, Exception error, Map<String, Object> props) {
cmds.remove(token);
SymbolLocation l = new SymbolLocation();
l.error = error;
l.props = props;
local_var_location.put(id, l);
List<Object> cmds = null;
if (props != null) cmds = (List<Object>)props.get(ISymbols.LOC_VALUE_CMDS);
if (error != null) {
if (error instanceof IErrorReport &&
((IErrorReport)error).getErrorCode() == IErrorReport.TCF_ERROR_INV_COMMAND) {
runTest();
return;
}
exit(error);
}
else if (cmds == null || cmds.size() == 0) {
exit(new Exception("Invalid symbol location info"));
}
else {
runTest();
}
}
}));
if (rnd.nextInt(16) == 0) return;
}
}
}
for (final String txt : test_expressions) {
if (local_var_expr_ids.length == 0) {
// Debug info not available
if (txt.indexOf("func2_local") >= 0) continue;
if (txt.indexOf("test_struct") >= 0) continue;
if (txt.indexOf("tcf_test_array") >= 0) continue;
if (txt.indexOf("(char *)") >= 0) continue;
if (txt.indexOf("enum_val") >= 0) continue;
}
if (local_var_expr_ids.length == 0 || global_var_ids.get("tcf_cpp_test_bool") == null) {
// Agent is not build with C++ compiler
if (txt.indexOf("tcf_cpp_test") >= 0) continue;
if (txt.indexOf("(bool)") >= 0) continue;
}
boolean vars_ok = true;
for (String nm : global_var_names) {
if (txt.indexOf(nm) >= 0 && global_var_ids.get(nm) == null) vars_ok = false;
}
if (!vars_ok) continue;
if (expr_ctx.get(txt) == null) {
testSymbolsFlushEvents();
if (rnd.nextBoolean()) {
Map<String,Object> scope = new HashMap<String,Object>();
scope.put(IExpressions.SCOPE_CONTEXT_ID, stack_trace[stack_trace.length - 2]);
if (rnd.nextBoolean()) scope.put(IExpressions.SCOPE_ADDRESS, sym_func3.getValue());
cmds.add(srv_expr.createInScope(scope, txt, new IExpressions.DoneCreate() {
public void doneCreate(IToken token, Exception error, IExpressions.Expression ctx) {
cmds.remove(token);
if (error instanceof IErrorReport && ((IErrorReport)error).getErrorCode() == IErrorReport.TCF_ERROR_INV_COMMAND) {
// Command not implemented, retry
runTest();
}
else if (error != null) {
exit(error);
}
else {
expr_to_dispose.add(ctx.getID());
expr_ctx.put(txt, ctx);
runTest();
}
}
}));
}
else {
cmds.add(srv_expr.create(stack_trace[stack_trace.length - 2], null, txt, new IExpressions.DoneCreate() {
public void doneCreate(IToken token, Exception error, IExpressions.Expression ctx) {
cmds.remove(token);
if (error != null) {
exit(error);
}
else {
expr_to_dispose.add(ctx.getID());
expr_ctx.put(txt, ctx);
runTest();
}
}
}));
}
if (rnd.nextInt(16) == 0) return;
}
}
for (final String id : local_var_expr_ids) {
if (expr_val.get(id) == null) {
testSymbolsFlushEvents();
cmds.add(srv_expr.evaluate(id, new IExpressions.DoneEvaluate() {
public void doneEvaluate(IToken token, Exception error, IExpressions.Value ctx) {
cmds.remove(token);
if (error != null) {
exit(error);
}
else {
expr_val.put(id, ctx);
runTest();
}
}
}));
if (rnd.nextInt(16) == 0) return;
}
}
for (final String id : expr_ctx.keySet()) {
if (expr_val.get(id) == null) {
testSymbolsFlushEvents();
cmds.add(srv_expr.evaluate(expr_ctx.get(id).getID(), new IExpressions.DoneEvaluate() {
public void doneEvaluate(IToken token, Exception error, IExpressions.Value ctx) {
cmds.remove(token);
if (error != null) {
exit(error);
}
else {
expr_val.put(id, ctx);
byte[] arr = ctx.getValue();
boolean b = false;
for (byte x : arr) {
if (x != 0) b = true;
}
if (!b) exit(new Exception("Invalid value of expression \"" + id + "\""));
runTest();
}
}
}));
if (rnd.nextInt(16) == 0) return;
}
}
if (srv_syms != null) {
for (final String id : expr_val.keySet()) {
if (expr_sym.get(id) == null) {
IExpressions.Value v = expr_val.get(id);
String type_id = v.getTypeID();
if (type_id != null) {
testSymbolsFlushEvents();
cmds.add(srv_syms.getContext(type_id, new ISymbols.DoneGetContext() {
public void doneGetContext(IToken token, Exception error, ISymbols.Symbol ctx) {
cmds.remove(token);
if (error != null) {
exit(error);
}
else if (ctx == null) {
exit(new Exception("Symbol.getContext returned null"));
}
else {
expr_sym.put(id, ctx);
runTest();
}
}
}));
if (rnd.nextInt(16) == 0) return;
}
}
}
for (final String id : expr_sym.keySet()) {
if (expr_chld.get(id) == null) {
testSymbolsFlushEvents();
ISymbols.Symbol sym = expr_sym.get(id);
cmds.add(srv_syms.getChildren(sym.getID(), new ISymbols.DoneGetChildren() {
public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
cmds.remove(token);
if (error != null) {
exit(error);
}
else {
if (context_ids == null) context_ids = new String[0];
expr_chld.put(id, context_ids);
runTest();
}
}
}));
if (rnd.nextInt(16) == 0) return;
}
}
}
if (cmds.size() > 0) return;
if (srv_dprintf != null && !dprintf_done && local_var_expr_ids.length > 0) {
cmds.add(srv_dprintf.open(null, new IDPrintf.DoneCommandOpen() {
int test_cnt;
int char_cnt;
@Override
public void doneCommandOpen(IToken token, Exception error, final String id) {
cmds.remove(token);
if (error != null) {
exit(error);
return;
}
cmds.add(srv_streams.connect(id, new IStreams.DoneConnect() {
@Override
public void doneConnect(IToken token, Exception error) {
cmds.remove(token);
if (error != null) {
exit(error);
return;
}
}
}));
cmds.add(srv_streams.read(id, 256, new IStreams.DoneRead() {
@Override
public void doneRead(IToken token, Exception error, int lost_size, byte[] data, boolean eos) {
cmds.remove(token);
if (error != null) {
exit(error);
return;
}
if (eos) {
exit(new Exception("Unexpected EOS"));
return;
}
for (byte b : data) {
while (test_dprintfs[test_cnt * 2 + 1] == null || test_dprintfs[test_cnt * 2 + 1].length() == 0) test_cnt++;
char ch = test_dprintfs[test_cnt * 2 + 1].charAt(char_cnt++);
if (b != ch) {
exit(new Exception("Invalid output of $printf: " + test_dprintfs[test_cnt * 2 + 1] + " -> " + new String(data)));
return;
}
if (char_cnt == test_dprintfs[test_cnt * 2 + 1].length()) {
char_cnt = 0;
test_cnt++;
}
}
if (test_cnt >= test_dprintfs.length / 2) {
cmds.add(srv_streams.disconnect(id, new IStreams.DoneDisconnect() {
@Override
public void doneDisconnect(IToken token, Exception error) {
cmds.remove(token);
if (error != null) {
exit(error);
return;
}
runTest();
}
}));
}
else {
cmds.add(srv_streams.read(id, 256, this));
}
}
}));
for (int n = 0; n < test_dprintfs.length; n += 2) {
final String txt = test_dprintfs[n];
final String res = test_dprintfs[n + 1];
testSymbolsFlushEvents();
cmds.add(srv_expr.create(stack_trace[stack_trace.length - 2], null, txt, new IExpressions.DoneCreate() {
public void doneCreate(IToken token, Exception error, IExpressions.Expression ctx) {
cmds.remove(token);
if (error != null) {
if (res != null) exit(error);
}
else {
if (res == null) exit(new Exception("Expressions service was expected to return error: " + txt));
testSymbolsFlushEvents();
expr_to_dispose.add(ctx.getID());
cmds.add(srv_expr.evaluate(ctx.getID(), new IExpressions.DoneEvaluate() {
@Override
public void doneEvaluate(IToken token, Exception error, Value value) {
cmds.remove(token);
if (error != null) {
exit(error);
return;
}
}
}));
}
}
}));
}
}
}));
dprintf_done = true;
return;
}
for (final String id : expr_to_dispose) {
cmds.add(srv_expr.dispose(id, new IExpressions.DoneDispose() {
public void doneDispose(IToken token, Exception error) {
cmds.remove(token);
if (error != null) {
exit(error);
}
else {
expr_to_dispose.remove(id);
runTest();
}
}
}));
}
if (cmds.size() > 0) return;
test_done = true;
}
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;
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("TestExpression", 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);
}
}
});
}
private void exit(Throwable x) {
if (!test_suite.isActive(this)) return;
srv_expr.removeListener(this);
srv_bp.removeListener(this);
srv_rc.removeListener(this);
test_suite.done(this, x);
}
//--------------------------- Run Control listener ---------------------------//
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) {
contextSuspended(id, null, null, null);
}
}
public void contextAdded(IRunControl.RunControlContext[] contexts) {
}
public void contextChanged(IRunControl.RunControlContext[] contexts) {
}
public void contextException(String context, String msg) {
if (test_done) return;
IRunControl.RunControlContext ctx = test_rc.getContext(context);
if (ctx != null) {
if (test_ctx_id == null) return;
String p = ctx.getParentID();
String c = ctx.getCreatorID();
if (!test_ctx_id.equals(c) && !test_ctx_id.equals(p)) return;
exit(new Exception("Context exception: " + msg));
}
}
public void contextRemoved(String[] context_ids) {
for (String id : context_ids) {
if (id.equals(test_ctx_id)) {
if (test_done) {
srv_bp.set(null, new IBreakpoints.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
exit(error);
}
});
}
else {
exit(new Exception("Test process exited too soon"));
}
return;
}
}
}
public void contextResumed(String id) {
if (id.equals(thread_id)) {
if (run_to_bp_done && !test_done) {
assert thread_ctx != null;
assert !canResume(thread_id);
exit(new Exception("Unexpected contextResumed event: " + id));
}
suspended_pc = null;
}
}
public void contextSuspended(String id, String pc, String reason, Map<String,Object> params) {
assert id != null;
if (id.equals(thread_id) && waiting_suspend) {
suspended_pc = pc;
waiting_suspend = false;
runTest();
}
}
//--------------------------- Expressions listener ---------------------------//
public void valueChanged(String id) {
}
//--------------------------- Breakpoints listener ---------------------------//
@SuppressWarnings("unchecked")
public void breakpointStatusChanged(String id, Map<String,Object> status) {
if (id.equals(bp_id) && process_id != null && !test_done) {
String s = (String)status.get(IBreakpoints.STATUS_ERROR);
if (s != null) exit(new Exception("Invalid BP status: " + s));
Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES);
if (list == null) return;
String err = null;
for (Map<String,Object> map : list) {
String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT);
if (process_id.equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null)
err = (String)map.get(IBreakpoints.INSTANCE_ERROR);
}
if (err != null) exit(new Exception("Invalid BP status: " + err));
}
}
public void contextAdded(Map<String,Object>[] bps) {
}
public void contextChanged(Map<String,Object>[] bps) {
}
//----------------------------- IDiagTestDone listener -------------------------//
@Override
public void testDone(String id) {
if (id.equals(test_ctx_id)) {
if (test_done) {
srv_bp.set(null, new IBreakpoints.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
exit(error);
}
});
}
else {
exit(new Exception("Test process exited too soon"));
}
}
}
}