mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 03:28:20 +02:00
4941 lines
No EOL
137 KiB
C++
4941 lines
No EOL
137 KiB
C++
#include "CeDebugger.h"
|
|
#include "CeMachine.h"
|
|
#include "BfCompiler.h"
|
|
#include "../DebugManager.h"
|
|
#include "BeefySysLib/util/BeefPerf.h"
|
|
#include "BfParser.h"
|
|
#include "BfReducer.h"
|
|
#include "BeefySysLib/util/UTF8.h"
|
|
#include "BfUtil.h"
|
|
#include "BfExprEvaluator.h"
|
|
#include "BeefySysLib/util/BitSet.h"
|
|
#include "../DebugVisualizers.h"
|
|
#include "BfAutoComplete.h"
|
|
#include "BfDemangler.h"
|
|
|
|
USING_NS_BF;
|
|
|
|
static addr_ce DecodeTargetDataPtr(const char*& strRef)
|
|
{
|
|
addr_ce val = (addr_ce)stouln(strRef, sizeof(addr_ce) * 2);
|
|
strRef += sizeof(addr_ce) * 2;
|
|
return val;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
CeBreakpointCondition::~CeBreakpointCondition()
|
|
{
|
|
delete mDbgEvaluationContext;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
CeBreakpoint::~CeBreakpoint()
|
|
{
|
|
delete mCondition;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
CePendingExpr::CePendingExpr()
|
|
{
|
|
mThreadId = -1;
|
|
mCallStackIdx = -1;
|
|
mPassInstance = NULL;
|
|
mParser = NULL;
|
|
mCursorPos = -1;
|
|
mExprNode = NULL;
|
|
mIdleTicks = 0;
|
|
mExplitType = NULL;
|
|
mExpressionFlags = DwEvalExpressionFlag_None;
|
|
mDone = false;
|
|
}
|
|
|
|
CePendingExpr::~CePendingExpr()
|
|
{
|
|
delete mParser;
|
|
delete mPassInstance;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
CeEvaluationContext::CeEvaluationContext(CeDebugger* winDebugger, const StringImpl& expr, CeFormatInfo* formatInfo, BfTypedValue contextValue)
|
|
{
|
|
Init(winDebugger, expr, formatInfo, contextValue);
|
|
}
|
|
|
|
void CeEvaluationContext::Init(CeDebugger* ceDebugger, const StringImpl& expr, CeFormatInfo* formatInfo, BfTypedValue contextValue)
|
|
{
|
|
mDebugger = ceDebugger;
|
|
|
|
mCallStackIdx = 0;
|
|
mParser = NULL;
|
|
mReducer = NULL;
|
|
mPassInstance = NULL;
|
|
mExprEvaluator = NULL;
|
|
mExprNode = NULL;
|
|
|
|
if (expr.empty())
|
|
return;
|
|
|
|
int atPos = (int)expr.IndexOf('@');
|
|
if ((atPos != -1) && (atPos < expr.mLength - 2) && (expr[atPos + 1] == '0') && (expr[atPos + 2] == 'x'))
|
|
{
|
|
bool isValid = true;
|
|
for (int i = 0; i < atPos; i++)
|
|
{
|
|
char c = expr[i];
|
|
if ((c < '0') || (c > '9'))
|
|
{
|
|
isValid = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (isValid)
|
|
{
|
|
int parseLength = expr.mLength;
|
|
for (int i = 0; i < expr.mLength; i++)
|
|
{
|
|
if ((expr[i] == ',') || (::isspace((uint8)expr[i])))
|
|
{
|
|
parseLength = i;
|
|
break;
|
|
}
|
|
}
|
|
mExprString = expr.Substring(0, parseLength);
|
|
|
|
String typeIdStr = expr.Substring(0, atPos);
|
|
String addrStr = expr.Substring(atPos + 3, parseLength - atPos - 3);
|
|
|
|
int typeId = strtol(typeIdStr.c_str(), NULL, 10);
|
|
int64 addrVal = strtoll(addrStr.c_str(), NULL, 16);
|
|
|
|
if ((typeId != 0) && (addrVal != 0))
|
|
{
|
|
auto type = ceDebugger->mCompiler->mContext->FindTypeById(typeId);
|
|
if (type != NULL)
|
|
{
|
|
auto module = ceDebugger->mCeMachine->mCeModule;
|
|
|
|
if (type->IsObjectOrInterface())
|
|
{
|
|
mResultOverride = BfTypedValue(module->mBfIRBuilder->CreateIntToPtr(
|
|
module->mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, (uint64)addrVal), module->mBfIRBuilder->MapType(type)), type);
|
|
}
|
|
else
|
|
{
|
|
mResultOverride = BfTypedValue(module->mBfIRBuilder->CreateConstAggCE(module->mBfIRBuilder->MapType(type), (addr_ce)addrVal), type, true);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
mParser = new BfParser(ceDebugger->mCompiler->mSystem);
|
|
mPassInstance = new BfPassInstance(ceDebugger->mCompiler->mSystem);
|
|
auto terminatedExpr = expr + ";";
|
|
mParser->SetSource(terminatedExpr.c_str(), (int)terminatedExpr.length());
|
|
mParser->Parse(mPassInstance);
|
|
|
|
mReducer = new BfReducer();
|
|
mReducer->mAlloc = mParser->mAlloc;
|
|
mReducer->mSystem = ceDebugger->mCompiler->mSystem;
|
|
mReducer->mPassInstance = mPassInstance;
|
|
mReducer->mVisitorPos = BfReducer::BfVisitorPos(mParser->mRootNode);
|
|
mReducer->mVisitorPos.MoveNext();
|
|
mReducer->mSource = mParser;
|
|
mExprNode = mReducer->CreateExpression(mParser->mRootNode->GetFirst());
|
|
mParser->Close();
|
|
mExprEvaluator = new BfExprEvaluator(ceDebugger->mCeMachine->mCeModule);
|
|
|
|
if ((formatInfo != NULL) && (mExprNode != NULL) && (mExprNode->GetSrcEnd() < (int)expr.length()))
|
|
{
|
|
String formatFlags = expr.Substring(mExprNode->GetSrcEnd());
|
|
String errorString = "Invalid expression";
|
|
if (!ceDebugger->ParseFormatInfo(formatFlags, formatInfo, mPassInstance, NULL, NULL, &errorString, contextValue))
|
|
{
|
|
mPassInstance->FailAt(errorString, mParser->mSourceData, mExprNode->GetSrcEnd(), (int)expr.length() - mExprNode->GetSrcEnd());
|
|
formatFlags = "";
|
|
}
|
|
}
|
|
|
|
if (formatInfo != NULL)
|
|
{
|
|
mExplicitThis = formatInfo->mExplicitThis;
|
|
mCallStackIdx = formatInfo->mCallStackIdx;
|
|
}
|
|
|
|
mExprNode->ToString(mExprString);
|
|
}
|
|
|
|
bool CeEvaluationContext::HasExpression()
|
|
{
|
|
return !mExprString.IsEmpty();
|
|
}
|
|
|
|
CeEvaluationContext::~CeEvaluationContext()
|
|
{
|
|
delete mParser;
|
|
delete mReducer;
|
|
delete mExprEvaluator;
|
|
delete mPassInstance;
|
|
}
|
|
|
|
BfTypedValue CeEvaluationContext::EvaluateInContext(BfTypedValue contextTypedValue, CeDbgState* dbgState)
|
|
{
|
|
if (mResultOverride)
|
|
return mResultOverride;
|
|
|
|
if (mExprNode == NULL)
|
|
return BfTypedValue();
|
|
mPassInstance->ClearErrors();
|
|
|
|
auto ceFrame = mDebugger->GetFrame(mCallStackIdx);
|
|
|
|
auto module = mDebugger->mCeMachine->mCeModule;
|
|
|
|
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(module->mCurTypeInstance, ceFrame->mFunction->mMethodInstance->GetOwner());
|
|
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(module->mCurMethodInstance, ceFrame->mFunction->mMethodInstance);
|
|
SetAndRestoreValue<BfPassInstance*> prevPassInstance(mDebugger->mCompiler->mPassInstance, mPassInstance);
|
|
SetAndRestoreValue<bool> prevIgnoreWrites(module->mBfIRBuilder->mIgnoreWrites, true);
|
|
|
|
BfMethodState methodState;
|
|
SetAndRestoreValue<BfMethodState*> prevMethodState(module->mCurMethodState, &methodState);
|
|
methodState.mTempKind = module->mCurMethodInstance->mMethodDef->mIsStatic ? BfMethodState::TempKind_Static : BfMethodState::TempKind_NonStatic;
|
|
|
|
CeDbgState localDbgState;
|
|
if (dbgState == NULL)
|
|
dbgState = &localDbgState;
|
|
|
|
dbgState->mActiveFrame = ceFrame;
|
|
dbgState->mCeContext = mDebugger->mCeMachine->mCurContext;
|
|
if (contextTypedValue)
|
|
dbgState->mExplicitThis = contextTypedValue;
|
|
else
|
|
dbgState->mExplicitThis = mExplicitThis;
|
|
SetAndRestoreValue<CeDbgState*> prevDbgState(mDebugger->mCurDbgState, dbgState);
|
|
|
|
BfTypedValue exprResult;
|
|
mExprEvaluator->VisitChildNoRef(mExprNode);
|
|
|
|
auto result = mExprEvaluator->mResult;
|
|
if ((result) && (!result.mType->IsComposite()))
|
|
result = module->LoadValue(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool CeEvaluationContext::HadError()
|
|
{
|
|
return (mPassInstance != NULL) && (mPassInstance->mFailedIdx != 0);
|
|
}
|
|
|
|
String CeEvaluationContext::GetErrorStr()
|
|
{
|
|
if (mPassInstance == NULL)
|
|
return "";
|
|
String errorStr = mPassInstance->mErrors[0]->mError;
|
|
if (mExprNode != NULL)
|
|
{
|
|
errorStr += ": ";
|
|
errorStr += mExprNode->ToString();
|
|
}
|
|
return errorStr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
CeDebugger::CeDebugger(DebugManager* debugManager, BfCompiler* bfCompiler)
|
|
{
|
|
mDebugManager = debugManager;
|
|
mCompiler = bfCompiler;
|
|
mCeMachine = bfCompiler->mCeMachine;
|
|
mRunState = RunState_Running;
|
|
mCeMachine->mDebugger = this;
|
|
mCeMachine->mDebugEvent.Reset();
|
|
mDebugPendingExpr = NULL;
|
|
mCurDbgState = NULL;
|
|
mBreakpointVersion = 0;
|
|
mBreakpointCacheDirty = false;
|
|
mBreakpointFramesDirty = false;
|
|
mCurDisasmFuncId = 0;
|
|
mActiveBreakpoint = NULL;
|
|
mCurEvaluationContext = NULL;
|
|
mPendingActiveFrameOffset = 0;
|
|
}
|
|
|
|
String CeDebugger::TypeToString(const BfTypedValue& typedValue)
|
|
{
|
|
if (typedValue.mType == NULL)
|
|
return "null";
|
|
if (typedValue.IsReadOnly())
|
|
return String("readonly ") + mCeMachine->mCeModule->TypeToString(typedValue.mType);
|
|
return mCeMachine->mCeModule->TypeToString(typedValue.mType);
|
|
}
|
|
|
|
String CeDebugger::TypeToString(BfType* type, CeTypeModKind typeModKind)
|
|
{
|
|
if (type == NULL)
|
|
return "null";
|
|
String str;
|
|
if (typeModKind == CeTypeModKind_ReadOnly)
|
|
str += "readonly ";
|
|
else if (typeModKind == CeTypeModKind_Const)
|
|
str += "const ";
|
|
str += mCeMachine->mCeModule->TypeToString(type);
|
|
return str;
|
|
}
|
|
|
|
CeDebugger::~CeDebugger()
|
|
{
|
|
mCeMachine->mDebugEvent.Set(true);
|
|
mCeMachine->mDebugger = NULL;
|
|
delete mDebugPendingExpr;
|
|
|
|
for (auto breakpoint : mBreakpoints)
|
|
delete breakpoint;
|
|
|
|
for (auto kv : mFileInfo)
|
|
delete kv.mValue;
|
|
}
|
|
|
|
void CeDebugger::OutputMessage(const StringImpl& msg)
|
|
{
|
|
if (this == NULL)
|
|
return;
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
mDebugManager->mOutMessages.push_back("msg " + msg);
|
|
}
|
|
|
|
void CeDebugger::OutputRawMessage(const StringImpl& msg)
|
|
{
|
|
if (this == NULL)
|
|
return;
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
mDebugManager->mOutMessages.push_back(msg);
|
|
}
|
|
|
|
int CeDebugger::GetAddrSize()
|
|
{
|
|
return sizeof(addr_ce);
|
|
}
|
|
|
|
bool CeDebugger::CanOpen(const StringImpl& fileName, DebuggerResult* outResult)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void CeDebugger::OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled)
|
|
{
|
|
}
|
|
|
|
bool CeDebugger::Attach(int processId, BfDbgAttachFlags attachFlags)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void CeDebugger::Run()
|
|
{
|
|
}
|
|
|
|
void CeDebugger::HotLoad(const Array<String>& objectFiles, int hotIdx)
|
|
{
|
|
}
|
|
|
|
void CeDebugger::InitiateHotResolve(DbgHotResolveFlags flags)
|
|
{
|
|
}
|
|
|
|
intptr CeDebugger::GetDbgAllocHeapSize()
|
|
{
|
|
return intptr();
|
|
}
|
|
|
|
String CeDebugger::GetDbgAllocInfo()
|
|
{
|
|
return String();
|
|
}
|
|
|
|
void CeDebugger::Update()
|
|
{
|
|
AutoCrit autoCrit(mCeMachine->mCritSect);
|
|
|
|
if ((mRunState == RunState_Terminated) || (mRunState == RunState_Terminating))
|
|
return;
|
|
|
|
if (mDebugPendingExpr != NULL)
|
|
{
|
|
if (mDebugPendingExpr->mDone)
|
|
mRunState = RunState_DebugEval_Done;
|
|
else
|
|
mRunState = RunState_DebugEval;
|
|
}
|
|
else if (mCeMachine->mDbgPaused)
|
|
mRunState = RunState_Paused;
|
|
else
|
|
mRunState = RunState_Running;
|
|
}
|
|
|
|
void CeDebugger::UpdateBreakpointFrames()
|
|
{
|
|
if (mBreakpointCacheDirty)
|
|
UpdateBreakpointCache();
|
|
for (auto& callStack : mCeMachine->mCurContext->mCallStack)
|
|
{
|
|
if (callStack.mFunction->mBreakpointVersion != mBreakpointVersion)
|
|
UpdateBreakpoints(callStack.mFunction);
|
|
}
|
|
}
|
|
|
|
void CeDebugger::ContinueDebugEvent()
|
|
{
|
|
AutoCrit autoCrit(mCeMachine->mCritSect);
|
|
|
|
mRunState = RunState_Running;
|
|
mActiveBreakpoint = NULL;
|
|
mPendingActiveFrameOffset = 0;
|
|
mDbgCallStack.Clear();
|
|
|
|
if (mBreakpointFramesDirty)
|
|
UpdateBreakpointFrames();
|
|
|
|
mCeMachine->mDebugEvent.Set();
|
|
}
|
|
|
|
void CeDebugger::ForegroundTarget()
|
|
{
|
|
}
|
|
|
|
bool CeDebugger::CheckConditionalBreakpoint(CeBreakpoint* breakpoint)
|
|
{
|
|
auto _SplitExpr = [&](const StringImpl& expr, StringImpl& outExpr, StringImpl& outSubject)
|
|
{
|
|
int crPos = (int)expr.IndexOf('\n');
|
|
if (crPos != -1)
|
|
{
|
|
outExpr += expr.Substring(0, crPos);
|
|
outSubject += expr.Substring(crPos + 1);
|
|
}
|
|
else
|
|
{
|
|
outExpr += expr;
|
|
}
|
|
};
|
|
|
|
if (breakpoint->mCondition != NULL)
|
|
{
|
|
ClearCallStack();
|
|
|
|
auto conditional = breakpoint->mCondition;
|
|
if (conditional->mDbgEvaluationContext == NULL)
|
|
{
|
|
StringT<256> expr;
|
|
StringT<256> subjectExpr;
|
|
_SplitExpr(conditional->mExpr, expr, subjectExpr);
|
|
|
|
conditional->mDbgEvaluationContext = new CeEvaluationContext(this, expr);
|
|
conditional->mDbgEvaluationContext->mCallStackIdx = -1;
|
|
}
|
|
|
|
CeDbgState dbgState;
|
|
BfTypedValue result = conditional->mDbgEvaluationContext->EvaluateInContext(BfTypedValue(), &dbgState);
|
|
|
|
if (conditional->mDbgEvaluationContext->mPassInstance->HasFailed())
|
|
{
|
|
String errorStr = "FAILED";
|
|
for (auto error : conditional->mDbgEvaluationContext->mPassInstance->mErrors)
|
|
{
|
|
if (!error->mIsWarning)
|
|
errorStr = error->mError;
|
|
}
|
|
String condError = StrFormat("error Conditional breakpoint expression '%s' failed: %s", conditional->mExpr.c_str(), errorStr.c_str());
|
|
mDebugManager->mOutMessages.push_back(condError);
|
|
return true;
|
|
}
|
|
else if (dbgState.mBlockedSideEffects)
|
|
{
|
|
mDebugManager->mOutMessages.push_back(StrFormat("error Conditional breakpoint expression '%s' contained function calls, which is not allowed", conditional->mExpr.c_str()));
|
|
return true;
|
|
}
|
|
else if ((!result) || (!result.mType->IsBoolean()))
|
|
{
|
|
mDebugManager->mOutMessages.push_back(StrFormat("error Conditional breakpoint expression '%s' must result in a boolean value", conditional->mExpr.c_str()));
|
|
return true;
|
|
}
|
|
else if (ValueToInt(result) <= 0)
|
|
return false;
|
|
}
|
|
|
|
breakpoint->mHitCount++;
|
|
switch (breakpoint->mHitCountBreakKind)
|
|
{
|
|
case DbgHitCountBreakKind_Equals:
|
|
if (breakpoint->mHitCount != breakpoint->mTargetHitCount)
|
|
return false;
|
|
break;
|
|
case DbgHitCountBreakKind_GreaterEquals:
|
|
if (breakpoint->mHitCount < breakpoint->mTargetHitCount)
|
|
return false;
|
|
break;
|
|
case DbgHitCountBreakKind_Multiple:
|
|
if ((breakpoint->mHitCount % breakpoint->mTargetHitCount) != 0)
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
if (!breakpoint->mLogging.IsEmpty())
|
|
{
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
|
|
CeDbgState dbgState;
|
|
dbgState.mActiveFrame = &ceContext->mCallStack[0];
|
|
dbgState.mCeContext = mCeMachine->mCurContext;
|
|
SetAndRestoreValue<CeDbgState*> prevDbgState(mCurDbgState, &dbgState);
|
|
|
|
ClearCallStack();
|
|
|
|
CeFormatInfo formatInfo;
|
|
formatInfo.mCallStackIdx = -1;
|
|
|
|
auto prevRunState = mRunState;
|
|
mRunState = RunState_Paused; // We need to be paused to avoid certain errors in the eval
|
|
String displayString;
|
|
|
|
String expr;
|
|
_SplitExpr(breakpoint->mLogging, expr, formatInfo.mSubjectExpr);
|
|
|
|
ProcessEvalString(BfTypedValue(), expr, displayString, formatInfo, NULL, false);
|
|
mRunState = prevRunState;
|
|
|
|
displayString.Insert(0, "log ");
|
|
displayString.Append("\n");
|
|
|
|
mDebugManager->mOutMessages.push_back(displayString);
|
|
|
|
if (!breakpoint->mBreakAfterLogging)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Breakpoint* CeDebugger::CreateBreakpoint(const StringImpl& fileName, int lineNum, int wantColumn, int instrOffset)
|
|
{
|
|
ClearBreakpointCache();
|
|
|
|
auto breakpoint = new CeBreakpoint();
|
|
breakpoint->mFilePath = fileName;
|
|
breakpoint->mRequestedLineNum = lineNum;
|
|
breakpoint->mLineNum = lineNum;
|
|
breakpoint->mColumn = wantColumn;
|
|
breakpoint->mInstrOffset = instrOffset;
|
|
mBreakpoints.Add(breakpoint);
|
|
|
|
mBreakpointVersion++;
|
|
|
|
return breakpoint;
|
|
}
|
|
|
|
Breakpoint* CeDebugger::CreateMemoryBreakpoint(intptr addr, int byteCount)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
Breakpoint* CeDebugger::CreateSymbolBreakpoint(const StringImpl& symbolName)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
Breakpoint* CeDebugger::CreateAddressBreakpoint(intptr address)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
uintptr CeDebugger::GetBreakpointAddr(Breakpoint* breakpoint)
|
|
{
|
|
if (mBreakpointCacheDirty)
|
|
{
|
|
UpdateBreakpointCache();
|
|
UpdateBreakpointAddrs();
|
|
}
|
|
return breakpoint->GetAddr();
|
|
}
|
|
|
|
void CeDebugger::CheckBreakpoint(Breakpoint* breakpoint)
|
|
{
|
|
}
|
|
|
|
void CeDebugger::HotBindBreakpoint(Breakpoint* breakpoint, int lineNum, int hotIdx)
|
|
{
|
|
}
|
|
|
|
int64 CeDebugger::ValueToInt(addr_ce addr, BfType* type)
|
|
{
|
|
if ((!type->IsInteger()) && (!type->IsBoolean()))
|
|
return 0;
|
|
|
|
auto primType = (BfPrimitiveType*)type;
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
|
|
int64 val = 0;
|
|
memcpy(&val, ceContext->mMemory.mVals + addr, type->mSize);
|
|
|
|
switch (primType->mTypeDef->mTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
val = *(int8*)&val;
|
|
break;
|
|
case BfTypeCode_Int16:
|
|
val = *(int16*)&val;
|
|
break;
|
|
case BfTypeCode_Int32:
|
|
val = *(int32*)&val;
|
|
break;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
int64 CeDebugger::ValueToInt(const BfTypedValue& typedVal)
|
|
{
|
|
auto ceModule = mCeMachine->mCeModule;
|
|
|
|
auto constant = ceModule->mBfIRBuilder->GetConstant(typedVal.mValue);
|
|
if (constant == NULL)
|
|
return 0;
|
|
|
|
if (typedVal.IsAddr())
|
|
{
|
|
BfType* type = typedVal.mType;
|
|
if (type->IsTypedPrimitive())
|
|
type = type->GetUnderlyingType();
|
|
|
|
if ((type->IsInteger()) || (type->IsBoolean()))
|
|
{
|
|
auto ceTypedVal = GetAddr(constant);
|
|
if (ceTypedVal)
|
|
return ValueToInt((addr_ce)ceTypedVal.mAddr, type);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (constant->mConstType == BfConstType_ExtractValue)
|
|
{
|
|
auto fromConstGEP = (BfConstantExtractValue*)constant;
|
|
auto fromTarget = ceModule->mBfIRBuilder->GetConstantById(fromConstGEP->mTarget);
|
|
if (fromTarget->mConstType == BfConstType_AggCE)
|
|
{
|
|
auto aggCE = (BfConstantAggCE*)fromTarget;
|
|
|
|
auto dbgTypeInfo = GetDbgTypeInfo(aggCE->mType);
|
|
if (dbgTypeInfo == NULL)
|
|
return 0;
|
|
|
|
auto typeInst = dbgTypeInfo->mType->ToTypeInstance();
|
|
if (typeInst == NULL)
|
|
return 0;
|
|
|
|
auto fieldInfo = &dbgTypeInfo->mFieldOffsets[fromConstGEP->mIdx0];
|
|
return ValueToInt(aggCE->mCEAddr + fieldInfo->mDataOffset, fieldInfo->mType);
|
|
}
|
|
}
|
|
|
|
if ((BfIRConstHolder::IsInt(constant->mTypeCode)) || (constant->mTypeCode == BfTypeCode_Boolean))
|
|
return constant->mInt64;
|
|
return 0;
|
|
}
|
|
|
|
void CeDebugger::DeleteBreakpoint(Breakpoint* breakpoint)
|
|
{
|
|
if (mActiveBreakpoint == breakpoint)
|
|
mActiveBreakpoint = NULL;
|
|
mBreakpoints.Remove((CeBreakpoint*)breakpoint);
|
|
delete breakpoint;
|
|
ClearBreakpointCache();
|
|
mBreakpointVersion++;
|
|
}
|
|
|
|
void CeDebugger::DetachBreakpoint(Breakpoint* breakpoint)
|
|
{
|
|
}
|
|
|
|
void CeDebugger::MoveBreakpoint(Breakpoint* breakpoint, int lineNum, int wantColumn, bool rebindNow)
|
|
{
|
|
breakpoint->mLineNum = lineNum;
|
|
breakpoint->mColumn = wantColumn;
|
|
ClearBreakpointCache();
|
|
mBreakpointVersion++;
|
|
}
|
|
|
|
void CeDebugger::MoveMemoryBreakpoint(Breakpoint* breakpoint, intptr addr, int byteCount)
|
|
{
|
|
}
|
|
|
|
void CeDebugger::DisableBreakpoint(Breakpoint* breakpoint)
|
|
{
|
|
}
|
|
|
|
void CeDebugger::SetBreakpointCondition(Breakpoint* breakpoint, const StringImpl& conditionExpr)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
CeBreakpoint* wdBreakpoint = (CeBreakpoint*)breakpoint;
|
|
BF_ASSERT(!wdBreakpoint->mIsLinkedSibling);
|
|
|
|
if (conditionExpr.empty())
|
|
{
|
|
delete wdBreakpoint->mCondition;
|
|
CeBreakpoint* curBreakpoint = wdBreakpoint;
|
|
wdBreakpoint->mCondition = NULL;
|
|
}
|
|
else
|
|
{
|
|
delete wdBreakpoint->mCondition;
|
|
auto condition = new CeBreakpointCondition();
|
|
condition->mExpr = conditionExpr;
|
|
wdBreakpoint->mCondition = condition;
|
|
}
|
|
}
|
|
|
|
void CeDebugger::SetBreakpointLogging(Breakpoint* breakpoint, const StringImpl& logging, bool breakAfterLogging)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
CeBreakpoint* wdBreakpoint = (CeBreakpoint*)breakpoint;
|
|
BF_ASSERT(!wdBreakpoint->mIsLinkedSibling);
|
|
wdBreakpoint->mLogging = logging;
|
|
wdBreakpoint->mBreakAfterLogging = breakAfterLogging;
|
|
}
|
|
|
|
Breakpoint* CeDebugger::FindBreakpointAt(intptr address)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
Breakpoint* CeDebugger::GetActiveBreakpoint()
|
|
{
|
|
return mActiveBreakpoint;
|
|
}
|
|
|
|
void CeDebugger::BreakAll()
|
|
{
|
|
mCeMachine->mSpecialCheck = true;
|
|
mCeMachine->mDbgWantBreak = true;
|
|
}
|
|
|
|
bool CeDebugger::TryRunContinue()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool CeDebugger::SetupStep(int frameIdx)
|
|
{
|
|
auto ceFrame = GetFrame(frameIdx);
|
|
if (ceFrame == NULL)
|
|
{
|
|
ContinueDebugEvent();
|
|
return false;
|
|
}
|
|
|
|
int entryIdx = 0;
|
|
auto curEntry = ceFrame->mFunction->FindEmitEntry(ceFrame->GetInstIdx(), &entryIdx);
|
|
if (curEntry == NULL)
|
|
{
|
|
ContinueDebugEvent();
|
|
return false;
|
|
}
|
|
|
|
auto ceMachine = mCeMachine;
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
|
|
if (entryIdx < ceFrame->mFunction->mEmitTable.mSize - 1)
|
|
{
|
|
int checkIdx = entryIdx + 1;
|
|
while (checkIdx < ceFrame->mFunction->mEmitTable.mSize)
|
|
{
|
|
auto checkEntry = &ceFrame->mFunction->mEmitTable[checkIdx];
|
|
ceMachine->mStepState.mNextInstIdx = checkEntry->mCodePos;
|
|
if ((checkEntry->mScope != curEntry->mScope) || (checkEntry->mLine != curEntry->mLine))
|
|
break;
|
|
++checkIdx;
|
|
}
|
|
}
|
|
else
|
|
ceMachine->mStepState.mNextInstIdx = ceFrame->mFunction->mCode.mSize;
|
|
ceMachine->mStepState.mStartDepth = ceContext->mCallStack.mSize - frameIdx;
|
|
ceMachine->mSpecialCheck = true;
|
|
|
|
ContinueDebugEvent();
|
|
return true;
|
|
}
|
|
|
|
void CeDebugger::StepInto(bool inAssembly)
|
|
{
|
|
if (!SetupStep())
|
|
return;
|
|
mCeMachine->mStepState.mKind = inAssembly ? CeStepState::Kind_StepInfo_Asm : CeStepState::Kind_StepInfo;
|
|
}
|
|
|
|
void CeDebugger::StepIntoSpecific(intptr addr)
|
|
{
|
|
}
|
|
|
|
void CeDebugger::StepOver(bool inAssembly)
|
|
{
|
|
if (!SetupStep())
|
|
return;
|
|
mCeMachine->mStepState.mKind = inAssembly ? CeStepState::Kind_StepOver_Asm : CeStepState::Kind_StepOver;
|
|
}
|
|
|
|
void CeDebugger::StepOut(bool inAssembly)
|
|
{
|
|
if (!SetupStep(1))
|
|
return;
|
|
mCeMachine->mStepState.mKind = inAssembly ? CeStepState::Kind_StepOut_Asm : CeStepState::Kind_StepOut;
|
|
}
|
|
|
|
void CeDebugger::SetNextStatement(bool inAssembly, const StringImpl& fileName, int64 lineNumOrAsmAddr, int wantColumn)
|
|
{
|
|
auto ceFrame = GetFrame(0);
|
|
if (ceFrame == NULL)
|
|
return;
|
|
|
|
if (inAssembly)
|
|
{
|
|
int32 instIdx = (int32)lineNumOrAsmAddr;
|
|
if (instIdx < ceFrame->mFunction->mCode.mSize)
|
|
{
|
|
mCeMachine->mSpecialCheck = true;
|
|
mCeMachine->mStepState.mKind = CeStepState::Kind_Jmp;
|
|
mCeMachine->mStepState.mNextInstIdx = instIdx;
|
|
ContinueDebugEvent();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (auto& emitEntry : ceFrame->mFunction->mEmitTable)
|
|
{
|
|
if (emitEntry.mScope == -1)
|
|
continue;
|
|
auto& scope = ceFrame->mFunction->mDbgScopes[emitEntry.mScope];
|
|
if ((FileNameEquals(fileName, scope.mFilePath) && (emitEntry.mLine == lineNumOrAsmAddr)))
|
|
{
|
|
mCeMachine->mSpecialCheck = true;
|
|
mCeMachine->mStepState.mKind = CeStepState::Kind_Jmp;
|
|
mCeMachine->mStepState.mNextInstIdx = emitEntry.mCodePos;
|
|
ContinueDebugEvent();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CeFrame* CeDebugger::GetFrame(int callStackIdx)
|
|
{
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
if (ceContext == NULL)
|
|
return NULL;
|
|
|
|
if ((callStackIdx == -1) && (!ceContext->mCallStack.IsEmpty()))
|
|
return &ceContext->mCallStack.back();
|
|
|
|
if (callStackIdx < mDbgCallStack.mSize)
|
|
{
|
|
int frameIdx = mDbgCallStack[callStackIdx].mFrameIdx;
|
|
if (frameIdx < 0)
|
|
return NULL;
|
|
auto ceFrame = &ceContext->mCallStack[mDbgCallStack[callStackIdx].mFrameIdx];
|
|
return ceFrame;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
String CeDebugger::GetAutocompleteOutput(BfAutoComplete& autoComplete)
|
|
{
|
|
String val = "\n:autocomplete\n";
|
|
|
|
if (autoComplete.mInsertStartIdx != -1)
|
|
{
|
|
val += StrFormat("insertRange\t%d %d\n", autoComplete.mInsertStartIdx, autoComplete.mInsertEndIdx);
|
|
}
|
|
|
|
Array<AutoCompleteEntry*> entries;
|
|
|
|
for (auto& entry : autoComplete.mEntriesSet)
|
|
{
|
|
entries.Add(&entry);
|
|
}
|
|
std::sort(entries.begin(), entries.end(), [](AutoCompleteEntry* lhs, AutoCompleteEntry* rhs)
|
|
{
|
|
return stricmp(lhs->mDisplay, rhs->mDisplay) < 0;
|
|
});
|
|
|
|
for (auto entry : entries)
|
|
{
|
|
val += String(entry->mEntryType);
|
|
val += "\t";
|
|
val += String(entry->mDisplay);
|
|
val += "\n";
|
|
}
|
|
|
|
/*if (autoComplete.mEntries.size() != 0)
|
|
{
|
|
for (auto& entry : autoComplete.mEntries)
|
|
{
|
|
val += String(entry.mEntryType) + "\t" + String(entry.mDisplay) + "\n";
|
|
}
|
|
}*/
|
|
return val;
|
|
}
|
|
|
|
String CeDebugger::DoEvaluate(CePendingExpr* pendingExpr, bool inCompilerThread)
|
|
{
|
|
auto ceFrame = GetFrame(pendingExpr->mCallStackIdx);
|
|
if (ceFrame == NULL)
|
|
{
|
|
return "!No comptime stack frame selected";
|
|
}
|
|
|
|
if (pendingExpr->mExprNode == NULL)
|
|
{
|
|
return "!No expression";
|
|
}
|
|
|
|
auto module = mCeMachine->mCeModule;
|
|
|
|
if (!mCeMachine->mDbgPaused)
|
|
pendingExpr->mPassInstance->ClearErrors();
|
|
|
|
SetAndRestoreValue<BfTypeState*> prevTypeState(module->mContext->mCurTypeState, NULL);
|
|
SetAndRestoreValue<BfConstraintState*> prevConstraintState(module->mContext->mCurConstraintState, NULL);
|
|
SetAndRestoreValue<BfTypeInstance*> prevTypeInstance(module->mCurTypeInstance, ceFrame->mFunction->mMethodInstance->GetOwner());
|
|
SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(module->mCurMethodInstance, ceFrame->mFunction->mMethodInstance);
|
|
SetAndRestoreValue<BfPassInstance*> prevPassInstance(mCompiler->mPassInstance, pendingExpr->mPassInstance);
|
|
SetAndRestoreValue<bool> prevIgnoreWrites(module->mBfIRBuilder->mIgnoreWrites, true);
|
|
|
|
BfMethodState methodState;
|
|
SetAndRestoreValue<BfMethodState*> prevMethodState(module->mCurMethodState, &methodState);
|
|
methodState.mTempKind = module->mCurMethodInstance->mMethodDef->mIsStatic ? BfMethodState::TempKind_Static : BfMethodState::TempKind_NonStatic;
|
|
|
|
CeDbgState dbgState;
|
|
dbgState.mActiveFrame = ceFrame;
|
|
dbgState.mCeContext = mCeMachine->mCurContext;
|
|
dbgState.mExplicitThis = pendingExpr->mFormatInfo.mExplicitThis;
|
|
dbgState.mDbgExpressionFlags = pendingExpr->mExpressionFlags;
|
|
if (!inCompilerThread)
|
|
dbgState.mDbgExpressionFlags = (DwEvalExpressionFlags)(dbgState.mDbgExpressionFlags & ~(DwEvalExpressionFlag_AllowCalls | DwEvalExpressionFlag_AllowPropertyEval));
|
|
dbgState.mFormatInfo = &pendingExpr->mFormatInfo;
|
|
SetAndRestoreValue<CeDbgState*> prevDbgState(mCurDbgState, &dbgState);
|
|
|
|
BfAutoComplete autoComplete;
|
|
autoComplete.mModule = module;
|
|
autoComplete.mCompiler = module->mCompiler;
|
|
autoComplete.mSystem = module->mSystem;
|
|
|
|
BfResolvePassData resolvePass;
|
|
resolvePass.mParsers.Add(pendingExpr->mParser);
|
|
resolvePass.mAutoComplete = &autoComplete;
|
|
|
|
SetAndRestoreValue<BfResolvePassData*> prevResolvePass;
|
|
if (pendingExpr->mCursorPos != -1)
|
|
{
|
|
pendingExpr->mParser->mParserFlags = (BfParserFlag)(pendingExpr->mParser->mParserFlags | ParserFlag_Autocomplete);
|
|
pendingExpr->mParser->mCursorIdx = pendingExpr->mCursorPos + 1;
|
|
pendingExpr->mParser->mCursorCheckIdx = pendingExpr->mCursorPos + 1;
|
|
prevResolvePass.Init(module->mCompiler->mResolvePassData, &resolvePass);
|
|
}
|
|
|
|
BfTypedValue exprResult;
|
|
BfTypedValue origExprResult;
|
|
|
|
if (auto typeRef = BfNodeDynCast<BfTypeReference>(pendingExpr->mExprNode))
|
|
{
|
|
auto resultType = mCeMachine->mCeModule->ResolveTypeRef(typeRef);
|
|
if (resultType != NULL)
|
|
exprResult = BfTypedValue(resultType);
|
|
}
|
|
else
|
|
{
|
|
BfExprEvaluator exprEvaluator(mCeMachine->mCeModule);
|
|
exprEvaluator.mBfEvalExprFlags = (BfEvalExprFlags)(exprEvaluator.mBfEvalExprFlags | BfEvalExprFlags_Comptime);
|
|
exprEvaluator.VisitChildNoRef(pendingExpr->mExprNode);
|
|
exprResult = exprEvaluator.GetResult();
|
|
origExprResult = exprResult;
|
|
if ((exprResult) && (!exprResult.mType->IsComposite()))
|
|
exprResult = module->LoadValue(exprResult);
|
|
module->FixIntUnknown(exprResult);
|
|
}
|
|
|
|
if (dbgState.mBlockedSideEffects)
|
|
{
|
|
if ((mCeMachine->mDbgPaused) && ((pendingExpr->mExpressionFlags & DwEvalExpressionFlag_AllowCalls) != 0))
|
|
{
|
|
// Reprocess in compiler thread
|
|
return "!pending";
|
|
}
|
|
|
|
return "!sideeffects";
|
|
}
|
|
|
|
if (!exprResult)
|
|
{
|
|
auto resultType = mCeMachine->mCeModule->ResolveTypeRef(pendingExpr->mExprNode, {}, BfPopulateType_Data, BfResolveTypeRefFlag_IgnoreLookupError);
|
|
if (resultType != NULL)
|
|
{
|
|
exprResult = BfTypedValue(resultType);
|
|
pendingExpr->mPassInstance->ClearErrors();
|
|
}
|
|
}
|
|
|
|
if ((exprResult.mType == NULL) && (dbgState.mReferencedIncompleteTypes))
|
|
{
|
|
if ((mCeMachine->mDbgPaused) && ((pendingExpr->mExpressionFlags & DwEvalExpressionFlag_AllowCalls) != 0))
|
|
{
|
|
// Reprocess in compiler thread
|
|
return "!pending";
|
|
}
|
|
return "!incomplete";
|
|
}
|
|
|
|
String val;
|
|
if (pendingExpr->mPassInstance->HasFailed())
|
|
{
|
|
BfLogDbgExpr("Evaluate Failed: %s\n", pendingExpr->mPassInstance->mErrors[0]->mError.c_str());
|
|
String errorStr = pendingExpr->mPassInstance->mErrors[0]->mError;
|
|
for (auto moreInfo : pendingExpr->mPassInstance->mErrors[0]->mMoreInfo)
|
|
{
|
|
errorStr += " ";
|
|
errorStr += moreInfo->mInfo;
|
|
}
|
|
|
|
errorStr.Replace('\n', ' ');
|
|
val = StrFormat("!%d\t%d\t%s", pendingExpr->mPassInstance->mErrors[0]->GetSrcStart(), pendingExpr->mPassInstance->mErrors[0]->GetSrcLength(), errorStr.c_str());
|
|
}
|
|
else if ((!exprResult) && (!exprResult.IsNoValueType()))
|
|
{
|
|
return "!Debug evaluate failed";
|
|
}
|
|
else
|
|
{
|
|
CeTypeModKind typeModKind = CeTypeModKind_Normal;
|
|
if ((origExprResult) && (!origExprResult.IsAddr()) && (origExprResult.mType->IsValueType()))
|
|
typeModKind = CeTypeModKind_Const;
|
|
else if (origExprResult.IsReadOnly())
|
|
typeModKind = CeTypeModKind_ReadOnly;
|
|
val = TypedValueToString(exprResult, pendingExpr->mExprNode->ToString(), pendingExpr->mFormatInfo, (pendingExpr->mExpressionFlags & DwEvalExpressionFlag_FullPrecision) != 0, typeModKind);
|
|
|
|
if ((!val.empty()) && (val[0] == '!'))
|
|
return val;
|
|
|
|
if (pendingExpr->mFormatInfo.mRawString)
|
|
return val;
|
|
|
|
if (pendingExpr->mPassInstance->HasMessages())
|
|
{
|
|
for (auto error : pendingExpr->mPassInstance->mErrors)
|
|
{
|
|
if (error->mIsWarning)
|
|
{
|
|
val += "\n:warn\t";
|
|
val += error->mError;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pendingExpr->mFormatInfo.mReferenceId.empty())
|
|
val += "\n:referenceId\t" + pendingExpr->mFormatInfo.mReferenceId;
|
|
|
|
// if ((exprResult.mSrcAddress != 0) && (HasMemoryBreakpoint(exprResult.mSrcAddress, exprResult.mType->GetByteCount())))
|
|
// val += StrFormat("\n:break\t%@", exprResult.mSrcAddress);
|
|
|
|
auto checkType = exprResult.mType;
|
|
if (checkType->IsObject())
|
|
val += "\n:type\tobject";
|
|
else if (checkType->IsPointer())
|
|
val += "\n:type\tpointer";
|
|
else if (checkType->IsInteger())
|
|
val += "\n:type\tint";
|
|
else if (checkType->IsFloat())
|
|
val += "\n:type\tfloat";
|
|
}
|
|
|
|
if (dbgState.mHadSideEffects)
|
|
val += "\n:sideeffects";
|
|
|
|
// auto resultConstant = module->mBfIRBuilder->GetConstant(exprResult.mValue);
|
|
// if (resultConstant != NULL)
|
|
// {
|
|
// auto ceResultTyped = GetAddr(resultConstant);
|
|
// if (ceResultTyped.mAddr != 0)
|
|
// val += "\n:canEdit";
|
|
// }
|
|
|
|
if ((origExprResult.mType != NULL) && (!origExprResult.mType->IsComposite()) && (!origExprResult.mType->IsRef()) &&
|
|
(origExprResult.IsAddr()) && (!origExprResult.IsReadOnly()))
|
|
{
|
|
val += "\n:canEdit";
|
|
}
|
|
|
|
if (pendingExpr->mCursorPos != -1)
|
|
val += GetAutocompleteOutput(autoComplete);
|
|
|
|
return val;
|
|
}
|
|
|
|
String CeDebugger::Evaluate(const StringImpl& expr, CeFormatInfo formatInfo, int callStackIdx, int cursorPos, int language, DwEvalExpressionFlags expressionFlags)
|
|
{
|
|
BP_ZONE_F("WinDebugger::Evaluate %s", BP_DYN_STR(expr.c_str()));
|
|
|
|
AutoCrit autoCrit(mCeMachine->mCritSect);
|
|
|
|
if ((expressionFlags & DwEvalExpressionFlag_RawStr) != 0)
|
|
{
|
|
formatInfo.mRawString = true;
|
|
}
|
|
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
bool valIsAddr = false;
|
|
|
|
BfParser* parser = new BfParser(mCompiler->mSystem);
|
|
|
|
BfPassInstance* bfPassInstance = new BfPassInstance(mCompiler->mSystem);
|
|
|
|
auto parseAsType = false;
|
|
|
|
auto terminatedExpr = expr;
|
|
terminatedExpr.Trim();
|
|
|
|
if (terminatedExpr.StartsWith("!"))
|
|
{
|
|
if (terminatedExpr == "!info")
|
|
{
|
|
auto ceFrame = GetFrame(callStackIdx);
|
|
if (ceFrame != NULL)
|
|
{
|
|
if (ceFrame->mFunction->mCeFunctionInfo->mMethodInstance != NULL)
|
|
OutputMessage(StrFormat("Module: %s\n", ceFrame->mFunction->mCeFunctionInfo->mMethodInstance->GetOwner()->mModule->mModuleName.c_str()));
|
|
OutputMessage(StrFormat("Link Name: %s\n", ceFrame->mFunction->mCeFunctionInfo->mName.c_str()));
|
|
}
|
|
return "";
|
|
}
|
|
}
|
|
|
|
if (terminatedExpr.EndsWith(">"))
|
|
parseAsType = true;
|
|
else if ((terminatedExpr.StartsWith("comptype(")) && (!terminatedExpr.Contains('.')))
|
|
parseAsType = true;
|
|
|
|
if (!parseAsType)
|
|
terminatedExpr += ";";
|
|
if ((terminatedExpr.length() > 2) && (terminatedExpr[0] == '@'))
|
|
{
|
|
if (terminatedExpr[1] == '!') // Return string as error
|
|
{
|
|
int errorEnd = (int)terminatedExpr.IndexOf("@!", 2);
|
|
if (errorEnd != -1)
|
|
return terminatedExpr.Substring(1, errorEnd - 1);
|
|
else
|
|
return terminatedExpr.Substring(1);
|
|
}
|
|
else if (terminatedExpr[1] == '>') // Return string as text
|
|
{
|
|
int errorEnd = (int)terminatedExpr.IndexOf("@>", 2);
|
|
if (errorEnd != -1)
|
|
return terminatedExpr.Substring(2, errorEnd - 1);
|
|
else
|
|
return terminatedExpr.Substring(2);
|
|
}
|
|
}
|
|
|
|
parser->SetSource(terminatedExpr.c_str(), (int)terminatedExpr.length());
|
|
parser->Parse(bfPassInstance);
|
|
|
|
BfReducer bfReducer;
|
|
bfReducer.mAlloc = parser->mAlloc;
|
|
bfReducer.mSystem = mCompiler->mSystem;
|
|
bfReducer.mPassInstance = bfPassInstance;
|
|
bfReducer.mVisitorPos = BfReducer::BfVisitorPos(parser->mRootNode);
|
|
bfReducer.mVisitorPos.MoveNext();
|
|
bfReducer.mSource = parser;
|
|
BfAstNode* exprNode = NULL;
|
|
if (parseAsType)
|
|
exprNode = bfReducer.CreateTypeRef(parser->mRootNode->mChildArr.GetAs<BfAstNode*>(0));
|
|
else
|
|
exprNode = bfReducer.CreateExpression(parser->mRootNode->mChildArr.GetAs<BfAstNode*>(0));
|
|
parser->Close();
|
|
|
|
formatInfo.mCallStackIdx = callStackIdx;
|
|
|
|
CePendingExpr* pendingExpr = new CePendingExpr();
|
|
pendingExpr->mPassInstance = bfPassInstance;
|
|
pendingExpr->mParser = parser;
|
|
pendingExpr->mCallStackIdx = callStackIdx;
|
|
pendingExpr->mCursorPos = cursorPos;
|
|
pendingExpr->mExpressionFlags = expressionFlags;
|
|
pendingExpr->mExprNode = exprNode;
|
|
|
|
BfType* explicitType = NULL;
|
|
String formatFlags;
|
|
String assignExpr;
|
|
int assignExprOffset = -1;
|
|
|
|
if ((exprNode != NULL) && (exprNode->GetSrcEnd() < (int)expr.length()))
|
|
{
|
|
int formatOffset = exprNode->GetSrcEnd();
|
|
while (formatOffset < (int)expr.length())
|
|
{
|
|
char c = expr[formatOffset];
|
|
if (c == ' ')
|
|
formatOffset++;
|
|
else
|
|
break;
|
|
}
|
|
|
|
formatFlags = Trim(expr.Substring(formatOffset));
|
|
bool isComplexType = false;
|
|
for (char c : formatFlags)
|
|
if (c == '>')
|
|
isComplexType = true;
|
|
if (isComplexType)
|
|
{
|
|
//explicitType = dbgModule->FindType(expr);
|
|
}
|
|
|
|
if ((explicitType == NULL) && (formatFlags.length() > 0))
|
|
{
|
|
String errorString = "Invalid expression";
|
|
if (!ParseFormatInfo(formatFlags, &formatInfo, bfPassInstance, &assignExprOffset, &assignExpr, &errorString))
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
bfPassInstance->FailAt(errorString, parser->mSourceData, exprNode->GetSrcEnd(), (int)expr.length() - exprNode->GetSrcEnd());
|
|
formatFlags = "";
|
|
}
|
|
if (assignExprOffset != -1)
|
|
assignExprOffset += formatOffset;
|
|
}
|
|
}
|
|
|
|
if (assignExpr.length() > 0)
|
|
{
|
|
String newEvalStr = exprNode->ToString() + " = ";
|
|
int errorOffset = (int)newEvalStr.length();
|
|
newEvalStr += assignExpr;
|
|
String result = Evaluate(newEvalStr, formatInfo, callStackIdx, cursorPos, language, expressionFlags);
|
|
if (result[0] == '!')
|
|
{
|
|
int tabPos = (int)result.IndexOf('\t');
|
|
if (tabPos > 0)
|
|
{
|
|
int errorStart = atoi(result.Substring(1, tabPos - 1).c_str());
|
|
if (errorStart >= errorOffset)
|
|
{
|
|
result = StrFormat("!%d", errorStart - errorOffset + assignExprOffset) + result.Substring(tabPos);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
pendingExpr->mExplitType = explicitType;
|
|
pendingExpr->mFormatInfo = formatInfo;
|
|
|
|
String result = DoEvaluate(pendingExpr, false);
|
|
if (result == "!pending")
|
|
{
|
|
BF_ASSERT(mDebugPendingExpr == NULL);
|
|
if (mDebugPendingExpr != NULL)
|
|
{
|
|
return "!retry"; // We already have a pending
|
|
}
|
|
mDebugPendingExpr = pendingExpr;
|
|
mCeMachine->mStepState.mKind = CeStepState::Kind_Evaluate;
|
|
mRunState = RunState_DebugEval;
|
|
ContinueDebugEvent();
|
|
}
|
|
else
|
|
delete pendingExpr;
|
|
return result;
|
|
}
|
|
|
|
void CeDebugger::ClearBreakpointCache()
|
|
{
|
|
if (!mCeMachine->mDbgPaused)
|
|
mCeMachine->mSpecialCheck = true;
|
|
mBreakpointFramesDirty = true;
|
|
mBreakpointCacheDirty = true;
|
|
for (auto kv : mFileInfo)
|
|
delete kv.mValue;
|
|
mFileInfo.Clear();
|
|
}
|
|
|
|
void CeDebugger::UpdateBreakpointAddrs()
|
|
{
|
|
for (auto breakpoint : mBreakpoints)
|
|
{
|
|
breakpoint->mCurBindAddr = 1;
|
|
}
|
|
|
|
CeFunction* ceFunction = NULL;
|
|
if (!mCeMachine->mFunctionIdMap.TryGetValue(mCurDisasmFuncId, &ceFunction))
|
|
return;
|
|
|
|
if (ceFunction->mBreakpointVersion != mBreakpointVersion)
|
|
UpdateBreakpoints(ceFunction);
|
|
|
|
for (auto kv : ceFunction->mBreakpoints)
|
|
kv.mValue.mBreakpoint->mCurBindAddr = ((intptr)ceFunction->mId << 32) | kv.mKey;
|
|
}
|
|
|
|
void CeDebugger::UpdateBreakpointCache()
|
|
{
|
|
AutoCrit autoCrit(mCeMachine->mCritSect);
|
|
|
|
mBreakpointCacheDirty = false;
|
|
if (!mFileInfo.IsEmpty())
|
|
return;
|
|
for (int i = 0; i < (int)mBreakpoints.mSize; i++)
|
|
{
|
|
auto breakpoint = mBreakpoints[i];
|
|
breakpoint->mIdx = i;
|
|
String fileName = breakpoint->mFilePath;
|
|
fileName = FixPathAndCase(fileName);
|
|
CeFileInfo** valuePtr;
|
|
CeFileInfo* fileInfo = NULL;
|
|
if (mFileInfo.TryAdd(fileName, NULL, &valuePtr))
|
|
{
|
|
fileInfo = new CeFileInfo();
|
|
*valuePtr = fileInfo;
|
|
}
|
|
else
|
|
fileInfo = *valuePtr;
|
|
fileInfo->mOrderedBreakpoints.Add(breakpoint);
|
|
}
|
|
|
|
for (auto kv : mFileInfo)
|
|
{
|
|
kv.mValue->mOrderedBreakpoints.Sort([](CeBreakpoint* lhs, CeBreakpoint* rhs)
|
|
{
|
|
return lhs->mLineNum < rhs->mLineNum;
|
|
});
|
|
}
|
|
}
|
|
|
|
static int CompareBreakpoint(CeBreakpoint* breakpoint, const int& lineNum)
|
|
{
|
|
return breakpoint->mRequestedLineNum - lineNum;
|
|
}
|
|
|
|
void CeDebugger::UpdateBreakpoints(CeFunction* ceFunction)
|
|
{
|
|
AutoCrit autoCrit(mCeMachine->mCritSect);
|
|
|
|
UpdateBreakpointCache();
|
|
|
|
ceFunction->UnbindBreakpoints();
|
|
|
|
String path;
|
|
int scope = -1;
|
|
CeFileInfo* ceFileInfo = NULL;
|
|
|
|
BitSet usedBreakpointSet(mBreakpoints.mSize);
|
|
|
|
for (auto& emitEntry : ceFunction->mEmitTable)
|
|
{
|
|
if (emitEntry.mScope != scope)
|
|
{
|
|
if (emitEntry.mScope != -1)
|
|
{
|
|
path = FixPathAndCase(ceFunction->mDbgScopes[emitEntry.mScope].mFilePath);
|
|
if (!mFileInfo.TryGetValue(path, &ceFileInfo))
|
|
ceFileInfo = NULL;
|
|
}
|
|
else
|
|
ceFileInfo = NULL;
|
|
scope = emitEntry.mScope;
|
|
}
|
|
|
|
if (ceFileInfo != NULL)
|
|
{
|
|
int idx = ceFileInfo->mOrderedBreakpoints.BinarySearchAlt<int>(emitEntry.mLine, CompareBreakpoint);
|
|
if (idx < 0)
|
|
idx = ~idx - 1;
|
|
|
|
while (idx > 0)
|
|
{
|
|
auto breakpoint = ceFileInfo->mOrderedBreakpoints[idx - 1];
|
|
if (breakpoint->mLineNum < emitEntry.mLine)
|
|
break;
|
|
idx--;
|
|
}
|
|
|
|
int tryBindCount = 0;
|
|
int bestRequestedBindLine = 0;
|
|
|
|
while ((idx >= 0) && (idx < ceFileInfo->mOrderedBreakpoints.mSize))
|
|
{
|
|
auto breakpoint = ceFileInfo->mOrderedBreakpoints[idx];
|
|
if (usedBreakpointSet.IsSet(breakpoint->mIdx))
|
|
{
|
|
idx++;
|
|
continue;
|
|
}
|
|
CeBreakpointBind* breakpointBind = NULL;
|
|
|
|
if (tryBindCount > 0)
|
|
{
|
|
if (breakpoint->mRequestedLineNum > bestRequestedBindLine)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
int lineDiff = emitEntry.mLine - breakpoint->mLineNum;
|
|
if ((lineDiff < 0) || (lineDiff > 4))
|
|
break;
|
|
|
|
if ((breakpoint->mHasBound) && (lineDiff != 0))
|
|
break;
|
|
|
|
bestRequestedBindLine = breakpoint->mRequestedLineNum;
|
|
}
|
|
|
|
tryBindCount++;
|
|
|
|
int codePos = emitEntry.mCodePos;
|
|
if (breakpoint->mInstrOffset > 0)
|
|
{
|
|
int instrOffsetLeft = breakpoint->mInstrOffset;
|
|
while (instrOffsetLeft > 0)
|
|
{
|
|
auto& opRef = *(CeOp*)(&ceFunction->mCode[codePos]);
|
|
int instSize = mCeMachine->GetInstSize(ceFunction, codePos);
|
|
codePos += instSize;
|
|
instrOffsetLeft--;
|
|
}
|
|
}
|
|
|
|
if (ceFunction->mBreakpoints.TryAdd(codePos, NULL, &breakpointBind))
|
|
{
|
|
usedBreakpointSet.Set(breakpoint->mIdx);
|
|
breakpoint->mLineNum = emitEntry.mLine;
|
|
breakpoint->mHasBound = true;
|
|
|
|
auto& opRef = *(CeOp*)(&ceFunction->mCode[codePos]);
|
|
breakpointBind->mPrevOpCode = opRef;
|
|
breakpointBind->mBreakpoint = breakpoint;
|
|
opRef = CeOp_DbgBreak;
|
|
}
|
|
|
|
idx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
ceFunction->mBreakpointVersion = mBreakpointVersion;
|
|
}
|
|
|
|
CeDbgTypeInfo* CeDebugger::GetDbgTypeInfo(int typeId)
|
|
{
|
|
CeDbgTypeInfo* dbgTypeInfo = NULL;
|
|
if (mDbgTypeInfoMap.TryAdd(typeId, NULL, &dbgTypeInfo))
|
|
{
|
|
auto type = mCeMachine->mCeModule->mContext->FindTypeById(typeId);
|
|
if (type == NULL)
|
|
{
|
|
mDbgTypeInfoMap.Remove(typeId);
|
|
return NULL;
|
|
}
|
|
dbgTypeInfo->mType = type;
|
|
|
|
auto typeInst = type->ToTypeInstance();
|
|
if (typeInst != NULL)
|
|
{
|
|
for (int fieldIdx = 0; fieldIdx < typeInst->mFieldInstances.mSize; fieldIdx++)
|
|
{
|
|
auto& fieldInst = typeInst->mFieldInstances[fieldIdx];
|
|
if (fieldInst.mDataIdx >= 0)
|
|
{
|
|
while (fieldInst.mDataIdx >= dbgTypeInfo->mFieldOffsets.mSize)
|
|
dbgTypeInfo->mFieldOffsets.Add(CeDbgFieldEntry());
|
|
dbgTypeInfo->mFieldOffsets[fieldInst.mDataIdx].mType = fieldInst.mResolvedType;
|
|
dbgTypeInfo->mFieldOffsets[fieldInst.mDataIdx].mDataOffset = fieldInst.mDataOffset;
|
|
}
|
|
|
|
if (fieldInst.mConstIdx != -1)
|
|
{
|
|
auto constant = typeInst->mConstHolder->GetConstantById(fieldInst.mConstIdx);
|
|
if ((constant != NULL) && (BfIRConstHolder::IsInt(constant->mTypeCode)))
|
|
{
|
|
CeDbgTypeInfo::ConstIntEntry constIntEntry;
|
|
constIntEntry.mFieldIdx = fieldIdx;
|
|
constIntEntry.mVal = constant->mInt64;
|
|
dbgTypeInfo->mConstIntEntries.Add(constIntEntry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return dbgTypeInfo;
|
|
}
|
|
|
|
CeDbgTypeInfo* CeDebugger::GetDbgTypeInfo(BfIRType irType)
|
|
{
|
|
if ((irType.mKind == BfIRTypeData::TypeKind_TypeId) || (irType.mKind == BfIRTypeData::TypeKind_TypeInstId))
|
|
return GetDbgTypeInfo(irType.mId);
|
|
if (irType.mKind == BfIRTypeData::TypeKind_TypeInstPtrId)
|
|
{
|
|
auto type = mCeMachine->mCeModule->mContext->FindTypeById(irType.mId);
|
|
if (type->IsObjectOrInterface())
|
|
return GetDbgTypeInfo(irType.mId);
|
|
else
|
|
return GetDbgTypeInfo(mCeMachine->mCeModule->CreatePointerType(type)->mTypeId);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static bool IsNormalChar(uint32 c)
|
|
{
|
|
return (c < 0x80);
|
|
}
|
|
|
|
template <typename T>
|
|
static String IntTypeToString(T val, const StringImpl& name, DwDisplayInfo* displayInfo, CeFormatInfo& formatInfo)
|
|
{
|
|
auto intDisplayType = displayInfo->mIntDisplayType;
|
|
if (formatInfo.mDisplayType == DwDisplayType_Decimal)
|
|
intDisplayType = DwIntDisplayType_Decimal;
|
|
else if (formatInfo.mDisplayType == DwDisplayType_HexUpper)
|
|
intDisplayType = DwIntDisplayType_HexadecimalUpper;
|
|
else if (formatInfo.mDisplayType == DwDisplayType_HexLower)
|
|
intDisplayType = DwIntDisplayType_HexadecimalLower;
|
|
|
|
if (intDisplayType == DwIntDisplayType_Binary)
|
|
{
|
|
String binary;
|
|
for (int i = 0; i < sizeof(T) * 8; i++)
|
|
{
|
|
if ((i != 0) && (i % 4 == 0))
|
|
binary = "'" + binary;
|
|
|
|
if ((i != 0) && (i % 16 == 0))
|
|
binary = "'" + binary;
|
|
|
|
binary = ((val & ((T)1 << i)) ? "1" : "0") + binary;
|
|
}
|
|
return StrFormat("0b'%s\n%s", binary.c_str(), name.c_str());
|
|
}
|
|
|
|
if (intDisplayType == DwIntDisplayType_Octal)
|
|
{
|
|
String format;
|
|
if (sizeof(T) == 8)
|
|
{
|
|
format = StrFormat("0o%%lo\n%s", name.c_str());
|
|
}
|
|
else
|
|
format = StrFormat("0o%%0%do\n%s", sizeof(val) * 2, name.c_str());
|
|
return StrFormat(format.c_str(), (typename std::make_unsigned<T>::type)(val));
|
|
}
|
|
|
|
if (intDisplayType == DwIntDisplayType_HexadecimalUpper)
|
|
{
|
|
String format;
|
|
if (sizeof(T) == 8)
|
|
{
|
|
format = StrFormat("0x%%l@\n%s", name.c_str());
|
|
}
|
|
else
|
|
format = StrFormat("0x%%0%dX\n%s", sizeof(val) * 2, name.c_str());
|
|
return StrFormat(format.c_str(), (typename std::make_unsigned<T>::type)(val));
|
|
}
|
|
|
|
//TODO: Implement HexadecimalLower
|
|
if (intDisplayType == DwIntDisplayType_HexadecimalLower)
|
|
{
|
|
String format;
|
|
if (sizeof(T) == 8)
|
|
{
|
|
format = StrFormat("0x%%l@\n%s", name.c_str());
|
|
}
|
|
else
|
|
format = StrFormat("0x%%0%dX\n%s", sizeof(val) * 2, name.c_str());
|
|
return StrFormat(format.c_str(), (typename std::make_unsigned<T>::type)(val));
|
|
}
|
|
|
|
if (std::is_unsigned<T>::value)
|
|
{
|
|
if (sizeof(T) == 8)
|
|
{
|
|
if (val > 0x7FFFFFFFF)
|
|
return StrFormat("%llu\n%s\n:editVal\t%lluUL", val, name.c_str(), val);
|
|
else
|
|
return StrFormat("%llu\n%s", val, name.c_str());
|
|
}
|
|
else
|
|
return StrFormat("%u\n%s", val, name.c_str());
|
|
}
|
|
else
|
|
{
|
|
if (sizeof(T) == 8)
|
|
{
|
|
if ((val > 0x7FFFFFFFF) || (val < -0x80000000LL))
|
|
return StrFormat("%lld\n%s\n:editVal\t%lldL", val, name.c_str(), val);
|
|
else
|
|
return StrFormat("%lld\n%s", val, name.c_str(), val);
|
|
}
|
|
else
|
|
return StrFormat("%d\n%s", val, name.c_str());
|
|
}
|
|
}
|
|
|
|
DwDisplayInfo* CeDebugger::GetDisplayInfo(const StringImpl& referenceId)
|
|
{
|
|
DwDisplayInfo* displayInfo = &mDebugManager->mDefaultDisplayInfo;
|
|
if (!referenceId.empty())
|
|
{
|
|
mDebugManager->mDisplayInfos.TryGetValue(referenceId, &displayInfo);
|
|
}
|
|
return displayInfo;
|
|
}
|
|
|
|
String CeDebugger::GetMemberList(BfType* type, addr_ce addr, addr_ce addrInst, bool isStatic)
|
|
{
|
|
auto typeInst = type->ToTypeInstance();
|
|
if (typeInst == NULL)
|
|
return "";
|
|
|
|
auto module = typeInst->mModule;
|
|
|
|
String retVal;
|
|
int fieldCount = 0;
|
|
|
|
if ((!isStatic) && (typeInst->mBaseType != NULL) && (!typeInst->mBaseType->IsInstanceOf(mCompiler->mValueTypeTypeDef)))
|
|
{
|
|
retVal += StrFormat("[base]\tthis,this=%d@0x%X, nd, na, nv", typeInst->mBaseType->mTypeId, addr);
|
|
fieldCount++;
|
|
}
|
|
|
|
auto ceContext = mCompiler->mCeMachine->mCurContext;
|
|
bool didStaticCtor = ceContext->mStaticCtorExecSet.Contains(type->mTypeId);
|
|
|
|
bool hasStaticFields = false;
|
|
for (auto& fieldInst : typeInst->mFieldInstances)
|
|
{
|
|
auto fieldDef = fieldInst.GetFieldDef();
|
|
if (fieldDef == NULL)
|
|
continue;
|
|
|
|
if (fieldDef->mIsStatic != isStatic)
|
|
{
|
|
if (fieldDef->mIsStatic)
|
|
hasStaticFields = true;
|
|
continue;
|
|
}
|
|
|
|
if (fieldCount > 0)
|
|
retVal += "\n";
|
|
|
|
retVal += fieldDef->mName;
|
|
if (fieldDef->mIsStatic)
|
|
{
|
|
if (didStaticCtor)
|
|
retVal += StrFormat("\tcomptype(%d).%s", type->mTypeId, fieldDef->mName.c_str());
|
|
else
|
|
retVal += StrFormat("\tcomptype(%d).%s", type->mTypeId, fieldDef->mName.c_str());
|
|
}
|
|
else
|
|
{
|
|
retVal += "\t";
|
|
retVal += fieldDef->mName;
|
|
retVal += StrFormat(",this=%d@0x%X", typeInst->mTypeId, addrInst);
|
|
}
|
|
|
|
fieldCount++;
|
|
}
|
|
|
|
if (hasStaticFields)
|
|
{
|
|
if (fieldCount > 0)
|
|
retVal += "\n";
|
|
|
|
retVal += StrFormat("Static values\tcomptype(%d)", typeInst->mTypeId);
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
bool CeDebugger::ParseFormatInfo(const StringImpl& formatInfoStr, CeFormatInfo* formatInfo, BfPassInstance* bfPassInstance, int* assignExprOffset, String* assignExprString, String* errorString, BfTypedValue contextTypedValue)
|
|
{
|
|
String formatFlags = formatInfoStr;
|
|
if (assignExprOffset != NULL)
|
|
*assignExprOffset = -1;
|
|
|
|
while (formatFlags.length() > 0)
|
|
{
|
|
formatFlags = Trim(formatFlags);
|
|
if (formatFlags.IsEmpty())
|
|
break;
|
|
if (formatFlags[0] != ',')
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
int nextComma = (int)formatFlags.IndexOf(',', 1);
|
|
int quotePos = (int)formatFlags.IndexOf('"', 1);
|
|
if ((quotePos != -1) && (quotePos < nextComma))
|
|
{
|
|
int nextQuotePos = (int)formatFlags.IndexOf('"', quotePos + 1);
|
|
if (nextQuotePos != -1)
|
|
nextComma = (int)formatFlags.IndexOf(',', nextQuotePos + 1);
|
|
}
|
|
if (nextComma == -1)
|
|
nextComma = (int)formatFlags.length();
|
|
|
|
String formatCmd = formatFlags.Substring(1, nextComma - 1);
|
|
formatCmd = Trim(formatCmd);
|
|
bool hadError = false;
|
|
|
|
if (strncmp(formatCmd.c_str(), "this=", 5) == 0)
|
|
{
|
|
formatCmd = formatFlags.Substring(1);
|
|
formatCmd = Trim(formatCmd);
|
|
String thisExpr = formatCmd.Substring(5);
|
|
if (thisExpr.empty())
|
|
break;
|
|
CeEvaluationContext dbgEvaluationContext(this, thisExpr, formatInfo);
|
|
formatInfo->mExplicitThis = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
|
|
if (dbgEvaluationContext.HadError())
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = dbgEvaluationContext.GetErrorStr();
|
|
return false;
|
|
}
|
|
formatFlags = thisExpr.Substring(dbgEvaluationContext.mExprString.GetLength());
|
|
continue;
|
|
}
|
|
else if (strncmp(formatCmd.c_str(), "count=", 6) == 0)
|
|
{
|
|
formatCmd = formatFlags.Substring(1);
|
|
formatCmd = Trim(formatCmd);
|
|
String countExpr = formatCmd.Substring(6);
|
|
if (countExpr.empty())
|
|
break;
|
|
CeEvaluationContext dbgEvaluationContext(this, countExpr, formatInfo);
|
|
BfTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
|
|
if ((countValue) && (countValue.mType->IsInteger()))
|
|
formatInfo->mOverrideCount = (intptr)ValueToInt(countValue);
|
|
if (dbgEvaluationContext.HadError())
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = dbgEvaluationContext.GetErrorStr();
|
|
return false;
|
|
}
|
|
formatFlags = countExpr.Substring(dbgEvaluationContext.mExprString.GetLength());
|
|
continue;
|
|
}
|
|
else if (strncmp(formatCmd.c_str(), "maxcount=", 9) == 0)
|
|
{
|
|
formatCmd = formatFlags.Substring(1);
|
|
formatCmd = Trim(formatCmd);
|
|
String countExpr = formatCmd.Substring(9);
|
|
if (countExpr.empty())
|
|
break;
|
|
CeEvaluationContext dbgEvaluationContext(this, countExpr, formatInfo);
|
|
BfTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
|
|
if ((countValue) && (countValue.mType->IsInteger()))
|
|
formatInfo->mMaxCount = (intptr)ValueToInt(countValue);
|
|
if (dbgEvaluationContext.HadError())
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = dbgEvaluationContext.GetErrorStr();
|
|
return false;
|
|
}
|
|
formatFlags = countExpr.Substring(dbgEvaluationContext.mExprString.GetLength());
|
|
continue;
|
|
}
|
|
else if (strncmp(formatCmd.c_str(), "arraysize=", 10) == 0)
|
|
{
|
|
formatCmd = formatFlags.Substring(1);
|
|
formatCmd = Trim(formatCmd);
|
|
String countExpr = formatCmd.Substring(10);
|
|
if (countExpr.empty())
|
|
break;
|
|
CeEvaluationContext dbgEvaluationContext(this, countExpr, formatInfo);
|
|
BfTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
|
|
if ((countValue) && (countValue.mType->IsInteger()))
|
|
formatInfo->mArrayLength = (intptr)ValueToInt(countValue);
|
|
if (dbgEvaluationContext.HadError())
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = dbgEvaluationContext.GetErrorStr();
|
|
return false;
|
|
}
|
|
formatFlags = countExpr.Substring(dbgEvaluationContext.mExprString.GetLength());
|
|
continue;
|
|
}
|
|
else if (strncmp(formatCmd.c_str(), "assign=", 7) == 0)
|
|
{
|
|
formatCmd = formatFlags.Substring(1);
|
|
formatCmd = Trim(formatCmd);
|
|
String assignExpr = formatCmd.Substring(7);
|
|
if (assignExpr.empty())
|
|
break;
|
|
CeEvaluationContext dbgEvaluationContext(this, assignExpr, formatInfo);
|
|
if (dbgEvaluationContext.HadError())
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = dbgEvaluationContext.GetErrorStr();
|
|
return false;
|
|
}
|
|
if (assignExprOffset != NULL)
|
|
{
|
|
//TODO: Keep track of the offset directly, this is a hack
|
|
*assignExprOffset = (int)formatInfoStr.IndexOf("assign=") + 7;
|
|
}
|
|
if (assignExprString != NULL)
|
|
*assignExprString = dbgEvaluationContext.mExprNode->ToString();
|
|
formatFlags = assignExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
|
|
continue;
|
|
}
|
|
else if (strncmp(formatCmd.c_str(), "refid=", 6) == 0)
|
|
{
|
|
formatInfo->mReferenceId = formatCmd.Substring(6);
|
|
if (formatInfo->mReferenceId[0] == '\"')
|
|
formatInfo->mReferenceId = formatInfo->mReferenceId.Substring(1, formatInfo->mReferenceId.length() - 2);
|
|
}
|
|
else if (strncmp(formatCmd.c_str(), "_=", 2) == 0)
|
|
{
|
|
formatInfo->mSubjectExpr = formatCmd.Substring(2);
|
|
if (formatInfo->mSubjectExpr[0] == '\"')
|
|
formatInfo->mSubjectExpr = formatInfo->mSubjectExpr.Substring(1, formatInfo->mSubjectExpr.length() - 2);
|
|
}
|
|
else if (strncmp(formatCmd.c_str(), "expectedType=", 13) == 0)
|
|
{
|
|
formatInfo->mExpectedType = formatCmd.Substring(13);
|
|
if (formatInfo->mExpectedType[0] == '\"')
|
|
formatInfo->mExpectedType = formatInfo->mExpectedType.Substring(1, formatInfo->mExpectedType.length() - 2);
|
|
}
|
|
else if (strncmp(formatCmd.c_str(), "namespaceSearch=", 16) == 0)
|
|
{
|
|
formatInfo->mNamespaceSearch = formatCmd.Substring(16);
|
|
if (formatInfo->mNamespaceSearch[0] == '\"')
|
|
formatInfo->mNamespaceSearch = formatInfo->mNamespaceSearch.Substring(1, formatInfo->mNamespaceSearch.length() - 2);
|
|
}
|
|
else if (formatCmd == "d")
|
|
{
|
|
formatInfo->mDisplayType = DwDisplayType_Decimal;
|
|
}
|
|
else if (formatCmd == "x")
|
|
{
|
|
formatInfo->mDisplayType = DwDisplayType_HexLower;
|
|
}
|
|
else if (formatCmd == "X")
|
|
{
|
|
formatInfo->mDisplayType = DwDisplayType_HexUpper;
|
|
}
|
|
else if (formatCmd == "s")
|
|
{
|
|
formatInfo->mHidePointers = true;
|
|
formatInfo->mDisplayType = DwDisplayType_Ascii;
|
|
}
|
|
else if (formatCmd == "s8")
|
|
{
|
|
formatInfo->mHidePointers = true;
|
|
formatInfo->mDisplayType = DwDisplayType_Utf8;
|
|
}
|
|
else if (formatCmd == "s16")
|
|
{
|
|
formatInfo->mHidePointers = true;
|
|
formatInfo->mDisplayType = DwDisplayType_Utf16;
|
|
}
|
|
else if (formatCmd == "s32")
|
|
{
|
|
formatInfo->mHidePointers = true;
|
|
formatInfo->mDisplayType = DwDisplayType_Utf32;
|
|
}
|
|
else if (formatCmd == "nd")
|
|
{
|
|
formatInfo->mIgnoreDerivedClassInfo = true;
|
|
}
|
|
else if (formatCmd == "na")
|
|
{
|
|
formatInfo->mHidePointers = true;
|
|
}
|
|
else if (formatCmd == "nm")
|
|
{
|
|
formatInfo->mNoMembers = true;
|
|
}
|
|
else if (formatCmd == "ne")
|
|
{
|
|
formatInfo->mNoEdit = true;
|
|
}
|
|
else if (formatCmd == "nv")
|
|
{
|
|
formatInfo->mNoVisualizers = true;
|
|
}
|
|
else if (formatCmd == "rawStr")
|
|
{
|
|
formatInfo->mRawString = true;
|
|
}
|
|
else if (((!formatCmd.IsEmpty()) && ((formatCmd[0] >= '0') && (formatCmd[0] <= '9'))) ||
|
|
(formatCmd.StartsWith("(")))
|
|
{
|
|
String countExpr = formatCmd;
|
|
if (countExpr.empty())
|
|
break;
|
|
CeEvaluationContext dbgEvaluationContext(this, countExpr, formatInfo);
|
|
BfTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
|
|
if ((countValue) && (countValue.mType->IsInteger()))
|
|
formatInfo->mArrayLength = (intptr)ValueToInt(countValue);
|
|
if (dbgEvaluationContext.HadError())
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = dbgEvaluationContext.GetErrorStr();
|
|
return false;
|
|
}
|
|
formatFlags = dbgEvaluationContext.mExprString;
|
|
continue;
|
|
}
|
|
else
|
|
hadError = true;
|
|
|
|
if (hadError)
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = "Invalid format flags";
|
|
return false;
|
|
}
|
|
|
|
formatFlags = formatFlags.Substring(nextComma);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
String CeDebugger::MaybeQuoteFormatInfoParam(const StringImpl& str)
|
|
{
|
|
bool needsQuote = false;
|
|
for (int i = 0; i < (int)str.length(); i++)
|
|
{
|
|
char c = str[i];
|
|
if (c == ',')
|
|
needsQuote = true;
|
|
}
|
|
if (!needsQuote)
|
|
return str;
|
|
|
|
String qStr = "\"";
|
|
qStr += str;
|
|
qStr += "\"";
|
|
return qStr;
|
|
}
|
|
|
|
BfTypedValue CeDebugger::EvaluateInContext(const BfTypedValue& contextTypedValue, const StringImpl& subExpr, CeFormatInfo* formatInfo, String* outReferenceId, String* outErrors)
|
|
{
|
|
CeEvaluationContext dbgEvaluationContext(this, subExpr, formatInfo, contextTypedValue);
|
|
// if (formatInfo != NULL)
|
|
// {
|
|
// dbgEvaluationContext.mDbgExprEvaluator->mSubjectExpr = formatInfo->mSubjectExpr;
|
|
// }
|
|
//dbgEvaluationContext.mDbgExprEvaluator->mReferenceId = outReferenceId;
|
|
|
|
SetAndRestoreValue<CeEvaluationContext*> prevEvalContext(mCurEvaluationContext, &dbgEvaluationContext);
|
|
|
|
//mCountResultOverride = -1;
|
|
auto result = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
|
|
// if ((formatInfo != NULL) && (dbgEvaluationContext.mDbgExprEvaluator->mCountResultOverride != -1))
|
|
// formatInfo->mOverrideCount = dbgEvaluationContext.mDbgExprEvaluator->mCountResultOverride;
|
|
if (dbgEvaluationContext.mPassInstance->HasFailed())
|
|
{
|
|
if (outErrors != NULL)
|
|
{
|
|
int errIdx = 0;
|
|
for (auto err : dbgEvaluationContext.mPassInstance->mErrors)
|
|
{
|
|
if (errIdx > 0)
|
|
(*outErrors) += "\n";
|
|
(*outErrors) += err->mError;
|
|
errIdx++;
|
|
}
|
|
}
|
|
return BfTypedValue();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void CeDebugger::DbgVisFailed(DebugVisualizerEntry* debugVis, const StringImpl& evalString, const StringImpl& errors)
|
|
{
|
|
bool onlyMemError = errors.StartsWith("Failed to read") && !errors.Contains('\n');
|
|
if ((!debugVis->mShowedError) && (!onlyMemError))
|
|
{
|
|
debugVis->mShowedError = true;
|
|
String errStr = StrFormat("DbgVis '%s' failed while evaluating condition '%s'\n", debugVis->mName.c_str(), evalString.c_str());
|
|
String spacedErrors = errors;
|
|
spacedErrors.Insert(0, " ");
|
|
spacedErrors.Replace("\n", "\n ");
|
|
errStr += spacedErrors;
|
|
OutputMessage(errStr);
|
|
}
|
|
}
|
|
|
|
bool CeDebugger::EvalCondition(DebugVisualizerEntry* debugVis, BfTypedValue typedVal, CeFormatInfo& formatInfo, const StringImpl& condition, const Array<String>& dbgVisWildcardCaptures, String& errorStr)
|
|
{
|
|
auto ceModule = mCeMachine->mCeModule;
|
|
|
|
CeFormatInfo displayStrFormatInfo = formatInfo;
|
|
displayStrFormatInfo.mHidePointers = false;
|
|
displayStrFormatInfo.mRawString = false;
|
|
|
|
String errors;
|
|
const String conditionStr = mDebugManager->mDebugVisualizers->DoStringReplace(condition, dbgVisWildcardCaptures);
|
|
BfTypedValue evalResult = EvaluateInContext(typedVal, conditionStr, &displayStrFormatInfo, NULL, &errors);
|
|
if ((!evalResult) || (!evalResult.mType->IsBoolean()))
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return false;
|
|
|
|
errorStr += "<DbgVis Failed>";
|
|
DbgVisFailed(debugVis, conditionStr, errors);
|
|
return false;
|
|
}
|
|
|
|
evalResult = ceModule->LoadValue(evalResult);
|
|
if (auto constant = ceModule->mBfIRBuilder->GetConstant(evalResult.mValue))
|
|
{
|
|
if (constant->mTypeCode == BfTypeCode_Boolean)
|
|
return constant->mBool;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
String CeDebugger::GetArrayItems(DebugVisualizerEntry* debugVis, BfType* valueType, BfTypedValue& curNode, int& count, String* outContinuationData)
|
|
{
|
|
CeEvaluationContext conditionEvaluationContext(this, debugVis->mCondition);
|
|
auto ceModule = mCeMachine->mCeModule;
|
|
|
|
String addrs;
|
|
|
|
bool checkLeft = true;
|
|
|
|
addr_ce curNodeAddr = 0;
|
|
|
|
int usedCount = 0;
|
|
while (usedCount < count)
|
|
{
|
|
if ((!curNode) || (!curNode.mType->IsPointer()))
|
|
break;
|
|
curNode = ceModule->LoadValue(curNode);
|
|
|
|
BfTypedValue condVal = conditionEvaluationContext.EvaluateInContext(curNode);
|
|
if (!condVal)
|
|
break;
|
|
|
|
auto ceTypedVal = GetAddr(curNode);
|
|
if (!ceTypedVal)
|
|
break;
|
|
|
|
if (ValueToInt(condVal) != 0)
|
|
{
|
|
auto val = curNode;
|
|
if (valueType == NULL)
|
|
{
|
|
//String typeAddr = val.mType->ToStringRaw();
|
|
String typeAddr = StrFormat("comptype(%d)", val.mType->mTypeId);
|
|
// RPad
|
|
typeAddr.Append(' ', sizeof(addr_ce) * 2 - (int)typeAddr.length());
|
|
addrs += typeAddr;
|
|
}
|
|
|
|
String addr = EncodeDataPtr((addr_ce)ceTypedVal.mAddr, false);
|
|
addrs += addr;
|
|
usedCount++;
|
|
}
|
|
auto elemType = curNode.mType->GetUnderlyingType();
|
|
curNodeAddr = (addr_ce)ceTypedVal.mAddr + elemType->GetStride();
|
|
curNode.mValue = ceModule->mBfIRBuilder->CreateIntToPtr(curNodeAddr, ceModule->mBfIRBuilder->MapType(curNode.mType));
|
|
}
|
|
count = usedCount;
|
|
|
|
if (outContinuationData != NULL)
|
|
{
|
|
*outContinuationData += EncodeDataPtr(debugVis, false) + EncodeDataPtr(valueType, false) +
|
|
EncodeDataPtr(curNode.mType, false) + EncodeDataPtr(curNodeAddr, false);
|
|
}
|
|
|
|
return addrs;
|
|
}
|
|
|
|
String CeDebugger::GetLinkedListItems(DebugVisualizerEntry* debugVis, addr_ce endNodePtr, BfType* valueType, BfTypedValue& curNode, int& count, String* outContinuationData)
|
|
{
|
|
CeEvaluationContext nextEvaluationContext(this, debugVis->mNextPointer);
|
|
CeEvaluationContext valueEvaluationContext(this, debugVis->mValuePointer);
|
|
auto ceModule = mCeMachine->mCeModule;
|
|
|
|
String addrs;
|
|
|
|
bool checkLeft = true;
|
|
|
|
int mapIdx;
|
|
for (mapIdx = 0; mapIdx < count; mapIdx++)
|
|
{
|
|
CeTypedValue ceNodeVal = GetAddr(curNode);
|
|
if (!ceNodeVal)
|
|
break;
|
|
|
|
if (ceNodeVal.mAddr == endNodePtr)
|
|
break;
|
|
BfTypedValue val = valueEvaluationContext.EvaluateInContext(curNode);
|
|
if (!val)
|
|
break;
|
|
|
|
auto ceVal = GetAddr(val);
|
|
if (!ceVal)
|
|
break;
|
|
if (ceVal.mAddr == 0)
|
|
break;
|
|
|
|
if (valueType == NULL)
|
|
{
|
|
String typeAddr = StrFormat("comptype(%d)", val.mType->mTypeId);
|
|
// RPad
|
|
typeAddr.Append(' ', sizeof(addr_ce) * 2 - (int)typeAddr.length());
|
|
addrs += typeAddr;
|
|
}
|
|
|
|
String addr = EncodeDataPtr((addr_ce)ceVal.mAddr, false);
|
|
addrs += addr;
|
|
|
|
curNode = nextEvaluationContext.EvaluateInContext(curNode);
|
|
}
|
|
count = mapIdx;
|
|
|
|
if (outContinuationData != NULL)
|
|
{
|
|
CeTypedValue ceNodeVal = GetAddr(curNode);
|
|
*outContinuationData += EncodeDataPtr(debugVis, false) + EncodeDataPtr(endNodePtr, false) + EncodeDataPtr(valueType, false) +
|
|
EncodeDataPtr(curNode.mType, false) + EncodeDataPtr((addr_ce)ceNodeVal.mAddr, false);
|
|
}
|
|
|
|
return addrs;
|
|
}
|
|
|
|
String CeDebugger::GetDictionaryItems(DebugVisualizerEntry* debugVis, BfTypedValue dictValue, int bucketIdx, int nodeIdx, int& count, String* outContinuationData)
|
|
{
|
|
CeEvaluationContext nextEvaluationContext(this, debugVis->mNextPointer);
|
|
auto ceModule = mCeMachine->mCeModule;
|
|
|
|
BfTypedValue bucketsPtr = EvaluateInContext(dictValue, debugVis->mBuckets);
|
|
BfTypedValue entriesPtr = EvaluateInContext(dictValue, debugVis->mEntries);
|
|
if ((!bucketsPtr) || (!entriesPtr))
|
|
{
|
|
count = -1;
|
|
return "";
|
|
}
|
|
|
|
auto ceDictTypedVal = GetAddr(dictValue);
|
|
if (!ceDictTypedVal)
|
|
return "";
|
|
|
|
auto ceBucketsTypedVal = GetAddr(bucketsPtr);
|
|
if (!ceBucketsTypedVal)
|
|
return "";
|
|
|
|
String addrs;
|
|
|
|
if ((!entriesPtr) || (!entriesPtr.mType->IsPointer()))
|
|
return "";
|
|
if ((!bucketsPtr) || (!bucketsPtr.mType->IsPointer()))
|
|
return "";
|
|
|
|
auto entryType = entriesPtr.mType->GetUnderlyingType();
|
|
int entrySize = entryType->GetStride();
|
|
int bucketIdxSize = bucketsPtr.mType->GetUnderlyingType()->GetStride();
|
|
|
|
auto ceElemTypedVal = GetAddr(entriesPtr);
|
|
if (!ceElemTypedVal)
|
|
return "";
|
|
|
|
bool checkLeft = true;
|
|
|
|
int encodeCount = 0;
|
|
while (encodeCount < count)
|
|
{
|
|
if (nodeIdx != -1)
|
|
{
|
|
addr_ce entryAddr = (addr_ce)ceElemTypedVal.mAddr + (nodeIdx * entrySize);
|
|
|
|
BfTypedValue entryValue = BfTypedValue(ceModule->mBfIRBuilder->CreateConstAggCE(ceModule->mBfIRBuilder->MapType(entryType), entryAddr), entryType);
|
|
addrs += EncodeDataPtr(entryAddr, false);
|
|
|
|
BfTypedValue nextValue = nextEvaluationContext.EvaluateInContext(entryValue);
|
|
if ((!nextValue) || (!nextValue.mType->IsInteger()))
|
|
{
|
|
break;
|
|
}
|
|
|
|
nodeIdx = (int)ValueToInt(nextValue);
|
|
encodeCount++;
|
|
}
|
|
else
|
|
{
|
|
if (bucketIdxSize == 4)
|
|
nodeIdx = ReadMemory<int>(ceBucketsTypedVal.mAddr + bucketIdx * sizeof(int32));
|
|
else
|
|
nodeIdx = (int)ReadMemory<int64>(ceBucketsTypedVal.mAddr + bucketIdx * sizeof(int64));
|
|
bucketIdx++;
|
|
}
|
|
}
|
|
|
|
count = encodeCount;
|
|
|
|
if (outContinuationData != NULL)
|
|
{
|
|
*outContinuationData += EncodeDataPtr(debugVis, false) + EncodeDataPtr(dictValue.mType, false) + EncodeDataPtr((addr_ce)ceDictTypedVal.mAddr, false) +
|
|
EncodeDataPtr((addr_ce)bucketIdx, false) + EncodeDataPtr((addr_ce)nodeIdx, false);
|
|
}
|
|
|
|
return addrs;
|
|
}
|
|
|
|
String CeDebugger::GetTreeItems(DebugVisualizerEntry* debugVis, Array<addr_ce>& parentList, BfType*& valueType, BfTypedValue& curNode, int count, String* outContinuationData)
|
|
{
|
|
CeEvaluationContext leftEvaluationContext(this, debugVis->mLeftPointer);
|
|
CeEvaluationContext rightEvaluationContext(this, debugVis->mRightPointer);
|
|
CeEvaluationContext valueEvaluationContext(this, debugVis->mValuePointer);
|
|
|
|
CeEvaluationContext conditionEvaluationContext(this, debugVis->mCondition);
|
|
|
|
String addrs;
|
|
|
|
//TODO:
|
|
// bool checkLeft = true;
|
|
//
|
|
// if ((curNode.mPtr & 2) != 0) // Flag from continuation
|
|
// {
|
|
// checkLeft = false;
|
|
// curNode.mPtr &= (addr_ce)~2;
|
|
// }
|
|
//
|
|
// HashSet<intptr> seenAddrs;
|
|
//
|
|
// for (int mapIdx = 0; mapIdx < count; mapIdx++)
|
|
// {
|
|
// BfTypedValue readNode;
|
|
// while (true)
|
|
// {
|
|
// bool checkNode = (curNode.mPtr & 1) == 0;
|
|
//
|
|
// readNode = curNode;
|
|
// readNode.mPtr &= (addr_ce)~1;
|
|
//
|
|
// if (checkLeft)
|
|
// {
|
|
// BfTypedValue leftValue = leftEvaluationContext.EvaluateInContext(readNode);
|
|
// bool isEmpty = leftValue.mPtr == NULL;
|
|
// if ((leftValue) && (conditionEvaluationContext.HasExpression()))
|
|
// {
|
|
// auto condValue = conditionEvaluationContext.EvaluateInContext(leftValue);
|
|
// if (condValue)
|
|
// isEmpty = !condValue.mBool;
|
|
// }
|
|
// if (isEmpty)
|
|
// {
|
|
// checkLeft = false;
|
|
// break; // Handle node
|
|
// }
|
|
//
|
|
// parentList.push_back(curNode.mPtr);
|
|
// curNode = leftValue;
|
|
// }
|
|
// else if (checkNode)
|
|
// {
|
|
// break; // Handle node
|
|
// }
|
|
// else
|
|
// {
|
|
// BfTypedValue rightValue = rightEvaluationContext.EvaluateInContext(readNode);
|
|
// bool isEmpty = rightValue.mPtr == NULL;
|
|
// if ((rightValue) && (conditionEvaluationContext.HasExpression()))
|
|
// {
|
|
// auto condValue = conditionEvaluationContext.EvaluateInContext(rightValue);
|
|
// if (condValue)
|
|
// isEmpty = !condValue.mBool;
|
|
// }
|
|
// if (!isEmpty)
|
|
// {
|
|
// curNode = rightValue;
|
|
// checkLeft = true;
|
|
// }
|
|
// else
|
|
// {
|
|
// if (parentList.size() == 0)
|
|
// {
|
|
// // Failed
|
|
// break;
|
|
// }
|
|
//
|
|
// curNode.mPtr = parentList.back();
|
|
// parentList.pop_back();
|
|
// continue; // Don't check against seenAddrs
|
|
// }
|
|
// }
|
|
//
|
|
// if (!seenAddrs.Add(curNode.mPtr))
|
|
// {
|
|
// // Failed!
|
|
// return "";
|
|
// }
|
|
// }
|
|
//
|
|
//
|
|
// BfTypedValue val = valueEvaluationContext.EvaluateInContext(readNode);
|
|
// if (valueType == NULL)
|
|
// valueType = val.mType;
|
|
//
|
|
// String addr = EncodeDataPtr(val.mPtr, false);
|
|
// addrs += addr;
|
|
//
|
|
// curNode.mPtr |= 1; // Node handled
|
|
// }
|
|
//
|
|
// if (!checkLeft)
|
|
// curNode.mPtr |= 2;
|
|
//
|
|
// if (outContinuationData != NULL)
|
|
// {
|
|
// *outContinuationData += EncodeDataPtr(debugVis, false) + EncodeDataPtr(valueType, false) + EncodeDataPtr(curNode.mType, false) + EncodeDataPtr(curNode.mPtr, false);
|
|
// for (auto parent : parentList)
|
|
// *outContinuationData += EncodeDataPtr(parent, false);
|
|
// }
|
|
|
|
return addrs;
|
|
}
|
|
|
|
String CeDebugger::GetCollectionContinuation(const StringImpl& continuationData, int callStackIdx, int count)
|
|
{
|
|
if (!mCeMachine->mDbgPaused)
|
|
return "";
|
|
|
|
auto ceModule = mCeMachine->mCeModule;
|
|
const char* dataPtr = continuationData.c_str();
|
|
DebugVisualizerEntry* debugVis = (DebugVisualizerEntry*)DecodeLocalDataPtr(dataPtr);
|
|
|
|
if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_TreeItems)
|
|
{
|
|
//TODO:
|
|
// DbgType* valueType = (DbgType*)DecodeLocalDataPtr(dataPtr);
|
|
// BfTypedValue curNode;
|
|
// curNode.mType = (DbgType*)DecodeLocalDataPtr(dataPtr);
|
|
// curNode.mPtr = DecodeTargetDataPtr(dataPtr);
|
|
//
|
|
// Array<addr_ce> parentList;
|
|
// String newContinuationData;
|
|
// while (*dataPtr != 0)
|
|
// parentList.push_back(DecodeTargetDataPtr(dataPtr));
|
|
//
|
|
// String retVal = GetTreeItems(dbgCompileUnit, debugVis, parentList, valueType, curNode, count, &newContinuationData);
|
|
// retVal += "\n" + newContinuationData;
|
|
// return retVal;
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_LinkedList)
|
|
{
|
|
addr_ce endNodePtr = DecodeTargetDataPtr(dataPtr);
|
|
BfType* valueType = (BfType*)DecodeLocalDataPtr(dataPtr);
|
|
BfType* nodeType = (BfType*)DecodeLocalDataPtr(dataPtr);
|
|
|
|
BfTypedValue curNode = BfTypedValue(
|
|
ceModule->mBfIRBuilder->CreateIntToPtr(DecodeTargetDataPtr(dataPtr), ceModule->mBfIRBuilder->MapType(nodeType)),
|
|
nodeType);
|
|
|
|
String newContinuationData;
|
|
|
|
if (count < 0)
|
|
count = 3;
|
|
|
|
String retVal = GetLinkedListItems(debugVis, endNodePtr, valueType, curNode, count, &newContinuationData);
|
|
retVal += "\n" + newContinuationData;
|
|
return retVal;
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_Array)
|
|
{
|
|
BfType* valueType = (BfType*)DecodeLocalDataPtr(dataPtr);
|
|
|
|
auto nodeType = (BfType*)DecodeLocalDataPtr(dataPtr);
|
|
BfTypedValue curNode = BfTypedValue(
|
|
ceModule->mBfIRBuilder->CreateIntToPtr(DecodeTargetDataPtr(dataPtr), ceModule->mBfIRBuilder->MapType(nodeType)),
|
|
nodeType);
|
|
|
|
String newContinuationData;
|
|
|
|
if (count < 0)
|
|
count = 3;
|
|
|
|
String retVal = GetArrayItems(debugVis, valueType, curNode, count, &newContinuationData);
|
|
retVal += "\n" + newContinuationData;
|
|
return retVal;
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_Dictionary)
|
|
{
|
|
auto dictValueType = (BfType*)DecodeLocalDataPtr(dataPtr);
|
|
BfTypedValue dictValue = BfTypedValue(
|
|
ceModule->mBfIRBuilder->CreateIntToPtr(DecodeTargetDataPtr(dataPtr), ceModule->mBfIRBuilder->MapType(dictValueType)),
|
|
dictValueType);
|
|
|
|
int bucketIdx = (int)DecodeTargetDataPtr(dataPtr);
|
|
int nodeIdx = (int)DecodeTargetDataPtr(dataPtr);
|
|
|
|
String newContinuationData;
|
|
String retVal = GetDictionaryItems(debugVis, dictValue, bucketIdx, nodeIdx, count, &newContinuationData);
|
|
retVal += "\n" + newContinuationData;
|
|
return retVal;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
CeTypedValue CeDebugger::GetAddr(BfConstant* constant, BfType* type)
|
|
{
|
|
auto module = mCeMachine->mCeModule;
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
|
|
if (constant->mConstType == BfConstType_GlobalVar)
|
|
{
|
|
auto globalVar = (BfGlobalVar*)constant;
|
|
|
|
String varName(globalVar->mName);
|
|
|
|
if (varName.StartsWith("__bfStrObj"))
|
|
{
|
|
int stringId = atoi(varName.c_str() + 10);
|
|
auto addr = ceContext->GetString(stringId);
|
|
return CeTypedValue(addr, globalVar->mType);
|
|
}
|
|
else if (varName.StartsWith("__bfStrData"))
|
|
{
|
|
auto stringType = module->ResolveTypeDef(module->mCompiler->mStringTypeDef)->ToTypeInstance();
|
|
int stringId = atoi(varName.c_str() + 11);
|
|
auto addr = ceContext->GetString(stringId) + stringType->mInstSize;
|
|
return CeTypedValue(addr, globalVar->mType);
|
|
}
|
|
|
|
CeStaticFieldInfo* fieldInfo = NULL;
|
|
if (ceContext->mStaticFieldMap.TryAdd(globalVar->mName, NULL, &fieldInfo))
|
|
{
|
|
auto dbgTypeInfo = GetDbgTypeInfo(globalVar->mType);
|
|
if (dbgTypeInfo != NULL)
|
|
{
|
|
uint8* ptr = ceContext->CeMalloc(dbgTypeInfo->mType->mSize);
|
|
if (dbgTypeInfo->mType->mSize > 0)
|
|
memset(ptr, 0, dbgTypeInfo->mType->mSize);
|
|
fieldInfo->mAddr = (addr_ce)(ptr - ceContext->mMemory.mVals);
|
|
}
|
|
else
|
|
fieldInfo->mAddr = 0;
|
|
}
|
|
return CeTypedValue(fieldInfo->mAddr, globalVar->mType);
|
|
}
|
|
else if (constant->mTypeCode == BfTypeCode_StringId)
|
|
{
|
|
auto stringType = module->ResolveTypeDef(module->mCompiler->mStringTypeDef)->ToTypeInstance();
|
|
if ((type != NULL) && (type->IsPointer()))
|
|
{
|
|
BfType* charType = module->GetPrimitiveType(BfTypeCode_Char8);
|
|
BfType* charPtrType = module->CreatePointerType(charType);
|
|
auto addr = ceContext->GetString(constant->mInt32) + stringType->mInstSize;
|
|
return CeTypedValue(addr, module->mBfIRBuilder->MapType(charPtrType));
|
|
}
|
|
else
|
|
{
|
|
auto addr = ceContext->GetString(constant->mInt32);
|
|
return CeTypedValue(addr, module->mBfIRBuilder->MapType(stringType));
|
|
}
|
|
}
|
|
else if (constant->mConstType == BfConstType_AggCE)
|
|
{
|
|
auto aggCE = (BfConstantAggCE*)constant;
|
|
return CeTypedValue(aggCE->mCEAddr, aggCE->mType);
|
|
}
|
|
else if (constant->mConstType == BfConstType_BitCast)
|
|
{
|
|
auto constBitCast = (BfConstantBitCast*)constant;
|
|
auto val = GetAddr(module->mBfIRBuilder->GetConstantById(constBitCast->mTarget));
|
|
if (!val)
|
|
return val;
|
|
return CeTypedValue(val.mAddr, constBitCast->mToType);
|
|
}
|
|
else if (constant->mConstType == BfConstType_IntToPtr)
|
|
{
|
|
auto fromPtrToInt = (BfConstantIntToPtr*)constant;
|
|
auto val = GetAddr(module->mBfIRBuilder->GetConstantById(fromPtrToInt->mTarget));
|
|
if (!val)
|
|
return val;
|
|
return CeTypedValue(val.mAddr, fromPtrToInt->mToType);
|
|
}
|
|
else if (constant->mConstType == BfConstType_GEP32_1)
|
|
{
|
|
auto gepConst = (BfConstantGEP32_1*)constant;
|
|
|
|
auto constant = module->mBfIRBuilder->GetConstantById(gepConst->mTarget);
|
|
auto typedVal = GetAddr(constant);
|
|
if (!typedVal)
|
|
return CeTypedValue();
|
|
|
|
auto dbgTypeInfo = GetDbgTypeInfo(typedVal.mType);
|
|
if (dbgTypeInfo == NULL)
|
|
return CeTypedValue();
|
|
|
|
auto addr = typedVal.mAddr;
|
|
|
|
if (gepConst->mIdx0 != 0)
|
|
addr += gepConst->mIdx0 * dbgTypeInfo->mType->GetUnderlyingType()->GetStride();
|
|
|
|
return CeTypedValue(addr, module->mBfIRBuilder->MapType(dbgTypeInfo->mType));
|
|
}
|
|
else if (constant->mConstType == BfConstType_GEP32_2)
|
|
{
|
|
auto gepConst = (BfConstantGEP32_2*)constant;
|
|
|
|
auto constant = module->mBfIRBuilder->GetConstantById(gepConst->mTarget);
|
|
auto typedVal = GetAddr(constant);
|
|
if (!typedVal.mAddr)
|
|
return CeTypedValue();
|
|
|
|
auto dbgTypeInfo = GetDbgTypeInfo(typedVal.mType);
|
|
|
|
if ((dbgTypeInfo != NULL) &&
|
|
((dbgTypeInfo->mType->IsPointer()) || ((dbgTypeInfo->mType->IsRef()))))
|
|
dbgTypeInfo = GetDbgTypeInfo(dbgTypeInfo->mType->GetUnderlyingType()->mTypeId);
|
|
|
|
if (dbgTypeInfo == NULL)
|
|
return CeTypedValue();
|
|
|
|
auto addr = typedVal.mAddr;
|
|
|
|
if (gepConst->mIdx0 != 0)
|
|
addr += gepConst->mIdx0 * dbgTypeInfo->mType->GetStride();
|
|
if (gepConst->mIdx1 != 0)
|
|
addr += dbgTypeInfo->mFieldOffsets[gepConst->mIdx1].mDataOffset;
|
|
BfType* ptrType = NULL;
|
|
if (gepConst->mIdx1 == 0)
|
|
{
|
|
auto typeInst = dbgTypeInfo->mType->ToTypeInstance();
|
|
if ((typeInst != NULL) && (typeInst->mBaseType != NULL))
|
|
{
|
|
ptrType = typeInst->mBaseType;
|
|
if (!ptrType->IsValueType())
|
|
ptrType = module->CreatePointerType(ptrType);
|
|
}
|
|
}
|
|
|
|
if (ptrType == NULL)
|
|
{
|
|
if (gepConst->mIdx1 > dbgTypeInfo->mFieldOffsets.mSize)
|
|
return CeTypedValue();
|
|
ptrType = module->CreatePointerType(dbgTypeInfo->mFieldOffsets[gepConst->mIdx1].mType);
|
|
}
|
|
return CeTypedValue(addr, module->mBfIRBuilder->MapType(ptrType));
|
|
}
|
|
else if ((constant->mTypeCode == BfTypeCode_Int32) ||
|
|
(constant->mTypeCode == BfTypeCode_Int64) ||
|
|
(constant->mTypeCode == BfTypeCode_IntPtr))
|
|
{
|
|
return CeTypedValue(constant->mInt64, module->mBfIRBuilder->GetPrimitiveType(constant->mTypeCode));
|
|
}
|
|
|
|
return CeTypedValue();
|
|
}
|
|
|
|
CeTypedValue CeDebugger::GetAddr(const BfTypedValue typedVal)
|
|
{
|
|
auto constant = mCeMachine->mCeModule->mBfIRBuilder->GetConstant(typedVal.mValue);
|
|
if (constant == NULL)
|
|
return CeTypedValue();
|
|
return GetAddr(constant, typedVal.mType);
|
|
}
|
|
|
|
#define GET_FROM(ptr, T) *((T*)(ptr += sizeof(T)) - 1)
|
|
|
|
String CeDebugger::ReadString(BfTypeCode charType, intptr addr, intptr maxLength, CeFormatInfo& formatInfo)
|
|
{
|
|
int origMaxLength = (int)maxLength;
|
|
if (addr == 0)
|
|
return "";
|
|
|
|
BP_ZONE("WinDebugger::ReadString");
|
|
|
|
String retVal = "\"";
|
|
bool wasTerminated = false;
|
|
String valString;
|
|
intptr maxShowSize = 255;
|
|
|
|
if (maxLength == -1)
|
|
maxLength = formatInfo.mOverrideCount;
|
|
else if (formatInfo.mOverrideCount != -1)
|
|
maxLength = BF_MIN(formatInfo.mOverrideCount, maxLength);
|
|
if (formatInfo.mMaxCount != -1)
|
|
maxLength = BF_MIN(formatInfo.mMaxCount, maxLength);
|
|
|
|
if (maxLength == -1)
|
|
maxLength = 8 * 1024 * 1024; // Is 8MB crazy?
|
|
if (!formatInfo.mRawString)
|
|
maxLength = BF_MIN(maxLength, maxShowSize);
|
|
|
|
//EnableMemCache();
|
|
bool readFailed = false;
|
|
intptr strPtr = addr;
|
|
|
|
int charLen = 1;
|
|
if (charType == BfTypeCode_Char16)
|
|
charLen = 2;
|
|
else if (charType == BfTypeCode_Char32)
|
|
charLen = 4;
|
|
|
|
bool isUTF8 = formatInfo.mDisplayType == DwDisplayType_Utf8;
|
|
|
|
int readSize = BF_MIN(1024, (int)maxLength * charLen);
|
|
uint8 buf[1024];
|
|
uint8* bufPtr = NULL;
|
|
uint8* bufEnd = NULL;
|
|
bool hasHighAscii = false;
|
|
|
|
int i;
|
|
for (i = 0; i < maxLength; i++)
|
|
{
|
|
if (bufPtr >= bufEnd)
|
|
{
|
|
while (true)
|
|
{
|
|
if (readSize < charLen)
|
|
{
|
|
readFailed = true;
|
|
break;
|
|
}
|
|
|
|
if (ReadMemory(strPtr, readSize, buf))
|
|
break;
|
|
|
|
readSize /= 2;
|
|
}
|
|
if (readFailed)
|
|
break;
|
|
|
|
bufPtr = buf;
|
|
bufEnd = buf + readSize;
|
|
}
|
|
|
|
switch (charLen)
|
|
{
|
|
case 1:
|
|
{
|
|
char c = GET_FROM(bufPtr, char);
|
|
if ((c != 0) || (formatInfo.mOverrideCount != -1))
|
|
{
|
|
if ((uint8)c >= 0x80)
|
|
hasHighAscii = true;
|
|
valString.Append(c);
|
|
}
|
|
else
|
|
wasTerminated = true;
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
uint16 c16 = GET_FROM(bufPtr, uint16);
|
|
if ((c16 != 0) || (formatInfo.mOverrideCount != -1))
|
|
{
|
|
char str[8];
|
|
u8_toutf8(str, 8, c16);
|
|
valString += str;
|
|
}
|
|
else
|
|
wasTerminated = true;
|
|
}
|
|
break;
|
|
case 4:
|
|
{
|
|
uint32 c32 = GET_FROM(bufPtr, uint32);
|
|
if ((c32 != 0) || (formatInfo.mOverrideCount != -1))
|
|
{
|
|
char str[8];
|
|
u8_toutf8(str, 8, c32);
|
|
valString += str;
|
|
}
|
|
else
|
|
wasTerminated = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ((wasTerminated) && (formatInfo.mOverrideCount != -1))
|
|
{
|
|
valString += '\x00';
|
|
wasTerminated = false;
|
|
}
|
|
|
|
if ((wasTerminated) || (readFailed))
|
|
{
|
|
break;
|
|
}
|
|
strPtr += charLen;
|
|
}
|
|
//DisableMemCache();
|
|
|
|
if (formatInfo.mOverrideCount != -1)
|
|
{
|
|
if (i == formatInfo.mOverrideCount)
|
|
wasTerminated = true;
|
|
}
|
|
|
|
if (strPtr == addr + origMaxLength)
|
|
wasTerminated = true;
|
|
|
|
if (valString.length() == formatInfo.mOverrideCount)
|
|
wasTerminated = true;
|
|
|
|
// if (formatInfo.mDisplayType == DwDisplayType_Ascii)
|
|
// {
|
|
// // Our encoding for retVal is already assumed to be UTF8, so the special case here actually Ascii
|
|
// valString = UTF8Encode(ToWString(valString));
|
|
// }
|
|
|
|
if (formatInfo.mRawString)
|
|
{
|
|
if ((formatInfo.mDisplayType == DwDisplayType_Utf8) || (!hasHighAscii))
|
|
return valString;
|
|
|
|
String utf8Str;
|
|
for (int i = 0; i < (int)valString.length(); i++)
|
|
{
|
|
char c = valString[i];
|
|
if ((uint8)c >= 0x80)
|
|
{
|
|
utf8Str += (char)(0xC0 | (((uint8)c & 0xFF) >> 6));
|
|
utf8Str += (char)(0x80 | ((uint8)c & 0x3F));
|
|
}
|
|
else
|
|
utf8Str += c;
|
|
}
|
|
return utf8Str;
|
|
}
|
|
|
|
if ((readFailed) && (valString.IsEmpty()))
|
|
return "< Failed to read string >";
|
|
|
|
retVal += SlashString(valString, true, true, true);
|
|
|
|
// We could go over 'maxShowSize' if we have a lot of slashed chars. An uninitialized string can be filled with '\xcc' chars
|
|
if ((!formatInfo.mRawString) && ((int)retVal.length() > maxShowSize))
|
|
{
|
|
retVal = retVal.Substring(0, maxShowSize);
|
|
wasTerminated = false;
|
|
}
|
|
|
|
if (wasTerminated)
|
|
retVal += "\"";
|
|
else
|
|
retVal += "...";
|
|
|
|
return retVal;
|
|
}
|
|
|
|
void CeDebugger::ProcessEvalString(BfTypedValue useTypedValue, String& evalStr, String& displayString, CeFormatInfo& formatInfo, DebugVisualizerEntry* debugVis, bool limitLength)
|
|
{
|
|
for (int i = 0; i < (int)evalStr.length(); i++)
|
|
{
|
|
char c = evalStr[i];
|
|
char nextC = 0;
|
|
if (i < (int)evalStr.length() - 1)
|
|
nextC = evalStr[i + 1];
|
|
if ((c == '{') && (nextC != '{'))
|
|
{
|
|
// Evaluate
|
|
|
|
int endIdx = i;
|
|
for (; endIdx < (int)evalStr.length(); endIdx++)
|
|
{
|
|
//TODO: Do better parsing - this paren could be inside a string, for example
|
|
if (evalStr[endIdx] == '}')
|
|
break;
|
|
}
|
|
|
|
CeFormatInfo displayStrFormatInfo = formatInfo;
|
|
displayStrFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + (int)displayString.length();
|
|
displayStrFormatInfo.mHidePointers = false;
|
|
|
|
if ((limitLength) && (displayStrFormatInfo.mTotalSummaryLength > 255))
|
|
{
|
|
displayString += "...";
|
|
}
|
|
else
|
|
{
|
|
String evalString = evalStr.Substring(i + 1, endIdx - i - 1);
|
|
String errors;
|
|
BfTypedValue evalResult = EvaluateInContext(useTypedValue, evalString, &displayStrFormatInfo, NULL, &errors);
|
|
if (evalResult)
|
|
{
|
|
if (displayStrFormatInfo.mNoEdit)
|
|
formatInfo.mNoEdit = true;
|
|
|
|
String result = TypedValueToString(evalResult, evalString, displayStrFormatInfo, NULL);
|
|
|
|
if ((formatInfo.mRawString) && (limitLength))
|
|
{
|
|
displayString = result;
|
|
return;
|
|
}
|
|
|
|
int crPos = (int)result.IndexOf('\n');
|
|
if (crPos != -1)
|
|
displayString += result.Substring(0, crPos);
|
|
else
|
|
displayString += result;
|
|
}
|
|
else if (debugVis != NULL)
|
|
{
|
|
displayString += "<DbgVis Failed>";
|
|
DbgVisFailed(debugVis, evalString, errors);
|
|
}
|
|
else
|
|
{
|
|
displayString += "<Eval Failed>";
|
|
}
|
|
}
|
|
|
|
i = endIdx;
|
|
continue;
|
|
}
|
|
else if ((c == '{') && (nextC == '{'))
|
|
{
|
|
// Skip next paren
|
|
i++;
|
|
}
|
|
else if ((c == '}') && (nextC == '}'))
|
|
{
|
|
// Skip next paren
|
|
i++;
|
|
}
|
|
|
|
displayString += c;
|
|
}
|
|
}
|
|
|
|
String CeDebugger::TypedValueToString(const BfTypedValue& origTypedValue, const StringImpl& expr, CeFormatInfo& formatInfo, bool fullPrecision, CeTypeModKind typeModKind)
|
|
{
|
|
BfTypedValue typedValue = origTypedValue;
|
|
|
|
auto module = mCeMachine->mCeModule;
|
|
|
|
String retVal;
|
|
if (typedValue.IsNoValueType())
|
|
{
|
|
String typeName = TypeToString(typedValue);
|
|
|
|
retVal += typeName;
|
|
retVal += "\n";
|
|
retVal += typeName;
|
|
retVal += "\n";
|
|
retVal += GetMemberList(typedValue.mType, 0, 0, true);
|
|
return retVal;
|
|
}
|
|
|
|
auto constant = module->mBfIRBuilder->GetConstant(typedValue.mValue);
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
if (constant == NULL)
|
|
{
|
|
return "!Invalid expression";
|
|
}
|
|
|
|
bool didAlloc = false;
|
|
addr_ce addr = 0;
|
|
|
|
defer(
|
|
{
|
|
if (didAlloc)
|
|
mCurDbgState->mCeContext->CeFree(addr);
|
|
}
|
|
);
|
|
|
|
if (constant->mConstType == BfConstType_AggCE)
|
|
{
|
|
auto aggCE = (BfConstantAggCE*)constant;
|
|
addr = aggCE->mCEAddr;
|
|
}
|
|
else if ((typedValue.IsAddr()) || (typedValue.mType->IsObjectOrInterface()) || (typedValue.mType->IsPointer()))
|
|
{
|
|
CeTypedValue typedVal = GetAddr(constant, typedValue.mType);
|
|
addr = (addr_ce)typedVal.mAddr;
|
|
if (!typedVal)
|
|
{
|
|
return "!Invalid addr type";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int allocSize = typedValue.mType->mSize;
|
|
auto typeInst = typedValue.mType->ToTypeInstance();
|
|
if (typeInst != NULL)
|
|
allocSize = typeInst->mInstSize;
|
|
if (allocSize < 0)
|
|
return "!Invalid size";
|
|
|
|
addr = (addr_ce)(mCurDbgState->mCeContext->CeMalloc(allocSize) - mCurDbgState->mCeContext->mMemory.mVals);
|
|
didAlloc = true;
|
|
|
|
if (!mCurDbgState->mCeContext->WriteConstant(mCeMachine->mCeModule, addr, constant, typedValue.mType))
|
|
{
|
|
return StrFormat("!Failed to encode value");
|
|
}
|
|
}
|
|
|
|
DwDisplayInfo* displayInfo = GetDisplayInfo(formatInfo.mReferenceId);
|
|
|
|
char str[32];
|
|
String result;
|
|
auto memStart = mCurDbgState->mCeContext->mMemory.mVals;
|
|
|
|
int checkMemSize = typedValue.mType->mSize;
|
|
if (typedValue.mType->IsPointer())
|
|
checkMemSize = typedValue.mType->GetUnderlyingType()->mSize;
|
|
|
|
uint8* data = ceContext->GetMemoryPtr(addr, checkMemSize);
|
|
if ((addr != 0) && (data == NULL))
|
|
{
|
|
if (typedValue.mType->IsPointer())
|
|
return EncodeDataPtr(addr, true) + "\n" + TypeToString(origTypedValue.mType, typeModKind);
|
|
else
|
|
return "!Invalid address";
|
|
}
|
|
|
|
addr_ce dataAddr = addr;
|
|
if ((typedValue.IsAddr()) && (typedValue.mType->IsObjectOrInterface()))
|
|
dataAddr = *(addr_ce*)data;
|
|
|
|
if (formatInfo.mRawString)
|
|
{
|
|
//if ((dwValueType->mTypeCode != DbgType_Struct) && (dwValueType->mTypeCode != DbgType_Class) && (dwValueType->mTypeCode != DbgType_Ptr) && (dwValueType->mTypeCode != DbgType_SizedArray))
|
|
if ((!typedValue.mType->IsPointer()) && (!typedValue.mType->IsObjectOrStruct()) && (!typedValue.mType->IsStruct()) && (!typedValue.mType->IsSizedArray()))
|
|
return "";
|
|
}
|
|
|
|
auto _ShowArraySummary = [&](String& retVal, addr_ce ptrVal, int64 arraySize, BfType* innerType)
|
|
{
|
|
auto ptrType = module->CreatePointerType(innerType);
|
|
|
|
String displayString;
|
|
displayString += "{";
|
|
for (int idx = 0; idx < arraySize; idx++)
|
|
{
|
|
if (formatInfo.mTotalSummaryLength + retVal.length() + displayString.length() > 255)
|
|
{
|
|
displayString += "...";
|
|
break;
|
|
}
|
|
|
|
if ((idx != 0) && (!displayString.EndsWith('{')))
|
|
displayString += ", ";
|
|
|
|
CeFormatInfo displayStrFormatInfo = formatInfo;
|
|
displayStrFormatInfo.mExpandItemDepth = 1;
|
|
displayStrFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + (int)retVal.length() + (int)displayString.length();
|
|
displayStrFormatInfo.mHidePointers = false;
|
|
displayStrFormatInfo.mArrayLength = -1;
|
|
|
|
// Why did we have this "na" on here? It made "void*[3]" type things show up as "{,,}"
|
|
//String evalStr = "((" + innerType->ToStringRaw(language) + "*)" + EncodeDataPtr(ptrVal, true) + StrFormat(")[%d], na", idx);
|
|
|
|
String evalStr = StrFormat("((comptype(%d))(void*)", ptrType->mTypeId) + EncodeDataPtr(ptrVal, true) + StrFormat(")[%lld]", idx);
|
|
BfTypedValue evalResult = EvaluateInContext(typedValue, evalStr, &displayStrFormatInfo);
|
|
String result;
|
|
if (evalResult)
|
|
{
|
|
result = TypedValueToString(evalResult, evalStr, displayStrFormatInfo, NULL);
|
|
int crPos = (int)result.IndexOf('\n');
|
|
if (crPos != -1)
|
|
result.RemoveToEnd(crPos);
|
|
}
|
|
else
|
|
result = "???";
|
|
|
|
displayString += result;
|
|
}
|
|
displayString += "}";
|
|
retVal += displayString;
|
|
};
|
|
|
|
if (formatInfo.mArrayLength != -1)
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
|
|
if (typedValue.mType->IsPointer())
|
|
{
|
|
auto elementType = typedValue.mType->GetUnderlyingType();
|
|
|
|
String retVal;
|
|
addr_ce ptrVal = addr;
|
|
if (!formatInfo.mHidePointers)
|
|
{
|
|
retVal = EncodeDataPtr(ptrVal, true) + " ";
|
|
retVal += module->TypeToString(elementType);
|
|
retVal += StrFormat("[%lld] ", (int64)formatInfo.mArrayLength);
|
|
}
|
|
|
|
_ShowArraySummary(retVal, ptrVal, formatInfo.mArrayLength, elementType);
|
|
|
|
String idxStr = "[{0}]";
|
|
|
|
retVal += "\n" + TypeToString(typedValue);
|
|
|
|
String evalStr = StrFormat("((comptype(%d))(void*)", typedValue.mType->mTypeId) + EncodeDataPtr(ptrVal, true) + ")[{0}]";
|
|
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, (int)BF_MAX(formatInfo.mArrayLength, 0), 10000) +
|
|
"\t" + idxStr + "\t" + evalStr;
|
|
return retVal;
|
|
}
|
|
else
|
|
{
|
|
CeFormatInfo newFormatInfo = formatInfo;
|
|
newFormatInfo.mArrayLength = -1;
|
|
|
|
String retVal = TypedValueToString(typedValue, expr, newFormatInfo);
|
|
|
|
int crPos = (int)retVal.IndexOf('\n');
|
|
if (crPos != -1)
|
|
retVal = "!Array length flag not valid with this type" + retVal.Substring(crPos);
|
|
return retVal;
|
|
}
|
|
}
|
|
|
|
if (typedValue.mType->IsRef())
|
|
{
|
|
if (dataAddr == 0)
|
|
return "<null>\n" + TypeToString(typedValue);
|
|
|
|
addr = *(addr_ce*)data;
|
|
dataAddr = addr;
|
|
|
|
data = ceContext->GetMemoryPtr(addr, checkMemSize);
|
|
if ((addr != 0) && (data == NULL))
|
|
return "!Invalid address";
|
|
|
|
typedValue = BfTypedValue(typedValue.mValue, typedValue.mType->GetUnderlyingType(), true);
|
|
}
|
|
|
|
if (typedValue.mType->IsPointer())
|
|
{
|
|
//ceContext->
|
|
|
|
addr_ce ptrVal = addr;
|
|
|
|
String retVal;
|
|
BfType* innerType = typedValue.mType->GetUnderlyingType();
|
|
if (innerType == NULL)
|
|
return EncodeDataPtr((uint32)ptrVal, true) + "\nvoid*";
|
|
|
|
bool isChar = false;
|
|
|
|
if (innerType->IsChar())
|
|
isChar = true;
|
|
|
|
if ((isChar) && (formatInfo.mArrayLength == -1))
|
|
{
|
|
auto primType = (BfPrimitiveType*)innerType;
|
|
|
|
if (!formatInfo.mHidePointers)
|
|
retVal = EncodeDataPtr(ptrVal, true);
|
|
|
|
int strLen = (int)formatInfo.mOverrideCount;
|
|
// if (typedValue.mIsLiteral)
|
|
// {
|
|
// if (strLen == -1)
|
|
// strLen = 0x7FFFFFFF;
|
|
// if (typedValue.mDataLen > 0)
|
|
// strLen = BF_MIN(strLen, typedValue.mDataLen);
|
|
// else
|
|
// strLen = BF_MIN(strLen, strlen(typedValue.mCharPtr));
|
|
// }
|
|
|
|
SetAndRestoreValue<intptr> prevOverrideLen(formatInfo.mOverrideCount, strLen);
|
|
String strResult = ReadString(primType->mTypeDef->mTypeCode, ptrVal, strLen, formatInfo);
|
|
if (formatInfo.mRawString)
|
|
return strResult;
|
|
if (!strResult.IsEmpty())
|
|
{
|
|
if (!retVal.IsEmpty())
|
|
retVal += " ";
|
|
retVal += strResult;
|
|
}
|
|
retVal += "\n" + TypeToString(origTypedValue.mType, typeModKind);
|
|
return retVal;
|
|
}
|
|
else if ((ptrVal != 0) && (innerType->IsComposite()))
|
|
{
|
|
// addr = *(addr_ce*)data;
|
|
// dataAddr = addr;
|
|
//
|
|
// data = ceContext->GetMemoryPtr(addr, checkMemSize);
|
|
// if ((addr != 0) && (data == NULL))
|
|
// return "!Invalid address";
|
|
|
|
typedValue = BfTypedValue(typedValue.mValue, typedValue.mType->GetUnderlyingType(), true);
|
|
}
|
|
else
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
|
|
String retVal;
|
|
if (!formatInfo.mHidePointers)
|
|
retVal = EncodeDataPtr((uint32)ptrVal, true);
|
|
|
|
if (ptrVal != 0)
|
|
{
|
|
BfTypedValue innerTypedVal = BfTypedValue(module->mBfIRBuilder->CreateConstAggCE(module->mBfIRBuilder->MapType(innerType), (addr_ce)ptrVal), innerType, true);
|
|
|
|
innerTypedVal.mType = innerType;
|
|
|
|
if (innerTypedVal)
|
|
{
|
|
CeFormatInfo defaultFormatInfo;
|
|
defaultFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + 2; // Take into accout the necessary {}'s
|
|
defaultFormatInfo.mExpandItemDepth++;
|
|
defaultFormatInfo.mCallStackIdx = formatInfo.mCallStackIdx;
|
|
String innerStr = TypedValueToString(innerTypedVal, "", defaultFormatInfo);
|
|
int crIdx = (int)innerStr.IndexOf('\n');
|
|
if (crIdx != -1)
|
|
{
|
|
String innerDataStr = innerStr.Substring(0, crIdx);
|
|
if (!innerDataStr.empty())
|
|
{
|
|
if (!retVal.empty())
|
|
retVal += " ";
|
|
retVal += "{" + innerDataStr + "}";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
retVal += " { ??? }";
|
|
}
|
|
}
|
|
}
|
|
|
|
retVal += "\n" + TypeToString(origTypedValue.mType, typeModKind);
|
|
module->PopulateType(innerType);
|
|
|
|
if ((ptrVal != 0) && (!innerType->IsValuelessType()))
|
|
{
|
|
//String ptrDataStr = StrFormat("(%s)", dwValueType->ToStringRaw(language).c_str()) + EncodeDataPtr(typedValue.mPtr, true);
|
|
retVal += "\n*\t";
|
|
// Why did we have this? It messed up a pointer to sized array
|
|
/*if (language == DbgLanguage_Beef)
|
|
retVal += "this";
|
|
else*/
|
|
retVal += "this";
|
|
|
|
if (!formatInfo.mReferenceId.empty())
|
|
retVal += ", refid=" + MaybeQuoteFormatInfoParam(formatInfo.mReferenceId);
|
|
|
|
retVal += StrFormat(", this=%d@0x%X", innerType->mTypeId, ptrVal);
|
|
}
|
|
|
|
retVal += "\n:canEdit\n:editVal\t" + EncodeDataPtr((uint32)addr, true);
|
|
|
|
return retVal;
|
|
}
|
|
}
|
|
|
|
if (typedValue.mType->IsPrimitiveType())
|
|
{
|
|
auto primType = (BfPrimitiveType*)typedValue.mType;
|
|
|
|
BfTypeCode typeCode = primType->mTypeDef->mTypeCode;
|
|
if (typeCode == BfTypeCode_IntPtr)
|
|
typeCode = (primType->mSize == 8) ? BfTypeCode_Int64 : BfTypeCode_Int32;
|
|
if (typeCode == BfTypeCode_UIntPtr)
|
|
typeCode = (primType->mSize == 8) ? BfTypeCode_UInt64 : BfTypeCode_UInt32;
|
|
|
|
switch (typeCode)
|
|
{
|
|
case BfTypeCode_None:
|
|
return "\nvoid";
|
|
case BfTypeCode_Boolean:
|
|
{
|
|
auto val = *(uint8*)(data);
|
|
if (val == 0)
|
|
return "false\n" + TypeToString(origTypedValue.mType, typeModKind);
|
|
else if (val == 1)
|
|
return "true\n" + TypeToString(origTypedValue.mType, typeModKind);
|
|
else
|
|
return StrFormat("true (%d)\n%s", val, TypeToString(origTypedValue.mType, typeModKind).c_str());
|
|
}
|
|
break;
|
|
case BfTypeCode_Char8:
|
|
{
|
|
auto val = *(uint8*)(data);
|
|
if (val != 0)
|
|
{
|
|
char str[2] = { (char)val };
|
|
result = SlashString(str, formatInfo.mDisplayType == DwDisplayType_Utf8, true);
|
|
|
|
if (!IsNormalChar(val))
|
|
result = StrFormat("'%s' (0x%02X)\n", result.c_str(), val);
|
|
else
|
|
result = StrFormat("'%s'\n", result.c_str());
|
|
}
|
|
else
|
|
result = "'\\0'\n";
|
|
return result + TypeToString(origTypedValue.mType, typeModKind);
|
|
}
|
|
break;
|
|
case BfTypeCode_Char16:
|
|
{
|
|
auto val = *(uint16*)(data);
|
|
if (val != 0)
|
|
{
|
|
u8_toutf8(str, 8, val);
|
|
result = SlashString(str, true, true);
|
|
if (!IsNormalChar(val))
|
|
result = StrFormat("'%s' (0x%02X)\n", result.c_str(), val);
|
|
else
|
|
result = StrFormat("'%s'\n", result.c_str());
|
|
}
|
|
else
|
|
result = "'\\0'\n";
|
|
return result + TypeToString(origTypedValue.mType, typeModKind);
|
|
}
|
|
break;
|
|
case BfTypeCode_Char32:
|
|
{
|
|
auto val = *(uint32*)(data);
|
|
if (val != 0)
|
|
{
|
|
u8_toutf8(str, 8, val);
|
|
result = SlashString(str, true, true);
|
|
if (!IsNormalChar(val))
|
|
result = StrFormat("'%s' (0x%02X)\n", result.c_str(), val);
|
|
else
|
|
result = StrFormat("'%s'\n", result.c_str());
|
|
}
|
|
else
|
|
result = "'\\0'\n";
|
|
return result + TypeToString(origTypedValue.mType, typeModKind);
|
|
}
|
|
break;
|
|
case BfTypeCode_Int8:
|
|
return IntTypeToString<int8>(*(int8*)(data), TypeToString(origTypedValue.mType, typeModKind), displayInfo, formatInfo);
|
|
case BfTypeCode_UInt8:
|
|
return IntTypeToString<uint8>(*(uint8*)(data), TypeToString(origTypedValue.mType, typeModKind), displayInfo, formatInfo);
|
|
case BfTypeCode_Int16:
|
|
return IntTypeToString<int16>(*(int16*)(data), TypeToString(origTypedValue.mType, typeModKind), displayInfo, formatInfo);
|
|
case BfTypeCode_UInt16:
|
|
return IntTypeToString<uint16>(*(uint16*)(data), TypeToString(origTypedValue.mType, typeModKind), displayInfo, formatInfo);
|
|
case BfTypeCode_Int32:
|
|
return IntTypeToString<int32>(*(int32*)(data), TypeToString(origTypedValue.mType, typeModKind), displayInfo, formatInfo);
|
|
case BfTypeCode_UInt32:
|
|
return IntTypeToString<uint32>(*(uint32*)(data), TypeToString(origTypedValue.mType, typeModKind), displayInfo, formatInfo);
|
|
case BfTypeCode_Int64:
|
|
return IntTypeToString<int64>(*(int64*)(data), TypeToString(origTypedValue.mType, typeModKind), displayInfo, formatInfo);
|
|
case BfTypeCode_UInt64:
|
|
return IntTypeToString<uint64>(*(uint64*)(data), TypeToString(origTypedValue.mType, typeModKind), displayInfo, formatInfo);
|
|
case BfTypeCode_Float:
|
|
{
|
|
DwFloatDisplayType floatDisplayType = displayInfo->mFloatDisplayType;
|
|
if (floatDisplayType == DwFloatDisplayType_Default)
|
|
floatDisplayType = DwFloatDisplayType_Minimal;
|
|
if (floatDisplayType == DwFloatDisplayType_Minimal)
|
|
ExactMinimalFloatToStr(*(float*)data, str);
|
|
else if (floatDisplayType == DwFloatDisplayType_Full)
|
|
sprintf(str, "%1.9g", *(float*)data);
|
|
else if (floatDisplayType == DwFloatDisplayType_HexUpper)
|
|
sprintf(str, "0x%04X", *(uint32*)data);
|
|
else //if (floatDisplayType == DwFloatDisplayType_HexLower)
|
|
sprintf(str, "0x%04x", *(uint32*)data);
|
|
return StrFormat("%s\n%s", str, TypeToString(origTypedValue.mType, typeModKind).c_str());
|
|
}
|
|
case BfTypeCode_Double:
|
|
{
|
|
DwFloatDisplayType floatDisplayType = displayInfo->mFloatDisplayType;
|
|
if (floatDisplayType == DwFloatDisplayType_Default)
|
|
floatDisplayType = DwFloatDisplayType_Minimal;
|
|
if (floatDisplayType == DwFloatDisplayType_Minimal)
|
|
ExactMinimalDoubleToStr(*(double*)data, str);
|
|
else if (floatDisplayType == DwFloatDisplayType_Full)
|
|
sprintf(str, "%1.17g", *(double*)data);
|
|
else if (floatDisplayType == DwFloatDisplayType_HexUpper)
|
|
sprintf(str, "0x%08llX", *(uint64*)data);
|
|
else //if (floatDisplayType == DwFloatDisplayType_HexLower)
|
|
sprintf(str, "0x%08llx", *(uint64*)data);
|
|
return StrFormat("%s\n%s", str, TypeToString(origTypedValue.mType, typeModKind).c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (typedValue.mType->IsSizedArray())
|
|
{
|
|
auto arrayType = (BfSizedArrayType*)typedValue.mType;
|
|
auto innerType = arrayType->mElementType;
|
|
|
|
String retVal;
|
|
addr_ce ptrVal = addr;
|
|
|
|
intptr arraySize = arrayType->mElementCount;
|
|
intptr innerSize = innerType->GetStride();
|
|
|
|
String idxStr = "[{0}]";
|
|
|
|
if (innerType->IsChar())
|
|
{
|
|
auto primType = (BfPrimitiveType*)innerType;
|
|
String strVal = ReadString(primType->mTypeDef->mTypeCode, ptrVal, arraySize, formatInfo);
|
|
if (formatInfo.mRawString)
|
|
return strVal;
|
|
retVal += strVal;
|
|
}
|
|
else
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
|
|
_ShowArraySummary(retVal, ptrVal, arraySize, innerType);
|
|
}
|
|
|
|
retVal += "\n" + TypeToString(origTypedValue.mType, typeModKind);
|
|
|
|
String referenceId = TypeToString(typedValue.mType);
|
|
String evalStr;
|
|
|
|
// Why did we have the "na"? Do we not want to show addresses for all members?
|
|
|
|
auto ptrType = module->CreatePointerType(innerType);
|
|
|
|
evalStr = StrFormat("((comptype(%d))(void*)", ptrType->mTypeId) + EncodeDataPtr(ptrVal, true) + ")[{0}], refid=" + MaybeQuoteFormatInfoParam(referenceId + ".[]");
|
|
if (typedValue.IsReadOnly())
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, (int)BF_MAX(arraySize, 0), 10000) +
|
|
"\t" + idxStr + "\t" + evalStr;
|
|
return retVal;
|
|
}
|
|
|
|
if (typedValue.mType->IsEnum())
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
|
|
String retVal;
|
|
if (typedValue.mType->IsTypedPrimitive())
|
|
{
|
|
int64 bitsLeft = ValueToInt(typedValue);
|
|
int valueCount = 0;
|
|
|
|
String editVal;
|
|
|
|
auto typeInst = typedValue.mType->ToTypeInstance();
|
|
auto dbgTypeInfo = GetDbgTypeInfo(typedValue.mType->mTypeId);
|
|
|
|
while ((dbgTypeInfo != NULL) && ((bitsLeft != 0) || (valueCount == 0)))
|
|
{
|
|
CeDbgTypeInfo::ConstIntEntry* bestMatch = NULL;
|
|
|
|
for (auto& constIntEntry : dbgTypeInfo->mConstIntEntries)
|
|
{
|
|
if (constIntEntry.mVal == bitsLeft)
|
|
{
|
|
bestMatch = &constIntEntry;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bestMatch == NULL)
|
|
{
|
|
for (auto& constIntEntry : dbgTypeInfo->mConstIntEntries)
|
|
{
|
|
if ((constIntEntry.mVal != 0) &&
|
|
((constIntEntry.mVal & bitsLeft) == constIntEntry.mVal))
|
|
{
|
|
bestMatch = &constIntEntry;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestMatch == NULL)
|
|
break;
|
|
|
|
if (valueCount > 0)
|
|
{
|
|
retVal += " | ";
|
|
}
|
|
|
|
auto bestFieldInstance = &typeInst->mFieldInstances[bestMatch->mFieldIdx];
|
|
|
|
retVal += ".";
|
|
retVal += bestFieldInstance->GetFieldDef()->mName;
|
|
|
|
valueCount++;
|
|
bitsLeft &= ~bestMatch->mVal;
|
|
}
|
|
|
|
if ((valueCount == 0) || (bitsLeft != 0))
|
|
{
|
|
if (valueCount > 0)
|
|
retVal += " | ";
|
|
retVal += StrFormat("%lld", bitsLeft);
|
|
}
|
|
|
|
retVal += "\n" + TypeToString(origTypedValue.mType, typeModKind);
|
|
retVal += "\n:canEdit";
|
|
|
|
return retVal;
|
|
}
|
|
else if (typedValue.mType->mDefineState >= BfTypeDefineState_Defined)
|
|
{
|
|
auto typeInst = typedValue.mType->ToTypeInstance();
|
|
BfType* dscrType = typeInst->GetDiscriminatorType();
|
|
BfType* payloadType = typeInst->GetUnionInnerType();
|
|
|
|
int dscrOffset = BF_ALIGN(payloadType->mSize, dscrType->mAlign);
|
|
|
|
int dscrVal = 0;
|
|
memcpy(&dscrVal, data + dscrOffset, dscrType->mSize);
|
|
|
|
for (auto& fieldInstance : typeInst->mFieldInstances)
|
|
{
|
|
auto fieldDef = fieldInstance.GetFieldDef();
|
|
if (!fieldInstance.mIsEnumPayloadCase)
|
|
continue;
|
|
|
|
int tagId = -fieldInstance.mDataIdx - 1;
|
|
if (dscrVal == tagId)
|
|
{
|
|
auto evalResult = BfTypedValue(module->mBfIRBuilder->CreateConstAggCE(
|
|
module->mBfIRBuilder->MapType(fieldInstance.mResolvedType), (addr_ce)dataAddr), fieldInstance.mResolvedType, true);
|
|
|
|
String innerResult = TypedValueToString(evalResult, "", formatInfo, NULL);
|
|
int crPos = (int)innerResult.IndexOf('\n');
|
|
if (crPos != -1)
|
|
innerResult.RemoveToEnd(crPos);
|
|
|
|
retVal += ".";
|
|
retVal += fieldDef->mName;
|
|
retVal += innerResult;
|
|
|
|
retVal += "\n" + TypeToString(origTypedValue.mType, typeModKind);
|
|
retVal += "\n:canEdit";
|
|
|
|
retVal += "\n" + GetMemberList(fieldInstance.mResolvedType, addr, dataAddr, false);
|
|
return retVal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (typedValue.mType->IsTypedPrimitive())
|
|
{
|
|
auto innerType = typedValue.mType->GetUnderlyingType();
|
|
|
|
BfTypedValue innerTypedVal = typedValue;
|
|
innerTypedVal.mType = innerType;
|
|
|
|
auto innerReturn = TypedValueToString(innerTypedVal, expr, formatInfo, fullPrecision);
|
|
if (innerReturn.StartsWith("!"))
|
|
return innerReturn;
|
|
|
|
int crPos = (int)innerReturn.IndexOf('\n');
|
|
if (crPos == -1)
|
|
return innerReturn;
|
|
|
|
retVal += "{ ";
|
|
retVal += innerReturn.Substring(0, crPos);
|
|
retVal += " }";
|
|
retVal += "\n" + TypeToString(origTypedValue.mType, typeModKind);
|
|
return retVal;
|
|
}
|
|
|
|
bool isCompositeType = typedValue.mType->IsStruct() || typedValue.mType->IsObject();
|
|
|
|
if (isCompositeType)
|
|
{
|
|
BfTypeInstance* displayType = typedValue.mType->ToTypeInstance();
|
|
|
|
bool isMemoryValid = true;
|
|
|
|
if ((dataAddr == 0) && (addr != 0))
|
|
dataAddr = *(addr_ce*)data;
|
|
|
|
if ((((origTypedValue.mType->IsObjectOrInterface()) || (origTypedValue.mType->IsPointer())) &&
|
|
(!formatInfo.mHidePointers) || (dataAddr == 0)))
|
|
{
|
|
retVal = EncodeDataPtr((uint32)dataAddr, true);
|
|
retVal += " ";
|
|
|
|
if (!ceContext->CheckMemory(dataAddr, displayType->mInstSize))
|
|
isMemoryValid = false;
|
|
}
|
|
|
|
bool isBadSrc = false;
|
|
bool isNull = dataAddr == 0;
|
|
bool hadCustomDisplayString = false;
|
|
|
|
BfTypeInstance* actualType = displayType;
|
|
bool useActualRawType = false;
|
|
|
|
bool isTuple = typedValue.mType->IsTuple();
|
|
|
|
String ptrDataStr;
|
|
|
|
if (!formatInfo.mIgnoreDerivedClassInfo)
|
|
{
|
|
if (actualType->IsObject())
|
|
{
|
|
if (dataAddr != 0)
|
|
{
|
|
uint8* dataPtr = ceContext->GetMemoryPtr(dataAddr, 4);
|
|
if (dataPtr == 0)
|
|
return "!Invalid object address";
|
|
int actualTypeid = *(int32*)dataPtr;
|
|
auto checkType = mCompiler->mContext->FindTypeById(actualTypeid);
|
|
if (checkType != NULL)
|
|
actualType = checkType->ToTypeInstance();
|
|
}
|
|
}
|
|
}
|
|
|
|
BfTypedValue summaryTypedValue = typedValue;
|
|
|
|
if ((actualType != NULL) && (actualType != displayType))
|
|
{
|
|
summaryTypedValue = BfTypedValue(module->mBfIRBuilder->CreateIntToPtr(addr, module->mBfIRBuilder->MapType(actualType)), actualType);
|
|
}
|
|
|
|
DebugVisualizerEntry* debugVis = NULL;
|
|
Array<String> dbgVisWildcardCaptures;
|
|
|
|
if ((!formatInfo.mNoVisualizers) && (!isNull) && (!isBadSrc))
|
|
{
|
|
debugVis = FindVisualizerForType(summaryTypedValue.mType, &dbgVisWildcardCaptures);
|
|
}
|
|
|
|
bool wantsCustomExpandedItems = false;
|
|
String displayString;
|
|
if (debugVis != NULL)
|
|
{
|
|
auto& displayStringList = formatInfo.mRawString ? debugVis->mStringViews : debugVis->mDisplayStrings;
|
|
|
|
for (auto displayEntry : displayStringList)
|
|
{
|
|
if (!displayEntry->mCondition.empty())
|
|
{
|
|
if (!EvalCondition(debugVis, summaryTypedValue, formatInfo, displayEntry->mCondition, dbgVisWildcardCaptures, displayString))
|
|
continue;
|
|
}
|
|
|
|
hadCustomDisplayString = true;
|
|
String displayStr = mDebugManager->mDebugVisualizers->DoStringReplace(displayEntry->mString, dbgVisWildcardCaptures);
|
|
if (displayString.length() > 0)
|
|
displayString += " ";
|
|
ProcessEvalString(summaryTypedValue, displayStr, displayString, formatInfo, debugVis, true);
|
|
if (formatInfo.mRawString)
|
|
return displayString;
|
|
|
|
break;
|
|
}
|
|
|
|
if ((!debugVis->mExpandItems.empty()) || (debugVis->mCollectionType != DebugVisualizerEntry::CollectionType_None))
|
|
{
|
|
wantsCustomExpandedItems = true;
|
|
}
|
|
}
|
|
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
|
|
String reflectedTypeName;
|
|
if (displayType->IsInstanceOf(mCompiler->mTypeTypeDef))
|
|
{
|
|
auto typeInst = displayType->ToTypeInstance();
|
|
auto typeIdField = typeInst->mTypeDef->GetFieldByName("mTypeId");
|
|
if (typeIdField != NULL)
|
|
{
|
|
auto& fieldInstance = typeInst->mFieldInstances[typeIdField->mIdx];
|
|
int typeId = 0;
|
|
memcpy(&typeId, data + fieldInstance.mDataOffset, fieldInstance.mResolvedType->mSize);
|
|
auto typeType = mCompiler->mContext->FindTypeById(typeId);
|
|
if (typeType != NULL)
|
|
reflectedTypeName = module->TypeToString(typeType);
|
|
}
|
|
}
|
|
|
|
if (!reflectedTypeName.IsEmpty())
|
|
{
|
|
displayString += "{ ";
|
|
displayString += reflectedTypeName;
|
|
displayString += " }";
|
|
}
|
|
else if ((!isNull) && (!formatInfo.mNoVisualizers) && (!hadCustomDisplayString))
|
|
{
|
|
// Create our own custom display
|
|
|
|
String firstRet;
|
|
String bigRet = isTuple ? "(" : "{ ";
|
|
|
|
int memberIdx = 0;
|
|
BfType* summaryType = summaryTypedValue.mType;
|
|
bool summaryDone = false;
|
|
bool truncatedMemberList = false;
|
|
|
|
String summaryDataStr = ptrDataStr;
|
|
String splatStr;
|
|
if (dataAddr == -1)
|
|
splatStr = expr;
|
|
|
|
while ((summaryType != NULL) && (isMemoryValid))
|
|
{
|
|
if ((summaryType->IsTypedPrimitive())
|
|
//&& ((summaryType->mBaseTypes.IsEmpty()) || (!summaryType->mBaseTypes.front()->mBaseType->IsTypedPrimitive())))
|
|
)
|
|
{
|
|
if (formatInfo.mTotalSummaryLength + (int)displayString.length() > 255)
|
|
{
|
|
truncatedMemberList = true;
|
|
summaryDone = true;
|
|
bigRet += "...";
|
|
}
|
|
else
|
|
{
|
|
CeFormatInfo displayStrFormatInfo = formatInfo;
|
|
displayStrFormatInfo.mExpandItemDepth = 1;
|
|
displayStrFormatInfo.mTotalSummaryLength += (int)displayString.length();
|
|
displayStrFormatInfo.mHidePointers = false;
|
|
|
|
BfType* primType = summaryType->GetUnderlyingType();
|
|
String result;
|
|
|
|
if (primType->IsInteger())
|
|
formatInfo.mTypeKindFlags = (DbgTypeKindFlags)(formatInfo.mTypeKindFlags | DbgTypeKindFlag_Int);
|
|
|
|
if ((dataAddr != 0) && (dataAddr != -1))
|
|
{
|
|
//String evalString = "(" + primType->ToString() + ")" + ptrDataStr;
|
|
// BfTypedValue evalResult = EvaluateInContext(dbgCompileUnit, origTypedValue, evalString, &displayStrFormatInfo);
|
|
// if (evalResult)
|
|
// result = TypedValueToString(evalResult, evalString, displayStrFormatInfo, NULL);
|
|
}
|
|
else
|
|
{
|
|
// BfTypedValue evalResult = origTypedValue;
|
|
// evalResult.mType = primType;
|
|
// String evalString = "(" + primType->ToString() + ")" + expr;
|
|
// result = TypedValueToString(evalResult, evalString, displayStrFormatInfo, NULL);
|
|
}
|
|
|
|
if (formatInfo.mRawString)
|
|
return result;
|
|
|
|
int crPos = (int)result.IndexOf('\n');
|
|
if (crPos != -1)
|
|
result.RemoveToEnd(crPos);
|
|
|
|
if (memberIdx == 0)
|
|
firstRet = result;
|
|
|
|
bigRet += result;
|
|
memberIdx++;
|
|
}
|
|
}
|
|
|
|
auto summaryTypeInst = summaryType->ToTypeInstance();
|
|
if (summaryTypeInst == NULL)
|
|
break;
|
|
|
|
module->PopulateType(summaryTypeInst);
|
|
for (auto& fieldInst : summaryTypeInst->mFieldInstances)
|
|
{
|
|
auto fieldDef = fieldInst.GetFieldDef();
|
|
if (fieldDef == NULL)
|
|
continue;
|
|
if (fieldInst.mResolvedType == NULL)
|
|
continue;
|
|
|
|
if (!fieldDef->mIsStatic)
|
|
{
|
|
if (formatInfo.mTotalSummaryLength + retVal.length() + bigRet.length() > 255)
|
|
{
|
|
truncatedMemberList = true;
|
|
summaryDone = true;
|
|
bigRet += "...";
|
|
break;
|
|
}
|
|
|
|
//if (fieldDef->mName != NULL)
|
|
{
|
|
// if (member->mName[0] == '$')
|
|
// continue;
|
|
|
|
if (!isdigit(fieldDef->mName[0]))
|
|
{
|
|
if (memberIdx != 0)
|
|
bigRet += isTuple ? ", " : " ";
|
|
|
|
if ((!isTuple) || (fieldDef->mName[0] != '_'))
|
|
{
|
|
bigRet += String(fieldDef->mName);
|
|
bigRet += isTuple ? ":" : "=";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (memberIdx != 0)
|
|
bigRet += ", ";
|
|
}
|
|
|
|
CeFormatInfo displayStrFormatInfo = formatInfo;
|
|
displayStrFormatInfo.mExpandItemDepth = 1;
|
|
displayStrFormatInfo.mHidePointers = false;
|
|
displayStrFormatInfo.mTotalSummaryLength = (int)(formatInfo.mTotalSummaryLength + retVal.length() + bigRet.length());
|
|
|
|
// String evalString;
|
|
// if (dataPtr != -1)
|
|
// {
|
|
// if ((fieldDef->mName[0] >= '0') && (fieldDef->mName[0] <= '9'))
|
|
// evalString += "this.";
|
|
// evalString += String(member->mName); // +", this=" + summaryDataStr;
|
|
// }
|
|
// else
|
|
// {
|
|
// evalString = "(";
|
|
// evalString += splatStr;
|
|
// evalString += ").";
|
|
// evalString += fieldDef->mName;
|
|
// }
|
|
String referenceId;
|
|
String result;
|
|
if (!fieldInst.mResolvedType->IsValuelessType())
|
|
{
|
|
auto addrVal = dataAddr + fieldInst.mDataOffset;
|
|
|
|
BfTypedValue evalResult;
|
|
if (fieldInst.mResolvedType->IsObjectOrInterface())
|
|
{
|
|
auto typeInst = fieldInst.mResolvedType->ToTypeInstance();
|
|
evalResult = BfTypedValue(module->mBfIRBuilder->CreateConstAggCE(module->mBfIRBuilder->MapTypeInst(typeInst), (addr_ce)addrVal), typeInst, true);
|
|
}
|
|
else
|
|
{
|
|
evalResult = BfTypedValue(module->mBfIRBuilder->CreateConstAggCE(module->mBfIRBuilder->MapType(fieldInst.mResolvedType), (addr_ce)addrVal), fieldInst.mResolvedType, true);
|
|
}
|
|
|
|
//BfTypedValue evalResult = EvaluateInContext(dbgCompileUnit, summaryTypedValue, evalString, &displayStrFormatInfo, &referenceId);
|
|
if (evalResult)
|
|
{
|
|
displayStrFormatInfo.mReferenceId = referenceId;
|
|
result = TypedValueToString(evalResult, "", displayStrFormatInfo, NULL);
|
|
int crPos = (int)result.IndexOf('\n');
|
|
if (crPos != -1)
|
|
result.RemoveToEnd(crPos);
|
|
}
|
|
else
|
|
result = "???";
|
|
}
|
|
|
|
if (fieldInst.mResolvedType->IsInteger())
|
|
formatInfo.mTypeKindFlags = (DbgTypeKindFlags)(formatInfo.mTypeKindFlags | DbgTypeKindFlag_Int);
|
|
|
|
if (formatInfo.mRawString)
|
|
return result;
|
|
|
|
if (memberIdx == 0)
|
|
firstRet = result;
|
|
|
|
bigRet += result;
|
|
//formatInfo.mEmbeddedDisplayCount = displayStrFormatInfo.mEmbeddedDisplayCount;
|
|
memberIdx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (truncatedMemberList)
|
|
break;
|
|
|
|
// Find first base class with members
|
|
BfType* nextSummaryType = summaryTypeInst->mBaseType;
|
|
summaryType = nextSummaryType;
|
|
|
|
if ((summaryType == NULL) || (summaryType == module->mContext->mBfObjectType))
|
|
break;
|
|
|
|
// If we don't have many members then find a base class with some members to show
|
|
if ((memberIdx != 0) && (displayString.length() >= 255))
|
|
{
|
|
truncatedMemberList = true;
|
|
bigRet += "...";
|
|
break;
|
|
}
|
|
}
|
|
|
|
bigRet += isTuple ? ")" : " }";
|
|
|
|
if (displayString.length() > 0)
|
|
displayString += " ";
|
|
if ((memberIdx == 1) && (!truncatedMemberList) && (firstRet.IndexOf('{') == -1) && (!isTuple))
|
|
displayString += "{ " + firstRet + " }";
|
|
else
|
|
displayString += bigRet;
|
|
}
|
|
|
|
retVal += displayString;
|
|
retVal += "\n" + TypeToString(origTypedValue.mType, typeModKind);
|
|
|
|
BfType* memberListType = displayType;
|
|
|
|
if ((actualType != NULL) && (actualType != displayType))
|
|
{
|
|
if (displayType == module->mContext->mBfObjectType)
|
|
{
|
|
memberListType = actualType;
|
|
}
|
|
else
|
|
{
|
|
String actualTypeName = module->TypeToString(actualType);
|
|
retVal += StrFormat(" {%s}\n[%s]\tthis,this=%d@0x%X", actualTypeName.c_str(), actualTypeName.c_str(), actualType->mTypeId, addr);
|
|
}
|
|
}
|
|
|
|
if (formatInfo.mNoMembers)
|
|
{
|
|
//
|
|
}
|
|
else if (wantsCustomExpandedItems)
|
|
{
|
|
HandleCustomExpandedItems(retVal, debugVis, summaryTypedValue, addr, dataAddr, dbgVisWildcardCaptures, formatInfo);
|
|
}
|
|
else if ((!isNull) && (!isBadSrc))
|
|
{
|
|
retVal += "\n" + GetMemberList(memberListType, addr, dataAddr, false);
|
|
}
|
|
|
|
if ((origTypedValue.mType->IsObjectOrInterface()) || (origTypedValue.mType->IsPointer()))
|
|
{
|
|
retVal += "\n:editVal\t" + EncodeDataPtr((uint32)dataAddr, true);
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
return "!Failed to display value";
|
|
}
|
|
|
|
void CeDebugger::HandleCustomExpandedItems(String& retVal, DebugVisualizerEntry* debugVis, BfTypedValue typedValue, addr_ce addr, addr_ce addrInst, Array<String>& dbgVisWildcardCaptures, CeFormatInfo& formatInfo)
|
|
{
|
|
auto debugVisualizers = mDebugManager->mDebugVisualizers;
|
|
auto ceModule = mCeMachine->mCeModule;
|
|
|
|
if (formatInfo.mExpandItemDepth > 10) // Avoid crashing on circular ExpandItems
|
|
return;
|
|
|
|
bool isReadOnly = false;
|
|
// if (useTypedValue.mIsReadOnly)
|
|
// isReadOnly = true;
|
|
|
|
String ptrUseDataStr = StrFormat("%d@0x%X", typedValue.mType->mTypeId, addrInst);
|
|
|
|
for (auto entry : debugVis->mExpandItems)
|
|
{
|
|
if (!entry->mCondition.empty())
|
|
{
|
|
String error;
|
|
if (!EvalCondition(debugVis, typedValue, formatInfo, entry->mCondition, dbgVisWildcardCaptures, error))
|
|
{
|
|
if (!error.empty())
|
|
retVal += "\n" + entry->mName + "\t@!<DbgVis Failed>@!";
|
|
continue;
|
|
}
|
|
}
|
|
String replacedStr = debugVisualizers->DoStringReplace(entry->mValue, dbgVisWildcardCaptures);
|
|
retVal += "\n" + entry->mName + "\t" + replacedStr + ", this=" + ptrUseDataStr;
|
|
}
|
|
|
|
String referenceId = ceModule->TypeToString(typedValue.mType);
|
|
|
|
if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_ExpandedItem)
|
|
{
|
|
BfTypedValue itemValue = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);
|
|
if (itemValue)
|
|
{
|
|
CeFormatInfo itemFormatInfo = formatInfo;
|
|
itemFormatInfo.mExpandItemDepth++;
|
|
String itemRetVal = TypedValueToString(itemValue, "", itemFormatInfo, NULL);
|
|
|
|
int crIdx = (int)itemRetVal.IndexOf('\n');
|
|
if (crIdx != -1)
|
|
{
|
|
crIdx = (int)itemRetVal.IndexOf('\n', crIdx + 1);
|
|
if (crIdx != -1)
|
|
retVal += itemRetVal.Substring(crIdx);
|
|
}
|
|
}
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_Array)
|
|
{
|
|
BfTypedValue sizeValue = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
|
|
Array<int> lowerDimSizes;
|
|
for (auto lowerDim : debugVis->mLowerDimSizes)
|
|
{
|
|
BfTypedValue lowerDimValue = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(lowerDim, dbgVisWildcardCaptures), &formatInfo);
|
|
int dimSize = 0;
|
|
if ((lowerDimValue) && (lowerDimValue.mType->IsInteger()))
|
|
dimSize = (int)ValueToInt(lowerDimValue);
|
|
dimSize = BF_MAX(dimSize, 1);
|
|
lowerDimSizes.push_back(dimSize);
|
|
}
|
|
|
|
if ((sizeValue) && (sizeValue.mType->IsInteger()) && (ValueToInt(sizeValue) > 0))
|
|
{
|
|
if (!debugVis->mCondition.IsEmpty())
|
|
{
|
|
int size = (int)ValueToInt(sizeValue);
|
|
BfTypedValue headPointer = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);
|
|
|
|
BfTypedValue curNode = headPointer;
|
|
Array<addr_ce> parentList;
|
|
String continuationData;
|
|
|
|
int totalSize = 2;
|
|
auto valueType = headPointer.mType;
|
|
String addrs = GetArrayItems(debugVis, valueType, headPointer, totalSize, &continuationData);
|
|
String firstAddr;
|
|
String secondAddr;
|
|
bool hasSecondAddr = valueType == NULL;
|
|
if (addrs.length() > 0)
|
|
{
|
|
const char* addrsPtr = addrs.c_str();
|
|
firstAddr = addrs.Substring(0, sizeof(addr_ce) * 2);
|
|
if (hasSecondAddr)
|
|
secondAddr = addrs.Substring(sizeof(addr_ce) * 2, sizeof(addr_ce) * 2);
|
|
}
|
|
|
|
String evalStr;
|
|
if (valueType != NULL)
|
|
{
|
|
auto ptrType = valueType;
|
|
if (!valueType->IsPointer())
|
|
ptrType = ceModule->CreatePointerType(valueType);
|
|
evalStr = StrFormat("(comptype(%d)", ptrType->mTypeId);
|
|
evalStr += ")(void*)0x{1}";
|
|
}
|
|
else
|
|
{
|
|
evalStr += "({1})(void*)0x{2}";
|
|
}
|
|
if (!debugVis->mShowElementAddrs)
|
|
evalStr.Insert(0, "*");
|
|
|
|
if (addrs.length() > 0)
|
|
{
|
|
evalStr += ", refid=\"" + referenceId + ".[]\"";
|
|
if (isReadOnly)
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, BF_MAX(size, 0), 10000) +
|
|
"\t[{0}]\t" + evalStr + "\t" + firstAddr;
|
|
|
|
if (hasSecondAddr)
|
|
retVal += "\t" + secondAddr;
|
|
|
|
if (size != 0)
|
|
{
|
|
retVal += "\n:addrs\t" + addrs;
|
|
if (valueType == NULL)
|
|
retVal += "\n:addrsEntrySize\t2";
|
|
if (continuationData.length() > 0)
|
|
retVal += "\n:continuation\t" + continuationData;
|
|
}
|
|
}
|
|
}
|
|
else if (lowerDimSizes.size() == 1)
|
|
{
|
|
int dimSize1 = lowerDimSizes[0];
|
|
|
|
String evalStr = "(" + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) +
|
|
StrFormat(" + {0} * %d), arraysize=%d, na, this=", dimSize1, dimSize1) + ptrUseDataStr;
|
|
evalStr += ", refid=\"" + referenceId + ".[]\"";
|
|
if (isReadOnly)
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, ValueToInt(sizeValue) / dimSize1, 50000) +
|
|
"\t[{0}]\t" + evalStr;
|
|
}
|
|
else if (lowerDimSizes.size() == 2)
|
|
{
|
|
int dimSize1 = lowerDimSizes[0];
|
|
int dimSize2 = lowerDimSizes[1];
|
|
|
|
BfTypedValue headPointer = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);
|
|
if ((headPointer.mType != NULL) && (headPointer.mType->IsPointer()))
|
|
{
|
|
String evalStr = StrFormat("((%s[%d]*)", ceModule->TypeToString(headPointer.mType->GetUnderlyingType()).c_str(), dimSize2) + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) +
|
|
StrFormat(" + {0} * %d), arraysize=%d, na, this=", dimSize1, dimSize1) + ptrUseDataStr;
|
|
evalStr += ", refid=\"" + referenceId + ".[]\"";
|
|
if (isReadOnly)
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, ValueToInt(sizeValue) / dimSize1 / dimSize2, 50000) +
|
|
"\t[{0}]\t" + evalStr;
|
|
}
|
|
}
|
|
else if (lowerDimSizes.size() == 3)
|
|
{
|
|
int dimSize1 = lowerDimSizes[0];
|
|
int dimSize2 = lowerDimSizes[1];
|
|
int dimSize3 = lowerDimSizes[2];
|
|
|
|
BfTypedValue headPointer = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);
|
|
if ((headPointer.mType != NULL) && (headPointer.mType->IsPointer()))
|
|
{
|
|
String evalStr = StrFormat("((%s[%d][%d]*)", ceModule->TypeToString(headPointer.mType->GetUnderlyingType()).c_str(), dimSize2, dimSize3) + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) +
|
|
StrFormat(" + {0} * %d), arraysize=%d, na, this=", dimSize1, dimSize1) + ptrUseDataStr;
|
|
evalStr += ", refid=\"" + referenceId + ".[]\"";
|
|
if (isReadOnly)
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, ValueToInt(sizeValue) / dimSize1 / dimSize2 / dimSize3, 50000) +
|
|
"\t[{0}]\t" + evalStr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
String evalStr = "*(" + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) + " + {0}), this=" + ptrUseDataStr;
|
|
evalStr += ", refid=\"" + referenceId + ".[]\"";
|
|
if (isReadOnly)
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, ValueToInt(sizeValue), 50000) +
|
|
"\t[{0}]\t" + evalStr;
|
|
}
|
|
}
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_IndexItems)
|
|
{
|
|
BfTypedValue sizeValue = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
|
|
|
|
if ((sizeValue) && (sizeValue.mType->IsInteger()) && (ValueToInt(sizeValue) > 0))
|
|
{
|
|
String evalStr = debugVis->mValuePointer + ", this=" + ptrUseDataStr;
|
|
evalStr.Replace("$i", "{0}");
|
|
|
|
evalStr += ", refid=\"" + referenceId + ".[]\"";
|
|
if (isReadOnly)
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, ValueToInt(sizeValue), 50000) +
|
|
"\t[{0}]\t" + evalStr;
|
|
}
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_LinkedList)
|
|
{
|
|
BfType* valueType = NULL;
|
|
if (!debugVis->mValueType.empty())
|
|
{
|
|
valueType = FindType(debugVisualizers->DoStringReplace(debugVis->mValueType, dbgVisWildcardCaptures));
|
|
}
|
|
|
|
BfTypedValue headPointer = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mHeadPointer, dbgVisWildcardCaptures), &formatInfo);
|
|
if (headPointer)
|
|
{
|
|
BfTypedValue endPointer;
|
|
if (!debugVis->mEndPointer.empty())
|
|
endPointer = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mEndPointer, dbgVisWildcardCaptures), &formatInfo);
|
|
BfTypedValue nextPointer = EvaluateInContext(headPointer, debugVisualizers->DoStringReplace(debugVis->mNextPointer, dbgVisWildcardCaptures), &formatInfo);
|
|
|
|
int size = -1;
|
|
if (!debugVis->mSize.empty())
|
|
{
|
|
auto sizeValue = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
|
|
if (sizeValue)
|
|
size = (int)ValueToInt(sizeValue);
|
|
}
|
|
|
|
auto ceEndPointerVal = GetAddr(endPointer);
|
|
|
|
BfTypedValue curNode = headPointer;
|
|
Array<addr_ce> parentList;
|
|
String continuationData;
|
|
|
|
int totalSize = 2;
|
|
String addrs = GetLinkedListItems(debugVis, (addr_ce)ceEndPointerVal.mAddr, valueType, curNode, totalSize, &continuationData);
|
|
String firstAddr;
|
|
String secondAddr;
|
|
bool hasSecondAddr = valueType == NULL;
|
|
if (addrs.length() > 0)
|
|
{
|
|
const char* addrsPtr = addrs.c_str();
|
|
firstAddr = addrs.Substring(0, sizeof(addr_ce) * 2);
|
|
if (hasSecondAddr)
|
|
secondAddr = addrs.Substring(sizeof(addr_ce) * 2, sizeof(addr_ce) * 2);
|
|
}
|
|
|
|
String evalStr;
|
|
if (valueType != NULL)
|
|
{
|
|
auto ptrType = valueType;
|
|
if (!valueType->IsPointer())
|
|
ptrType = ceModule->CreatePointerType(valueType);
|
|
evalStr = StrFormat("(comptype(%d)", ptrType->mTypeId);
|
|
evalStr += ")0x{1}";
|
|
}
|
|
else
|
|
{
|
|
evalStr += "({1})0x{2}";
|
|
}
|
|
if (!debugVis->mShowElementAddrs)
|
|
evalStr.Insert(0, "*");
|
|
|
|
if (addrs.length() > 0)
|
|
{
|
|
evalStr += ", refid=\"" + referenceId + ".[]\"";
|
|
if (isReadOnly)
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, size, 10000) +
|
|
"\t[{0}]\t" + evalStr + "\t" + firstAddr;
|
|
|
|
if (hasSecondAddr)
|
|
retVal += "\t" + secondAddr;
|
|
|
|
if (size != 0)
|
|
{
|
|
retVal += "\n:addrs\t" + addrs;
|
|
if (valueType == NULL)
|
|
retVal += "\n:addrsEntrySize\t2";
|
|
if (continuationData.length() > 0)
|
|
retVal += "\n:continuation\t" + continuationData;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_TreeItems)
|
|
{
|
|
BfType* valueType = NULL;
|
|
if (!debugVis->mValueType.empty())
|
|
{
|
|
valueType = FindType(debugVisualizers->DoStringReplace(debugVis->mValueType, dbgVisWildcardCaptures));
|
|
}
|
|
|
|
BfTypedValue sizeValue = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
|
|
BfTypedValue headPointer = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mHeadPointer, dbgVisWildcardCaptures), &formatInfo);
|
|
|
|
if ((sizeValue) && (headPointer) && (sizeValue.mType->IsInteger()) && (ValueToInt(sizeValue) > 0))
|
|
{
|
|
BfTypedValue curNode = headPointer;
|
|
Array<addr_ce> parentList;
|
|
String continuationData;
|
|
|
|
int getItemCount = (int)BF_MIN(ValueToInt(sizeValue), 32LL);
|
|
|
|
String addrs = GetTreeItems(debugVis, parentList, valueType, curNode, getItemCount, &continuationData);
|
|
addr_ce firstAddr = 0;
|
|
addr_ce secondAddr = 0;
|
|
bool hasSecondAddr = valueType == NULL;
|
|
if (addrs.length() > 0)
|
|
{
|
|
const char* addrsPtr = addrs.c_str();
|
|
firstAddr = DecodeTargetDataPtr(addrsPtr);
|
|
if (hasSecondAddr)
|
|
secondAddr = DecodeTargetDataPtr(addrsPtr);
|
|
}
|
|
|
|
String evalStr;
|
|
if (valueType != NULL)
|
|
{
|
|
auto ptrType = valueType;
|
|
if (!valueType->IsPointer())
|
|
ptrType = ceModule->CreatePointerType(valueType);
|
|
evalStr = StrFormat("(comptype(%d)", ptrType->mTypeId);
|
|
evalStr += ")0x{1}";
|
|
}
|
|
else
|
|
{
|
|
evalStr += "*(_T_{1}*)0x{2}";
|
|
}
|
|
|
|
int size = (int)ValueToInt(sizeValue);
|
|
if (addrs.length() == 0)
|
|
{
|
|
evalStr = ""; // Failed
|
|
}
|
|
|
|
evalStr += ", refid=\"" + referenceId + ".[]\"";
|
|
if (isReadOnly)
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, size, 10000) +
|
|
"\t[{0}]\t" + evalStr + "\t" + EncodeDataPtr(firstAddr, false);
|
|
|
|
if (hasSecondAddr)
|
|
retVal += "\t" + EncodeDataPtr(secondAddr, false);
|
|
|
|
if (addrs.length() > 0)
|
|
{
|
|
retVal += "\n:addrs\t" + addrs;
|
|
if (continuationData.length() > 0)
|
|
retVal += "\n:continuation\t" + continuationData;
|
|
}
|
|
}
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_Dictionary)
|
|
{
|
|
BfTypedValue sizeValue = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
|
|
BfTypedValue entriesPtrValue = EvaluateInContext(typedValue, debugVisualizers->DoStringReplace(debugVis->mEntries, dbgVisWildcardCaptures), &formatInfo);
|
|
|
|
if ((sizeValue) && (entriesPtrValue) && (sizeValue.mType->IsInteger()) && (ValueToInt(sizeValue) > 0))
|
|
{
|
|
String continuationData;
|
|
|
|
BfType* valueType = entriesPtrValue.mType;
|
|
|
|
int getItemCount = (int)BF_MIN(ValueToInt(sizeValue), 2LL);
|
|
|
|
BfType* useTypedValType = typedValue.mType;
|
|
|
|
String addrs = GetDictionaryItems(debugVis, typedValue, 0, -1, getItemCount, &continuationData);
|
|
addr_ce firstAddr = 0;
|
|
if (addrs.length() > 0)
|
|
{
|
|
const char* addrsPtr = addrs.c_str();
|
|
firstAddr = DecodeTargetDataPtr(addrsPtr);
|
|
}
|
|
|
|
String evalStr = "((comptype(" + StrFormat("%d", valueType->mTypeId) + "))(void*)0x{1}), na";
|
|
|
|
evalStr += ", refid=\"" + referenceId + ".[]\"";
|
|
if (isReadOnly)
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, (int)ValueToInt(sizeValue), 10000) +
|
|
"\t[{0}]\t" + evalStr + "\t" + EncodeDataPtr(firstAddr, false);
|
|
|
|
if (addrs.length() > 0)
|
|
{
|
|
retVal += "\n:addrs\t" + addrs;
|
|
if (continuationData.length() > 0)
|
|
retVal += "\n:continuation\t" + continuationData;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (formatInfo.mExpandItemDepth == 0)
|
|
{
|
|
retVal += "\n[Raw View]\tthis,this=" + ptrUseDataStr + ", nv";
|
|
}
|
|
}
|
|
|
|
String CeDebugger::Evaluate(const StringImpl& expr, int callStackIdx, int cursorPos, int language, DwEvalExpressionFlags expressionFlags)
|
|
{
|
|
CeFormatInfo formatInfo;
|
|
return Evaluate(expr, formatInfo, callStackIdx, cursorPos, language, expressionFlags);
|
|
}
|
|
|
|
String CeDebugger::EvaluateContinue()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (mDebugPendingExpr == NULL)
|
|
return "";
|
|
|
|
if (!mDebugPendingExpr->mDone)
|
|
return "!pending";
|
|
|
|
String result = mDebugPendingExpr->mResult;
|
|
delete mDebugPendingExpr;
|
|
mDebugPendingExpr = NULL;
|
|
return result;
|
|
}
|
|
|
|
void CeDebugger::EvaluateContinueKeep()
|
|
{
|
|
}
|
|
|
|
String CeDebugger::EvaluateToAddress(const StringImpl& expr, int callStackIdx, int cursorPos)
|
|
{
|
|
return String();
|
|
}
|
|
|
|
String CeDebugger::EvaluateAtAddress(const StringImpl& expr, intptr atAddr, int cursorPos)
|
|
{
|
|
return String();
|
|
}
|
|
|
|
String CeDebugger::GetAutoExpressions(int callStackIdx, uint64 memoryRangeStart, uint64 memoryRangeLen)
|
|
{
|
|
return String();
|
|
}
|
|
|
|
String CeDebugger::GetAutoLocals(int callStackIdx, bool showRegs)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!mCeMachine->mDbgPaused)
|
|
return "";
|
|
|
|
String result;
|
|
|
|
auto ceFrame = GetFrame(callStackIdx);
|
|
if (ceFrame == NULL)
|
|
return result;
|
|
|
|
int scopeIdx = -1;
|
|
auto ceEntry = ceFrame->mFunction->FindEmitEntry(ceFrame->GetInstIdx());
|
|
if (ceEntry != NULL)
|
|
scopeIdx = ceEntry->mScope;
|
|
|
|
int instIdx = ceFrame->GetInstIdx();
|
|
if (ceFrame->mFunction->mDbgInfo != NULL)
|
|
{
|
|
auto ceFunction = ceFrame->mFunction;
|
|
|
|
struct CeDbgInfo
|
|
{
|
|
int mShadowCount;
|
|
int mPrevShadowIdx;
|
|
bool mIncluded;
|
|
|
|
CeDbgInfo()
|
|
{
|
|
mShadowCount = 0;
|
|
mPrevShadowIdx = -1;
|
|
mIncluded = true;
|
|
}
|
|
};
|
|
|
|
Array<CeDbgInfo> dbgInfo;
|
|
Dictionary<String, int> nameIndices;
|
|
|
|
dbgInfo.Resize(ceFunction->mDbgInfo->mVariables.mSize);
|
|
for (int i = 0; i < ceFunction->mDbgInfo->mVariables.mSize; i++)
|
|
{
|
|
auto& dbgVar = ceFunction->mDbgInfo->mVariables[i];
|
|
|
|
if ((dbgVar.mScope == scopeIdx) && (instIdx >= dbgVar.mStartCodePos) && (instIdx < dbgVar.mEndCodePos))
|
|
{
|
|
int* idxPtr = NULL;
|
|
if (!nameIndices.TryAdd(dbgVar.mName, NULL, &idxPtr))
|
|
{
|
|
int checkIdx = *idxPtr;
|
|
dbgInfo[i].mPrevShadowIdx = checkIdx;
|
|
|
|
while (checkIdx != -1)
|
|
{
|
|
dbgInfo[checkIdx].mShadowCount++;
|
|
checkIdx = dbgInfo[checkIdx].mPrevShadowIdx;
|
|
}
|
|
}
|
|
*idxPtr = i;
|
|
}
|
|
else
|
|
{
|
|
dbgInfo[i].mIncluded = false;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < ceFunction->mDbgInfo->mVariables.mSize; i++)
|
|
{
|
|
auto& dbgVar = ceFunction->mDbgInfo->mVariables[i];
|
|
if (dbgInfo[i].mIncluded)
|
|
{
|
|
for (int shadowIdx = 0; shadowIdx < dbgInfo[i].mShadowCount; shadowIdx++)
|
|
result += "@";
|
|
result += dbgVar.mName;
|
|
result += "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
String CeDebugger::CompactChildExpression(const StringImpl& expr, const StringImpl& parentExpr, int callStackIdx)
|
|
{
|
|
return String();
|
|
}
|
|
|
|
String CeDebugger::GetProcessInfo()
|
|
{
|
|
return String();
|
|
}
|
|
|
|
DebugVisualizerEntry* CeDebugger::FindVisualizerForType(BfType* dbgType, Array<String>* wildcardCaptures)
|
|
{
|
|
auto ceModule = mCeMachine->mCeModule;
|
|
|
|
ceModule->PopulateType(dbgType);
|
|
auto entry = mDebugManager->mDebugVisualizers->FindEntryForType(ceModule->TypeToString(dbgType), DbgFlavor_Unknown, wildcardCaptures);
|
|
|
|
if (entry == NULL)
|
|
{
|
|
auto typeInst = dbgType->ToTypeInstance();
|
|
if ((typeInst != NULL) && (typeInst->mBaseType != NULL))
|
|
entry = FindVisualizerForType(typeInst->mBaseType, wildcardCaptures);
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
String CeDebugger::GetThreadInfo()
|
|
{
|
|
return String();
|
|
}
|
|
|
|
void CeDebugger::SetActiveThread(int threadId)
|
|
{
|
|
}
|
|
|
|
int CeDebugger::GetActiveThread()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void CeDebugger::FreezeThread(int threadId)
|
|
{
|
|
}
|
|
|
|
void CeDebugger::ThawThread(int threadId)
|
|
{
|
|
}
|
|
|
|
bool CeDebugger::IsActiveThreadWaiting()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void CeDebugger::ClearCallStack()
|
|
{
|
|
AutoCrit autoCrit(mCeMachine->mCritSect);
|
|
mDbgCallStack.Clear();
|
|
}
|
|
|
|
void CeDebugger::UpdateCallStack(bool slowEarlyOut)
|
|
{
|
|
AutoCrit autoCrit(mCeMachine->mCritSect);
|
|
|
|
if (!mDbgCallStack.IsEmpty())
|
|
return;
|
|
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
for (int frameIdx = ceContext->mCallStack.mSize - 1; frameIdx >= 0; frameIdx--)
|
|
{
|
|
auto ceFrame = &ceContext->mCallStack[frameIdx];
|
|
|
|
auto instIdx = ceFrame->GetInstIdx();
|
|
auto emitEntry = ceFrame->mFunction->FindEmitEntry(instIdx);
|
|
if (emitEntry == NULL)
|
|
continue;
|
|
|
|
int scopeIdx = emitEntry->mScope;
|
|
int prevInlineIdx = -1;
|
|
while (scopeIdx != -1)
|
|
{
|
|
CeDbgStackInfo ceDbgStackInfo;
|
|
ceDbgStackInfo.mFrameIdx = frameIdx;
|
|
ceDbgStackInfo.mScopeIdx = scopeIdx;
|
|
ceDbgStackInfo.mInlinedFrom = prevInlineIdx;
|
|
mDbgCallStack.Add(ceDbgStackInfo);
|
|
|
|
auto ceScope = &ceFrame->mFunction->mDbgScopes[scopeIdx];
|
|
if (ceScope->mInlinedAt == -1)
|
|
break;
|
|
auto inlineInfo = &ceFrame->mFunction->mDbgInlineTable[ceScope->mInlinedAt];
|
|
scopeIdx = inlineInfo->mScope;
|
|
prevInlineIdx = ceScope->mInlinedAt;
|
|
}
|
|
}
|
|
|
|
CeDbgStackInfo ceDbgStackInfo;
|
|
ceDbgStackInfo.mFrameIdx = -1;
|
|
ceDbgStackInfo.mScopeIdx = -1;
|
|
ceDbgStackInfo.mInlinedFrom = -1;
|
|
mDbgCallStack.Add(ceDbgStackInfo);
|
|
}
|
|
|
|
int CeDebugger::GetCallStackCount()
|
|
{
|
|
AutoCrit autoCrit(mCeMachine->mCritSect);
|
|
|
|
if (!mCeMachine->mDbgPaused)
|
|
return 0;
|
|
|
|
UpdateCallStack();
|
|
return mDbgCallStack.mSize;
|
|
}
|
|
|
|
int CeDebugger::GetRequestedStackFrameIdx()
|
|
{
|
|
return mPendingActiveFrameOffset;
|
|
}
|
|
|
|
int CeDebugger::GetBreakStackFrameIdx()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool CeDebugger::ReadMemory(intptr address, uint64 length, void* dest, bool local)
|
|
{
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
if (ceContext == NULL)
|
|
return false;
|
|
auto ptr = ceContext->GetMemoryPtr((addr_ce)address, (int32)length);
|
|
if (ptr == NULL)
|
|
return false;
|
|
memcpy(dest, ptr, length);
|
|
return true;
|
|
}
|
|
|
|
bool CeDebugger::WriteMemory(intptr address, void* src, uint64 length)
|
|
{
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
if (ceContext == NULL)
|
|
return false;
|
|
auto ptr = ceContext->GetMemoryPtr((addr_ce)address, (int32)length);
|
|
if (ptr == NULL)
|
|
return false;
|
|
memcpy(ptr, src, length);
|
|
return true;
|
|
}
|
|
|
|
DbgMemoryFlags CeDebugger::GetMemoryFlags(intptr address)
|
|
{
|
|
return (DbgMemoryFlags)(DbgMemoryFlags_Read | DbgMemoryFlags_Write);
|
|
}
|
|
|
|
void CeDebugger::UpdateRegisterUsage(int stackFrameIdx)
|
|
{
|
|
}
|
|
|
|
void CeDebugger::UpdateCallStackMethod(int stackFrameIdx)
|
|
{
|
|
}
|
|
|
|
void CeDebugger::GetCodeAddrInfo(intptr addr, intptr inlineCallAddr, String* outFile, int* outHotIdx, int* outDefLineStart, int* outDefLineEnd, int* outLine, int* outColumn)
|
|
{
|
|
}
|
|
|
|
void CeDebugger::GetStackAllocInfo(intptr addr, int* outThreadId, int* outStackIdx)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
*outThreadId = 0;
|
|
if (outStackIdx != NULL)
|
|
*outStackIdx = -1;
|
|
|
|
if (!mCeMachine->mDbgPaused)
|
|
return;
|
|
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
for (int i = 0; i < (int)ceContext->mCallStack.mSize; i++)
|
|
{
|
|
auto ceFrame = &ceContext->mCallStack[i];
|
|
if ((addr_ce)addr > ceFrame->mFrameAddr)
|
|
{
|
|
if (outStackIdx != NULL)
|
|
*outStackIdx = (int)ceContext->mCallStack.mSize - i - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
String CeDebugger::GetStackFrameInfo(int stackFrameIdx, intptr* addr, String* outFile, int32* outHotIdx, int32* outDefLineStart, int32* outDefLineEnd, int32* outLine, int32* outColumn, int32* outLanguage, int32* outStackSize, int8* outFlags)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!mCeMachine->mDbgPaused)
|
|
return "";
|
|
|
|
UpdateCallStack();
|
|
|
|
enum FrameFlags
|
|
{
|
|
FrameFlags_Optimized = 1,
|
|
FrameFlags_HasPendingDebugInfo = 2,
|
|
FrameFlags_CanGetOldSource = 4,
|
|
FrameFlags_WasHotReplaced = 8,
|
|
FrameFlags_HadError = 0x10
|
|
};
|
|
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
|
|
*addr = 0;
|
|
*outFile = "";
|
|
*outHotIdx = 0;
|
|
*outDefLineStart = -1;
|
|
*outDefLineEnd = -1;
|
|
*outLine = -1;
|
|
*outColumn = 0;
|
|
*outLanguage = DbgLanguage_Beef;
|
|
*outStackSize = 0;
|
|
*outFlags = 0;
|
|
|
|
if (ceContext == NULL)
|
|
return "";
|
|
|
|
auto& dbgCallstackInfo = mDbgCallStack[stackFrameIdx];
|
|
|
|
if (dbgCallstackInfo.mFrameIdx == -1)
|
|
{
|
|
if (ceContext->mCurCallSource != NULL)
|
|
{
|
|
int line = -1;
|
|
int lineChar = -1;
|
|
auto parserData = ceContext->mCurCallSource->mRefNode->GetParserData();
|
|
if (parserData != NULL)
|
|
{
|
|
parserData->GetLineCharAtIdx(ceContext->mCurCallSource->mRefNode->GetSrcStart(), line, lineChar);
|
|
*outLine = line;
|
|
*outColumn = lineChar;
|
|
*outFile = parserData->mFileName;
|
|
}
|
|
}
|
|
|
|
// Entry marker
|
|
String result = "const eval";
|
|
if (ceContext->mCurCallSource->mKind == CeCallSource::Kind_FieldInit)
|
|
{
|
|
return String("field init of : ") + ceContext->mCurModule->TypeToString(ceContext->mCurCallSource->mFieldInstance->mOwner) + "." +
|
|
ceContext->mCurCallSource->mFieldInstance->GetFieldDef()->mName;
|
|
}
|
|
else if (ceContext->mCurCallSource->mKind == CeCallSource::Kind_MethodInit)
|
|
{
|
|
return ("method init of : ") + ceContext->mCurModule->MethodToString(ceContext->mCallerMethodInstance);
|
|
}
|
|
else if (ceContext->mCurCallSource->mKind == CeCallSource::Kind_TypeInit)
|
|
result = "type init";
|
|
else if (ceContext->mCurCallSource->mKind == CeCallSource::Kind_TypeDone)
|
|
result = "type done";
|
|
|
|
if (ceContext->mCallerMethodInstance != NULL)
|
|
result += String(" in : ") + ceContext->mCurModule->MethodToString(ceContext->mCallerMethodInstance);
|
|
else if (ceContext->mCallerTypeInstance != NULL)
|
|
result += String(" in : ") + ceContext->mCurModule->TypeToString(ceContext->mCallerTypeInstance);
|
|
return result;
|
|
}
|
|
|
|
auto ceFrame = &ceContext->mCallStack[dbgCallstackInfo.mFrameIdx];
|
|
auto ceFunction = ceFrame->mFunction;
|
|
|
|
if (ceFunction->mFailed)
|
|
*outFlags |= FrameFlags_HadError;
|
|
|
|
int instIdx = (int)(ceFrame->mInstPtr - &ceFunction->mCode[0] - 2);
|
|
|
|
BF_ASSERT(ceFunction->mId != -1);
|
|
*addr = ((intptr)ceFunction->mId << 32) | instIdx;
|
|
|
|
CeEmitEntry* emitEntry = ceFunction->FindEmitEntry(instIdx);
|
|
|
|
*outStackSize = ceContext->mStackSize - ceFrame->mStackAddr;
|
|
if (stackFrameIdx < mDbgCallStack.mSize - 1)
|
|
{
|
|
auto& nextStackInfo = mDbgCallStack[stackFrameIdx + 1];
|
|
if (nextStackInfo.mFrameIdx >= 0)
|
|
{
|
|
auto prevFrame = &ceContext->mCallStack[nextStackInfo.mFrameIdx];
|
|
*outStackSize = prevFrame->mStackAddr - ceFrame->mStackAddr;
|
|
}
|
|
}
|
|
|
|
CeDbgScope* ceScope = NULL;
|
|
if (dbgCallstackInfo.mScopeIdx != -1)
|
|
ceScope = &ceFunction->mDbgScopes[dbgCallstackInfo.mScopeIdx];
|
|
|
|
if (emitEntry != NULL)
|
|
{
|
|
if (emitEntry->mScope != -1)
|
|
*outFile = ceFunction->mDbgScopes[emitEntry->mScope].mFilePath;
|
|
*outLine = emitEntry->mLine;
|
|
*outColumn = emitEntry->mColumn;
|
|
}
|
|
|
|
if (dbgCallstackInfo.mInlinedFrom != -1)
|
|
{
|
|
auto dbgInlineInfo = &ceFunction->mDbgInlineTable[dbgCallstackInfo.mInlinedFrom];
|
|
*outLine = dbgInlineInfo->mLine;
|
|
*outColumn = dbgInlineInfo->mColumn;
|
|
}
|
|
|
|
if ((ceScope != NULL) && (ceScope->mMethodVal != -1))
|
|
{
|
|
if ((ceScope->mMethodVal & CeDbgScope::MethodValFlag_MethodRef) != 0)
|
|
{
|
|
auto dbgMethodRef = &ceFunction->mDbgMethodRefTable[ceScope->mMethodVal & CeDbgScope::MethodValFlag_IdxMask];
|
|
return dbgMethodRef->ToString();
|
|
}
|
|
else
|
|
{
|
|
auto callTableEntry = &ceFunction->mCallTable[ceScope->mMethodVal];
|
|
return ceContext->mCurModule->MethodToString(callTableEntry->mFunctionInfo->mMethodInstance);
|
|
}
|
|
}
|
|
|
|
return ceContext->mCurModule->MethodToString(ceFrame->mFunction->mMethodInstance);
|
|
}
|
|
|
|
BfType* CeDebugger::FindType(const StringImpl& name)
|
|
{
|
|
if (name == "System.Object")
|
|
return mCeMachine->mCeModule->mContext->mBfObjectType;
|
|
|
|
BfParser parser(mCompiler->mSystem);
|
|
BfPassInstance passInstance(mCompiler->mSystem);
|
|
parser.SetSource(name.c_str(), (int)name.length());
|
|
parser.Parse(&passInstance);
|
|
|
|
BfReducer reducer;
|
|
reducer.mAlloc = parser.mAlloc;
|
|
reducer.mSystem = mCompiler->mSystem;
|
|
reducer.mPassInstance = &passInstance;
|
|
reducer.mVisitorPos = BfReducer::BfVisitorPos(parser.mRootNode);
|
|
reducer.mVisitorPos.MoveNext();
|
|
reducer.mSource = &parser;
|
|
auto typeRef = reducer.CreateTypeRef(parser.mRootNode->GetFirst());
|
|
parser.Close();
|
|
|
|
auto ceModule = mCeMachine->mCeModule;
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(ceModule->mIgnoreErrors, true);
|
|
SetAndRestoreValue<bool> prevIgnoreWarning(ceModule->mIgnoreWarnings, true);
|
|
|
|
return ceModule->ResolveTypeRef(typeRef, {}, BfPopulateType_Declaration, BfResolveTypeRefFlag_IgnoreLookupError);
|
|
}
|
|
|
|
String CeDebugger::Callstack_GetStackFrameOldFileInfo(int stackFrameIdx)
|
|
{
|
|
return String();
|
|
}
|
|
|
|
#define CE_GET(T) *((T*)(ptr += sizeof(T)) - 1)
|
|
|
|
int CeDebugger::GetJmpState(int stackFrameIdx)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!mCeMachine->mDbgPaused)
|
|
return -1;
|
|
|
|
if (stackFrameIdx != 0)
|
|
return -1;
|
|
|
|
auto ceFrame = GetFrame(stackFrameIdx);
|
|
if (ceFrame == NULL)
|
|
return -1;
|
|
|
|
int instIdx = ceFrame->GetInstIdx();
|
|
|
|
auto ptr = &ceFrame->mFunction->mCode[instIdx];
|
|
auto op = CE_GET(CeOp);
|
|
switch (op)
|
|
{
|
|
case CeOp_Jmp:
|
|
return 1;
|
|
case CeOp_JmpIf:
|
|
case CeOp_JmpIfNot:
|
|
{
|
|
CE_GET(int32);
|
|
int frameOfs = CE_GET(int32);
|
|
bool willJump = *(bool*)(mCeMachine->mCurContext->mMemory.mVals + ceFrame->mFrameAddr + frameOfs);
|
|
if (op == CeOp_JmpIfNot)
|
|
willJump = !willJump;
|
|
return willJump ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
intptr CeDebugger::GetStackFrameCalleeAddr(int stackFrameIdx)
|
|
{
|
|
return intptr();
|
|
}
|
|
|
|
String CeDebugger::GetStackMethodOwner(int stackFrameIdx, int& language)
|
|
{
|
|
return String();
|
|
}
|
|
|
|
String CeDebugger::FindCodeAddresses(const StringImpl& fileName, int line, int column, bool allowAutoResolve)
|
|
{
|
|
return String();
|
|
}
|
|
|
|
String CeDebugger::GetAddressSourceLocation(intptr address)
|
|
{
|
|
return String();
|
|
}
|
|
|
|
String CeDebugger::GetAddressSymbolName(intptr address, bool demangle)
|
|
{
|
|
return String();
|
|
}
|
|
|
|
String CeDebugger::DisassembleAtRaw(intptr address)
|
|
{
|
|
return String();
|
|
}
|
|
|
|
String CeDebugger::DisassembleAt(intptr address)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!mCeMachine->mDbgPaused)
|
|
return "";
|
|
|
|
auto ceContext = mCeMachine->mCurContext;
|
|
|
|
mCurDisasmFuncId = (int)(address >> 32);
|
|
UpdateBreakpointAddrs();
|
|
|
|
CeFunction* ceFunction = NULL;
|
|
if (!mCeMachine->mFunctionIdMap.TryGetValue(mCurDisasmFuncId, &ceFunction))
|
|
return "";
|
|
|
|
CeDumpContext dumpCtx;
|
|
dumpCtx.mCeFunction = ceFunction;
|
|
dumpCtx.mStart = &ceFunction->mCode[0];
|
|
dumpCtx.mPtr = dumpCtx.mStart;
|
|
dumpCtx.mEnd = dumpCtx.mPtr + ceFunction->mCode.mSize;
|
|
|
|
uint8* start = dumpCtx.mStart;
|
|
|
|
dumpCtx.mStr += StrFormat("T Frame Size: %d\n", ceFunction->mFrameSize);
|
|
dumpCtx.mStr += StrFormat("A %llX\n", (intptr)mCurDisasmFuncId << 32);
|
|
|
|
int curEmitIdx = 0;
|
|
CeEmitEntry* prevEmitEntry = NULL;
|
|
CeEmitEntry* curEmitEntry = NULL;
|
|
|
|
String prevSourcePath;
|
|
|
|
while (dumpCtx.mPtr < dumpCtx.mEnd)
|
|
{
|
|
int ofs = (int)(dumpCtx.mPtr - start);
|
|
|
|
while ((curEmitIdx < ceFunction->mEmitTable.mSize - 1) && (ofs >= ceFunction->mEmitTable[curEmitIdx + 1].mCodePos))
|
|
curEmitIdx++;
|
|
if (curEmitIdx < ceFunction->mEmitTable.mSize)
|
|
curEmitEntry = &ceFunction->mEmitTable[curEmitIdx];
|
|
|
|
if (prevEmitEntry != curEmitEntry)
|
|
{
|
|
if ((curEmitEntry != NULL) && (curEmitEntry->mLine != -1))
|
|
{
|
|
bool pathChanged = false;
|
|
if ((prevEmitEntry == NULL) || (curEmitEntry->mScope != prevEmitEntry->mScope))
|
|
{
|
|
auto ceDbgScope = &ceFunction->mDbgScopes[curEmitEntry->mScope];
|
|
if (ceDbgScope->mFilePath != prevSourcePath)
|
|
{
|
|
pathChanged = true;
|
|
dumpCtx.mStr += StrFormat("S %s\n", ceDbgScope->mFilePath.c_str());
|
|
prevSourcePath = ceDbgScope->mFilePath;
|
|
}
|
|
}
|
|
|
|
if ((prevEmitEntry != NULL) && (!pathChanged) && (curEmitEntry->mLine >= prevEmitEntry->mLine))
|
|
{
|
|
dumpCtx.mStr += StrFormat("L %d %d\n", prevEmitEntry->mLine + 1, curEmitEntry->mLine - prevEmitEntry->mLine);
|
|
}
|
|
else
|
|
{
|
|
int startLine = BF_MAX(0, curEmitEntry->mLine - 5);
|
|
dumpCtx.mStr += StrFormat("L %d %d\n", startLine, curEmitEntry->mLine - startLine + 1);
|
|
}
|
|
|
|
prevEmitEntry = curEmitEntry;
|
|
}
|
|
}
|
|
|
|
dumpCtx.mStr += StrFormat("D %04X: ", ofs);
|
|
dumpCtx.Next();
|
|
dumpCtx.mStr += "\n";
|
|
|
|
if (dumpCtx.mJmp != -1)
|
|
{
|
|
dumpCtx.mStr += StrFormat("J %X\n", dumpCtx.mJmp);
|
|
dumpCtx.mJmp = -1;
|
|
}
|
|
}
|
|
|
|
return dumpCtx.mStr;
|
|
}
|
|
|
|
String CeDebugger::FindLineCallAddresses(intptr address)
|
|
{
|
|
return String();
|
|
}
|
|
|
|
String CeDebugger::GetCurrentException()
|
|
{
|
|
return String();
|
|
}
|
|
|
|
String CeDebugger::GetModulesInfo()
|
|
{
|
|
return String();
|
|
}
|
|
|
|
void CeDebugger::SetAliasPath(const StringImpl& origPath, const StringImpl& localPath)
|
|
{
|
|
}
|
|
|
|
void CeDebugger::CancelSymSrv()
|
|
{
|
|
}
|
|
|
|
bool CeDebugger::HasPendingDebugLoads()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int CeDebugger::LoadImageForModule(const StringImpl& moduleName, const StringImpl& debugFileName)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int CeDebugger::LoadDebugInfoForModule(const StringImpl& moduleName)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int CeDebugger::LoadDebugInfoForModule(const StringImpl& moduleName, const StringImpl& debugFileName)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void CeDebugger::StopDebugging()
|
|
{
|
|
mRunState = RunState_Terminating;
|
|
mCeMachine->mDebugEvent.Set(true);
|
|
}
|
|
|
|
void CeDebugger::Terminate()
|
|
{
|
|
}
|
|
|
|
void CeDebugger::Detach()
|
|
{
|
|
mRunState = RunState_Terminated;
|
|
mDbgCallStack.Clear();
|
|
}
|
|
|
|
Profiler* CeDebugger::StartProfiling()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
Profiler* CeDebugger::PopProfiler()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
void CeDebugger::ReportMemory(MemReporter* memReporter)
|
|
{
|
|
}
|
|
|
|
bool CeDebugger::IsOnDemandDebugger()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool CeDebugger::GetEmitSource(const StringImpl& filePath, String& outText)
|
|
{
|
|
return false;
|
|
} |