⛏️ index : haiku.git

/*
 * Copyright 2013-2015, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */
#include "ExceptionStopConfigView.h"

#include <CheckBox.h>
#include <LayoutBuilder.h>

#include <AutoDeleter.h>
#include <AutoLocker.h>

#include "FunctionInstance.h"
#include "Image.h"
#include "ImageDebugInfo.h"
#include "MessageCodes.h"
#include "UserInterface.h"
#include "Team.h"


enum {
	MSG_STOP_ON_THROWN_EXCEPTION_CHANGED	= 'stec',
	MSG_STOP_ON_CAUGHT_EXCEPTION_CHANGED	= 'scec',
};


ExceptionStopConfigView::ExceptionStopConfigView(::Team* team,
	UserInterfaceListener* listener)
	:
	BGroupView(B_VERTICAL),
	fTeam(team),
	fListener(listener),
	fExceptionThrown(NULL),
	fExceptionCaught(NULL)
{
	SetName("Exceptions");
}


ExceptionStopConfigView::~ExceptionStopConfigView()
{
}


ExceptionStopConfigView*
ExceptionStopConfigView::Create(::Team* team, UserInterfaceListener* listener)
{
	ExceptionStopConfigView* self = new ExceptionStopConfigView(
		team, listener);

	try {
		self->_Init();
	} catch (...) {
		delete self;
		throw;
	}

	return self;

}


void
ExceptionStopConfigView::AttachedToWindow()
{
	fExceptionThrown->SetTarget(this);
	fExceptionCaught->SetTarget(this);

	AutoLocker< ::Team> teamLocker(fTeam);
	_UpdateExceptionState();

	BGroupView::AttachedToWindow();
}


void
ExceptionStopConfigView::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case MSG_STOP_ON_THROWN_EXCEPTION_CHANGED:
		{
			_UpdateThrownBreakpoints(fExceptionThrown->Value()
				== B_CONTROL_ON);
			break;
		}
		case MSG_STOP_ON_CAUGHT_EXCEPTION_CHANGED:
		{
			break;
		}
		default:
			BGroupView::MessageReceived(message);
			break;
	}

}


void
ExceptionStopConfigView::_Init()
{
	BLayoutBuilder::Group<>(this, B_VERTICAL)
		.SetInsets(B_USE_DEFAULT_SPACING)
		.AddGlue()
		.Add(fExceptionThrown = new BCheckBox("exceptionThrown",
			"Stop when an exception is thrown",
			new BMessage(MSG_STOP_ON_THROWN_EXCEPTION_CHANGED)))
		.Add(fExceptionCaught = new BCheckBox("exceptionCaught",
			"Stop when an exception is caught",
			new BMessage(MSG_STOP_ON_CAUGHT_EXCEPTION_CHANGED)))
		.AddGlue();

	// TODO: enable once implemented
	fExceptionCaught->SetEnabled(false);
}


void
ExceptionStopConfigView::_UpdateThrownBreakpoints(bool enable)
{
	AutoLocker< ::Team> teamLocker(fTeam);
	for (ImageList::ConstIterator it = fTeam->Images().GetIterator();
		it.HasNext();) {
		Image* image = it.Next();

		ImageDebugInfo* info = image->GetImageDebugInfo();
		target_addr_t address;
		if (_FindExceptionFunction(info, address) != B_OK)
			continue;

		if (enable)
			fListener->SetBreakpointRequested(address, true, true);
		else
			fListener->ClearBreakpointRequested(address);
	}
}


status_t
ExceptionStopConfigView::_FindExceptionFunction(ImageDebugInfo* info,
	target_addr_t& _foundAddress) const
{
	if (info != NULL) {
		FunctionInstance* instance = info->FunctionByName(
			"__cxa_allocate_exception");
		if (instance == NULL)
			instance = info->FunctionByName("__throw(void)");

		if (instance != NULL) {
			_foundAddress = instance->Address();
			return B_OK;
		}
	}

	return B_NAME_NOT_FOUND;
}


void
ExceptionStopConfigView::_UpdateExceptionState()
{
	// check if the exception breakpoints are already installed
	for (ImageList::ConstIterator it = fTeam->Images().GetIterator();
		it.HasNext();) {
		Image* image = it.Next();

		ImageDebugInfo* info = image->GetImageDebugInfo();
		target_addr_t address;
		if (_FindExceptionFunction(info, address) != B_OK)
			continue;

		if (fTeam->BreakpointAtAddress(address) != NULL) {
			fExceptionThrown->SetValue(B_CONTROL_ON);
			break;
		}
	}
}