//----------------------------------------------------------------------------
//
// Breakpoint handling functions.
//
// Copyright (C) Microsoft Corporation, 1997-2002.
//
//----------------------------------------------------------------------------

#include "ntsdp.hpp"

// Currently used only to watch for list changes when
// doing callbacks for breakpoint hit notifications.
BOOL g_BreakpointListChanged;

// Always update data breakpoints the very first time in
// order to flush out any stale data breakpoints.
BOOL g_UpdateDataBreakpoints = TRUE;
BOOL g_DataBreakpointsChanged;
BOOL g_BreakpointsSuspended;

Breakpoint* g_StepTraceBp;      // Trace breakpoint.
CHAR g_StepTraceCmdState;
Breakpoint* g_DeferBp;          // Deferred breakpoint.
BOOL g_DeferDefined;            // TRUE if deferred breakpoint is active.

Breakpoint* g_LastBreakpointHit;
ADDR g_LastBreakpointHitPc;

HRESULT
BreakpointInit(void)
{
    // These breakpoints are never put in any list so their
    // IDs can be anything.  Pick unusual numbers to make them
    // easy to identify when debugging the debugger.
    g_StepTraceBp = new
        CodeBreakpoint(NULL, 0xffff0000, IMAGE_FILE_MACHINE_UNKNOWN);
    g_StepTraceCmdState = 't';
    g_DeferBp = new
        CodeBreakpoint(NULL, 0xffff0001, IMAGE_FILE_MACHINE_UNKNOWN);
    if (g_StepTraceBp == NULL ||
        g_DeferBp == NULL)
    {
        delete g_StepTraceBp;
        g_StepTraceBp = NULL;
        delete g_DeferBp;
        g_DeferBp = NULL;
        return E_OUTOFMEMORY;
    }

    return S_OK;
}

//----------------------------------------------------------------------------
//
// Breakpoint.
//
//----------------------------------------------------------------------------

Breakpoint::Breakpoint(DebugClient* Adder, ULONG Id, ULONG Type,
                       ULONG ProcType)
{
    m_Next = NULL;
    m_Prev = NULL;
    m_Refs = 1;
    m_Id = Id;
    m_BreakType = Type;
    // Breakpoints are always created disabled since they
    // are not initialized at the time of creation.
    m_Flags = 0;
    m_CodeFlags = IBI_DEFAULT;
    ADDRFLAT(&m_Addr, 0);
    // Initial data parameters must be set to something
    // valid so that Validate calls will allow the offset
    // to be changed.
    m_DataSize = 1;
    m_DataAccessType = DEBUG_BREAK_EXECUTE;
    m_PassCount = 1;
    m_CurPassCount = 1;
    m_CommandLen = 0;
    m_Command = NULL;
    m_MatchThread = NULL;
    m_Process = g_Process;
    m_OffsetExprLen = 0;
    m_OffsetExpr = NULL;
    m_Adder = Adder;
    m_MatchThreadData = 0;
    m_MatchProcessData = 0;

    SetProcType(ProcType);

    if (m_BreakType == DEBUG_BREAKPOINT_DATA)
    {
        g_DataBreakpointsChanged = TRUE;
    }
}

Breakpoint::~Breakpoint(void)
{
    ULONG i;

    // There used to be an assert here checking that
    // the inserted flag wasn't set before a breakpoint
    // structure was deleted.  However, the inserted flag
    // might still be set at this point if a breakpoint
    // restore failed, so the assert is not valid.

    if (m_BreakType == DEBUG_BREAKPOINT_DATA)
    {
        g_DataBreakpointsChanged = TRUE;
    }

    // Make sure stale pointers aren't left in the
    // go breakpoints array.  This can happen if
    // a process exits or the target reboots while
    // go breakpoints are active.
    for (i = 0; i < g_NumGoBreakpoints; i++)
    {
        if (g_GoBreakpoints[i] == this)
        {
            g_GoBreakpoints[i] = NULL;
        }
    }

    if (this == g_LastBreakpointHit)
    {
        g_LastBreakpointHit = NULL;
    }

    // Take this item out of the list if necessary.
    if (m_Flags & BREAKPOINT_IN_LIST)
    {
        UnlinkFromList();
    }

    delete [] (PSTR)m_Command;
    delete [] (PSTR)m_OffsetExpr;
}

STDMETHODIMP
Breakpoint::QueryInterface(
    THIS_
    IN REFIID InterfaceId,
    OUT PVOID* Interface
    )
{
    *Interface = NULL;

    // Interface specific casts are necessary in order to
    // get the right vtable pointer in our multiple
    // inheritance scheme.
    if (DbgIsEqualIID(InterfaceId, IID_IUnknown) ||
        DbgIsEqualIID(InterfaceId, IID_IDebugBreakpoint))
    {
        *Interface = (IDebugBreakpoint *)this;
        AddRef();
        return S_OK;
    }
    else
    {
        return E_NOINTERFACE;
    }
}

STDMETHODIMP_(ULONG)
Breakpoint::AddRef(
    THIS
    )
{
    // This object's lifetime is not controlled by
    // the interface.
    return 1;
}

STDMETHODIMP_(ULONG)
Breakpoint::Release(
    THIS
    )
{
    // This object's lifetime is not controlled by
    // the interface.
    return 0;
}

STDMETHODIMP
Breakpoint::GetId(
    THIS_
    OUT PULONG Id
    )
{
    ENTER_ENGINE();

    *Id = m_Id;

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::GetType(
    THIS_
    OUT PULONG BreakType,
    OUT PULONG ProcType
    )
{
    ENTER_ENGINE();

    *BreakType = m_BreakType;
    *ProcType = m_ProcType;

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::GetAdder(
    THIS_
    OUT PDEBUG_CLIENT* Adder
    )
{
    ENTER_ENGINE();

    *Adder = (PDEBUG_CLIENT)m_Adder;
    m_Adder->AddRef();

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::GetFlags(
    THIS_
    OUT PULONG Flags
    )
{
    ENTER_ENGINE();

    *Flags = m_Flags & BREAKPOINT_EXTERNAL_FLAGS;

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::AddFlags(
    THIS_
    IN ULONG Flags
    )
{
    if (Flags & ~BREAKPOINT_EXTERNAL_MODIFY_FLAGS)
    {
        return E_INVALIDARG;
    }

    ENTER_ENGINE();

    m_Flags |= Flags;

    if (m_BreakType == DEBUG_BREAKPOINT_DATA)
    {
        g_DataBreakpointsChanged = TRUE;
    }

    UpdateInternal();
    NotifyChanged();

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::RemoveFlags(
    THIS_
    IN ULONG Flags
    )
{
    if (Flags & ~BREAKPOINT_EXTERNAL_MODIFY_FLAGS)
    {
        return E_INVALIDARG;
    }

    ENTER_ENGINE();

    m_Flags &= ~Flags;

    if (m_BreakType == DEBUG_BREAKPOINT_DATA)
    {
        g_DataBreakpointsChanged = TRUE;
    }

    UpdateInternal();
    NotifyChanged();

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::SetFlags(
    THIS_
    IN ULONG Flags
    )
{
    if (Flags & ~BREAKPOINT_EXTERNAL_MODIFY_FLAGS)
    {
        return E_INVALIDARG;
    }

    ENTER_ENGINE();

    m_Flags = (m_Flags & ~BREAKPOINT_EXTERNAL_MODIFY_FLAGS) |
        (Flags & BREAKPOINT_EXTERNAL_MODIFY_FLAGS);

    if (m_BreakType == DEBUG_BREAKPOINT_DATA)
    {
        g_DataBreakpointsChanged = TRUE;
    }

    UpdateInternal();
    NotifyChanged();

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::GetOffset(
    THIS_
    OUT PULONG64 Offset
    )
{
    if (m_Flags & DEBUG_BREAKPOINT_DEFERRED)
    {
        return E_NOINTERFACE;
    }

    ENTER_ENGINE();

    *Offset = Flat(m_Addr);

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::SetOffset(
    THIS_
    IN ULONG64 Offset
    )
{
    if (m_Flags & DEBUG_BREAKPOINT_DEFERRED)
    {
        return E_UNEXPECTED;
    }

    ENTER_ENGINE();

    ADDR Addr;
    HRESULT Status;

    ADDRFLAT(&Addr, Offset);
    Status = SetAddr(&Addr, BREAKPOINT_WARN_MATCH);
    if (Status == S_OK)
    {
        NotifyChanged();
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
Breakpoint::GetDataParameters(
    THIS_
    OUT PULONG Size,
    OUT PULONG AccessType
    )
{
    if (m_BreakType != DEBUG_BREAKPOINT_DATA)
    {
        return E_NOINTERFACE;
    }

    ENTER_ENGINE();

    *Size = m_DataSize;
    *AccessType = m_DataAccessType;

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::SetDataParameters(
    THIS_
    IN ULONG Size,
    IN ULONG AccessType
    )
{
    if (m_BreakType != DEBUG_BREAKPOINT_DATA)
    {
        return E_NOINTERFACE;
    }

    ENTER_ENGINE();

    ULONG OldSize = m_DataSize;
    ULONG OldAccess = m_DataAccessType;
    HRESULT Status;

    m_DataSize = Size;
    m_DataAccessType = AccessType;
    Status = Validate();
    if (Status != S_OK)
    {
        m_DataSize = OldSize;
        m_DataAccessType = OldAccess;
    }
    else
    {
        g_DataBreakpointsChanged = TRUE;
        NotifyChanged();
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
Breakpoint::GetPassCount(
    THIS_
    OUT PULONG Count
    )
{
    ENTER_ENGINE();

    *Count = m_PassCount;

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::SetPassCount(
    THIS_
    IN ULONG Count
    )
{
    if (Count < 1)
    {
        return E_INVALIDARG;
    }

    ENTER_ENGINE();

    m_PassCount = Count;
    m_CurPassCount = Count;
    NotifyChanged();

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::GetCurrentPassCount(
    THIS_
    OUT PULONG Count
    )
{
    ENTER_ENGINE();

    *Count = m_CurPassCount;

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
Breakpoint::GetMatchThreadId(
    THIS_
    OUT PULONG Id
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (m_MatchThread)
    {
        *Id = m_MatchThread->m_UserId;
        Status = S_OK;
    }
    else
    {
        Status = E_NOINTERFACE;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
Breakpoint::SetMatchThreadId(
    THIS_
    IN ULONG Id
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (IS_KERNEL_TARGET(m_Process->m_Target) &&
        m_BreakType == DEBUG_BREAKPOINT_DATA)
    {
        ErrOut("Kernel data breakpoints cannot be limited to a processor\n");
        Status = E_INVALIDARG;
    }
    else
    {
        ThreadInfo* Thread = FindAnyThreadByUserId(Id);
        if (Thread != NULL)
        {
            m_MatchThread = Thread;
            NotifyChanged();
            Status = S_OK;
        }
        else
        {
            Status = E_NOINTERFACE;
        }
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
Breakpoint::GetCommand(
    THIS_
    OUT OPTIONAL PSTR Buffer,
    IN ULONG BufferSize,
    OUT OPTIONAL PULONG CommandSize
    )
{
    ENTER_ENGINE();

    HRESULT Status = FillStringBuffer(m_Command, m_CommandLen,
                                      Buffer, BufferSize, CommandSize);

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
Breakpoint::SetCommand(
    THIS_
    IN PCSTR Command
    )
{
    HRESULT Status;

    if (strlen(Command) >= MAX_COMMAND)
    {
        return E_INVALIDARG;
    }

    ENTER_ENGINE();

    Status = ChangeString((PSTR*)&m_Command, &m_CommandLen, Command);
    if (Status == S_OK)
    {
        NotifyChanged();
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
Breakpoint::GetOffsetExpression(
    THIS_
    OUT OPTIONAL PSTR Buffer,
    IN ULONG BufferSize,
    OUT OPTIONAL PULONG ExpressionSize
    )
{
    ENTER_ENGINE();

    HRESULT Status = FillStringBuffer(m_OffsetExpr, m_OffsetExprLen,
                                      Buffer, BufferSize, ExpressionSize);

    LEAVE_ENGINE();
    return Status;
}

HRESULT
Breakpoint::SetEvaluatedOffsetExpression(PCSTR Expr,
                                         BreakpointEvalResult Valid,
                                         PADDR Addr)
{
    HRESULT Status =
        ChangeString((PSTR*)&m_OffsetExpr, &m_OffsetExprLen, Expr);
    if (Status != S_OK)
    {
        return Status;
    }

    if (Expr != NULL)
    {
        // Do initial evaluation in case the expression can be
        // resolved right away.  This will also set the deferred
        // flag if the expression can't be evaluated.
        EvalOffsetExpr(Valid, Addr);
    }
    else
    {
        // This breakpoint is no longer deferred since there's
        // no way to activate it later any more.
        m_Flags &= ~DEBUG_BREAKPOINT_DEFERRED;
        UpdateInternal();
    }

    NotifyChanged();
    return S_OK;
}

STDMETHODIMP
Breakpoint::SetOffsetExpression(
    THIS_
    IN PCSTR Expression
    )
{
    HRESULT Status;

    if (strlen(Expression) >= MAX_COMMAND)
    {
        return E_INVALIDARG;
    }

    ENTER_ENGINE();

    ADDR Addr;

    Status = SetEvaluatedOffsetExpression(Expression, BPEVAL_UNKNOWN, &Addr);

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
Breakpoint::GetParameters(
    THIS_
    OUT PDEBUG_BREAKPOINT_PARAMETERS Params
    )
{
    ENTER_ENGINE();

    if (m_Flags & DEBUG_BREAKPOINT_DEFERRED)
    {
        Params->Offset = DEBUG_INVALID_OFFSET;
    }
    else
    {
        Params->Offset = Flat(m_Addr);
    }
    Params->Id = m_Id;
    Params->BreakType = m_BreakType;
    Params->ProcType = m_ProcType;
    Params->Flags = m_Flags & BREAKPOINT_EXTERNAL_FLAGS;
    if (m_BreakType == DEBUG_BREAKPOINT_DATA)
    {
        Params->DataSize = m_DataSize;
        Params->DataAccessType = m_DataAccessType;
    }
    else
    {
        Params->DataSize = 0;
        Params->DataAccessType = 0;
    }
    Params->PassCount = m_PassCount;
    Params->CurrentPassCount = m_CurPassCount;
    Params->MatchThread = m_MatchThread != NULL ?
        m_MatchThread->m_UserId : DEBUG_ANY_ID;
    Params->CommandSize = m_CommandLen;
    Params->OffsetExpressionSize = m_OffsetExprLen;

    LEAVE_ENGINE();
    return S_OK;
}

void
Breakpoint::LinkIntoList(void)
{
    Breakpoint* NextBp;
    Breakpoint* PrevBp;

    DBG_ASSERT((m_Flags & BREAKPOINT_IN_LIST) == 0);

    // Link into list sorted by ID.
    PrevBp = NULL;
    for (NextBp = m_Process->m_Breakpoints;
         NextBp != NULL;
         NextBp = NextBp->m_Next)
    {
        if (m_Id < NextBp->m_Id)
        {
            break;
        }

        PrevBp = NextBp;
    }

    m_Prev = PrevBp;
    if (PrevBp == NULL)
    {
        m_Process->m_Breakpoints = this;
    }
    else
    {
        PrevBp->m_Next = this;
    }
    m_Next = NextBp;
    if (NextBp == NULL)
    {
        m_Process->m_BreakpointsTail = this;
    }
    else
    {
        NextBp->m_Prev = this;
    }

    m_Flags |= BREAKPOINT_IN_LIST;
    m_Process->m_NumBreakpoints++;
    g_BreakpointListChanged = TRUE;
}

void
Breakpoint::UnlinkFromList(void)
{
    DBG_ASSERT(m_Flags & BREAKPOINT_IN_LIST);

    if (m_Prev == NULL)
    {
        m_Process->m_Breakpoints = m_Next;
    }
    else
    {
        m_Prev->m_Next = m_Next;
    }
    if (m_Next == NULL)
    {
        m_Process->m_BreakpointsTail = m_Prev;
    }
    else
    {
        m_Next->m_Prev = m_Prev;
    }

    m_Flags &= ~BREAKPOINT_IN_LIST;
    m_Process->m_NumBreakpoints--;
    g_BreakpointListChanged = TRUE;
}

void
Breakpoint::UpdateInternal(void)
{
    // This only has an effect with internal breakpoints.
    if ((m_Flags & BREAKPOINT_KD_INTERNAL) == 0)
    {
        return;
    }

    // If the breakpoint is ready turn it on, otherwise
    // turn it off.
    ULONG Flags;

    if ((m_Flags & (DEBUG_BREAKPOINT_ENABLED |
                    DEBUG_BREAKPOINT_DEFERRED)) == DEBUG_BREAKPOINT_ENABLED)
    {
        Flags = (m_Flags & BREAKPOINT_KD_COUNT_ONLY) ?
            DBGKD_INTERNAL_BP_FLAG_COUNTONLY : 0;
    }
    else
    {
        Flags = DBGKD_INTERNAL_BP_FLAG_INVALID;
    }

    BpOut("Set internal bp at %s to %X\n",
          FormatAddr64(Flat(m_Addr)), Flags);

    if (Flags != DBGKD_INTERNAL_BP_FLAG_INVALID)
    {
        m_Process->m_Target->
            InsertTargetCountBreakpoint(&m_Addr, Flags);
    }
    else
    {
        m_Process->m_Target->
            RemoveTargetCountBreakpoint(&m_Addr);
    }
}

BreakpointEvalResult
EvalAddrExpression(ProcessInfo* Process, ULONG Machine, PADDR Addr)
{
    BOOL Error = FALSE;
    ULONG NumUn;
    StackSaveLayers Save;
    EvalExpression* Eval;
    EvalExpression* RelChain;

    //
    // This function can be reentered if evaluating an
    // expression causes symbol changes which provoke
    // reevaluation of existing address expressions.
    // Save away current settings to support nesting.
    //

    // Evaluate the expression in the context of the breakpoint's
    // machine type so that registers and such are available.
    ULONG OldMachine = Process->m_Target->m_EffMachineType;
    Process->m_Target->SetEffMachine(Machine, FALSE);

    SetLayersFromProcess(Process);

    RelChain = g_EvalReleaseChain;
    g_EvalReleaseChain = NULL;

    __try
    {
        Eval = GetCurEvaluator();
        Eval->m_AllowUnresolvedSymbols++;
        Eval->EvalCurAddr(SEGREG_CODE, Addr);
        NumUn = Eval->m_NumUnresolvedSymbols;
        ReleaseEvaluator(Eval);
    }
    __except(CommandExceptionFilter(GetExceptionInformation()))
    {
        // Skip the remainder of the command as there
        // was an error during processing.
        g_CurCmd += strlen(g_CurCmd);
        Error = TRUE;
    }

    g_EvalReleaseChain = RelChain;
    Process->m_Target->SetEffMachine(OldMachine, FALSE);

    if (Error)
    {
        return BPEVAL_ERROR;
    }
    else if (NumUn > 0)
    {
        return BPEVAL_UNRESOLVED;
    }
    else
    {
        ImageInfo* Image;

        // Check if this address falls within an existing module.
        for (Image = Process->m_ImageHead;
             Image != NULL;
             Image = Image->m_Next)
        {
            if (Flat(*Addr) >= Image->m_BaseOfImage &&
                Flat(*Addr) < Image->m_BaseOfImage + Image->m_SizeOfImage)
            {
                return BPEVAL_RESOLVED;
            }
        }

        return BPEVAL_RESOLVED_NO_MODULE;
    }
}

BOOL
Breakpoint::EvalOffsetExpr(BreakpointEvalResult Valid, PADDR Addr)
{
    ULONG OldFlags = m_Flags;

    DBG_ASSERT(m_OffsetExpr != NULL);

    if (Valid == BPEVAL_UNKNOWN)
    {
        PSTR CurCommand = g_CurCmd;

        g_CurCmd = (PSTR)m_OffsetExpr;
        g_DisableErrorPrint++;
        g_PrefixSymbols = TRUE;

        Valid = EvalAddrExpression(m_Process, m_ProcType, Addr);

        g_PrefixSymbols = FALSE;
        g_DisableErrorPrint--;
        g_CurCmd = CurCommand;
    }

    // Silently allow matching breakpoints when resolving
    // as it is difficult for the expression setter to know
    // whether there'll be matches or not at the time
    // the expression is set.
    if (Valid == BPEVAL_RESOLVED)
    {
        m_Flags &= ~DEBUG_BREAKPOINT_DEFERRED;

        if (SetAddr(Addr, BREAKPOINT_ALLOW_MATCH) != S_OK)
        {
            m_Flags |= DEBUG_BREAKPOINT_DEFERRED;
        }
    }
    else
    {
        m_Flags |= DEBUG_BREAKPOINT_DEFERRED;
        // The module containing the breakpoint is being
        // unloaded so just mark this breakpoint as not-inserted.
        m_Flags &= ~BREAKPOINT_INSERTED;
    }

    if ((OldFlags ^ m_Flags) & DEBUG_BREAKPOINT_DEFERRED)
    {
        // Update internal BP status.
        UpdateInternal();

        if (m_Flags & DEBUG_BREAKPOINT_DEFERRED)
        {
            BpOut("Deferring %u '%s'\n", m_Id, m_OffsetExpr);
        }
        else
        {
            BpOut("Enabling deferred %u '%s' at %s\n",
                  m_Id, m_OffsetExpr, FormatAddr64(Flat(m_Addr)));
        }

        return TRUE;
    }

    return FALSE;
}

HRESULT
Breakpoint::CheckAddr(PADDR Addr)
{
    ULONG AddrSpace, AddrFlags;

    if (m_Process->m_Target->
        QueryAddressInformation(m_Process,
                                Flat(*Addr), DBGKD_QUERY_MEMORY_VIRTUAL,
                                &AddrSpace, &AddrFlags) != S_OK)
    {
        ErrOut("Invalid breakpoint address\n");
        return E_INVALIDARG;
    }

    if (m_BreakType != DEBUG_BREAKPOINT_DATA &&
        !(AddrFlags & DBGKD_QUERY_MEMORY_WRITE) ||
        (AddrFlags & DBGKD_QUERY_MEMORY_FIXED))
    {
        ErrOut("Software breakpoints cannot be used on ROM code or\n"
               "other read-only memory. "
               "Use hardware execution breakpoints (ba e) instead.\n");
        return E_INVALIDARG;
    }

    if (m_BreakType != DEBUG_BREAKPOINT_DATA &&
        AddrSpace == DBGKD_QUERY_MEMORY_SESSION)
    {
        WarnOut("WARNING: Software breakpoints on session "
                "addresses can cause bugchecks.\n"
                "Use hardware execution breakpoints (ba e) "
                "if possible.\n");
        return S_FALSE;
    }

    return S_OK;
}

HRESULT
Breakpoint::SetAddr(PADDR Addr, BreakpointMatchAction MatchAction)
{
    if (m_Flags & DEBUG_BREAKPOINT_DEFERRED)
    {
        // Address is unknown.
        return S_OK;
    }

    // Lock the breakpoint processor type to the
    // type of the module containing it.
    ULONG ProcType = m_ProcType;
    if (m_BreakType == DEBUG_BREAKPOINT_CODE)
    {
        ImageInfo* Image = m_Process->FindImageByOffset(Flat(*Addr), FALSE);
        if (Image)
        {
            ProcType = Image->GetMachineType();
            if (ProcType == IMAGE_FILE_MACHINE_UNKNOWN)
            {
                ProcType = m_ProcType;
            }
        }
        else
        {
            ProcType = m_ProcType;
        }
    }

    if (m_Flags & BREAKPOINT_VIRT_ADDR)
    {
        // Old flag used on ALPHA
    }

    ADDR OldAddr = m_Addr;
    HRESULT Valid;

    m_Addr = *Addr;

    Valid = Validate();
    if (Valid != S_OK)
    {
        m_Addr = OldAddr;
        return Valid;
    }

    if (ProcType != m_ProcType)
    {
        SetProcType(ProcType);
    }

    if (m_BreakType == DEBUG_BREAKPOINT_DATA)
    {
        g_DataBreakpointsChanged = TRUE;
    }

    if (MatchAction == BREAKPOINT_ALLOW_MATCH)
    {
        return S_OK;
    }

    for (;;)
    {
        Breakpoint* MatchBp;

        MatchBp = CheckMatchingBreakpoints(this, TRUE, 0xffffffff);
        if (MatchBp == NULL)
        {
            break;
        }

        if (MatchAction == BREAKPOINT_REMOVE_MATCH)
        {
            ULONG MoveId;

            WarnOut("breakpoint %u redefined\n", MatchBp->m_Id);
            // Move breakpoint towards lower IDs.
            if (MatchBp->m_Id < m_Id)
            {
                MoveId = MatchBp->m_Id;
            }
            else
            {
                MoveId = DEBUG_ANY_ID;
            }

            RemoveBreakpoint(MatchBp);

            if (MoveId != DEBUG_ANY_ID)
            {
                // Take over the removed ID.
                UnlinkFromList();
                m_Id = MoveId;
                LinkIntoList();
            }
        }
        else
        {
            WarnOut("Breakpoints %u and %u match\n",
                    m_Id, MatchBp->m_Id);
            break;
        }
    }

    return S_OK;
}

#define INSERTION_MATCH_FLAGS \
    (BREAKPOINT_KD_INTERNAL | BREAKPOINT_VIRT_ADDR)

BOOL
Breakpoint::IsInsertionMatch(Breakpoint* Match)
{
    if ((m_Flags & DEBUG_BREAKPOINT_DEFERRED) ||
        (Match->m_Flags & DEBUG_BREAKPOINT_DEFERRED) ||
        m_BreakType != Match->m_BreakType ||
        ((m_Flags ^ Match->m_Flags) & INSERTION_MATCH_FLAGS) ||
        !AddrEqu(m_Addr, Match->m_Addr) ||
        m_Process != Match->m_Process ||
        (m_BreakType == DEBUG_BREAKPOINT_DATA &&
         m_MatchThread != Match->m_MatchThread))
    {
        return FALSE;
    }
    else
    {
        return TRUE;
    }
}

#define PUBLIC_MATCH_FLAGS \
    (BREAKPOINT_HIDDEN | DEBUG_BREAKPOINT_ADDER_ONLY)

BOOL
Breakpoint::IsPublicMatch(Breakpoint* Match)
{
    if (!IsInsertionMatch(Match) ||
        m_ProcType != Match->m_ProcType ||
        ((m_Flags ^ Match->m_Flags) & PUBLIC_MATCH_FLAGS) ||
        ((m_Flags & DEBUG_BREAKPOINT_ADDER_ONLY) &&
         m_Adder != Match->m_Adder) ||
        m_MatchThread != Match->m_MatchThread ||
        m_MatchThreadData != Match->m_MatchThreadData ||
        m_MatchProcessData != Match->m_MatchProcessData)
    {
        return FALSE;
    }
    else
    {
        return TRUE;
    }
}

BOOL
Breakpoint::MatchesCurrentState(void)
{
    HRESULT Status;
    ULONG64 ThreadData = 0, ProcData = 0;

    // If querying the current state fails go ahead
    // and return a match so that the breakpoint will
    // break as often as possible.
    if (m_MatchThreadData)
    {
        if ((Status = g_EventTarget->
             GetThreadInfoDataOffset(g_EventThread, 0, &ThreadData)) != S_OK)
        {
            ErrOut("Unable to determine current thread data, %s\n",
                   FormatStatusCode(Status));
            return TRUE;
        }
    }
    if (m_MatchProcessData)
    {
        if ((Status = g_EventTarget->
             GetProcessInfoDataOffset(g_EventThread, 0, 0, &ProcData)) != S_OK)
        {
            ErrOut("Unable to determine current process data, %s\n",
                   FormatStatusCode(Status));
            return TRUE;
        }
    }

    return
        (m_MatchThread == NULL ||
         m_MatchThread == g_EventThread) &&
        m_MatchThreadData == ThreadData &&
        m_MatchProcessData == ProcData;
}

//----------------------------------------------------------------------------
//
// CodeBreakpoint.
//
//----------------------------------------------------------------------------

HRESULT
CodeBreakpoint::Validate(void)
{
    // No easy way to check for validity of offset.
    return S_OK;
}

HRESULT
CodeBreakpoint::Insert(void)
{
    if (m_Flags & BREAKPOINT_INSERTED)
    {
        // Nothing to insert.  This can happen in cases where
        // the breakpoint remove failed.
        return S_OK;
    }

    if (!m_Process)
    {
        return E_UNEXPECTED;
    }

    HRESULT Status;

    DBG_ASSERT((m_Flags & (DEBUG_BREAKPOINT_DEFERRED |
                           BREAKPOINT_KD_INTERNAL)) == 0);

    // Force recomputation of flat address.
    NotFlat(m_Addr);
    ComputeFlatAddress(&m_Addr, NULL);

    Status = m_Process->m_Target->
        InsertCodeBreakpoint(m_Process,
                             m_Process->m_Target->
                             m_Machines[m_ProcIndex],
                             &m_Addr,
                             m_CodeFlags,
                             m_InsertStorage);
    if (Status == S_OK)
    {
        BpOut("  inserted bp %u at %s\n",
              m_Id, FormatAddr64(Flat(m_Addr)));

        m_Flags |= BREAKPOINT_INSERTED;
        return S_OK;
    }
    else
    {
        ErrOut("Unable to insert breakpoint %u at %s, %s\n    \"%s\"\n",
               m_Id, FormatAddr64(Flat(m_Addr)),
               FormatStatusCode(Status), FormatStatus(Status));
        return Status;
    }
}

HRESULT
CodeBreakpoint::Remove(void)
{
    if ((m_Flags & BREAKPOINT_INSERTED) == 0)
    {
        // Nothing to remove.  This can happen in cases where
        // the breakpoint insertion failed.
        return S_OK;
    }

    if (!m_Process)
    {
        return E_UNEXPECTED;
    }

    HRESULT Status;

    DBG_ASSERT((m_Flags & (DEBUG_BREAKPOINT_DEFERRED |
                           BREAKPOINT_KD_INTERNAL)) == 0);

    // Force recomputation of flat address.
    NotFlat(m_Addr);
    ComputeFlatAddress(&m_Addr, NULL);

    Status = m_Process->m_Target->
        RemoveCodeBreakpoint(m_Process,
                             m_Process->m_Target->
                             m_Machines[m_ProcIndex],
                             &m_Addr,
                             m_CodeFlags,
                             m_InsertStorage);
    if (Status == S_OK)
    {
        BpOut("  removed bp %u from %s\n",
              m_Id, FormatAddr64(Flat(m_Addr)));

        m_Flags &= ~BREAKPOINT_INSERTED;
        return S_OK;
    }
    else
    {
        ErrOut("Unable to remove breakpoint %u at %s, %s\n    \"%s\"\n",
               m_Id, FormatAddr64(Flat(m_Addr)),
               FormatStatusCode(Status), FormatStatus(Status));
        return Status;
    }
}

ULONG
CodeBreakpoint::IsHit(PADDR Addr)
{
    // Code breakpoints are code modifications and
    // therefore aren't restricted to a particular
    // thread.
    // If this breakpoint can only match hits on
    // a particular thread this is a partial hit
    // because the exception occurred but it's
    // being ignored.
    if (AddrEqu(m_Addr, *Addr))
    {
        if (MatchesCurrentState())
        {
            return BREAKPOINT_HIT;
        }
        else
        {
            return BREAKPOINT_HIT_IGNORED;
        }
    }
    else
    {
        return BREAKPOINT_NOT_HIT;
    }
}

//----------------------------------------------------------------------------
//
// DataBreakpoint.
//
//----------------------------------------------------------------------------

HRESULT
DataBreakpoint::Insert(void)
{
    HRESULT Status;
    ThreadInfo* Thread;

    DBG_ASSERT((m_Flags & (BREAKPOINT_INSERTED |
                           DEBUG_BREAKPOINT_DEFERRED)) == 0);

    // Force recomputation of flat address for non-I/O breakpoints.
    if (m_Flags & BREAKPOINT_VIRT_ADDR)
    {
        NotFlat(m_Addr);
        ComputeFlatAddress(&m_Addr, NULL);
    }

    Status = m_Process->m_Target->
        InsertDataBreakpoint(m_Process,
                             m_MatchThread,
                             m_Process->m_Target->
                             m_Machines[m_ProcIndex],
                             &m_Addr,
                             m_DataSize,
                             m_DataAccessType,
                             m_InsertStorage);
    // If the target returns S_FALSE it wants
    // the generic insertion processing to occur.
    if (Status == S_OK)
    {
        BpOut("  service inserted dbp %u at %s\n",
              m_Id, FormatAddr64(Flat(m_Addr)));

        m_Flags |= BREAKPOINT_INSERTED;
        return Status;
    }
    else if (Status != S_FALSE)
    {
        ErrOut("Unable to insert breakpoint %u at %s, %s\n    \"%s\"\n",
               m_Id, FormatAddr64(Flat(m_Addr)),
               FormatStatusCode(Status), FormatStatus(Status));
        return Status;
    }

    // If this breakpoint is restricted to a thread
    // only modify that thread's state.  Otherwise
    // update all threads in the process.
    Thread = m_Process->m_ThreadHead;
    while (Thread)
    {
        if (Thread->m_NumDataBreaks >=
            m_Process->m_Target->m_Machine->m_MaxDataBreakpoints)
        {
            ErrOut("Too many data breakpoints for %s %d\n",
                   IS_KERNEL_TARGET(m_Process->m_Target) ?
                   "processor" : "thread", Thread->m_UserId);
            return HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
        }
        else if (m_MatchThread == NULL || m_MatchThread == Thread)
        {
            BpOut("Add %s data bp %u to thread %u\n",
                  m_Process->m_Target->m_Machines[m_ProcIndex]->m_AbbrevName,
                  m_Id, Thread->m_UserId);

            AddToThread(Thread);
        }

        Thread = Thread->m_Next;
    }

    g_UpdateDataBreakpoints = TRUE;
    m_Flags |= BREAKPOINT_INSERTED;

    return S_OK;
}

HRESULT
DataBreakpoint::Remove(void)
{
    HRESULT Status;

    if ((m_Flags & BREAKPOINT_INSERTED) == 0)
    {
        // Nothing to remove.  This can happen in cases where
        // the breakpoint insertion failed.
        return S_OK;
    }

    DBG_ASSERT((m_Flags & DEBUG_BREAKPOINT_DEFERRED) == 0);

    Status = m_Process->m_Target->
        RemoveDataBreakpoint(m_Process,
                             m_MatchThread,
                             m_Process->m_Target->
                             m_Machines[m_ProcIndex],
                             &m_Addr,
                             m_DataSize,
                             m_DataAccessType,
                             m_InsertStorage);
    // If the target returns S_FALSE it wants
    // the generic removal processing to occur.
    if (Status == S_OK)
    {
        BpOut("  service removed dbp %u at %s\n",
              m_Id, FormatAddr64(Flat(m_Addr)));

        m_Flags &= ~BREAKPOINT_INSERTED;
        return Status;
    }
    else if (Status != S_FALSE)
    {
        ErrOut("Unable to remove breakpoint %u at %s, %s\n    \"%s\"\n",
               m_Id, FormatAddr64(Flat(m_Addr)),
               FormatStatusCode(Status), FormatStatus(Status));
        return Status;
    }

    // When breakpoints are inserted the data breakpoint state
    // is always started completely empty so no special
    // work needs to be done when removing.
    g_UpdateDataBreakpoints = TRUE;
    m_Flags &= ~BREAKPOINT_INSERTED;
    return S_OK;
}

void
DataBreakpoint::ClearThreadDataBreaks(ThreadInfo* Thread)
{
    Thread->m_NumDataBreaks = 0;
    memset(Thread->m_DataBreakBps, 0, sizeof(Thread->m_DataBreakBps));
}

void
DataBreakpoint::AddToThread(ThreadInfo* Thread)
{
    DBG_ASSERT(Thread->m_NumDataBreaks <
               Thread->m_Process->m_Target->m_Machine->m_MaxDataBreakpoints);

    Thread->m_DataBreakBps[Thread->m_NumDataBreaks] = this;
    Thread->m_NumDataBreaks++;
}

//----------------------------------------------------------------------------
//
// X86DataBreakpoint.
//
//----------------------------------------------------------------------------

HRESULT
X86DataBreakpoint::Validate(void)
{
    ULONG Dr7Bits;
    ULONG Align;

    if (!IsPow2(m_DataSize) || m_DataSize == 0 ||
        (m_ProcType == IMAGE_FILE_MACHINE_AMD64 && m_DataSize > 8) ||
        (m_ProcType != IMAGE_FILE_MACHINE_AMD64 && m_DataSize > 4))
    {
        ErrOut("Unsupported data breakpoint size\n");
        return E_INVALIDARG;
    }

    Align = (ULONG)(Flat(m_Addr) & (m_DataSize - 1));
    if (Align != 0)
    {
        ErrOut("Data breakpoint must be aligned\n");
        return E_INVALIDARG;
    }

    if (m_DataSize < 8)
    {
        Dr7Bits = (m_DataSize - 1) << X86_DR7_LEN0_SHIFT;
    }
    else
    {
        Dr7Bits = 2 << X86_DR7_LEN0_SHIFT;
    }

    switch(m_DataAccessType)
    {
    case DEBUG_BREAK_EXECUTE:
        Dr7Bits |= X86_DR7_RW0_EXECUTE;
        // Code execution breakpoints must have a
        // size of one.
        // They must also be at the beginning
        // of an instruction.  This could be checked via
        // examining the instructions but it doesn't seem
        // that worth the trouble.
        if (m_DataSize > 1)
        {
            ErrOut("Execution data breakpoint too large\n");
            return E_INVALIDARG;
        }
        break;
    case DEBUG_BREAK_WRITE:
        Dr7Bits |= X86_DR7_RW0_WRITE;
        break;
    case DEBUG_BREAK_IO:
        if (IS_USER_TARGET(m_Process->m_Target) ||
            !(m_Process->m_Target->m_Machines[m_ProcIndex]->
              GetReg32(m_Cr4Reg) & X86_CR4_DEBUG_EXTENSIONS))
        {
            ErrOut("I/O breakpoints not enabled\n");
            return E_INVALIDARG;
        }
        if (Flat(m_Addr) > 0xffff)
        {
            ErrOut("I/O breakpoint port too large\n");
            return E_INVALIDARG;
        }

        Dr7Bits |= X86_DR7_RW0_IO;
        break;
    case DEBUG_BREAK_READ:
    case DEBUG_BREAK_READ | DEBUG_BREAK_WRITE:
        // There is no pure read-only option so
        // lump it in with read-write.
        Dr7Bits |= X86_DR7_RW0_READ_WRITE;
        break;
    default:
        ErrOut("Unsupported data breakpoint access type\n");
        return E_INVALIDARG;
    }

    m_Dr7Bits = Dr7Bits | X86_DR7_L0_ENABLE;
    if (m_DataAccessType == DEBUG_BREAK_IO)
    {
        m_Flags &= ~BREAKPOINT_VIRT_ADDR;
    }
    else
    {
        m_Flags |= BREAKPOINT_VIRT_ADDR;
    }

    return S_OK;
}

ULONG
X86DataBreakpoint::IsHit(PADDR Addr)
{
    ULONG i;
    ThreadInfo* Thread = g_EventThread;
    HRESULT Status;

    // Data breakpoints are only inserted in the contexts
    // of matching threads so if the event thread doesn't match
    // the breakpoint can't be hit.
    if (m_MatchThread && m_MatchThread != Thread)
    {
        return BREAKPOINT_NOT_HIT;
    }

    Status = m_Process->m_Target->
        IsDataBreakpointHit(Thread, &m_Addr,
                            m_DataSize, m_DataAccessType,
                            m_InsertStorage);
    // If the target returns S_FALSE it wants
    // the generic processing to occur.
    if (Status == S_OK)
    {
        if (MatchesCurrentState())
        {
            return BREAKPOINT_HIT;
        }
        else
        {
            return BREAKPOINT_HIT_IGNORED;
        }
    }
    else if (Status != S_FALSE)
    {
        return BREAKPOINT_NOT_HIT;
    }

    // Locate this breakpoint in the thread's data breakpoints
    // if possible.
    for (i = 0; i < Thread->m_NumDataBreaks; i++)
    {
        // Check for match in addition to equality to handle
        // multiple identical data breakpoints.
        if (Thread->m_DataBreakBps[i] == this ||
            IsInsertionMatch(Thread->m_DataBreakBps[i]))
        {
            // Is this breakpoint's index set in the debug status register?
            // Address is not meaningful so this is the only way to check.
            if ((m_Process->m_Target->m_Machines[m_ProcIndex]->
                 GetReg32(m_Dr6Reg) >> i) & 1)
            {
                if (MatchesCurrentState())
                {
                    return BREAKPOINT_HIT;
                }
                else
                {
                    return BREAKPOINT_HIT_IGNORED;
                }
            }
            else
            {
                // Breakpoint can't be listed in more than one slot
                // so there's no need to finish the loop.
                return BREAKPOINT_NOT_HIT;
            }
        }
    }

    return BREAKPOINT_NOT_HIT;
}

//----------------------------------------------------------------------------
//
// Ia64DataBreakpoint.
//
//----------------------------------------------------------------------------

HRESULT
Ia64DataBreakpoint::Validate(void)
{
    if (!IsPow2(m_DataSize))
    {
        ErrOut("Hardware breakpoint size must be power of 2\n");
        return E_INVALIDARG;
    }

    if (Flat(m_Addr) & (m_DataSize - 1))
    {
        ErrOut("Hardware breakpoint must be size aligned\n");
        return E_INVALIDARG;
    }

    switch (m_DataAccessType)
    {
    case DEBUG_BREAK_WRITE:
    case DEBUG_BREAK_READ:
    case DEBUG_BREAK_READ | DEBUG_BREAK_WRITE:
        break;
    case DEBUG_BREAK_EXECUTE:
        if (m_DataSize & 0xf)
        {
            if (m_DataSize > 0xf)
            {
                ErrOut("Execution breakpoint size must be bundle aligned.\n");
                return E_INVALIDARG;
            }
            else
            {
                WarnOut("Execution breakpoint size extended to bundle size "
                          "(16 bytes).\n");
                m_DataSize = 0x10;
            }
        }
        break;
    default:
        ErrOut("Unsupported data breakpoint access type\n");
        return E_INVALIDARG;
    }

    m_Control = GetControl(m_DataAccessType, m_DataSize);
    m_Flags |= BREAKPOINT_VIRT_ADDR;

    return S_OK;
}

ULONG
Ia64DataBreakpoint::IsHit(PADDR Addr)
{
    HRESULT Status;
    ULONG i;
    ThreadInfo* Thread = g_EventThread;

    // Data breakpoints are only inserted in the contexts
    // of matching threads so if the event thread doesn't match
    // the breakpoint can't be hit.
    if (m_MatchThread && m_MatchThread != Thread)
    {
        return BREAKPOINT_NOT_HIT;
    }

    Status = m_Process->m_Target->
        IsDataBreakpointHit(Thread, &m_Addr,
                            m_DataSize, m_DataAccessType,
                            m_InsertStorage);
    // If the target returns S_FALSE it wants
    // the generic processing to occur.
    if (Status == S_OK)
    {
        if (MatchesCurrentState())
        {
            return BREAKPOINT_HIT;
        }
        else
        {
            return BREAKPOINT_HIT_IGNORED;
        }
    }
    else if (Status != S_FALSE)
    {
        return BREAKPOINT_NOT_HIT;
    }

    // Locate this breakpoint in the thread's data breakpoints
    // if possible.
    for (i = 0; i < Thread->m_NumDataBreaks; i++)
    {
        // Check for match in addition to equality to handle
        // multiple identical data breakpoints.
        if (Thread->m_DataBreakBps[i] == this ||
            IsInsertionMatch(Thread->m_DataBreakBps[i]))
        {
            if ((Flat(*Thread->m_DataBreakBps[i]->GetAddr()) ^
                 Flat(*Addr)) &
                (m_Control & IA64_DBG_MASK_MASK))
            {
                // Breakpoint can't be listed in more than one slot
                // so there's no need to finish the loop.
                return BREAKPOINT_NOT_HIT;
            }
            else
            {
                if (MatchesCurrentState())
                {
                    return BREAKPOINT_HIT;
                }
                else
                {
                    return BREAKPOINT_HIT_IGNORED;
                }
            }
        }
    }

    return BREAKPOINT_NOT_HIT;
}

ULONG64
Ia64DataBreakpoint::GetControl(ULONG AccessType, ULONG Size)
{
    ULONG64 Control = (ULONG64(IA64_DBG_REG_PLM_ALL) |
                       ULONG64(IA64_DBG_MASK_MASK)) &
                      ~ULONG64(Size - 1);

    switch (AccessType)
    {
    case DEBUG_BREAK_WRITE:
        Control |= IA64_DBR_WR;
        break;
    case DEBUG_BREAK_READ:
        Control |= IA64_DBR_RD;
        break;
    case DEBUG_BREAK_READ | DEBUG_BREAK_WRITE:
        Control |= IA64_DBR_RDWR;
        break;
    case DEBUG_BREAK_EXECUTE:
        Control |= IA64_DBR_EXEC;
        break;
    }

    return Control;
}

//----------------------------------------------------------------------------
//
// X86OnIa64DataBreakpoint.
//
//----------------------------------------------------------------------------

X86OnIa64DataBreakpoint::X86OnIa64DataBreakpoint(DebugClient* Adder, ULONG Id)
    : X86DataBreakpoint(Adder, Id, X86_CR4, X86_DR6, IMAGE_FILE_MACHINE_I386)
{
    m_Control = 0;
}

HRESULT
X86OnIa64DataBreakpoint::Validate(void)
{
    HRESULT Status = X86DataBreakpoint::Validate();
    if (Status != S_OK)
    {
        return Status;
    }

    switch (m_DataAccessType)
    {
    case DEBUG_BREAK_WRITE:
    case DEBUG_BREAK_READ:
    case DEBUG_BREAK_READ | DEBUG_BREAK_WRITE:
    case DEBUG_BREAK_EXECUTE:
        break;
    default:
        ErrOut("Unsupported data breakpoint access type\n");
        return E_INVALIDARG;
    }

    m_Control = Ia64DataBreakpoint::GetControl(m_DataAccessType, m_DataSize);

    return S_OK;
}


// XXX olegk -This is pure hack
// (see X86OnIa64MachineInfo::IsBreakpointOrStepException implementation
// for more info)

ULONG
X86OnIa64DataBreakpoint::IsHit(PADDR Addr)
{
    HRESULT Status;
    ULONG i;
    ThreadInfo* Thread = g_EventThread;

    // Data breakpoints are only inserted in the contexts
    // of matching threads so if the event thread doesn't match
    // the breakpoint can't be hit.
    if (m_MatchThread && m_MatchThread != Thread)
    {
        return BREAKPOINT_NOT_HIT;
    }

    Status = m_Process->m_Target->
        IsDataBreakpointHit(Thread, &m_Addr,
                            m_DataSize, m_DataAccessType,
                            m_InsertStorage);
    // If the target returns S_FALSE it wants
    // the generic processing to occur.
    if (Status == S_OK)
    {
        if (MatchesCurrentState())
        {
            return BREAKPOINT_HIT;
        }
        else
        {
            return BREAKPOINT_HIT_IGNORED;
        }
    }
    else if (Status != S_FALSE)
    {
        return BREAKPOINT_NOT_HIT;
    }

    // Locate this breakpoint in the thread's data breakpoints
    // if possible.
    for (i = 0; i < Thread->m_NumDataBreaks; i++)
    {
        // Check for match in addition to equality to handle
        // multiple identical data breakpoints.
        if (Thread->m_DataBreakBps[i] == this ||
            IsInsertionMatch(Thread->m_DataBreakBps[i]))
        {
            if (((ULONG)Flat(*Thread->m_DataBreakBps[i]->GetAddr()) ^
                 (ULONG)Flat(*Addr)) &
                (ULONG)(m_Control & IA64_DBG_MASK_MASK))
            {
                // Breakpoint can't be listed in more than one slot
                // so there's no need to finish the loop.
                return BREAKPOINT_NOT_HIT;
            }
            else
            {
                if (MatchesCurrentState())
                {
                    return BREAKPOINT_HIT;
                }
                else
                {
                    return BREAKPOINT_HIT_IGNORED;
                }
            }
        }
    }

    return BREAKPOINT_NOT_HIT;
}

//----------------------------------------------------------------------------
//
// Functions.
//
//----------------------------------------------------------------------------

BOOL
BreakpointNeedsToBeDeferred(Breakpoint* Bp,
                            ProcessInfo* Process, PADDR PcAddr)
{
    if (Process && IS_CONTEXT_POSSIBLE(Process->m_Target) &&
        (Bp->m_Process == Process))
    {
        // If this breakpoint matches the current IP and
        // the current thread is going to be running
        // we can't insert the breakpoint as the thread
        // needs to run the real code.
        if ((Bp->m_Flags & BREAKPOINT_VIRT_ADDR) &&
            AddrEqu(*Bp->GetAddr(), *PcAddr) &&
            ThreadWillResume(g_EventThread))
        {
            return TRUE;
        }

        if ((Bp == g_LastBreakpointHit) && Bp->PcAtHit() &&
            AddrEqu(g_LastBreakpointHitPc, *PcAddr))
        {
            if (g_ContextChanged)
            {
                WarnOut("Breakpoint %u will not be deferred because of "
                        "changes in the context. Breakpoint may hit again.\n",
                        Bp->m_Id);
                return FALSE;
            }
            return TRUE;
        }
    }
    return FALSE;
}

//----------------------------------------------------------------------------
//
// Modify debuggee to activate current breakpoints.
//
//----------------------------------------------------------------------------

HRESULT
InsertBreakpoints(void)
{
    HRESULT Status = S_OK;
    ADDR PcAddr;
    BOOL DeferredData = FALSE;
    ThreadInfo* OldThread;

    if (g_Thread != NULL)
    {
        // Aggressively clear this flag always in order to be
        // as conservative as possible when recognizing
        // trace events.  We would rather misrecognize
        // single-step events and break in instead of
        // misrecognizing an app-generated single-step and
        // ignoring it.
        g_Thread->m_Flags &= ~ENG_THREAD_DEFER_BP_TRACE;
    }

    if ((g_EngStatus & ENG_STATUS_BREAKPOINTS_INSERTED) ||
        (g_EngStatus & ENG_STATUS_SUSPENDED) == 0)
    {
        return Status;
    }

    g_DeferDefined = FALSE;
    ADDRFLAT(&PcAddr, 0);

    // Switch to the event thread to get the event thread's
    // PC so we can see if we need to defer breakpoints in
    // order to allow the event thread to keep running.
    if (g_EventTarget)
    {
        OldThread = g_EventTarget->m_RegContextThread;
        g_EventTarget->ChangeRegContext(g_EventThread);
    }

    if (g_BreakpointsSuspended)
    {
        goto StepTraceOnly;
    }

    //
    // Turn off all data breakpoints.  (We will turn the enabled ones back
    // on when we restart execution).
    //

    TargetInfo* Target;

    // Allow target to prepare for breakpoint insertion.
    ForAllLayersToTarget()
    {
        if ((Status = Target->BeginInsertingBreakpoints()) != S_OK)
        {
            return Status;
        }
    }

    if (IS_CONTEXT_POSSIBLE(g_EventTarget))
    {
        g_EventTarget->m_EffMachine->GetPC(&PcAddr);
    }

    BpOut("InsertBreakpoints PC ");
    if (IS_CONTEXT_POSSIBLE(g_EventTarget))
    {
        MaskOutAddr(DEBUG_IOUTPUT_BREAKPOINT, &PcAddr);
        BpOut("\n");
    }
    else
    {
        BpOut("?\n");
    }

    //
    // Set any appropriate permanent breakpoints.
    //

    Breakpoint* Bp;
    ProcessInfo* Process;

    ForAllLayersToProcess()
    {
        BpOut("  Process %d with %d bps\n", Process->m_UserId,
              Process->m_NumBreakpoints);

        for (Bp = Process->m_Breakpoints; Bp != NULL; Bp = Bp->m_Next)
        {
            if (Bp->IsNormalEnabled() &&
                (g_CmdState == 'g' ||
                 (Bp->m_Flags & DEBUG_BREAKPOINT_GO_ONLY) == 0))
            {
                Bp->ForceFlatAddr();

                // Check if this breakpoint matches a previously
                // inserted breakpoint.  If so there's no need
                // to insert this one.
                Breakpoint* MatchBp;

                for (MatchBp = Bp->m_Prev;
                     MatchBp != NULL;
                     MatchBp = MatchBp->m_Prev)
                {
                    if ((MatchBp->m_Flags & BREAKPOINT_INSERTED) &&
                        Bp->IsInsertionMatch(MatchBp))
                    {
                        break;
                    }
                }
                if (MatchBp != NULL)
                {
                    // Skip this breakpoint.  It won't be marked as
                    // inserted so Remove is automatically handled.
                    continue;
                }

                if (BreakpointNeedsToBeDeferred(Bp,
                                                g_EventProcess, &PcAddr))
                {
                    g_DeferDefined = TRUE;
                    if (Bp->m_BreakType == DEBUG_BREAKPOINT_DATA)
                    {
                        DeferredData = TRUE;
                        // Force data breakpoint addresses to
                        // get updated because a dbp will now
                        // be missing.
                        g_DataBreakpointsChanged = TRUE;
                    }
                    BpOut("    deferred bp %u, dd %d\n",
                          Bp->m_Id, DeferredData);
                }
                else
                {
                    HRESULT InsertStatus;

                    InsertStatus = Bp->Insert();
                    if (InsertStatus != S_OK)
                    {
                        if (Bp->m_Flags & DEBUG_BREAKPOINT_GO_ONLY)
                        {
                            ErrOut("go ");
                        }
                        ErrOut("bp%u at ", Bp->m_Id);
                        MaskOutAddr(DEBUG_OUTPUT_ERROR, Bp->GetAddr());
                        ErrOut("failed\n");

                        Status = InsertStatus;
                    }
                }
            }
        }
    }

    ForAllLayersToTarget()
    {
        Target->EndInsertingBreakpoints();
    }

    // If we deferred a data breakpoint we haven't
    // fully updated the data breakpoint state
    // so leave the update flags set.
    if (g_UpdateDataBreakpoints && !DeferredData)
    {
        g_UpdateDataBreakpoints = FALSE;
        g_DataBreakpointsChanged = FALSE;
    }
    BpOut("  after insert udb %d, dbc %d\n",
          g_UpdateDataBreakpoints, g_DataBreakpointsChanged);

 StepTraceOnly:

    //  set the step/trace breakpoint if appropriate

    if (g_StepTraceBp->m_Flags & DEBUG_BREAKPOINT_ENABLED)
    {
        BpOut("Step/trace addr = ");
        MaskOutAddr(DEBUG_IOUTPUT_BREAKPOINT, g_StepTraceBp->GetAddr());
        BpOut("\n");

        if (Flat(*g_StepTraceBp->GetAddr()) == OFFSET_TRACE)
        {
            ThreadInfo* StepRegThread;

            Target = g_StepTraceBp->m_Process->m_Target;

            if (g_StepTraceBp->m_MatchThread &&
                IS_USER_TARGET(Target))
            {
                StepRegThread = Target->m_RegContextThread;
                Target->ChangeRegContext(g_StepTraceBp->m_MatchThread);
            }

            BpOut("Setting trace flag for step/trace thread %d:%x\n",
                  Target->m_RegContextThread ?
                  Target->m_RegContextThread->m_UserId : 0,
                  Target->m_RegContextThread ?
                  Target->m_RegContextThread->m_SystemId : 0);
            Target->m_EffMachine->
                QuietSetTraceMode(g_StepTraceCmdState == 'b' ?
                                  TRACE_TAKEN_BRANCH :
                                  TRACE_INSTRUCTION);

            if (g_StepTraceBp->m_MatchThread &&
                IS_USER_TARGET(Target))
            {
                Target->ChangeRegContext(StepRegThread);
            }
        }
        else if (IS_CONTEXT_POSSIBLE(g_EventTarget) &&
                 AddrEqu(*g_StepTraceBp->GetAddr(), PcAddr))
        {
            BpOut("Setting defer flag for step/trace\n");

            g_DeferDefined = TRUE;
        }
        else if (CheckMatchingBreakpoints(g_StepTraceBp, FALSE,
                                          BREAKPOINT_INSERTED))
        {
            // There's already a breakpoint inserted at the
            // step/trace address so we don't need to set another.
            BpOut("Trace bp matches existing bp\n");
        }
        else
        {
            if (g_StepTraceBp->Insert() != S_OK)
            {
                ErrOut("Trace bp at addr ");
                MaskOutAddr(DEBUG_OUTPUT_ERROR, g_StepTraceBp->GetAddr());
                ErrOut("failed\n");

                Status = E_FAIL;
            }
            else
            {
                BpOut("Trace bp at addr ");
                MaskOutAddr(DEBUG_IOUTPUT_BREAKPOINT,
                            g_StepTraceBp->GetAddr());
                BpOut("succeeded\n");
            }
        }
    }

    // Process deferred breakpoint.
    // If a deferred breakpoint is active it means that
    // the debugger needs to do some work on the current instruction
    // so it wants to step forward one instruction and then
    // get control back.  The deferred breakpoint forces a break
    // back to the debugger as soon as possible so that it
    // can carry out any deferred work.

    if (g_DeferDefined)
    {
        ULONG NextMachine;

        g_DeferBp->m_Process = g_EventProcess;
        g_EventTarget->m_EffMachine->
            GetNextOffset(g_EventProcess, FALSE,
                          g_DeferBp->GetAddr(), &NextMachine);
        g_DeferBp->SetProcType(NextMachine);

        BpOut("Defer addr = ");
        MaskOutAddr(DEBUG_IOUTPUT_BREAKPOINT, g_DeferBp->GetAddr());
        BpOut("\n");

        if ((g_EngOptions & DEBUG_ENGOPT_SYNCHRONIZE_BREAKPOINTS) &&
            IS_USER_TARGET(g_Target) &&
            !IsSelectedExecutionThread(NULL, SELTHREAD_ANY))
        {
            // The user wants breakpoint management to occur
            // precisely in order to properly handle breakpoints
            // in code executed by multiple threads.  Force
            // the defer thread to be the only thread executing
            // in order to avoid other threads running through
            // the breakpoint location or generating events.
            SelectExecutionThread(g_EventThread, SELTHREAD_INTERNAL_THREAD);
        }

        if (Flat(*g_DeferBp->GetAddr()) == OFFSET_TRACE)
        {
            BpOut("Setting trace flag for defer thread %d:%x\n",
                  g_EventTarget->m_RegContextThread ?
                  g_EventTarget->m_RegContextThread->m_UserId : 0,
                  g_EventTarget->m_RegContextThread ?
                  g_EventTarget->m_RegContextThread->m_SystemId : 0);
            g_EventTarget->m_EffMachine->
                QuietSetTraceMode(TRACE_INSTRUCTION);

            if (IS_USER_TARGET(g_EventTarget) &&
                g_EventThread != NULL)
            {
                // If the debugger is setting the trace flag
                // for the current thread remember that it
                // did so in order to properly recognize
                // debugger-provoked single-step events even
                // when events occur on other threads before
                // the single-step event comes back.
                g_EventThread->m_Flags |=
                    ENG_THREAD_DEFER_BP_TRACE;
            }
        }
        else
        {
            // If an existing breakpoint or the step/trace breakpoint
            // isn't already set on the next offset, insert the deferred
            // breakpoint.
            if (CheckMatchingBreakpoints(g_DeferBp, FALSE,
                                         BREAKPOINT_INSERTED) == NULL &&
                ((g_StepTraceBp->m_Flags & BREAKPOINT_INSERTED) == 0 ||
                 !AddrEqu(*g_StepTraceBp->GetAddr(), *g_DeferBp->GetAddr())))
            {
                if (g_DeferBp->Insert() != S_OK)
                {
                    ErrOut("Deferred bp at addr ");
                    MaskOutAddr(DEBUG_OUTPUT_ERROR, g_DeferBp->GetAddr());
                    ErrOut("failed\n");

                    Status = E_FAIL;
                }
                else
                {
                    BpOut("Deferred bp at addr ");
                    MaskOutAddr(DEBUG_IOUTPUT_BREAKPOINT,
                                g_DeferBp->GetAddr());
                    BpOut("succeeded\n");
                }
            }
            else
            {
                BpOut("Defer bp matches existing bp\n");
            }
        }
    }

    if (g_EventTarget)
    {
        g_EventTarget->ChangeRegContext(OldThread);
    }

    // Always consider breakpoints inserted since some
    // of them may have been inserted even if some failed.
    g_EngStatus |= ENG_STATUS_BREAKPOINTS_INSERTED;

    return Status;
}

//----------------------------------------------------------------------------
//
// Reverse any debuggee changes caused by breakpoint insertion.
//
//----------------------------------------------------------------------------

HRESULT
RemoveBreakpoints(void)
{
    if ((g_EngStatus & ENG_STATUS_BREAKPOINTS_INSERTED) == 0 ||
        (g_EngStatus & ENG_STATUS_SUSPENDED) == 0)
    {
        return S_FALSE; // do nothing
    }

    BpOut("RemoveBreakpoints\n");

    //  restore the deferred breakpoint if set
    g_DeferBp->Remove();

    //  restore the step/trace breakpoint if set
    g_StepTraceBp->Remove();

    if (!g_BreakpointsSuspended)
    {
        //
        // Restore any appropriate permanent breakpoints (reverse order).
        //

        TargetInfo* Target;
        ProcessInfo* Process;
        Breakpoint* Bp;

        ForAllLayersToTarget()
        {
            Target->BeginRemovingBreakpoints();
        }

        ForAllLayersToProcess()
        {
            BpOut("  Process %d with %d bps\n", Process->m_UserId,
                  Process->m_NumBreakpoints);

            for (Bp = Process->m_BreakpointsTail;
                 Bp != NULL;
                 Bp = Bp->m_Prev)
            {
                Bp->Remove();
            }
        }

        ForAllLayersToTarget()
        {
            Target->EndRemovingBreakpoints();
        }
    }

    g_EngStatus &= ~ENG_STATUS_BREAKPOINTS_INSERTED;
    return S_OK;
}

//----------------------------------------------------------------------------
//
// Create a new breakpoint object.
//
//----------------------------------------------------------------------------

HRESULT
AddBreakpoint(DebugClient* Client,
              MachineInfo* Machine,
              ULONG Type,
              ULONG DesiredId,
              Breakpoint** RetBp)
{
    Breakpoint* Bp;
    ULONG Id;
    TargetInfo* Target;
    ProcessInfo* Process;

    if (DesiredId == DEBUG_ANY_ID)
    {
        // Find the lowest unused ID across all processes.
        // Breakpoint IDs are kept unique across all
        // breakpoints to prevent user confusion and also
        // to give extensions a unique ID for breakpoints.
        Id = 0;

    Restart:
        // Search all bps to see if the current ID is in use.
        ForAllLayersToProcess()
        {
            for (Bp = Process->m_Breakpoints; Bp; Bp = Bp->m_Next)
            {
                if (Bp->m_Id == Id)
                {
                    // A breakpoint is already using the current ID.
                    // Try the next one.
                    Id++;
                    goto Restart;
                }
            }
        }
    }
    else
    {
        // Check to see if the desired ID is in use.
        ForAllLayersToProcess()
        {
            for (Bp = Process->m_Breakpoints; Bp != NULL; Bp = Bp->m_Next)
            {
                if (Bp->m_Id == DesiredId)
                {
                    return E_INVALIDARG;
                }
            }
        }

        Id = DesiredId;
    }

    HRESULT Status = Machine->NewBreakpoint(Client, Type, Id, &Bp);
    if (Status != S_OK)
    {
        return Status;
    }

    *RetBp = Bp;
    Bp->LinkIntoList();

    // If this is an internal, hidden breakpoint set
    // the flag immediately and do not notify.
    if (Type & BREAKPOINT_HIDDEN)
    {
        Bp->m_Flags |= BREAKPOINT_HIDDEN;
    }
    else
    {
        NotifyChangeEngineState(DEBUG_CES_BREAKPOINTS, Id, TRUE);
    }

    return S_OK;
}

//----------------------------------------------------------------------------
//
// Delete a breakpoint object.
//
//----------------------------------------------------------------------------

void
RemoveBreakpoint(Breakpoint* Bp)
{
    ULONG Id = Bp->m_Id;
    ULONG Flags = Bp->m_Flags;

    Bp->Relinquish();

    if ((Flags & BREAKPOINT_HIDDEN) == 0)
    {
        NotifyChangeEngineState(DEBUG_CES_BREAKPOINTS, Id, TRUE);
    }
}

//----------------------------------------------------------------------------
//
// Clean up breakpoints owned by a particular process or thread.
//
//----------------------------------------------------------------------------

void
RemoveProcessBreakpoints(ProcessInfo* Process)
{
    g_EngNotify++;

    Breakpoint* Bp;
    Breakpoint* NextBp;
    BOOL NeedNotify = FALSE;

    for (Bp = Process->m_Breakpoints; Bp != NULL; Bp = NextBp)
    {
        NextBp = Bp->m_Next;

        DBG_ASSERT(Bp->m_Process == Process);

        RemoveBreakpoint(Bp);
        NeedNotify = TRUE;
    }

    g_EngNotify--;
    if (NeedNotify)
    {
        NotifyChangeEngineState(DEBUG_CES_BREAKPOINTS, DEBUG_ANY_ID, TRUE);
    }
}

void
RemoveThreadBreakpoints(ThreadInfo* Thread)
{
    g_EngNotify++;

    Breakpoint* Bp;
    Breakpoint* NextBp;
    BOOL NeedNotify = FALSE;

    DBG_ASSERT(Thread->m_Process);

    for (Bp = Thread->m_Process->m_Breakpoints; Bp != NULL; Bp = NextBp)
    {
        NextBp = Bp->m_Next;

        DBG_ASSERT(Bp->m_Process == Thread->m_Process);

        if (Bp->m_MatchThread == Thread)
        {
            RemoveBreakpoint(Bp);
            NeedNotify = TRUE;
        }
    }

    g_EngNotify--;
    if (NeedNotify)
    {
        NotifyChangeEngineState(DEBUG_CES_BREAKPOINTS, DEBUG_ANY_ID, TRUE);
    }
}

//----------------------------------------------------------------------------
//
// Remove all breakpoints and reset breakpoint state.
//
//----------------------------------------------------------------------------

void
RemoveAllBreakpoints(ULONG Reason)
{
    TargetInfo* Target;
    ProcessInfo* Process;

    g_EngNotify++;

    ForAllLayersToProcess()
    {
        while (Process->m_Breakpoints != NULL)
        {
            RemoveBreakpoint(Process->m_Breakpoints);
        }
    }

    g_EngNotify--;
    NotifyChangeEngineState(DEBUG_CES_BREAKPOINTS, DEBUG_ANY_ID, TRUE);

    g_NumGoBreakpoints = 0;

    // If the machine is not waiting for commands we can't
    // remove breakpoints.  This happens when rebooting or
    // when a wait doesn't successfully receive a state change.
    if (Reason != DEBUG_SESSION_REBOOT &&
        Reason != DEBUG_SESSION_HIBERNATE &&
        Reason != DEBUG_SESSION_FAILURE &&
        (g_EngStatus & ENG_STATUS_WAIT_SUCCESSFUL))
    {
        ForAllLayersToTarget()
        {
            Target->RemoveAllTargetBreakpoints();
            ForTargetProcesses(Target)
            {
                Target->RemoveAllDataBreakpoints(Process);
            }
        }
    }

    // Always update data breakpoints the very first time in
    // order to flush out any stale data breakpoints.
    g_UpdateDataBreakpoints = TRUE;

    g_DataBreakpointsChanged = FALSE;
    g_BreakpointsSuspended = FALSE;

    g_DeferDefined = FALSE;
}

//----------------------------------------------------------------------------
//
// Look up breakpoints.
//
//----------------------------------------------------------------------------

Breakpoint*
GetBreakpointByIndex(DebugClient* Client, ULONG Index)
{
    Breakpoint* Bp;

    DBG_ASSERT(g_Process);

    for (Bp = g_Process->m_Breakpoints;
         Bp != NULL && Index > 0;
         Bp = Bp->m_Next)
    {
        Index--;
    }

    if (Bp != NULL &&
        (Bp->m_Flags & BREAKPOINT_HIDDEN) == 0 &&
        ((Bp->m_Flags & DEBUG_BREAKPOINT_ADDER_ONLY) == 0 ||
         Bp->m_Adder == Client))
    {
        return Bp;
    }

    return NULL;
}

Breakpoint*
GetBreakpointById(DebugClient* Client, ULONG Id)
{
    Breakpoint* Bp;

    DBG_ASSERT(g_Process);

    for (Bp = g_Process->m_Breakpoints; Bp != NULL; Bp = Bp->m_Next)
    {
        if (Bp->m_Id == Id)
        {
            if ((Bp->m_Flags & BREAKPOINT_HIDDEN) == 0 &&
                ((Bp->m_Flags & DEBUG_BREAKPOINT_ADDER_ONLY) == 0 ||
                 Bp->m_Adder == Client))
            {
                return Bp;
            }

            break;
        }
    }

    return NULL;
}

//----------------------------------------------------------------------------
//
// Check to see if two breakpoints refer to the same breakpoint
// conditions.
//
//----------------------------------------------------------------------------

Breakpoint*
CheckMatchingBreakpoints(Breakpoint* Match, BOOL Public, ULONG IncFlags)
{
    Breakpoint* Bp;

    for (Bp = Match->m_Process->m_Breakpoints; Bp != NULL; Bp = Bp->m_Next)
    {
        if (Bp == Match || (Bp->m_Flags & IncFlags) == 0)
        {
            continue;
        }

        if ((Public && Bp->IsPublicMatch(Match)) ||
            (!Public && Bp->IsInsertionMatch(Match)))
        {
            return Bp;
        }
    }

    return NULL;
}

//----------------------------------------------------------------------------
//
// Starting at the given breakpoint, check to see if a breakpoint
// is hit with the current processor state.  Breakpoint types
// can be included or excluded by flags.
//
//----------------------------------------------------------------------------

Breakpoint*
CheckBreakpointHit(ProcessInfo* Process, Breakpoint* Start, PADDR Addr,
                   ULONG ExbsType, ULONG IncFlags, ULONG ExcFlags,
                   PULONG HitType,
                   BOOL SetLastBreakpointHit)
{
    DBG_ASSERT(ExbsType & EXBS_BREAKPOINT_ANY);

    ULONG BreakType;

    switch(ExbsType)
    {
    case EXBS_BREAKPOINT_CODE:
        BreakType = DEBUG_BREAKPOINT_CODE;
        break;
    case EXBS_BREAKPOINT_DATA:
        BreakType = DEBUG_BREAKPOINT_DATA;
        break;
    default:
        ExbsType = EXBS_BREAKPOINT_ANY;
        break;
    }

    Breakpoint* Bp;

    BpOut("CheckBp addr ");
    MaskOutAddr(DEBUG_IOUTPUT_BREAKPOINT, Addr);
    BpOut("\n");

    for (Bp = (Start == NULL ? Process->m_Breakpoints : Start);
         Bp != NULL;
         Bp = Bp->m_Next)
    {
        // Allow different kinds of breakpoints to be scanned
        // separately if desired.

        if ((ExbsType != EXBS_BREAKPOINT_ANY &&
             Bp->m_BreakType != BreakType) ||
            (Bp->m_Flags & IncFlags) == 0 ||
            (Bp->m_Flags & ExcFlags) != 0)
        {
            continue;
        }

        // Common code is inlined here rather than in the
        // base class because both pre- and post-derived
        // checks are necessary.

        // Force recomputation of flat address.
        if (Bp->m_Flags & BREAKPOINT_VIRT_ADDR)
        {
            NotFlat(*Bp->GetAddr());
            ComputeFlatAddress(Bp->GetAddr(), NULL);
        }

        if (Bp->IsNormalEnabled())
        {
            // We've got a partial match.  Further checks
            // depend on what kind of breakpoint it is.
            *HitType = Bp->IsHit(Addr);
            if (*HitType != BREAKPOINT_NOT_HIT)
            {
                // Do a final check for the pass count.  If the
                // pass count is nonzero this will become a partial hit.
                if (*HitType == BREAKPOINT_HIT &&
                    !Bp->PassHit())
                {
                    *HitType = BREAKPOINT_HIT_IGNORED;
                }

                BpOut("  hit %u\n", Bp->m_Id);

                if (SetLastBreakpointHit)
                {
                    g_LastBreakpointHit = Bp;
                    g_Target->m_EffMachine->GetPC(&g_LastBreakpointHitPc);
                }

                return Bp;
            }
        }
    }

    BpOut("  no hit\n");

    *HitType = BREAKPOINT_NOT_HIT;

    if (SetLastBreakpointHit)
    {
        g_LastBreakpointHit = NULL;
        ZeroMemory(&g_LastBreakpointHitPc, sizeof(g_LastBreakpointHitPc));
    }

    return NULL;
}

//----------------------------------------------------------------------------
//
// Walk the breakpoint list and invoke event callbacks for
// any breakpoints that need it.  Watch for and handle list changes
// caused by callbacks.
//
//----------------------------------------------------------------------------

ULONG
NotifyHitBreakpoints(ULONG EventStatus)
{
    Breakpoint* Bp;
    TargetInfo* Target;
    ProcessInfo* Process;

 Restart:
    g_BreakpointListChanged = FALSE;

    ForAllLayersToProcess()
    {
        for (Bp = Process->m_Breakpoints; Bp != NULL; Bp = Bp->m_Next)
        {
            if (Bp->m_Flags & BREAKPOINT_NOTIFY)
            {
                // Ensure the breakpoint isn't cleaned up before
                // we're done with it.
                Bp->Preserve();

                Bp->m_Flags &= ~BREAKPOINT_NOTIFY;
                EventStatus = NotifyBreakpointEvent(EventStatus, Bp);

                if (Bp->m_Flags & DEBUG_BREAKPOINT_ONE_SHOT)
                {
                    RemoveBreakpoint(Bp);
                }

                Bp->Relinquish();

                // If the callback caused the breakpoint list to
                // change we can no longer rely on the pointer
                // we have and we need to restart the iteration.
                if (g_BreakpointListChanged)
                {
                    goto Restart;
                }
            }
        }
    }

    return EventStatus;
}

//----------------------------------------------------------------------------
//
// A module load/unload event has occurred so go through every
// breakpoint with an offset expression and reevaluate it.
//
//----------------------------------------------------------------------------

void
EvaluateOffsetExpressions(ProcessInfo* Process, ULONG Flags)
{
    static BOOL s_Evaluating;

    // Don't reevaluate when not notifying because
    // lack of notification usually means that a group
    // of operations is being done and that notify/reevaluate
    // will be done later after all of them are finished.
    // It is also possible to have nested evaluations as
    // evaluation may provoke symbol loads on deferred
    // modules, which leads to a symbol notification and
    // thus another evaluation.  If we're already evaluating
    // there's no need to evaluate again.
    if (g_EngNotify > 0 || s_Evaluating)
    {
        return;
    }
    s_Evaluating = TRUE;

    Breakpoint* Bp;
    BOOL AnyEnabled = FALSE;

    for (Bp = Process->m_Breakpoints; Bp != NULL; Bp = Bp->m_Next)
    {
        // Optimize evaluation somewhat.
        // If a module is added then deferred breakpoints
        // can become active.  If a module is removed then
        // active breakpoints can become deferred.
        // XXX drewb - This doesn't hold up with general
        // conditional expressions but currently the
        // only thing that is officially supported is a simple symbol.
        if (Bp->m_OffsetExpr != NULL &&
            (((Flags & DEBUG_CSS_LOADS) &&
              (Bp->m_Flags & DEBUG_BREAKPOINT_DEFERRED)) ||
             ((Flags & DEBUG_CSS_UNLOADS) &&
              (Bp->m_Flags & DEBUG_BREAKPOINT_DEFERRED) == 0)))
        {
            ADDR Addr;

            if (Bp->EvalOffsetExpr(BPEVAL_UNKNOWN, &Addr) &&
                (Bp->m_Flags & DEBUG_BREAKPOINT_DEFERRED) == 0)
            {
                // No need to update on newly disabled breakpoints
                // as the module is being unloaded so they'll
                // go away anyway.  The disabled breakpoint
                // is simply marked not-inserted in EvalOffsetExpr.
                AnyEnabled = TRUE;
            }
        }

        if (PollUserInterrupt(TRUE))
        {
            // Leave the interrupt set as this may be
            // called in the middle of a symbol operation
            // and we want the interrupt to interrupt
            // the entire symbol operation.
            break;
        }
    }

    if (AnyEnabled)
    {
        // A deferred breakpoint has become enabled.
        // Force a refresh of the breakpoints so
        // that the newly enabled breakpoints get inserted.
        SuspendExecution();
        RemoveBreakpoints();
    }

    s_Evaluating = FALSE;
}

//----------------------------------------------------------------------------
//
// Alters breakpoint state for b[cde]<idlist>.
//
//----------------------------------------------------------------------------

void
ChangeBreakpointState(DebugClient* Client, ProcessInfo* ForProcess,
                      ULONG Id, UCHAR StateChange)
{
    Breakpoint* Bp;
    Breakpoint* NextBp;
    TargetInfo* Target;
    ProcessInfo* Process;

    ForAllLayersToProcess()
    {
        if (ForProcess != NULL && Process != ForProcess)
        {
            continue;
        }

        for (Bp = Process->m_Breakpoints; Bp != NULL; Bp = NextBp)
        {
            // Prefetch the next breakpoint in case we remove
            // the current breakpoint from the list.
            NextBp = Bp->m_Next;

            if ((Id == ALL_ID_LIST || Bp->m_Id == Id) &&
                (Bp->m_Flags & BREAKPOINT_HIDDEN) == 0 &&
                ((Bp->m_Flags & DEBUG_BREAKPOINT_ADDER_ONLY) == 0 ||
                 Bp->m_Adder == Client))
            {
                if (StateChange == 'c')
                {
                    RemoveBreakpoint(Bp);
                }
                else
                {
                    if (StateChange == 'e')
                    {
                        Bp->AddFlags(DEBUG_BREAKPOINT_ENABLED);
                    }
                    else
                    {
                        Bp->RemoveFlags(DEBUG_BREAKPOINT_ENABLED);
                    }
                }
            }
        }
    }
}

//----------------------------------------------------------------------------
//
// Lists current breakpoints for bl.
//
//----------------------------------------------------------------------------

void
ListBreakpoints(DebugClient* Client, ProcessInfo* ForProcess,
                ULONG Id)
{
    StackSaveLayers Save;
    Breakpoint* Bp;
    TargetInfo* Target;
    ProcessInfo* Process;

    ForAllLayersToProcess()
    {
        if (ForProcess != NULL && Process != ForProcess)
        {
            continue;
        }

        SetLayersFromProcess(Process);

        for (Bp = Process->m_Breakpoints; Bp != NULL; Bp = Bp->m_Next)
        {
            char StatusChar;

            if ((Bp->m_Flags & BREAKPOINT_HIDDEN) ||
                ((Bp->m_Flags & DEBUG_BREAKPOINT_ADDER_ONLY) &&
                 Client != Bp->m_Adder) ||
                (Id != ALL_ID_LIST && Bp->m_Id != Id))
            {
                continue;
            }

            if (Bp->m_Flags & DEBUG_BREAKPOINT_ENABLED)
            {
                if (Bp->m_Flags & BREAKPOINT_KD_INTERNAL)
                {
                    StatusChar = (Bp->m_Flags & BREAKPOINT_KD_COUNT_ONLY) ?
                        'i' : 'w';
                }
                else
                {
                    StatusChar = 'e';
                }
            }
            else
            {
                StatusChar = 'd';
            }

            dprintf("%2u %c", Bp->m_Id, StatusChar);

            if (Bp->GetProcType() != g_Target->m_MachineType)
            {
                dprintf("%s ",
                        Bp->m_Process->m_Target->
                        m_Machines[Bp->GetProcIndex()]->m_AbbrevName);
            }

            if ((Bp->m_Flags & DEBUG_BREAKPOINT_DEFERRED) == 0)
            {
                dprintf(" ");
                if (Bp->m_BreakType == DEBUG_BREAKPOINT_CODE &&
                    (g_SrcOptions & SRCOPT_STEP_SOURCE))
                {
                    if (!OutputLineAddr(Flat(*Bp->GetAddr()), "[%s @ %d]"))
                    {
                        dprintAddr(Bp->GetAddr());
                    }
                }
                else
                {
                    dprintAddr(Bp->GetAddr());
                }
            }
            else if (g_Target->m_Machine->m_Ptr64)
            {
                dprintf("u                  ");
            }
            else
            {
                dprintf("u         ");
            }

            char OptionChar;

            if (Bp->m_BreakType == DEBUG_BREAKPOINT_DATA)
            {
                switch(Bp->m_DataAccessType)
                {
                case DEBUG_BREAK_EXECUTE:
                    OptionChar = 'e';
                    break;
                case DEBUG_BREAK_WRITE:
                    OptionChar = 'w';
                    break;
                case DEBUG_BREAK_IO:
                    OptionChar = 'i';
                    break;
                case DEBUG_BREAK_READ:
                case DEBUG_BREAK_READ | DEBUG_BREAK_WRITE:
                    OptionChar = 'r';
                    break;
                default:
                    OptionChar = '?';
                    break;
                }
                dprintf("%c %d", OptionChar, Bp->m_DataSize);
            }
            else
            {
                dprintf("   ");
            }

            if (Bp->m_Flags & DEBUG_BREAKPOINT_ONE_SHOT)
            {
                dprintf("/1 ");
            }

            dprintf(" %04lx (%04lx) ",
                    Bp->m_CurPassCount, Bp->m_PassCount);

            if ((Bp->m_Flags & DEBUG_BREAKPOINT_DEFERRED) == 0)
            {
                if (IS_USER_TARGET(Bp->m_Process->m_Target))
                {
                    dprintf("%2ld:", Bp->m_Process->m_UserId);
                    if (Bp->m_MatchThread != NULL)
                    {
                        dprintf("~%03ld ", Bp->m_MatchThread->m_UserId);
                    }
                    else
                    {
                        dprintf("*** ");
                    }
                }

                OutputSymAddr(Flat(*Bp->GetAddr()), SYMADDR_FORCE, NULL);

                if (Bp->m_Command != NULL)
                {
                    dprintf("\"%s\"", Bp->m_Command);
                }
            }
            else
            {
                dprintf(" (%s)", Bp->m_OffsetExpr);
            }

            dprintf("\n");

            if (Bp->m_MatchThreadData || Bp->m_MatchProcessData)
            {
                dprintf("   ");
                if (Bp->m_MatchThreadData)
                {
                    dprintf("  Match thread data %s",
                            FormatAddr64(Bp->m_MatchThreadData));
                }
                if (Bp->m_MatchProcessData)
                {
                    dprintf("  Match process data %s",
                            FormatAddr64(Bp->m_MatchProcessData));
                }
                dprintf("\n");
            }
        }
    }

    if (IS_KERNEL_TARGET(g_Target))
    {
        dprintf("\n");

        ForAllLayersToProcess()
        {
            if (ForProcess != NULL && Process != ForProcess)
            {
                continue;
            }

            SetLayersFromProcess(Process);

            for (Bp = Process->m_Breakpoints; Bp != NULL; Bp = Bp->m_Next)
            {
                if (Bp->m_Flags & BREAKPOINT_KD_INTERNAL)
                {
                    ULONG Flags, Calls, MinInst, MaxInst, TotInst, MaxCps;

                    g_Target->QueryTargetCountBreakpoint(Bp->GetAddr(),
                                                         &Flags,
                                                         &Calls,
                                                         &MinInst,
                                                         &MaxInst,
                                                         &TotInst,
                                                         &MaxCps);
                    dprintf("%s %6d %8d %8d %8d %2x %4d",
                            FormatAddr64(Flat(*Bp->GetAddr())),
                            Calls, MinInst, MaxInst,
                            TotInst, Flags, MaxCps);
                    OutputSymAddr(Flat(*Bp->GetAddr()), SYMADDR_FORCE, " ");
                    dprintf("\n");
                }
            }
        }
    }
}

//----------------------------------------------------------------------------
//
// Outputs commands necessary to recreate current breakpoints.
//
//----------------------------------------------------------------------------

void
ListBreakpointsAsCommands(DebugClient* Client, ProcessInfo* Process,
                          ULONG Flags)
{
    Breakpoint* Bp;

    if (Process == NULL)
    {
        return;
    }

    for (Bp = Process->m_Breakpoints; Bp != NULL; Bp = Bp->m_Next)
    {
        if ((Bp->m_Flags & BREAKPOINT_HIDDEN) ||
            ((Bp->m_Flags & DEBUG_BREAKPOINT_ADDER_ONLY) &&
             Client != Bp->m_Adder) ||
            ((Flags & BPCMDS_EXPR_ONLY && Bp->m_OffsetExpr == NULL)))
        {
            continue;
        }

        if (IS_USER_TARGET(Bp->m_Process->m_Target))
        {
            if (Bp->m_MatchThread != NULL ||
                Bp->m_MatchThreadData ||
                Bp->m_MatchProcessData)
            {
                // Ignore thread- and data-specific breakpoints
                // as the things they are specific to may
                // not exist in a new session.
                continue;
            }
        }

        if (Bp->GetProcType() != Process->m_Target->m_MachineType)
        {
            dprintf(".effmach %s;%c",
                    Bp->m_Process->m_Target->
                    m_Machines[Bp->GetProcIndex()]->m_AbbrevName,
                    (Flags & BPCMDS_ONE_LINE) ? ' ' : '\n');
        }

        if ((Flags & BPCMDS_MODULE_HINT) &&
            (Bp->m_Flags & (DEBUG_BREAKPOINT_DEFERRED |
                            BREAKPOINT_VIRT_ADDR)) == BREAKPOINT_VIRT_ADDR)
        {
            ImageInfo* Image =
                Bp->m_Process->FindImageByOffset(Flat(*Bp->GetAddr()), FALSE);
            if (Image != NULL)
            {
                dprintf("ld %s;%c", Image->m_ModuleName,
                    (Flags & BPCMDS_ONE_LINE) ? ' ' : '\n');
            }
        }

        char TypeChar;

        if (Bp->m_Flags & BREAKPOINT_KD_INTERNAL)
        {
            TypeChar = (Bp->m_Flags & BREAKPOINT_KD_COUNT_ONLY) ? 'i' : 'w';
        }
        else if (Bp->m_BreakType == DEBUG_BREAKPOINT_CODE)
        {
            TypeChar = Bp->m_OffsetExpr != NULL ? 'u' : 'p';
        }
        else
        {
            TypeChar = 'a';
        }

        dprintf("b%c%u", TypeChar, Bp->m_Id);

        char OptionChar;

        if (Bp->m_BreakType == DEBUG_BREAKPOINT_DATA)
        {
            switch(Bp->m_DataAccessType)
            {
            case DEBUG_BREAK_EXECUTE:
                OptionChar = 'e';
                break;
            case DEBUG_BREAK_WRITE:
                OptionChar = 'w';
                break;
            case DEBUG_BREAK_IO:
                OptionChar = 'i';
                break;
            case DEBUG_BREAK_READ:
            case DEBUG_BREAK_READ | DEBUG_BREAK_WRITE:
                OptionChar = 'r';
                break;
            default:
                continue;
            }
            dprintf(" %c%d", OptionChar, Bp->m_DataSize);
        }

        if (Bp->m_Flags & DEBUG_BREAKPOINT_ONE_SHOT)
        {
            dprintf(" /1");
        }

        if (Bp->m_OffsetExpr != NULL)
        {
            dprintf(" %s", Bp->m_OffsetExpr);
        }
        else
        {
            dprintf(" 0x");
            dprintAddr(Bp->GetAddr());
        }

        if (Bp->m_PassCount > 1)
        {
            dprintf(" 0x%x", Bp->m_PassCount);
        }

        if (Bp->m_Command != NULL)
        {
            dprintf(" \"%s\"", Bp->m_Command);
        }

        dprintf(";%c", (Flags & BPCMDS_ONE_LINE) ? ' ' : '\n');

        if ((Flags & BPCMDS_FORCE_DISABLE) ||
            (Bp->m_Flags & DEBUG_BREAKPOINT_ENABLED) == 0)
        {
            dprintf("bd %u;%c", Bp->m_Id,
                    (Flags & BPCMDS_ONE_LINE) ? ' ' : '\n');
        }

        if (Bp->GetProcType() != Process->m_Target->m_MachineType)
        {
            dprintf(".effmach .;%c",
                    (Flags & BPCMDS_ONE_LINE) ? ' ' : '\n');
        }
    }

    if (Flags & BPCMDS_ONE_LINE)
    {
        dprintf("\n");
    }
}

//----------------------------------------------------------------------------
//
// Parses command-line breakpoint commands for b[aimpw].
//
//----------------------------------------------------------------------------

struct SET_SYMBOL_MATCH_BP
{
    DebugClient* Client;
    ProcessInfo* Process;
    PSTR MatchString;
    Breakpoint* ProtoBp;
    ULONG Matches;
    ULONG Error;
};

BOOL CALLBACK
SetSymbolMatchBp(PSYMBOL_INFO SymInfo,
                 ULONG Size,
                 PVOID UserContext)
{
    SET_SYMBOL_MATCH_BP* Context =
        (SET_SYMBOL_MATCH_BP*)UserContext;

    ImageInfo* Image = Context->Process->
        FindImageByOffset(SymInfo->Address, FALSE);
    if (!Image)
    {
        return TRUE;
    }

    MachineInfo* Machine = MachineTypeInfo(Context->Process->m_Target,
                                           Image->GetMachineType());

    if (IgnoreEnumeratedSymbol(Context->Process, Context->MatchString,
                               Machine, SymInfo) ||
        !ForceSymbolCodeAddress(Context->Process, SymInfo, Machine))
    {
        return TRUE;
    }

    ADDR Addr;

    ADDRFLAT(&Addr, SymInfo->Address);
    if (FAILED(Context->ProtoBp->CheckAddr(&Addr)))
    {
        Context->Error = MEMORY;
        return FALSE;
    }

    Breakpoint* Bp;

    if (AddBreakpoint(Context->Client, Machine, DEBUG_BREAKPOINT_CODE,
                      DEBUG_ANY_ID, &Bp) != S_OK)
    {
        Context->Error = BPLISTFULL;
        return FALSE;
    }

    if (Context->ProtoBp->m_Flags & DEBUG_BREAKPOINT_ONE_SHOT)
    {
        Bp->AddFlags(DEBUG_BREAKPOINT_ONE_SHOT);
    }

    Bp->m_CodeFlags = Context->ProtoBp->m_CodeFlags;
    Bp->m_MatchProcessData = Context->ProtoBp->m_MatchProcessData;
    Bp->m_MatchThreadData = Context->ProtoBp->m_MatchThreadData;
    Bp->SetPassCount(Context->ProtoBp->m_PassCount);
    if (Context->ProtoBp->m_MatchThread)
    {
        Bp->SetMatchThreadId(Context->ProtoBp->m_MatchThread->m_UserId);
    }

    if (Bp->SetAddr(&Addr, BREAKPOINT_REMOVE_MATCH) != S_OK ||
        (Context->ProtoBp->m_Command &&
         Bp->SetCommand(Context->ProtoBp->m_Command) != S_OK))
    {
        Bp->Relinquish();
        Context->Error = NOMEMORY;
        return FALSE;
    }

    Bp->AddFlags(DEBUG_BREAKPOINT_ENABLED);

    dprintf("%3d: %s %s!%s\n",
            Bp->m_Id, FormatAddr64(SymInfo->Address),
            Image->m_ModuleName, SymInfo->Name);

    Context->Matches++;

    return TRUE;
}

PDEBUG_BREAKPOINT
ParseBpCmd(DebugClient* Client,
           UCHAR Type,
           ThreadInfo* Thread)
{
    ULONG UserId = DEBUG_ANY_ID;
    char Ch;
    ADDR Addr;
    Breakpoint* Bp;

    if (IS_LOCAL_KERNEL_TARGET(g_Target) || IS_DUMP_TARGET(g_Target))
    {
        error(SESSIONNOTSUP);
    }
    if (!IS_CUR_CONTEXT_ACCESSIBLE())
    {
        error(BADTHREAD);
    }

    if (IS_LIVE_USER_TARGET(g_Target) && Type == 'a' &&
        (g_EngStatus & ENG_STATUS_AT_INITIAL_BREAK))
    {
        ErrOut("The system resets thread contexts after the process\n");
        ErrOut("breakpoint so hardware breakpoints cannot be set.\n");
        ErrOut("Go to the executable's entry point and set it then.\n");
        *g_CurCmd = 0;
        return NULL;
    }

    //  get the breakpoint number if given

    Ch = *g_CurCmd;
    if (Ch == '[')
    {
        UserId = (ULONG)GetTermExpression("Breakpoint ID missing from");
    }
    else if (Ch >= '0' && Ch <= '9')
    {
        UserId = Ch - '0';
        Ch = *++g_CurCmd;
        while (Ch >= '0' && Ch <= '9')
        {
            UserId = UserId * 10 + Ch - '0';
            Ch = *++g_CurCmd;
        }

        if (Ch != ' ' && Ch != '\t' && Ch != '\0')
        {
            error(SYNTAX);
        }
    }

    if (UserId != DEBUG_ANY_ID)
    {
        // Remove any existing breakpoint with the given ID.
        Breakpoint* IdBp;

        if (Type == 'm')
        {
            error(SYNTAX);
        }

        if ((IdBp = GetBreakpointById(Client, UserId)) != NULL)
        {
            WarnOut("breakpoint %ld exists, redefining\n", UserId);
            RemoveBreakpoint(IdBp);
        }
    }

    // Create a new breakpoint.
    if (AddBreakpoint(Client, g_Machine, Type == 'a' ?
                      DEBUG_BREAKPOINT_DATA : DEBUG_BREAKPOINT_CODE,
                      UserId, &Bp) != S_OK)
    {
        error(BPLISTFULL);
    }

    // Add in KD internal flags if necessary.
    if (Type == 'i' || Type == 'w')
    {
        if (IS_KERNEL_TARGET(g_Target))
        {
            Bp->m_Flags = Bp->m_Flags | BREAKPOINT_KD_INTERNAL |
                (Type == 'i' ? BREAKPOINT_KD_COUNT_ONLY : 0);
            if (Type == 'w')
            {
                g_Target->InitializeTargetControlledStepping();
            }
        }
        else
        {
            // KD internal breakpoints are only supported in
            // kernel debugging.
            Bp->Relinquish();
            error(SYNTAX);
        }
    }

    // If data breakpoint, get option and size values.
    if (Type == 'a')
    {
        ULONG64 Size;
        ULONG AccessType;

        Ch = PeekChar();
        Ch = (char)tolower(Ch);

        if (Ch == 'e')
        {
            AccessType = DEBUG_BREAK_EXECUTE;
        }
        else if (Ch == 'w')
        {
            AccessType = DEBUG_BREAK_WRITE;
        }
        else if (Ch == 'i')
        {
            AccessType = DEBUG_BREAK_IO;
        }
        else if (Ch == 'r')
        {
            AccessType = DEBUG_BREAK_READ;
        }
        else
        {
            Bp->Relinquish();
            error(SYNTAX);
        }

        g_CurCmd++;
        Size = GetTermExpression("Hardware breakpoint length missing from");
        if (Size & ~ULONG(-1))
        {
            ErrOut("Breakpoint length too big\n");
            Bp->Relinquish();
            error(SYNTAX);
        }

        // Validate the selections.  This assumes that
        // the default offset of zero won't cause problems.
        if (Bp->SetDataParameters((ULONG)Size, AccessType) != S_OK)
        {
            Bp->Relinquish();
            error(SYNTAX);
        }

        g_CurCmd++;
    }

    //
    // Parse breakpoint options.
    //

    while (PeekChar() == '/')
    {
        g_CurCmd++;
        switch(*g_CurCmd++)
        {
        case '1':
            Bp->AddFlags(DEBUG_BREAKPOINT_ONE_SHOT);
            break;
        case 'f':
            Bp->m_CodeFlags = (ULONG)GetTermExpression(NULL);
            break;
        case 'p':
            Bp->m_MatchProcessData = GetTermExpression(NULL);
            break;
        case 't':
            Bp->m_MatchThreadData = GetTermExpression(NULL);
            break;
        default:
            ErrOut("Unknown option '%c'\n", *g_CurCmd);
            break;
        }
    }

    PSTR ExprStart, ExprEnd;

    Ch = PeekChar();

    if (Type == 'm')
    {
        char Save;

        ExprStart = StringValue(STRV_SPACE_IS_SEPARATOR |
                                STRV_TRIM_TRAILING_SPACE, &Save);
        ExprEnd = g_CurCmd;
        *g_CurCmd = Save;
        Ch = PeekChar();
    }
    else
    {
        //
        // Get the breakpoint address, if given, otherwise
        // default to the current IP.
        //

        BreakpointEvalResult AddrValid = BPEVAL_RESOLVED;

        g_Target->m_EffMachine->GetPC(&Addr);

        if (Ch != '"' && Ch != '\0')
        {
            ExprStart = g_CurCmd;

            g_PrefixSymbols = Type == 'p' || Type == 'u';

            AddrValid = EvalAddrExpression(g_Process,
                                           g_Target->m_EffMachineType,
                                           &Addr);

            g_PrefixSymbols = FALSE;

            if (AddrValid == BPEVAL_ERROR)
            {
                Bp->Relinquish();
                return NULL;
            }

            // If an unresolved symbol was encountered this
            // breakpoint will be deferred.  Users can also force
            // breakpoints to use expressions for cases where the
            // address could be resolved but also may become invalid
            // later.
            if (Type == 'u' || AddrValid == BPEVAL_UNRESOLVED)
            {
                HRESULT Status;
                UCHAR Save = *g_CurCmd;
                *g_CurCmd = 0;

                Status = Bp->SetEvaluatedOffsetExpression(ExprStart,
                                                          AddrValid,
                                                          &Addr);

                if (Type != 'u' && Status == S_OK)
                {
                    WarnOut("Bp expression '%s' could not be resolved, "
                            "adding deferred bp\n", ExprStart);
                }

                *g_CurCmd = Save;

                if (Status != S_OK)
                {
                    Bp->Relinquish();
                    error(NOMEMORY);
                }
            }

            Ch = PeekChar();
        }

        if (AddrValid != BPEVAL_UNRESOLVED &&
            FAILED(Bp->CheckAddr(&Addr)))
        {
            Bp->Relinquish();
            error(MEMORY);
        }

        // The public interface only supports flat addresses
        // so use an internal method to set the true address.
        // Do not allow matching breakpoints through the parsing
        // interface as that was the previous behavior.
        if (Bp->SetAddr(&Addr, BREAKPOINT_REMOVE_MATCH) != S_OK)
        {
            Bp->Relinquish();
            error(SYNTAX);
        }
    }

    // Get the pass count, if given.
    if (Ch != '"' && Ch != ';' && Ch != '\0')
    {
        ULONG64 PassCount = GetExpression();
        if (PassCount < 1 || PassCount > 0xffffffff)
        {
            error(BADRANGE);
        }
        Bp->SetPassCount((ULONG)PassCount);
        Ch = PeekChar();
    }

    // If next character is double quote, get the command string.
    if (Ch == '"')
    {
        PSTR Str;
        CHAR Save;

        Str = StringValue(STRV_ESCAPED_CHARACTERS, &Save);

        if (Bp->SetCommand(Str) != S_OK)
        {
            Bp->Relinquish();
            error(NOMEMORY);
        }

        *g_CurCmd = Save;
    }

    // Set some final information.
    if (Thread != NULL)
    {
        Bp->SetMatchThreadId(Thread->m_UserId);
    }

    // Turn breakpoint on.
    Bp->AddFlags(DEBUG_BREAKPOINT_ENABLED);

    if (Type == 'm')
    {
        SET_SYMBOL_MATCH_BP Context;

        // Now that we have the prototype breakpoint created,
        // enumerate all symbol matches for the symbol
        // expression and create specific breakpoints for
        // each hit from the prototype breakpoint.
        *ExprEnd = 0;
        Context.Client = Client;
        Context.Process = g_Process;
        Context.MatchString = ExprStart;
        Context.ProtoBp = Bp;
        Context.Matches = 0;
        Context.Error = 0;

        SymEnumSymbols(g_Process->m_SymHandle, 0, ExprStart,
                       SetSymbolMatchBp, &Context);
        if (Context.Error)
        {
            error(Context.Error);
        }
        if (!Context.Matches)
        {
            ErrOut("No matching symbols found, no breakpoints set\n");
        }

        Bp->Relinquish();
        Bp = NULL;
    }

    return Bp;
}

inline BOOL
IsCodeBreakpointInsertedInRange(Breakpoint* Bp,
                                ULONG64 Start, ULONG64 End)
{
    return (Bp->m_Flags & BREAKPOINT_INSERTED) &&
        Bp->m_BreakType == DEBUG_BREAKPOINT_CODE &&
        Flat(*Bp->GetAddr()) >= Start &&
        Flat(*Bp->GetAddr()) <= End;
}

BOOL
CheckBreakpointInsertedInRange(ProcessInfo* Process,
                               ULONG64 Start, ULONG64 End)
{
    if ((g_EngStatus & ENG_STATUS_BREAKPOINTS_INSERTED) == 0)
    {
        return FALSE;
    }

    //
    // Check for a breakpoint that might have caused
    // a break instruction to be inserted in the given
    // offset range.  Data breakpoints don't count
    // as they don't actually modify the address they
    // break on.
    //

    Breakpoint* Bp;

    for (Bp = Process->m_Breakpoints; Bp != NULL; Bp = Bp->m_Next)
    {
        if (IsCodeBreakpointInsertedInRange(Bp, Start, End))
        {
            return TRUE;
        }
    }

    if ((g_DeferBp->m_Process == Process &&
         IsCodeBreakpointInsertedInRange(g_DeferBp, Start, End)) ||
        (g_StepTraceBp->m_Process == Process &&
         IsCodeBreakpointInsertedInRange(g_StepTraceBp, Start, End)))
    {
        return TRUE;
    }

    return FALSE;
}

//----------------------------------------------------------------------------
//
// TargetInfo methods.
//
//----------------------------------------------------------------------------

HRESULT
TargetInfo::BeginInsertingBreakpoints(void)
{
    ProcessInfo* Process;
    ThreadInfo* Thread;

    ForTargetProcesses(this)
    {
        ForProcessThreads(Process)
        {
            DataBreakpoint::ClearThreadDataBreaks(Thread);
        }
    }

    return S_OK;
}

HRESULT
TargetInfo::InsertDataBreakpoint(ProcessInfo* Process,
                                 ThreadInfo* Thread,
                                 class MachineInfo* Machine,
                                 PADDR Addr,
                                 ULONG Size,
                                 ULONG AccessType,
                                 PUCHAR StorageSpace)
{
    // Ask for default processing.
    return S_FALSE;
}

void
TargetInfo::EndInsertingBreakpoints(void)
{
    ProcessInfo* Process;
    ThreadInfo* Thread;

    if (!g_UpdateDataBreakpoints ||
        !IS_CONTEXT_POSSIBLE(this))
    {
        return;
    }

    //
    // It's the target machine's responsibility to manage
    // all data breakpoints for all machines, so always
    // force the usage of the target machine here.
    //

    ThreadInfo* SaveThread = m_RegContextThread;

    ForTargetProcesses(this)
    {
        ForProcessThreads(Process)
        {
            SetLayersFromThread(Thread);
            ChangeRegContext(Thread);
            m_Machine->InsertThreadDataBreakpoints();
        }
    }

    ChangeRegContext(SaveThread);
}

void
TargetInfo::BeginRemovingBreakpoints(void)
{
    // No work necessary.
}

HRESULT
TargetInfo::RemoveDataBreakpoint(ProcessInfo* Process,
                                 ThreadInfo* Thread,
                                 class MachineInfo* Machine,
                                 PADDR Addr,
                                 ULONG Size,
                                 ULONG AccessType,
                                 PUCHAR StorageSpace)
{
    // Ask for default processing.
    return S_FALSE;
}

void
TargetInfo::EndRemovingBreakpoints(void)
{
    // No work necessary.
}

HRESULT
TargetInfo::RemoveAllDataBreakpoints(ProcessInfo* Process)
{
    // No work necessary.
    return S_OK;
}

HRESULT
TargetInfo::RemoveAllTargetBreakpoints(void)
{
    // No work necessary.
    return S_OK;
}

HRESULT
TargetInfo::IsDataBreakpointHit(ThreadInfo* Thread,
                                PADDR Addr,
                                ULONG Size,
                                ULONG AccessType,
                                PUCHAR StorageSpace)
{
    // Ask for default processing.
    return S_FALSE;
}

HRESULT
ConnLiveKernelTargetInfo::InsertCodeBreakpoint(ProcessInfo* Process,
                                               MachineInfo* Machine,
                                               PADDR Addr,
                                               ULONG InstrFlags,
                                               PUCHAR StorageSpace)
{
    if (InstrFlags != IBI_DEFAULT)
    {
        return E_INVALIDARG;
    }

    DBGKD_MANIPULATE_STATE64 m;
    PDBGKD_MANIPULATE_STATE64 Reply;
    PDBGKD_WRITE_BREAKPOINT64 a = &m.u.WriteBreakPoint;
    NTSTATUS st;
    ULONG rc;

    //
    // Format state manipulate message
    //

    m.ApiNumber = DbgKdWriteBreakPointApi;
    m.ReturnStatus = STATUS_PENDING;
    a->BreakPointAddress = Flat(*Addr);

    //
    // Send the message and context and then wait for reply
    //

    do
    {
        m_Transport->WritePacket(&m, sizeof(m),
                                 PACKET_TYPE_KD_STATE_MANIPULATE,
                                 NULL, 0);
        rc = m_Transport->
            WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
    } while (rc != DBGKD_WAIT_PACKET ||
             Reply->ApiNumber != DbgKdWriteBreakPointApi);

    st = Reply->ReturnStatus;

    *(PULONG)StorageSpace = Reply->u.WriteBreakPoint.BreakPointHandle;

    KdOut("DbgKdWriteBreakPoint(%s) returns %08lx, %x\n",
          FormatAddr64(Flat(*Addr)), st,
          Reply->u.WriteBreakPoint.BreakPointHandle);

    return CONV_NT_STATUS(st);
}

NTSTATUS
ConnLiveKernelTargetInfo::KdRestoreBreakPoint(ULONG BreakPointHandle)
{
    DBGKD_MANIPULATE_STATE64 m;
    PDBGKD_MANIPULATE_STATE64 Reply;
    PDBGKD_RESTORE_BREAKPOINT a = &m.u.RestoreBreakPoint;
    NTSTATUS st;
    ULONG rc;

    //
    // Format state manipulate message
    //

    m.ApiNumber = DbgKdRestoreBreakPointApi;
    m.ReturnStatus = STATUS_PENDING;
    a->BreakPointHandle = BreakPointHandle;

    //
    // Send the message and context and then wait for reply
    //

    do
    {
        m_Transport->WritePacket(&m, sizeof(m),
                                 PACKET_TYPE_KD_STATE_MANIPULATE,
                                 NULL, 0);
        rc = m_Transport->
            WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
    } while (rc != DBGKD_WAIT_PACKET ||
             Reply->ApiNumber != DbgKdRestoreBreakPointApi);

    st = Reply->ReturnStatus;

    KdOut("DbgKdRestoreBreakPoint(%x) returns %08lx\n",
          BreakPointHandle, st);

    return st;
}

HRESULT
ConnLiveKernelTargetInfo::RemoveCodeBreakpoint(ProcessInfo* Process,
                                               MachineInfo* Machine,
                                               PADDR Addr,
                                               ULONG InstrFlags,
                                               PUCHAR StorageSpace)
{
    // When the kernel fills out the CONTROL_REPORT.InstructionStream
    // array it clears any breakpoints that might fall within the
    // array.  This means that some breakpoints may already be
    // restored, so the restore call will fail.  We could do some
    // checks to try and figure out which ones might be affected
    // but it doesn't seem worthwhile.  Just ignore the return
    // value from the restore.
    KdRestoreBreakPoint(*(PULONG)StorageSpace);
    return S_OK;
}

HRESULT
ConnLiveKernelTargetInfo::RemoveAllDataBreakpoints(ProcessInfo* Process)
{
    if (m_Transport->m_WaitingThread)
    {
        // A thread is waiting so we can't communicate
        // with the target machine.
        return E_UNEXPECTED;
    }

    // If there were any data breakpoints active
    // remove them from all processors.  This can't be in
    // RemoveAllKernelBreakpoints as that
    // code is called in the middle of state
    // change processing when the context hasn't
    // been initialized.
    if (g_UpdateDataBreakpoints)
    {
        ULONG Proc;

        SetEffMachine(m_MachineType, FALSE);

        g_EngNotify++;
        for (Proc = 0; Proc < m_NumProcessors; Proc++)
        {
            SetCurrentProcessorThread(this, Proc, TRUE);

            // Force the context to be dirty so it
            // gets written back.
            m_Machine->GetContextState(MCTX_DIRTY);
            m_Machine->RemoveThreadDataBreakpoints();
        }
        g_EngNotify--;

        // Flush final context.
        ChangeRegContext(NULL);
    }

    return S_OK;
}

HRESULT
ConnLiveKernelTargetInfo::RemoveAllTargetBreakpoints(void)
{
    ULONG i;

    if (m_Transport->m_WaitingThread)
    {
        // A thread is waiting so we can't communicate
        // with the target machine.
        return E_UNEXPECTED;
    }

    // Indices are array index plus one.
    for (i = 1; i <= BREAKPOINT_TABLE_SIZE; i++)
    {
        KdRestoreBreakPoint(i);
    }

    // ClearAllInternalBreakpointsApi was added for XP
    // so it fails against any previous OS.
    if (m_KdMaxManipulate > DbgKdClearAllInternalBreakpointsApi)
    {
        DBGKD_MANIPULATE_STATE64 Request;

        Request.ApiNumber = DbgKdClearAllInternalBreakpointsApi;
        Request.ReturnStatus = STATUS_PENDING;

        m_Transport->WritePacket(&Request, sizeof(Request),
                                 PACKET_TYPE_KD_STATE_MANIPULATE,
                                 NULL, 0);
    }

    return S_OK;
}

HRESULT
ConnLiveKernelTargetInfo::InsertTargetCountBreakpoint(PADDR Addr,
                                                      ULONG Flags)
{
    DBGKD_MANIPULATE_STATE64 m;
    ULONG64 Offset = Flat(*Addr);

    m.ApiNumber = DbgKdSetInternalBreakPointApi;
    m.ReturnStatus = STATUS_PENDING;

#ifdef IBP_WORKAROUND
    // The kernel code keeps a ULONG64 for an internal breakpoint
    // address but older kernels did not sign-extend the current IP
    // when comparing against them.  In order to work with both
    // broken and fixed kernels send down zero-extended addresses.
    // Don't actually enable this workaround right now as other
    // internal breakpoint bugs can cause the machine to bugcheck.
    Offset = m_Machine->m_Ptr64 ? Offset : (ULONG)Offset;
#endif

    m.u.SetInternalBreakpoint.BreakpointAddress = Offset;
    m.u.SetInternalBreakpoint.Flags = Flags;

    m_Transport->WritePacket(&m, sizeof(m),
                             PACKET_TYPE_KD_STATE_MANIPULATE,
                             NULL, 0);

    KdOut("DbgKdSetInternalBp returns 0x00000000\n");
    return S_OK;
}

HRESULT
ConnLiveKernelTargetInfo::RemoveTargetCountBreakpoint(PADDR Addr)
{
    DBGKD_MANIPULATE_STATE64 m;
    ULONG64 Offset = Flat(*Addr);

    m.ApiNumber = DbgKdSetInternalBreakPointApi;
    m.ReturnStatus = STATUS_PENDING;

#ifdef IBP_WORKAROUND
    // The kernel code keeps a ULONG64 for an internal breakpoint
    // address but older kernels did not sign-extend the current IP
    // when comparing against them.  In order to work with both
    // broken and fixed kernels send down zero-extended addresses.
    // Don't actually enable this workaround right now as other
    // internal breakpoint bugs can cause the machine to bugcheck.
    Offset = m_Machine->m_Ptr64 ? Offset : (ULONG)Offset;
#endif

    m.u.SetInternalBreakpoint.BreakpointAddress = Offset;
    m.u.SetInternalBreakpoint.Flags = DBGKD_INTERNAL_BP_FLAG_INVALID;

    m_Transport->WritePacket(&m, sizeof(m),
                             PACKET_TYPE_KD_STATE_MANIPULATE,
                             NULL, 0);

    KdOut("DbgKdSetInternalBp returns 0x00000000\n");
    return S_OK;
}

HRESULT
ConnLiveKernelTargetInfo::QueryTargetCountBreakpoint(PADDR Addr,
                                                     PULONG Flags,
                                                     PULONG Calls,
                                                     PULONG MinInstr,
                                                     PULONG MaxInstr,
                                                     PULONG TotInstr,
                                                     PULONG MaxCps)
{
    DBGKD_MANIPULATE_STATE64 m;
    PDBGKD_MANIPULATE_STATE64 Reply;
    ULONG rc;
    ULONG64 Offset = Flat(*Addr);

    m.ApiNumber = DbgKdGetInternalBreakPointApi;
    m.ReturnStatus = STATUS_PENDING;

#ifdef IBP_WORKAROUND
    // The kernel code keeps a ULONG64 for an internal breakpoint
    // address but older kernels did not sign-extend the current IP
    // when comparing against them.  In order to work with both
    // broken and fixed kernels send down zero-extended addresses.
    // Don't actually enable this workaround right now as other
    // internal breakpoint bugs can cause the machine to bugcheck.
    Offset = m_Machine->m_Ptr64 ? Offset : (ULONG)Offset;
#endif

    m.u.GetInternalBreakpoint.BreakpointAddress = Offset;

    do
    {
        m_Transport->WritePacket(&m, sizeof(m),
                                 PACKET_TYPE_KD_STATE_MANIPULATE,
                                 NULL, 0);
        rc = m_Transport->
            WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
    }
    while (rc != DBGKD_WAIT_PACKET);

    *Flags = Reply->u.GetInternalBreakpoint.Flags;
    *Calls = Reply->u.GetInternalBreakpoint.Calls;
    *MaxInstr = Reply->u.GetInternalBreakpoint.MaxInstructions;
    *MinInstr = Reply->u.GetInternalBreakpoint.MinInstructions;
    *TotInstr = Reply->u.GetInternalBreakpoint.TotalInstructions;
    *MaxCps = Reply->u.GetInternalBreakpoint.MaxCallsPerPeriod;

    KdOut("DbgKdGetInternalBp returns 0x00000000\n");
    return S_OK;
}

HRESULT
ExdiLiveKernelTargetInfo::BeginInsertingBreakpoints(void)
{
    if (!m_ExdiDataBreaks)
    {
        return TargetInfo::BeginInsertingBreakpoints();
    }
    else
    {
        // If direct eXDI support for data breakpoints is
        // used this method doesn't need to do anything.
        return S_OK;
    }
}

HRESULT
ExdiLiveKernelTargetInfo::InsertCodeBreakpoint(ProcessInfo* Process,
                                               MachineInfo* Machine,
                                               PADDR Addr,
                                               ULONG InstrFlags,
                                               PUCHAR StorageSpace)
{
    if (InstrFlags != IBI_DEFAULT)
    {
        return E_INVALIDARG;
    }

    IeXdiCodeBreakpoint** BpStorage = (IeXdiCodeBreakpoint**)StorageSpace;
    HRESULT Status = m_Server->
        AddCodeBreakpoint(Flat(*Addr), m_CodeBpType, mtVirtual, 0, 0,
                          BpStorage);
    if (Status == S_OK)
    {
        // Breakpoints are created disabled so enable it.
        Status = (*BpStorage)->SetState(TRUE, TRUE);
        if (Status != S_OK)
        {
            m_Server->DelCodeBreakpoint(*BpStorage);
            RELEASE(*BpStorage);
        }
    }
    return Status;
}

HRESULT
ExdiLiveKernelTargetInfo::InsertDataBreakpoint(ProcessInfo* Process,
                                               ThreadInfo* Thread,
                                               class MachineInfo* Machine,
                                               PADDR Addr,
                                               ULONG Size,
                                               ULONG AccessType,
                                               PUCHAR StorageSpace)
{
    if (!m_ExdiDataBreaks)
    {
        return TargetInfo::InsertDataBreakpoint(Process, Thread, Machine,
                                                Addr, Size, AccessType,
                                                StorageSpace);
    }

    DATA_ACCESS_TYPE ExdiAccess;

    if (AccessType & (DEBUG_BREAK_IO | DEBUG_BREAK_EXECUTE))
    {
        // Not supported.
        return E_NOTIMPL;
    }
    switch(AccessType)
    {
    case 0:
        return E_INVALIDARG;
    case DEBUG_BREAK_READ:
        ExdiAccess = daRead;
        break;
    case DEBUG_BREAK_WRITE:
        ExdiAccess = daWrite;
        break;
    case DEBUG_BREAK_READ | DEBUG_BREAK_WRITE:
        ExdiAccess = daBoth;
        break;
    }

    IeXdiDataBreakpoint** BpStorage = (IeXdiDataBreakpoint**)StorageSpace;
    HRESULT Status = m_Server->
        AddDataBreakpoint(Flat(*Addr), -1, 0, 0, (BYTE)(Size * 8),
                          mtVirtual, 0, ExdiAccess, 0, BpStorage);
    if (Status == S_OK)
    {
        // Breakpoints are created disabled so enable it.
        Status = (*BpStorage)->SetState(TRUE, TRUE);
        if (Status != S_OK)
        {
            m_Server->DelDataBreakpoint(*BpStorage);
            RELEASE(*BpStorage);
        }
    }
    return Status;
}

void
ExdiLiveKernelTargetInfo::EndInsertingBreakpoints(void)
{
    if (!m_ExdiDataBreaks)
    {
        TargetInfo::EndInsertingBreakpoints();
    }
}

void
ExdiLiveKernelTargetInfo::BeginRemovingBreakpoints(void)
{
    if (!m_ExdiDataBreaks)
    {
        TargetInfo::BeginRemovingBreakpoints();
    }
}

HRESULT
ExdiLiveKernelTargetInfo::RemoveCodeBreakpoint(ProcessInfo* Process,
                                               MachineInfo* Machine,
                                               PADDR Addr,
                                               ULONG InstrFlags,
                                               PUCHAR StorageSpace)
{
    IeXdiCodeBreakpoint** BpStorage = (IeXdiCodeBreakpoint**)StorageSpace;
    HRESULT Status = m_Server->
        DelCodeBreakpoint(*BpStorage);
    if (Status == S_OK)
    {
        RELEASE(*BpStorage);
    }
    return Status;
}

HRESULT
ExdiLiveKernelTargetInfo::RemoveDataBreakpoint(ProcessInfo* Process,
                                               ThreadInfo* Thread,
                                               class MachineInfo* Machine,
                                               PADDR Addr,
                                               ULONG Size,
                                               ULONG AccessType,
                                               PUCHAR StorageSpace)
{
    if (!m_ExdiDataBreaks)
    {
        return TargetInfo::RemoveDataBreakpoint(Process, Thread, Machine,
                                                Addr, Size, AccessType,
                                                StorageSpace);
    }

    IeXdiDataBreakpoint** BpStorage = (IeXdiDataBreakpoint**)StorageSpace;
    HRESULT Status = m_Server->
        DelDataBreakpoint(*BpStorage);
    if (Status == S_OK)
    {
        RELEASE(*BpStorage);
    }
    return Status;
}

void
ExdiLiveKernelTargetInfo::EndRemovingBreakpoints(void)
{
    if (!m_ExdiDataBreaks)
    {
        TargetInfo::EndRemovingBreakpoints();
    }
}

HRESULT
ExdiLiveKernelTargetInfo::IsDataBreakpointHit(ThreadInfo* Thread,
                                              PADDR Addr,
                                              ULONG Size,
                                              ULONG AccessType,
                                              PUCHAR StorageSpace)
{
    if (!m_ExdiDataBreaks)
    {
        return S_FALSE;
    }

    if (m_BpHit.Type != DBGENG_EXDI_IOCTL_BREAKPOINT_DATA ||
        m_BpHit.Address != Flat(*Addr) ||
        m_BpHit.AccessWidth != Size)
    {
        return E_NOINTERFACE;
    }

    return S_OK;
}

HRESULT
LiveUserTargetInfo::BeginInsertingBreakpoints(void)
{
    if (m_ServiceFlags & DBGSVC_GENERIC_DATA_BREAKPOINTS)
    {
        return TargetInfo::BeginInsertingBreakpoints();
    }

    // Services handle everything so there's no preparation.
    return S_OK;
}

HRESULT
LiveUserTargetInfo::InsertCodeBreakpoint(ProcessInfo* Process,
                                         MachineInfo* Machine,
                                         PADDR Addr,
                                         ULONG InstrFlags,
                                         PUCHAR StorageSpace)
{
    HRESULT Status;

    if (m_ServiceFlags & DBGSVC_GENERIC_CODE_BREAKPOINTS)
    {
        ULONG64 ChangeStart;
        ULONG ChangeLen;

        Status = Machine->
            InsertBreakpointInstruction(m_Services,
                                        Process->m_SysHandle,
                                        Flat(*Addr), InstrFlags, StorageSpace,
                                        &ChangeStart, &ChangeLen);
        if ((Status == HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY) ||
             Status == HRESULT_FROM_WIN32(ERROR_NOACCESS) ||
             Status == HRESULT_FROM_WIN32(ERROR_WRITE_FAULT)) &&
            (g_EngOptions & DEBUG_ENGOPT_ALLOW_READ_ONLY_BREAKPOINTS))
        {
            HRESULT NewStatus;
            ULONG OldProtect;

            // Change the page protections to read-write and try again.
            NewStatus = m_Services->
                ProtectVirtual(Process->m_SysHandle, ChangeStart, ChangeLen,
                               PAGE_READWRITE, &OldProtect);
            if (NewStatus == S_OK)
            {
                // If the page was already writable there's no point in
                // retrying
                if ((OldProtect & (PAGE_READWRITE |
                                   PAGE_WRITECOPY |
                                   PAGE_EXECUTE_READWRITE |
                                   PAGE_EXECUTE_WRITECOPY)) == 0)
                {
                    NewStatus = Machine->
                        InsertBreakpointInstruction(m_Services,
                                                    Process->m_SysHandle,
                                                    Flat(*Addr), InstrFlags,
                                                    StorageSpace,
                                                    &ChangeStart, &ChangeLen);
                    if (NewStatus == S_OK)
                    {
                        Status = S_OK;
                    }
                }

                NewStatus = m_Services->
                    ProtectVirtual(Process->m_SysHandle,
                                   ChangeStart, ChangeLen,
                                   OldProtect, &OldProtect);
                if (NewStatus != S_OK)
                {
                    // Couldn't restore page permissions so fail.
                    if (Status == S_OK)
                    {
                        Machine->
                            RemoveBreakpointInstruction(m_Services,
                                                        Process->m_SysHandle,
                                                        Flat(*Addr),
                                                        StorageSpace,
                                                        &ChangeStart,
                                                        &ChangeLen);
                    }

                    Status = NewStatus;
                }
            }
        }

        return Status;
    }
    else
    {
        if (InstrFlags != IBI_DEFAULT)
        {
            return E_INVALIDARG;
        }

        return m_Services->
            InsertCodeBreakpoint(Process->m_SysHandle,
                                 Flat(*Addr), Machine->m_ExecTypes[0],
                                 StorageSpace, MAX_BREAKPOINT_LENGTH);
    }
}

HRESULT
LiveUserTargetInfo::InsertDataBreakpoint(ProcessInfo* Process,
                                         ThreadInfo* Thread,
                                         class MachineInfo* Machine,
                                         PADDR Addr,
                                         ULONG Size,
                                         ULONG AccessType,
                                         PUCHAR StorageSpace)
{
    if (m_ServiceFlags & DBGSVC_GENERIC_DATA_BREAKPOINTS)
    {
        return S_FALSE;
    }

    return m_Services->
        InsertDataBreakpoint(Process->m_SysHandle,
                             Thread ? Thread->m_Handle : 0,
                             Flat(*Addr), Size, AccessType,
                             Machine->m_ExecTypes[0]);
}

void
LiveUserTargetInfo::EndInsertingBreakpoints(void)
{
    if (m_ServiceFlags & DBGSVC_GENERIC_DATA_BREAKPOINTS)
    {
        return TargetInfo::EndInsertingBreakpoints();
    }

    // Services handle everything so there's no preparation.
}

HRESULT
LiveUserTargetInfo::RemoveCodeBreakpoint(ProcessInfo* Process,
                                         MachineInfo* Machine,
                                         PADDR Addr,
                                         ULONG InstrFlags,
                                         PUCHAR StorageSpace)
{
    HRESULT Status;

    if (m_ServiceFlags & DBGSVC_GENERIC_CODE_BREAKPOINTS)
    {
        ULONG64 ChangeStart;
        ULONG ChangeLen;

        Status = Machine->
            RemoveBreakpointInstruction(m_Services,
                                        Process->m_SysHandle,
                                        Flat(*Addr), StorageSpace,
                                        &ChangeStart, &ChangeLen);
        if ((Status == HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY) ||
             Status == HRESULT_FROM_WIN32(ERROR_NOACCESS) ||
             Status == HRESULT_FROM_WIN32(ERROR_WRITE_FAULT)) &&
            (g_EngOptions & DEBUG_ENGOPT_ALLOW_READ_ONLY_BREAKPOINTS))
        {
            HRESULT NewStatus;
            ULONG OldProtect;

            // Change the page protections to read-write and try again.
            NewStatus = m_Services->
                ProtectVirtual(Process->m_SysHandle, ChangeStart, ChangeLen,
                               PAGE_READWRITE, &OldProtect);
            if (NewStatus == S_OK)
            {
                // If the page was already writable there's no point in
                // retrying
                if ((OldProtect & (PAGE_READWRITE |
                                   PAGE_WRITECOPY |
                                   PAGE_EXECUTE_READWRITE |
                                   PAGE_EXECUTE_WRITECOPY)) == 0)
                {
                    NewStatus = Machine->
                        RemoveBreakpointInstruction(m_Services,
                                                    Process->m_SysHandle,
                                                    Flat(*Addr), StorageSpace,
                                                    &ChangeStart, &ChangeLen);
                    if (NewStatus == S_OK)
                    {
                        Status = S_OK;
                    }
                }

                NewStatus = m_Services->
                    ProtectVirtual(Process->m_SysHandle, ChangeStart,
                                   ChangeLen, OldProtect, &OldProtect);
                if (NewStatus != S_OK)
                {
                    // Couldn't restore page permissions so fail.
                    if (Status == S_OK)
                    {
                        Machine->
                            InsertBreakpointInstruction(m_Services,
                                                        Process->m_SysHandle,
                                                        Flat(*Addr),
                                                        InstrFlags,
                                                        StorageSpace,
                                                        &ChangeStart,
                                                        &ChangeLen);
                    }

                    Status = NewStatus;
                }
            }
        }

        return Status;
    }
    else
    {
        return m_Services->
            RemoveCodeBreakpoint(Process->m_SysHandle,
                                 Flat(*Addr), Machine->m_ExecTypes[0],
                                 StorageSpace, MAX_BREAKPOINT_LENGTH);
    }
}

HRESULT
LiveUserTargetInfo::RemoveDataBreakpoint(ProcessInfo* Process,
                                         ThreadInfo* Thread,
                                         class MachineInfo* Machine,
                                         PADDR Addr,
                                         ULONG Size,
                                         ULONG AccessType,
                                         PUCHAR StorageSpace)
{
    if (m_ServiceFlags & DBGSVC_GENERIC_DATA_BREAKPOINTS)
    {
        return S_FALSE;
    }

    return m_Services->
        RemoveDataBreakpoint(Process->m_SysHandle,
                             Thread ? Thread->m_Handle : 0,
                             Flat(*Addr), Size, AccessType,
                             Machine->m_ExecTypes[0]);
}

HRESULT
LiveUserTargetInfo::IsDataBreakpointHit(ThreadInfo* Thread,
                                        PADDR Addr,
                                        ULONG Size,
                                        ULONG AccessType,
                                        PUCHAR StorageSpace)
{
    if (m_ServiceFlags & DBGSVC_GENERIC_DATA_BREAKPOINTS)
    {
        // Ask for default processing.
        return S_FALSE;
    }

    if (!m_DataBpAddrValid)
    {
        m_DataBpAddrStatus = m_Services->
             GetLastDataBreakpointHit(Thread->m_Process->m_SysHandle,
                                      Thread->m_Handle,
                                      &m_DataBpAddr, &m_DataBpAccess);
        m_DataBpAddrValid = TRUE;
    }

    if (m_DataBpAddrStatus != S_OK ||
        m_DataBpAddr < Flat(*Addr) ||
        m_DataBpAddr >= Flat(*Addr) + Size ||
        !(m_DataBpAccess & AccessType))
    {
        return E_NOINTERFACE;
    }
    else
    {
        return S_OK;
    }
}
