//===-- BreakpointResolverAddress.cpp ---------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/Breakpoint/BreakpointResolverAddress.h"

// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes

#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

using namespace lldb;
using namespace lldb_private;

//----------------------------------------------------------------------
// BreakpointResolverAddress:
//----------------------------------------------------------------------
BreakpointResolverAddress::BreakpointResolverAddress
(
    Breakpoint *bkpt,
    const Address &addr,
    const FileSpec &module_spec
) :
    BreakpointResolver (bkpt, BreakpointResolver::AddressResolver),
    m_addr (addr),
    m_resolved_addr(LLDB_INVALID_ADDRESS),
    m_module_filespec(module_spec)
{
}

BreakpointResolverAddress::BreakpointResolverAddress
(
    Breakpoint *bkpt,
    const Address &addr
) :
    BreakpointResolver (bkpt, BreakpointResolver::AddressResolver),
    m_addr (addr),
    m_resolved_addr(LLDB_INVALID_ADDRESS),
    m_module_filespec()
{
}

BreakpointResolverAddress::~BreakpointResolverAddress ()
{

}

void
BreakpointResolverAddress::ResolveBreakpoint (SearchFilter &filter)
{
    // If the address is not section relative, then we should not try to re-resolve it, it is just some
    // random address and we wouldn't know what to do on reload.  But if it is section relative, we need to
    // re-resolve it since the section it's in may have shifted on re-run.
    bool re_resolve = false;
    if (m_addr.GetSection() || m_module_filespec)
        re_resolve = true;
    else if (m_breakpoint->GetNumLocations() == 0)
        re_resolve = true;
    
    if (re_resolve)
        BreakpointResolver::ResolveBreakpoint(filter);
}

void
BreakpointResolverAddress::ResolveBreakpointInModules
(
    SearchFilter &filter,
    ModuleList &modules
)
{
    // See comment in ResolveBreakpoint.
    bool re_resolve = false;
    if (m_addr.GetSection())
        re_resolve = true;
    else if (m_breakpoint->GetNumLocations() == 0)
        re_resolve = true;
    
    if (re_resolve)
        BreakpointResolver::ResolveBreakpointInModules (filter, modules);
}

Searcher::CallbackReturn
BreakpointResolverAddress::SearchCallback
(
    SearchFilter &filter,
    SymbolContext &context,
    Address *addr,
    bool containing
)
{
    assert (m_breakpoint != NULL);

    if (filter.AddressPasses (m_addr))
    {
        if (m_breakpoint->GetNumLocations() == 0)
        {
            // If the address is just an offset, and we're given a module, see if we can find the appropriate module
            // loaded in the binary, and fix up m_addr to use that.
            if (!m_addr.IsSectionOffset() && m_module_filespec)
            {
                Target &target = m_breakpoint->GetTarget();
                ModuleSpec module_spec(m_module_filespec);
                ModuleSP module_sp = target.GetImages().FindFirstModule(module_spec);
                if (module_sp)
                {
                    Address tmp_address;
                    if (module_sp->ResolveFileAddress(m_addr.GetOffset(), tmp_address))
                        m_addr = tmp_address;
                }
            }
            
            m_resolved_addr = m_addr.GetLoadAddress(&m_breakpoint->GetTarget());
            BreakpointLocationSP bp_loc_sp(AddLocation(m_addr));
            if (bp_loc_sp && !m_breakpoint->IsInternal())
            {
                    StreamString s;
                    bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
                    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
                    if (log)
                        log->Printf ("Added location: %s\n", s.GetData());
            }
        }
        else
        {
            BreakpointLocationSP loc_sp = m_breakpoint->GetLocationAtIndex(0);
            lldb::addr_t cur_load_location = m_addr.GetLoadAddress(&m_breakpoint->GetTarget());
            if (cur_load_location != m_resolved_addr)
            {
                m_resolved_addr = cur_load_location;
                loc_sp->ClearBreakpointSite();
                loc_sp->ResolveBreakpointSite();
            }
        }
    }
    return Searcher::eCallbackReturnStop;
}

Searcher::Depth
BreakpointResolverAddress::GetDepth()
{
    return Searcher::eDepthTarget;
}

void
BreakpointResolverAddress::GetDescription (Stream *s)
{
    s->PutCString ("address = ");
    m_addr.Dump(s, m_breakpoint->GetTarget().GetProcessSP().get(), Address::DumpStyleModuleWithFileAddress, Address::DumpStyleLoadAddress);
}

void
BreakpointResolverAddress::Dump (Stream *s) const
{

}

lldb::BreakpointResolverSP
BreakpointResolverAddress::CopyForBreakpoint (Breakpoint &breakpoint)
{
    lldb::BreakpointResolverSP ret_sp(new BreakpointResolverAddress(&breakpoint, m_addr));
    return ret_sp;
}

