/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.tele.debug; import static java.io.StreamTokenizer.*; import java.io.*; import java.util.*; import com.sun.cri.ci.*; import com.sun.max.tele.*; import com.sun.max.unsafe.*; /** * Simple conditional breakpoints. */ public class BreakpointCondition extends AbstractVmHolder implements VMTriggerEventHandler { private String condition; private StreamTokenizer streamTokenizer; private TeleIntegerRegisters integerRegisters; private static Map<String, CiRegister> integerRegisterSymbols; private Expression expression; public BreakpointCondition(TeleVM vm, String condition) throws ExpressionException { super(vm); this.condition = condition; if (integerRegisterSymbols == null) { integerRegisterSymbols = new HashMap<String, CiRegister>(); for (CiRegister reg : TeleIntegerRegisters.getIntegerRegisters()) { integerRegisterSymbols.put(reg.name.toUpperCase(), reg); integerRegisterSymbols.put(reg.name.toLowerCase(), reg); } } this.expression = parse(); } public boolean handleTriggerEvent(TeleNativeThread teleNativeThread) { return evaluate(vm().teleProcess(), teleNativeThread); } public boolean evaluate(TeleProcess teleProcess, TeleNativeThread teleNativeThread) { if (expression == null) { return false; } integerRegisters = teleNativeThread.registers().teleIntegerRegisters(); if (integerRegisters == null) { return false; } try { final Expression result = expression.evaluate(); if (result instanceof BooleanExpression) { return ((BooleanExpression) result).value(); } return false; } catch (ExpressionException ex) { return false; } } private void initializeTokenizer() { streamTokenizer = new StreamTokenizer(new StringReader(condition)); streamTokenizer.ordinaryChars('0', '9'); streamTokenizer.wordChars('0', '9'); } Expression parse() throws ExpressionException { try { initializeTokenizer(); streamTokenizer.nextToken(); final Expression result = parseExpression(); // Must be empty or a BinaryExpr if (result == null || result instanceof BinaryExpression) { return result; } throw new ExpressionException("expression must return boolean result"); } catch (IOException ex) { throw new ExpressionException("syntax error"); } } // CheckStyle: stop inner assignment checks Expression parseExpression() throws ExpressionException, IOException { Expression result = null; while (streamTokenizer.ttype != TT_EOF) { Operator op; Expression expression = null; if (streamTokenizer.ttype == '(') { streamTokenizer.nextToken(); expression = parseExpression(); streamTokenizer.nextToken(); expectChar(')'); } else if (streamTokenizer.ttype == '[') { expression = expectMemory(); } else if ((expression = isRegister()) != null) { // } else if ((expression = isNumber()) != null) { // } else if ((op = isBinaryOperator()) != null) { if (result == null) { throw new ExpressionException("no left operand for binary operator"); } streamTokenizer.nextToken(); expression = new BinaryExpression(op, result, parseExpression()); } result = expression; streamTokenizer.nextToken(); } return result; } // CheckStyle: resume inner assignment checks private RegisterExpression isRegister() { if (streamTokenizer.ttype == TT_WORD) { final CiRegister register = integerRegisterSymbols.get(streamTokenizer.sval); if (register != null) { return new RegisterExpression(register); } } return null; } private Operator isBinaryOperator() throws IOException, ExpressionException { switch (streamTokenizer.ttype) { case '+': return Operator.PLUS; case '-': return Operator.MINUS; case '=': streamTokenizer.nextToken(); if (streamTokenizer.ttype == '=') { return Operator.EQ; } streamTokenizer.pushBack(); break; case '!': streamTokenizer.nextToken(); if (streamTokenizer.ttype == '=') { return Operator.NE; } streamTokenizer.pushBack(); break; case '>': streamTokenizer.nextToken(); if (streamTokenizer.ttype == '=') { return Operator.GE; } streamTokenizer.pushBack(); return Operator.GT; case '<': streamTokenizer.nextToken(); if (streamTokenizer.ttype == '=') { return Operator.LE; } streamTokenizer.pushBack(); return Operator.LT; case '&': streamTokenizer.nextToken(); if (streamTokenizer.ttype == '&') { return Operator.LOGAND; } streamTokenizer.pushBack(); break; case '|': streamTokenizer.nextToken(); if (streamTokenizer.ttype == '|') { return Operator.LOGOR; } streamTokenizer.pushBack(); break; default: break; } return null; } private Expression expectMemory() throws ExpressionException, IOException { if (streamTokenizer.ttype == '[') { Expression result = null; streamTokenizer.nextToken(); final Expression addressExpression = expectRegisterOrNumber(); streamTokenizer.nextToken(); final Operator op = isBinaryOperator(); if (op != null) { if (op == Operator.PLUS || op == Operator.MINUS) { streamTokenizer.nextToken(); final Expression addressOffsetExpression = expectRegisterOrNumber(); if (addressExpression instanceof RegisterExpression && addressOffsetExpression instanceof NumberExpression) { result = new OffsetRegisterMemoryExpression((RegisterExpression) addressExpression, op, (NumberExpression) addressOffsetExpression); } else if (addressOffsetExpression instanceof RegisterExpression && addressExpression instanceof NumberExpression) { result = new OffsetRegisterMemoryExpression((RegisterExpression) addressOffsetExpression, op, (NumberExpression) addressExpression); } else if (addressOffsetExpression instanceof NumberExpression && addressExpression instanceof NumberExpression) { Address address1 = Address.fromLong(((NumberExpression) addressExpression).value()); final Address address2 = Address.fromLong(((NumberExpression) addressOffsetExpression).value()); switch (op) { case PLUS: address1 = address1.plus(address2); break; case MINUS: address1 = address1.minus(address2); break; default: throw new ExpressionException("illegal operator in memory expression"); } result = new AddressMemoryExpression(address1); } else { throw new ExpressionException("illegal memory expression"); } streamTokenizer.nextToken(); } else { streamTokenizer.pushBack(); } } else { if (addressExpression instanceof RegisterExpression) { result = new RegisterMemoryExpression((RegisterExpression) addressExpression); } else { result = new AddressMemoryExpression(Address.fromLong(((NumberExpression) addressExpression).value())); } } expectChar(']'); return result; } throw new ExpressionException("syntax error: [ expected"); } private void expectChar(char ch) throws ExpressionException { if (streamTokenizer.ttype != ch) { throw new ExpressionException("syntax error: " + ch + " expected"); } } private Expression expectRegisterOrNumber() throws ExpressionException { Expression result = isRegister(); if (result != null) { return result; } result = isNumber(); if (result != null) { return result; } throw new ExpressionException("register or number expected"); } private Expression isNumber() { if (streamTokenizer.ttype == TT_WORD) { final int length = streamTokenizer.sval.length(); if (length > 0) { final char firstChar = streamTokenizer.sval.charAt(0); if ((firstChar >= '0') && (firstChar <= '9')) { final boolean isHex = (firstChar == '0') && (length > 1 && Character.toUpperCase(streamTokenizer.sval.charAt(1)) == 'X'); final int radix = isHex ? 16 : 10; String toParse = streamTokenizer.sval; if (isHex) { toParse = toParse.substring(2); } return new NumberExpression(Long.parseLong(toParse, radix)); } } } return null; } @Override public String toString() { return condition; } public static class ExpressionException extends Exception { ExpressionException(String s) { super(s); } } abstract static class Expression { private static final String ILLEGAL_OPERATION = "illegal operator for type"; abstract Expression evaluate() throws ExpressionException; boolean lt(Object other) throws ExpressionException { throw new ExpressionException(ILLEGAL_OPERATION); } boolean le(Object other) throws ExpressionException { throw new ExpressionException(ILLEGAL_OPERATION); } boolean ge(Object other) throws ExpressionException { throw new ExpressionException(ILLEGAL_OPERATION); } boolean gt(Object other) throws ExpressionException { throw new ExpressionException(ILLEGAL_OPERATION); } } class BinaryExpression extends Expression { private Operator operator; private Expression left; private Expression right; BinaryExpression(Operator operator, Expression left, Expression right) { this.operator = operator; this.left = left; this.right = right; } Operator operator() { return operator; } Expression left() { return left; } Expression right() { return right; } @Override Expression evaluate() throws ExpressionException { final Expression leftValue = left.evaluate(); final Expression rightValue = right.evaluate(); switch (operator) { case LT: return new BooleanExpression(leftValue.lt(rightValue)); case LE: return new BooleanExpression(leftValue.le(rightValue)); case EQ: return new BooleanExpression(leftValue.equals(rightValue)); case NE: return new BooleanExpression(!leftValue.equals(rightValue)); case GE: return new BooleanExpression(leftValue.ge(rightValue)); case GT: return new BooleanExpression(leftValue.gt(rightValue)); case LOGAND: case LOGOR: final BooleanExpression leftBoolean = checkBoolean(leftValue); final BooleanExpression rightBoolean = checkBoolean(rightValue); if (operator == Operator.LOGAND) { return new BooleanExpression(leftBoolean.value() && rightBoolean.value()); } return new BooleanExpression(leftBoolean.value() || rightBoolean.value()); default: } return new BooleanExpression(false); } private BooleanExpression checkBoolean(Expression expr) throws ExpressionException { if (expr instanceof BooleanExpression) { return (BooleanExpression) expr; } throw new ExpressionException("operands must be boolean"); } } class RegisterExpression extends Expression { CiRegister register; RegisterExpression(CiRegister register) { this.register = register; } CiRegister register() { return register; } @Override Expression evaluate() { return new NumberExpression(integerRegisters.getValue(register).toLong()); } @Override public boolean equals(Object other) { if (other instanceof RegisterExpression) { final RegisterExpression otherNumber = (RegisterExpression) other; return evaluate().equals(otherNumber.evaluate()); } return false; } } class NumberExpression extends Expression { long value; NumberExpression(long value) { this.value = value; } long value() { return value; } @Override Expression evaluate() { return this; } @Override public boolean equals(Object other) { if (other instanceof NumberExpression) { final NumberExpression otherNumber = (NumberExpression) other; return otherNumber.value == value; } return false; } @Override boolean lt(Object other) { if (other instanceof NumberExpression) { final NumberExpression otherNumber = (NumberExpression) other; return otherNumber.value < value; } return false; } @Override boolean le(Object other) { if (other instanceof NumberExpression) { final NumberExpression otherNumber = (NumberExpression) other; return otherNumber.value <= value; } return false; } @Override boolean gt(Object other) { if (other instanceof NumberExpression) { final NumberExpression otherNumber = (NumberExpression) other; return otherNumber.value > value; } return false; } @Override boolean ge(Object other) { if (other instanceof NumberExpression) { final NumberExpression otherNumber = (NumberExpression) other; return otherNumber.value >= value; } return false; } } static class BooleanExpression extends Expression { boolean value; BooleanExpression(boolean value) { this.value = value; } boolean value() { return value; } @Override Expression evaluate() { return this; } } abstract static class MemoryExpression extends Expression { } class AddressMemoryExpression extends MemoryExpression { Address address; AddressMemoryExpression(Address address) { this.address = address; } void setAddress(Address address) { this.address = address; } Address address() { return address; } @Override Expression evaluate() { return new NumberExpression(memory().readLong(address)); } } class RegisterMemoryExpression extends MemoryExpression { RegisterExpression registerExpression; AddressMemoryExpression addressMemoryExpression = new AddressMemoryExpression(Address.zero()); RegisterMemoryExpression(RegisterExpression registerExpression) { this.registerExpression = registerExpression; } @Override Expression evaluate() { final NumberExpression addressExpression = (NumberExpression) registerExpression.evaluate(); addressMemoryExpression.setAddress(Address.fromLong(addressExpression.value())); return addressMemoryExpression.evaluate(); } } class OffsetRegisterMemoryExpression extends RegisterMemoryExpression { int offset; Operator operator; OffsetRegisterMemoryExpression(RegisterExpression registerExpression, Operator operator, NumberExpression offset) { super(registerExpression); this.offset = (int) offset.value(); this.operator = operator; } @Override Expression evaluate() { final NumberExpression addressExpression = (NumberExpression) registerExpression.evaluate(); Address address = Address.fromLong(addressExpression.value()); switch (operator) { case PLUS: address = address.plus(offset); break; case MINUS: address = address.minus(offset); break; default: } return new AddressMemoryExpression(address).evaluate(); } } enum Operator { LT, LE, EQ, NE, GE, GT, PLUS, MINUS, LOGAND, LOGOR } }