/******************************************************************************* * Copyright (c) 2010, 2016 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.events; import java.util.StringTokenizer; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; import org.eclipse.cdt.dsf.mi.service.command.output.MIStreamRecord; /** * @since 3.0 */ public class MICatchpointHitEvent extends MIBreakpointHitEvent { /** * See {@link #getReason()} */ private String fReason; /** @since 5.0 */ protected MICatchpointHitEvent(IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame, String bkptno, String reason) { super(ctx, token, results, frame, bkptno); fReason = reason; } /** * Returns the event the cachpoint caught. E.g., a C++ exception was thrown. * This string comes either from gdb when the catchpoint is hit, or it's the * gdb catchpoint keyword ('catch', 'throw', 'fork', etc) that was used to * set the catchpoint */ public String getReason() { return fReason; } /** * This variant is for catchpoint-hit in gdb < 7.0. For those versions, gdb * sends us a stopped event, but it doesn't include a reason in it. * Fortunately, it does output a stream record that tells us not only that a * catchpoint was hit, but what its breakpoint number is. * * @param streamRecord * the stream record that reveals that a catchpoint was hit and * what the event was */ public static MIBreakpointHitEvent parse(IExecutionDMContext dmc, int token, MIResult[] results, MIStreamRecord streamRecord) { // stream record example: "Catchpoint 1 (exception caught)" StringTokenizer tokenizer = new StringTokenizer(streamRecord.getString()); tokenizer.nextToken(); // "Catchpoint" try { String bkptNumber = tokenizer.nextToken(); // "1" StringBuilder reason = new StringBuilder(); boolean first = true; while (tokenizer.hasMoreElements()) { if (!first) { reason.append(" "); //$NON-NLS-1$ ok; technically, the delim could be any whitespace, but we know it's s a space char } reason.append(tokenizer.nextElement()); first = false; } // remove the parentheses if (reason.charAt(0) == '(') { reason.deleteCharAt(0); } if (reason.charAt(reason.length()-1) == ')') { reason.deleteCharAt(reason.length()-1); } MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); return new MICatchpointHitEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), bkptNumber, reason.toString()); } catch (NumberFormatException exc) { assert false : "unexpected catchpoint stream record format: " + streamRecord.getString(); //$NON-NLS-1$ return null; } } /** * This variant is for a catchpoint-hit in gdb >= 7.0. * {@link MIBreakpointHitEvent#parse(IExecutionDMContext, int, MIResult[], MIStreamRecord[]) * delegates to us if it determines that the breakpoint hit was actually * caused by catchpoint. In this case, we use the event keyword used to set * the catchpoint as the reason (e.g., "catch", "throw"), whereas in the gdb * < 7.0 case we use the reason provided in the stream record (e.g., * "exception caught"). The inconsistency is fine. The user will get the * insight he needs either way. * * @since 5.0 */ public static MICatchpointHitEvent parse(IExecutionDMContext dmc, int token, MIResult[] results, String bkptNumber, String gdbKeyword) { MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); return new MICatchpointHitEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), bkptNumber, gdbKeyword); } }