mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-07 19:18:19 +02:00
13888 lines
No EOL
402 KiB
C++
13888 lines
No EOL
402 KiB
C++
#pragma warning(disable:4996)
|
|
// TODO: Remove for 64-bit
|
|
#pragma warning(disable:4244)
|
|
#pragma warning(disable:4267)
|
|
|
|
#define NTDDI_VERSION 0x06020000
|
|
|
|
#include "WinDebugger.h"
|
|
#include "CPU.h"
|
|
#include "DbgModule.h"
|
|
#include "DebugVisualizers.h"
|
|
#include "MiniDumpDebugger.h"
|
|
#include "X86.h"
|
|
#include "BeefySysLib/Common.h"
|
|
#include "BeefySysLib/util/PerfTimer.h"
|
|
#include "BeefySysLib/util/BeefPerf.h"
|
|
#include "BeefySysLib/util/CritSect.h"
|
|
#include "BeefySysLib/util/UTF8.h"
|
|
#include "BeefySysLib/FileStream.h"
|
|
#include "BeefySysLib/FileHandleStream.h"
|
|
#include "BeefySysLib/util/FileEnumerator.h"
|
|
#include <inttypes.h>
|
|
#include <windows.h>
|
|
#include "DbgExprEvaluator.h"
|
|
#include "Compiler/BfSystem.h"
|
|
#include "Compiler/BfParser.h"
|
|
#include "Compiler/BfReducer.h"
|
|
#include "Compiler/BfDemangler.h"
|
|
#include "Compiler/BfPrinter.h"
|
|
#include <Shlobj.h>
|
|
#include "NetManager.h"
|
|
#include "DebugManager.h"
|
|
#include "X86Target.h"
|
|
#include "HotHeap.h"
|
|
#include "HotScanner.h"
|
|
#include "Profiler.h"
|
|
#include <float.h>
|
|
|
|
#include <psapi.h>
|
|
|
|
#if !defined BF32 || !defined BF_DBG_64
|
|
|
|
#define STATUS_WX86_CONTINUE 0x4000001DL
|
|
#define STATUS_WX86_SINGLE_STEP 0x4000001EL
|
|
#define STATUS_WX86_BREAKPOINT 0x4000001FL
|
|
#define STATUS_WX86_EXCEPTION_CONTINUE 0x40000020L
|
|
|
|
#pragma pack(push, 1)
|
|
struct HotJumpOp
|
|
{
|
|
uint8 mOpCode;
|
|
int32 mRelTarget;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
#include "BeefySysLib/util/AllocDebug.h"
|
|
|
|
#include <limits>
|
|
|
|
USING_NS_BF_DBG;
|
|
|
|
static void FilterThreadName(String& name)
|
|
{
|
|
for (int i = 0; i < (int)name.length(); i++)
|
|
{
|
|
uint8 c = name[i];
|
|
if (c == 0)
|
|
{
|
|
name.RemoveToEnd(i);
|
|
return;
|
|
}
|
|
|
|
if (c < 32)
|
|
{
|
|
name.Remove(i);
|
|
i--;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool IsHandleValid(HANDLE handle)
|
|
{
|
|
return (handle != NULL) && (handle != INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
WdBreakpointCondition::~WdBreakpointCondition()
|
|
{
|
|
delete mDbgEvaluationContext;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
DbgEvaluationContext::DbgEvaluationContext(WinDebugger* winDebugger, DbgModule* dbgModule, const StringImpl& expr, DwFormatInfo* formatInfo, DbgTypedValue contextValue)
|
|
{
|
|
Init(winDebugger, dbgModule, expr, formatInfo, contextValue);
|
|
}
|
|
|
|
DbgEvaluationContext::DbgEvaluationContext(WinDebugger* winDebugger, DbgCompileUnit* dbgCompileUnit, const StringImpl& expr, DwFormatInfo* formatInfo, DbgTypedValue contextValue)
|
|
{
|
|
DbgModule* dbgModule = NULL;
|
|
if (dbgCompileUnit != NULL)
|
|
dbgModule = dbgCompileUnit->mDbgModule;
|
|
Init(winDebugger, dbgModule, expr, formatInfo, contextValue);
|
|
}
|
|
|
|
void DbgEvaluationContext::Init(WinDebugger* winDebugger, DbgModule* dbgModule, const StringImpl& expr, DwFormatInfo* formatInfo, DbgTypedValue contextValue)
|
|
{
|
|
if (expr.empty())
|
|
{
|
|
mParser = NULL;
|
|
mReducer = NULL;
|
|
mPassInstance = NULL;
|
|
mDbgExprEvaluator = NULL;
|
|
mExprNode = NULL;
|
|
return;
|
|
}
|
|
|
|
mParser = new BfParser(winDebugger->mBfSystem);
|
|
mParser->mCompatMode = true;
|
|
mPassInstance = new BfPassInstance(winDebugger->mBfSystem);
|
|
auto terminatedExpr = expr + ";";
|
|
mParser->SetSource(terminatedExpr.c_str(), terminatedExpr.length());
|
|
mParser->Parse(mPassInstance);
|
|
|
|
mReducer = new BfReducer();
|
|
mReducer->mAlloc = mParser->mAlloc;
|
|
mReducer->mSystem = winDebugger->mBfSystem;
|
|
mReducer->mPassInstance = mPassInstance;
|
|
mReducer->mVisitorPos = BfReducer::BfVisitorPos(mParser->mRootNode);
|
|
mReducer->mVisitorPos.MoveNext();
|
|
mReducer->mCompatMode = mParser->mCompatMode;
|
|
mReducer->mSource = mParser;
|
|
mExprNode = mReducer->CreateExpression(mParser->mRootNode->GetFirst());
|
|
mParser->Close();
|
|
mDbgExprEvaluator = new DbgExprEvaluator(winDebugger, dbgModule, mPassInstance, -1, -1);
|
|
|
|
if ((formatInfo != NULL) && (mExprNode != NULL) && (mExprNode->GetSrcEnd() < (int) expr.length()))
|
|
{
|
|
String formatFlags = expr.Substring(mExprNode->GetSrcEnd());
|
|
String errorString = "Invalid expression";
|
|
if (!winDebugger->ParseFormatInfo(dbgModule, formatFlags, formatInfo, mPassInstance, NULL, NULL, &errorString, contextValue))
|
|
{
|
|
mPassInstance->FailAt(errorString, mParser->mSourceData, mExprNode->GetSrcEnd(), (int)expr.length() - mExprNode->GetSrcEnd());
|
|
formatFlags = "";
|
|
}
|
|
}
|
|
|
|
if (formatInfo != NULL)
|
|
{
|
|
mDbgExprEvaluator->mExplicitThis = formatInfo->mExplicitThis;
|
|
mDbgExprEvaluator->mCallStackIdx = formatInfo->mCallStackIdx;
|
|
mDbgExprEvaluator->mLanguage = formatInfo->mLanguage;
|
|
}
|
|
}
|
|
|
|
bool DbgEvaluationContext::HasExpression()
|
|
{
|
|
return mExprNode != NULL;
|
|
}
|
|
|
|
DbgEvaluationContext::~DbgEvaluationContext()
|
|
{
|
|
delete mParser;
|
|
delete mReducer;
|
|
delete mDbgExprEvaluator;
|
|
delete mPassInstance;
|
|
}
|
|
|
|
DbgTypedValue DbgEvaluationContext::EvaluateInContext(DbgTypedValue contextTypedValue)
|
|
{
|
|
if (mExprNode == NULL)
|
|
return DbgTypedValue();
|
|
mPassInstance->ClearErrors();
|
|
if (contextTypedValue)
|
|
{
|
|
mDbgExprEvaluator->mExplicitThis = contextTypedValue;
|
|
if ((mDbgExprEvaluator->mExplicitThis.mType->IsPointer()) && (mDbgExprEvaluator->mExplicitThis.mType->mTypeParam->WantsRefThis()))
|
|
{
|
|
mDbgExprEvaluator->mExplicitThis.mType = mDbgExprEvaluator->mExplicitThis.mType->mTypeParam;
|
|
mDbgExprEvaluator->mExplicitThis.mSrcAddress = mDbgExprEvaluator->mExplicitThis.mPtr;
|
|
mDbgExprEvaluator->mExplicitThis.mPtr = 0;
|
|
}
|
|
|
|
if ((mDbgExprEvaluator->mExplicitThis.mType->IsCompositeType()) && (!mDbgExprEvaluator->mExplicitThis.mType->WantsRefThis()))
|
|
{
|
|
if (mDbgExprEvaluator->mExplicitThis.mSrcAddress != 0)
|
|
{
|
|
mDbgExprEvaluator->mExplicitThis.mType = mDbgExprEvaluator->mDbgModule->GetPointerType(mDbgExprEvaluator->mExplicitThis.mType);
|
|
mDbgExprEvaluator->mExplicitThis.mPtr = mDbgExprEvaluator->mExplicitThis.mSrcAddress;
|
|
mDbgExprEvaluator->mExplicitThis.mSrcAddress = 0;
|
|
}
|
|
}
|
|
}
|
|
if (contextTypedValue.mType != NULL)
|
|
mDbgExprEvaluator->mDbgCompileUnit = contextTypedValue.mType->mCompileUnit;
|
|
DbgTypedValue exprResult;
|
|
auto result = mDbgExprEvaluator->Resolve(mExprNode);
|
|
return result;
|
|
}
|
|
|
|
bool DbgEvaluationContext::HadError()
|
|
{
|
|
return mPassInstance->mFailedIdx != 0;
|
|
}
|
|
|
|
String DbgEvaluationContext::GetErrorStr()
|
|
{
|
|
String errorStr = mPassInstance->mErrors[0]->mError;
|
|
if (mExprNode != NULL)
|
|
{
|
|
errorStr += ": ";
|
|
errorStr += mExprNode->ToString();
|
|
}
|
|
return errorStr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef HRESULT(WINAPI* SetThreadDescription_t)(HANDLE hThread, PCWSTR lpThreadDescription);
|
|
typedef HRESULT(WINAPI* GetThreadDescription_t)(HANDLE hThread, PWSTR* lpThreadDescription);
|
|
static SetThreadDescription_t gSetThreadDescription = NULL;
|
|
static GetThreadDescription_t gGetThreadDescription = NULL;
|
|
static HMODULE gKernelDll = NULL;
|
|
|
|
static void ImportKernel()
|
|
{
|
|
if (gKernelDll != NULL)
|
|
return;
|
|
|
|
WCHAR path[MAX_PATH];
|
|
GetSystemDirectory(path, MAX_PATH);
|
|
wcscat(path, L"\\kernel32.dll");
|
|
gKernelDll = GetModuleHandle(path);
|
|
if (gKernelDll == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
gSetThreadDescription = (SetThreadDescription_t)GetProcAddress(gKernelDll, "SetThreadDescription");
|
|
gGetThreadDescription = (GetThreadDescription_t)GetProcAddress(gKernelDll, "GetThreadDescription");
|
|
}
|
|
|
|
void WinDebugger::TryGetThreadName(WdThreadInfo* threadInfo)
|
|
{
|
|
if (threadInfo->mHThread == NULL)
|
|
return;
|
|
|
|
ImportKernel();
|
|
|
|
PWSTR wStr = NULL;
|
|
if (gGetThreadDescription != NULL)
|
|
{
|
|
gGetThreadDescription(threadInfo->mHThread, &wStr);
|
|
if (wStr == NULL)
|
|
return;
|
|
|
|
threadInfo->mName = UTF8Encode(wStr);
|
|
FilterThreadName(threadInfo->mName);
|
|
LocalFree(wStr);
|
|
}
|
|
}
|
|
|
|
static void CreateFilterName(String& name, DbgType* type)
|
|
{
|
|
CreateFilterName(name, type->mParent);
|
|
|
|
switch (type->mTypeCode)
|
|
{
|
|
case DbgType_Namespace:
|
|
case DbgType_Struct:
|
|
case DbgType_Class:
|
|
name += type->mName;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void CreateFilterName(String& name, const char* srcStr, DbgLanguage language)
|
|
{
|
|
int chevronDepth = 0;
|
|
const char* cPtr = srcStr;
|
|
for (; true; cPtr++)
|
|
{
|
|
char c = *cPtr;
|
|
if (c == 0)
|
|
break;
|
|
if (c == '>')
|
|
chevronDepth--;
|
|
bool inGeneric = chevronDepth > 0;
|
|
if (c == '<')
|
|
chevronDepth++;
|
|
if (inGeneric) // Bundle all generic instances together
|
|
continue;
|
|
if (c == '[') // Bundle all arrays together
|
|
name.clear();
|
|
if (c == '(')
|
|
return; // Start of params
|
|
if ((c == ':') && (cPtr[1] == ':') && (language == DbgLanguage_Beef))
|
|
{
|
|
name.Append('.');
|
|
cPtr++;
|
|
}
|
|
else
|
|
name.Append(c);
|
|
}
|
|
}
|
|
|
|
static void CreateFilterName(String& name, DbgSubprogram* subprogram)
|
|
{
|
|
auto language = subprogram->GetLanguage();
|
|
if (subprogram->mName == NULL)
|
|
{
|
|
if (subprogram->mLinkName[0] == '<')
|
|
{
|
|
name += subprogram->mLinkName;
|
|
return;
|
|
}
|
|
name = BfDemangler::Demangle(subprogram->mLinkName, language);
|
|
// Strip off the params since we need to generate those ourselves
|
|
int parenPos = (int)name.IndexOf('(');
|
|
if (parenPos != -1)
|
|
name.RemoveToEnd(parenPos);
|
|
return;
|
|
}
|
|
else if (subprogram->mHasQualifiedName)
|
|
{
|
|
const char* cPtr = subprogram->mName;
|
|
if (strncmp(cPtr, "_bf::", 5) == 0)
|
|
{
|
|
CreateFilterName(name, cPtr + 5, DbgLanguage_Beef);
|
|
name.Replace(".__BfStaticCtor", ".this$static");
|
|
name.Replace(".__BfCtorClear", ".this$clear");
|
|
name.Replace(".__BfCtor", ".this");
|
|
}
|
|
else
|
|
CreateFilterName(name, subprogram->mName, language);
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (subprogram->mParentType != NULL)
|
|
{
|
|
String parentName = subprogram->mParentType->ToString();
|
|
CreateFilterName(name, parentName.c_str(), language);
|
|
if (!name.empty())
|
|
{
|
|
if (language == DbgLanguage_Beef)
|
|
name += ".";
|
|
else
|
|
name += "::";
|
|
}
|
|
}
|
|
|
|
if ((language == DbgLanguage_Beef) && (subprogram->mParentType != NULL) && (subprogram->mParentType->mTypeName != NULL) &&
|
|
(strcmp(subprogram->mName, subprogram->mParentType->mTypeName) == 0))
|
|
name += "this";
|
|
else if ((language == DbgLanguage_Beef) && (subprogram->mName[0] == '~'))
|
|
name += "~this";
|
|
else if (strncmp(subprogram->mName, "_bf::", 5) == 0)
|
|
{
|
|
CreateFilterName(name, subprogram->mName + 5, DbgLanguage_Beef);
|
|
}
|
|
else
|
|
{
|
|
CreateFilterName(name, subprogram->mName, language);
|
|
}
|
|
}
|
|
|
|
if (name.empty())
|
|
name += "`anon";
|
|
if ((name[name.length() - 1] == '!') || (name[0] == '<'))
|
|
{
|
|
if (language == DbgLanguage_Beef)
|
|
{
|
|
// It's a mixin - assert that there's no params
|
|
//BF_ASSERT(subprogram->mParams.Size() == 0);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
DbgPendingExpr::DbgPendingExpr()
|
|
{
|
|
mThreadId = -1;
|
|
mCallStackIdx = -1;
|
|
mParser = NULL;
|
|
mCursorPos = -1;
|
|
mExprNode = NULL;
|
|
mIdleTicks = 0;
|
|
mExplitType = NULL;
|
|
mExpressionFlags = DwEvalExpressionFlag_None;
|
|
mUsedSpecifiedLock = false;
|
|
mStackIdxOverride = -1;
|
|
}
|
|
|
|
DbgPendingExpr::~DbgPendingExpr()
|
|
{
|
|
delete mParser;
|
|
}
|
|
|
|
// conversion logic based on table at http://en.wikipedia.org/wiki/Extended_precision
|
|
//CDH TODO put this somewhere more general
|
|
static double ConvertFloat80ToDouble(const byte fp80[10])
|
|
{
|
|
uint16 e = *((uint16*)&fp80[8]);
|
|
uint64 m = *((uint64*)&fp80[0]);
|
|
uint64 bit63 = (uint64)1 << 63;
|
|
uint64 bit62 = (uint64)1 << 62;
|
|
|
|
bool isNegative = (e & 0x8000) != 0;
|
|
double s = isNegative ? -1.0 : 1.0;
|
|
e &= 0x7fff;
|
|
|
|
if (!e)
|
|
{
|
|
// the high bit and mantissa content will determine whether it's an actual zero, or a denormal or
|
|
// pseudo-denormal number with an effective exponent of -16382. But since that exponent is so far
|
|
// below anything we can handle in double-precision (even accounting for denormal bit shifts), we're
|
|
// effectively still dealing with zero.
|
|
return s * 0.0;
|
|
}
|
|
else if (e == 0x7fff)
|
|
{
|
|
if (m & bit63)
|
|
{
|
|
if (m & bit62)
|
|
{
|
|
return std::numeric_limits<double>::quiet_NaN();
|
|
}
|
|
else
|
|
{
|
|
if (m == bit63)
|
|
return s * std::numeric_limits<double>::infinity();
|
|
else
|
|
return std::numeric_limits<double>::signaling_NaN();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return std::numeric_limits<double>::quiet_NaN();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(m & bit63))
|
|
return std::numeric_limits<double>::quiet_NaN(); // unnormal (we don't handle these since 80387 and later treat them as invalid operands anyway)
|
|
|
|
// else is a normalized value
|
|
}
|
|
|
|
int useExponent = (int)e - 16383;
|
|
if (useExponent < -1022)
|
|
return s * 0.0; // we could technically support e from -1023 to -1074 as denormals, but don't bother with that for now.
|
|
else if (useExponent > 1023)
|
|
return s * HUGE_VAL;
|
|
|
|
useExponent += 1023;
|
|
|
|
BF_ASSERT((useExponent > 0) && (useExponent < 0x7ff)); // assume we've filtered for valid exponent range
|
|
BF_ASSERT(m & bit63); // assume we've filtered out values that aren't normalized by now
|
|
|
|
uint64 result = 0;
|
|
if (isNegative)
|
|
result |= bit63;
|
|
result |= (uint64)useExponent << 52;
|
|
result |= (m & ~bit63) >> 11;
|
|
|
|
return *reinterpret_cast<double*>(&result);
|
|
}
|
|
|
|
addr_target NS_BF_DBG::DecodeTargetDataPtr(const char*& strRef)
|
|
{
|
|
addr_target val = (addr_target)stouln(strRef, sizeof(intptr_target) * 2);
|
|
strRef += sizeof(intptr_target) * 2;
|
|
return val;
|
|
}
|
|
|
|
WinDebugger::WinDebugger(DebugManager* debugManager) : mDbgSymSrv(this)
|
|
{
|
|
ZeroMemory(&mProcessInfo, sizeof(mProcessInfo));
|
|
|
|
mActiveHotIdx = -1;
|
|
mGotStartupEvent = false;
|
|
mIsContinuingFromException = false;
|
|
mDestroying = false;
|
|
mDebugManager = debugManager;
|
|
mNeedsRehupBreakpoints = false;
|
|
mStepInAssembly = false;
|
|
mStepSP = 0;
|
|
mStepIsRecursing = false;
|
|
mStepStopOnNextInstruction = false;
|
|
mDebugTarget = NULL;
|
|
mShuttingDown = false;
|
|
mBfSystem = new BfSystem();
|
|
mAtBreakThread = NULL;
|
|
mActiveThread = NULL;
|
|
mActiveBreakpoint = NULL;
|
|
mSteppingThread = NULL;
|
|
mExplicitStopThread = NULL;
|
|
mStepSwitchedThreads = false;
|
|
mIsDebuggerWaiting = false;
|
|
mWantsDebugContinue = false;
|
|
mContinueFromBreakpointFailed = false;
|
|
mIsStepIntoSpecific = false;
|
|
mDbgBreak = false;
|
|
mDebuggerWaitingThread = NULL;
|
|
mStepType = StepType_None;
|
|
mOrigStepType = StepType_None;
|
|
mLastValidStepIntoPC = 0;
|
|
mActiveSymSrvRequest = NULL;
|
|
mStdInputPipe = INVALID_HANDLE_VALUE;
|
|
mStdOutputPipe = INVALID_HANDLE_VALUE;
|
|
mStdErrorPipe = INVALID_HANDLE_VALUE;
|
|
|
|
mStoredReturnValueAddr = 0;
|
|
#ifdef BF_DBG_32
|
|
mCPU = gX86Target->mX86CPU;
|
|
#else
|
|
mCPU = gX86Target->mX64CPU;
|
|
#endif
|
|
mRunState = RunState_NotStarted;
|
|
mIsRunning = false;
|
|
mSavedAtBreakpointAddress = 0;
|
|
mSavedBreakpointAddressContinuing = 0;
|
|
mRequestedStackFrameIdx = 0;
|
|
mShowPCOverride = 0;
|
|
mCurNoInfoStepTries = 0;
|
|
mDbgAttachFlags = BfDbgAttachFlag_None;
|
|
mDbgProcessHandle = 0;
|
|
mDbgThreadHandle = 0;
|
|
mDbgProcessId = 0;
|
|
mDbgHeapData = NULL;
|
|
mIsPartialCallStack = true;
|
|
mHotSwapEnabled = false;
|
|
mOpenFileFlags = DbgOpenFileFlag_None;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
mFreeMemoryBreakIndices.push_back(i);
|
|
}
|
|
mMemoryBreakpointVersion = 0;
|
|
|
|
SYSTEM_INFO systemInfo;
|
|
GetSystemInfo(&systemInfo);
|
|
mPageSize = systemInfo.dwPageSize;
|
|
|
|
mEmptyDebugTarget = new DebugTarget(this);
|
|
mEmptyDebugTarget->CreateEmptyTarget();
|
|
mEmptyDebugTarget->mIsEmpty = true;
|
|
mDebugTarget = mEmptyDebugTarget;
|
|
mDebugPendingExpr = NULL;
|
|
mDebugEvalThreadInfo = WdThreadInfo();
|
|
|
|
mMemCacheAddr = 0;
|
|
mDebuggerThreadId = 0;
|
|
}
|
|
|
|
WinDebugger::~WinDebugger()
|
|
{
|
|
mDestroying = true;
|
|
|
|
delete gDbgPerfManager;
|
|
gDbgPerfManager = NULL;
|
|
|
|
if ((mDebugTarget != NULL) && (mDebugTarget != mEmptyDebugTarget))
|
|
Detach();
|
|
|
|
for (auto breakpoint : mBreakpoints)
|
|
{
|
|
auto checkBreakpoint = breakpoint->mLinkedSibling;
|
|
while (checkBreakpoint != NULL)
|
|
{
|
|
auto nextBreakpoint = checkBreakpoint->mLinkedSibling;
|
|
delete checkBreakpoint;
|
|
checkBreakpoint = nextBreakpoint;
|
|
}
|
|
|
|
delete breakpoint;
|
|
}
|
|
|
|
delete mEmptyDebugTarget;
|
|
|
|
delete mBfSystem;
|
|
for (auto kv : mPendingProfilerMap)
|
|
delete kv.mValue;
|
|
for (auto profiler : mNewProfilerList)
|
|
delete profiler;
|
|
delete mDebugPendingExpr;
|
|
}
|
|
|
|
void WinDebugger::Fail(const StringImpl& error)
|
|
{
|
|
if (mIsRunning)
|
|
mDebugManager->mOutMessages.push_back(StrFormat("error %s", error.c_str()));
|
|
}
|
|
|
|
// Leave active thread unpaused
|
|
void WinDebugger::ThreadRestorePause(WdThreadInfo* onlyPauseThread, WdThreadInfo* dontPauseThread)
|
|
{
|
|
BfLogDbg("ThreadRestorePause %d %d\n", (onlyPauseThread != NULL) ? onlyPauseThread->mThreadId : 0, (dontPauseThread != NULL) ? dontPauseThread->mThreadId : 0);
|
|
for (auto threadInfo : mThreadList)
|
|
{
|
|
if (((threadInfo != dontPauseThread) && (!threadInfo->mIsBreakRestorePaused)) &&
|
|
((onlyPauseThread == NULL) || (threadInfo == onlyPauseThread)))
|
|
{
|
|
BF_ASSERT(!threadInfo->mIsBreakRestorePaused);
|
|
|
|
BfLogDbg("SuspendThread %d\n", threadInfo->mThreadId);
|
|
::SuspendThread(threadInfo->mHThread);
|
|
|
|
threadInfo->mIsBreakRestorePaused = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinDebugger::ThreadRestoreUnpause()
|
|
{
|
|
BfLogDbg("ThreadRestoreUnpause\n");
|
|
for (auto threadInfo : mThreadList)
|
|
{
|
|
if (threadInfo->mIsBreakRestorePaused)
|
|
{
|
|
BfLogDbg("ResumeThread %d\n", threadInfo->mThreadId);
|
|
::ResumeThread(threadInfo->mHThread);
|
|
threadInfo->mIsBreakRestorePaused = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinDebugger::UpdateThreadDebugRegisters(WdThreadInfo* threadInfo)
|
|
{
|
|
if (threadInfo->mMemoryBreakpointVersion == mMemoryBreakpointVersion)
|
|
return;
|
|
|
|
auto threadId = threadInfo->mHThread;
|
|
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_DEBUG_REGISTERS;
|
|
BF_GetThreadContext(threadId, &lcContext);
|
|
for (int memoryBreakIdx = 0; memoryBreakIdx < 4; memoryBreakIdx++)
|
|
{
|
|
WdMemoryBreakpointBind memoryBreakpointBind = mMemoryBreakpoints[memoryBreakIdx];
|
|
|
|
WdBreakpoint* wdBreakpoint = memoryBreakpointBind.mBreakpoint;
|
|
if (wdBreakpoint == NULL)
|
|
{
|
|
*(&lcContext.Dr0 + memoryBreakIdx) = 0;
|
|
lcContext.Dr7 &= ~((1 << (memoryBreakIdx * 2)) | (1 << (16 + memoryBreakIdx * 4)) | (3 << (18 + memoryBreakIdx * 4)));
|
|
}
|
|
else
|
|
{
|
|
int sizeCode = 0;
|
|
if (memoryBreakpointBind.mByteCount == 2)
|
|
sizeCode = 1;
|
|
else if (memoryBreakpointBind.mByteCount == 4)
|
|
sizeCode = 3;
|
|
else if (memoryBreakpointBind.mByteCount == 8)
|
|
sizeCode = 2;
|
|
|
|
addr_target calcAddr = wdBreakpoint->mMemoryBreakpointInfo->mMemoryAddress + memoryBreakpointBind.mOfs;
|
|
BF_ASSERT(calcAddr == memoryBreakpointBind.mAddress);
|
|
|
|
*(&lcContext.Dr0 + memoryBreakIdx) = calcAddr;
|
|
lcContext.Dr7 |= (1 << (memoryBreakIdx * 2)) | (1 << (16 + memoryBreakIdx * 4)) | (sizeCode << (18 + memoryBreakIdx * 4));
|
|
}
|
|
}
|
|
bool worked = BF_SetThreadContext(threadId, &lcContext) != 0;
|
|
BF_ASSERT(worked || (mRunState == RunState_Terminating) || (mRunState == RunState_Terminated));
|
|
threadInfo->mMemoryBreakpointVersion = mMemoryBreakpointVersion;
|
|
}
|
|
|
|
void WinDebugger::UpdateThreadDebugRegisters()
|
|
{
|
|
for (auto threadInfo : mThreadList)
|
|
{
|
|
::SuspendThread(threadInfo->mHThread);
|
|
UpdateThreadDebugRegisters(threadInfo);
|
|
::ResumeThread(threadInfo->mHThread);
|
|
}
|
|
}
|
|
|
|
void WinDebugger::PhysSetBreakpoint(addr_target address)
|
|
{
|
|
BfLogDbg("PhysSetBreakpoint %p\n", address);
|
|
|
|
uint8 newData = 0xCC;
|
|
|
|
// This ensure that we have the orig image data cached
|
|
DbgMemoryFlags flags = mDebugTarget->ReadOrigImageData(address, NULL, 1);
|
|
if ((flags & DbgMemoryFlags_Execute) == 0)
|
|
{
|
|
BfLogDbg("Breakpoint ignored - execute flag NOT set in breakpoint address\n", address);
|
|
|
|
BfLogDbg("Memory Flags = %d\n", gDebugger->GetMemoryFlags(address));
|
|
|
|
return;
|
|
}
|
|
|
|
// Replace it with Breakpoint
|
|
SIZE_T dwReadBytes;
|
|
BOOL worked = ::WriteProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, &newData, 1, &dwReadBytes);
|
|
if (!worked)
|
|
{
|
|
int err = GetLastError();
|
|
BfLogDbg("SetBreakpoint FAILED %p\n", address);
|
|
}
|
|
FlushInstructionCache(mProcessInfo.hProcess, (void*)(intptr)address, 1);
|
|
|
|
{
|
|
uint8 mem = ReadMemory<uint8>(address);
|
|
BfLogDbg("Breakpoint byte %X\n", mem);
|
|
}
|
|
}
|
|
|
|
void WinDebugger::SetBreakpoint(addr_target address, bool fromRehup)
|
|
{
|
|
int* countPtr = NULL;
|
|
if (mPhysBreakpointAddrMap.TryAdd(address, NULL, &countPtr))
|
|
{
|
|
BfLogDbg("SetBreakpoint %p\n", address);
|
|
*countPtr = 1;
|
|
}
|
|
else
|
|
{
|
|
if (fromRehup)
|
|
{
|
|
BfLogDbg("SetBreakpoint %p Count: %d. Rehup (ignored).\n", address, *countPtr);
|
|
return;
|
|
}
|
|
|
|
(*countPtr)++;
|
|
BfLogDbg("SetBreakpoint %p Count: %d\n", address, *countPtr);
|
|
return;
|
|
}
|
|
|
|
PhysSetBreakpoint(address);
|
|
}
|
|
|
|
void WinDebugger::SetTempBreakpoint(addr_target address)
|
|
{
|
|
BfLogDbg("SetTempBreakpoint %p\n", address);
|
|
mTempBreakpoint.push_back(address);
|
|
SetBreakpoint(address);
|
|
}
|
|
|
|
void WinDebugger::PhysRemoveBreakpoint(addr_target address)
|
|
{
|
|
BfLogDbg("PhysRemoveBreakpoint %p\n", address);
|
|
|
|
uint8 origData;
|
|
DbgMemoryFlags flags = mDebugTarget->ReadOrigImageData(address, &origData, 1);
|
|
if ((flags & DbgMemoryFlags_Execute) == 0)
|
|
{
|
|
//BF_ASSERT("Failed" == 0);
|
|
return;
|
|
}
|
|
|
|
SIZE_T dwReadBytes;
|
|
if (!WriteProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, &origData, 1, &dwReadBytes))
|
|
{
|
|
int err = GetLastError();
|
|
BfLogDbg("RemoveBreakpoint FAILED %p\n", address);
|
|
}
|
|
FlushInstructionCache(mProcessInfo.hProcess, (void*)(intptr)address, 1);
|
|
}
|
|
|
|
void WinDebugger::RemoveBreakpoint(addr_target address)
|
|
{
|
|
int* countPtr = NULL;
|
|
mPhysBreakpointAddrMap.TryGetValue(address, &countPtr);
|
|
|
|
// This can happen when we shutdown and we're continuing from a breakpoint
|
|
//BF_ASSERT(*countPtr != NULL);
|
|
if (countPtr == NULL)
|
|
{
|
|
BfLogDbg("RemoveBreakpoint %p FAILED\n", address);
|
|
return;
|
|
}
|
|
|
|
BfLogDbg("RemoveBreakpoint %p count: %d\n", address, *countPtr);
|
|
if (*countPtr > 1)
|
|
{
|
|
(*countPtr)--;
|
|
return;
|
|
}
|
|
mPhysBreakpointAddrMap.Remove(address);
|
|
PhysRemoveBreakpoint(address);
|
|
}
|
|
|
|
void WinDebugger::SingleStepX86()
|
|
{
|
|
// In what cases did this catch bugs?
|
|
// This caused other failures (caught in tests)
|
|
// if (mActiveThread->mIsAtBreakpointAddress != 0)
|
|
// {
|
|
// ContinueFromBreakpoint();
|
|
// return;
|
|
// }
|
|
|
|
BfLogDbg("Setup SingleStepX86 ActiveThread: %d\n", (mActiveThread != NULL) ? mActiveThread->mThreadId : -1);
|
|
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_ALL;
|
|
|
|
BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
lcContext.EFlags |= 0x100; // Set trap flag, which raises "single-step" exception
|
|
BF_SetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
}
|
|
|
|
bool WinDebugger::IsInRunState()
|
|
{
|
|
return (mRunState == RunState_Running) || (mRunState == RunState_Running_ToTempBreakpoint);
|
|
}
|
|
|
|
bool WinDebugger::ContinueFromBreakpoint()
|
|
{
|
|
if (mDebuggerWaitingThread->mFrozen)
|
|
{
|
|
BfLogDbg("ContinueFromBreakpoint bailout on frozen thread\n");
|
|
mDebuggerWaitingThread->mStoppedAtAddress = 0;
|
|
mDebuggerWaitingThread->mIsAtBreakpointAddress = 0;
|
|
return true;
|
|
}
|
|
|
|
mActiveThread = mDebuggerWaitingThread;
|
|
mActiveBreakpoint = NULL;
|
|
|
|
BfLogDbg("ContinueFromBreakpoint. ActiveThread: %d\n", (mActiveThread != NULL) ? mActiveThread->mThreadId : -1);
|
|
|
|
BfLogDbg("ResumeThread %d\n", mActiveThread->mThreadId);
|
|
BOOL success = ::ResumeThread(mActiveThread->mHThread);
|
|
if (success)
|
|
{
|
|
// It's possible the active thread is suspended - possibly by the GC, so we would deadlock if we
|
|
// attempted to pause the other threads
|
|
BfLogDbg("SuspendThread %d\n", mActiveThread->mThreadId);
|
|
BfLogDbg("Thread already paused!\n");
|
|
::SuspendThread(mActiveThread->mHThread);
|
|
return false;
|
|
}
|
|
|
|
ThreadRestorePause(NULL, mActiveThread);
|
|
|
|
PhysRemoveBreakpoint(mActiveThread->mIsAtBreakpointAddress);
|
|
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_ALL;
|
|
|
|
BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
lcContext.EFlags |= 0x100; // Set trap flag, which raises "single-step" exception
|
|
BF_SetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
|
|
mActiveThread->mStoppedAtAddress = 0;
|
|
mActiveThread->mBreakpointAddressContinuing = mActiveThread->mIsAtBreakpointAddress;
|
|
mActiveThread->mIsAtBreakpointAddress = 0;
|
|
BfLogDbg("ContinueFromBreakpoint set mIsAtBreakpointAddress = 0\n");
|
|
return true;
|
|
}
|
|
|
|
void WinDebugger::ValidateBreakpoints()
|
|
{
|
|
HashSet<addr_target> usedBreakpoints;
|
|
|
|
std::function<void(WdBreakpoint*)> _AddBreakpoint = [&](WdBreakpoint* breakpoint)
|
|
{
|
|
if (breakpoint->mAddr != 0)
|
|
{
|
|
usedBreakpoints.Add(breakpoint->mAddr);
|
|
|
|
WdBreakpoint* foundBreakpoint = NULL;
|
|
auto itr = mBreakpointAddrMap.Find(breakpoint->mAddr);
|
|
bool found = false;
|
|
while (itr != mBreakpointAddrMap.end())
|
|
{
|
|
WdBreakpoint* foundBreakpoint = itr->mValue;
|
|
found |= foundBreakpoint == breakpoint;
|
|
itr.NextWithSameKey(breakpoint->mAddr);
|
|
}
|
|
|
|
BF_ASSERT(found);
|
|
}
|
|
|
|
auto checkSibling = (WdBreakpoint*)breakpoint->mLinkedSibling;
|
|
while (checkSibling != NULL)
|
|
{
|
|
_AddBreakpoint(checkSibling);
|
|
checkSibling = (WdBreakpoint*)checkSibling->mLinkedSibling;
|
|
}
|
|
};
|
|
|
|
for (auto breakpoint : mBreakpoints)
|
|
_AddBreakpoint(breakpoint);
|
|
|
|
for (auto& entry : mBreakpointAddrMap)
|
|
{
|
|
BF_ASSERT(usedBreakpoints.Contains(entry.mKey));
|
|
}
|
|
}
|
|
|
|
Breakpoint* WinDebugger::FindBreakpointAt(intptr address)
|
|
{
|
|
#ifdef _DEBUG
|
|
//ValidateBreakpoints();
|
|
#endif
|
|
|
|
WdBreakpoint* breakpoint = NULL;
|
|
mBreakpointAddrMap.TryGetValue(address, &breakpoint);
|
|
return breakpoint;
|
|
}
|
|
|
|
Breakpoint* WinDebugger::GetActiveBreakpoint()
|
|
{
|
|
if ((mActiveBreakpoint != NULL) && (mActiveBreakpoint->mHead != NULL))
|
|
return mActiveBreakpoint->mHead;
|
|
return mActiveBreakpoint;
|
|
}
|
|
|
|
void WinDebugger::DebugThreadProc()
|
|
{
|
|
BpSetThreadName("DebugThread");
|
|
BfpThread_SetName(NULL, "DebugThread", NULL);
|
|
|
|
mDebuggerThreadId = GetCurrentThreadId();
|
|
|
|
if (!IsMiniDumpDebugger())
|
|
{
|
|
if (!DoOpenFile(mLaunchPath, mArgs, mWorkingDir, mEnvBlock, mOpenFileFlags))
|
|
{
|
|
if (mDbgProcessId != 0)
|
|
OutputRawMessage("error Unable to attach to process");
|
|
else
|
|
OutputRawMessage(StrFormat("error Failed to launch: %s", mLaunchPath.c_str()));
|
|
mShuttingDown = true;
|
|
mRunState = RunState_Terminated;
|
|
}
|
|
}
|
|
|
|
while (!mShuttingDown)
|
|
{
|
|
DoUpdate();
|
|
}
|
|
|
|
mIsRunning = false;
|
|
|
|
for (int i = 0; i < (int) mBreakpoints.size(); i++)
|
|
{
|
|
WdBreakpoint* wdBreakpoint = mBreakpoints[i];
|
|
|
|
if (wdBreakpoint->mAddr != 0)
|
|
mBreakpointAddrMap.Remove(wdBreakpoint->mAddr, wdBreakpoint);
|
|
|
|
wdBreakpoint->mAddr = 0;
|
|
wdBreakpoint->mLineData = DbgLineDataEx();
|
|
wdBreakpoint->mSrcFile = NULL;
|
|
if (wdBreakpoint->mLinkedSibling != NULL)
|
|
{
|
|
DeleteBreakpoint(wdBreakpoint->mLinkedSibling);
|
|
wdBreakpoint->mLinkedSibling = NULL;
|
|
}
|
|
}
|
|
|
|
if (!IsMiniDumpDebugger())
|
|
{
|
|
while (true)
|
|
{
|
|
if (!mIsDebuggerWaiting)
|
|
{
|
|
if (!WaitForDebugEvent(&mDebugEvent, 0))
|
|
break;
|
|
}
|
|
if (mDebuggerWaitingThread != NULL)
|
|
{
|
|
BF_ASSERT_REL((mDebuggerWaitingThread->mIsAtBreakpointAddress == 0) || (mShuttingDown));
|
|
::ContinueDebugEvent(mDebuggerWaitingThread->mProcessId, mDebuggerWaitingThread->mThreadId, DBG_CONTINUE);
|
|
BfLogDbg("::ContinueDebugEvent startup ThreadId:%d\n", mDebuggerWaitingThread->mThreadId);
|
|
}
|
|
mIsDebuggerWaiting = false;
|
|
mDebuggerWaitingThread = NULL;
|
|
}
|
|
}
|
|
|
|
mDebuggerThreadId = 0;
|
|
}
|
|
|
|
static void DebugThreadProcThunk(void* winDebugger)
|
|
{
|
|
((WinDebugger*) winDebugger)->DebugThreadProc();
|
|
}
|
|
|
|
int WinDebugger::GetAddrSize()
|
|
{
|
|
return sizeof(addr_target);
|
|
}
|
|
|
|
bool WinDebugger::CanOpen(const StringImpl& fileName, DebuggerResult* outResult)
|
|
{
|
|
FILE* fp = fopen(fileName.c_str(), "rb");
|
|
if (fp == NULL)
|
|
{
|
|
*outResult = DebuggerResult_CannotOpen;
|
|
return false;
|
|
}
|
|
|
|
FileStream fs;
|
|
fs.mFP = fp;
|
|
|
|
*outResult = DebuggerResult_Ok;
|
|
bool canRead = DbgModule::CanRead(&fs, outResult);
|
|
fclose(fp);
|
|
return canRead;
|
|
}
|
|
|
|
void WinDebugger::OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled, DbgOpenFileFlags openFileFlags)
|
|
{
|
|
BF_ASSERT(!mIsRunning);
|
|
mLaunchPath = launchPath;
|
|
mTargetPath = targetPath;
|
|
mArgs = args;
|
|
mWorkingDir = workingDir;
|
|
mEnvBlock = envBlock;
|
|
mHotSwapEnabled = hotSwapEnabled;
|
|
mOpenFileFlags = openFileFlags;
|
|
mDebugTarget = new DebugTarget(this);
|
|
}
|
|
|
|
bool WinDebugger::Attach(int processId, BfDbgAttachFlags attachFlags)
|
|
{
|
|
BF_ASSERT(!mIsRunning);
|
|
|
|
mDbgAttachFlags = attachFlags;
|
|
mDbgProcessHandle = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)processId);
|
|
if (mDbgProcessHandle == 0)
|
|
return false;
|
|
BOOL is32Bit = false;
|
|
if (!IsWow64Process(mDbgProcessHandle, &is32Bit))
|
|
{
|
|
mDbgProcessHandle = 0;
|
|
::CloseHandle(mDbgProcessHandle);
|
|
return false;
|
|
}
|
|
|
|
bool want32Bit = sizeof(intptr_target) == 4;
|
|
if (want32Bit != (is32Bit != 0))
|
|
{
|
|
mDbgProcessHandle = 0;
|
|
::CloseHandle(mDbgProcessHandle);
|
|
return false;
|
|
}
|
|
|
|
HMODULE mainModule = 0;
|
|
DWORD memNeeded = 0;
|
|
::EnumProcessModules(mDbgProcessHandle, &mainModule, sizeof(HMODULE), &memNeeded);
|
|
|
|
WCHAR fileName[MAX_PATH] = {0};
|
|
GetModuleFileNameExW(mDbgProcessHandle, mainModule, fileName, MAX_PATH);
|
|
mLaunchPath = UTF8Encode(fileName);
|
|
mTargetPath = mLaunchPath;
|
|
|
|
mDbgProcessId = processId;
|
|
mDbgProcessHandle = 0;
|
|
::CloseHandle(mDbgProcessHandle);
|
|
|
|
mDebugTarget = new DebugTarget(this);
|
|
|
|
return true;
|
|
}
|
|
|
|
void WinDebugger::GetStdHandles(BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if ((outStdIn != NULL) && (IsHandleValid(mStdInputPipe)))
|
|
{
|
|
*outStdIn = BfpFile_GetFromHandle((intptr)mStdInputPipe, NULL);
|
|
mStdInputPipe = 0;
|
|
}
|
|
|
|
if ((outStdOut != NULL) && (IsHandleValid(mStdOutputPipe)))
|
|
{
|
|
*outStdOut = BfpFile_GetFromHandle((intptr)mStdOutputPipe, NULL);
|
|
mStdOutputPipe = 0;
|
|
}
|
|
|
|
if ((outStdErr != NULL) && (IsHandleValid(mStdErrorPipe)))
|
|
{
|
|
*outStdErr = BfpFile_GetFromHandle((intptr)mStdErrorPipe, NULL);
|
|
mStdErrorPipe = 0;
|
|
}
|
|
}
|
|
|
|
void WinDebugger::Run()
|
|
{
|
|
mIsRunning = true;
|
|
DWORD localThreadId;
|
|
HANDLE hThread = ::CreateThread(NULL, 64 * 1024, (LPTHREAD_START_ROUTINE) &DebugThreadProcThunk, (void*)this, 0, &localThreadId);
|
|
CloseHandle(hThread);
|
|
}
|
|
|
|
bool WinDebugger::HasLoadedTargetBinary()
|
|
{
|
|
if (mDebugTarget == NULL)
|
|
return false;
|
|
return mDebugTarget->mTargetBinary != NULL;
|
|
}
|
|
|
|
void WinDebugger::HotLoad(const Array<String>& objectFiles, int hotIdx)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (mDebugTarget->mTargetBinary == NULL)
|
|
{
|
|
Fail("Hot swapping failed because the hot target binary has not yet been loaded.");
|
|
return;
|
|
}
|
|
|
|
if (mDebugTarget->mHotHeap == NULL)
|
|
{
|
|
Fail("There is no hot heap space available for hot swapping.");
|
|
return;
|
|
}
|
|
|
|
BfLogDbg("WinDebugger::HotLoad Start %d\n", hotIdx);
|
|
|
|
SetAndRestoreValue<int> prevHotIdx(mActiveHotIdx, hotIdx);
|
|
|
|
BF_ASSERT(mHotThreadStates.empty());
|
|
mHotThreadStates.Resize(mThreadList.size());
|
|
for (int threadIdx = 0; threadIdx < (int)mThreadList.size(); threadIdx++)
|
|
{
|
|
WdThreadInfo* threadInfo = mThreadList[threadIdx];
|
|
SetAndRestoreValue<WdThreadInfo*> prevActiveThread(mActiveThread, threadInfo);
|
|
BfLogDbg("SuspendThread %d\n", threadInfo->mThreadId);
|
|
::SuspendThread(threadInfo->mHThread);
|
|
mHotThreadStates[threadIdx].mThreadId = threadInfo->mThreadId;
|
|
PopulateRegisters(&mHotThreadStates[threadIdx].mRegisters);
|
|
}
|
|
|
|
for (auto address : mTempBreakpoint)
|
|
RemoveBreakpoint(address);
|
|
mTempBreakpoint.Clear();
|
|
mStepBreakpointAddrs.Clear();
|
|
for (auto breakpoint : mBreakpoints)
|
|
{
|
|
DetachBreakpoint(breakpoint);
|
|
}
|
|
|
|
int startingModuleIdx = (int)mDebugTarget->mDbgModules.size();
|
|
|
|
bool hasHotVData = false;
|
|
|
|
bool failed = false;
|
|
for (auto fileName : objectFiles)
|
|
{
|
|
if ((fileName.IndexOf("/vdata.") != -1) || (fileName.IndexOf("\\vdata.") != -1))
|
|
hasHotVData = true;
|
|
|
|
BfLogDbg("WinDebugger::HotLoad: %s\n", fileName.c_str());
|
|
DbgModule* newBinary = mDebugTarget->HotLoad(fileName, hotIdx);
|
|
if ((newBinary != NULL) && (newBinary->mFailed))
|
|
failed = true;
|
|
}
|
|
|
|
for (int moduleIdx = startingModuleIdx; moduleIdx < (int)mDebugTarget->mDbgModules.size(); moduleIdx++)
|
|
{
|
|
auto dbgModule = mDebugTarget->mDbgModules[moduleIdx];
|
|
BF_ASSERT(dbgModule->IsObjectFile());
|
|
BF_ASSERT(dbgModule->mHotIdx == hotIdx);
|
|
dbgModule->FinishHotSwap();
|
|
}
|
|
|
|
for (auto dwarf : mDebugTarget->mDbgModules)
|
|
dwarf->RevertWritingEnable();
|
|
|
|
int blockAllocSinceClean = mDebugTarget->mHotHeap->mBlockAllocIdx - mDebugTarget->mLastHotHeapCleanIdx;
|
|
// Clean up the hot heap every 64MB
|
|
int blocksBetweenCleans = (64 * 1024 * 1024) / HotHeap::BLOCK_SIZE;
|
|
|
|
#ifdef _DEBUG
|
|
//TODO: This is just for testing
|
|
blocksBetweenCleans = 1;
|
|
#endif
|
|
|
|
//TODO: Put this back after we fix the cleanup
|
|
if (blockAllocSinceClean >= blocksBetweenCleans)
|
|
CleanupHotHeap();
|
|
|
|
mDebugTarget->RehupSrcFiles();
|
|
|
|
if (hasHotVData)
|
|
mDebugTarget->mVDataHotIdx = hotIdx;
|
|
|
|
for (int breakIdx = 0; breakIdx < (int)mBreakpoints.size(); breakIdx++)
|
|
{
|
|
auto breakpoint = mBreakpoints[breakIdx];
|
|
CheckBreakpoint(breakpoint);
|
|
}
|
|
|
|
for (int hotThreadIdx = 0; hotThreadIdx < (int)mHotThreadStates.size(); hotThreadIdx++)
|
|
{
|
|
auto& hotThreadState = mHotThreadStates[hotThreadIdx];
|
|
WdThreadInfo* threadInfo = NULL;
|
|
if (!mThreadMap.TryGetValue((uint32)hotThreadState.mThreadId, &threadInfo))
|
|
continue;
|
|
|
|
BfLogDbg("ResumeThread %d\n", threadInfo->mThreadId);
|
|
::ResumeThread(threadInfo->mHThread);
|
|
}
|
|
|
|
mHotThreadStates.Clear();
|
|
|
|
if (IsPaused())
|
|
{
|
|
ClearCallStack();
|
|
UpdateCallStack();
|
|
}
|
|
}
|
|
|
|
void WinDebugger::InitiateHotResolve(DbgHotResolveFlags flags)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
delete mHotResolveData;
|
|
mHotResolveData = NULL;
|
|
|
|
mHotResolveData = new DbgHotResolveData();
|
|
DbgHotScanner* hotScanner = new DbgHotScanner(this);
|
|
hotScanner->Scan(flags);
|
|
delete hotScanner;
|
|
}
|
|
|
|
intptr WinDebugger::GetDbgAllocHeapSize()
|
|
{
|
|
if (mDbgHeapData == NULL)
|
|
{
|
|
Beefy::String memName = StrFormat("BFGC_stats_%d", mProcessInfo.dwProcessId);
|
|
|
|
mDbgHeapData = new WinDbgHeapData();
|
|
mDbgHeapData->mFileMapping = ::OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, memName.c_str());
|
|
if (mDbgHeapData->mFileMapping == 0)
|
|
{
|
|
delete mDbgHeapData;
|
|
mDbgHeapData = NULL;
|
|
return 0;
|
|
}
|
|
|
|
mDbgHeapData->mStats = (WinDbgHeapData::Stats*)MapViewOfFile(mDbgHeapData->mFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(WinDbgHeapData::Stats));
|
|
}
|
|
|
|
if (mDbgHeapData->mStats == NULL)
|
|
return 0;
|
|
|
|
return mDbgHeapData->mStats->mHeapSize;
|
|
}
|
|
|
|
String WinDebugger::GetDbgAllocInfo()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
for (auto threadInfo : mThreadList)
|
|
::SuspendThread(threadInfo->mHThread);
|
|
|
|
delete mHotResolveData;
|
|
mHotResolveData = NULL;
|
|
|
|
mHotResolveData = new DbgHotResolveData();
|
|
DbgHotScanner* hotScanner = new DbgHotScanner(this);
|
|
hotScanner->Scan((DbgHotResolveFlags)(DbgHotResolveFlag_Allocations | DbgHotResolveFlag_KeepThreadState));
|
|
delete hotScanner;
|
|
|
|
String result;
|
|
|
|
if (mHotResolveData != NULL)
|
|
{
|
|
DbgExprEvaluator exprEvaluator(this, NULL, NULL, -1, -1);
|
|
exprEvaluator.mDebugTarget = mDebugTarget;
|
|
|
|
String typeName;
|
|
|
|
result += ":types\n";
|
|
|
|
for (int typeId = 0; typeId < mHotResolveData->mTypeData.size(); typeId++)
|
|
{
|
|
auto& typeData = mHotResolveData->mTypeData[typeId];
|
|
if (typeData.mCount > 0)
|
|
{
|
|
auto type = exprEvaluator.GetBeefTypeById(typeId);
|
|
typeName.Clear();
|
|
exprEvaluator.BeefTypeToString(type, typeName);
|
|
if (typeName.IsEmpty())
|
|
typeName = StrFormat("Type #%d", typeId);
|
|
result += StrFormat("type\t%d\t%s\t%lld\t%lld\n", typeId, typeName.c_str(), typeData.mCount, typeData.mSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto threadInfo : mThreadList)
|
|
::ResumeThread(threadInfo->mHThread);
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool CreatePipeWithSecurityAttributes(HANDLE& hReadPipe, HANDLE& hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes, int32 nSize)
|
|
{
|
|
hReadPipe = 0;
|
|
hWritePipe = 0;
|
|
bool ret = ::CreatePipe(&hReadPipe, &hWritePipe, lpPipeAttributes, nSize);
|
|
if (!ret || (hReadPipe == INVALID_HANDLE_VALUE) || (hWritePipe == INVALID_HANDLE_VALUE))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool CreatePipe(HANDLE& parentHandle, HANDLE& childHandle, bool parentInputs)
|
|
{
|
|
SECURITY_ATTRIBUTES securityAttributesParent = { 0 };
|
|
securityAttributesParent.bInheritHandle = 1;
|
|
|
|
HANDLE hTmp = INVALID_HANDLE_VALUE;
|
|
if (parentInputs)
|
|
CreatePipeWithSecurityAttributes(childHandle, hTmp, &securityAttributesParent, 0);
|
|
else
|
|
CreatePipeWithSecurityAttributes(hTmp, childHandle, &securityAttributesParent, 0);
|
|
|
|
HANDLE dupHandle = 0;
|
|
|
|
// Duplicate the parent handle to be non-inheritable so that the child process
|
|
// doesn't have access. This is done for correctness sake, exact reason is unclear.
|
|
// One potential theory is that child process can do something brain dead like
|
|
// closing the parent end of the pipe and there by getting into a blocking situation
|
|
// as parent will not be draining the pipe at the other end anymore.
|
|
if (!::DuplicateHandle(GetCurrentProcess(), hTmp,
|
|
GetCurrentProcess(), &dupHandle,
|
|
0, false, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
parentHandle = dupHandle;
|
|
|
|
if (hTmp != INVALID_HANDLE_VALUE)
|
|
::CloseHandle(hTmp);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WinDebugger::DoOpenFile(const StringImpl& fileName, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, DbgOpenFileFlags openFileFlags)
|
|
{
|
|
BP_ZONE("WinDebugger::DoOpenFile");
|
|
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
//gDbgPerfManager->StartRecording();
|
|
|
|
STARTUPINFOW si;
|
|
ZeroMemory(&si, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
ZeroMemory(&mProcessInfo, sizeof(mProcessInfo));
|
|
|
|
DWORD flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | CREATE_DEFAULT_ERROR_MODE;
|
|
BOOL inheritHandles = false;
|
|
|
|
// set up the streams
|
|
if ((openFileFlags & (DbgOpenFileFlag_RedirectStdInput | DbgOpenFileFlag_RedirectStdOutput | DbgOpenFileFlag_RedirectStdError)) != 0)
|
|
{
|
|
if ((openFileFlags & DbgOpenFileFlag_RedirectStdInput) != 0)
|
|
CreatePipe(mStdInputPipe, si.hStdInput, true);
|
|
else if (::GetConsoleWindow() != NULL)
|
|
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
|
else
|
|
si.hStdInput = INVALID_HANDLE_VALUE;
|
|
|
|
if ((openFileFlags & DbgOpenFileFlag_RedirectStdOutput) != 0)
|
|
CreatePipe(mStdOutputPipe, si.hStdOutput, false);
|
|
else
|
|
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
if ((openFileFlags & DbgOpenFileFlag_RedirectStdError) != 0)
|
|
CreatePipe(mStdErrorPipe, si.hStdError, false);
|
|
else
|
|
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
|
flags |= CREATE_NO_WINDOW;
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
inheritHandles = true;
|
|
}
|
|
|
|
if (mDbgProcessId != 0)
|
|
{
|
|
BOOL success = ::DebugActiveProcess(mDbgProcessId);
|
|
if (!success)
|
|
return false;
|
|
|
|
mProcessInfo.dwProcessId = mDbgProcessId;
|
|
}
|
|
else
|
|
{
|
|
BP_ZONE("DoOpenFile_CreateProcessW");
|
|
|
|
UTF16String envW;
|
|
|
|
void* envPtr = NULL;
|
|
if (!envBlock.IsEmpty())
|
|
{
|
|
//UTF16?
|
|
if (envBlock[1] == 0)
|
|
{
|
|
envPtr = (void*)&envBlock[0];
|
|
flags |= CREATE_UNICODE_ENVIRONMENT;
|
|
}
|
|
else
|
|
{
|
|
String str8((char*)&envBlock[0], (int)envBlock.size());
|
|
envW = UTF8Decode(str8);
|
|
|
|
envPtr = (void*)envW.c_str();
|
|
flags |= CREATE_UNICODE_ENVIRONMENT;
|
|
}
|
|
}
|
|
|
|
String cmdLine = "\"";
|
|
cmdLine += fileName;
|
|
cmdLine += "\"";
|
|
|
|
if (!args.IsEmpty())
|
|
{
|
|
cmdLine += " ";
|
|
cmdLine += args;
|
|
}
|
|
|
|
BOOL worked = CreateProcessW(NULL, (WCHAR*)UTF8Decode(cmdLine).c_str(), NULL, NULL, inheritHandles,
|
|
flags, envPtr, (WCHAR*)UTF8Decode(workingDir).c_str(), &si, &mProcessInfo);
|
|
|
|
if ((openFileFlags & DbgOpenFileFlag_RedirectStdInput) != 0)
|
|
::CloseHandle(si.hStdInput);
|
|
if ((openFileFlags & DbgOpenFileFlag_RedirectStdOutput) != 0)
|
|
::CloseHandle(si.hStdOutput);
|
|
if ((openFileFlags & DbgOpenFileFlag_RedirectStdError) != 0)
|
|
::CloseHandle(si.hStdError);
|
|
|
|
if (!worked)
|
|
{
|
|
if (IsHandleValid(mStdInputPipe))
|
|
{
|
|
::CloseHandle(mStdInputPipe);
|
|
mStdInputPipe = 0;
|
|
}
|
|
if (IsHandleValid(mStdOutputPipe))
|
|
{
|
|
::CloseHandle(mStdOutputPipe);
|
|
mStdOutputPipe = 0;
|
|
}
|
|
if (IsHandleValid(mStdErrorPipe))
|
|
{
|
|
::CloseHandle(mStdErrorPipe);
|
|
mStdErrorPipe = 0;
|
|
}
|
|
|
|
auto lastError = ::GetLastError();
|
|
if (lastError == ERROR_DIRECTORY)
|
|
{
|
|
mDebugManager->mOutMessages.push_back(StrFormat("error Unable to locate specified working directory '%s'", SlashString(workingDir, false, false).c_str()));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
WdThreadInfo* threadInfo = new WdThreadInfo();
|
|
threadInfo->mProcessId = mProcessInfo.dwProcessId;
|
|
threadInfo->mThreadId = mProcessInfo.dwThreadId;
|
|
threadInfo->mHThread = mProcessInfo.hThread;
|
|
threadInfo->mThreadLocalBase = NULL;
|
|
threadInfo->mStartAddress = NULL;
|
|
mThreadMap[mProcessInfo.dwThreadId] = threadInfo;
|
|
mThreadList.push_back(threadInfo);
|
|
}
|
|
|
|
mRunState = RunState_Running;
|
|
|
|
while (true)
|
|
{
|
|
BP_ZONE("DoOpenFile_WaitForImageBase");
|
|
autoCrit.mCritSect->Unlock();
|
|
DoUpdate();
|
|
autoCrit.mCritSect->Lock();
|
|
|
|
ContinueDebugEvent();
|
|
|
|
if ((mDebugTarget->mLaunchBinary != NULL) && (mDebugTarget->mLaunchBinary->mOrigImageData != NULL))
|
|
break;
|
|
}
|
|
|
|
RehupBreakpoints(true);
|
|
|
|
//gDbgPerfManager->StopRecording();
|
|
//gDbgPerfManager->DbgPrint();
|
|
|
|
return true;
|
|
}
|
|
|
|
void WinDebugger::StopDebugging()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
BfLogDbg("WinDebugger::Terminate\n");
|
|
if (mActiveSymSrvRequest != NULL)
|
|
mActiveSymSrvRequest->Cancel();
|
|
if ((mRunState == RunState_NotStarted) || (mRunState == RunState_Terminated) || (mRunState == RunState_Terminating))
|
|
return;
|
|
|
|
if ((mDbgProcessId != 0) && ((mDbgAttachFlags & BfDbgAttachFlag_ShutdownOnExit) == 0))
|
|
{
|
|
for (auto address : mTempBreakpoint)
|
|
RemoveBreakpoint(address);
|
|
for (auto breakpoint : mBreakpoints)
|
|
DetachBreakpoint(breakpoint);
|
|
|
|
BfLogDbg("StopDebugging\n");
|
|
::DebugActiveProcessStop(mDbgProcessId);
|
|
mRunState = RunState_Terminated;
|
|
BfLogDbg("mRunState = RunState_Terminated\n");
|
|
}
|
|
else
|
|
{
|
|
TerminateProcess(mProcessInfo.hProcess, 0);
|
|
mRunState = RunState_Terminating;
|
|
BfLogDbg("mRunState = RunState_Terminating\n");
|
|
}
|
|
}
|
|
|
|
void WinDebugger::Terminate()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
BfLogDbg("WinDebugger::Terminate\n");
|
|
if (mActiveSymSrvRequest != NULL)
|
|
mActiveSymSrvRequest->Cancel();
|
|
if ((mRunState == RunState_NotStarted) || (mRunState == RunState_Terminated) || (mRunState == RunState_Terminating))
|
|
return;
|
|
TerminateProcess(mProcessInfo.hProcess, 0);
|
|
mRunState = RunState_Terminating;
|
|
BfLogDbg("mRunState = RunState_Terminating\n");
|
|
}
|
|
|
|
static int gDebugUpdateCnt = 0;
|
|
|
|
void WinDebugger::Detach()
|
|
{
|
|
BfLogDbg("Debugger Detach\n");
|
|
|
|
mDebugManager->mNetManager->CancelAll();
|
|
while ((mIsRunning) || (mDebuggerThreadId != 0))
|
|
{
|
|
mShuttingDown = true;
|
|
Sleep(1);
|
|
}
|
|
|
|
for (auto profiler : mProfilerSet)
|
|
profiler->Stop();
|
|
|
|
BfLogDbg("Debugger Detach - thread finished\n");
|
|
|
|
mPendingProfilerMap.Clear();
|
|
for (auto profiler : mNewProfilerList)
|
|
delete profiler;
|
|
mNewProfilerList.Clear();
|
|
|
|
mPendingImageLoad.Clear();
|
|
mPendingDebugInfoLoad.Clear();
|
|
|
|
RemoveTempBreakpoints();
|
|
mContinueEvent.Reset();
|
|
if (mDebugTarget != mEmptyDebugTarget)
|
|
delete mDebugTarget;
|
|
mDebugTarget = mEmptyDebugTarget;
|
|
|
|
mShuttingDown = false;
|
|
mStepSP = 0;
|
|
ClearCallStack();
|
|
mRunState = RunState_NotStarted;
|
|
mStepType = StepType_None;
|
|
mHadImageFindError = false;
|
|
mIsPartialCallStack = true;
|
|
|
|
delete mDebugPendingExpr;
|
|
mDebugPendingExpr = NULL;
|
|
|
|
for (auto threadPair : mThreadMap)
|
|
{
|
|
auto threadInfo = threadPair.mValue;
|
|
delete threadInfo;
|
|
}
|
|
mThreadMap.Clear();
|
|
mThreadList.Clear();
|
|
mHotTargetMemory.Clear();
|
|
|
|
// We don't need to close the hThread when we have attached to a process
|
|
if (mDbgProcessId == 0)
|
|
{
|
|
CloseHandle(mProcessInfo.hThread);
|
|
CloseHandle(mProcessInfo.hProcess);
|
|
}
|
|
|
|
for (auto breakpoint : mBreakpoints)
|
|
{
|
|
if (!mDestroying)
|
|
{
|
|
BF_FATAL("Breakpoints should be deleted already");
|
|
}
|
|
|
|
if (breakpoint->mMemoryBreakpointInfo != NULL)
|
|
{
|
|
DetachBreakpoint(breakpoint);
|
|
}
|
|
}
|
|
|
|
ZeroMemory(&mProcessInfo, sizeof(mProcessInfo));
|
|
mStepBreakpointAddrs.Clear();
|
|
mIsRunning = false;
|
|
|
|
mDbgAttachFlags = BfDbgAttachFlag_None;
|
|
mDbgProcessId = 0;
|
|
delete mDbgHeapData;
|
|
mDbgHeapData = NULL;
|
|
mDbgProcessHandle = 0;
|
|
ClearCallStack();
|
|
mWantsDebugContinue = false;
|
|
mAtBreakThread = NULL;
|
|
mActiveThread = NULL;
|
|
mActiveBreakpoint = NULL;
|
|
mSteppingThread = NULL;
|
|
mExplicitStopThread = NULL;
|
|
mIsContinuingFromException = false;
|
|
mGotStartupEvent = false;
|
|
|
|
mIsDebuggerWaiting = false;
|
|
mPhysBreakpointAddrMap.Clear();
|
|
mBreakpointAddrMap.Clear();
|
|
|
|
gDebugUpdateCnt = 0;
|
|
|
|
if (IsHandleValid(mStdInputPipe))
|
|
::CloseHandle(mStdInputPipe);
|
|
mStdInputPipe = INVALID_HANDLE_VALUE;
|
|
|
|
if (IsHandleValid(mStdOutputPipe))
|
|
::CloseHandle(mStdOutputPipe);
|
|
mStdOutputPipe = INVALID_HANDLE_VALUE;
|
|
|
|
if (IsHandleValid(mStdErrorPipe))
|
|
::CloseHandle(mStdErrorPipe);
|
|
mStdErrorPipe = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
Profiler* WinDebugger::StartProfiling()
|
|
{
|
|
return new DbgProfiler(this);
|
|
}
|
|
|
|
Profiler* WinDebugger::PopProfiler()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
if (mNewProfilerList.IsEmpty())
|
|
return NULL;
|
|
auto profiler = (DbgProfiler*)mNewProfilerList[0];
|
|
mNewProfilerList.erase(mNewProfilerList.begin());
|
|
return profiler;
|
|
}
|
|
|
|
void WinDebugger::AddProfiler(DbgProfiler * profiler)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
mProfilerSet.Add(profiler);
|
|
}
|
|
|
|
void WinDebugger::RemoveProfiler(DbgProfiler * profiler)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
mProfilerSet.Remove(profiler);
|
|
}
|
|
|
|
void WinDebugger::ReportMemory(MemReporter* memReporter)
|
|
{
|
|
mEmptyDebugTarget->ReportMemory(memReporter);
|
|
if (mDebugTarget != mEmptyDebugTarget)
|
|
mDebugTarget->ReportMemory(memReporter);
|
|
}
|
|
|
|
bool WinDebugger::GetEmitSource(const StringImpl& filePath, String& outText)
|
|
{
|
|
if (!filePath.StartsWith("$Emit"))
|
|
return false;
|
|
|
|
int dollarPos = filePath.IndexOf('$', 1);
|
|
String numStr = filePath.Substring(5, dollarPos - 5);
|
|
int id = atoi(numStr.c_str());
|
|
|
|
for (auto dbgModule : mDebugTarget->mDbgModules)
|
|
{
|
|
if (dbgModule->mId == id)
|
|
return dbgModule->GetEmitSource(filePath, outText);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void WinDebugger::ModuleChanged(DbgModule* dbgModule)
|
|
{
|
|
mDebugManager->mOutMessages.push_back(String("dbgInfoLoaded ") + dbgModule->mFilePath);
|
|
}
|
|
|
|
bool WinDebugger::DoUpdate()
|
|
{
|
|
if ((mDbgProcessId != 0) && ((mDbgAttachFlags & BfDbgAttachFlag_ShutdownOnExit) == 0))
|
|
::DebugSetProcessKillOnExit(FALSE);
|
|
else
|
|
::DebugSetProcessKillOnExit(TRUE);
|
|
|
|
//
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
auto _ModuleChanged = [&](DbgModule* dbgModule)
|
|
{
|
|
ModuleChanged(dbgModule);
|
|
ClearCallStack(); // We may have actual dbgSubprograms and stuff now...
|
|
};
|
|
|
|
for (auto dbgModule : mPendingImageLoad)
|
|
{
|
|
dbgModule->PreCacheImage();
|
|
}
|
|
|
|
for (auto kv : mPendingDebugInfoLoad)
|
|
{
|
|
kv.mKey->PreCacheDebugInfo();
|
|
}
|
|
|
|
while (!mPendingImageLoad.IsEmpty())
|
|
{
|
|
auto dbgModule = mPendingImageLoad.back();
|
|
mPendingImageLoad.pop_back();
|
|
dbgModule->RequestImage();
|
|
_ModuleChanged(dbgModule);
|
|
}
|
|
|
|
if (!mPendingDebugInfoLoad.IsEmpty())
|
|
{
|
|
Array<DbgPendingDebugInfoLoad> pendingList;
|
|
for (auto kv : mPendingDebugInfoLoad)
|
|
pendingList.Add(kv.mValue);
|
|
mPendingDebugInfoLoad.Clear();
|
|
|
|
for (auto& entry : pendingList)
|
|
{
|
|
auto dbgModule = entry.mModule;
|
|
entry.mModule->RequestDebugInfo(entry.mAllowRemote);
|
|
// We do a "_ModuleChanged" even if the load failed, so we rehup the callstack and stop
|
|
// saying "<Loading...>"
|
|
_ModuleChanged(entry.mModule);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IsMiniDumpDebugger())
|
|
{
|
|
//
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
if (mRunState == RunState_Terminating)
|
|
{
|
|
mRunState = RunState_Terminated;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Sleep(20);
|
|
return false;
|
|
}
|
|
|
|
if (mIsDebuggerWaiting)
|
|
{
|
|
if ((IsInRunState()) || (mRunState == RunState_Terminating) || (mRunState == RunState_DebugEval))
|
|
ContinueDebugEvent();
|
|
if (mContinueEvent.WaitFor(8))
|
|
{
|
|
BF_ASSERT(!mWantsDebugContinue); // mWantsDebugContinue should already been reset
|
|
BfLogDbg("::ContinueDebugEvent 1 ThreadId:%d\n", mDebuggerWaitingThread->mThreadId);
|
|
BF_ASSERT_REL(mDebuggerWaitingThread->mIsAtBreakpointAddress == 0);
|
|
::ContinueDebugEvent(mDebuggerWaitingThread->mProcessId, mDebuggerWaitingThread->mThreadId, mIsContinuingFromException ? DBG_EXCEPTION_NOT_HANDLED : DBG_CONTINUE);
|
|
mIsContinuingFromException = false;
|
|
mIsDebuggerWaiting = false;
|
|
mDebuggerWaitingThread = NULL;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
if (!WaitForDebugEvent(&mDebugEvent, 8))
|
|
return false;
|
|
|
|
gDebugUpdateCnt++;
|
|
|
|
static const char* eventNames[] = { "DBG_EVENT ?",
|
|
"EXCEPTION_DEBUG_EVENT",
|
|
"CREATE_THREAD_DEBUG_EVENT",
|
|
"CREATE_PROCESS_DEBUG_EVENT",
|
|
"EXIT_THREAD_DEBUG_EVENT",
|
|
"EXIT_PROCESS_DEBUG_EVENT",
|
|
"LOAD_DLL_DEBUG_EVENT",
|
|
"UNLOAD_DLL_DEBUG_EVENT",
|
|
"OUTPUT_DEBUG_STRING_EVENT",
|
|
"RIP_EVENT"};
|
|
|
|
BfLogDbg("WaitForDebugEvent %s ThreadId:%d\n", eventNames[mDebugEvent.dwDebugEventCode], mDebugEvent.dwThreadId);
|
|
|
|
BP_ZONE(eventNames[mDebugEvent.dwDebugEventCode]);
|
|
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
mActiveBreakpoint = NULL;
|
|
mIsDebuggerWaiting = true;
|
|
mWantsDebugContinue = true;
|
|
mRequestedStackFrameIdx = 0;
|
|
mBreakStackFrameIdx = 0;
|
|
mShowPCOverride = 0;
|
|
|
|
WdThreadInfo* threadInfo = NULL;
|
|
|
|
mThreadMap.TryGetValue(mDebugEvent.dwThreadId, &threadInfo);
|
|
|
|
mDebuggerWaitingThread = threadInfo;
|
|
mExplicitStopThread = mDebuggerWaitingThread;
|
|
|
|
switch (mDebugEvent.dwDebugEventCode)
|
|
{
|
|
case CREATE_PROCESS_DEBUG_EVENT:
|
|
{
|
|
if (threadInfo == NULL)
|
|
{
|
|
BF_ASSERT(mThreadMap.size() == 0);
|
|
|
|
WdThreadInfo* newThreadInfo = new WdThreadInfo();
|
|
newThreadInfo->mProcessId = mDebugEvent.dwProcessId;
|
|
newThreadInfo->mThreadId = mDebugEvent.dwThreadId;
|
|
newThreadInfo->mHThread = mDebugEvent.u.CreateProcessInfo.hThread;
|
|
newThreadInfo->mThreadLocalBase = mDebugEvent.u.CreateProcessInfo.lpThreadLocalBase;
|
|
newThreadInfo->mStartAddress = (void*)mDebugEvent.u.CreateProcessInfo.lpStartAddress;
|
|
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_CONTROL;
|
|
BF_GetThreadContext(newThreadInfo->mHThread, &lcContext);
|
|
newThreadInfo->mStartSP = BF_CONTEXT_SP(lcContext);
|
|
|
|
mThreadMap[mDebugEvent.dwThreadId] = newThreadInfo;
|
|
mDebuggerWaitingThread = newThreadInfo;
|
|
mThreadList.push_back(mDebuggerWaitingThread);
|
|
UpdateThreadDebugRegisters();
|
|
|
|
if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ThreadCreateMessages))
|
|
OutputMessage(StrFormat("Creating thread from CREATE_PROCESS_DEBUG_EVENT %d\n", mDebugEvent.dwThreadId));
|
|
|
|
threadInfo = mDebuggerWaitingThread;
|
|
mProcessInfo.dwThreadId = threadInfo->mThreadId;
|
|
mProcessInfo.hThread = threadInfo->mHThread;
|
|
mProcessInfo.hProcess = mDebugEvent.u.CreateProcessInfo.hProcess;
|
|
}
|
|
else
|
|
{
|
|
threadInfo->mThreadLocalBase = mDebugEvent.u.CreateProcessInfo.lpThreadLocalBase;
|
|
threadInfo->mStartAddress = (void*)mDebugEvent.u.CreateProcessInfo.lpStartAddress;
|
|
}
|
|
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_CONTROL;
|
|
BF_GetThreadContext(threadInfo->mHThread, &lcContext);
|
|
threadInfo->mStartSP = BF_CONTEXT_SP(lcContext);
|
|
|
|
DbgModule* launchBinary = mDebugTarget->Init(mLaunchPath, mTargetPath, (addr_target)(intptr)mDebugEvent.u.CreateProcessInfo.lpBaseOfImage);
|
|
addr_target gotImageBase = (addr_target)(intptr)mDebugEvent.u.CreateProcessInfo.lpBaseOfImage;
|
|
if (launchBinary->mImageBase != gotImageBase)
|
|
{
|
|
BF_FATAL("Image base didn't match");
|
|
}
|
|
|
|
launchBinary->mImageBase = gotImageBase;
|
|
launchBinary->mImageSize = (int)launchBinary->GetImageSize();
|
|
launchBinary->mOrigImageData = new DbgModuleMemoryCache(launchBinary->mImageBase, launchBinary->mImageSize);
|
|
|
|
if (launchBinary == mDebugTarget->mTargetBinary)
|
|
mDebugTarget->SetupTargetBinary();
|
|
|
|
if (mDebugEvent.u.CreateProcessInfo.hFile != NULL)
|
|
CloseHandle(mDebugEvent.u.CreateProcessInfo.hFile);
|
|
|
|
mDbgProcessHandle = mDebugEvent.u.CreateProcessInfo.hProcess;
|
|
mDbgThreadHandle = mDebugEvent.u.CreateProcessInfo.hThread;
|
|
|
|
mGotStartupEvent = true;
|
|
mDebugManager->mOutMessages.push_back("modulesChanged");
|
|
}
|
|
break;
|
|
case EXIT_PROCESS_DEBUG_EVENT:
|
|
{
|
|
BfLogDbg("EXIT_PROCESS_DEBUG_EVENT\n");
|
|
|
|
DWORD exitCode = mDebugEvent.u.ExitProcess.dwExitCode;
|
|
|
|
String exitMessage;
|
|
switch (exitCode)
|
|
{
|
|
case STATUS_DLL_NOT_FOUND:
|
|
exitMessage = "STATUS_DLL_NOT_FOUND";
|
|
break;
|
|
case STATUS_DLL_INIT_FAILED:
|
|
exitMessage = "STATUS_DLL_INIT_FAILED";
|
|
break;
|
|
case STATUS_ENTRYPOINT_NOT_FOUND:
|
|
exitMessage = "STATUS_ENTRYPOINT_NOT_FOUND";
|
|
break;
|
|
}
|
|
|
|
String exitCodeStr;
|
|
if ((exitCode >= 0x10000000) && (exitCode <= 0xF7000000))
|
|
exitCodeStr = StrFormat("0x%X", exitCode);
|
|
else
|
|
exitCodeStr = StrFormat("%d", exitCode);
|
|
|
|
if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ProcessExitMessages))
|
|
{
|
|
if (!exitMessage.IsEmpty())
|
|
OutputMessage(StrFormat("Process terminated. ExitCode: %s (%s).\n", exitCodeStr.c_str(), exitMessage.c_str()));
|
|
else
|
|
OutputMessage(StrFormat("Process terminated. ExitCode: %s.\n", exitCodeStr.c_str()));
|
|
}
|
|
|
|
mRunState = RunState_Terminated;
|
|
mDebugManager->mOutMessages.push_back("modulesChanged");
|
|
}
|
|
break;
|
|
case LOAD_DLL_DEBUG_EVENT:
|
|
{
|
|
WCHAR moduleNameStr[MAX_PATH] = { 0 };
|
|
GetFinalPathNameByHandleW(mDebugEvent.u.LoadDll.hFile, moduleNameStr, MAX_PATH, FILE_NAME_NORMALIZED);
|
|
|
|
std::wstring wow64Dir;
|
|
std::wstring systemDir;
|
|
|
|
PWSTR wow64DirPtr = NULL;
|
|
SHGetKnownFolderPath(FOLDERID_SystemX86, KF_FLAG_NO_ALIAS, NULL, &wow64DirPtr);
|
|
if (wow64DirPtr != NULL)
|
|
{
|
|
wow64Dir = wow64DirPtr;
|
|
CoTaskMemFree(wow64DirPtr);
|
|
}
|
|
|
|
PWSTR systemDirPtr = NULL;
|
|
SHGetKnownFolderPath(FOLDERID_System, KF_FLAG_NO_ALIAS, NULL, &systemDirPtr);
|
|
if (systemDirPtr != NULL)
|
|
{
|
|
systemDir = systemDirPtr;
|
|
CoTaskMemFree(systemDirPtr);
|
|
}
|
|
|
|
if ((mDebugEvent.u.LoadDll.lpImageName != 0) && (mDebugEvent.u.LoadDll.fUnicode))
|
|
{
|
|
addr_target strAddr = ReadMemory<addr_target>((addr_target)(intptr)mDebugEvent.u.LoadDll.lpImageName);
|
|
|
|
for (int i = 0; i < MAX_PATH - 1; i++)
|
|
{
|
|
WCHAR c = ReadMemory<WCHAR>(strAddr + i*2);
|
|
moduleNameStr[i] = (WCHAR)c;
|
|
if (c == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
String origModuleName = UTF8Encode(moduleNameStr);
|
|
String moduleName = origModuleName;
|
|
|
|
String loadMsg;
|
|
HANDLE altFileHandle = INVALID_HANDLE_VALUE;
|
|
if (moduleName != origModuleName)
|
|
{
|
|
loadMsg = StrFormat("Loading DLL: %s(%s) @ %s", origModuleName.c_str(), moduleName.c_str(), EncodeDataPtr((addr_target)(intptr)mDebugEvent.u.LoadDll.lpBaseOfDll, true).c_str());
|
|
altFileHandle = ::CreateFileW(UTF8Decode(moduleName).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
}
|
|
else
|
|
{
|
|
loadMsg = StrFormat("Loading DLL: %s @ %s", moduleName.c_str(), EncodeDataPtr((addr_target)(intptr)mDebugEvent.u.LoadDll.lpBaseOfDll, true).c_str());
|
|
}
|
|
|
|
BfLogDbg("LOAD_DLL_DEBUG_EVENT %s\n", moduleName.c_str());
|
|
|
|
bool skipLoad = false;
|
|
#ifdef BF_DBG_32
|
|
if (((uintptr)mDebugEvent.u.LoadDll.lpBaseOfDll & 0xFFFFFFFF00000000LL) != 0)
|
|
{
|
|
skipLoad = true;
|
|
loadMsg += " - Skipped";
|
|
}
|
|
#endif
|
|
|
|
if (!skipLoad)
|
|
{
|
|
FileHandleStream stream;
|
|
stream.mFileHandle = mDebugEvent.u.LoadDll.hFile;
|
|
if (altFileHandle != INVALID_HANDLE_VALUE)
|
|
stream.mFileHandle = altFileHandle;
|
|
if (mDebugTarget->SetupDyn(moduleName, &stream, (intptr)mDebugEvent.u.LoadDll.lpBaseOfDll) == NULL)
|
|
loadMsg += " - Failed to load";
|
|
stream.mFileHandle = 0;
|
|
}
|
|
|
|
if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ModuleLoadMessages))
|
|
OutputMessage(loadMsg + "\n");
|
|
|
|
if (altFileHandle != INVALID_HANDLE_VALUE)
|
|
::CloseHandle(altFileHandle);
|
|
::CloseHandle(mDebugEvent.u.LoadDll.hFile);
|
|
|
|
// Try to bind any breakpoints tied to this DLL
|
|
RehupBreakpoints(true);
|
|
mDebugManager->mOutMessages.push_back("modulesChanged");
|
|
}
|
|
break;
|
|
case UNLOAD_DLL_DEBUG_EVENT:
|
|
{
|
|
bool needsBreakpointRehup = false;
|
|
|
|
String name = "???";
|
|
DbgModule* dbgModule = mDebugTarget->FindDbgModuleForAddress((addr_target)(intptr)mDebugEvent.u.UnloadDll.lpBaseOfDll);
|
|
if (dbgModule != NULL)
|
|
{
|
|
name = dbgModule->mFilePath;
|
|
|
|
for (int i = 0; i < (int)mBreakpoints.size(); i++)
|
|
{
|
|
auto breakpoint = mBreakpoints[i];
|
|
auto checkBreakpoint = breakpoint;
|
|
bool hasAddr = false;
|
|
while (checkBreakpoint != NULL)
|
|
{
|
|
if ((checkBreakpoint->mAddr >= dbgModule->mImageBase) && (checkBreakpoint->mAddr < dbgModule->mImageBase + dbgModule->mImageSize))
|
|
hasAddr = true;
|
|
checkBreakpoint = (WdBreakpoint*)checkBreakpoint->mLinkedSibling;
|
|
}
|
|
|
|
if (hasAddr)
|
|
{
|
|
DetachBreakpoint(breakpoint);
|
|
needsBreakpointRehup = true;
|
|
}
|
|
}
|
|
|
|
bool hadTarget = mDebugTarget->mTargetBinary != NULL;
|
|
mDebugTarget->UnloadDyn(dbgModule->mImageBase);
|
|
if (needsBreakpointRehup)
|
|
RehupBreakpoints(true);
|
|
|
|
if ((mDebugTarget->mTargetBinary == NULL) && (hadTarget))
|
|
{
|
|
mRunState = RunState_TargetUnloaded;
|
|
}
|
|
|
|
mPendingDebugInfoLoad.Remove(dbgModule);
|
|
mPendingDebugInfoRequests.Remove(dbgModule);
|
|
mDebugManager->mOutMessages.push_back("modulesChanged");
|
|
}
|
|
|
|
if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ModuleUnloadMessages) && !name.empty())
|
|
OutputMessage(StrFormat("Unloading DLL: %s @ %0s\n", name.c_str(), EncodeDataPtr((addr_target)(intptr)mDebugEvent.u.UnloadDll.lpBaseOfDll, true).c_str()));
|
|
|
|
BfLogDbg("UNLOAD_DLL_DEBUG_EVENT %s\n", name.c_str());
|
|
}
|
|
break;
|
|
case OUTPUT_DEBUG_STRING_EVENT:
|
|
{
|
|
const int maxChars = 1024 * 1024;
|
|
int len = BF_MIN(maxChars, (int)mDebugEvent.u.DebugString.nDebugStringLength); // 1MB max
|
|
char* message = new char[len + 1];
|
|
|
|
message[0] = 0;
|
|
message[len] = 0;
|
|
ReadMemory((addr_target)(intptr)mDebugEvent.u.DebugString.lpDebugStringData, len, message);
|
|
|
|
if ((mRunState == RunState_DebugEval) && (mDebuggerWaitingThread->mThreadId == mDebugEvalThreadInfo.mThreadId))
|
|
mDebugManager->mOutMessages.push_back(String("dbgEvalMsg ") + message);
|
|
else
|
|
mDebugManager->mOutMessages.push_back(String("msg ") + message);
|
|
|
|
BfLogDbg("OUTPUT_DEBUG_STRING_EVENT (BreakAddr:%@): %s\n", threadInfo->mIsAtBreakpointAddress, message);
|
|
|
|
BF_ASSERT_REL(threadInfo->mIsAtBreakpointAddress == 0);
|
|
|
|
delete [] message;
|
|
}
|
|
break;
|
|
case CREATE_THREAD_DEBUG_EVENT:
|
|
{
|
|
WdThreadInfo* threadInfo = new WdThreadInfo();
|
|
threadInfo->mProcessId = mDebugEvent.dwProcessId;
|
|
threadInfo->mThreadId = mDebugEvent.dwThreadId;
|
|
threadInfo->mHThread = mDebugEvent.u.CreateThread.hThread;
|
|
threadInfo->mThreadLocalBase = mDebugEvent.u.CreateThread.lpThreadLocalBase;
|
|
threadInfo->mStartAddress = (void*)mDebugEvent.u.CreateThread.lpStartAddress;
|
|
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_CONTROL;
|
|
BF_GetThreadContext(threadInfo->mHThread, &lcContext);
|
|
threadInfo->mStartSP = BF_CONTEXT_SP(lcContext);
|
|
|
|
mThreadMap[mDebugEvent.dwThreadId] = threadInfo;
|
|
mDebuggerWaitingThread = threadInfo;
|
|
mThreadList.push_back(mDebuggerWaitingThread);
|
|
UpdateThreadDebugRegisters();
|
|
if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ThreadCreateMessages))
|
|
OutputMessage(StrFormat("Creating thread %d\n", mDebugEvent.dwThreadId));
|
|
}
|
|
break;
|
|
case EXIT_THREAD_DEBUG_EVENT:
|
|
{
|
|
if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ThreadExitMessages))
|
|
OutputMessage(StrFormat("Exiting thread %d\n", mDebugEvent.dwThreadId));
|
|
|
|
if (mSteppingThread == threadInfo)
|
|
{
|
|
// We were attempting stepping on this thread, but not anymore!
|
|
ClearStep();
|
|
}
|
|
::ContinueDebugEvent(mDebuggerWaitingThread->mProcessId, mDebuggerWaitingThread->mThreadId, DBG_CONTINUE);
|
|
mIsDebuggerWaiting = false;
|
|
mWantsDebugContinue = false;
|
|
|
|
if ((mRunState == RunState_DebugEval) && (mDebuggerWaitingThread->mThreadId == mDebugEvalThreadInfo.mThreadId))
|
|
{
|
|
// Thread terminated while evaluating! Is there a more graceful way of handling this?
|
|
CleanupDebugEval(false);
|
|
mRunState = RunState_Running;
|
|
}
|
|
|
|
mThreadList.Remove(mDebuggerWaitingThread);
|
|
|
|
delete mDebuggerWaitingThread;
|
|
mDebuggerWaitingThread = NULL;
|
|
mThreadMap.Remove(mDebugEvent.dwThreadId);
|
|
return true;
|
|
}
|
|
break;
|
|
case RIP_EVENT:
|
|
OutputMessage("RIP Event\n");
|
|
break;
|
|
case EXCEPTION_DEBUG_EVENT:
|
|
{
|
|
auto exceptionRecord = &mDebugEvent.u.Exception.ExceptionRecord;
|
|
|
|
switch (exceptionRecord->ExceptionCode)
|
|
{
|
|
case STATUS_WX86_BREAKPOINT:
|
|
case EXCEPTION_BREAKPOINT:
|
|
{
|
|
if (mRunState == RunState_Terminating)
|
|
{
|
|
BfLogDbg("Ignoring event because of RunState_Terminating\n");
|
|
break;
|
|
}
|
|
|
|
mAtBreakThread = threadInfo;
|
|
mActiveThread = mAtBreakThread;
|
|
|
|
bool isHighAddr = false;
|
|
#ifdef BF_DBG_32
|
|
if (((uintptr)exceptionRecord->ExceptionAddress & 0xFFFFFFFF00000000) != 0)
|
|
{
|
|
if (mActiveThread == mThreadList.front())
|
|
{
|
|
// Skip the initial Wow64 ntdll.dll!LdrpDoDebuggerBreak
|
|
mRunState = RunState_Running;
|
|
break;
|
|
}
|
|
isHighAddr = true;
|
|
}
|
|
#endif
|
|
|
|
addr_target pcAddress = (addr_target)(intptr)exceptionRecord->ExceptionAddress;
|
|
if (isHighAddr)
|
|
pcAddress = (addr_target)-1;
|
|
//mStoppedAtAddress = pcAddress;
|
|
|
|
bool isStepOut = false;
|
|
|
|
if ((mStepType == StepType_StepOut) || (mStepType == StepType_StepOut_ThenInto))
|
|
{
|
|
isStepOut = mStepBreakpointAddrs.Contains(pcAddress);
|
|
}
|
|
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_ALL;
|
|
BF_GetThreadContext(threadInfo->mHThread, &lcContext);
|
|
|
|
BfLogDbg("EXCEPTION_BREAKPOINT Thread:%d %p SP:%p\n", mActiveThread->mThreadId, pcAddress, BF_CONTEXT_SP(lcContext));
|
|
|
|
uint8 origImageData = 0xCC;
|
|
mDebugTarget->ReadOrigImageData(pcAddress, &origImageData, 1);
|
|
bool wasDebugBreakpoint = origImageData != 0xCC;
|
|
|
|
DbgSubprogram* dwSubprogram = NULL;
|
|
DbgLineData* dwLineData = NULL;
|
|
if (!isStepOut)
|
|
{
|
|
dwLineData = FindLineDataAtAddress(pcAddress, &dwSubprogram, NULL, NULL, DbgOnDemandKind_LocalOnly);
|
|
if (dwSubprogram == NULL)
|
|
dwSubprogram = mDebugTarget->FindSubProgram(pcAddress, DbgOnDemandKind_LocalOnly);
|
|
}
|
|
|
|
bool isLineStart = (dwLineData != NULL) && (dwSubprogram->GetLineAddr(*dwLineData) == pcAddress);
|
|
bool isNonDebuggerBreak = false;
|
|
|
|
if (wasDebugBreakpoint)
|
|
{
|
|
// Go ahead and set EIP back one instruction
|
|
BF_CONTEXT_IP(lcContext)--;
|
|
BF_SetThreadContext(threadInfo->mHThread, &lcContext);
|
|
|
|
if ((dwSubprogram != NULL) && (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Invalid) &&
|
|
(pcAddress == dwSubprogram->mBlock.mLowPC))
|
|
{
|
|
BfLogDbg("Hit HotReplaceKind_Invalid breakpoint\n");
|
|
mRunState = RunState_Paused;
|
|
mDebugManager->mOutMessages.push_back("error This lambda was replaced by a new version that has incompatible captures. A program restart is required.");
|
|
PhysRemoveBreakpoint(pcAddress);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This was an actual "break" instruction
|
|
BfLogDbg("Non-debugger break\n");
|
|
isNonDebuggerBreak = true;
|
|
|
|
auto prevState = mRunState;
|
|
|
|
// Make it an "auto" stop, so for example when we have an assert/retry we won't stop inside assembly
|
|
mRequestedStackFrameIdx = -2;
|
|
|
|
mRunState = RunState_Paused;
|
|
CheckNonDebuggerBreak();
|
|
if (IsInRunState())
|
|
{
|
|
BF_ASSERT((prevState == RunState_Running) || (prevState == RunState_DebugEval));
|
|
mRunState = prevState;
|
|
break; // Continue as if nothing happened
|
|
}
|
|
|
|
if (prevState == RunState_DebugEval)
|
|
mRequestedStackFrameIdx = -1; // Don't show a rolled back stack idx if a debug eval fails
|
|
|
|
ClearStep();
|
|
}
|
|
|
|
if (threadInfo->mIsBreakRestorePaused)
|
|
{
|
|
// The thread is supposed to be paused, but the IP has been reset
|
|
// so just break here so we'll hit that breakpoint again once we're
|
|
// actually unpaused properly
|
|
BfLogDbg("Ignoring EXCEPTION_BREAKPOINT\n", threadInfo->mThreadId);
|
|
break;
|
|
}
|
|
|
|
if ((mRunState == RunState_DebugEval) || (mRunState == RunState_HotStep))
|
|
{
|
|
// If we hit a breakpoint while doing a debug eval, we just remove the breakpoint
|
|
// and expect to reinstate it during a rehup after the evaluation has completed
|
|
WdBreakpoint* breakpoint = (WdBreakpoint*)FindBreakpointAt((uintptr_t) exceptionRecord->ExceptionAddress);
|
|
if (breakpoint != NULL)
|
|
{
|
|
mNeedsRehupBreakpoints = true;
|
|
RemoveBreakpoint(breakpoint->mLineData.GetAddress());
|
|
}
|
|
break;
|
|
}
|
|
|
|
bool isDeeper = false;
|
|
|
|
int stepBreakAddrIdx = (int)mStepBreakpointAddrs.IndexOf(pcAddress);
|
|
|
|
WdBreakpoint* breakpoint = NULL;
|
|
bool ignoreBreakpoint = false;
|
|
|
|
if ((mStepType != StepType_None) && (mSteppingThread == mAtBreakThread))
|
|
{
|
|
if (mStepType == StepType_ToTempBreakpoint)
|
|
{
|
|
RemoveTempBreakpoints();
|
|
mRunState = RunState_Paused;
|
|
break;
|
|
}
|
|
|
|
if (mContinueFromBreakpointFailed)
|
|
{
|
|
BfLogDbg("Continuing from ContinueFromBreakpointFailed\n");
|
|
SetupStep(mStepType);
|
|
mRunState = RunState_Running;
|
|
break;
|
|
}
|
|
|
|
if (!isStepOut)
|
|
breakpoint = (WdBreakpoint*)FindBreakpointAt(pcAddress);
|
|
|
|
// Ignore breakpoint if it's on the line we're stepping off of
|
|
if ((breakpoint != NULL) && (breakpoint->mAddr == mStepPC) &&
|
|
(mStepSP == BF_CONTEXT_SP(lcContext)))
|
|
{
|
|
ignoreBreakpoint = true;
|
|
}
|
|
else if ((breakpoint != NULL) && (stepBreakAddrIdx == -1) && (!CheckConditionalBreakpoint(breakpoint, dwSubprogram, pcAddress)))
|
|
{
|
|
ignoreBreakpoint = true;
|
|
}
|
|
|
|
if ((stepBreakAddrIdx == -1) && (breakpoint == NULL) && (!isNonDebuggerBreak))
|
|
{
|
|
// If a breakpoint is removed in a prior thread
|
|
BfLogDbg("Ignoring step break (old breakpoint)\n");
|
|
|
|
if ((mSteppingThread == mAtBreakThread) && (mStepSwitchedThreads))
|
|
{
|
|
SetupStep(mStepType);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ((stepBreakAddrIdx != -1) && (breakpoint == NULL) && (mSteppingThread != mActiveThread))
|
|
{
|
|
BfLogDbg("Ignoring break (wrong thread)\n");
|
|
ThreadRestorePause(mSteppingThread, mActiveThread);
|
|
threadInfo->mIsAtBreakpointAddress = pcAddress;
|
|
break;
|
|
}
|
|
|
|
isDeeper = mStepSP > BF_CONTEXT_SP(lcContext);
|
|
if ((mStepType == StepType_StepOut) || (mStepType == StepType_StepOut_ThenInto))
|
|
{
|
|
isDeeper = mStepSP >= BF_CONTEXT_SP(lcContext);
|
|
BfLogDbg("StepOut Iteration SP:%p StartSP:%p IsDeeper:%d\n", BF_CONTEXT_SP(lcContext), mStepSP, isDeeper);
|
|
}
|
|
|
|
if (((mStepType == StepType_StepOut) || (mStepType == StepType_StepOut_ThenInto)) && (breakpoint == NULL) && (isDeeper))
|
|
{
|
|
// We're encountered recursion
|
|
|
|
// Make sure we don't already have one of these stored
|
|
BF_ASSERT(mStoredReturnValueAddr == 0);
|
|
threadInfo->mIsAtBreakpointAddress = pcAddress;
|
|
break; // Don't fall through, we don't want to set mIsAtBreakpointAddress
|
|
}
|
|
|
|
if (isStepOut)
|
|
{
|
|
threadInfo->mIsAtBreakpointAddress = pcAddress;
|
|
|
|
if (mStepType == StepType_StepOut_ThenInto)
|
|
{
|
|
dwLineData = FindLineDataAtAddress(pcAddress, &dwSubprogram, NULL, NULL, DbgOnDemandKind_LocalOnly);
|
|
if ((dwLineData != NULL) && (pcAddress == dwSubprogram->GetLineAddr(*dwLineData)))
|
|
{
|
|
// Our step out from a filtered function put us at the start of a new line. Stop here
|
|
// <do nothing>
|
|
}
|
|
else
|
|
{
|
|
// .. otherwise keep going until we get to the start of a new line
|
|
SetupStep(StepType_StepInto);
|
|
mRunState = RunState_Running;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!mStepInAssembly)
|
|
{
|
|
// Keep stepping out until we find a frame that we have source for
|
|
DbgSubprogram* dwSubprogram = NULL;
|
|
DbgLineData* dwLineData = FindLineDataAtAddress(BF_CONTEXT_IP(lcContext), &dwSubprogram);
|
|
if (dwLineData == NULL)
|
|
{
|
|
SetupStep(StepType_StepOut);
|
|
break;
|
|
}
|
|
|
|
if ((dwLineData->mColumn == -1) && (!dwSubprogram->HasValidLines()))
|
|
{
|
|
// This is a method we don't actually want to be in, it has no valid lines!
|
|
SetupStep(StepType_StepOut);
|
|
break;
|
|
}
|
|
|
|
if ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC))
|
|
{
|
|
// We've stepped out, but right into the start of an inlined method, so step out of this inlined method now...
|
|
SetupStep(StepType_StepOut);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ClearStep();
|
|
mRunState = RunState_Paused;
|
|
threadInfo->mStoppedAtAddress = pcAddress;
|
|
|
|
break;
|
|
}
|
|
|
|
mRunState = RunState_Paused;
|
|
if (breakpoint != NULL)
|
|
{
|
|
// While stepping we hit a legit breakpoint
|
|
threadInfo->mIsAtBreakpointAddress = breakpoint->mAddr;
|
|
|
|
// Ignore breakpoint on return statement if we're return-stepping
|
|
mRunState = RunState_Breakpoint;
|
|
}
|
|
|
|
if ((mStepType == StepType_StepInto) && (dwSubprogram != NULL))
|
|
{
|
|
// Don't filter out the current subprogram (would break cases where we explicitly stepped into or hit breakpoint in a filtered subprogram)
|
|
bool isInStartSubprogram = (mStepStartPC >= dwSubprogram->mBlock.mLowPC) && (mStepStartPC < dwSubprogram->mBlock.mHighPC);
|
|
if ((!isInStartSubprogram) && (IsStepFiltered(dwSubprogram, dwLineData)))
|
|
{
|
|
BfLogDbg("Hit step filter\n");
|
|
mRunState = RunState_Running;
|
|
SetupStep(StepType_StepOut_ThenInto);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((mStepType == StepType_StepOver) && (stepBreakAddrIdx == 0) && (mStepBreakpointAddrs[0] != 0) && (mStepBreakpointAddrs.size() > 1))
|
|
{
|
|
// Break was on the 'call' instruction, not the instruction after it -- means recursion
|
|
BfLogDbg("StepOver detected recursing\n");
|
|
mStepIsRecursing = true;
|
|
if (mTempBreakpoint.Remove(mStepBreakpointAddrs[0]))
|
|
{
|
|
RemoveBreakpoint(mStepBreakpointAddrs[0]);
|
|
}
|
|
mStepBreakpointAddrs[0] = 0;
|
|
mRunState = RunState_Running;
|
|
break;
|
|
}
|
|
|
|
if ((mStepType == StepType_StepOver) && (stepBreakAddrIdx > 0) && (mStepBreakpointAddrs[0] != 0) && (isDeeper))
|
|
{
|
|
// This is the first time we've hit the target breakpoint.
|
|
if (HasSteppedIntoCall())
|
|
{
|
|
mStepIsRecursing = true;
|
|
RemoveBreakpoint(mStepBreakpointAddrs[0]);
|
|
mStepBreakpointAddrs[0] = 0;
|
|
//mStepBreakpointAddrs.erase(mStepBreakpointAddrs.begin());
|
|
}
|
|
}
|
|
|
|
if ((mStepType == StepType_StepOver) && (mStepIsRecursing) && (stepBreakAddrIdx != -1) && (isDeeper))
|
|
{
|
|
// Decrement so the equality test on "step out" marks us as not being deeper when we
|
|
// hit the expected SP
|
|
BfLogDbg("Converting StepOver to StepOut\n");
|
|
mStepSP--;
|
|
mStepType = StepType_StepOut_ThenInto;
|
|
//SetupStep(StepType_StepOut);
|
|
mRunState = RunState_Running;
|
|
break;
|
|
}
|
|
|
|
if ((mStepType == StepType_StepOver) && (!ignoreBreakpoint) && (breakpoint == NULL) && (!mStepInAssembly))
|
|
{
|
|
// Test for stepping over inline method
|
|
DbgSubprogram* dwSubprogram = mDebugTarget->FindSubProgram(pcAddress);
|
|
|
|
// mTempBreakpoints will have 2 entries if we are on a 'call' line. If we have an inlined call immediately following a call, then we
|
|
// assume we're hitting a return break
|
|
/*if ((dwSubprogram != NULL) && (dwSubprogram->mInlineParent != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC) && (mTempBreakpoint.size() < 2))
|
|
{
|
|
BfLogDbg("Attempting StepOver of inlined method\n");
|
|
SetupStep(StepType_StepOut);
|
|
mRunState = RunState_Running;
|
|
break;
|
|
} */
|
|
|
|
//TODO: The previous logic with the "(mTempBreakpoint.size() < 2)" was causing Try!(Method()); stepovers to enter into Try!. What did we mean by
|
|
// "assume we're hitting a return break"?
|
|
if ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC))
|
|
{
|
|
RemoveTempBreakpoints();
|
|
BfLogDbg("Attempting StepOver of inlined method\n");
|
|
SetupStep(StepType_StepOut);
|
|
mRunState = RunState_Running;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mStepType == StepType_StepOut_Inline)
|
|
{
|
|
if (mOrigStepType == StepType_StepOver)
|
|
{
|
|
// For the step over, if we are still inside the source line after an inline then step over again...
|
|
DbgSubprogram* origSubprogram = NULL;
|
|
auto origLineData = FindLineDataAtAddress(mStepStartPC, &origSubprogram);
|
|
DbgSubprogram* curSubprogram = NULL;
|
|
auto curLineData = FindLineDataAtAddress(pcAddress, &curSubprogram);
|
|
if ((origLineData != NULL) &&
|
|
((origLineData == curLineData) ||
|
|
((origSubprogram == curSubprogram) && (origLineData->mLine == curLineData->mLine))))
|
|
{
|
|
mRunState = RunState_Running;
|
|
SetupStep(StepType_StepOver);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ClearStep();
|
|
break;
|
|
}
|
|
|
|
if ((mStepType != StepType_None) && (ignoreBreakpoint) && (!mStepInAssembly) && (stepBreakAddrIdx == -1))
|
|
{
|
|
// Ignore breakpoint by just continuing...
|
|
mRunState = RunState_Running;
|
|
break;
|
|
}
|
|
|
|
RemoveTempBreakpoints();
|
|
|
|
if ((mStepType != StepType_None) && (!mStepInAssembly) && (!isLineStart) && (stepBreakAddrIdx != -1))
|
|
{
|
|
SetupStep(mStepType);
|
|
mRunState = RunState_Running;
|
|
}
|
|
else
|
|
{
|
|
//if (mStepType != StepType_Return)
|
|
if (stepBreakAddrIdx != -1)
|
|
{
|
|
// Even if we've detected we're at a breakpoint, we mark ourselves as just stepping if we also
|
|
// have a step breakpoint here
|
|
StepLineTryPause(pcAddress, true);
|
|
}
|
|
|
|
if (mRunState == RunState_Paused)
|
|
ClearStep();
|
|
}
|
|
|
|
if (ignoreBreakpoint)
|
|
{
|
|
SetupStep(mStepType);
|
|
mRunState = RunState_Running;
|
|
}
|
|
|
|
if ((mRunState == RunState_Paused) && (breakpoint != NULL))
|
|
{
|
|
// Just do the 'check' here so we can do the logging/condition stuff
|
|
CheckConditionalBreakpoint(breakpoint, dwSubprogram, pcAddress);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
breakpoint = (WdBreakpoint*)FindBreakpointAt((uintptr_t)exceptionRecord->ExceptionAddress);
|
|
if ((breakpoint != NULL) && (!CheckConditionalBreakpoint(breakpoint, dwSubprogram, pcAddress)))
|
|
{
|
|
ClearCallStack();
|
|
BfLogDbg("Skipping conditional breakpoint. Setting mIsAtBreakpointAddress = %p\n", breakpoint->mAddr);
|
|
threadInfo->mIsAtBreakpointAddress = breakpoint->mAddr;
|
|
mRunState = RunState_Running;
|
|
break;
|
|
}
|
|
if (breakpoint != NULL)
|
|
{
|
|
BfLogDbg("Breakpoint hit. mIsAtBreakpointAddress = %p\n", breakpoint->mAddr);
|
|
threadInfo->mIsAtBreakpointAddress = breakpoint->mAddr;
|
|
mRunState = RunState_Breakpoint;
|
|
}
|
|
else if ((stepBreakAddrIdx != -1) || (isNonDebuggerBreak))
|
|
{
|
|
if (mRunState != RunState_DebugEval)
|
|
{
|
|
// Was in mStepBreakpointAddrs list
|
|
if ((isNonDebuggerBreak) || (mStepType == StepType_None) || (mSteppingThread == mAtBreakThread))
|
|
{
|
|
BfLogDbg("Hit mStepBreakpointAddrs breakpoint\n");
|
|
mRunState = RunState_Paused;
|
|
}
|
|
else
|
|
{
|
|
BfLogDbg("Ignored mStepBreakpointAddrs breakpoint (wrong thread)\n");
|
|
mRunState = RunState_Running;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BfLogDbg("Ignoring break (old or ignored breakpoint)\n");
|
|
mRunState = RunState_Running;
|
|
}
|
|
}
|
|
|
|
if ((breakpoint != NULL) && (!ignoreBreakpoint))
|
|
{
|
|
mActiveBreakpoint = breakpoint;
|
|
mBreakStackFrameIdx = -1;
|
|
}
|
|
|
|
if ((mRunState == RunState_Paused) || (mRunState == RunState_Breakpoint))
|
|
threadInfo->mStoppedAtAddress = pcAddress;
|
|
}
|
|
break;
|
|
case STATUS_WX86_SINGLE_STEP:
|
|
case EXCEPTION_SINGLE_STEP:
|
|
{
|
|
if (mRunState == RunState_Terminating)
|
|
{
|
|
BfLogDbg("Ignoring event because of RunState_Terminating\n");
|
|
break;
|
|
}
|
|
|
|
if ((mStepSwitchedThreads) && (mActiveThread == mSteppingThread) && (mActiveThread->mIsAtBreakpointAddress != NULL))
|
|
{
|
|
ContinueFromBreakpoint();
|
|
break;
|
|
}
|
|
|
|
if (mRunState == RunState_HotStep)
|
|
{
|
|
BF_ASSERT(mActiveThread == mDebuggerWaitingThread);
|
|
mRunState = RunState_Paused;
|
|
break;
|
|
}
|
|
|
|
mActiveThread = mDebuggerWaitingThread;
|
|
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_ALL;
|
|
BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
addr_target pcAddress = BF_CONTEXT_IP(lcContext);
|
|
|
|
bool wasUnfilteredStep = mStepType == StepType_StepInto_Unfiltered;
|
|
if (mStepType == StepType_StepInto_UnfilteredSingle)
|
|
{
|
|
wasUnfilteredStep = true;
|
|
mStepType = StepType_StepInto;
|
|
mStepStartPC = pcAddress;
|
|
}
|
|
|
|
BfLogDbg("EXCEPTION_SINGLE_STEP Thread:%d PC:%p\n", mActiveThread->mThreadId, exceptionRecord->ExceptionAddress);
|
|
|
|
if (lcContext.Dr6 & 0x0F) // Memory breakpoint hit
|
|
{
|
|
WdBreakpoint* foundBreakpoint = NULL;
|
|
for (int memoryWatchSlot = 0; memoryWatchSlot < 4; memoryWatchSlot++)
|
|
{
|
|
if ((lcContext.Dr6 & ((intptr_target)1 << memoryWatchSlot)) != 0)
|
|
{
|
|
foundBreakpoint = mMemoryBreakpoints[memoryWatchSlot].mBreakpoint;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (foundBreakpoint == NULL)
|
|
{
|
|
BfLogDbg("Unknown memory breakpoint hit %p\n", pcAddress);
|
|
mDebugManager->mOutMessages.push_back(StrFormat("memoryBreak %s", EncodeDataPtr(pcAddress, false).c_str()));
|
|
mRunState = RunState_Paused;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DbgSubprogram* subprogram = mDebugTarget->FindSubProgram(pcAddress);
|
|
if (CheckConditionalBreakpoint(foundBreakpoint, subprogram, pcAddress))
|
|
{
|
|
if (foundBreakpoint != NULL)
|
|
{
|
|
mDebugManager->mOutMessages.push_back(StrFormat("memoryBreak %s", EncodeDataPtr(foundBreakpoint->mMemoryBreakpointInfo->mMemoryAddress, false).c_str()));
|
|
mRunState = RunState_Paused;
|
|
}
|
|
|
|
mActiveBreakpoint = foundBreakpoint;
|
|
mBreakStackFrameIdx = -1;
|
|
RemoveTempBreakpoints();
|
|
BfLogDbg("Memory breakpoint hit: %p\n", foundBreakpoint);
|
|
}
|
|
else
|
|
ClearCallStack();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((mRunState == RunState_DebugEval) && (mDebugEvalThreadInfo.mThreadId == mDebuggerWaitingThread->mThreadId))
|
|
{
|
|
if ((addr_target)(intptr)exceptionRecord->ExceptionAddress == mDebugEvalSetRegisters.GetPC())
|
|
{
|
|
// This indicates we are returning from kernel mode and our registers are clobbered
|
|
SetRegisters(&mDebugEvalSetRegisters);
|
|
}
|
|
break;
|
|
}
|
|
|
|
bool hadBreakpointContinue = true;
|
|
if (threadInfo->mBreakpointAddressContinuing != 0)
|
|
{
|
|
bool wantsBreakpoint = WantsBreakpointAt(threadInfo->mBreakpointAddressContinuing);
|
|
BfLogDbg("Continuing breakpoint at %p WantsReset:%d\n", threadInfo->mBreakpointAddressContinuing, wantsBreakpoint);
|
|
|
|
if (wantsBreakpoint)
|
|
{
|
|
PhysSetBreakpoint(threadInfo->mBreakpointAddressContinuing);
|
|
}
|
|
threadInfo->mBreakpointAddressContinuing = NULL;
|
|
hadBreakpointContinue = true;
|
|
|
|
ThreadRestoreUnpause();
|
|
}
|
|
|
|
if ((mSteppingThread != NULL) && (mSteppingThread != mActiveThread))
|
|
{
|
|
// This SINGLE_STEP happened in the wrong thread - we need the stepping thread to do the stepping!
|
|
// Try again.
|
|
mActiveThread = mSteppingThread;
|
|
SingleStepX86();
|
|
break;
|
|
}
|
|
|
|
bool isDeeper = mStepSP > BF_CONTEXT_SP(lcContext);
|
|
if ((mStepSwitchedThreads) && (mStepType == StepType_StepOver) && (isDeeper))
|
|
{
|
|
if (HasSteppedIntoCall())
|
|
{
|
|
// Since we switched threads, we needed to do a hardware step which has placed us inside a
|
|
// call, so we need to step out of that now...
|
|
SetupStep(StepType_StepOut_NoFrame);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we don't have a mStepBreakpointAddrs set, that means we're stepping through individual instructions --
|
|
// so process the new location here
|
|
if (((mStepType == StepType_StepInto) || (mStepType == StepType_StepInto_Unfiltered) || (mStepType == StepType_StepOver)) && (mStepBreakpointAddrs.size() == 0))
|
|
{
|
|
DbgSubprogram* dwSubprogram = NULL;
|
|
DbgLineData* dwLineData = FindLineDataAtAddress(pcAddress, &dwSubprogram, NULL, NULL, DbgOnDemandKind_LocalOnly);
|
|
|
|
if ((dwSubprogram != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC) && (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced))
|
|
{
|
|
BfLogDbg("Stepping through hot thunk\n");
|
|
mRunState = RunState_Running;
|
|
SingleStepX86();
|
|
break;
|
|
}
|
|
|
|
if ((mStepType == StepType_StepOver) && (!mStepInAssembly))
|
|
{
|
|
if ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC))
|
|
{
|
|
BfLogDbg("Attempting StepOver of inlined method - SingleStep\n");
|
|
SetupStep(StepType_StepOut);
|
|
mRunState = RunState_Running;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Column of -1 means "Illegal", keep stepping!
|
|
if ((mStepInAssembly) ||
|
|
((dwLineData != NULL) && (dwLineData->IsStackFrameSetup()) && (dwLineData->mColumn >= 0) &&
|
|
((dwSubprogram->GetLineAddr(*dwLineData) == pcAddress) || (mStepStopOnNextInstruction))))
|
|
{
|
|
// Hit a line while stepping, we're done!
|
|
mRunState = RunState_Paused;
|
|
StepLineTryPause(pcAddress, false);
|
|
if (mRunState == RunState_Paused)
|
|
{
|
|
if ((mStepType == StepType_StepInto) && (!wasUnfilteredStep) && (!mStepInAssembly) && (dwSubprogram != NULL))
|
|
{
|
|
// Don't filter out the current subprogram (would break cases where we explicitly stepped into or hit breakpoint in a filtered subprogram)
|
|
bool isInStartSubprogram = (mStepStartPC >= dwSubprogram->mBlock.mLowPC) && (mStepStartPC < dwSubprogram->mBlock.mHighPC);
|
|
if ((!isInStartSubprogram) && (IsStepFiltered(dwSubprogram, dwLineData)))
|
|
{
|
|
BfLogDbg("Hit step filter (2)\n");
|
|
mRunState = RunState_Running;
|
|
SetupStep(StepType_StepOut_ThenInto);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ClearStep();
|
|
mCurNoInfoStepTries = 0; // Reset
|
|
}
|
|
else
|
|
SetupStep(mStepType);
|
|
}
|
|
else if (dwSubprogram != NULL)
|
|
{
|
|
if ((dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) && ((mStepType == StepType_StepInto) || (mStepType == StepType_StepInto_Unfiltered)))
|
|
{
|
|
SingleStepX86();
|
|
}
|
|
else
|
|
{
|
|
// Inside a line's instruction, keep going
|
|
SetupStep(mStepType);
|
|
mCurNoInfoStepTries = 0; // Reset
|
|
}
|
|
}
|
|
else if (mStepType == StepType_StepInto_Unfiltered)
|
|
{
|
|
CPUInst inst;
|
|
if (mDebugTarget->DecodeInstruction(pcAddress, &inst))
|
|
{
|
|
if (inst.IsBranch())
|
|
{
|
|
auto target = inst.GetTarget();
|
|
if (target != 0)
|
|
{
|
|
DbgSubprogram* destSubprogram = mDebugTarget->FindSubProgram(target);
|
|
if ((destSubprogram != NULL) && (target == destSubprogram->mBlock.mLowPC))
|
|
{
|
|
// We're jumping to an actual subprogram, so continue stepping here
|
|
mStepType = StepType_StepInto_UnfilteredSingle;
|
|
SingleStepX86();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We requested to step into this method so stop here even if we don't have source
|
|
mRunState = RunState_Paused;
|
|
}
|
|
else
|
|
{
|
|
// No debug info!
|
|
bool doStepOut = false;
|
|
if (mCurNoInfoStepTries < 16)
|
|
{
|
|
mCurNoInfoStepTries++;
|
|
BfLogDbg("NoInfoStepTries: %d\n", mCurNoInfoStepTries);
|
|
if (!SetupStep(mStepType))
|
|
doStepOut = true;
|
|
}
|
|
else
|
|
doStepOut = true;
|
|
if (doStepOut)
|
|
{
|
|
// Step out of current call.
|
|
mStepSP = 0;
|
|
SetupStep(StepType_StepOut_NoFrame);
|
|
// Aggressive stepout - don't monitor BP
|
|
mStepSP = 0;
|
|
}
|
|
}
|
|
}
|
|
else if (!hadBreakpointContinue)
|
|
{
|
|
BF_DBG_FATAL("EXCEPTION_SINGLE_STEP bad debugger state");
|
|
}
|
|
|
|
if (mRunState == RunState_Paused)
|
|
threadInfo->mStoppedAtAddress = pcAddress;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
bool isSystemException =
|
|
(exceptionRecord->ExceptionCode >= STATUS_ACCESS_VIOLATION) &&
|
|
(exceptionRecord->ExceptionCode <= STATUS_ASSERTION_FAILURE);
|
|
|
|
bool isFirstChance = mDebugEvent.u.Exception.dwFirstChance != 0;
|
|
bool handled = false;
|
|
|
|
//TODO: Use a user-defined filter here to determine whether to stop or continue
|
|
if (isFirstChance)
|
|
{
|
|
if (exceptionRecord->ExceptionCode == 0x406D1388) // Visual C
|
|
{
|
|
if ((int32)exceptionRecord->ExceptionInformation[0] == 0x1000)
|
|
{
|
|
struct THREADNAME_INFO
|
|
{
|
|
DWORD dwType; // Must be 0x1000.
|
|
LPCSTR szName; // Pointer to name (in user addr space).
|
|
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
|
DWORD dwFlags; // Reserved for future use, must be zero.
|
|
};
|
|
|
|
THREADNAME_INFO* threadNameInfo = (THREADNAME_INFO*)exceptionRecord->ExceptionInformation;
|
|
|
|
DwFormatInfo formatInfo;
|
|
formatInfo.mRawString = true;
|
|
String nameStr = ReadString(DbgType_SChar, (intptr)threadNameInfo->szName, false, 1024, formatInfo, false);
|
|
|
|
WdThreadInfo* namingThreadInfo = threadInfo;
|
|
if (threadNameInfo->dwThreadID != (DWORD)-1)
|
|
{
|
|
namingThreadInfo = NULL;
|
|
mThreadMap.TryGetValue(threadNameInfo->dwThreadID, &namingThreadInfo);
|
|
}
|
|
|
|
if (namingThreadInfo != NULL)
|
|
{
|
|
namingThreadInfo->mName = nameStr;
|
|
FilterThreadName(namingThreadInfo->mName);
|
|
}
|
|
}
|
|
else if (((int32)exceptionRecord->ExceptionInformation[0] == 0x1001) && ((int32)exceptionRecord->ExceptionInformation[1] == 0x1002))
|
|
{
|
|
struct FailMessage
|
|
{
|
|
addr_target mPtr0; // Unknown
|
|
addr_target mPtr1; // 0
|
|
addr_target mPtr2; // 0
|
|
addr_target mPtr3; // Unknown
|
|
addr_target mErrorStr;
|
|
};
|
|
|
|
FailMessage failMessage = ReadMemory<FailMessage>(exceptionRecord->ExceptionInformation[2]);
|
|
DwFormatInfo formatInfo;
|
|
String failStr = ReadString(DbgType_SChar16, failMessage.mErrorStr, false, 8192, formatInfo, false);
|
|
|
|
mDebugManager->mOutMessages.push_back(StrFormat("error Run-Time Check Failure %d - %s", exceptionRecord->ExceptionInformation[6], failStr.c_str()));
|
|
mRunState = RunState_Paused;
|
|
mRequestedStackFrameIdx = -2; // -2 = "auto"
|
|
handled = true;
|
|
}
|
|
}
|
|
|
|
if (!handled)
|
|
{
|
|
if (mRunState != RunState_DebugEval)
|
|
OutputMessage(StrFormat("Skipping first chance exception %08X at address %@ in thread %d\n", exceptionRecord->ExceptionCode, exceptionRecord->ExceptionAddress, threadInfo->mThreadId));
|
|
::ContinueDebugEvent(mDebuggerWaitingThread->mProcessId, mDebuggerWaitingThread->mThreadId, DBG_EXCEPTION_NOT_HANDLED);
|
|
mIsDebuggerWaiting = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BfLogDbg("EXCEPTION in thread %d at %p\n", threadInfo->mThreadId, exceptionRecord->ExceptionAddress);
|
|
OutputDebugStrF("EXCEPTION\n");
|
|
mActiveThread = threadInfo;
|
|
memcpy(&mCurException, exceptionRecord, sizeof(EXCEPTION_RECORD));
|
|
|
|
if (mRunState == RunState_DebugEval)
|
|
{
|
|
if ((intptr)mCurException.ExceptionAddress == 42)
|
|
{
|
|
BfLogDbg("RunState_DebugEval_Done\n");
|
|
OutputDebugStrF(" RunState_DebugEval_Done\n");
|
|
}
|
|
else
|
|
{
|
|
BfLogDbg("Exception at 0x%@ in thread %d, exception code 0x%08X",
|
|
mCurException.ExceptionAddress, mActiveThread->mThreadId, mCurException.ExceptionCode);
|
|
mDebugPendingExpr->mException = StrFormat("Exception at 0x%@ in thread %d, exception code 0x%08X",
|
|
mCurException.ExceptionAddress, mActiveThread->mThreadId, mCurException.ExceptionCode);
|
|
}
|
|
mRunState = RunState_DebugEval_Done;
|
|
|
|
mExplicitStopThread = mActiveThread;
|
|
mRequestedStackFrameIdx = mDebugPendingExpr->mCallStackIdx;
|
|
}
|
|
else
|
|
{
|
|
mRunState = RunState_Exception;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ((mDebugEvalThreadInfo.mThreadId != 0) && (mRunState != RunState_DebugEval) && (mRunState != RunState_DebugEval_Done))
|
|
{
|
|
CleanupDebugEval();
|
|
}
|
|
|
|
// Stepping done?
|
|
if (mStepType == StepType_None)
|
|
{
|
|
mLastValidStepIntoPC = 0;
|
|
}
|
|
|
|
BF_ASSERT(mDebuggerWaitingThread != NULL);
|
|
return true;
|
|
}
|
|
|
|
void WinDebugger::Update()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
// if (mRunState == RunState_DebugEval)
|
|
// ContinueDebugEvent();
|
|
if (mRunState == RunState_DebugEval_Done)
|
|
{
|
|
if (mDebugPendingExpr != NULL)
|
|
{
|
|
mDebugPendingExpr->mIdleTicks++;
|
|
if (mDebugPendingExpr->mIdleTicks >= 2)
|
|
{
|
|
BfLogDbg("Finishing pending expr in thread %d\n", mDebugEvalThreadInfo.mThreadId);
|
|
|
|
mRunState = RunState_Paused;
|
|
CleanupDebugEval();
|
|
}
|
|
}
|
|
}
|
|
else if (mDebugPendingExpr != NULL)
|
|
{
|
|
mDebugPendingExpr->mIdleTicks = 0;
|
|
}
|
|
}
|
|
|
|
void WinDebugger::ContinueDebugEvent()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
BF_ASSERT(mRunState != RunState_DebugEval_Done);
|
|
|
|
if (!mWantsDebugContinue)
|
|
return;
|
|
|
|
if (!TryRunContinue())
|
|
return;
|
|
|
|
// if ((mRunState == RunState_DebugEval) && (mDebuggerWaitingThread->mThreadId != mDebugEvalThreadInfo.mThreadId))
|
|
// {
|
|
// // Don't process the 'mIsAtBreakpointAddress' stuff
|
|
// mWantsDebugContinue = false;
|
|
// mContinueEvent.Set();
|
|
// return;
|
|
// }
|
|
|
|
if ((mDebuggerWaitingThread->mIsAtBreakpointAddress == 0) && (mDebuggerWaitingThread->mStoppedAtAddress != 0))
|
|
{
|
|
auto breakpoint = FindBreakpointAt(mDebuggerWaitingThread->mStoppedAtAddress);
|
|
if (breakpoint != NULL)
|
|
{
|
|
mDebuggerWaitingThread->mIsAtBreakpointAddress = mDebuggerWaitingThread->mStoppedAtAddress;
|
|
}
|
|
}
|
|
|
|
if (mDebuggerWaitingThread->mIsAtBreakpointAddress != 0)
|
|
{
|
|
if (!ContinueFromBreakpoint())
|
|
{
|
|
BfLogDbg("ContinueFromBreakpoint failed\n");
|
|
|
|
ClearCallStack();
|
|
mDebuggerWaitingThread->mStoppedAtAddress = 0;
|
|
mDebuggerWaitingThread->mIsAtBreakpointAddress = 0;
|
|
mWantsDebugContinue = false;
|
|
mContinueFromBreakpointFailed = true;
|
|
mContinueEvent.Set();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((mRunState == RunState_Breakpoint) || (mRunState == RunState_Paused) || (mRunState == RunState_TargetUnloaded))
|
|
{
|
|
ClearCallStack();
|
|
mRunState = RunState_Running;
|
|
}
|
|
|
|
mDebuggerWaitingThread->mStoppedAtAddress = 0;
|
|
mWantsDebugContinue = false;
|
|
BF_ASSERT_REL(mDebuggerWaitingThread->mIsAtBreakpointAddress == 0);
|
|
mContinueEvent.Set();
|
|
}
|
|
|
|
static BOOL CALLBACK WdEnumWindowsProc(HWND hwnd, LPARAM lParam)
|
|
{
|
|
int wantProcessId = lParam;
|
|
|
|
HWND owner = GetWindow(hwnd, GW_OWNER);
|
|
if (!IsWindowVisible(hwnd))
|
|
return TRUE;
|
|
|
|
DWORD processId = 0;
|
|
DWORD threadId = GetWindowThreadProcessId(hwnd, &processId);
|
|
|
|
if (processId != wantProcessId)
|
|
return TRUE;
|
|
|
|
while (true)
|
|
{
|
|
HWND parentHWnd = GetParent(hwnd);
|
|
if (parentHWnd != NULL)
|
|
{
|
|
hwnd = parentHWnd;
|
|
continue;
|
|
}
|
|
SetForegroundWindow(hwnd);
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void WinDebugger::ForegroundTarget(int altProcessId)
|
|
{
|
|
int wantProcessId = altProcessId;
|
|
if (wantProcessId == 0)
|
|
wantProcessId = ((WinDebugger*)gDebugger)->mProcessInfo.dwProcessId;
|
|
|
|
HWND hwnd = ::GetForegroundWindow();
|
|
if (hwnd != INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD processId = 0;
|
|
GetWindowThreadProcessId(hwnd, &processId);
|
|
if (processId == ((WinDebugger*)gDebugger)->mProcessInfo.dwProcessId)
|
|
return; // Already good
|
|
}
|
|
|
|
EnumWindows(WdEnumWindowsProc, wantProcessId);
|
|
}
|
|
|
|
static int gFindLineDataAt = 0;
|
|
|
|
DbgLineData* WinDebugger::FindLineDataAtAddress(addr_target address, DbgSubprogram** outSubProgram, DbgSrcFile** outSrcFile, int* outLineIdx, DbgOnDemandKind onDemandKind)
|
|
{
|
|
gFindLineDataAt++;
|
|
|
|
BP_ZONE("WinDebugger::FindLineDataAtAddress");
|
|
|
|
auto dwSubprogram = mDebugTarget->FindSubProgram((addr_target)address, onDemandKind);
|
|
if (dwSubprogram == NULL)
|
|
return NULL;
|
|
|
|
FixupLineDataForSubprogram(dwSubprogram);
|
|
auto lineData = dwSubprogram->FindClosestLine(address, outSubProgram, outSrcFile, outLineIdx);
|
|
|
|
return lineData;
|
|
}
|
|
|
|
DbgLineData* WinDebugger::FindLineDataInSubprogram(addr_target address, DbgSubprogram* dwSubprogram)
|
|
{
|
|
auto dwCompileUnit = dwSubprogram->mCompileUnit;
|
|
|
|
FixupLineDataForSubprogram(dwSubprogram);
|
|
auto lineData = dwSubprogram->FindClosestLine(address);
|
|
return lineData;
|
|
}
|
|
|
|
bool WinDebugger::IsStepFiltered(DbgSubprogram* dbgSubprogram, DbgLineData* dbgLineData)
|
|
{
|
|
if (mIsStepIntoSpecific)
|
|
return false;
|
|
|
|
if (dbgSubprogram->mStepFilterVersion != mDebugManager->mStepFilterVersion)
|
|
{
|
|
String filterName;
|
|
CreateFilterName(filterName, dbgSubprogram);
|
|
dbgSubprogram->PopulateSubprogram();
|
|
|
|
bool doDefault = false;
|
|
|
|
StepFilter* stepFilterPtr;
|
|
if (mDebugManager->mStepFilters.TryGetValue(filterName, &stepFilterPtr))
|
|
{
|
|
switch (stepFilterPtr->mFilterKind)
|
|
{
|
|
case BfStepFilterKind_Default:
|
|
doDefault = true;
|
|
break;
|
|
case BfStepFilterKind_Filtered:
|
|
dbgSubprogram->mIsStepFiltered = true;
|
|
break;
|
|
case BfStepFilterKind_NotFiltered:
|
|
dbgSubprogram->mIsStepFiltered = false;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
doDefault = true;
|
|
}
|
|
|
|
if (doDefault)
|
|
{
|
|
dbgSubprogram->mIsStepFiltered = dbgSubprogram->mIsStepFilteredDefault;
|
|
}
|
|
|
|
dbgSubprogram->mStepFilterVersion = mDebugManager->mStepFilterVersion;
|
|
}
|
|
|
|
if (!dbgSubprogram->mIsStepFiltered)
|
|
{
|
|
if (dbgLineData != NULL)
|
|
{
|
|
auto dbgSrcFile = dbgSubprogram->GetLineSrcFile(*dbgLineData);
|
|
if (dbgSrcFile->mStepFilterVersion != mDebugManager->mStepFilterVersion)
|
|
{
|
|
dbgSrcFile->mFileExistKind = dbgSubprogram->mCompileUnit->mDbgModule->CheckSourceFileExist(dbgSrcFile->GetLocalPath());
|
|
dbgSrcFile->mStepFilterVersion = mDebugManager->mStepFilterVersion;
|
|
}
|
|
|
|
switch (dbgSrcFile->mFileExistKind)
|
|
{
|
|
case DbgFileExistKind_NotFound:
|
|
return true;
|
|
case DbgFileExistKind_HasOldSourceCommand:
|
|
if (mDebugManager->mStepOverExternalFiles)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return dbgSubprogram->mIsStepFiltered;
|
|
}
|
|
|
|
void WinDebugger::RemoveTempBreakpoints()
|
|
{
|
|
BfLogDbg("RemoveTempBreakpoints\n");
|
|
|
|
for (auto address : mTempBreakpoint)
|
|
{
|
|
RemoveBreakpoint(address);
|
|
// if (FindBreakpointAt(address) == NULL)
|
|
// {
|
|
// RemoveBreakpoint(address);
|
|
// }
|
|
// else
|
|
// {
|
|
// BfLogDbg("Ignoring remove on temp breakpoint %p\n", address);
|
|
// }
|
|
}
|
|
mTempBreakpoint.Clear();
|
|
mStepBreakpointAddrs.Clear();
|
|
}
|
|
|
|
void WinDebugger::RehupBreakpoints(bool doFlush)
|
|
{
|
|
BfLogDbg("RehupBreakpoints\n");
|
|
|
|
// First pass- detach breakpoints that need to be rebound
|
|
for (int i = 0; i < (int)mBreakpoints.size(); i++)
|
|
{
|
|
auto breakpoint = mBreakpoints[i];
|
|
while (breakpoint != NULL)
|
|
{
|
|
if (((breakpoint->mSrcFile != NULL) && (breakpoint->mSrcFile->mDeferredRefs.size() > 0)) ||
|
|
(!breakpoint->mSymbolName.IsEmpty()))
|
|
{
|
|
// This breakpoint was already bound, but we loaded a debug module that also had this file so rebind it
|
|
DetachBreakpoint(breakpoint);
|
|
}
|
|
|
|
breakpoint = (WdBreakpoint*)breakpoint->mLinkedSibling;
|
|
}
|
|
}
|
|
|
|
// Second pass- actually set breakpoints
|
|
for (int i = 0; i < (int)mBreakpoints.size(); i++)
|
|
{
|
|
auto breakpoint = mBreakpoints[i];
|
|
while (breakpoint != NULL)
|
|
{
|
|
CheckBreakpoint(breakpoint);
|
|
if (breakpoint->mAddr != 0)
|
|
SetBreakpoint(breakpoint->mAddr, true);
|
|
breakpoint = (WdBreakpoint*)breakpoint->mLinkedSibling;
|
|
}
|
|
}
|
|
|
|
mNeedsRehupBreakpoints = false;
|
|
}
|
|
|
|
bool WinDebugger::WantsBreakpointAt(addr_target address)
|
|
{
|
|
if (mTempBreakpoint.Contains(address))
|
|
return true;
|
|
for (auto breakpoint : mBreakpoints)
|
|
{
|
|
WdBreakpoint* checkBreakpoint = breakpoint;
|
|
while (checkBreakpoint != NULL)
|
|
{
|
|
if (address == checkBreakpoint->mAddr)
|
|
return true;
|
|
checkBreakpoint = (WdBreakpoint*)checkBreakpoint->mLinkedSibling;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void WinDebugger::CheckBreakpoint(WdBreakpoint* wdBreakpoint, DbgSrcFile* srcFile, int lineNum, int hotIdx)
|
|
{
|
|
BP_ZONE("WinDebugger::CheckBreakpoint:atLoc");
|
|
|
|
if (hotIdx == -1)
|
|
{
|
|
BF_ASSERT(wdBreakpoint->mPendingHotBindIdx == -1);
|
|
}
|
|
WdBreakpoint* headBreakpoint = wdBreakpoint;
|
|
headBreakpoint->mPendingHotBindIdx = -1;
|
|
|
|
bool foundInSequence = false;
|
|
DbgSubprogram* lastFoundSubprogram = NULL;
|
|
|
|
int highestHotIdx = -1;
|
|
bool foundLine = false;
|
|
|
|
int bestLineNum = -1;
|
|
int bestLineOffset = 0x7FFFFFFF;
|
|
|
|
auto _CheckLineInfo = [&](DbgSubprogram* dbgSubprogram, DbgLineInfo* dbgLineInfo)
|
|
{
|
|
// Scan first so we can determine if we want to do fix up line data or not.
|
|
bool hasNear = false;
|
|
int maxLineDist = 6;
|
|
|
|
for (int lineIdx = 0; lineIdx < dbgLineInfo->mLines.mSize; lineIdx++)
|
|
{
|
|
auto lineData = &dbgLineInfo->mLines[lineIdx];
|
|
auto& ctx = dbgLineInfo->mContexts[lineData->mCtxIdx];
|
|
if (ctx.mSrcFile != srcFile)
|
|
continue;
|
|
|
|
int lineOffset = lineData->mLine - lineNum;
|
|
if ((lineOffset >= 0) && (lineOffset <= maxLineDist))
|
|
hasNear = true;
|
|
}
|
|
|
|
if (!hasNear)
|
|
return;
|
|
|
|
FixupLineDataForSubprogram(dbgSubprogram);
|
|
|
|
for (int lineIdx = 0; lineIdx < dbgLineInfo->mLines.mSize; lineIdx++)
|
|
{
|
|
//TODO: Do fixup lineData... ?
|
|
auto lineData = &dbgLineInfo->mLines[lineIdx];
|
|
auto& ctx = dbgLineInfo->mContexts[lineData->mCtxIdx];
|
|
if (ctx.mSrcFile != srcFile)
|
|
continue;
|
|
|
|
// if (ctx.mInlinee != NULL)
|
|
// {
|
|
// if (lineIdx + 1 < dbgLineInfo->mLines.mSize)
|
|
// {
|
|
// auto nextLineData = &dbgLineInfo->mLines[lineIdx + 1];
|
|
// if (nextLineData->mRelAddress == lineData->mRelAddress)
|
|
// {
|
|
// // Use the later entry (same logic from DisassembleAt)
|
|
// continue;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
if ((lineData->mColumn == -1) && (wdBreakpoint->mInstrOffset == -1))
|
|
continue;
|
|
|
|
int lineOffset = lineData->mLine - lineNum;
|
|
|
|
if (lineOffset == 0)
|
|
{
|
|
foundLine = true;
|
|
|
|
auto address = dbgSubprogram->GetLineAddr(*lineData);
|
|
auto subProgram = mDebugTarget->FindSubProgram(address);
|
|
if (subProgram->mNeedLineDataFixup)
|
|
FixupLineDataForSubprogram(subProgram);
|
|
|
|
if (subProgram != NULL)
|
|
highestHotIdx = BF_MAX(highestHotIdx, subProgram->mCompileUnit->mDbgModule->mHotIdx);
|
|
|
|
if ((foundInSequence) && (subProgram != lastFoundSubprogram))
|
|
foundInSequence = false;
|
|
|
|
if ((subProgram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) && (address < subProgram->mBlock.mLowPC + sizeof(HotJumpOp)))
|
|
{
|
|
// If this breakpoint ends up on the hot jmp instruction
|
|
continue;
|
|
}
|
|
|
|
if (!foundInSequence)
|
|
{
|
|
lastFoundSubprogram = subProgram;
|
|
|
|
if ((subProgram != NULL) && (subProgram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) && (address == subProgram->mBlock.mLowPC))
|
|
{
|
|
// This instruction is actually the hot jump, we don't need a breakpoint here
|
|
foundInSequence = true;
|
|
continue;
|
|
}
|
|
|
|
if (wdBreakpoint->mSrcFile != NULL)
|
|
{
|
|
wdBreakpoint = new WdBreakpoint();
|
|
// Insert at head
|
|
wdBreakpoint->mLinkedSibling = headBreakpoint->mLinkedSibling;
|
|
headBreakpoint->mLinkedSibling = wdBreakpoint;
|
|
wdBreakpoint->mRequestedLineNum = headBreakpoint->mRequestedLineNum;
|
|
wdBreakpoint->mLineNum = headBreakpoint->mLineNum;
|
|
wdBreakpoint->mColumn = headBreakpoint->mColumn;
|
|
wdBreakpoint->mInstrOffset = headBreakpoint->mInstrOffset;
|
|
wdBreakpoint->mIsLinkedSibling = true;
|
|
wdBreakpoint->mHead = headBreakpoint;
|
|
}
|
|
|
|
if (wdBreakpoint->mInstrOffset > 0)
|
|
{
|
|
for (int instIdx = 0; instIdx < wdBreakpoint->mInstrOffset; instIdx++)
|
|
{
|
|
CPUInst inst;
|
|
if (!mDebugTarget->DecodeInstruction(address, &inst))
|
|
break;
|
|
address += inst.mSize;
|
|
}
|
|
}
|
|
|
|
wdBreakpoint->mSrcFile = ctx.mSrcFile;
|
|
wdBreakpoint->mLineData = DbgLineDataEx(lineData, subProgram);
|
|
wdBreakpoint->mBreakpointType = BreakpointType_User;
|
|
wdBreakpoint->mAddr = address;
|
|
|
|
if ((mDebuggerWaitingThread != NULL) && (mDebuggerWaitingThread->mStoppedAtAddress == address))
|
|
{
|
|
BfLogDbg("CheckBreakpoint setting mIsAtBreakpointAddress = %p\n", address);
|
|
mDebuggerWaitingThread->mIsAtBreakpointAddress = address;
|
|
}
|
|
|
|
BfLogDbg("Breakpoint %p found at %s in %s\n", wdBreakpoint, subProgram->mName, GetFileName(subProgram->mCompileUnit->mDbgModule->mFilePath).c_str());
|
|
|
|
mBreakpointAddrMap.ForceAdd(address, wdBreakpoint);
|
|
SetBreakpoint(address);
|
|
|
|
foundInSequence = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//TODO: We didn't have this here, but if we don't have this then there are some cases where the method-closing brace generates code in
|
|
// multiple places so we need to ensure this will break on them all
|
|
foundInSequence = false;
|
|
}
|
|
|
|
if ((lineOffset >= 0) && (lineOffset <= maxLineDist) && (lineOffset <= bestLineOffset))
|
|
{
|
|
if (lineOffset < bestLineOffset)
|
|
{
|
|
bestLineNum = lineData->mLine;
|
|
bestLineOffset = lineOffset;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
for (int pass = 0; pass < 2; pass++)
|
|
{
|
|
if (lineNum == -1)
|
|
break;
|
|
|
|
bestLineNum = -1;
|
|
bestLineOffset = 0x7FFFFFFF;
|
|
|
|
if (hotIdx >= 0)
|
|
{
|
|
if (hotIdx >= srcFile->mHotReplacedDbgLineInfo.size())
|
|
return;
|
|
|
|
auto hotReplacedLineInfo = srcFile->mHotReplacedDbgLineInfo[hotIdx];
|
|
for (auto& hotReplacedEntry : hotReplacedLineInfo->mEntries)
|
|
{
|
|
_CheckLineInfo(hotReplacedEntry.mSubprogram, hotReplacedEntry.mLineInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (auto subprogram : srcFile->mLineDataRefs)
|
|
_CheckLineInfo(subprogram, subprogram->mLineInfo);
|
|
}
|
|
|
|
if (foundLine)
|
|
break;
|
|
|
|
// Don't allow the breakpoint to be inexactly bound -- only match on pass 0
|
|
if (hotIdx != -1)
|
|
break;
|
|
|
|
if (bestLineNum == -1)
|
|
break;
|
|
|
|
lineNum = bestLineNum;
|
|
wdBreakpoint->mLineNum = bestLineNum;
|
|
}
|
|
|
|
int highestCheckHotIdx = highestHotIdx - 1;
|
|
if (hotIdx != -1)
|
|
highestCheckHotIdx = hotIdx - 1;
|
|
|
|
for (int hotFileIdx = highestCheckHotIdx; hotFileIdx >= 0; hotFileIdx--)
|
|
{
|
|
auto& hotReplacedDbgLineData = wdBreakpoint->mSrcFile->mHotReplacedDbgLineInfo;
|
|
// Only try to bind to an old hot version if we haven't unloaded the hot module
|
|
if ((hotFileIdx < (int)hotReplacedDbgLineData.size()) && (hotReplacedDbgLineData[hotFileIdx]->mEntries.size() > 0))
|
|
{
|
|
headBreakpoint->mPendingHotBindIdx = hotFileIdx;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinDebugger::HotBindBreakpoint(Breakpoint* breakpoint, int lineNum, int hotIdx)
|
|
{
|
|
WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
|
|
CheckBreakpoint(wdBreakpoint, wdBreakpoint->mSrcFile, lineNum, hotIdx);
|
|
}
|
|
|
|
void WinDebugger::CheckBreakpoint(WdBreakpoint* wdBreakpoint)
|
|
{
|
|
if (!mGotStartupEvent)
|
|
return;
|
|
|
|
if (wdBreakpoint->mThreadId == 0) // Not bound to threadId yet...
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (wdBreakpoint->mMemoryBreakpointInfo != NULL)
|
|
{
|
|
if (wdBreakpoint->mMemoryBreakpointInfo->mMemoryWatchSlotBitmap != 0)
|
|
return;
|
|
|
|
if (mFreeMemoryBreakIndices.size() == 0)
|
|
return;
|
|
|
|
if ((IsInRunState()) || (mActiveThread == NULL))
|
|
return;
|
|
|
|
int wantBytes[4];
|
|
int wantBindCount = 0;
|
|
int bytesLeft = wdBreakpoint->mMemoryBreakpointInfo->mByteCount;
|
|
addr_target curAddr = wdBreakpoint->mMemoryBreakpointInfo->mMemoryAddress;
|
|
while (bytesLeft > 0)
|
|
{
|
|
if (wantBindCount >= mFreeMemoryBreakIndices.size())
|
|
return;
|
|
|
|
int curByteCount = 1;
|
|
#ifdef BF_DBG_64
|
|
if ((bytesLeft >= 8) && ((curAddr & 7) == 0))
|
|
curByteCount = 8;
|
|
else
|
|
#endif
|
|
if ((bytesLeft >= 4) && ((curAddr & 3) == 0))
|
|
curByteCount = 4;
|
|
else if ((bytesLeft >= 2) && ((curAddr & 1) == 0))
|
|
curByteCount = 2;
|
|
|
|
wantBytes[wantBindCount++] = curByteCount;
|
|
bytesLeft -= curByteCount;
|
|
curAddr += curByteCount;
|
|
}
|
|
|
|
addr_target curOfs = 0;
|
|
for (int i = 0; i < wantBindCount; i++)
|
|
{
|
|
int memoryBreakIdx = mFreeMemoryBreakIndices.back();
|
|
mFreeMemoryBreakIndices.pop_back();
|
|
|
|
mMemoryBreakpoints[memoryBreakIdx].mBreakpoint = wdBreakpoint;
|
|
mMemoryBreakpoints[memoryBreakIdx].mAddress = wdBreakpoint->mMemoryBreakpointInfo->mMemoryAddress + curOfs;
|
|
mMemoryBreakpoints[memoryBreakIdx].mByteCount = wantBytes[i];
|
|
mMemoryBreakpoints[memoryBreakIdx].mOfs = curOfs;
|
|
curOfs += wantBytes[i];
|
|
mMemoryBreakpointVersion++;
|
|
|
|
wdBreakpoint->mMemoryBreakpointInfo->mMemoryWatchSlotBitmap |= 1<<memoryBreakIdx;
|
|
}
|
|
|
|
UpdateThreadDebugRegisters();
|
|
}
|
|
|
|
if (wdBreakpoint->mAddr != 0)
|
|
return;
|
|
|
|
if (!wdBreakpoint->mSymbolName.IsEmpty())
|
|
{
|
|
auto headBreakpoint = wdBreakpoint->GetHeadBreakpoint();
|
|
|
|
String symbolName = wdBreakpoint->mSymbolName;
|
|
bool onlyBindFirst = false;
|
|
if (symbolName.StartsWith("-"))
|
|
{
|
|
symbolName.Remove(0);
|
|
onlyBindFirst = true;
|
|
}
|
|
|
|
for (auto dbgModule : mDebugTarget->mDbgModules)
|
|
{
|
|
dbgModule->ParseSymbolData();
|
|
|
|
addr_target targetAddr = -1;
|
|
auto entry = dbgModule->mSymbolNameMap.Find(symbolName.c_str());
|
|
if (entry != NULL)
|
|
{
|
|
DbgSymbol* dwSymbol = entry->mValue;
|
|
targetAddr = dwSymbol->mAddress;
|
|
}
|
|
|
|
if (targetAddr == -1)
|
|
{
|
|
if (symbolName == ".")
|
|
{
|
|
targetAddr = mDebugTarget->mLaunchBinary->mImageBase + mDebugTarget->mLaunchBinary->mEntryPoint;
|
|
onlyBindFirst = true;
|
|
}
|
|
}
|
|
|
|
if (targetAddr != -1)
|
|
{
|
|
if (wdBreakpoint->mAddr == 0)
|
|
{
|
|
wdBreakpoint->mAddr = targetAddr;
|
|
wdBreakpoint->mBreakpointType = BreakpointType_User;
|
|
mBreakpointAddrMap.ForceAdd(wdBreakpoint->mAddr, wdBreakpoint);
|
|
SetBreakpoint(wdBreakpoint->mAddr);
|
|
}
|
|
else
|
|
{
|
|
wdBreakpoint = new WdBreakpoint();
|
|
// Insert at head
|
|
wdBreakpoint->mLinkedSibling = headBreakpoint->mLinkedSibling;
|
|
headBreakpoint->mLinkedSibling = wdBreakpoint;
|
|
wdBreakpoint->mSymbolName = headBreakpoint->mSymbolName;
|
|
wdBreakpoint->mIsLinkedSibling = true;
|
|
wdBreakpoint->mHead = headBreakpoint;
|
|
}
|
|
|
|
if (onlyBindFirst)
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
BP_ZONE("WinDebugger::CheckBreakpoint");
|
|
|
|
// Rehup if we load a DLL that also uses this file we bound to (thus the mDeferredRefs check)
|
|
if (wdBreakpoint->mSrcFile == NULL)
|
|
{
|
|
DbgSrcFile* srcFile = mDebugTarget->GetSrcFile(wdBreakpoint->mFilePath);
|
|
if (srcFile == NULL)
|
|
return;
|
|
|
|
for (auto& deferredSrcFileRef : srcFile->mDeferredRefs)
|
|
{
|
|
deferredSrcFileRef.mDbgModule->ParseCompileUnit(deferredSrcFileRef.mCompileUnitId);
|
|
}
|
|
srcFile->mDeferredRefs.Clear();
|
|
|
|
CheckBreakpoint(wdBreakpoint, srcFile, wdBreakpoint->mRequestedLineNum, -1);
|
|
}
|
|
}
|
|
|
|
bool WinDebugger::IsMemoryBreakpointSizeValid(addr_target addr, int size)
|
|
{
|
|
int wantBindCount = 0;
|
|
int bytesLeft = size;
|
|
addr_target curAddr = addr;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int curByteCount = 1;
|
|
#ifdef BF_DBG_64
|
|
if ((bytesLeft >= 8) && ((curAddr & 7) == 0))
|
|
curByteCount = 8;
|
|
else
|
|
#endif
|
|
if ((bytesLeft >= 4) && ((curAddr & 3) == 0))
|
|
curByteCount = 4;
|
|
else if ((bytesLeft >= 2) && ((curAddr & 1) == 0))
|
|
curByteCount = 2;
|
|
bytesLeft -= curByteCount;
|
|
curAddr += curByteCount;
|
|
|
|
if (bytesLeft == 0)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool WinDebugger::HasMemoryBreakpoint(addr_target addr, int size)
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if ((mMemoryBreakpoints[i].mAddress == addr) &&
|
|
(mMemoryBreakpoints[i].mOfs == 0) &&
|
|
(mMemoryBreakpoints[i].mBreakpoint->mMemoryBreakpointInfo->mByteCount == size))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Breakpoint* WinDebugger::CreateBreakpoint(const StringImpl& fileName, int lineNum, int wantColumn, int instrOffset)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
BfLogDbg("CreateBreakpoint %s %d %d\n", fileName.c_str(), lineNum, wantColumn);
|
|
|
|
WdBreakpoint* wdBreakpoint = new WdBreakpoint();
|
|
wdBreakpoint->mFilePath = FixPathAndCase(fileName);
|
|
wdBreakpoint->mRequestedLineNum = lineNum;
|
|
wdBreakpoint->mLineNum = lineNum;
|
|
wdBreakpoint->mColumn = wantColumn;
|
|
wdBreakpoint->mInstrOffset = instrOffset;
|
|
mBreakpoints.push_back(wdBreakpoint);
|
|
|
|
BfLogDbg("CreateBreakpoint Created %p\n", wdBreakpoint);
|
|
|
|
return wdBreakpoint;
|
|
}
|
|
|
|
void WinDebugger::CheckBreakpoint(Breakpoint* checkBreakpoint)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
CheckBreakpoint((WdBreakpoint*)checkBreakpoint);
|
|
}
|
|
|
|
Breakpoint* WinDebugger::CreateMemoryBreakpoint(intptr addr, int byteCount)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
BfLogDbg("CreateMemoryBreakpoint %p %d\n", addr, byteCount);
|
|
|
|
WdBreakpoint* wdBreakpoint = new WdBreakpoint();
|
|
|
|
WdMemoryBreakpointInfo* memoryBreakInfo = new WdMemoryBreakpointInfo();
|
|
memoryBreakInfo->mMemoryAddress = addr;
|
|
memoryBreakInfo->mByteCount = byteCount;
|
|
wdBreakpoint->mMemoryBreakpointInfo = memoryBreakInfo;
|
|
mBreakpoints.push_back(wdBreakpoint);
|
|
CheckBreakpoint(wdBreakpoint);
|
|
return wdBreakpoint;
|
|
}
|
|
|
|
Breakpoint* WinDebugger::CreateSymbolBreakpoint(const StringImpl& symbolName)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
BfLogDbg("CreateSymbolBreakpoint %s\n", symbolName.c_str());
|
|
|
|
WdBreakpoint* wdBreakpoint = new WdBreakpoint();
|
|
wdBreakpoint->mSymbolName = symbolName;
|
|
mBreakpoints.push_back(wdBreakpoint);
|
|
CheckBreakpoint(wdBreakpoint);
|
|
return wdBreakpoint;
|
|
}
|
|
|
|
Breakpoint* WinDebugger::CreateAddressBreakpoint(intptr inAddress)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
BfLogDbg("CreateAddressBreakpoint %p\n", inAddress);
|
|
|
|
addr_target address = (addr_target)inAddress;
|
|
WdBreakpoint* wdBreakpoint = new WdBreakpoint();
|
|
wdBreakpoint->mAddr = address;
|
|
mBreakpointAddrMap.ForceAdd(wdBreakpoint->mAddr, wdBreakpoint);
|
|
SetBreakpoint(address);
|
|
|
|
if ((mDebuggerWaitingThread != NULL) && (mDebuggerWaitingThread->mStoppedAtAddress == address))
|
|
{
|
|
BfLogDbg("CreateAddressBreakpoint setting mIsAtBreakpointAddress = %p\n", address);
|
|
mDebuggerWaitingThread->mIsAtBreakpointAddress = address;
|
|
}
|
|
|
|
mBreakpoints.push_back(wdBreakpoint);
|
|
return wdBreakpoint;
|
|
}
|
|
|
|
void WinDebugger::DeleteBreakpoint(Breakpoint* breakpoint)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
|
|
|
|
while (wdBreakpoint != NULL)
|
|
{
|
|
BfLogDbg("WinDebugger::DeleteBreakpoint %p Count:%d\n", wdBreakpoint, mBreakpoints.size());
|
|
|
|
if (wdBreakpoint == mActiveBreakpoint)
|
|
mActiveBreakpoint = NULL;
|
|
|
|
if (wdBreakpoint->mCondition != NULL)
|
|
{
|
|
if (!wdBreakpoint->mIsLinkedSibling)
|
|
delete wdBreakpoint->mCondition;
|
|
}
|
|
|
|
if (wdBreakpoint->mMemoryBreakpointInfo != NULL)
|
|
{
|
|
for (int memoryWatchSlot = 0; memoryWatchSlot < 4; memoryWatchSlot++)
|
|
{
|
|
if (mMemoryBreakpoints[memoryWatchSlot].mBreakpoint == wdBreakpoint)
|
|
{
|
|
mFreeMemoryBreakIndices.push_back(memoryWatchSlot);
|
|
mMemoryBreakpoints[memoryWatchSlot] = WdMemoryBreakpointBind();
|
|
mMemoryBreakpointVersion++;
|
|
UpdateThreadDebugRegisters();
|
|
}
|
|
}
|
|
|
|
wdBreakpoint->mMemoryBreakpointInfo->mMemoryWatchSlotBitmap = 0;
|
|
}
|
|
|
|
if (wdBreakpoint->mAddr != 0)
|
|
{
|
|
mBreakpointAddrMap.Remove(wdBreakpoint->mAddr, wdBreakpoint);
|
|
RemoveBreakpoint(wdBreakpoint->mAddr);
|
|
|
|
for (auto thread : mThreadList)
|
|
{
|
|
if (thread->mIsAtBreakpointAddress == wdBreakpoint->mAddr)
|
|
thread->mIsAtBreakpointAddress = NULL;
|
|
if (thread->mBreakpointAddressContinuing == wdBreakpoint->mAddr)
|
|
thread->mBreakpointAddressContinuing = NULL;
|
|
}
|
|
}
|
|
|
|
if (!wdBreakpoint->mIsLinkedSibling)
|
|
{
|
|
mBreakpoints.Remove(wdBreakpoint);
|
|
}
|
|
|
|
auto nextBreakpoint = (WdBreakpoint*)wdBreakpoint->mLinkedSibling;
|
|
delete wdBreakpoint;
|
|
|
|
wdBreakpoint = nextBreakpoint;
|
|
}
|
|
}
|
|
|
|
void WinDebugger::DetachBreakpoint(Breakpoint* breakpoint)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
BfLogDbg("WinDebugger::DetachBreakpoint %p\n", breakpoint);
|
|
|
|
WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
|
|
|
|
if (wdBreakpoint->mAddr != 0)
|
|
{
|
|
mBreakpointAddrMap.Remove(wdBreakpoint->mAddr, wdBreakpoint);
|
|
RemoveBreakpoint(wdBreakpoint->mAddr);
|
|
if ((mDebuggerWaitingThread != NULL) && (mDebuggerWaitingThread->mIsAtBreakpointAddress == wdBreakpoint->mAddr))
|
|
mDebuggerWaitingThread->mIsAtBreakpointAddress = NULL;
|
|
if ((mDebuggerWaitingThread != NULL) && (mDebuggerWaitingThread->mBreakpointAddressContinuing == wdBreakpoint->mAddr))
|
|
mDebuggerWaitingThread->mBreakpointAddressContinuing = NULL;
|
|
wdBreakpoint->mLineData = DbgLineDataEx();
|
|
wdBreakpoint->mAddr = 0;
|
|
}
|
|
|
|
if (wdBreakpoint->mCondition != NULL)
|
|
{
|
|
delete wdBreakpoint->mCondition->mDbgEvaluationContext;
|
|
wdBreakpoint->mCondition->mDbgEvaluationContext = NULL;
|
|
}
|
|
|
|
if (wdBreakpoint->mMemoryBreakpointInfo != NULL)
|
|
{
|
|
for (int memoryWatchSlot = 0; memoryWatchSlot < 4; memoryWatchSlot++)
|
|
{
|
|
if (mMemoryBreakpoints[memoryWatchSlot].mBreakpoint == wdBreakpoint)
|
|
{
|
|
mFreeMemoryBreakIndices.push_back(memoryWatchSlot);
|
|
mMemoryBreakpoints[memoryWatchSlot] = WdMemoryBreakpointBind();
|
|
mMemoryBreakpointVersion++;
|
|
UpdateThreadDebugRegisters();
|
|
}
|
|
}
|
|
|
|
wdBreakpoint->mMemoryBreakpointInfo->mMemoryWatchSlotBitmap = 0;
|
|
}
|
|
|
|
if (wdBreakpoint->mLinkedSibling != NULL)
|
|
{
|
|
DeleteBreakpoint(wdBreakpoint->mLinkedSibling);
|
|
wdBreakpoint->mLinkedSibling = NULL;
|
|
}
|
|
|
|
wdBreakpoint->mSrcFile = NULL;
|
|
wdBreakpoint->mPendingHotBindIdx = -1;
|
|
}
|
|
|
|
void WinDebugger::MoveBreakpoint(Breakpoint* breakpoint, int lineNum, int wantColumn, bool rebindNow)
|
|
{
|
|
WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
|
|
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
DetachBreakpoint(wdBreakpoint);
|
|
|
|
//TODO: This doesn't actually rebind correctly while the app is running
|
|
if ((lineNum != -1) && (wantColumn != -1))
|
|
{
|
|
wdBreakpoint->mRequestedLineNum = lineNum;
|
|
wdBreakpoint->mLineNum = lineNum;
|
|
wdBreakpoint->mColumn = wantColumn;
|
|
}
|
|
if (rebindNow)
|
|
CheckBreakpoint(wdBreakpoint);
|
|
}
|
|
|
|
void WinDebugger::MoveMemoryBreakpoint(Breakpoint* breakpoint, intptr addr, int byteCount)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
|
|
DetachBreakpoint(wdBreakpoint);
|
|
wdBreakpoint->mMemoryBreakpointInfo->mMemoryAddress = addr;
|
|
wdBreakpoint->mMemoryBreakpointInfo->mByteCount = byteCount;
|
|
CheckBreakpoint(wdBreakpoint);
|
|
}
|
|
|
|
void WinDebugger::DisableBreakpoint(Breakpoint* breakpoint)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
|
|
DetachBreakpoint(wdBreakpoint);
|
|
|
|
delete wdBreakpoint->mMemoryBreakpointInfo;
|
|
wdBreakpoint->mMemoryBreakpointInfo = NULL;
|
|
}
|
|
|
|
void WinDebugger::SetBreakpointCondition(Breakpoint* breakpoint, const StringImpl& conditionExpr)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
|
|
BF_ASSERT(!wdBreakpoint->mIsLinkedSibling);
|
|
|
|
if (conditionExpr.empty())
|
|
{
|
|
delete wdBreakpoint->mCondition;
|
|
WdBreakpoint* curBreakpoint = wdBreakpoint;
|
|
wdBreakpoint->mCondition = NULL;
|
|
}
|
|
else
|
|
{
|
|
delete wdBreakpoint->mCondition;
|
|
auto condition = new WdBreakpointCondition();
|
|
condition->mExpr = conditionExpr;
|
|
wdBreakpoint->mCondition = condition;
|
|
}
|
|
}
|
|
|
|
void WinDebugger::SetBreakpointLogging(Breakpoint* breakpoint, const StringImpl& logging, bool breakAfterLogging)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
|
|
BF_ASSERT(!wdBreakpoint->mIsLinkedSibling);
|
|
wdBreakpoint->mLogging = logging;
|
|
wdBreakpoint->mBreakAfterLogging = breakAfterLogging;
|
|
}
|
|
|
|
bool WinDebugger::CheckConditionalBreakpoint(WdBreakpoint* breakpoint, DbgSubprogram* dbgSubprogram, addr_target pcAddress)
|
|
{
|
|
// What was this assertion for?
|
|
//BF_ASSERT(mCallStack.size() == 0);
|
|
|
|
auto headBreakpoint = breakpoint->GetHeadBreakpoint();
|
|
|
|
if (headBreakpoint->mThreadId != -1)
|
|
{
|
|
if ((mActiveThread != NULL) && (mActiveThread->mThreadId != headBreakpoint->mThreadId))
|
|
return false;
|
|
}
|
|
|
|
auto _SplitExpr = [&](const StringImpl& expr, StringImpl& outExpr, StringImpl& outSubject)
|
|
{
|
|
int crPos = expr.IndexOf('\n');
|
|
if (crPos != -1)
|
|
{
|
|
outExpr += expr.Substring(0, crPos);
|
|
outSubject += expr.Substring(crPos + 1);
|
|
}
|
|
else
|
|
{
|
|
outExpr += expr;
|
|
}
|
|
};
|
|
|
|
if (headBreakpoint->mCondition != NULL)
|
|
{
|
|
ClearCallStack();
|
|
|
|
auto conditional = headBreakpoint->mCondition;
|
|
if (conditional->mDbgEvaluationContext == NULL)
|
|
{
|
|
CPURegisters registers;
|
|
PopulateRegisters(®isters);
|
|
auto pcAddress = registers.GetPC();
|
|
DbgSubprogram* subprogram = mDebugTarget->FindSubProgram(pcAddress);
|
|
if (subprogram == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
StringT<256> expr;
|
|
StringT<256> subjectExpr;
|
|
if (breakpoint->mMemoryBreakpointInfo != NULL)
|
|
{
|
|
subjectExpr += "*";
|
|
}
|
|
|
|
_SplitExpr(conditional->mExpr, expr, subjectExpr);
|
|
DbgLanguage language = DbgLanguage_Unknown;
|
|
if (expr.StartsWith("@Beef:"))
|
|
{
|
|
expr.Remove(0, 6);
|
|
language = DbgLanguage_Beef;
|
|
}
|
|
else if (expr.StartsWith("@C:"))
|
|
{
|
|
expr.Remove(0, 3);
|
|
language = DbgLanguage_C;
|
|
}
|
|
|
|
conditional->mDbgEvaluationContext = new DbgEvaluationContext(this, subprogram->mCompileUnit->mDbgModule, expr);
|
|
if (language != DbgLanguage_Unknown)
|
|
conditional->mDbgEvaluationContext->mDbgExprEvaluator->mLanguage = language;
|
|
conditional->mDbgEvaluationContext->mDbgExprEvaluator->mSubjectExpr = subjectExpr;
|
|
conditional->mDbgEvaluationContext->mDbgExprEvaluator->mDbgCompileUnit = subprogram->mCompileUnit;
|
|
conditional->mDbgEvaluationContext->mDbgExprEvaluator->mCallStackIdx = 0;
|
|
conditional->mDbgEvaluationContext->mDbgExprEvaluator->mExpressionFlags = (DwEvalExpressionFlags)(DwEvalExpressionFlag_AllowSideEffects);
|
|
}
|
|
|
|
WdStackFrame* wdStackFrame = new WdStackFrame();
|
|
PopulateRegisters(&wdStackFrame->mRegisters);
|
|
mCallStack.Add(wdStackFrame);
|
|
DbgTypedValue result = conditional->mDbgEvaluationContext->EvaluateInContext(DbgTypedValue());
|
|
ClearCallStack();
|
|
|
|
if ((result.mType != NULL) && (result.mType->mTypeCode == DbgType_Bitfield))
|
|
result.mType = result.mType->mTypeParam;
|
|
|
|
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 (conditional->mDbgEvaluationContext->mDbgExprEvaluator->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 (!result.mBool)
|
|
return false;
|
|
}
|
|
|
|
headBreakpoint->mHitCount++;
|
|
switch (headBreakpoint->mHitCountBreakKind)
|
|
{
|
|
case DbgHitCountBreakKind_Equals:
|
|
if (headBreakpoint->mHitCount != headBreakpoint->mTargetHitCount)
|
|
return false;
|
|
break;
|
|
case DbgHitCountBreakKind_GreaterEquals:
|
|
if (headBreakpoint->mHitCount < headBreakpoint->mTargetHitCount)
|
|
return false;
|
|
break;
|
|
case DbgHitCountBreakKind_Multiple:
|
|
if ((headBreakpoint->mHitCount % headBreakpoint->mTargetHitCount) != 0)
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
mActiveBreakpoint = breakpoint;
|
|
mBreakStackFrameIdx = -1;
|
|
if (!headBreakpoint->mLogging.IsEmpty())
|
|
{
|
|
ClearCallStack();
|
|
|
|
DwFormatInfo formatInfo;
|
|
formatInfo.mCallStackIdx = 0;
|
|
|
|
DbgCompileUnit* dbgCompileUnit = NULL;
|
|
if (dbgSubprogram == NULL)
|
|
dbgSubprogram = mDebugTarget->FindSubProgram(pcAddress);
|
|
if (dbgSubprogram != NULL)
|
|
{
|
|
dbgCompileUnit = dbgSubprogram->mCompileUnit;
|
|
formatInfo.mLanguage = dbgSubprogram->GetLanguage();
|
|
}
|
|
|
|
auto prevRunState = mRunState;
|
|
mRunState = RunState_Paused; // We need to be paused to avoid certain errors in the eval
|
|
String displayString;
|
|
|
|
String expr;
|
|
_SplitExpr(headBreakpoint->mLogging, expr, formatInfo.mSubjectExpr);
|
|
|
|
if (expr.StartsWith("@Script:"))
|
|
{
|
|
displayString = "script ";
|
|
displayString += expr.Substring(8);
|
|
}
|
|
else
|
|
{
|
|
if (expr.StartsWith("@Beef:"))
|
|
{
|
|
expr.Remove(0, 6);
|
|
formatInfo.mLanguage = DbgLanguage_Beef;
|
|
}
|
|
else if (expr.StartsWith("@C:"))
|
|
{
|
|
expr.Remove(0, 3);
|
|
formatInfo.mLanguage = DbgLanguage_C;
|
|
}
|
|
|
|
ProcessEvalString(dbgCompileUnit, DbgTypedValue(), expr, displayString, formatInfo, NULL, false);
|
|
mRunState = prevRunState;
|
|
|
|
displayString.Insert(0, "log ");
|
|
displayString.Append("\n");
|
|
}
|
|
|
|
mDebugManager->mOutMessages.push_back(displayString);
|
|
|
|
if (!headBreakpoint->mBreakAfterLogging)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WinDebugger::CleanupDebugEval(bool restoreRegisters)
|
|
{
|
|
BfLogDbg("CleanupDebugEval ThreadId=%d\n", mDebugEvalThreadInfo.mThreadId);
|
|
|
|
WdThreadInfo* evalThreadInfo = NULL;
|
|
if (mThreadMap.TryGetValue(mDebugEvalThreadInfo.mThreadId, &evalThreadInfo))
|
|
{
|
|
if ((restoreRegisters) && (!mDbgBreak))
|
|
{
|
|
SetAndRestoreValue<WdThreadInfo*> activeThread(mActiveThread, evalThreadInfo);
|
|
RestoreAllRegisters();
|
|
// if (mRunState == RunState_Running_ToTempBreakpoint)
|
|
// mRunState = RunState_Paused;
|
|
}
|
|
|
|
evalThreadInfo->mStartSP = mDebugEvalThreadInfo.mStartSP;
|
|
evalThreadInfo->mStoppedAtAddress = mDebugEvalThreadInfo.mStoppedAtAddress;
|
|
evalThreadInfo->mIsAtBreakpointAddress = mDebugEvalThreadInfo.mIsAtBreakpointAddress;
|
|
evalThreadInfo->mBreakpointAddressContinuing = mDebugEvalThreadInfo.mBreakpointAddressContinuing;
|
|
}
|
|
|
|
delete mDebugPendingExpr;
|
|
mDebugPendingExpr = NULL;
|
|
mDebugEvalThreadInfo = WdThreadInfo();
|
|
|
|
OutputRawMessage("rehupLoc");
|
|
}
|
|
|
|
bool WinDebugger::FixCallStackIdx(int& callStackIdx)
|
|
{
|
|
callStackIdx = BF_MAX(callStackIdx, 0);
|
|
if (mCallStack.IsEmpty())
|
|
UpdateCallStack();
|
|
|
|
int stackSize = (int)mCallStack.size();
|
|
while (callStackIdx >= mCallStack.size())
|
|
{
|
|
UpdateCallStack();
|
|
if (stackSize == (int)mCallStack.size())
|
|
break; // Didn't change
|
|
stackSize = (int)mCallStack.size();
|
|
}
|
|
|
|
if (callStackIdx >= stackSize)
|
|
{
|
|
callStackIdx = 0;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool WinDebugger::HasLineInfoAt(addr_target address)
|
|
{
|
|
BP_ZONE("WinDebugger::HasLineInfoAt");
|
|
|
|
DbgSubprogram* dbgSubprogram = NULL;
|
|
auto dwLineData = FindLineDataAtAddress(address, &dbgSubprogram);
|
|
return (dwLineData != NULL) && (dwLineData->IsStackFrameSetup()) && (dbgSubprogram->GetLineAddr(*dwLineData) == address);
|
|
}
|
|
|
|
void WinDebugger::StepLineTryPause(addr_target address, bool requireExactMatch)
|
|
{
|
|
if (mStepInAssembly)
|
|
return;
|
|
|
|
if (mStepLineData.mLineData != NULL)
|
|
{
|
|
DbgSubprogram* dbgSubprogram = NULL;
|
|
DbgSrcFile* dbgSrcFile = NULL;
|
|
auto dwLineData = FindLineDataAtAddress(address, &dbgSubprogram, &dbgSrcFile);
|
|
if ((dwLineData != NULL) && (dwLineData->IsStackFrameSetup()) && ((!requireExactMatch) || (dbgSubprogram->GetLineAddr(*dwLineData) == address)))
|
|
{
|
|
// "Invalid" line
|
|
if (dwLineData->mColumn == -1)
|
|
{
|
|
SetupStep(mStepType);
|
|
mRunState = RunState_Running;
|
|
return;
|
|
}
|
|
|
|
// If we're on the same line but a different column or a <= address then keep it keep looking
|
|
if ((dbgSrcFile == mStepLineData.GetSrcFile()) &&
|
|
((!requireExactMatch) || (dwLineData != mStepLineData.mLineData) || (address <= mStepStartPC)) &&
|
|
(dwLineData->mLine == mStepLineData.mLineData->mLine))
|
|
{
|
|
SetupStep(mStepType);
|
|
mRunState = RunState_Running;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
mRunState = RunState_Paused;
|
|
}
|
|
|
|
void WinDebugger::BreakAll()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
::DebugBreakProcess(mProcessInfo.hProcess);
|
|
}
|
|
|
|
void WinDebugger::StepInto(bool inAssembly)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!TryRunContinue())
|
|
return;
|
|
|
|
BfLogDbg("StepInto\n");
|
|
|
|
mCurNoInfoStepTries = 0; // Reset
|
|
mStepInAssembly = inAssembly;
|
|
|
|
SetupStep(StepType_StepInto);
|
|
ContinueDebugEvent();
|
|
}
|
|
|
|
void WinDebugger::StepIntoSpecific(intptr inAddr)
|
|
{
|
|
addr_target addr = (addr_target)inAddr;
|
|
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!TryRunContinue())
|
|
return;
|
|
|
|
BfLogDbg("StepIntoSpecific %p\n", addr);
|
|
|
|
mCurNoInfoStepTries = 0; // Reset
|
|
mStepInAssembly = false;
|
|
|
|
SetupStep(StepType_StepInto);
|
|
mIsStepIntoSpecific = true;
|
|
mStepType = StepType_StepInto_Unfiltered;
|
|
if (mStepStartPC != addr)
|
|
{
|
|
RemoveTempBreakpoints();
|
|
SetTempBreakpoint(addr);
|
|
mStepBreakpointAddrs.push_back(addr);
|
|
}
|
|
|
|
ContinueDebugEvent();
|
|
}
|
|
|
|
void WinDebugger::PushValue(CPURegisters* registers, int64 val)
|
|
{
|
|
addr_target* regSP = registers->GetSPRegisterRef();
|
|
*regSP -= sizeof(addr_target);
|
|
WriteMemory<addr_target>(*regSP, (addr_target)val);
|
|
}
|
|
|
|
void WinDebugger::PushValue(CPURegisters* registers, const DbgTypedValue& typedValue)
|
|
{
|
|
addr_target* regSP = registers->GetSPRegisterRef();
|
|
|
|
int byteCount = typedValue.mType->GetByteCount();
|
|
if ((byteCount == 8) || (sizeof(addr_target) == 8))
|
|
{
|
|
*regSP -= sizeof(int64);
|
|
|
|
addr_target val = typedValue.mInt64;
|
|
if (typedValue.mType->IsCompositeType())
|
|
val = typedValue.mSrcAddress;
|
|
WriteMemory<int64>(*regSP, val);
|
|
}
|
|
else
|
|
{
|
|
*regSP -= sizeof(int32);
|
|
addr_target val = typedValue.mInt32;
|
|
if (typedValue.mType->IsCompositeType())
|
|
val = typedValue.mSrcAddress;
|
|
WriteMemory<int32>(*regSP, val);
|
|
}
|
|
}
|
|
|
|
void WinDebugger::SetThisRegister(CPURegisters* registers, addr_target val)
|
|
{
|
|
#if BF_DBG_32
|
|
registers->mIntRegs.ecx = val;
|
|
#else
|
|
registers->mIntRegs.rcx = val;
|
|
#endif
|
|
}
|
|
|
|
void WinDebugger::AddParamValue(int paramIdx, bool hadThis, CPURegisters* registers, const DbgTypedValue& typedValue)
|
|
{
|
|
#if BF_DBG_32
|
|
PushValue(registers, typedValue);
|
|
#else
|
|
int regIdx = paramIdx + (hadThis ? 1 : 0);
|
|
if (typedValue.mType->IsFloat())
|
|
{
|
|
PushValue(registers, typedValue);
|
|
if (regIdx < 4)
|
|
{
|
|
if (typedValue.mType->mTypeCode == DbgType_Single)
|
|
{
|
|
registers->mXmmRegsArray[regIdx].f[0] = typedValue.mSingle;
|
|
}
|
|
else
|
|
{
|
|
registers->mXmmDRegsArray[regIdx].d[0] = typedValue.mDouble;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PushValue(registers, typedValue);
|
|
if (regIdx < 4)
|
|
{
|
|
int64 val;
|
|
if (typedValue.mType->IsCompositeType())
|
|
val = typedValue.mSrcAddress;
|
|
else
|
|
val = typedValue.mPtr;
|
|
if (regIdx == 0)
|
|
registers->mIntRegs.rcx = val;
|
|
else if (regIdx == 1)
|
|
registers->mIntRegs.rdx = val;
|
|
else if (regIdx == 2)
|
|
registers->mIntRegs.r8 = val;
|
|
else if (regIdx == 3)
|
|
registers->mIntRegs.r9 = val;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool WinDebugger::CheckNeedsSRetArgument(DbgType* retType)
|
|
{
|
|
if (!retType->IsCompositeType())
|
|
return false;
|
|
|
|
if (retType->GetByteCount() == 0)
|
|
return false;
|
|
|
|
//TODO: Change when we change the calling convention
|
|
if (retType->GetLanguage() == DbgLanguage_Beef)
|
|
return true;
|
|
|
|
int retSize = retType->GetByteCount();
|
|
//TODO: Check for 'POD' type?
|
|
if ((retSize == 1) || (retSize == 2) || (retSize == 4) || (retSize == sizeof(addr_target)))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
DbgTypedValue WinDebugger::ReadReturnValue(CPURegisters* registers, DbgType* type)
|
|
{
|
|
DbgTypedValue retValue;
|
|
|
|
if (type->IsFloat())
|
|
{
|
|
retValue.mType = type;
|
|
#if BF_DBG_32
|
|
retValue.mDouble = ConvertFloat80ToDouble(registers->mFpMmRegsArray[0].fp.fp80);
|
|
if (type->mSize == 4)
|
|
retValue.mSingle = (float)retValue.mDouble;
|
|
#else
|
|
if (retValue.mType->mTypeCode == DbgType_Single)
|
|
retValue.mSingle = registers->mXmmRegsArray[0].f[0];
|
|
else
|
|
retValue.mDouble = registers->mXmmDRegsArray[0].d[0];
|
|
#endif
|
|
}
|
|
else if (type->IsCompositeType())
|
|
{
|
|
retValue.mType = type;
|
|
if (CheckNeedsSRetArgument(type))
|
|
{
|
|
#ifdef BF_DBG_32
|
|
retValue.mSrcAddress = mSavedContext.Esp - BF_ALIGN(type->GetByteCount(), 16);
|
|
#else
|
|
retValue.mSrcAddress = mSavedContext.Rsp - BF_ALIGN(type->GetByteCount(), 16);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef BF_DBG_32
|
|
retValue.mInt32 = mSavedContext.Eax;
|
|
#else
|
|
retValue.mInt64 = mSavedContext.Rax;
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef BF_DBG_32
|
|
retValue.mType = type;
|
|
retValue.mInt32 = registers->mIntRegs.eax;
|
|
if (type->mSize == 8)
|
|
(&retValue.mInt32)[1] = registers->mIntRegs.edx;
|
|
#else
|
|
retValue.mType = type;
|
|
retValue.mInt64 = registers->mIntRegs.rax;
|
|
#endif
|
|
return retValue;
|
|
}
|
|
|
|
return retValue;
|
|
}
|
|
|
|
bool WinDebugger::SetRegisters(CPURegisters* registers)
|
|
{
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_CONTROL | BF_CONTEXT_INTEGER | BF_CONTEXT_FLOATING_POINT | BF_CONTEXT_EXTENDED_REGISTERS | BF_CONTEXT_SEGMENTS;
|
|
lcContext.ContextFlags |= BF_CONTEXT_EXCEPTION_REQUEST;
|
|
|
|
BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
|
|
#ifdef BF_DBG_32
|
|
lcContext.Eax = registers->mIntRegs.eax;
|
|
lcContext.Ecx = registers->mIntRegs.ecx;
|
|
lcContext.Edx = registers->mIntRegs.edx;
|
|
lcContext.Ebx = registers->mIntRegs.ebx;
|
|
lcContext.Esp = registers->mIntRegs.esp;
|
|
lcContext.Ebp = registers->mIntRegs.ebp;
|
|
lcContext.Esi = registers->mIntRegs.esi;
|
|
lcContext.Edi = registers->mIntRegs.edi;
|
|
lcContext.Eip = registers->mIntRegs.eip;
|
|
lcContext.EFlags = registers->mIntRegs.efl;
|
|
|
|
BF_ASSERT(sizeof(lcContext.FloatSave.RegisterArea) == sizeof(registers->mFpMmRegsArray));
|
|
memcpy(lcContext.FloatSave.RegisterArea, registers->mFpMmRegsArray, sizeof(lcContext.FloatSave.RegisterArea));
|
|
|
|
BF_ASSERT(sizeof(registers->mXmmRegsArray) == 32*sizeof(float));
|
|
memcpy(&lcContext.ExtendedRegisters[160], registers->mXmmRegsArray, sizeof(registers->mXmmRegsArray));
|
|
#else
|
|
lcContext.Rax = registers->mIntRegs.rax;
|
|
lcContext.Rcx = registers->mIntRegs.rcx;
|
|
lcContext.Rdx = registers->mIntRegs.rdx;
|
|
lcContext.Rbx = registers->mIntRegs.rbx;
|
|
lcContext.Rsp = registers->mIntRegs.rsp;
|
|
lcContext.Rbp = registers->mIntRegs.rbp;
|
|
lcContext.Rsi = registers->mIntRegs.rsi;
|
|
lcContext.Rdi = registers->mIntRegs.rdi;
|
|
lcContext.Rip = registers->mIntRegs.rip;
|
|
lcContext.EFlags = (DWORD)registers->mIntRegs.efl;
|
|
|
|
lcContext.R8 = registers->mIntRegs.r8;
|
|
lcContext.R9 = registers->mIntRegs.r9;
|
|
lcContext.R10 = registers->mIntRegs.r10;
|
|
lcContext.R11 = registers->mIntRegs.r11;
|
|
lcContext.R12 = registers->mIntRegs.r12;
|
|
lcContext.R13 = registers->mIntRegs.r13;
|
|
lcContext.R14 = registers->mIntRegs.r14;
|
|
lcContext.R15 = registers->mIntRegs.r15;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
memcpy(&lcContext.FltSave.FloatRegisters[i], ®isters->mFpMmRegsArray[i], 10);
|
|
}
|
|
|
|
BF_ASSERT(sizeof(registers->mXmmRegsArray) == 64 * sizeof(float));
|
|
memcpy(BF_CONTEXT_XMMDATA(lcContext), registers->mXmmRegsArray, sizeof(registers->mXmmRegsArray));
|
|
#endif
|
|
|
|
//lcContext.ContextFlags |= BF_CONTEXT_EXCEPTION_REQUEST;
|
|
|
|
BF_SetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
|
|
return (lcContext.ContextFlags & (BF_CONTEXT_EXCEPTION_ACTIVE | BF_CONTEXT_SERVICE_ACTIVE)) == 0;
|
|
}
|
|
|
|
void WinDebugger::SaveAllRegisters()
|
|
{
|
|
BfLogDbg("SaveAllRegisters setting mSavedAtBreakpointAddress = %p\n", mActiveThread->mIsAtBreakpointAddress);
|
|
mSavedAtBreakpointAddress = mActiveThread->mIsAtBreakpointAddress;
|
|
mSavedBreakpointAddressContinuing = mActiveThread->mBreakpointAddressContinuing;
|
|
mSavedContext.ContextFlags = BF_CONTEXT_ALL;
|
|
BF_GetThreadContext(mActiveThread->mHThread, &mSavedContext);
|
|
}
|
|
|
|
void WinDebugger::RestoreAllRegisters()
|
|
{
|
|
BfLogDbg("RestoreAllRegisters setting mIsAtBreakpointAddress = %p\n", mSavedAtBreakpointAddress);
|
|
mActiveThread->mIsAtBreakpointAddress = mSavedAtBreakpointAddress;
|
|
mActiveThread->mBreakpointAddressContinuing = mSavedBreakpointAddressContinuing;
|
|
BF_SetThreadContext(mActiveThread->mHThread, &mSavedContext);
|
|
|
|
#ifdef BF_DBG_32
|
|
//TODO: Find the test that this was required for...
|
|
// if (mActiveThread->mIsAtBreakpointAddress == mSavedContext.Eip)
|
|
// {
|
|
// if (mRunState == RunState_Running_ToTempBreakpoint)
|
|
// mRunState = RunState_Paused;
|
|
// }
|
|
// else
|
|
// {
|
|
// SetTempBreakpoint(mSavedContext.Eip);
|
|
// mRunState = RunState_Running_ToTempBreakpoint;
|
|
// mStepType = StepType_ToTempBreakpoint;
|
|
// mSteppingThread = mActiveThread;
|
|
// }
|
|
#endif
|
|
}
|
|
|
|
void WinDebugger::OutputMessage(const StringImpl& msg)
|
|
{
|
|
if (this == NULL)
|
|
return;
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
mDebugManager->mOutMessages.push_back("msg " + msg);
|
|
}
|
|
|
|
void WinDebugger::OutputRawMessage(const StringImpl& msg)
|
|
{
|
|
if (this == NULL)
|
|
return;
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
mDebugManager->mOutMessages.push_back(msg);
|
|
}
|
|
|
|
void WinDebugger::SetRunState(RunState runState)
|
|
{
|
|
mRunState = runState;
|
|
}
|
|
|
|
bool WinDebugger::TryRunContinue()
|
|
{
|
|
if (mRunState == RunState_Exception)
|
|
{
|
|
mIsContinuingFromException = true;
|
|
mRunState = RunState_Paused;
|
|
}
|
|
|
|
if (((mRunState == RunState_Paused) || (mRunState == RunState_Breakpoint)) && (mNeedsRehupBreakpoints))
|
|
RehupBreakpoints(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
void WinDebugger::ClearStep()
|
|
{
|
|
BfLogDbg("ClearStep\n");
|
|
|
|
RemoveTempBreakpoints();
|
|
mOrigStepType = StepType_None;
|
|
mStepType = StepType_None;
|
|
mStepStartPC = 0;
|
|
mStepSP = 0;
|
|
mStepPC = 0;
|
|
mIsStepIntoSpecific = false;
|
|
mStepIsRecursing = false;
|
|
mStepStopOnNextInstruction = false;
|
|
mStepLineData = DbgLineDataEx();
|
|
}
|
|
|
|
bool WinDebugger::SetupStep(StepType stepType)
|
|
{
|
|
BP_ZONE("SetupStep");
|
|
|
|
RemoveTempBreakpoints();
|
|
if (mNeedsRehupBreakpoints)
|
|
RehupBreakpoints(true);
|
|
|
|
if (mOrigStepType == StepType_None)
|
|
mOrigStepType = stepType;
|
|
mStepType = stepType;
|
|
mSteppingThread = mActiveThread;
|
|
mStepSwitchedThreads = false;
|
|
mContinueFromBreakpointFailed = false;
|
|
|
|
CPURegisters registers;
|
|
PopulateRegisters(®isters);
|
|
addr_target pcAddress = registers.GetPC();
|
|
|
|
if (mStepLineData.IsNull())
|
|
{
|
|
DbgSubprogram* dbgSubprogram = NULL;
|
|
auto dbgLineData = FindLineDataAtAddress(pcAddress, &dbgSubprogram);
|
|
mStepLineData = DbgLineDataEx(dbgLineData, dbgSubprogram);
|
|
mStepStartPC = registers.GetPC();
|
|
}
|
|
|
|
bool isDeeper = mStepSP > registers.GetSP();
|
|
|
|
BfLogDbg("SetupStep %d PC:%p SP:%p StepStartSP:%p Thread:%d\n", stepType, (addr_target)registers.GetPC(), (addr_target)registers.GetSP(), (addr_target)mStepSP, mSteppingThread->mThreadId);
|
|
|
|
mStepSP = registers.GetSP();
|
|
mStepPC = registers.GetPC();
|
|
|
|
if ((mStepType == StepType_StepOut) || (mStepType == StepType_StepOut_NoFrame) || (mStepType == StepType_StepOut_ThenInto))
|
|
{
|
|
if (mStepType != StepType_StepOut_NoFrame)
|
|
{
|
|
// Test for stepping out of an inline method
|
|
DbgSubprogram* dwSubprogram = mDebugTarget->FindSubProgram(pcAddress);
|
|
if ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL))
|
|
{
|
|
DbgSubprogram* topSubprogram = dwSubprogram->GetRootInlineParent();
|
|
|
|
if ((mOrigStepType == StepType_StepInto) || (mOrigStepType == StepType_StepInto_Unfiltered))
|
|
{
|
|
mStepType = mOrigStepType;
|
|
}
|
|
else
|
|
{
|
|
mStepType = StepType_StepOut_Inline;
|
|
// Set up pcAddress to detect recursion
|
|
//TODO: We can't set a physical breakpoint here because we will immediately hit it when attempting to step over an inlined method.
|
|
// An inlined method can't recurse anyway, but store the pcAddress in mTempBreakpoints because we still check that for recursion
|
|
// SetTempBreakpoint(pcAddress);
|
|
//mTempBreakpoint.push_back(pcAddress);
|
|
mStepBreakpointAddrs.push_back(pcAddress);
|
|
}
|
|
|
|
addr_target endAddress = dwSubprogram->mBlock.mHighPC;
|
|
if (dwSubprogram->mHasLineAddrGaps)
|
|
{
|
|
// Keep bumping out the address as long as we can find lines that contain the nextPC
|
|
addr_target nextAddr = pcAddress;
|
|
for (auto& lineInfo : topSubprogram->mLineInfo->mLines)
|
|
{
|
|
auto lineAddr = topSubprogram->GetLineAddr(lineInfo);
|
|
if ((nextAddr >= lineAddr) && (nextAddr < lineAddr + lineInfo.mContribSize))
|
|
{
|
|
auto ctx = topSubprogram->mLineInfo->mContexts[lineInfo.mCtxIdx];
|
|
if (ctx.mInlinee == dwSubprogram)
|
|
{
|
|
nextAddr = lineAddr + lineInfo.mContribSize;
|
|
}
|
|
}
|
|
}
|
|
if (nextAddr != pcAddress)
|
|
endAddress = nextAddr;
|
|
}
|
|
|
|
BfLogDbg("Stepping out of inlined method, end address: %p\n", endAddress);
|
|
SetTempBreakpoint(endAddress);
|
|
mStepBreakpointAddrs.push_back(endAddress);
|
|
|
|
addr_target decodeAddress = dwSubprogram->mBlock.mLowPC;
|
|
while (decodeAddress < endAddress)
|
|
{
|
|
CPUInst inst;
|
|
if (!mDebugTarget->DecodeInstruction(decodeAddress, &inst))
|
|
break;
|
|
|
|
addr_target targetAddress = inst.GetTarget();
|
|
// We need to find a targetAddress
|
|
if ((targetAddress != 0) &&
|
|
!((targetAddress >= dwSubprogram->mBlock.mLowPC) && (targetAddress < dwSubprogram->mBlock.mHighPC)) &&
|
|
((targetAddress >= topSubprogram->mBlock.mLowPC) && (targetAddress < topSubprogram->mBlock.mHighPC)))
|
|
{
|
|
BfLogDbg("Stepping out of inlined method, branch address: %p\n", targetAddress);
|
|
SetTempBreakpoint(targetAddress);
|
|
mStepBreakpointAddrs.push_back(targetAddress);
|
|
}
|
|
|
|
decodeAddress += inst.GetLength();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if ((mStepType != StepType_StepOut_NoFrame) && (RollBackStackFrame(®isters, true)))
|
|
{
|
|
bool isStackAdjust = false;
|
|
DbgSubprogram* dwSubprogram = mDebugTarget->FindSubProgram(pcAddress);
|
|
if (dwSubprogram != NULL)
|
|
{
|
|
if ((strcmp(dwSubprogram->mName, "_chkstk") == 0) ||
|
|
(strcmp(dwSubprogram->mName, "__chkstk") == 0) ||
|
|
(strcmp(dwSubprogram->mName, "_alloca_probe") == 0))
|
|
isStackAdjust = true;
|
|
}
|
|
|
|
pcAddress = registers.GetPC();
|
|
if (isStackAdjust)
|
|
{
|
|
// We set it to zero so we never detect an "isDeeper" condition which would skip over the return-location breakpoint
|
|
mStepSP = 0;
|
|
}
|
|
else
|
|
{
|
|
addr_target oldAddress = pcAddress;
|
|
|
|
CPUInst inst;
|
|
while (true)
|
|
{
|
|
if (mStepInAssembly)
|
|
break;
|
|
if (!mDebugTarget->DecodeInstruction(pcAddress, &inst))
|
|
break;
|
|
if ((inst.IsBranch()) || (inst.IsCall()) || (inst.IsReturn()))
|
|
break;
|
|
#ifdef BF_DBG_32
|
|
if (!inst.StackAdjust(mStepSP))
|
|
break;
|
|
#endif
|
|
DbgSubprogram* checkSubprogram = NULL;
|
|
auto checkLineData = FindLineDataAtAddress(pcAddress, &checkSubprogram, NULL, NULL, DbgOnDemandKind_LocalOnly);
|
|
if (checkLineData == NULL)
|
|
break;
|
|
if (checkSubprogram->GetLineAddr(*checkLineData) == pcAddress)
|
|
break;
|
|
pcAddress += inst.GetLength();
|
|
}
|
|
|
|
if (pcAddress != oldAddress)
|
|
{
|
|
BfLogDbg("Adjusting stepout address from %p to %p\n", oldAddress, pcAddress);
|
|
}
|
|
}
|
|
|
|
BfLogDbg("SetupStep Stepout SetTempBreakpoint %p\n", pcAddress);
|
|
SetTempBreakpoint(pcAddress);
|
|
mStepBreakpointAddrs.push_back(pcAddress);
|
|
if (mStepType != StepType_StepOut_ThenInto)
|
|
mStepType = StepType_StepOut;
|
|
}
|
|
else
|
|
{
|
|
// Try to handle the case where we just entered this call so the return address is the first entry on the stack
|
|
addr_target* regSP = registers.GetSPRegisterRef();
|
|
pcAddress = ReadMemory<addr_target>(*regSP);
|
|
*regSP += sizeof(addr_target);
|
|
if (mDebugTarget->FindSubProgram(pcAddress) != NULL)
|
|
{
|
|
BfLogDbg("SetupStep Stepout SetTempBreakpoint (2) %p\n", pcAddress);
|
|
|
|
SetTempBreakpoint(pcAddress);
|
|
mStepBreakpointAddrs.push_back(pcAddress);
|
|
if (mOrigStepType == StepType_StepInto)
|
|
mStepType = StepType_StepInto;
|
|
else
|
|
mStepType = StepType_StepOver;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Just do stepovers until we eventually step out
|
|
//BF_DBG_FATAL("StepOut Failed");
|
|
BfLogDbg("StepOut Failed\n");
|
|
|
|
if (mLastValidStepIntoPC != 0)
|
|
{
|
|
BfLogDbg("Using mLastValidStepIntoPC: %p\n", mLastValidStepIntoPC);
|
|
if (mOrigStepType == StepType_StepInto)
|
|
mStepType = StepType_StepInto;
|
|
else
|
|
mStepType = StepType_StepOver;
|
|
SetTempBreakpoint(mLastValidStepIntoPC);
|
|
mStepBreakpointAddrs.push_back(0);
|
|
mStepBreakpointAddrs.push_back(mLastValidStepIntoPC);
|
|
mLastValidStepIntoPC = 0;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
BfLogDbg("Stopping");
|
|
mStepType = StepType_None;
|
|
mRunState = RunState_Paused;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((mStepType != StepType_StepOut) && (mStepType != StepType_StepOut_ThenInto))
|
|
{
|
|
if (mDebuggerWaitingThread != mSteppingThread)
|
|
{
|
|
// We've switched threads, so there's a possible race condition:
|
|
// This new thread may already have an EXCEPTION_BREAKPOINT queued up so the PC is actually
|
|
// located one byte past the BREAK instruction, which is one byte into whatever instruction
|
|
// was previously there. We can't insert normal BREAK instructions because we don't know
|
|
// if the current PC is actually at an instruction start, so we do a single step with a
|
|
// slower stack call check to see if we need to step out after a "step over"
|
|
BfLogDbg("Step - switched threads mIsAtBreakpointAddress:%p\n", mSteppingThread->mIsAtBreakpointAddress);
|
|
mStepSwitchedThreads = true;
|
|
SingleStepX86();
|
|
return true;
|
|
}
|
|
|
|
bool breakOnNext = false;
|
|
|
|
int instIdx = 0;
|
|
for (instIdx = 0; true; instIdx++)
|
|
{
|
|
bool isAtLine = false;
|
|
DbgSubprogram* dwSubprogram = NULL;
|
|
|
|
auto dwLineData = FindLineDataAtAddress(pcAddress, &dwSubprogram, NULL, NULL, DbgOnDemandKind_LocalOnly);
|
|
isAtLine = (instIdx > 0) && (dwLineData != NULL) && (dwLineData->IsStackFrameSetup()) && (dwSubprogram->GetLineAddr(*dwLineData) == pcAddress);
|
|
|
|
// "Never step into" line
|
|
if ((dwLineData != NULL) && (dwLineData->mColumn == -2) && (stepType == StepType_StepInto))
|
|
stepType = StepType_StepOver;
|
|
|
|
CPUInst inst;
|
|
if (!mDebugTarget->DecodeInstruction(pcAddress, &inst))
|
|
{
|
|
BfLogDbg("Decode failed, set up SingleStepX86 %p\n", pcAddress);
|
|
SingleStepX86();
|
|
mStepStopOnNextInstruction = true;
|
|
return true;
|
|
}
|
|
|
|
if (instIdx > 256)
|
|
{
|
|
BfLogDbg("Too many SetupStep iterations");
|
|
breakOnNext = true;
|
|
}
|
|
|
|
if ((inst.IsReturn()) && (instIdx == 0) && (!mStepInAssembly))
|
|
{
|
|
// Do actual STEP OUT so we set up proper "stepping over unimportant post-return instructions"
|
|
if (stepType == StepType_StepInto)
|
|
return SetupStep(StepType_StepOut_ThenInto);
|
|
else
|
|
return SetupStep(StepType_StepOut);
|
|
}
|
|
|
|
if ((breakOnNext) || (mStepInAssembly) || (isAtLine) || (inst.IsBranch()) || (inst.IsCall()) || (inst.IsReturn()))
|
|
{
|
|
if (((instIdx == 0) || (mStepInAssembly)) && (!breakOnNext))
|
|
{
|
|
if ((stepType == StepType_StepOver) && (inst.IsCall()))
|
|
{
|
|
// Continue - sets a breakpoint on the call line to detect recursion.
|
|
// The next loop through will set a breakpoint on the line after the return
|
|
BfLogDbg("StepHadCall\n");
|
|
breakOnNext = true;
|
|
|
|
BfLogDbg("StepHadCall setting mIsAtBreakpointAddress = %p\n", pcAddress);
|
|
mSteppingThread->mIsAtBreakpointAddress = pcAddress;
|
|
|
|
SetTempBreakpoint(pcAddress);
|
|
mStepBreakpointAddrs.push_back(pcAddress);
|
|
}
|
|
else
|
|
{
|
|
if (inst.IsCall())
|
|
{
|
|
if ((mLastValidStepIntoPC == 0) || (dwSubprogram != NULL))
|
|
mLastValidStepIntoPC = pcAddress + inst.mSize;
|
|
}
|
|
|
|
if ((dwLineData != NULL) && (inst.IsBranch()))
|
|
{
|
|
addr_target targetAddr = inst.GetTarget();
|
|
if (targetAddr < dwSubprogram->GetLineAddr(*dwLineData))
|
|
{
|
|
// Jumping backwards, stop at next instruction
|
|
mStepStopOnNextInstruction = true;
|
|
}
|
|
}
|
|
|
|
bool isPrefixOnly = false;
|
|
if ((mStepInAssembly) && (stepType == StepType_StepOver) && (inst.IsRep(isPrefixOnly)))
|
|
{
|
|
if (isPrefixOnly)
|
|
{
|
|
CPUInst nextInst;
|
|
if (mDebugTarget->DecodeInstruction(pcAddress + inst.GetLength(), &nextInst))
|
|
{
|
|
if (nextInst.IsBranch())
|
|
{
|
|
// repne jmp - this appears in __chkstk (for example)
|
|
// We don't have a good way to "step over" this one, so just do a single step
|
|
}
|
|
else
|
|
{
|
|
// Step over the rep + target instruction
|
|
auto doneAddr = pcAddress + inst.GetLength() + nextInst.GetLength();
|
|
BfLogDbg("SetupStep SetTempBreakpoint %p\n", doneAddr);
|
|
SetTempBreakpoint(doneAddr);
|
|
mStepBreakpointAddrs.push_back(doneAddr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Step over the instruction
|
|
auto doneAddr = pcAddress + inst.GetLength();
|
|
BfLogDbg("SetupStep SetTempBreakpoint %p\n", doneAddr);
|
|
SetTempBreakpoint(doneAddr);
|
|
mStepBreakpointAddrs.push_back(doneAddr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Just step a single instruction
|
|
BfLogDbg("SetupStep SingleStepX86 %p\n", pcAddress);
|
|
SingleStepX86();
|
|
if (inst.IsReturn())
|
|
mStepStopOnNextInstruction = true;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Move us to this instruction so we can hardware single-step into it
|
|
BfLogDbg("SetupStep SetTempBreakpoint %p\n", pcAddress);
|
|
SetTempBreakpoint(pcAddress);
|
|
mStepBreakpointAddrs.push_back(pcAddress);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Not an interesting instruction - move to next
|
|
pcAddress += inst.mSize;
|
|
|
|
if ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL) && (pcAddress >= dwSubprogram->mBlock.mHighPC))
|
|
{
|
|
auto endAddress = dwSubprogram->mBlock.mHighPC;
|
|
BfLogDbg("Stepping past end of inlined method, end address: %p\n", endAddress);
|
|
|
|
mStepType = StepType_StepOut_Inline;
|
|
SetTempBreakpoint(endAddress);
|
|
mStepBreakpointAddrs.push_back(endAddress);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (instIdx > 1)
|
|
BfLogDbg("SetupStep instIdx: %d\n", instIdx);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WinDebugger::CheckNonDebuggerBreak()
|
|
{
|
|
enum MessageType
|
|
{
|
|
MessageType_None = 0,
|
|
MessageType_Error = 1,
|
|
MessageType_ProfilerCmd = 2
|
|
};
|
|
|
|
CPURegisters registers;
|
|
PopulateRegisters(®isters);
|
|
addr_target pcAddress = registers.GetPC();
|
|
|
|
addr_target debugMessageDataAddr = (addr_target)-1;
|
|
if (mDebugTarget->mTargetBinary != NULL)
|
|
{
|
|
mDebugTarget->mTargetBinary->ParseSymbolData();
|
|
debugMessageDataAddr = mDebugTarget->FindSymbolAddr("gBfDebugMessageData");
|
|
}
|
|
if (debugMessageDataAddr != (addr_target)-1)
|
|
{
|
|
struct BfDebugMessageData
|
|
{
|
|
int mMessageType; // 0 = none, 1 = error
|
|
int mStackWindbackCount;
|
|
int mBufParamLen;
|
|
addr_target mBufParam;
|
|
addr_target mPCOverride;
|
|
};
|
|
BfDebugMessageData messageData = ReadMemory<BfDebugMessageData>(debugMessageDataAddr);
|
|
WriteMemory<int>(debugMessageDataAddr, 0); // Zero out type so we won't trigger again
|
|
if (messageData.mMessageType != 0)
|
|
{
|
|
llvm::SmallVector<char, 4096> strBuf;
|
|
int strLen = messageData.mBufParamLen;
|
|
strBuf.resize(strLen + 1);
|
|
|
|
char* str = &strBuf[0];
|
|
str[strLen] = 0;
|
|
if (ReadMemory(messageData.mBufParam, strLen, str))
|
|
{
|
|
if (messageData.mMessageType == MessageType_Error)
|
|
{
|
|
mRequestedStackFrameIdx = messageData.mStackWindbackCount;
|
|
if (messageData.mPCOverride != 0)
|
|
{
|
|
mShowPCOverride = messageData.mPCOverride;
|
|
mRequestedStackFrameIdx = -2;
|
|
}
|
|
mDebugManager->mOutMessages.push_back(StrFormat("error %s", str));
|
|
}
|
|
else if (messageData.mMessageType == MessageType_ProfilerCmd)
|
|
{
|
|
// It's important to set this here, because we unlock the critSect during StopSampling and we can't have the
|
|
// IDE thinking that we're actually paused when it checks the mRunState
|
|
mRunState = RunState_Running;
|
|
|
|
char* cmd = strtok(str, "\t");
|
|
if (strcmp(cmd, "StartSampling") == 0)
|
|
{
|
|
char* sessionIdStr = strtok(NULL, "\t");
|
|
char* threadIdStr = strtok(NULL, "\t");
|
|
char* sampleRateStr = strtok(NULL, "\t");
|
|
char* descStr = strtok(NULL, "\t");
|
|
|
|
if (threadIdStr != NULL)
|
|
{
|
|
int threadId = atoi(threadIdStr);
|
|
int sampleRate = atoi(sampleRateStr);
|
|
int sessionId = atoi(sessionIdStr);
|
|
|
|
Profiler** profilerPtr;
|
|
if (mPendingProfilerMap.TryAdd(sessionId, NULL, &profilerPtr))
|
|
{
|
|
DbgProfiler* profiler = new DbgProfiler(this);
|
|
if (descStr != NULL)
|
|
profiler->mDescription = descStr;
|
|
if (sampleRate > 0)
|
|
profiler->mSamplesPerSecond = sampleRate;
|
|
profiler->Start();
|
|
*profilerPtr = profiler;
|
|
|
|
mDebugManager->mOutMessages.push_back("newProfiler");
|
|
mNewProfilerList.push_back(profiler);
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(cmd, "StopSampling") == 0)
|
|
{
|
|
char* sessionIdStr = strtok(NULL, "\t");
|
|
if (sessionIdStr != NULL)
|
|
{
|
|
int sessionId = atoi(sessionIdStr);
|
|
|
|
Profiler* profiler;
|
|
if (mPendingProfilerMap.Remove(sessionId, &profiler))
|
|
{
|
|
if (profiler->IsSampling())
|
|
{
|
|
// Need to unlock so we don't deadlock
|
|
mDebugManager->mCritSect.Unlock();
|
|
profiler->Stop();
|
|
mDebugManager->mCritSect.Lock();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(cmd, "ClearSampling") == 0)
|
|
{
|
|
for (auto& kv : mPendingProfilerMap)
|
|
{
|
|
auto profiler = kv.mValue;
|
|
profiler->Clear();
|
|
}
|
|
}
|
|
else if (strcmp(cmd, "ClearOutput") == 0)
|
|
{
|
|
mDebugManager->mOutMessages.push_back("clearOutput");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
intptr_target objAddr;
|
|
auto dbgBreakKind = mDebugTarget->GetDbgBreakKind(pcAddress, ®isters, &objAddr);
|
|
if (dbgBreakKind == DbgBreakKind_ObjectAccess)
|
|
{
|
|
String errorStr = "error Attempted to access deleted object";
|
|
String objectAddr = EncodeDataPtr((addr_target)objAddr, true);
|
|
errorStr += StrFormat("\x1LEAK\t(System.Object)%s\n (%s)%s\n", objectAddr.c_str(), "System.Object", objectAddr.c_str());
|
|
mDebugManager->mOutMessages.push_back(errorStr);
|
|
return;
|
|
}
|
|
else if (dbgBreakKind == DbgBreakKind_ArithmeticOverflow)
|
|
{
|
|
String errorStr = "error Arithmetic overflow detected";
|
|
mDebugManager->mOutMessages.push_back(errorStr);
|
|
return;
|
|
}
|
|
|
|
bool showMainThread = false;
|
|
|
|
String symbol;
|
|
addr_target offset;
|
|
DbgModule* dbgModule;
|
|
if (mDebugTarget->FindSymbolAt(pcAddress, &symbol, &offset, &dbgModule))
|
|
{
|
|
if ((symbol == "DbgBreakPoint") || (symbol == "RtlUserThreadStart") || (symbol == "RtlUserThreadStart@8"))
|
|
{
|
|
showMainThread = true;
|
|
}
|
|
}
|
|
#ifdef BF_DBG_32
|
|
else if ((dbgModule != NULL) && (dbgModule->mDisplayName.Equals("kernel32.dll", StringImpl::CompareKind_OrdinalIgnoreCase)))
|
|
{
|
|
showMainThread = true;
|
|
}
|
|
#endif
|
|
|
|
if (showMainThread)
|
|
{
|
|
// This is a manual break, show the main thread
|
|
mActiveThread = mThreadList.front();
|
|
if (mDebugPendingExpr != NULL)
|
|
{
|
|
for (auto thread : mThreadList)
|
|
{
|
|
if (thread->mThreadId == mDebugEvalThreadInfo.mThreadId)
|
|
{
|
|
mActiveThread = thread;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool WinDebugger::HasSteppedIntoCall()
|
|
{
|
|
// Some calls (like __chkstk) actually push results to the stack, so we need to check
|
|
// if we're REALLY deeper or not, by rolling back the callstack once
|
|
|
|
CPURegisters registers;
|
|
PopulateRegisters(®isters);
|
|
if (RollBackStackFrame(®isters, true))
|
|
{
|
|
// If the previous frames SP is equal or deeper than our step start then we are indeed inside a call!
|
|
if (mStepSP >= registers.GetSP())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void WinDebugger::StepOver(bool inAssembly)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
BfLogDbg("StepOver\n");
|
|
|
|
if (!TryRunContinue())
|
|
return;
|
|
|
|
mCurNoInfoStepTries = 0; // Reset
|
|
mStepInAssembly = inAssembly;
|
|
|
|
SetupStep(StepType_StepOver);
|
|
ContinueDebugEvent();
|
|
}
|
|
|
|
void WinDebugger::StepOut(bool inAssembly)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
BfLogDbg("StepOut\n");
|
|
|
|
if (!TryRunContinue())
|
|
return;
|
|
|
|
mCurNoInfoStepTries = 0; // Reset
|
|
mStepInAssembly = inAssembly;
|
|
|
|
SetupStep(StepType_StepOut);
|
|
|
|
ContinueDebugEvent();
|
|
}
|
|
|
|
void WinDebugger::SetNextStatement(bool inAssembly, const StringImpl& fileName, int64 lineNumOrAsmAddr, int wantColumn)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
DbgSubprogram* subProgram = NULL;
|
|
if (!inAssembly)
|
|
{
|
|
if (mCallStack.size() == 0)
|
|
UpdateCallStack();
|
|
if (mCallStack.size() > 0)
|
|
{
|
|
UpdateCallStackMethod(0);
|
|
subProgram = mCallStack[0]->mSubProgram;
|
|
}
|
|
if (subProgram == NULL)
|
|
return;
|
|
}
|
|
|
|
DbgSubprogram* rootInlineParent = NULL;
|
|
if (subProgram != NULL)
|
|
rootInlineParent = subProgram->GetRootInlineParent();
|
|
|
|
String result;
|
|
if (mDebugTarget == NULL)
|
|
return;
|
|
|
|
DbgSrcFile* srcFile = NULL;
|
|
if (!fileName.IsEmpty())
|
|
{
|
|
srcFile = mDebugTarget->GetSrcFile(fileName);
|
|
if (srcFile == NULL)
|
|
return;
|
|
}
|
|
|
|
addr_target pcAddress = 0;
|
|
if (inAssembly)
|
|
{
|
|
pcAddress = lineNumOrAsmAddr;
|
|
}
|
|
else
|
|
{
|
|
int lineNum = (int)lineNumOrAsmAddr;
|
|
|
|
addr_target bestAddr[2] = { 0, 0 };
|
|
int checkLineNum[2] = { lineNum - 1, lineNum };
|
|
|
|
auto _CheckLineInfo = [&](DbgSubprogram* dbgSubprogram, DbgLineInfo* dbgLineInfo)
|
|
{
|
|
for (int iPass = 0; iPass < 2; ++iPass)
|
|
{
|
|
int bestLineOffset = 0x7FFFFFFF;
|
|
|
|
for (auto& lineData : dbgLineInfo->mLines)
|
|
{
|
|
auto addr = dbgSubprogram->GetLineAddr(lineData);
|
|
if ((addr < subProgram->mBlock.mLowPC) || (addr >= subProgram->mBlock.mHighPC))
|
|
continue;
|
|
|
|
int lineOffset = lineData.mLine - checkLineNum[iPass];
|
|
|
|
if ((lineOffset >= 0) && (lineOffset <= 6) && (lineOffset <= bestLineOffset))
|
|
{
|
|
if (lineOffset < bestLineOffset)
|
|
{
|
|
bestLineOffset = lineOffset;
|
|
bestAddr[iPass] = addr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
for (int checkHotIdx = -1; checkHotIdx < (int)srcFile->mHotReplacedDbgLineInfo.size(); checkHotIdx++)
|
|
{
|
|
if (checkHotIdx >= 0)
|
|
{
|
|
auto hotReplacedLineInfo = srcFile->mHotReplacedDbgLineInfo[checkHotIdx];
|
|
for (auto& hotReplacedEntry : hotReplacedLineInfo->mEntries)
|
|
{
|
|
_CheckLineInfo(hotReplacedEntry.mSubprogram, hotReplacedEntry.mLineInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (auto subprogram : srcFile->mLineDataRefs)
|
|
_CheckLineInfo(subprogram, subprogram->mLineInfo);
|
|
}
|
|
|
|
if (bestAddr[1] != 0)
|
|
break;
|
|
}
|
|
|
|
if (bestAddr[1] != 0)
|
|
{
|
|
const int kMaxAddrDist = 64; // within reasonable range
|
|
if ((bestAddr[0] != 0) && (bestAddr[1] - bestAddr[0] <= kMaxAddrDist))
|
|
{
|
|
addr_target addrStart = bestAddr[0];
|
|
addr_target addrEnd = bestAddr[1];
|
|
addr_target addr = addrStart;
|
|
|
|
BF_ASSERT(addrEnd - addr <= kMaxAddrDist);
|
|
|
|
addr_target lastOp = 0;
|
|
while (addr < addrEnd)
|
|
{
|
|
CPUInst inst;
|
|
if (!mDebugTarget->DecodeInstruction(addr, &inst))
|
|
break;
|
|
lastOp = addr;
|
|
addr += inst.GetLength();
|
|
}
|
|
}
|
|
|
|
pcAddress = (uint64)bestAddr[1];
|
|
}
|
|
}
|
|
|
|
if (pcAddress)
|
|
{
|
|
BF_ASSERT(mActiveThread->mBreakpointAddressContinuing == 0);
|
|
mActiveThread->mIsAtBreakpointAddress = 0;
|
|
mActiveThread->mStoppedAtAddress = pcAddress;
|
|
|
|
if (mCallStack.size() == 0)
|
|
UpdateCallStack();
|
|
|
|
CPURegisters* regs = &mCallStack.front()->mRegisters;
|
|
*regs->GetPCRegisterRef() = pcAddress;
|
|
|
|
SetRegisters(regs);
|
|
|
|
WdBreakpoint* breakpoint = (WdBreakpoint*)FindBreakpointAt(pcAddress);
|
|
if (breakpoint != NULL)
|
|
{
|
|
BfLogDbg("SetNextStatement setting mIsAtBreakpointAddress = %p\n", breakpoint->mAddr);
|
|
mActiveThread->mIsAtBreakpointAddress = breakpoint->mAddr;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool WinDebugger::PopulateRegisters(CPURegisters* registers, BF_CONTEXT& lcContext)
|
|
{
|
|
#ifdef BF_DBG_32
|
|
registers->mIntRegs.eax = lcContext.Eax;
|
|
registers->mIntRegs.ecx = lcContext.Ecx;
|
|
registers->mIntRegs.edx = lcContext.Edx;
|
|
registers->mIntRegs.ebx = lcContext.Ebx;
|
|
registers->mIntRegs.esp = lcContext.Esp;
|
|
registers->mIntRegs.ebp = lcContext.Ebp;
|
|
registers->mIntRegs.esi = lcContext.Esi;
|
|
registers->mIntRegs.edi = lcContext.Edi;
|
|
registers->mIntRegs.eip = lcContext.Eip;
|
|
registers->mIntRegs.efl = lcContext.EFlags;
|
|
|
|
BF_ASSERT(sizeof(lcContext.FloatSave.RegisterArea) == sizeof(registers->mFpMmRegsArray));
|
|
memcpy(registers->mFpMmRegsArray, lcContext.FloatSave.RegisterArea, sizeof(lcContext.FloatSave.RegisterArea));
|
|
|
|
BF_ASSERT(sizeof(registers->mXmmRegsArray) == 32 * sizeof(float));
|
|
memcpy(registers->mXmmRegsArray, &lcContext.ExtendedRegisters[160], sizeof(registers->mXmmRegsArray));
|
|
#else
|
|
registers->mIntRegs.rax = lcContext.Rax;
|
|
registers->mIntRegs.rcx = lcContext.Rcx;
|
|
registers->mIntRegs.rdx = lcContext.Rdx;
|
|
registers->mIntRegs.rbx = lcContext.Rbx;
|
|
registers->mIntRegs.rsp = lcContext.Rsp;
|
|
registers->mIntRegs.rbp = lcContext.Rbp;
|
|
registers->mIntRegs.rsi = lcContext.Rsi;
|
|
registers->mIntRegs.rdi = lcContext.Rdi;
|
|
registers->mIntRegs.rip = lcContext.Rip;
|
|
registers->mIntRegs.efl = lcContext.EFlags;
|
|
|
|
registers->mIntRegs.r8 = lcContext.R8;
|
|
registers->mIntRegs.r9 = lcContext.R9;
|
|
registers->mIntRegs.r10 = lcContext.R10;
|
|
registers->mIntRegs.r11 = lcContext.R11;
|
|
registers->mIntRegs.r12 = lcContext.R12;
|
|
registers->mIntRegs.r13 = lcContext.R13;
|
|
registers->mIntRegs.r14 = lcContext.R14;
|
|
registers->mIntRegs.r15 = lcContext.R15;
|
|
|
|
registers->mIntRegs.gs = lcContext.SegGs;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
memcpy(®isters->mFpMmRegsArray[i], &lcContext.FltSave.FloatRegisters[i], 10);
|
|
}
|
|
|
|
BF_ASSERT(sizeof(registers->mXmmRegsArray) == 64 * sizeof(float));
|
|
memcpy(registers->mXmmRegsArray, BF_CONTEXT_XMMDATA(lcContext), sizeof(registers->mXmmRegsArray));
|
|
#endif
|
|
|
|
return (lcContext.ContextFlags & (BF_CONTEXT_EXCEPTION_ACTIVE | BF_CONTEXT_SERVICE_ACTIVE)) == 0;
|
|
}
|
|
|
|
bool WinDebugger::PopulateRegisters(CPURegisters* registers)
|
|
{
|
|
/*static bool sCheckedProcessorFeatures = false;
|
|
static bool sMmxAvailable = false;
|
|
static bool sXmmAvailable = false;
|
|
if (!sCheckedProcessorFeatures)
|
|
{
|
|
//CDH we don't do anything with these yet since we grab BF_CONTEXT_ALL anyway, but could be useful
|
|
sMmxAvailable = ::IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE) != 0;
|
|
sXmmAvailable = ::IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE) != 0;
|
|
sCheckedProcessorFeatures = true;
|
|
}*/
|
|
|
|
BF_ASSERT(registers != nullptr);
|
|
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_ALL | BF_CONTEXT_EXCEPTION_REQUEST;
|
|
BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
|
|
return PopulateRegisters(registers, lcContext);
|
|
}
|
|
|
|
bool WinDebugger::RollBackStackFrame(CPURegisters* registers, bool isStackStart)
|
|
{
|
|
BF_ASSERT(registers != nullptr);
|
|
|
|
return mDebugTarget->RollBackStackFrame(registers, NULL, isStackStart);
|
|
}
|
|
|
|
bool WinDebugger::SetHotJump(DbgSubprogram* oldSubprogram, addr_target newTarget, int newTargetSize)
|
|
{
|
|
BfLogDbg("SetHotJump %s %p->%p\n", oldSubprogram->mName, oldSubprogram->mBlock.mLowPC, newTarget);
|
|
|
|
//AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
BF_ASSERT(mDebugManager->mCritSect.mLockCount == 1);
|
|
|
|
addr_target jmpInstStart = oldSubprogram->mBlock.mLowPC;
|
|
addr_target jmpInstEnd = jmpInstStart + sizeof(HotJumpOp);
|
|
|
|
if (jmpInstEnd > oldSubprogram->mBlock.mHighPC)
|
|
{
|
|
if ((oldSubprogram->mBlock.mHighPC - oldSubprogram->mBlock.mLowPC == 1) &&
|
|
(newTargetSize == 1))
|
|
return true; // Special case for just stub 'ret' methods
|
|
String err = StrFormat("Failed to hot replace method, method '%s' too small to insert hot thunk", oldSubprogram->ToString().c_str());
|
|
Fail(err);
|
|
return false;
|
|
}
|
|
|
|
if (oldSubprogram->mHotReplaceKind != DbgSubprogram::HotReplaceKind_Replaced)
|
|
{
|
|
for (int hotThreadIdx = 0; hotThreadIdx < (int)mHotThreadStates.size(); hotThreadIdx++)
|
|
{
|
|
auto& hotThreadState = mHotThreadStates[hotThreadIdx];
|
|
WdThreadInfo* threadInfo = NULL;
|
|
if (!mThreadMap.TryGetValue((uint32)hotThreadState.mThreadId, &threadInfo))
|
|
continue;
|
|
|
|
int tryStart = GetTickCount();
|
|
|
|
while ((hotThreadState.mRegisters.GetPC() >= jmpInstStart) && (hotThreadState.mRegisters.GetPC() < jmpInstEnd))
|
|
{
|
|
if (GetTickCount() - tryStart >= 8000)
|
|
{
|
|
Fail("Failed to hot replace method, can't move past prelude");
|
|
return false;
|
|
}
|
|
|
|
BfLogDbg("SetHotJump skipping through %p\n", hotThreadState.mRegisters.GetPC());
|
|
|
|
bool removedBreakpoint = false;
|
|
|
|
mActiveThread = threadInfo;
|
|
if ((mActiveThread->mStoppedAtAddress >= jmpInstStart) && (mActiveThread->mStoppedAtAddress < jmpInstEnd))
|
|
{
|
|
for (addr_target addr = jmpInstStart; addr < jmpInstEnd; addr++)
|
|
{
|
|
if (mPhysBreakpointAddrMap.ContainsKey(addr))
|
|
{
|
|
removedBreakpoint = true;
|
|
RemoveBreakpoint(addr);
|
|
}
|
|
}
|
|
}
|
|
|
|
RunState oldRunState = mRunState;
|
|
mRunState = RunState_HotStep;
|
|
|
|
if (mWantsDebugContinue)
|
|
{
|
|
mWantsDebugContinue = false;
|
|
BF_ASSERT_REL(mActiveThread->mIsAtBreakpointAddress == 0);
|
|
mContinueEvent.Set();
|
|
}
|
|
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_ALL;
|
|
BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
lcContext.EFlags |= 0x100; // Set trap flag, which raises "single-step" exception
|
|
BF_SetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
|
|
::ResumeThread(mActiveThread->mHThread);
|
|
BfLogDbg("ResumeThread %d\n", mActiveThread->mThreadId);
|
|
|
|
while (mRunState != RunState_Terminated)
|
|
{
|
|
mDebugManager->mCritSect.Unlock();
|
|
Sleep(0);
|
|
mDebugManager->mCritSect.Lock();
|
|
if (IsPaused())
|
|
break;
|
|
if (mWantsDebugContinue)
|
|
{
|
|
BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
|
|
mWantsDebugContinue = false;
|
|
BF_ASSERT_REL(mActiveThread->mIsAtBreakpointAddress == 0);
|
|
mContinueEvent.Set();
|
|
}
|
|
}
|
|
|
|
BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
|
|
|
|
::SuspendThread(mActiveThread->mHThread);
|
|
BfLogDbg("SuspendThread %d\n", mActiveThread->mThreadId);
|
|
mRunState = oldRunState;
|
|
|
|
if ((mRunState != RunState_Terminated) && (mRunState != RunState_Terminating))
|
|
{
|
|
if (!IsPaused())
|
|
{
|
|
BF_ASSERT(mWantsDebugContinue);
|
|
mWantsDebugContinue = false;
|
|
BF_ASSERT_REL(mActiveThread->mIsAtBreakpointAddress == 0);
|
|
mContinueEvent.Set();
|
|
}
|
|
}
|
|
|
|
PopulateRegisters(&hotThreadState.mRegisters);
|
|
}
|
|
}
|
|
}
|
|
|
|
HotJumpOp jumpOp;
|
|
jumpOp.mOpCode = 0xE9;
|
|
jumpOp.mRelTarget = newTarget - oldSubprogram->mBlock.mLowPC - sizeof(HotJumpOp);
|
|
WriteMemory(oldSubprogram->mBlock.mLowPC, jumpOp);
|
|
::FlushInstructionCache(mProcessInfo.hProcess, (void*)(intptr)oldSubprogram->mBlock.mLowPC, sizeof(HotJumpOp));
|
|
return true;
|
|
}
|
|
|
|
DbgSubprogram* WinDebugger::TryFollowHotJump(DbgSubprogram* subprogram, addr_target addr)
|
|
{
|
|
if (subprogram->mHotReplaceKind != DbgSubprogram::HotReplaceKind_Replaced)
|
|
return subprogram;
|
|
|
|
if (addr != subprogram->mBlock.mLowPC)
|
|
return subprogram;
|
|
|
|
auto dbgModule = subprogram->mCompileUnit->mDbgModule;
|
|
|
|
HotJumpOp jumpOp = ReadMemory<HotJumpOp>(addr);
|
|
if (jumpOp.mOpCode != 0xE9)
|
|
return subprogram;
|
|
addr_target jumpAddr = addr + jumpOp.mRelTarget + sizeof(HotJumpOp);
|
|
|
|
auto jumpSubprogram = mDebugTarget->FindSubProgram(jumpAddr);
|
|
if (jumpSubprogram == NULL)
|
|
return subprogram;
|
|
|
|
return jumpSubprogram;
|
|
}
|
|
|
|
bool WinDebugger::ShouldShowStaticMember(DbgType* dbgType, DbgVariable* member)
|
|
{
|
|
// If locationData is non-null, that means it was added in addition to the static declaration in the CV type info,
|
|
// so only add the names from the type definition
|
|
auto flavor = dbgType->mCompileUnit->mDbgModule->mDbgFlavor;
|
|
return ((((dbgType->IsNamespace()) || (flavor != DbgFlavor_MS)) && ((member->mLocationData != NULL) || member->mIsConst)) ||
|
|
((flavor == DbgFlavor_MS) && (member->mLocationData == NULL)));
|
|
}
|
|
|
|
String WinDebugger::GetMemberList(DbgType* dbgType, const StringImpl& expr, bool isPtr, bool isStatic, bool forceCast, bool isSplat, bool isReadOnly)
|
|
{
|
|
auto dbgModule = dbgType->GetDbgModule();
|
|
dbgType->PopulateType();
|
|
auto language = dbgType->GetLanguage();
|
|
|
|
if (!isStatic)
|
|
{
|
|
String retVal;
|
|
bool needsNewline = false;
|
|
bool isBfObject = false;
|
|
|
|
if (dbgType->IsBfObjectPtr())
|
|
{
|
|
isBfObject = true;
|
|
dbgType = dbgType->mTypeParam;
|
|
}
|
|
|
|
int baseIdx = 0;
|
|
for (auto baseTypeEntry : dbgType->mBaseTypes)
|
|
{
|
|
auto baseType = baseTypeEntry->mBaseType;
|
|
if ((baseType->mSize > 0) || (baseType->mTypeCode != DbgType_Struct) || (strcmp(baseType->mTypeName, "ValueType") != 0))
|
|
{
|
|
String baseTypeStr = baseType->ToStringRaw(language);
|
|
if (baseIdx > 0)
|
|
retVal += "\n";
|
|
if (isSplat)
|
|
retVal += "[base]\t((" + baseTypeStr + ")" + expr + "), nv";
|
|
else if (dbgType->WantsRefThis())
|
|
retVal += "[base]\t((" + baseTypeStr + ")this), nd, na, nv, this=" + expr;
|
|
else
|
|
retVal += "[base]\t((" + baseTypeStr + "*)this), nd, na, nv, this=" + expr;
|
|
if (isReadOnly)
|
|
retVal += ", ne";
|
|
}
|
|
|
|
needsNewline = true;
|
|
|
|
baseIdx++;
|
|
}
|
|
|
|
String thisExpr = expr;
|
|
String castString;
|
|
if (dbgType->IsBfObject())
|
|
{
|
|
auto ptrType = dbgType->GetDbgModule()->GetPointerType(dbgType);
|
|
castString = ptrType->ToStringRaw(language);
|
|
}
|
|
else
|
|
castString = dbgType->ToStringRaw(language);
|
|
|
|
bool hadStatics = false;
|
|
for (auto member : dbgType->mMemberList)
|
|
{
|
|
if (member->mMemberOffset < 0)
|
|
continue;
|
|
|
|
if (member->mIsStatic)
|
|
{
|
|
if (ShouldShowStaticMember(dbgType, member))
|
|
hadStatics = true;
|
|
}
|
|
else
|
|
{
|
|
bool ignoreMember = false;
|
|
if (member->mName != NULL)
|
|
{
|
|
if ((member->mName[0] == '?') ||
|
|
(member->mName[0] == '$') ||
|
|
(strncmp(member->mName, "_vptr$", 6) == 0))
|
|
ignoreMember = true;
|
|
}
|
|
|
|
if (!ignoreMember)
|
|
{
|
|
if (needsNewline)
|
|
retVal += "\n";
|
|
|
|
if (member->mName == NULL)
|
|
{
|
|
retVal += GetMemberList(member->mType, expr, isPtr, isStatic, forceCast, isSplat, isReadOnly);
|
|
}
|
|
else
|
|
{
|
|
retVal += String(member->mName);
|
|
if (isSplat)
|
|
{
|
|
retVal += "\t(" + thisExpr + ")." + String(member->mName);
|
|
// We don't want to rely on this being enforced here. For one, ref types shouldn't get ", ne" added,
|
|
// and this doesn't solve the issue of attempting to assign via the Immediate window
|
|
/*if (isReadOnly)
|
|
retVal += ", ne";*/
|
|
}
|
|
else
|
|
{
|
|
if (forceCast)
|
|
retVal += "\t((" + castString + ")this)." + String(member->mName);
|
|
else if ((member->mName[0] >= '0') && (member->mName[0] <= '9')) // Numbered tuple member?
|
|
retVal += "\tthis." + String(member->mName);
|
|
else
|
|
retVal += "\t" + String(member->mName);
|
|
|
|
retVal += ", this=" + thisExpr;
|
|
// if (isReadOnly)
|
|
// retVal += ", ne";
|
|
}
|
|
}
|
|
needsNewline = true;
|
|
}
|
|
}
|
|
}
|
|
if (hadStatics)
|
|
{
|
|
if (needsNewline)
|
|
retVal += "\n";
|
|
retVal += "Static values\t" + castString;
|
|
}
|
|
return retVal;
|
|
}
|
|
else
|
|
{
|
|
if (dbgType->IsBfObjectPtr())
|
|
dbgType = dbgType->mTypeParam;
|
|
|
|
String retVal;
|
|
String memberPrefix = expr;
|
|
|
|
bool needsNewline = false;
|
|
bool hadStatics = false;
|
|
for (auto member : dbgType->mMemberList)
|
|
{
|
|
if (member->mIsStatic)
|
|
{
|
|
if (ShouldShowStaticMember(dbgType, member))
|
|
{
|
|
if (needsNewline)
|
|
retVal += "\n";
|
|
retVal += String(member->mName) + "\t" + memberPrefix + "." + String(member->mName);
|
|
needsNewline = true;
|
|
}
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
bool WinDebugger::ParseFormatInfo(DbgModule* dbgModule, const StringImpl& formatInfoStr, DwFormatInfo* formatInfo, BfPassInstance* bfPassInstance, int* assignExprOffset, String* assignExprString, String* errorString, DbgTypedValue 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 = formatFlags.IndexOf(',', 1);
|
|
int quotePos = formatFlags.IndexOf('"', 1);
|
|
if ((quotePos != -1) && (quotePos < nextComma))
|
|
{
|
|
int nextQuotePos = formatFlags.IndexOf('"', quotePos + 1);
|
|
if (nextQuotePos != -1)
|
|
nextComma = formatFlags.IndexOf(',', nextQuotePos + 1);
|
|
}
|
|
if (nextComma == -1)
|
|
nextComma = 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;
|
|
DbgEvaluationContext dbgEvaluationContext(this, dbgModule, thisExpr, formatInfo);
|
|
formatInfo->mExplicitThis = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
|
|
if (dbgEvaluationContext.HadError())
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = dbgEvaluationContext.GetErrorStr();
|
|
return false;
|
|
}
|
|
formatFlags = thisExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
|
|
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;
|
|
DbgEvaluationContext dbgEvaluationContext(this, dbgModule, countExpr, formatInfo);
|
|
DbgTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
|
|
if ((countValue) && (countValue.mType->IsInteger()))
|
|
formatInfo->mOverrideCount = (intptr)countValue.GetInt64();
|
|
if (dbgEvaluationContext.HadError())
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = dbgEvaluationContext.GetErrorStr();
|
|
return false;
|
|
}
|
|
formatFlags = countExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
|
|
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;
|
|
DbgEvaluationContext dbgEvaluationContext(this, dbgModule, countExpr, formatInfo);
|
|
DbgTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
|
|
if ((countValue) && (countValue.mType->IsInteger()))
|
|
formatInfo->mMaxCount = (intptr)countValue.GetInt64();
|
|
if (dbgEvaluationContext.HadError())
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = dbgEvaluationContext.GetErrorStr();
|
|
return false;
|
|
}
|
|
formatFlags = countExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
|
|
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;
|
|
DbgEvaluationContext dbgEvaluationContext(this, dbgModule, countExpr, formatInfo);
|
|
DbgTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
|
|
if ((countValue) && (countValue.mType->IsInteger()))
|
|
formatInfo->mArrayLength = (intptr)countValue.GetInt64();
|
|
if (dbgEvaluationContext.HadError())
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = dbgEvaluationContext.GetErrorStr();
|
|
return false;
|
|
}
|
|
formatFlags = countExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
|
|
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;
|
|
DbgEvaluationContext dbgEvaluationContext(this, dbgModule, 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.mLength >= 2) && (formatInfo->mReferenceId[0] == '\"'))
|
|
formatInfo->mReferenceId = formatInfo->mReferenceId.Substring(1, formatInfo->mReferenceId.length() - 2);
|
|
}
|
|
else if (strncmp(formatCmd.c_str(), "action=", 7) == 0)
|
|
{
|
|
formatInfo->mAction = formatCmd.Substring(7);
|
|
if ((formatInfo->mAction.mLength >= 2) && (formatInfo->mAction[0] == '\"'))
|
|
formatInfo->mAction = formatInfo->mReferenceId.Substring(1, formatInfo->mReferenceId.length() - 2);
|
|
}
|
|
else if (strncmp(formatCmd.c_str(), "_=", 2) == 0)
|
|
{
|
|
formatInfo->mSubjectExpr = formatCmd.Substring(2);
|
|
if ((formatInfo->mSubjectExpr.mLength >= 2) && (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.mLength >= 2) && (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.mLength >= 2) && (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;
|
|
DbgEvaluationContext dbgEvaluationContext(this, dbgModule, countExpr, formatInfo);
|
|
DbgTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
|
|
if ((countValue) && (countValue.mType->IsInteger()))
|
|
formatInfo->mArrayLength = (intptr)countValue.GetInt64();
|
|
if (dbgEvaluationContext.HadError())
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = dbgEvaluationContext.GetErrorStr();
|
|
return false;
|
|
}
|
|
formatFlags = countExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
|
|
continue;
|
|
}
|
|
else
|
|
hadError = true;
|
|
|
|
if (hadError)
|
|
{
|
|
if (errorString != NULL)
|
|
*errorString = "Invalid format flags";
|
|
return false;
|
|
}
|
|
|
|
formatFlags = formatFlags.Substring(nextComma);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
String WinDebugger::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;
|
|
}
|
|
|
|
DbgTypedValue WinDebugger::EvaluateInContext(DbgCompileUnit* dbgCompileUnit, const DbgTypedValue& contextTypedValue, const StringImpl& subExpr, DwFormatInfo* formatInfo, String* outReferenceId, String* outErrors)
|
|
{
|
|
DbgEvaluationContext dbgEvaluationContext(this, dbgCompileUnit->mDbgModule, subExpr, formatInfo, contextTypedValue);
|
|
if (dbgEvaluationContext.mDbgExprEvaluator == NULL)
|
|
return DbgTypedValue();
|
|
dbgEvaluationContext.mDbgExprEvaluator->mDbgCompileUnit = dbgCompileUnit;
|
|
if (formatInfo != NULL)
|
|
{
|
|
dbgEvaluationContext.mDbgExprEvaluator->mLanguage = formatInfo->mLanguage;
|
|
dbgEvaluationContext.mDbgExprEvaluator->mSubjectExpr = formatInfo->mSubjectExpr;
|
|
}
|
|
dbgEvaluationContext.mDbgExprEvaluator->mReferenceId = outReferenceId;
|
|
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 DbgTypedValue();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void WinDebugger::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 WinDebugger::EvalCondition(DebugVisualizerEntry* debugVis, DbgCompileUnit* dbgCompileUnit, DbgTypedValue typedVal, DwFormatInfo& formatInfo, const StringImpl& condition, const Array<String>& dbgVisWildcardCaptures, String& errorStr)
|
|
{
|
|
DwFormatInfo displayStrFormatInfo = formatInfo;
|
|
displayStrFormatInfo.mHidePointers = false;
|
|
displayStrFormatInfo.mRawString = false;
|
|
|
|
String errors;
|
|
const String conditionStr = mDebugManager->mDebugVisualizers->DoStringReplace(condition, dbgVisWildcardCaptures);
|
|
DbgTypedValue evalResult = EvaluateInContext(dbgCompileUnit, typedVal, conditionStr, &displayStrFormatInfo, NULL, &errors);
|
|
if ((!evalResult) || (!evalResult.mType->IsBoolean()))
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return false;
|
|
|
|
errorStr += "<DbgVis Failed>";
|
|
DbgVisFailed(debugVis, conditionStr, errors);
|
|
return false;
|
|
}
|
|
|
|
return evalResult.mBool;
|
|
}
|
|
|
|
String WinDebugger::GetArrayItems(DbgCompileUnit* dbgCompileUnit, DebugVisualizerEntry* debugVis, DbgType* valueType, DbgTypedValue& curNode, int& count, String* outContinuationData)
|
|
{
|
|
DbgEvaluationContext conditionEvaluationContext(this, dbgCompileUnit, debugVis->mCondition);
|
|
|
|
String addrs;
|
|
|
|
bool checkLeft = true;
|
|
|
|
int usedCount = 0;
|
|
while (usedCount < count)
|
|
{
|
|
DbgTypedValue condVal = conditionEvaluationContext.EvaluateInContext(curNode);
|
|
if (!condVal)
|
|
break;
|
|
if (condVal.mBool)
|
|
{
|
|
auto val = curNode;
|
|
if (valueType == NULL)
|
|
{
|
|
String typeAddr = val.mType->ToStringRaw();
|
|
// RPad
|
|
typeAddr.Append(' ', sizeof(addr_target) * 2 - typeAddr.length());
|
|
addrs += typeAddr;
|
|
}
|
|
|
|
String addr = EncodeDataPtr(val.mPtr, false);
|
|
addrs += addr;
|
|
usedCount++;
|
|
}
|
|
|
|
curNode.mPtr += curNode.mType->mTypeParam->GetStride();
|
|
}
|
|
count = usedCount;
|
|
|
|
if (outContinuationData != NULL)
|
|
{
|
|
*outContinuationData += EncodeDataPtr(debugVis, false) + EncodeDataPtr(valueType, false) +
|
|
EncodeDataPtr(curNode.mType, false) + EncodeDataPtr(curNode.mPtr, false);
|
|
}
|
|
|
|
return addrs;
|
|
}
|
|
|
|
String WinDebugger::GetLinkedListItems(DbgCompileUnit* dbgCompileUnit, DebugVisualizerEntry* debugVis, addr_target endNodePtr, DbgType* valueType, DbgTypedValue& curNode, int& count, String* outContinuationData)
|
|
{
|
|
DbgEvaluationContext nextEvaluationContext(this, dbgCompileUnit, debugVis->mNextPointer);
|
|
DbgEvaluationContext valueEvaluationContext(this, dbgCompileUnit, debugVis->mValuePointer);
|
|
|
|
String addrs;
|
|
|
|
bool checkLeft = true;
|
|
|
|
int mapIdx;
|
|
for (mapIdx = 0; mapIdx < count; mapIdx++)
|
|
{
|
|
if (curNode.mPtr == endNodePtr)
|
|
break;
|
|
DbgTypedValue val = valueEvaluationContext.EvaluateInContext(curNode);
|
|
if (!val)
|
|
break;
|
|
if (val.mPtr == 0)
|
|
break;
|
|
|
|
if (valueType == NULL)
|
|
{
|
|
String typeAddr = val.mType->ToStringRaw();
|
|
// RPad
|
|
typeAddr.Append(' ', sizeof(addr_target)*2 - typeAddr.length());
|
|
addrs += typeAddr;
|
|
}
|
|
|
|
String addr = EncodeDataPtr(val.mPtr, false);
|
|
addrs += addr;
|
|
|
|
curNode = nextEvaluationContext.EvaluateInContext(curNode);
|
|
}
|
|
count = mapIdx;
|
|
|
|
if (outContinuationData != NULL)
|
|
{
|
|
*outContinuationData += EncodeDataPtr(debugVis, false) + EncodeDataPtr(endNodePtr, false) + EncodeDataPtr(valueType, false) +
|
|
EncodeDataPtr(curNode.mType, false) + EncodeDataPtr(curNode.mPtr, false);
|
|
}
|
|
|
|
return addrs;
|
|
}
|
|
|
|
String WinDebugger::GetDictionaryItems(DbgCompileUnit* dbgCompileUnit, DebugVisualizerEntry* debugVis, DbgTypedValue dictValue, int bucketIdx, int nodeIdx, int& count, String* outContinuationData)
|
|
{
|
|
//DbgEvaluationContext bucketsEvaluationContext(this, dbgModule, debugVis->mBuckets);
|
|
DbgEvaluationContext nextEvaluationContext(this, dbgCompileUnit->mDbgModule, debugVis->mNextPointer);
|
|
|
|
DbgTypedValue bucketsPtr = EvaluateInContext(dbgCompileUnit, dictValue, debugVis->mBuckets);
|
|
DbgTypedValue entriesPtr = EvaluateInContext(dbgCompileUnit, dictValue, debugVis->mEntries);
|
|
if ((!bucketsPtr) || (!entriesPtr))
|
|
{
|
|
count = -1;
|
|
return "";
|
|
}
|
|
int entrySize = entriesPtr.mType->mTypeParam->GetStride();
|
|
int bucketIdxSize = bucketsPtr.mType->mTypeParam->GetStride();
|
|
|
|
String addrs;
|
|
|
|
bool checkLeft = true;
|
|
|
|
int encodeCount = 0;
|
|
while (encodeCount < count)
|
|
{
|
|
if (nodeIdx != -1)
|
|
{
|
|
DbgTypedValue entryValue;
|
|
entryValue.mSrcAddress = entriesPtr.mPtr + (nodeIdx * entrySize);
|
|
entryValue.mType = entriesPtr.mType->mTypeParam;
|
|
|
|
addrs += EncodeDataPtr(entryValue.mSrcAddress, false);
|
|
|
|
DbgTypedValue nextValue = nextEvaluationContext.EvaluateInContext(entryValue);
|
|
if ((!nextValue) || (!nextValue.mType->IsInteger()))
|
|
{
|
|
break;
|
|
}
|
|
|
|
nodeIdx = (int)nextValue.GetInt64();
|
|
encodeCount++;
|
|
}
|
|
else
|
|
{
|
|
if (bucketIdxSize == 4)
|
|
nodeIdx = ReadMemory<int>(bucketsPtr.mPtr + bucketIdx * sizeof(int32));
|
|
else
|
|
nodeIdx = (int)ReadMemory<int64>(bucketsPtr.mPtr + bucketIdx * sizeof(int64));
|
|
bucketIdx++;
|
|
}
|
|
}
|
|
|
|
count = encodeCount;
|
|
//count = mapIdx;
|
|
|
|
if (outContinuationData != NULL)
|
|
{
|
|
*outContinuationData += EncodeDataPtr(debugVis, false) + EncodeDataPtr(dictValue.mType, false) + EncodeDataPtr(dictValue.mSrcAddress, false) +
|
|
EncodeDataPtr((addr_target)bucketIdx, false) + EncodeDataPtr((addr_target)nodeIdx, false);
|
|
}
|
|
|
|
return addrs;
|
|
}
|
|
|
|
String WinDebugger::GetTreeItems(DbgCompileUnit* dbgCompileUnit, DebugVisualizerEntry* debugVis, Array<addr_target>& parentList, DbgType*& valueType, DbgTypedValue& curNode, int count, String* outContinuationData)
|
|
{
|
|
DbgEvaluationContext leftEvaluationContext(this, dbgCompileUnit, debugVis->mLeftPointer);
|
|
DbgEvaluationContext rightEvaluationContext(this, dbgCompileUnit, debugVis->mRightPointer);
|
|
DbgEvaluationContext valueEvaluationContext(this, dbgCompileUnit, debugVis->mValuePointer);
|
|
|
|
DbgEvaluationContext conditionEvaluationContext(this, dbgCompileUnit, debugVis->mCondition);
|
|
|
|
String addrs;
|
|
|
|
bool checkLeft = true;
|
|
|
|
if ((curNode.mPtr & 2) != 0) // Flag from continuation
|
|
{
|
|
checkLeft = false;
|
|
curNode.mPtr &= (addr_target)~2;
|
|
}
|
|
|
|
HashSet<intptr> seenAddrs;
|
|
|
|
for (int mapIdx = 0; mapIdx < count; mapIdx++)
|
|
{
|
|
DbgTypedValue readNode;
|
|
while (true)
|
|
{
|
|
bool checkNode = (curNode.mPtr & 1) == 0;
|
|
|
|
readNode = curNode;
|
|
readNode.mPtr &= (addr_target)~1;
|
|
|
|
if (checkLeft)
|
|
{
|
|
DbgTypedValue 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
|
|
{
|
|
DbgTypedValue 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 "";
|
|
}
|
|
}
|
|
|
|
DbgTypedValue 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 WinDebugger::GetCollectionContinuation(const StringImpl& continuationData, int callStackIdx, int count)
|
|
{
|
|
DbgCompileUnit* dbgCompileUnit = GetCallStackCompileUnit(callStackIdx);;
|
|
|
|
if (!IsPaused())
|
|
return "";
|
|
|
|
const char* dataPtr = continuationData.c_str();
|
|
DebugVisualizerEntry* debugVis = (DebugVisualizerEntry*)DecodeLocalDataPtr(dataPtr);
|
|
|
|
if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_TreeItems)
|
|
{
|
|
DbgType* valueType = (DbgType*)DecodeLocalDataPtr(dataPtr);
|
|
DbgTypedValue curNode;
|
|
curNode.mType = (DbgType*)DecodeLocalDataPtr(dataPtr);
|
|
curNode.mPtr = DecodeTargetDataPtr(dataPtr);
|
|
|
|
Array<addr_target> 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_target endNodePtr = DecodeTargetDataPtr(dataPtr);
|
|
DbgType* valueType = (DbgType*) DecodeLocalDataPtr(dataPtr);
|
|
DbgTypedValue curNode;
|
|
curNode.mType = (DbgType*)DecodeLocalDataPtr(dataPtr);
|
|
curNode.mPtr = DecodeTargetDataPtr(dataPtr);
|
|
|
|
String newContinuationData;
|
|
|
|
if (count < 0)
|
|
count = 3;
|
|
|
|
String retVal = GetLinkedListItems(dbgCompileUnit, debugVis, endNodePtr, valueType, curNode, count, &newContinuationData);
|
|
retVal += "\n" + newContinuationData;
|
|
return retVal;
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_Array)
|
|
{
|
|
DbgType* valueType = (DbgType*)DecodeLocalDataPtr(dataPtr);
|
|
DbgTypedValue curNode;
|
|
curNode.mType = (DbgType*)DecodeLocalDataPtr(dataPtr);
|
|
curNode.mPtr = DecodeTargetDataPtr(dataPtr);
|
|
|
|
String newContinuationData;
|
|
|
|
if (count < 0)
|
|
count = 3;
|
|
|
|
String retVal = GetArrayItems(dbgCompileUnit, debugVis, valueType, curNode, count, &newContinuationData);
|
|
retVal += "\n" + newContinuationData;
|
|
return retVal;
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_Dictionary)
|
|
{
|
|
DbgTypedValue dictValue;
|
|
dictValue.mType = (DbgType*)DecodeLocalDataPtr(dataPtr);
|
|
dictValue.mSrcAddress = DecodeTargetDataPtr(dataPtr);
|
|
|
|
int bucketIdx = (int)DecodeTargetDataPtr(dataPtr);
|
|
int nodeIdx = (int)DecodeTargetDataPtr(dataPtr);
|
|
|
|
String newContinuationData;
|
|
String retVal = GetDictionaryItems(dbgCompileUnit, debugVis, dictValue, bucketIdx, nodeIdx, count, &newContinuationData);
|
|
retVal += "\n" + newContinuationData;
|
|
return retVal;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
template <typename T>
|
|
static String IntTypeToString(T val, const StringImpl& name, DwDisplayInfo* displayInfo, DwFormatInfo& 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(), (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(), (std::make_unsigned<T>::type)(val));
|
|
}
|
|
|
|
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 ToLower(StrFormat(format.c_str(), (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* WinDebugger::GetDisplayInfo(const StringImpl& referenceId)
|
|
{
|
|
DwDisplayInfo* displayInfo = &mDebugManager->mDefaultDisplayInfo;
|
|
if (!referenceId.empty())
|
|
{
|
|
if (!mDebugManager->mDisplayInfos.TryGetValue(referenceId, &displayInfo))
|
|
{
|
|
int dollarIdx = referenceId.LastIndexOf('$');
|
|
if ((dollarIdx > 0) && (referenceId[dollarIdx - 1] == ']'))
|
|
{
|
|
// Try getting series displayinfo
|
|
mDebugManager->mDisplayInfos.TryGetValueWith(StringView(referenceId, 0, dollarIdx), &displayInfo);
|
|
}
|
|
}
|
|
}
|
|
return displayInfo;
|
|
}
|
|
|
|
static String WrapWithModifiers(const StringImpl& origName, DbgType* dbgType, DbgLanguage language)
|
|
{
|
|
if (language == DbgLanguage_Unknown)
|
|
language = dbgType->GetLanguage();
|
|
|
|
String name = origName;
|
|
while (true)
|
|
{
|
|
if (dbgType->mTypeCode == DbgType_Const)
|
|
{
|
|
if (language == DbgLanguage_Beef)
|
|
name = "readonly " + name;
|
|
else
|
|
name = "const " + name;
|
|
dbgType = dbgType->mTypeParam;
|
|
}
|
|
else if (dbgType->mTypeCode == DbgType_Volatile)
|
|
{
|
|
name = "volatile " + name;
|
|
dbgType = dbgType->mTypeParam;
|
|
}
|
|
else if (dbgType->mTypeCode == DbgType_TypeDef)
|
|
{
|
|
dbgType = dbgType->mTypeParam;
|
|
}
|
|
else if (dbgType->mTypeCode == DbgType_Ref)
|
|
{
|
|
if (language == DbgLanguage_Beef)
|
|
name = "ref " + name;
|
|
else
|
|
name = name + "&";
|
|
dbgType = dbgType->mTypeParam;
|
|
}
|
|
else if (dbgType->mTypeCode == DbgType_Bitfield)
|
|
{
|
|
return dbgType->ToString(language);
|
|
}
|
|
else
|
|
return name;
|
|
}
|
|
}
|
|
|
|
DebugVisualizerEntry* WinDebugger::FindVisualizerForType(DbgType* dbgType, Array<String>* wildcardCaptures)
|
|
{
|
|
auto entry = mDebugManager->mDebugVisualizers->FindEntryForType(dbgType->ToString(DbgLanguage_Unknown, true), dbgType->mCompileUnit->mDbgModule->mDbgFlavor, wildcardCaptures);
|
|
|
|
if (entry == NULL)
|
|
{
|
|
dbgType = dbgType->GetPrimaryType();
|
|
dbgType->PopulateType();
|
|
for (auto baseTypeEntry : dbgType->mBaseTypes)
|
|
{
|
|
entry = FindVisualizerForType(baseTypeEntry->mBaseType, wildcardCaptures);
|
|
if (entry != NULL)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
#define GET_FROM(ptr, T) *((T*)(ptr += sizeof(T)) - 1)
|
|
|
|
String WinDebugger::ReadString(DbgTypeCode charType, intptr addr, bool isLocalAddr, intptr maxLength, DwFormatInfo& formatInfo, bool wantStringView)
|
|
{
|
|
int origMaxLength = 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) && (!wantStringView))
|
|
maxLength = BF_MIN(maxLength, maxShowSize);
|
|
|
|
if (wantStringView)
|
|
{
|
|
// Limit the original string view to 1MB, reevaluate on "More"
|
|
maxLength = BF_MIN(maxLength, 1024 * 1024);
|
|
}
|
|
|
|
//EnableMemCache();
|
|
bool readFailed = false;
|
|
intptr strPtr = addr;
|
|
|
|
int charLen = 1;
|
|
if ((charType == DbgType_SChar16) || (charType == DbgType_UChar16))
|
|
charLen = 2;
|
|
else if ((charType == DbgType_SChar32) || (charType == DbgType_UChar32))
|
|
charLen = 4;
|
|
|
|
bool isUTF8 = formatInfo.mDisplayType == DwDisplayType_Utf8;
|
|
|
|
int readSize = BF_MIN(1024, 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, isLocalAddr))
|
|
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) || (wantStringView))
|
|
{
|
|
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, formatInfo.mLanguage == DbgLanguage_Beef);
|
|
|
|
// 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) && (!wantStringView) && ((int)retVal.length() > maxShowSize))
|
|
{
|
|
retVal = retVal.Substring(0, maxShowSize);
|
|
wasTerminated = false;
|
|
}
|
|
|
|
if (wasTerminated)
|
|
retVal += "\"";
|
|
else
|
|
retVal += "...";
|
|
|
|
return retVal;
|
|
}
|
|
|
|
bool WinDebugger::ProcessEvalString(DbgCompileUnit* dbgCompileUnit, DbgTypedValue useTypedValue, String& evalStr, String& displayString, DwFormatInfo& formatInfo, DebugVisualizerEntry* debugVis, bool limitLength)
|
|
{
|
|
bool success = true;
|
|
|
|
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;
|
|
}
|
|
|
|
DwFormatInfo 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;
|
|
DbgTypedValue evalResult = EvaluateInContext(dbgCompileUnit, useTypedValue, evalString, &displayStrFormatInfo, NULL, &errors);
|
|
if (evalResult)
|
|
{
|
|
if (displayStrFormatInfo.mNoEdit)
|
|
formatInfo.mNoEdit = true;
|
|
|
|
String result = DbgTypedValueToString(evalResult, evalString, displayStrFormatInfo, NULL);
|
|
|
|
if ((formatInfo.mRawString) && (limitLength))
|
|
{
|
|
displayString = result;
|
|
return success;
|
|
}
|
|
|
|
if (displayStrFormatInfo.mRawString)
|
|
{
|
|
displayString += result;
|
|
}
|
|
else
|
|
{
|
|
int crPos = result.IndexOf('\n');
|
|
if (crPos != -1)
|
|
displayString += result.Substring(0, crPos);
|
|
else
|
|
displayString += result;
|
|
}
|
|
}
|
|
else if (debugVis != NULL)
|
|
{
|
|
success = false;
|
|
displayString += "<DbgVis Failed>";
|
|
DbgVisFailed(debugVis, evalString, errors);
|
|
}
|
|
else
|
|
{
|
|
success = false;
|
|
displayString += "<Eval Failed>";
|
|
}
|
|
}
|
|
|
|
i = endIdx;
|
|
continue;
|
|
}
|
|
else if ((c == '{') && (nextC == '{'))
|
|
{
|
|
// Skip next paren
|
|
i++;
|
|
}
|
|
else if ((c == '}') && (nextC == '}'))
|
|
{
|
|
// Skip next paren
|
|
i++;
|
|
}
|
|
|
|
displayString += c;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
static bool IsNormalChar(uint32 c)
|
|
{
|
|
return (c < 0x80);
|
|
}
|
|
|
|
String WinDebugger::DbgTypedValueToString(const DbgTypedValue& origTypedValue, const StringImpl& expr, DwFormatInfo& formatInfo, DbgExprEvaluator* optEvaluator, bool fullPrecision)
|
|
{
|
|
BP_ZONE("WinDebugger::DbgTypedValueToString");
|
|
|
|
DbgTypedValue typedValue = origTypedValue;
|
|
auto dbgCompileUnit = typedValue.mType->mCompileUnit;
|
|
auto dbgModule = typedValue.mType->GetDbgModule();
|
|
auto language = origTypedValue.mType->GetLanguage();
|
|
if (language == DbgLanguage_Unknown)
|
|
language = formatInfo.mLanguage;
|
|
formatInfo.mLanguage = language;
|
|
bool isBeef = language == DbgLanguage_Beef;
|
|
|
|
char str[32];
|
|
bool readFailed = false;
|
|
bool isCompositeType = false;
|
|
bool isSizedArray = false;
|
|
bool isEnum = false;
|
|
int64 enumVal = 0;
|
|
String result;
|
|
String stringViewData;
|
|
|
|
DwDisplayInfo* displayInfo = GetDisplayInfo(formatInfo.mReferenceId);
|
|
bool wantStringView = (displayInfo->mFormatStr == "str") && (formatInfo.mAllowStringView);
|
|
|
|
DbgType* origValueType = typedValue.mType;
|
|
bool origHadRef = false;
|
|
DbgType* dwValueType = typedValue.mType->RemoveModifiers(&origHadRef);
|
|
|
|
if (dwValueType == NULL)
|
|
dwValueType = dbgModule->GetPrimitiveType(DbgType_Void, language);
|
|
else
|
|
dwValueType = dwValueType->GetPrimaryType();
|
|
|
|
if (dwValueType->mTypeCode == DbgType_TypeDef)
|
|
{
|
|
DbgTypedValue realTypedVal = typedValue;
|
|
realTypedVal.mType = dwValueType->mTypeParam;
|
|
return DbgTypedValueToString(realTypedVal, expr, formatInfo, optEvaluator);
|
|
}
|
|
|
|
if (formatInfo.mRawString)
|
|
{
|
|
if ((dwValueType->mTypeCode != DbgType_Struct) && (dwValueType->mTypeCode != DbgType_Class) && (dwValueType->mTypeCode != DbgType_Ptr) && (dwValueType->mTypeCode != DbgType_SizedArray))
|
|
return "";
|
|
}
|
|
|
|
auto _ShowArraySummary = [&](String& retVal, addr_target ptrVal, int64 arraySize, DbgType* 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 += ", ";
|
|
|
|
DwFormatInfo displayStrFormatInfo = formatInfo;
|
|
displayStrFormatInfo.mExpandItemDepth = 1;
|
|
displayStrFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + retVal.length() + 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 = "((" + innerType->ToStringRaw(language) + "*)" + EncodeDataPtr(ptrVal, true) + StrFormat(")[%lld]", idx);
|
|
DbgTypedValue evalResult = EvaluateInContext(dbgCompileUnit, typedValue, evalStr, &displayStrFormatInfo);
|
|
String result;
|
|
if (evalResult)
|
|
{
|
|
result = DbgTypedValueToString(evalResult, evalStr, displayStrFormatInfo, NULL);
|
|
int crPos = 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 (dwValueType->IsPointer())
|
|
{
|
|
String retVal;
|
|
addr_target ptrVal = (addr_target)typedValue.mPtr;
|
|
if ((!typedValue.mIsLiteral) && (!formatInfo.mHidePointers))
|
|
{
|
|
retVal = EncodeDataPtr(ptrVal, true) + " ";
|
|
retVal += dwValueType->mTypeParam->ToString(language);
|
|
retVal += StrFormat("[%lld] ", (int64)formatInfo.mArrayLength);
|
|
}
|
|
|
|
_ShowArraySummary(retVal, ptrVal, formatInfo.mArrayLength, dwValueType->mTypeParam);
|
|
|
|
String idxStr = "[{0}]";
|
|
|
|
DbgType* innerType = dwValueType->mTypeParam;
|
|
|
|
retVal += "\n" + dwValueType->ToString(language);
|
|
|
|
String evalStr = "*((" + typedValue.mType->ToStringRaw(language) + ")" + 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
|
|
{
|
|
DwFormatInfo newFormatInfo = formatInfo;
|
|
newFormatInfo.mArrayLength = -1;
|
|
|
|
String retVal = DbgTypedValueToString(typedValue, expr, newFormatInfo, optEvaluator);
|
|
|
|
int crPos = (int)retVal.IndexOf('\n');
|
|
if (crPos != -1)
|
|
retVal = "!Array length flag not valid with this type" + retVal.Substring(crPos);
|
|
return retVal;
|
|
}
|
|
}
|
|
|
|
switch (dwValueType->mTypeCode)
|
|
{
|
|
case DbgType_Void:
|
|
return "\nvoid";
|
|
case DbgType_Bool:
|
|
{
|
|
if (typedValue.mUInt8 == 0)
|
|
return "false\n" + WrapWithModifiers("bool", origValueType, language);
|
|
else if (typedValue.mUInt8 == 1)
|
|
return "true\n" + WrapWithModifiers("bool", origValueType, language);
|
|
else
|
|
return StrFormat("true (%d)\n", typedValue.mUInt8) + WrapWithModifiers("bool", origValueType, language);
|
|
}
|
|
break;
|
|
case DbgType_UChar:
|
|
if (language != DbgLanguage_Beef)
|
|
return IntTypeToString<uint8>(typedValue.mUInt8, WrapWithModifiers("uint8_t", origValueType, language), displayInfo, formatInfo);
|
|
case DbgType_SChar:
|
|
{
|
|
if (typedValue.mInt8 != 0)
|
|
{
|
|
char str[2] = {(char)typedValue.mInt8};
|
|
result = SlashString(str, formatInfo.mDisplayType == DwDisplayType_Utf8, true);
|
|
|
|
if (!IsNormalChar(typedValue.mUInt8))
|
|
result = StrFormat("'%s' (0x%02X)\n", result.c_str(), typedValue.mUInt8);
|
|
else
|
|
result = StrFormat("'%s'\n", result.c_str());
|
|
}
|
|
else
|
|
result = "'\\0'\n";
|
|
return result + WrapWithModifiers("char", origValueType, language);
|
|
}
|
|
break;
|
|
case DbgType_UChar16:
|
|
if (language != DbgLanguage_Beef)
|
|
return IntTypeToString<int16>(typedValue.mUInt8, WrapWithModifiers("uint16_t", origValueType, language), displayInfo, formatInfo);
|
|
case DbgType_SChar16:
|
|
{
|
|
if (typedValue.mInt16 != 0)
|
|
{
|
|
u8_toutf8(str, 8, typedValue.mUInt32);
|
|
result = SlashString(str, true, true);
|
|
if (!IsNormalChar(typedValue.mUInt32))
|
|
result = StrFormat("'%s' (0x%02X)\n", result.c_str(), typedValue.mUInt16);
|
|
else
|
|
result = StrFormat("'%s'\n", result.c_str());
|
|
}
|
|
else
|
|
result = "'\\0'\n";
|
|
return result + WrapWithModifiers(isBeef ? "char16" : "int16_t", origValueType, language);
|
|
}
|
|
break;
|
|
case DbgType_UChar32:
|
|
case DbgType_SChar32:
|
|
{
|
|
if (typedValue.mInt32 != 0)
|
|
{
|
|
u8_toutf8(str, 8, typedValue.mUInt32);
|
|
result = SlashString(str, true, true);
|
|
if (!IsNormalChar(typedValue.mUInt32))
|
|
result = StrFormat("'%s' (0x%02X)\n", result.c_str(), typedValue.mUInt32);
|
|
else
|
|
result = StrFormat("'%s'\n", result.c_str());
|
|
}
|
|
else
|
|
result = "'\\0'\n";
|
|
return result + WrapWithModifiers(isBeef ? "char32" : "int32_t", origValueType, language);
|
|
}
|
|
break;
|
|
case DbgType_i8:
|
|
return IntTypeToString<int8>(typedValue.mInt8, WrapWithModifiers(isBeef ? "int8" : "int8_t", origValueType, language), displayInfo, formatInfo);
|
|
case DbgType_u8:
|
|
return IntTypeToString<uint8>(typedValue.mUInt8, WrapWithModifiers(isBeef ? "uint8" : "uint8_t", origValueType, language), displayInfo, formatInfo);
|
|
case DbgType_i16:
|
|
return IntTypeToString<int16>(typedValue.mInt16, WrapWithModifiers(isBeef ? "int16" : "int16_t", origValueType, language), displayInfo, formatInfo);
|
|
case DbgType_u16:
|
|
return IntTypeToString<uint16>(typedValue.mUInt16, WrapWithModifiers(isBeef ? "uint16" : "uint16_t", origValueType, language), displayInfo, formatInfo);
|
|
case DbgType_i32:
|
|
return IntTypeToString<int32>(typedValue.mInt32, WrapWithModifiers(isBeef ? "int32" : "int32_t", origValueType, language), displayInfo, formatInfo);
|
|
case DbgType_u32:
|
|
return IntTypeToString<uint32>(typedValue.mUInt32, WrapWithModifiers(isBeef ? "uint32" : "uint32_t", origValueType, language), displayInfo, formatInfo);
|
|
case DbgType_i64:
|
|
return IntTypeToString<int64>(typedValue.mInt64, WrapWithModifiers(isBeef ? "int64" : "int64_t", origValueType, language), displayInfo, formatInfo);
|
|
case DbgType_u64:
|
|
return IntTypeToString<uint64>(typedValue.mUInt64, WrapWithModifiers(isBeef ? "uint64" : "uint64_t", origValueType, language), displayInfo, formatInfo);
|
|
case DbgType_RegGroup:
|
|
{
|
|
if ((typedValue.mRegNum >= CPUReg_M128_XMMREG_FIRST) && (typedValue.mRegNum <= CPUReg_M128_XMMREG_LAST))
|
|
{
|
|
int callStackIdx = formatInfo.mCallStackIdx;
|
|
FixCallStackIdx(callStackIdx);
|
|
|
|
UpdateRegisterUsage(callStackIdx);
|
|
WdStackFrame* wdStackFrame = mCallStack[callStackIdx];
|
|
RegForm regForm = RegForm_Unknown;
|
|
if (typedValue.mRegNum < (int)wdStackFrame->mRegForms.size())
|
|
regForm = wdStackFrame->mRegForms[typedValue.mRegNum];
|
|
|
|
int xmmMajor = typedValue.mRegNum - CPUReg_M128_XMMREG_FIRST;
|
|
String headerStr;
|
|
|
|
String xmmType = "__m128";
|
|
|
|
int xmmCount = 4;
|
|
if ((regForm == RegForm_Double) || (regForm == RegForm_Double2) ||
|
|
(regForm == RegForm_Long) || (regForm == RegForm_Long2) ||
|
|
(regForm == RegForm_ULong) || (regForm == RegForm_ULong2))
|
|
xmmCount = 2;
|
|
//TODO: add byte, short, int, etc...
|
|
|
|
if (optEvaluator)
|
|
{
|
|
DwMmDisplayType mmDwMmDisplayType = displayInfo->mMmDisplayType;
|
|
if (mmDwMmDisplayType == DwMmDisplayType_Default)
|
|
{
|
|
if ((regForm == RegForm_Double) || (regForm == RegForm_Double2))
|
|
mmDwMmDisplayType = DwMmDisplayType_Double;
|
|
else if (regForm == RegForm_Int4)
|
|
mmDwMmDisplayType = DwMmDisplayType_Int32;
|
|
}
|
|
|
|
if (mmDwMmDisplayType == DwMmDisplayType_Double)
|
|
{
|
|
xmmType = "__m128d";
|
|
xmmCount = 2;
|
|
double xmmRegVals[2];
|
|
CPURegisters* regs = optEvaluator->GetRegisters();
|
|
for (int xmmMinor = 0; xmmMinor < xmmCount; ++xmmMinor)
|
|
{
|
|
DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
|
|
BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_Double);
|
|
BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
|
|
xmmRegVals[xmmMinor] = xmmReg.mDouble;
|
|
}
|
|
headerStr = StrFormat("(%f, %f)", xmmRegVals[0], xmmRegVals[1]);
|
|
}
|
|
else if (mmDwMmDisplayType == DwMmDisplayType_UInt8)
|
|
{
|
|
int xmmRegVals[4];
|
|
xmmCount = 16;
|
|
CPURegisters* regs = optEvaluator->GetRegisters();
|
|
for (int xmmMinor = 0; xmmMinor < BF_ARRAY_COUNT(xmmRegVals); ++xmmMinor)
|
|
{
|
|
DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
|
|
BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_i32);
|
|
BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
|
|
xmmRegVals[xmmMinor] = xmmReg.mInt32;
|
|
}
|
|
headerStr = StrFormat("(%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)",
|
|
xmmRegVals[0] & 0xFF, (xmmRegVals[0] >> 8) & 0xFF, (xmmRegVals[0] >> 16) & 0xFF, (xmmRegVals[0] >> 24) & 0xFF,
|
|
xmmRegVals[1] & 0xFF, (xmmRegVals[1] >> 8) & 0xFF, (xmmRegVals[1] >> 16) & 0xFF, (xmmRegVals[1] >> 24) & 0xFF,
|
|
xmmRegVals[2] & 0xFF, (xmmRegVals[2] >> 8) & 0xFF, (xmmRegVals[2] >> 16) & 0xFF, (xmmRegVals[2] >> 24) & 0xFF,
|
|
xmmRegVals[3] & 0xFF, (xmmRegVals[3] >> 8) & 0xFF, (xmmRegVals[3] >> 16) & 0xFF, (xmmRegVals[3] >> 24) & 0xFF);
|
|
}
|
|
else if (mmDwMmDisplayType == DwMmDisplayType_Int16)
|
|
{
|
|
int xmmRegVals[4];
|
|
xmmCount = 8;
|
|
CPURegisters* regs = optEvaluator->GetRegisters();
|
|
for (int xmmMinor = 0; xmmMinor < BF_ARRAY_COUNT(xmmRegVals); ++xmmMinor)
|
|
{
|
|
DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
|
|
BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_i32);
|
|
BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
|
|
xmmRegVals[xmmMinor] = xmmReg.mInt32;
|
|
}
|
|
headerStr = StrFormat("(%d, %d, %d, %d, %d, %d, %d, %d)",
|
|
xmmRegVals[0] & 0xFFFF, (xmmRegVals[0] >> 16) & 0xFFFF,
|
|
xmmRegVals[1] & 0xFFFF, (xmmRegVals[1] >> 16) & 0xFFFF,
|
|
xmmRegVals[2] & 0xFFFF, (xmmRegVals[2] >> 16) & 0xFFFF,
|
|
xmmRegVals[3] & 0xFFFF, (xmmRegVals[3] >> 16) & 0xFFFF);
|
|
}
|
|
else if (mmDwMmDisplayType == DwMmDisplayType_Int32)
|
|
{
|
|
int xmmRegVals[4];
|
|
xmmCount = 4;
|
|
CPURegisters* regs = optEvaluator->GetRegisters();
|
|
for (int xmmMinor = 0; xmmMinor < xmmCount; ++xmmMinor)
|
|
{
|
|
DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
|
|
BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_i32);
|
|
BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
|
|
xmmRegVals[xmmMinor] = xmmReg.mInt32;
|
|
}
|
|
headerStr = StrFormat("(%d, %d, %d, %d)", xmmRegVals[0], xmmRegVals[1], xmmRegVals[2], xmmRegVals[3]);
|
|
}
|
|
else if (mmDwMmDisplayType == DwMmDisplayType_Int64)
|
|
{
|
|
int64 xmmRegVals[2];
|
|
xmmCount = 2;
|
|
CPURegisters* regs = optEvaluator->GetRegisters();
|
|
for (int xmmMinor = 0; xmmMinor < xmmCount; ++xmmMinor)
|
|
{
|
|
DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
|
|
BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_i64);
|
|
BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
|
|
xmmRegVals[xmmMinor] = xmmReg.mInt64;
|
|
}
|
|
headerStr = StrFormat("(%lld, %lld)", xmmRegVals[0], xmmRegVals[1]);
|
|
}
|
|
else // float
|
|
{
|
|
float xmmRegVals[4];
|
|
xmmCount = 4;
|
|
CPURegisters* regs = optEvaluator->GetRegisters();
|
|
for (int xmmMinor = 0; xmmMinor < xmmCount; ++xmmMinor)
|
|
{
|
|
DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
|
|
BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_Single);
|
|
BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
|
|
xmmRegVals[xmmMinor] = xmmReg.mSingle;
|
|
}
|
|
headerStr = StrFormat("(%f, %f, %f, %f)", xmmRegVals[0], xmmRegVals[1], xmmRegVals[2], xmmRegVals[3]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
headerStr = StrFormat("XMM%d", xmmMajor);
|
|
}
|
|
|
|
result = headerStr + "\n" + xmmType;
|
|
for (int i = 0; i < xmmCount; i++)
|
|
{
|
|
if (xmmCount == 16)
|
|
result += WrapWithModifiers(StrFormat("\n[%d]\t(uint8)($xmm%d_%d >> %d)", i, xmmMajor, i / 4, (i % 4)*8), origValueType, language);
|
|
else if (xmmCount == 8)
|
|
result += WrapWithModifiers(StrFormat("\n[%d]\t(int16)($xmm%d_%d >> %d)", i, xmmMajor, i / 2, (i % 2)*8), origValueType, language);
|
|
else
|
|
result += WrapWithModifiers(StrFormat("\n[%d]\t$xmm%d_%d", i, xmmMajor, i), origValueType, language);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
switch (typedValue.mRegNum)
|
|
{
|
|
case CPUReg_CAT_ALLREGS:
|
|
{
|
|
return "ALLREGS\n__allregs\niregs\t$iregs\nflags\t$flags\nfpregs\t$fpregs\nmmregs\t$mmregs\nxmmregs\t$xmmregs";
|
|
}
|
|
break;
|
|
case CPUReg_CAT_IREGS:
|
|
{
|
|
#ifdef BF_DBG_32
|
|
String headerStr;
|
|
if (optEvaluator)
|
|
{
|
|
CPURegisters* regs = optEvaluator->GetRegisters();
|
|
headerStr = StrFormat("(eax=0x%08x, ebx=0x%08x, ecx=0x%08x, edx=0x%08x, esi=0x%08x, edi=0x%08x, esp=0x%08x, ebp=0x%08x, eip=0x%08x, efl=0x%08x)",
|
|
(uint32)regs->mIntRegs.eax, (uint32)regs->mIntRegs.ebx, (uint32)regs->mIntRegs.ecx, (uint32)regs->mIntRegs.edx,
|
|
(uint32)regs->mIntRegs.esi, (uint32)regs->mIntRegs.edi, (uint32)regs->mIntRegs.esp, (uint32)regs->mIntRegs.ebp,
|
|
(uint32)regs->mIntRegs.eip, (uint32)regs->mIntRegs.efl);
|
|
}
|
|
else
|
|
{
|
|
headerStr = "IREGS";
|
|
}
|
|
|
|
return StrFormat("%s\n__iregs\neax\t$eax\nebx\t$ebx\necx\t$ecx\nedx\t$edx\nesi\t$esi\nedi\t$edi\nesp\t$esp\nebp\t$ebp\neip\t$eip", headerStr.c_str());
|
|
#else
|
|
String headerStr;
|
|
if (optEvaluator)
|
|
{
|
|
CPURegisters* regs = optEvaluator->GetRegisters();
|
|
headerStr = StrFormat("(rax=0x%@, rbx=0x%@, rcx=0x%@, rdx=0x%@, rsi=0x%@, rdi=0x%@, rsp=0x%@, rbp=0x%@, eip=0x%@, r8=0x%@, r9=0x%@, r10=0x%@, r11=0x%@, r12=0x%@, r13=0x%@, r14=0x%@, r15=0x%@, efl=0x%08x)",
|
|
(uint64)regs->mIntRegs.rax, (uint64)regs->mIntRegs.rbx, (uint64)regs->mIntRegs.rcx, (uint64)regs->mIntRegs.rdx,
|
|
(uint64)regs->mIntRegs.rsi, (uint64)regs->mIntRegs.rdi, (uint64)regs->mIntRegs.rsp, (uint64)regs->mIntRegs.rbp,
|
|
(uint64)regs->mIntRegs.rip,
|
|
(uint64)regs->mIntRegs.r8, (uint64)regs->mIntRegs.r9, (uint64)regs->mIntRegs.r10, (uint64)regs->mIntRegs.r11,
|
|
(uint64)regs->mIntRegs.r12, (uint64)regs->mIntRegs.r13, (uint64)regs->mIntRegs.r14, (uint64)regs->mIntRegs.r15,
|
|
(uint32)regs->mIntRegs.efl);
|
|
}
|
|
else
|
|
{
|
|
headerStr = "IREGS";
|
|
}
|
|
|
|
return StrFormat("%s\n__iregs\neax\t$eax\nebx\t$ebx\necx\t$ecx\nedx\t$edx\nesi\t$esi\nedi\t$edi\nesp\t$esp\nebp\t$ebp\neip\t$eip\nr8\t$r8\nr9\t$r9\nr10\t$r10\nr11\t$r11\nr12\t$r12\nr13\t$r13\nr14\t$r14\nr15\t$r15", headerStr.c_str());
|
|
#endif
|
|
}
|
|
break;
|
|
case CPUReg_CAT_FPREGS:
|
|
{
|
|
String headerStr;
|
|
if (optEvaluator)
|
|
{
|
|
CPURegisters* regs = optEvaluator->GetRegisters();
|
|
headerStr = "(";
|
|
for (int i = 0; i < CPURegisters::kNumFpMmRegs; ++i)
|
|
{
|
|
if (i)
|
|
headerStr += ", ";
|
|
double val = ConvertFloat80ToDouble(regs->mFpMmRegsArray[i].fp.fp80);
|
|
headerStr += StrFormat("%f", val);
|
|
}
|
|
headerStr += ")";
|
|
}
|
|
else
|
|
{
|
|
headerStr = "FPREGS";
|
|
}
|
|
|
|
result = StrFormat("%s\n__fpregs", headerStr.c_str());
|
|
for (int i = 0; i < CPURegisters::kNumFpMmRegs; ++i)
|
|
result += StrFormat("\n[%d]\t$st%d", i, i);
|
|
|
|
return result;
|
|
}
|
|
break;
|
|
case CPUReg_CAT_MMREGS:
|
|
{
|
|
String headerStr;
|
|
if (optEvaluator)
|
|
{
|
|
CPURegisters* regs = optEvaluator->GetRegisters();
|
|
headerStr = "(";
|
|
for (int i = 0; i < CPURegisters::kNumFpMmRegs; ++i)
|
|
{
|
|
if (i)
|
|
headerStr += ", ";
|
|
uint64 val = regs->mFpMmRegsArray[i].mm;
|
|
headerStr += StrFormat("0x%016llx", val);
|
|
}
|
|
headerStr += ")";
|
|
}
|
|
else
|
|
{
|
|
headerStr = "MMREGS";
|
|
}
|
|
|
|
result = StrFormat("%s\n__mmregs", headerStr.c_str());
|
|
for (int i = 0; i < CPURegisters::kNumFpMmRegs; ++i)
|
|
result += StrFormat("\n[%d]\t$mm%d", i, i);
|
|
|
|
return result;
|
|
}
|
|
break;
|
|
case CPUReg_CAT_XMMREGS:
|
|
{
|
|
String headerStr = StrFormat("XMMREGS[%d]", CPURegisters::kNumXmmRegs); // these are too big to put a useful header for the entire category
|
|
|
|
result = StrFormat("%s\n__xmmregs", headerStr.c_str());
|
|
for (int i = 0; i < CPURegisters::kNumXmmRegs; ++i)
|
|
result += StrFormat("\n[%d]\t$xmm%d", i, i);
|
|
|
|
return result;
|
|
}
|
|
break;
|
|
case CPUReg_CAT_FLAGS:
|
|
{
|
|
String headerStr;
|
|
if (optEvaluator)
|
|
{
|
|
CPURegisters* regs = optEvaluator->GetRegisters();
|
|
#ifdef BF_DBG_32
|
|
#define FLAGVAR(abbr, name) int flag##abbr = ((regs->mIntRegs.efl & ((uint64)1 << CPURegisters::GetFlagBitForRegister(X86Reg_FLAG_##abbr##_##name))) != 0) ? 1 : 0
|
|
FLAGVAR(CF, CARRY);
|
|
FLAGVAR(PF, PARITY);
|
|
FLAGVAR(AF, ADJUST);
|
|
FLAGVAR(ZF, ZERO);
|
|
FLAGVAR(SF, SIGN);
|
|
FLAGVAR(IF, INTERRUPT);
|
|
FLAGVAR(DF, DIRECTION);
|
|
FLAGVAR(OF, OVERFLOW);
|
|
#undef FLAGVAR
|
|
#else
|
|
#define FLAGVAR(abbr, name) int flag##abbr = ((regs->mIntRegs.efl & ((uint64)1 << CPURegisters::GetFlagBitForRegister(X64Reg_FLAG_##abbr##_##name))) != 0) ? 1 : 0
|
|
FLAGVAR(CF, CARRY);
|
|
FLAGVAR(PF, PARITY);
|
|
FLAGVAR(AF, ADJUST);
|
|
FLAGVAR(ZF, ZERO);
|
|
FLAGVAR(SF, SIGN);
|
|
FLAGVAR(IF, INTERRUPT);
|
|
FLAGVAR(DF, DIRECTION);
|
|
FLAGVAR(OF, OVERFLOW);
|
|
#undef FLAGVAR
|
|
#endif
|
|
headerStr = StrFormat("(CF=%d, PF=%d, AF=%d, ZF=%d, SF=%d, IF=%d, DF=%d, OF=%d)",
|
|
flagCF, flagPF, flagAF, flagZF, flagSF, flagIF, flagDF, flagOF);
|
|
}
|
|
else
|
|
{
|
|
headerStr = "FLAGS";
|
|
}
|
|
|
|
return StrFormat("%s\n__flags\nCarry (CF)\t$flagcf\nParity (PF)\t$flagpf\nAdjust (AF)\t$flagaf\nZero (ZF)\t$flagzf\nSign (SF)\t$flagsf\nInterrupt (IF)\t$flagif\nDirection (DF)\t$flagdf\nOverflow (OF)\t$flagof",
|
|
headerStr.c_str());
|
|
}
|
|
break;
|
|
default:
|
|
BF_ASSERT(false && "unknown category register");
|
|
return "UNKNOWNCATEGORY\n__unknown\n";
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case DbgType_Single:
|
|
{
|
|
DwFloatDisplayType floatDisplayType = displayInfo->mFloatDisplayType;
|
|
if (floatDisplayType == DwFloatDisplayType_Default)
|
|
floatDisplayType = DwFloatDisplayType_Minimal;
|
|
if (floatDisplayType == DwFloatDisplayType_Minimal)
|
|
ExactMinimalFloatToStr(typedValue.mSingle, str);
|
|
else if (floatDisplayType == DwFloatDisplayType_Full)
|
|
sprintf(str, "%1.9g", typedValue.mSingle);
|
|
else if (floatDisplayType == DwFloatDisplayType_HexUpper)
|
|
sprintf(str, "0x%04X", typedValue.mUInt32);
|
|
else //if (floatDisplayType == DwFloatDisplayType_HexLower)
|
|
sprintf(str, "0x%04x", typedValue.mUInt32);
|
|
return StrFormat("%s\n%s", str, WrapWithModifiers("float", origValueType, language).c_str());
|
|
}
|
|
case DbgType_Double:
|
|
{
|
|
DwFloatDisplayType floatDisplayType = displayInfo->mFloatDisplayType;
|
|
if (floatDisplayType == DwFloatDisplayType_Default)
|
|
floatDisplayType = DwFloatDisplayType_Minimal;
|
|
if (floatDisplayType == DwFloatDisplayType_Minimal)
|
|
ExactMinimalDoubleToStr(typedValue.mDouble, str);
|
|
else if (floatDisplayType == DwFloatDisplayType_Full)
|
|
sprintf(str, "%1.17g", typedValue.mDouble);
|
|
else if (floatDisplayType == DwFloatDisplayType_HexUpper)
|
|
sprintf(str, "0x%08llX", typedValue.mUInt64);
|
|
else //if (floatDisplayType == DwFloatDisplayType_HexLower)
|
|
sprintf(str, "0x%08llx", typedValue.mUInt64);
|
|
return StrFormat("%s\n%s", str, WrapWithModifiers("double", origValueType, language).c_str());
|
|
}
|
|
case DbgType_Subroutine:
|
|
if (typedValue.mCharPtr != NULL)
|
|
return StrFormat("%s\nfunc", typedValue.mCharPtr);
|
|
else
|
|
return "\nfunc";
|
|
case DbgType_RawText:
|
|
return StrFormat("%s\nrawtext", typedValue.mCharPtr);
|
|
case DbgType_Ptr:
|
|
{
|
|
addr_target ptrVal = (addr_target)typedValue.mPtr;
|
|
String retVal;
|
|
DbgType* innerType = dwValueType->mTypeParam;
|
|
if (innerType == NULL)
|
|
return EncodeDataPtr(ptrVal, true) + "\nvoid*";
|
|
|
|
bool isChar = false;
|
|
|
|
DbgType* unmodInnerType = innerType->RemoveModifiers();
|
|
if (unmodInnerType != NULL)
|
|
{
|
|
if (language == DbgLanguage_Beef)
|
|
{
|
|
if ((unmodInnerType->mTypeCode == DbgType_UChar) ||
|
|
(unmodInnerType->mTypeCode == DbgType_UChar16) ||
|
|
(unmodInnerType->mTypeCode == DbgType_UChar32))
|
|
isChar = true;
|
|
}
|
|
else
|
|
{
|
|
if ((unmodInnerType->mTypeCode == DbgType_SChar) ||
|
|
(unmodInnerType->mTypeCode == DbgType_SChar16) ||
|
|
(unmodInnerType->mTypeCode == DbgType_SChar32))
|
|
isChar = true;
|
|
}
|
|
}
|
|
|
|
if ((isChar) && (formatInfo.mArrayLength == -1))
|
|
{
|
|
if ((!typedValue.mIsLiteral) && (!formatInfo.mHidePointers))
|
|
retVal = EncodeDataPtr(ptrVal, true);
|
|
|
|
int strLen = 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(unmodInnerType->mTypeCode, typedValue.mLocalIntPtr, typedValue.mIsLiteral, strLen, formatInfo, wantStringView);
|
|
if (formatInfo.mRawString)
|
|
return strResult;
|
|
if (!strResult.IsEmpty())
|
|
{
|
|
if (!retVal.IsEmpty())
|
|
retVal += " ";
|
|
if (!wantStringView)
|
|
retVal += strResult;
|
|
}
|
|
retVal += "\n" + origValueType->ToString(language);
|
|
retVal += "\n:stringView";
|
|
if (wantStringView)
|
|
{
|
|
retVal += "\t";
|
|
retVal += SlashString(strResult, false, false, true);
|
|
}
|
|
return retVal;
|
|
}
|
|
else if ((unmodInnerType != NULL) &&
|
|
((unmodInnerType->mTypeCode == DbgType_Class) || (unmodInnerType->mTypeCode == DbgType_Struct) || (unmodInnerType->mTypeCode == DbgType_Union)))
|
|
{
|
|
isCompositeType = true;
|
|
}
|
|
else if ((unmodInnerType != NULL) && (unmodInnerType->mTypeCode == DbgType_SizedArray))
|
|
{
|
|
isSizedArray = true;
|
|
}
|
|
else if (unmodInnerType->mTypeCode == DbgType_Subroutine)
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
addr_target funcPtr = (addr_target)typedValue.mPtr;
|
|
String retVal;
|
|
if ((!typedValue.mIsLiteral) && (!formatInfo.mHidePointers))
|
|
retVal = EncodeDataPtr(funcPtr, true);
|
|
|
|
String symbolName;
|
|
addr_target offset;
|
|
DbgModule* dwarf;
|
|
String demangledName;
|
|
auto subProgram = mDebugTarget->FindSubProgram(funcPtr);
|
|
if (subProgram != NULL)
|
|
{
|
|
demangledName = subProgram->ToString();
|
|
}
|
|
else if (mDebugTarget->FindSymbolAt(funcPtr, &symbolName, &offset, &dwarf))
|
|
{
|
|
demangledName = BfDemangler::Demangle(symbolName, language);
|
|
|
|
if (offset != 0)
|
|
demangledName += StrFormat("+%d", offset);
|
|
}
|
|
else
|
|
{
|
|
auto dbgModule = mDebugTarget->FindDbgModuleForAddress(funcPtr);
|
|
if (dbgModule != NULL)
|
|
{
|
|
demangledName += dbgModule->GetLinkedModule()->mDisplayName + "!";
|
|
demangledName += StrFormat("0x%@", funcPtr);
|
|
}
|
|
}
|
|
|
|
if (!demangledName.IsEmpty())
|
|
{
|
|
retVal += " {";
|
|
retVal += demangledName;
|
|
retVal += "}";
|
|
}
|
|
retVal += "\n" + origValueType->ToString(language);
|
|
|
|
return retVal;
|
|
}
|
|
else if (unmodInnerType->mTypeCode == DbgType_Void)
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
addr_target ptr = (addr_target)typedValue.mPtr;
|
|
String symbolName;
|
|
addr_target offset;
|
|
DbgModule* dwarf;
|
|
String demangledName;
|
|
|
|
retVal += demangledName = StrFormat("0x%@", ptr);
|
|
|
|
if (mDebugTarget->FindSymbolAt(ptr, &symbolName, &offset, &dwarf))
|
|
{
|
|
if (offset == 0)
|
|
{
|
|
retVal += " {";
|
|
retVal += BfDemangler::Demangle(symbolName, language);
|
|
retVal += "}";
|
|
}
|
|
}
|
|
|
|
retVal += "\n" + origValueType->ToString(language);
|
|
|
|
return retVal;
|
|
}
|
|
else
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
addr_target ptrVal = (addr_target)typedValue.mPtr;
|
|
String retVal;
|
|
if ((!typedValue.mIsLiteral) && (!formatInfo.mHidePointers))
|
|
retVal = EncodeDataPtr(ptrVal, true);
|
|
|
|
if (ptrVal != 0)
|
|
{
|
|
DbgExprEvaluator dbgExprEvaluator(this, dbgModule, NULL, -1, -1);
|
|
DbgTypedValue innerTypedVal = dbgExprEvaluator.ReadTypedValue(NULL, innerType, typedValue.mPtr, DbgAddrType_Target);
|
|
if (innerTypedVal)
|
|
{
|
|
DwFormatInfo defaultFormatInfo;
|
|
defaultFormatInfo.mLanguage = formatInfo.mLanguage;
|
|
defaultFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + 2; // Take into accout the necessary {}'s
|
|
defaultFormatInfo.mExpandItemDepth++;
|
|
String innerStr = DbgTypedValueToString(innerTypedVal, "", defaultFormatInfo, &dbgExprEvaluator);
|
|
int crIdx = 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" + origValueType->ToString(language);
|
|
innerType->PopulateType();
|
|
|
|
if ((ptrVal != 0) &&
|
|
((!innerType->mMemberList.IsEmpty()) || (innerType->mSize > 0) || (innerType->mTypeParam != NULL)))
|
|
{
|
|
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 += ", this=" + ptrDataStr;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case DbgType_Union:
|
|
case DbgType_Class:
|
|
case DbgType_Struct:
|
|
isCompositeType = true;
|
|
break;
|
|
case DbgType_Enum:
|
|
enumVal = typedValue.GetInt64();
|
|
isEnum = true;
|
|
break;
|
|
case DbgType_SizedArray:
|
|
{
|
|
isSizedArray = true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (isSizedArray)
|
|
{
|
|
String retVal;
|
|
addr_target ptrVal = 0;
|
|
|
|
DbgType* arrayType = dwValueType;
|
|
DbgType* innerType = dwValueType->mTypeParam;
|
|
if (dwValueType->mTypeCode == DbgType_SizedArray)
|
|
{
|
|
ptrVal = (addr_target)typedValue.mSrcAddress;
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(dwValueType->mTypeCode == DbgType_Ptr);
|
|
arrayType = innerType;
|
|
innerType = arrayType->mTypeParam;
|
|
ptrVal = typedValue.mPtr;
|
|
|
|
if ((!typedValue.mIsLiteral) && (!formatInfo.mHidePointers))
|
|
retVal = EncodeDataPtr(ptrVal, true) + " ";
|
|
}
|
|
if (ptrVal == 0)
|
|
ptrVal = typedValue.mPtr;
|
|
|
|
intptr arraySize = 0;
|
|
intptr innerSize = innerType->GetStride();
|
|
if (innerSize > 0)
|
|
arraySize = arrayType->GetStride() / innerSize;
|
|
else
|
|
{
|
|
// Failure!
|
|
}
|
|
|
|
String idxStr = "[{0}]";
|
|
|
|
if (innerType->IsChar(language))
|
|
{
|
|
String strVal = ReadString(innerType->mTypeCode, typedValue.mSrcAddress, false, arraySize, formatInfo, false);
|
|
if (formatInfo.mRawString)
|
|
return strVal;
|
|
retVal += strVal;
|
|
}
|
|
else
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
|
|
_ShowArraySummary(retVal, ptrVal, arraySize, innerType);
|
|
}
|
|
|
|
retVal += "\n" + origValueType->ToString(language);
|
|
|
|
String referenceId = dwValueType->ToString(language);
|
|
String evalStr;
|
|
|
|
// Why did we have the "na"? Do we not want to show addresses for all members?
|
|
evalStr = "((" + innerType->ToStringRaw(language) + "*)" + EncodeDataPtr(ptrVal, true) + ")[{0}], refid=" + MaybeQuoteFormatInfoParam(referenceId + ".[]");
|
|
if (typedValue.mIsReadOnly)
|
|
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;
|
|
}
|
|
|
|
dwValueType->PopulateType();
|
|
|
|
if (isEnum)
|
|
{
|
|
String retVal;
|
|
int64 bitsLeft = enumVal;
|
|
int valueCount = 0;
|
|
|
|
String editVal;
|
|
|
|
dwValueType = dwValueType->GetPrimaryType();
|
|
|
|
dwValueType->PopulateType();
|
|
|
|
while ((bitsLeft != 0) || (valueCount == 0))
|
|
{
|
|
DbgVariable* bestMatch = NULL;
|
|
|
|
for (auto member : dwValueType->mMemberList)
|
|
{
|
|
if (member->mConstValue == bitsLeft)
|
|
{
|
|
bestMatch = member;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bestMatch == NULL)
|
|
{
|
|
for (auto member : dwValueType->mMemberList)
|
|
{
|
|
if ((member->mConstValue != 0) &&
|
|
((member->mConstValue & bitsLeft) == member->mConstValue))
|
|
{
|
|
bestMatch = member;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestMatch == NULL)
|
|
break;
|
|
|
|
if (valueCount > 0)
|
|
{
|
|
retVal += " | ";
|
|
if (language == DbgLanguage_C)
|
|
editVal += " | ";
|
|
}
|
|
|
|
if (language == DbgLanguage_Beef)
|
|
retVal += ".";
|
|
retVal += bestMatch->mName;
|
|
|
|
if (language == DbgLanguage_C)
|
|
{
|
|
if (dwValueType->mParent != NULL)
|
|
{
|
|
editVal += dwValueType->mParent->ToString(language);
|
|
editVal += "::";
|
|
}
|
|
editVal += bestMatch->mName;
|
|
}
|
|
|
|
valueCount++;
|
|
bitsLeft &= ~bestMatch->mConstValue;
|
|
}
|
|
|
|
if ((valueCount == 0) || (bitsLeft != 0))
|
|
{
|
|
if (valueCount > 0)
|
|
retVal += " | ";
|
|
retVal += StrFormat("%lld", bitsLeft);
|
|
|
|
if (language == DbgLanguage_C)
|
|
{
|
|
if (valueCount > 0)
|
|
editVal += " | ";
|
|
editVal += StrFormat("%lld", bitsLeft);
|
|
}
|
|
}
|
|
|
|
retVal += "\n" + origValueType->ToString();
|
|
if (language == DbgLanguage_C)
|
|
{
|
|
retVal += "\n:editVal\t";
|
|
retVal += editVal;
|
|
}
|
|
retVal += "\n:canEdit";
|
|
|
|
return retVal;
|
|
}
|
|
else if (isCompositeType)
|
|
{
|
|
addr_target ptrVal;
|
|
if (dwValueType->IsPointer())
|
|
ptrVal = (addr_target)typedValue.mPtr;
|
|
else
|
|
ptrVal = (addr_target)typedValue.mSrcAddress;
|
|
String retVal;
|
|
if ((!typedValue.mIsLiteral) && (dwValueType->IsPointer()) &&
|
|
((!formatInfo.mHidePointers) || (ptrVal == 0)))
|
|
retVal = EncodeDataPtr(ptrVal, true);
|
|
|
|
DbgType* innerType = dwValueType;
|
|
bool wasPtr = false;
|
|
if (innerType->mTypeCode == DbgType_Ptr)
|
|
{
|
|
wasPtr = true;
|
|
innerType = dwValueType->mTypeParam;
|
|
innerType = innerType->RemoveModifiers();
|
|
}
|
|
|
|
innerType = innerType->GetPrimaryType();
|
|
addr_target dataPtr = wasPtr ? typedValue.mPtr : typedValue.mSrcAddress;
|
|
DbgType* actualType = NULL;
|
|
bool useActualRawType = false;
|
|
|
|
bool isBfObject = innerType->IsBfObject();
|
|
bool hasCPPVTable = false;
|
|
if (!isBfObject)
|
|
hasCPPVTable = innerType->HasCPPVTable();
|
|
|
|
int bfObjectFlags = 0;
|
|
addr_target classVDataPtr = 0;
|
|
bool isAppendBfObject = false;
|
|
bool isStackBfObject = false;
|
|
bool isDeletedBfObject = false;
|
|
bool isCompositeWithoutAddress = false;
|
|
|
|
if (innerType->IsBfPayloadEnum())
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
|
|
auto tagMember = innerType->mMemberList.mTail;
|
|
int tagIdx = 0;
|
|
|
|
if (dataPtr == -1)
|
|
{
|
|
DbgEvaluationContext dbgEvaluationContext(this, dbgModule, "(int)" + expr, &formatInfo);
|
|
auto dscValue = dbgEvaluationContext.EvaluateInContext(DbgTypedValue());
|
|
tagIdx = dscValue.mInt32;
|
|
}
|
|
else if (!ReadMemory((intptr)ptrVal + tagMember->mMemberOffset, tagMember->mType->mSize, (void*)&tagIdx))
|
|
{
|
|
return StrFormat("!Failed to read from 0x%@", ptrVal);
|
|
}
|
|
|
|
char findStr[16];
|
|
findStr[0] = '_';
|
|
itoa(tagIdx, findStr + 1, 10);
|
|
int len = strlen(findStr);
|
|
findStr[len] = '_';
|
|
len++;
|
|
|
|
if (!retVal.empty())
|
|
retVal += " ";
|
|
|
|
int startIdx = 0;
|
|
for (auto member : innerType->mMemberList)
|
|
{
|
|
if (strncmp(member->mName, findStr, len) == 0)
|
|
{
|
|
retVal += ".";
|
|
retVal += member->mName + len;
|
|
|
|
String tupleExpr;
|
|
|
|
DbgTypedValue tupleVal;
|
|
if (dataPtr == -1)
|
|
{
|
|
tupleVal.mSrcAddress = -1;
|
|
tupleVal.mType = member->mType;
|
|
//tupleExpr = "$" + expr + "$u";
|
|
tupleVal.mVariable = typedValue.mVariable;
|
|
tupleExpr = "(" + member->mType->ToStringRaw() + ")(" + expr + ")";
|
|
}
|
|
else
|
|
{
|
|
tupleVal.mType = member->mType;
|
|
tupleVal.mSrcAddress = ptrVal;
|
|
}
|
|
|
|
DwFormatInfo displayStrFormatInfo = formatInfo;
|
|
displayStrFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + (int)retVal.length();
|
|
displayStrFormatInfo.mExpandItemDepth++;
|
|
displayStrFormatInfo.mHidePointers = false;
|
|
retVal += DbgTypedValueToString(tupleVal, tupleExpr, displayStrFormatInfo, NULL);
|
|
int idx = (int)retVal.IndexOf('\n');
|
|
if (idx != -1)
|
|
{
|
|
if ((idx > 2) && (strncmp(retVal.c_str() + idx - 2, "()", 2) == 0))
|
|
{
|
|
// Take off a terminating "()" on the value, if there is one
|
|
retVal.Remove(idx - 2, 2);
|
|
}
|
|
|
|
String typeName = innerType->ToString(DbgLanguage_Unknown, true);
|
|
typeName += " ";
|
|
retVal.Insert(idx + 1, typeName);
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isBfObject)
|
|
{
|
|
classVDataPtr = ReadMemory<addr_target>(ptrVal);
|
|
mDebugTarget->GetCompilerSettings();
|
|
if (mDebugTarget->mBfObjectHasFlags)
|
|
{
|
|
bfObjectFlags = ((int)classVDataPtr) & 0xFF;
|
|
|
|
if ((bfObjectFlags & BfObjectFlag_Deleted) != 0)
|
|
isDeletedBfObject = true;
|
|
if ((bfObjectFlags & BfObjectFlag_AppendAlloc) != 0)
|
|
isAppendBfObject = true;
|
|
if ((bfObjectFlags & (BfObjectFlag_StackAlloc | BfObjectFlag_Allocated)) == BfObjectFlag_StackAlloc)
|
|
isStackBfObject = true;
|
|
|
|
classVDataPtr &= ~0xFF;
|
|
}
|
|
}
|
|
|
|
if (!formatInfo.mIgnoreDerivedClassInfo)
|
|
{
|
|
if (isBfObject)
|
|
{
|
|
dbgModule->ParseSymbolData();
|
|
|
|
String symbolName;
|
|
addr_target symOffset;
|
|
if ((mDebugTarget->FindSymbolAt(classVDataPtr, &symbolName, &symOffset)) && (symOffset < 0x100))
|
|
{
|
|
String mangledClassName;
|
|
|
|
const char* symEnd = "sBfClassVData";
|
|
int symEndLen = strlen(symEnd);
|
|
if (((int)symbolName.length() > symEndLen) && (strstr(symbolName.c_str(), symEnd) != NULL))
|
|
mangledClassName = symbolName;
|
|
|
|
// If we have flags then we may be pointing past the _typeData, actually. We could fix this by masking out
|
|
// the flags area, but we need to be sure we are running a build that supports flags
|
|
symEnd = "sBfTypeData";
|
|
symEndLen = strlen(symEnd);
|
|
if (((int) symbolName.length() > symEndLen) && (strstr(symbolName.c_str(), symEnd) != NULL))
|
|
mangledClassName = symbolName;
|
|
|
|
if (mangledClassName.length() > 0)
|
|
{
|
|
String className = BfDemangler::Demangle(mangledClassName, innerType->GetLanguage(), BfDemangler::Flag_RawDemangle);
|
|
|
|
for (int i = 0; i < className.length() - 3; i++)
|
|
{
|
|
if ((className[i] == 'b') &&
|
|
(className[i + 1] == 'f') &&
|
|
(className[i + 2] == '.'))
|
|
{
|
|
bool matches;
|
|
if (i == 0)
|
|
matches = true;
|
|
else
|
|
{
|
|
char prevC = className[i - 1];
|
|
if ((prevC == ' ') ||
|
|
(prevC == ',') ||
|
|
(prevC == '<'))
|
|
{
|
|
matches = true;
|
|
}
|
|
}
|
|
if (matches)
|
|
className.Remove(i, 3);
|
|
}
|
|
}
|
|
|
|
int lastDot = (int)className.LastIndexOf('.');
|
|
if (lastDot > 0)
|
|
className = className.Substring(0, lastDot);
|
|
|
|
const char* arrPrefix = "System.Array1<";
|
|
if (strncmp(className.c_str(), arrPrefix, strlen(arrPrefix)) == 0)
|
|
{
|
|
className = className.Substring(strlen(arrPrefix), className.length() - strlen(arrPrefix) - 1);
|
|
className += "[]";
|
|
}
|
|
|
|
auto typeEntry = dbgModule->GetLinkedModule()->mTypeMap.Find(className.c_str(), DbgLanguage_BeefUnfixed);
|
|
if (typeEntry != NULL)
|
|
{
|
|
actualType = typeEntry->mValue;
|
|
if (!actualType->IsBfObject())
|
|
{
|
|
if (actualType->mTypeCode == DbgType_Ptr)
|
|
{
|
|
actualType = actualType->mTypeParam;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (hasCPPVTable)
|
|
{
|
|
dbgModule->ParseSymbolData();
|
|
|
|
addr_target classVDataPtr = ReadMemory<addr_target>(ptrVal);
|
|
String symbolName;
|
|
|
|
addr_target offset = 0;
|
|
if (mDebugTarget->FindSymbolAt(classVDataPtr, &symbolName, &offset, NULL))
|
|
{
|
|
// On GNU, vtable indices can "go negative" for things like RTTI and virtual inheritance, so
|
|
// we can't rely on an exact vtable address lookup
|
|
if (offset < 0x200)
|
|
{
|
|
DbgLanguage lang = innerType->GetLanguage();
|
|
const char* symStart = (innerType->mCompileUnit->mDbgModule->mDbgFlavor == DbgFlavor_GNU) ? "_ZTV" : "??_7";
|
|
if (strncmp(symbolName.c_str(), symStart, strlen(symStart)) == 0)
|
|
{
|
|
//String mangledClassName = symbolName.Substring(1);
|
|
String className = BfDemangler::Demangle(symbolName, lang);
|
|
int vtableNameIdx = (int)className.IndexOf("::`vftable'");
|
|
if (vtableNameIdx != -1)
|
|
className = className.Substring(0, vtableNameIdx);
|
|
|
|
auto typeEntry = dbgModule->mTypeMap.Find(className.c_str(), DbgLanguage_C);
|
|
if (typeEntry != NULL)
|
|
{
|
|
actualType = typeEntry->mValue;
|
|
|
|
if ((int)className.IndexOf('<') != -1)
|
|
useActualRawType = true;
|
|
|
|
int thisOffset = 0;
|
|
if (!DbgExprEvaluator::TypeIsSubTypeOf(actualType, innerType, &thisOffset))
|
|
{
|
|
// This catches virtual inheritance cases where we can't downcast
|
|
actualType = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DbgType* displayType = origValueType;
|
|
|
|
String displayString;
|
|
bool wantsCustomExpandedItems = false;
|
|
DebugVisualizerEntry* debugVis = NULL;
|
|
Array<String> dbgVisWildcardCaptures;
|
|
DbgType* dwUseType = (actualType != NULL) ? actualType : innerType;
|
|
//auto ptrDataType = dwValueType;
|
|
//TODO: Changed this from the above to account for COFF types where 'this' is always a fwd reference, does this cause any issues?
|
|
auto ptrDataType = innerType;
|
|
String ptrDataStr;
|
|
if (/*(!innerType->IsBfObject()) &&*/ (!ptrDataType->IsPointer()))
|
|
{
|
|
if ((dataPtr != 0) || (ptrDataType->GetByteCount() > sizeof(addr_target)))
|
|
{
|
|
bool wantsRefThis = ptrDataType->WantsRefThis();
|
|
ptrDataType = ptrDataType->GetDbgModule()->GetPointerType(ptrDataType);
|
|
if (wantsRefThis)
|
|
ptrDataStr += "*";
|
|
}
|
|
else
|
|
{
|
|
// Data is inline - must be int-sized or less
|
|
isCompositeWithoutAddress = true;
|
|
dataPtr = typedValue.mPtr;
|
|
}
|
|
}
|
|
String ptrDataTypeStr = ptrDataType->ToStringRaw();
|
|
ptrDataStr += StrFormat("(%s)", ptrDataTypeStr.c_str()) + EncodeDataPtr(dataPtr, true);
|
|
|
|
DbgType* dwUsePtrType = dwUseType;
|
|
String ptrUseDataStr;
|
|
if (!dwUsePtrType->IsPointer())
|
|
{
|
|
bool wantsRefThis = dwUsePtrType->WantsRefThis();
|
|
dwUsePtrType = dwUsePtrType->GetDbgModule()->GetPointerType(dwUsePtrType);
|
|
if (wantsRefThis)
|
|
ptrUseDataStr += "*";
|
|
}
|
|
String ptrUseDataTypeStr = dwUsePtrType->ToStringRaw();
|
|
ptrUseDataStr += StrFormat("(%s)", ptrUseDataTypeStr.c_str()) + EncodeDataPtr(dataPtr, true);
|
|
|
|
if ((origTypedValue.mSrcAddress == -1) && (origTypedValue.mVariable != NULL))
|
|
{
|
|
ptrDataStr = origTypedValue.mVariable->mName;
|
|
if (!origTypedValue.mType->RemoveModifiers()->Equals(origTypedValue.mVariable->mType->RemoveModifiers()))
|
|
{
|
|
//ptrDataStr = StrFormat("(%s)%s", origTypedValue.mType->ToString().c_str(), origTypedValue.mVariable->mName);
|
|
ptrDataStr = expr;
|
|
}
|
|
ptrUseDataStr = ptrDataStr;
|
|
}
|
|
|
|
bool isNull = wasPtr && (dataPtr == 0);
|
|
bool isBadSrc = !wasPtr && (dataPtr == 0) && (!dwValueType->IsValuelessType());
|
|
|
|
if ((ptrVal == 0) && (dwValueType->IsTypedPrimitive()))
|
|
{
|
|
DbgTypedValue rawVal;
|
|
rawVal.mInt64 = origTypedValue.mInt64;
|
|
rawVal.mType = dwValueType->GetRootBaseType();
|
|
|
|
ptrDataStr = "(" + dwUseType->ToStringRaw() + ")";
|
|
ptrDataStr += DbgTypedValueToString(rawVal, expr, formatInfo, optEvaluator, fullPrecision);
|
|
|
|
int editValIdx = ptrDataStr.IndexOf(":editVal");
|
|
if (editValIdx != -1)
|
|
ptrDataStr.Remove(0, editValIdx + 9);
|
|
int crPos = (int)ptrDataStr.IndexOf('\n');
|
|
if (crPos != -1)
|
|
ptrDataStr.RemoveToEnd(crPos);
|
|
ptrUseDataStr = ptrDataStr;
|
|
|
|
if ((origTypedValue.mRegNum != -1) && (!expr.IsEmpty()) && (!formatInfo.mExplicitThis))
|
|
{
|
|
// There's no address, use direct local identifier
|
|
ptrDataStr = expr;
|
|
ptrUseDataStr = expr;
|
|
}
|
|
|
|
// This keeps 'function' types from showing null as "<null parent>"
|
|
isBadSrc = false;
|
|
}
|
|
else if ((ptrVal == 0) && (dwValueType->IsCompositeType()))
|
|
{
|
|
}
|
|
|
|
DbgTypedValue useTypedValue = typedValue;
|
|
if ((origHadRef) || ((typedValue.mType->HasPointer()) && (!dwUseType->HasPointer())))
|
|
{
|
|
useTypedValue.mSrcAddress = useTypedValue.mPtr;
|
|
useTypedValue.mPtr = 0;
|
|
|
|
if (dwUseType->IsTypedPrimitive())
|
|
{
|
|
int byteCount = dwUseType->GetByteCount();
|
|
if (byteCount <= sizeof(intptr))
|
|
{
|
|
ReadMemory(useTypedValue.mSrcAddress, byteCount, &useTypedValue.mPtr);
|
|
}
|
|
}
|
|
}
|
|
useTypedValue.mType = dwUseType;
|
|
if ((!formatInfo.mNoVisualizers) && (!isNull) && (!isBadSrc))
|
|
{
|
|
if (language == DbgLanguage_Beef)
|
|
dwUseType->FixName();
|
|
debugVis = FindVisualizerForType(dwUseType, &dbgVisWildcardCaptures);
|
|
}
|
|
|
|
bool hadCustomDisplayString = false;
|
|
if (debugVis != NULL)
|
|
{
|
|
auto& displayStringList = (formatInfo.mRawString || wantStringView) ? debugVis->mStringViews : debugVis->mDisplayStrings;
|
|
|
|
for (auto displayEntry : displayStringList)
|
|
{
|
|
if (!displayEntry->mCondition.empty())
|
|
{
|
|
if (!EvalCondition(debugVis, dbgCompileUnit, useTypedValue, formatInfo, displayEntry->mCondition, dbgVisWildcardCaptures, displayString))
|
|
continue;
|
|
}
|
|
|
|
hadCustomDisplayString = true;
|
|
String displayStr = mDebugManager->mDebugVisualizers->DoStringReplace(displayEntry->mString, dbgVisWildcardCaptures);
|
|
if (displayString.length() > 0)
|
|
displayString += " ";
|
|
|
|
if (wantStringView)
|
|
{
|
|
DwFormatInfo strFormatInfo = formatInfo;
|
|
strFormatInfo.mRawString = true;
|
|
ProcessEvalString(dbgCompileUnit, useTypedValue, displayStr, stringViewData, strFormatInfo, debugVis, true);
|
|
}
|
|
else
|
|
ProcessEvalString(dbgCompileUnit, useTypedValue, 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 "";
|
|
|
|
bool isTuple = (dwUseType->mName != NULL) && (dwUseType->mName[0] == '(') && (language == DbgLanguage_Beef);
|
|
|
|
if (isBadSrc)
|
|
{
|
|
displayString += "<null parent>";
|
|
}
|
|
else if ((!isNull) && (!formatInfo.mNoVisualizers) && (!hadCustomDisplayString))
|
|
{
|
|
// Create our own custom display
|
|
|
|
String firstRet;
|
|
String bigRet = isTuple ? "(" : "{ ";
|
|
|
|
int memberIdx = 0;
|
|
DbgType* summaryType = dwUseType;
|
|
bool summaryDone = false;
|
|
bool truncatedMemberList = false;
|
|
|
|
DbgTypedValue summaryTypedValue = useTypedValue;
|
|
String summaryDataStr = ptrDataStr;
|
|
String splatStr;
|
|
if (dataPtr == -1)
|
|
splatStr = expr;
|
|
|
|
while (summaryType != NULL)
|
|
{
|
|
summaryType->PopulateType();
|
|
|
|
if ((summaryType->IsTypedPrimitive()) &&
|
|
((summaryType->mBaseTypes.IsEmpty()) || (!summaryType->mBaseTypes.front()->mBaseType->IsTypedPrimitive())))
|
|
{
|
|
if (formatInfo.mTotalSummaryLength + (int)displayString.length() > 255)
|
|
{
|
|
truncatedMemberList = true;
|
|
summaryDone = true;
|
|
bigRet += "...";
|
|
}
|
|
else
|
|
{
|
|
DwFormatInfo displayStrFormatInfo = formatInfo;
|
|
displayStrFormatInfo.mExpandItemDepth = 1;
|
|
displayStrFormatInfo.mTotalSummaryLength += (int)displayString.length();
|
|
displayStrFormatInfo.mHidePointers = false;
|
|
|
|
DbgType* primType = summaryType->mTypeParam;
|
|
String result;
|
|
|
|
if (primType->IsInteger())
|
|
formatInfo.mTypeKindFlags = (DbgTypeKindFlags)(formatInfo.mTypeKindFlags | DbgTypeKindFlag_Int);
|
|
|
|
if ((dataPtr != 0) && (dataPtr != -1))
|
|
{
|
|
String evalString = "(" + primType->ToString() + ")" + ptrDataStr;
|
|
DbgTypedValue evalResult = EvaluateInContext(dbgCompileUnit, origTypedValue, evalString, &displayStrFormatInfo);
|
|
if (evalResult)
|
|
result = DbgTypedValueToString(evalResult, evalString, displayStrFormatInfo, NULL);
|
|
}
|
|
else
|
|
{
|
|
DbgTypedValue evalResult = origTypedValue;
|
|
evalResult.mType = primType;
|
|
String evalString = "(" + primType->ToString() + ")" + expr;
|
|
result = DbgTypedValueToString(evalResult, evalString, displayStrFormatInfo, NULL);
|
|
}
|
|
|
|
if (formatInfo.mRawString)
|
|
return result;
|
|
|
|
int crPos = result.IndexOf('\n');
|
|
if (crPos != -1)
|
|
result.RemoveToEnd(crPos);
|
|
|
|
if (memberIdx == 0)
|
|
firstRet = result;
|
|
|
|
bigRet += result;
|
|
memberIdx++;
|
|
}
|
|
}
|
|
|
|
for (auto member : summaryType->mMemberList)
|
|
{
|
|
if (!member->mIsStatic)
|
|
{
|
|
if (formatInfo.mTotalSummaryLength + retVal.length() + bigRet.length() > 255)
|
|
{
|
|
truncatedMemberList = true;
|
|
summaryDone = true;
|
|
bigRet += "...";
|
|
break;
|
|
}
|
|
|
|
if (member->mName != NULL)
|
|
{
|
|
if (member->mName[0] == '$')
|
|
continue;
|
|
|
|
if (!isdigit(*member->mName))
|
|
{
|
|
if (memberIdx != 0)
|
|
bigRet += isTuple ? ", " : " ";
|
|
|
|
if ((!isTuple) || (member->mName[0] != '_'))
|
|
{
|
|
bigRet += String(member->mName);
|
|
bigRet += isTuple ? ":" : "=";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (memberIdx != 0)
|
|
bigRet += ", ";
|
|
}
|
|
|
|
DwFormatInfo displayStrFormatInfo = formatInfo;
|
|
displayStrFormatInfo.mExpandItemDepth = 1;
|
|
displayStrFormatInfo.mHidePointers = false;
|
|
displayStrFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + retVal.length() + bigRet.length();
|
|
|
|
String evalString;
|
|
if (dataPtr != -1)
|
|
{
|
|
if ((member->mName[0] >= '0') && (member->mName[0] <= '9'))
|
|
evalString += "this.";
|
|
evalString += String(member->mName); // +", this=" + summaryDataStr;
|
|
}
|
|
else
|
|
{
|
|
evalString = "(";
|
|
evalString += splatStr;
|
|
evalString += ").";
|
|
evalString += member->mName;
|
|
}
|
|
String referenceId;
|
|
String result;
|
|
if (!member->mType->IsValuelessType())
|
|
{
|
|
DbgTypedValue evalResult = EvaluateInContext(dbgCompileUnit, summaryTypedValue, evalString, &displayStrFormatInfo, &referenceId);
|
|
if (evalResult)
|
|
{
|
|
displayStrFormatInfo.mReferenceId = referenceId;
|
|
result = DbgTypedValueToString(evalResult, evalString, displayStrFormatInfo, NULL);
|
|
int crPos = result.IndexOf('\n');
|
|
if (crPos != -1)
|
|
result.RemoveToEnd(crPos);
|
|
}
|
|
else
|
|
result = "???";
|
|
}
|
|
|
|
if (member->mType->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++;
|
|
}
|
|
else
|
|
{
|
|
//TODO: Handle C++ unions?
|
|
}
|
|
}
|
|
}
|
|
|
|
if (truncatedMemberList)
|
|
break;
|
|
|
|
// Find first base class with members
|
|
DbgType* nextSummaryType = NULL;
|
|
for (auto checkBase : summaryType->mBaseTypes)
|
|
{
|
|
auto checkBaseType = checkBase->mBaseType;
|
|
checkBaseType = checkBaseType->GetPrimaryType();
|
|
checkBaseType->PopulateType();
|
|
if ((checkBaseType->GetByteCount() > 0) || (checkBaseType->IsPrimitiveType()))
|
|
{
|
|
if (!splatStr.empty())
|
|
{
|
|
splatStr = "(" + checkBaseType->ToString() + ")" + splatStr;
|
|
}
|
|
else
|
|
{
|
|
summaryTypedValue.mType = checkBaseType;
|
|
}
|
|
nextSummaryType = checkBaseType;
|
|
break;
|
|
}
|
|
}
|
|
summaryType = nextSummaryType;
|
|
|
|
if (summaryType == NULL)
|
|
break;
|
|
|
|
// Don't add the Object members
|
|
if ((summaryType->GetBaseType() == NULL) && (summaryType->IsBfObject()))
|
|
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;
|
|
}
|
|
|
|
DbgType* memberListType = actualType;
|
|
bool memberListForceCast = false;
|
|
if (actualType != NULL)
|
|
{
|
|
String valTypeName = displayType->ToString();
|
|
String actualTypeName = actualType->ToString(DbgLanguage_Unknown, true);
|
|
String actualUseTypeName = actualTypeName;
|
|
|
|
if ((int)actualTypeName.IndexOf('^') != -1)
|
|
useActualRawType = true;
|
|
|
|
if (useActualRawType)
|
|
actualUseTypeName = actualType->ToStringRaw();
|
|
|
|
if (displayString.empty())
|
|
{
|
|
// Nothing to display
|
|
}
|
|
else
|
|
{
|
|
if (!retVal.empty())
|
|
retVal += " ";
|
|
retVal += displayString;
|
|
}
|
|
|
|
retVal += "\n" + valTypeName;
|
|
|
|
if ((innerType->IsBaseBfObject()) || (innerType->IsInterface()))
|
|
{
|
|
if (actualType != innerType)
|
|
{
|
|
retVal += " {" + actualTypeName + "}";
|
|
memberListForceCast = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (actualType != innerType)
|
|
{
|
|
retVal += " {" + actualTypeName + "}";
|
|
retVal += "\n";
|
|
if (!wantsCustomExpandedItems)
|
|
{
|
|
retVal += "[" + actualTypeName + "]\t((" + actualUseTypeName;
|
|
if (!actualType->IsBfObject())
|
|
retVal += "*";
|
|
retVal += ")this), nd, na, nv, this=" + ptrDataStr;
|
|
memberListType = innerType;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((formatInfo.mHidePointers) && (formatInfo.mIgnoreDerivedClassInfo))
|
|
{
|
|
displayType = innerType;
|
|
if (displayString.empty())
|
|
retVal += displayType->ToString(DbgLanguage_Unknown, true);
|
|
}
|
|
|
|
if (!displayString.empty())
|
|
{
|
|
if (!retVal.empty())
|
|
retVal += " ";
|
|
retVal += displayString;
|
|
}
|
|
else
|
|
{
|
|
if (formatInfo.mRawString)
|
|
return "";
|
|
}
|
|
|
|
retVal += "\n" + displayType->ToString(DbgLanguage_Unknown, true);
|
|
memberListType = innerType;
|
|
}
|
|
|
|
if ((isBfObject) && (mDebugTarget->mBfObjectHasFlags) && (!formatInfo.mNoVisualizers) && (!formatInfo.mRawString))
|
|
{
|
|
int stackTraceLen = 1;
|
|
addr_target stackTraceAddr = ptrVal + sizeof(addr_target);
|
|
if ((bfObjectFlags & BfObjectFlag_AllocInfo) != 0)
|
|
{
|
|
addr_target objectSize = ReadMemory<addr_target>(ptrVal + sizeof(addr_target));
|
|
addr_target largeAllocInfo = ReadMemory<addr_target>(ptrVal + objectSize);
|
|
stackTraceLen = (largeAllocInfo >> 8) & 0xFFFF;
|
|
stackTraceAddr = ptrVal + objectSize + sizeof(addr_target);
|
|
}
|
|
else if ((bfObjectFlags & BfObjectFlag_AllocInfo_Short) != 0)
|
|
{
|
|
addr_target dbgAllocInfo = ReadMemory<addr_target>(ptrVal + sizeof(addr_target));
|
|
stackTraceLen = dbgAllocInfo & 0xFF;
|
|
stackTraceAddr = ptrVal + (dbgAllocInfo >> 16);
|
|
}
|
|
|
|
retVal += StrFormat("\n[AllocStackTrace]\t(System.CallStackList)%s, count=%d, na", EncodeDataPtr(stackTraceAddr, true).c_str(), stackTraceLen);
|
|
}
|
|
|
|
retVal += StrFormat("\n:language\t%d", language);
|
|
|
|
if (formatInfo.mNoMembers)
|
|
{
|
|
//
|
|
}
|
|
else if (wantsCustomExpandedItems)
|
|
{
|
|
HandleCustomExpandedItems(retVal, dbgCompileUnit, debugVis, dwUseType, dwValueType, ptrUseDataStr, ptrDataStr, useTypedValue, dbgVisWildcardCaptures, formatInfo);
|
|
}
|
|
else if ((!isNull) && (!isBadSrc))
|
|
{
|
|
if (dataPtr == -1)
|
|
{
|
|
//String splatName = ((origTypedValue.mSrcAddress == -1) && (origTypedValue.mVariable != NULL)) ? origTypedValue.mVariable->mName : expr;
|
|
String splatName = expr;
|
|
retVal += "\n" + GetMemberList(memberListType, splatName, wasPtr, false, false, true, origTypedValue.mIsReadOnly);
|
|
}
|
|
else
|
|
{
|
|
retVal += "\n" + GetMemberList(memberListType, ptrDataStr, wasPtr, false, memberListForceCast, isCompositeWithoutAddress, origTypedValue.mIsReadOnly);
|
|
}
|
|
}
|
|
|
|
if (formatInfo.mExpandItemDepth > 0)
|
|
return retVal;
|
|
if (isAppendBfObject)
|
|
retVal += "\n:appendAlloc";
|
|
if (isStackBfObject)
|
|
retVal += "\n:stack";
|
|
if (isDeletedBfObject)
|
|
retVal += "\n:deleted";
|
|
|
|
if (!formatInfo.mAction.IsEmpty())
|
|
{
|
|
retVal += "\n:action\t";
|
|
retVal += formatInfo.mAction;
|
|
}
|
|
else if ((debugVis != NULL) && (!debugVis->mAction.empty()))
|
|
{
|
|
String rawActionStr = mDebugManager->mDebugVisualizers->DoStringReplace(debugVis->mAction, dbgVisWildcardCaptures);
|
|
String actionStr;
|
|
ProcessEvalString(dbgCompileUnit, useTypedValue, rawActionStr, actionStr, formatInfo, debugVis, true);
|
|
retVal += "\n:action\t" + actionStr;
|
|
}
|
|
|
|
if ((!typedValue.mIsLiteral) && (dwValueType->IsPointer()))
|
|
{
|
|
retVal += "\n:editVal\t" + EncodeDataPtr(ptrVal, true);
|
|
}
|
|
|
|
if (((debugVis != NULL) && (!debugVis->mStringViews.IsEmpty())) || (wantStringView))
|
|
retVal += "\n:stringView";
|
|
|
|
if (wantStringView)
|
|
{
|
|
retVal += "\t";
|
|
retVal += SlashString(stringViewData, false, false, true);
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
return "Unknown Type\n" + origValueType->ToString();
|
|
}
|
|
|
|
void WinDebugger::HandleCustomExpandedItems(String& retVal, DbgCompileUnit* dbgCompileUnit, DebugVisualizerEntry* debugVis, DbgType* dwUseType, DbgType* dwValueType, String& ptrUseDataStr, String& ptrDataStr, DbgTypedValue useTypedValue, Array<String>& dbgVisWildcardCaptures, DwFormatInfo& formatInfo)
|
|
{
|
|
auto debugVisualizers = mDebugManager->mDebugVisualizers;
|
|
auto dbgModule = dbgCompileUnit->mDbgModule;
|
|
|
|
if (formatInfo.mExpandItemDepth > 10) // Avoid crashing on circular ExpandItems
|
|
return;
|
|
|
|
auto language = formatInfo.mLanguage;
|
|
|
|
bool isReadOnly = false;
|
|
if (useTypedValue.mIsReadOnly)
|
|
isReadOnly = true;
|
|
|
|
for (auto entry : debugVis->mExpandItems)
|
|
{
|
|
if (!entry->mCondition.empty())
|
|
{
|
|
String error;
|
|
if (!EvalCondition(debugVis, dbgCompileUnit, useTypedValue, 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 = dwUseType->ToString();
|
|
|
|
if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_ExpandedItem)
|
|
{
|
|
DbgTypedValue itemValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);
|
|
if (itemValue)
|
|
{
|
|
DwFormatInfo itemFormatInfo = formatInfo;
|
|
itemFormatInfo.mExpandItemDepth++;
|
|
String itemRetVal = DbgTypedValueToString(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)
|
|
{
|
|
DbgTypedValue sizeValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
|
|
Array<int> lowerDimSizes;
|
|
for (auto lowerDim : debugVis->mLowerDimSizes)
|
|
{
|
|
DbgTypedValue lowerDimValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(lowerDim, dbgVisWildcardCaptures), &formatInfo);
|
|
int dimSize = 0;
|
|
if ((lowerDimValue) && (lowerDimValue.mType->IsInteger()))
|
|
dimSize = (int)lowerDimValue.GetInt64();
|
|
dimSize = BF_MAX(dimSize, 1);
|
|
lowerDimSizes.push_back(dimSize);
|
|
}
|
|
|
|
if ((sizeValue) && (sizeValue.mType->IsInteger()) && (sizeValue.GetInt64() > 0))
|
|
{
|
|
if (!debugVis->mCondition.IsEmpty())
|
|
{
|
|
int size = (int)sizeValue.GetInt64();
|
|
DbgTypedValue headPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);
|
|
|
|
DbgTypedValue curNode = headPointer;
|
|
Array<addr_target> parentList;
|
|
String continuationData;
|
|
|
|
int totalSize = 2;
|
|
auto valueType = headPointer.mType;
|
|
String addrs = GetArrayItems(dbgCompileUnit, 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_target) * 2);
|
|
if (hasSecondAddr)
|
|
secondAddr = addrs.Substring(sizeof(addr_target) * 2, sizeof(addr_target) * 2);
|
|
}
|
|
|
|
String evalStr;
|
|
if (valueType != NULL)
|
|
{
|
|
evalStr = "(" + valueType->ToStringRaw();
|
|
if (!valueType->IsPointer())
|
|
evalStr += "*";
|
|
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, 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, sizeValue.GetInt64() / dimSize1, 50000) +
|
|
"\t[{0}]\t" + evalStr;
|
|
}
|
|
else if (lowerDimSizes.size() == 2)
|
|
{
|
|
int dimSize1 = lowerDimSizes[0];
|
|
int dimSize2 = lowerDimSizes[1];
|
|
|
|
DbgTypedValue headPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);
|
|
if ((headPointer.mType != NULL) && (headPointer.mType->IsPointer()))
|
|
{
|
|
String evalStr = StrFormat("((%s[%d]*)", headPointer.mType->mTypeParam->ToStringRaw(language).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, sizeValue.GetInt64() / dimSize1 / dimSize2, 50000) +
|
|
"\t[{0}]\t" + evalStr;
|
|
}
|
|
}
|
|
else if (lowerDimSizes.size() == 3)
|
|
{
|
|
int dimSize1 = lowerDimSizes[0];
|
|
int dimSize2 = lowerDimSizes[1];
|
|
int dimSize3 = lowerDimSizes[2];
|
|
|
|
DbgTypedValue headPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);
|
|
if ((headPointer.mType != NULL) && (headPointer.mType->IsPointer()))
|
|
{
|
|
String evalStr = StrFormat("((%s[%d][%d]*)", headPointer.mType->mTypeParam->ToStringRaw(language).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, sizeValue.GetInt64() / dimSize1 / dimSize2 / dimSize3, 50000) +
|
|
"\t[{0}]\t" + evalStr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
String evalStr = "*(" + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) + " + {0}), this=" + ptrUseDataStr;
|
|
evalStr += ", refid=\"" + referenceId + ".[]${0}\"";
|
|
if (isReadOnly)
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, sizeValue.GetInt64(), 50000) +
|
|
"\t[{0}]\t" + evalStr;
|
|
}
|
|
}
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_IndexItems)
|
|
{
|
|
DbgTypedValue sizeValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
|
|
|
|
if ((sizeValue) && (sizeValue.mType->IsInteger()) && (sizeValue.GetInt64() > 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, sizeValue.GetInt64(), 50000) +
|
|
"\t[{0}]\t" + evalStr;
|
|
}
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_LinkedList)
|
|
{
|
|
DbgType* valueType = NULL;
|
|
if (!debugVis->mValueType.empty())
|
|
{
|
|
valueType = dbgModule->FindType(debugVisualizers->DoStringReplace(debugVis->mValueType, dbgVisWildcardCaptures), dwValueType);
|
|
if (valueType != NULL)
|
|
valueType = valueType->ResolveTypeDef();
|
|
}
|
|
|
|
DbgTypedValue headPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mHeadPointer, dbgVisWildcardCaptures), &formatInfo);
|
|
if (headPointer)
|
|
{
|
|
DbgTypedValue endPointer;
|
|
if (!debugVis->mEndPointer.empty())
|
|
endPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mEndPointer, dbgVisWildcardCaptures), &formatInfo);
|
|
DbgTypedValue nextPointer = EvaluateInContext(dbgCompileUnit, headPointer, debugVisualizers->DoStringReplace(debugVis->mNextPointer, dbgVisWildcardCaptures), &formatInfo);
|
|
|
|
int size = -1;
|
|
if (!debugVis->mSize.empty())
|
|
{
|
|
auto sizeValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
|
|
if (sizeValue)
|
|
size = (int)sizeValue.GetInt64();
|
|
}
|
|
|
|
DbgTypedValue curNode = headPointer;
|
|
Array<addr_target> parentList;
|
|
String continuationData;
|
|
|
|
int totalSize = 2;
|
|
String addrs = GetLinkedListItems(dbgCompileUnit, debugVis, endPointer.mPtr, 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_target)*2);
|
|
if (hasSecondAddr)
|
|
secondAddr = addrs.Substring(sizeof(addr_target)*2, sizeof(addr_target)*2);
|
|
}
|
|
|
|
String evalStr;
|
|
if (valueType != NULL)
|
|
{
|
|
evalStr = "(" + valueType->ToStringRaw();
|
|
if (!valueType->IsPointer())
|
|
evalStr += "*";
|
|
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)
|
|
{
|
|
DbgType* valueType = NULL;
|
|
if (!debugVis->mValueType.empty())
|
|
{
|
|
valueType = dbgModule->FindType(debugVisualizers->DoStringReplace(debugVis->mValueType, dbgVisWildcardCaptures), dwValueType);
|
|
if (valueType != NULL)
|
|
valueType = valueType->ResolveTypeDef();
|
|
}
|
|
|
|
DbgTypedValue sizeValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
|
|
DbgTypedValue headPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mHeadPointer, dbgVisWildcardCaptures), &formatInfo);
|
|
|
|
if (sizeValue)
|
|
sizeValue.mType = sizeValue.mType->RemoveModifiers();
|
|
if ((sizeValue) && (headPointer) && (sizeValue.mType->IsInteger()) && (sizeValue.GetInt64() > 0))
|
|
{
|
|
DbgTypedValue curNode = headPointer;
|
|
Array<addr_target> parentList;
|
|
String continuationData;
|
|
|
|
int getItemCount = (int)BF_MIN(sizeValue.GetInt64(), 32LL);
|
|
|
|
String addrs = GetTreeItems(dbgCompileUnit, debugVis, parentList, valueType, curNode, getItemCount, &continuationData);
|
|
addr_target firstAddr = 0;
|
|
addr_target 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)
|
|
{
|
|
evalStr = "*(" + valueType->ToStringRaw();
|
|
if (!valueType->IsPointer())
|
|
evalStr += "*";
|
|
evalStr += ")0x{1}";
|
|
}
|
|
else
|
|
{
|
|
evalStr += "*(_T_{1}*)0x{2}";
|
|
}
|
|
|
|
int size = (int)sizeValue.GetInt64();
|
|
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)
|
|
{
|
|
DbgTypedValue sizeValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
|
|
DbgTypedValue entriesPtrValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mEntries, dbgVisWildcardCaptures), &formatInfo);
|
|
|
|
if (sizeValue)
|
|
sizeValue.mType = sizeValue.mType->RemoveModifiers();
|
|
if ((sizeValue) && (entriesPtrValue) && (sizeValue.mType->IsInteger()) && (sizeValue.GetInt64() > 0))
|
|
{
|
|
String continuationData;
|
|
|
|
DbgType* valueType = entriesPtrValue.mType;
|
|
|
|
int getItemCount = (int)std::min(sizeValue.GetInt64(), 2LL);
|
|
|
|
DbgType* useTypedValType = useTypedValue.mType;
|
|
addr_target useTypedValPtr = useTypedValue.mPtr;
|
|
addr_target useTypedValAddr = useTypedValue.mSrcAddress;
|
|
|
|
String addrs = GetDictionaryItems(dbgCompileUnit, debugVis, useTypedValue, 0, -1, getItemCount, &continuationData);
|
|
addr_target firstAddr = 0;
|
|
if (addrs.length() > 0)
|
|
{
|
|
const char* addrsPtr = addrs.c_str();
|
|
firstAddr = DecodeTargetDataPtr(addrsPtr);
|
|
}
|
|
|
|
String evalStr = "((" + valueType->ToStringRaw() + ")0x{1}), na";
|
|
|
|
evalStr += ", refid=\"" + referenceId + ".[]\"";
|
|
if (isReadOnly)
|
|
evalStr += ", ne";
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, (int)sizeValue.GetInt64(), 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;
|
|
}
|
|
}
|
|
}
|
|
else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_CallStackList)
|
|
{
|
|
int size = 0;
|
|
|
|
String addrs;
|
|
String firstVal;
|
|
auto ptr = useTypedValue.mPtr;
|
|
|
|
for (int i = 0; i < formatInfo.mOverrideCount; i++)
|
|
{
|
|
auto funcAddr = ReadMemory<addr_target>(ptr + i * sizeof(addr_target));
|
|
auto srcFuncAddr = funcAddr;
|
|
|
|
addrs += EncodeDataPtr(funcAddr - 1, false);
|
|
if (i == 0)
|
|
firstVal = addrs;
|
|
addrs += EncodeDataPtr((addr_target)0, false);
|
|
size++;
|
|
|
|
int inlineIdx = 0;
|
|
|
|
auto subProgram = mDebugTarget->FindSubProgram(funcAddr - 1, DbgOnDemandKind_LocalOnly);
|
|
while (subProgram != NULL)
|
|
{
|
|
if (subProgram->mInlineeInfo == NULL)
|
|
break;
|
|
|
|
auto prevFuncAddr = subProgram->mBlock.mLowPC;
|
|
|
|
subProgram = subProgram->mInlineeInfo->mInlineParent;
|
|
addrs += EncodeDataPtr(subProgram->mBlock.mLowPC + 1, false);
|
|
addrs += EncodeDataPtr(prevFuncAddr, false);
|
|
size++;
|
|
|
|
inlineIdx++;
|
|
}
|
|
}
|
|
|
|
String evalStr = "(System.CallStackAddr)0x{1}";
|
|
evalStr += ", refid=\"" + referenceId + ".[]\"";
|
|
evalStr += ", ne";
|
|
|
|
retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, size, 10000) +
|
|
"\t[{0}]\t" + evalStr + ", action=ShowCodeAddr {1} {2}\t" + firstVal + "\t" + EncodeDataPtr((addr_target)0, false);
|
|
|
|
retVal += "\n:addrs\t" + addrs;
|
|
retVal += "\n:addrsEntrySize\t2";
|
|
return;
|
|
}
|
|
|
|
if (formatInfo.mExpandItemDepth == 0)
|
|
{
|
|
//retVal += "\n[Raw View]\tthis, this=" + ptrDataStr + ", nv";
|
|
retVal += "\n[Raw View]\t" + ptrDataStr + ", nv";
|
|
}
|
|
}
|
|
|
|
bool WinDebugger::IsPaused()
|
|
{
|
|
return (mRunState == RunState_Paused) || (mRunState == RunState_Breakpoint) || (mRunState == RunState_Exception) || (mRunState == RunState_DebugEval_Done);
|
|
}
|
|
|
|
DbgTypedValue WinDebugger::GetRegister(const StringImpl& regName, DbgLanguage language, CPURegisters* registers, Array<RegForm>* regForms)
|
|
{
|
|
int regNum = -1;
|
|
|
|
String lwrRegName(regName);
|
|
_strlwr((char*)lwrRegName.c_str());
|
|
|
|
// int regs
|
|
|
|
#ifdef BF_DBG_32
|
|
DbgTypeCode regType = DbgType_i32;
|
|
if (lwrRegName == "eax")
|
|
regNum = X86Reg_EAX;
|
|
else if (lwrRegName == "ecx")
|
|
regNum = X86Reg_ECX;
|
|
else if (lwrRegName == "edx")
|
|
regNum = X86Reg_EDX;
|
|
else if (lwrRegName == "ebx")
|
|
regNum = X86Reg_EBX;
|
|
else if (lwrRegName == "esp")
|
|
regNum = X86Reg_ESP;
|
|
else if (lwrRegName == "ebp")
|
|
regNum = X86Reg_EBP;
|
|
else if (lwrRegName == "esi")
|
|
regNum = X86Reg_ESI;
|
|
else if (lwrRegName == "edi")
|
|
regNum = X86Reg_EDI;
|
|
else if (lwrRegName == "eip")
|
|
regNum = X86Reg_EIP;
|
|
else if (lwrRegName == "efl")
|
|
regNum = X86Reg_EFL;
|
|
#else
|
|
DbgTypeCode regType = DbgType_i64;
|
|
|
|
if (lwrRegName == "rax")
|
|
regNum = X64Reg_RAX;
|
|
else if (lwrRegName == "rcx")
|
|
regNum = X64Reg_RCX;
|
|
else if (lwrRegName == "rdx")
|
|
regNum = X64Reg_RDX;
|
|
else if (lwrRegName == "rbx")
|
|
regNum = X64Reg_RBX;
|
|
else if (lwrRegName == "rsp")
|
|
regNum = X64Reg_RSP;
|
|
else if (lwrRegName == "rbp")
|
|
regNum = X64Reg_RBP;
|
|
else if (lwrRegName == "rsi")
|
|
regNum = X64Reg_RSI;
|
|
else if (lwrRegName == "rdi")
|
|
regNum = X64Reg_RDI;
|
|
else if (lwrRegName == "rip")
|
|
regNum = X64Reg_RIP;
|
|
else if (lwrRegName == "r8")
|
|
regNum = X64Reg_R8;
|
|
else if (lwrRegName == "r9")
|
|
regNum = X64Reg_R9;
|
|
else if (lwrRegName == "r10")
|
|
regNum = X64Reg_R10;
|
|
else if (lwrRegName == "r11")
|
|
regNum = X64Reg_R11;
|
|
else if (lwrRegName == "r12")
|
|
regNum = X64Reg_R12;
|
|
else if (lwrRegName == "r13")
|
|
regNum = X64Reg_R13;
|
|
else if (lwrRegName == "r14")
|
|
regNum = X64Reg_R14;
|
|
else if (lwrRegName == "r15")
|
|
regNum = X64Reg_R15;
|
|
else
|
|
{
|
|
regType = DbgType_i32;
|
|
if (lwrRegName == "eax")
|
|
regNum = X64Reg_RAX;
|
|
else if (lwrRegName == "ecx")
|
|
regNum = X64Reg_RCX;
|
|
else if (lwrRegName == "edx")
|
|
regNum = X64Reg_RDX;
|
|
else if (lwrRegName == "ebx")
|
|
regNum = X64Reg_RBX;
|
|
else if (lwrRegName == "efl")
|
|
regNum = X64Reg_EFL;
|
|
else if (lwrRegName == "esi")
|
|
regNum = X64Reg_RSI;
|
|
else if (lwrRegName == "edi")
|
|
regNum = X64Reg_RDI;
|
|
else if (lwrRegName == "r8d")
|
|
regNum = X64Reg_R8;
|
|
else if (lwrRegName == "r9d")
|
|
regNum = X64Reg_R9;
|
|
else if (lwrRegName == "r10d")
|
|
regNum = X64Reg_R10;
|
|
else if (lwrRegName == "r11d")
|
|
regNum = X64Reg_R11;
|
|
else if (lwrRegName == "r12d")
|
|
regNum = X64Reg_R12;
|
|
else if (lwrRegName == "r13d")
|
|
regNum = X64Reg_R13;
|
|
else if (lwrRegName == "r14d")
|
|
regNum = X64Reg_R14;
|
|
else if (lwrRegName == "r15d")
|
|
regNum = X64Reg_R15;
|
|
else
|
|
{
|
|
regType = DbgType_i16;
|
|
if (lwrRegName == "ax")
|
|
regNum = X64Reg_RAX;
|
|
else if (lwrRegName == "cx")
|
|
regNum = X64Reg_RCX;
|
|
else if (lwrRegName == "dx")
|
|
regNum = X64Reg_RDX;
|
|
else if (lwrRegName == "bx")
|
|
regNum = X64Reg_RBX;
|
|
else if (lwrRegName == "si")
|
|
regNum = X64Reg_RSI;
|
|
else if (lwrRegName == "di")
|
|
regNum = X64Reg_RDI;
|
|
else if (lwrRegName == "r8w")
|
|
regNum = X64Reg_R8;
|
|
else if (lwrRegName == "r9w")
|
|
regNum = X64Reg_R9;
|
|
else if (lwrRegName == "r10w")
|
|
regNum = X64Reg_R10;
|
|
else if (lwrRegName == "r11w")
|
|
regNum = X64Reg_R11;
|
|
else if (lwrRegName == "r12w")
|
|
regNum = X64Reg_R12;
|
|
else if (lwrRegName == "r13w")
|
|
regNum = X64Reg_R13;
|
|
else if (lwrRegName == "r14w")
|
|
regNum = X64Reg_R14;
|
|
else if (lwrRegName == "r15w")
|
|
regNum = X64Reg_R15;
|
|
else
|
|
{
|
|
regType = DbgType_i8;
|
|
if (lwrRegName == "al")
|
|
regNum = X64Reg_RAX;
|
|
else if (lwrRegName == "cl")
|
|
regNum = X64Reg_RCX;
|
|
else if (lwrRegName == "dl")
|
|
regNum = X64Reg_RDX;
|
|
else if (lwrRegName == "bl")
|
|
regNum = X64Reg_RBX;
|
|
else if (lwrRegName == "sil")
|
|
regNum = X64Reg_RSI;
|
|
else if (lwrRegName == "dil")
|
|
regNum = X64Reg_RDI;
|
|
else if (lwrRegName == "r8b")
|
|
regNum = X64Reg_R8;
|
|
else if (lwrRegName == "r9b")
|
|
regNum = X64Reg_R9;
|
|
else if (lwrRegName == "r10b")
|
|
regNum = X64Reg_R10;
|
|
else if (lwrRegName == "r11b")
|
|
regNum = X64Reg_R11;
|
|
else if (lwrRegName == "r12b")
|
|
regNum = X64Reg_R12;
|
|
else if (lwrRegName == "r13b")
|
|
regNum = X64Reg_R13;
|
|
else if (lwrRegName == "r14b")
|
|
regNum = X64Reg_R14;
|
|
else if (lwrRegName == "r15b")
|
|
regNum = X64Reg_R15;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
auto dbgModule = mEmptyDebugTarget->GetMainDbgModule();
|
|
|
|
if (regNum != -1)
|
|
{
|
|
DbgTypedValue typedVal;
|
|
typedVal.mType = dbgModule->GetPrimitiveType(regType, language);
|
|
typedVal.mInt64 = registers->mIntRegsArray[regNum];
|
|
typedVal.mRegNum = regNum;
|
|
return typedVal;
|
|
}
|
|
|
|
// st regs
|
|
|
|
if ((lwrRegName.length() == 3) && (lwrRegName[0] == 's') && (lwrRegName[1] == 't') && (lwrRegName[2] >= '0') && (lwrRegName[2] <= '7'))
|
|
{
|
|
regNum = CPUReg_FPSTREG_FIRST + (lwrRegName[2] - '0');
|
|
}
|
|
if (regNum != -1)
|
|
{
|
|
DbgTypedValue typedVal;
|
|
typedVal.mType = dbgModule->GetPrimitiveType(DbgType_Double, language);
|
|
typedVal.mDouble = ConvertFloat80ToDouble(registers->mFpMmRegsArray[regNum - CPUReg_FPSTREG_FIRST].fp.fp80);
|
|
typedVal.mRegNum = regNum;
|
|
return typedVal;
|
|
}
|
|
|
|
// mm regs
|
|
|
|
if ((lwrRegName.length() == 3) && (lwrRegName[0] == 'm') && (lwrRegName[1] == 'm') && (lwrRegName[2] >= '0') && (lwrRegName[2] <= '7'))
|
|
{
|
|
regNum = CPUReg_MMREG_FIRST + (lwrRegName[2] - '0');
|
|
}
|
|
if (regNum != -1)
|
|
{
|
|
DbgTypedValue typedVal;
|
|
typedVal.mType = dbgModule->GetPrimitiveType(DbgType_i64, language);
|
|
typedVal.mInt64 = registers->mFpMmRegsArray[regNum - CPUReg_MMREG_FIRST].mm;
|
|
typedVal.mRegNum = regNum;
|
|
return typedVal;
|
|
}
|
|
|
|
// xmm regs
|
|
|
|
#ifdef BF_DBG_32
|
|
if ((lwrRegName.length() == 6) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] >= '0') && (lwrRegName[3] <= '7') &&
|
|
(lwrRegName[4] == '_') && (lwrRegName[5] >= '0') && (lwrRegName[5] <= '3'))
|
|
{
|
|
regNum = CPUReg_XMMREG_FIRST + ((lwrRegName[3] - '0') * 4) + (lwrRegName[5] - '0');
|
|
}
|
|
#else
|
|
if ((lwrRegName.length() == 6) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] >= '0') && (lwrRegName[3] <= '9') &&
|
|
(lwrRegName[4] == '_') && (lwrRegName[5] >= '0') && (lwrRegName[5] <= '3'))
|
|
{
|
|
regNum = CPUReg_XMMREG_FIRST + ((lwrRegName[3] - '0') * 4) + (lwrRegName[5] - '0');
|
|
}
|
|
if ((lwrRegName.length() == 7) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] == '1') && (lwrRegName[4] >= '0') && (lwrRegName[4] <= '9') &&
|
|
(lwrRegName[5] == '_') && (lwrRegName[6] >= '0') && (lwrRegName[6] <= '3'))
|
|
{
|
|
regNum = CPUReg_XMMREG_FIRST + ((10 + (lwrRegName[4] - '0')) * 4) + (lwrRegName[6] - '0');
|
|
}
|
|
#endif
|
|
|
|
if (regNum != -1)
|
|
{
|
|
int xmmMajor = (regNum - CPUReg_XMMREG_FIRST) >> 2;
|
|
int xmmMinor = (regNum - CPUReg_XMMREG_FIRST) & 3;
|
|
|
|
DwMmDisplayType mmDisplayType = GetDisplayInfo(StrFormat("$XMM%d", xmmMajor))->mMmDisplayType;
|
|
|
|
RegForm regForm = RegForm_Unknown;
|
|
if (regForms != NULL)
|
|
{
|
|
int regFormIdx = CPUReg_M128_XMMREG_FIRST + xmmMajor;
|
|
if (regFormIdx < (int)regForms->size())
|
|
regForm = (*regForms)[regFormIdx];
|
|
}
|
|
if (mmDisplayType == DwMmDisplayType_Default)
|
|
{
|
|
if ((regForm == RegForm_Double) || (regForm == RegForm_Double2))
|
|
mmDisplayType = DwMmDisplayType_Double;
|
|
else if (regForm == RegForm_Int4)
|
|
mmDisplayType = DwMmDisplayType_Int32;
|
|
}
|
|
|
|
//TODO: Add int types
|
|
if (mmDisplayType == DwMmDisplayType_Double)
|
|
{
|
|
DbgTypedValue typedVal;
|
|
typedVal.mType = dbgModule->GetPrimitiveType(DbgType_Double, language);
|
|
typedVal.mDouble = registers->mXmmDRegsArray[xmmMajor].d[xmmMinor];
|
|
typedVal.mRegNum = regNum;
|
|
return typedVal;
|
|
}
|
|
else if ((mmDisplayType == DwMmDisplayType_UInt8) || (mmDisplayType == DwMmDisplayType_Int16) || (mmDisplayType == DwMmDisplayType_Int32))
|
|
{
|
|
DbgTypedValue typedVal;
|
|
typedVal.mType = dbgModule->GetPrimitiveType(DbgType_i32, language);
|
|
typedVal.mInt32 = registers->mXmmI32RegsARray[xmmMajor].i[xmmMinor];
|
|
typedVal.mRegNum = regNum;
|
|
return typedVal;
|
|
}
|
|
else if (mmDisplayType == DwMmDisplayType_Int64)
|
|
{
|
|
DbgTypedValue typedVal;
|
|
typedVal.mType = dbgModule->GetPrimitiveType(DbgType_i64, language);
|
|
typedVal.mInt64 = registers->mXmmI64RegsARray[xmmMajor].i[xmmMinor];
|
|
typedVal.mRegNum = regNum;
|
|
return typedVal;
|
|
}
|
|
|
|
DbgTypedValue typedVal;
|
|
typedVal.mType = dbgModule->GetPrimitiveType(DbgType_Single, language);
|
|
typedVal.mSingle = registers->mXmmRegsArray[xmmMajor].f[xmmMinor];
|
|
typedVal.mRegNum = regNum;
|
|
return typedVal;
|
|
}
|
|
#ifdef BF_DBG_32
|
|
if ((lwrRegName.length() == 4) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] >= '0') && (lwrRegName[3] <= '7'))
|
|
{
|
|
regNum = CPUReg_M128_XMMREG_FIRST + (lwrRegName[3] - '0');
|
|
}
|
|
#else
|
|
if ((lwrRegName.length() == 4) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] >= '0') && (lwrRegName[3] <= '9'))
|
|
{
|
|
regNum = CPUReg_M128_XMMREG_FIRST + (lwrRegName[3] - '0');
|
|
}
|
|
if ((lwrRegName.length() == 5) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] == '1') && (lwrRegName[4] >= '0') && (lwrRegName[4] <= '5'))
|
|
{
|
|
regNum = CPUReg_M128_XMMREG_FIRST + 10 + (lwrRegName[4] - '0');
|
|
}
|
|
#endif
|
|
if (regNum != -1)
|
|
{
|
|
DbgTypedValue typedVal;
|
|
typedVal.mType = dbgModule->GetPrimitiveType(DbgType_RegGroup, language);
|
|
typedVal.mSingle = 0.0f; // ignored at a higher level (but if it's used as an rvalue in the meantime, it'll resolve to zero)
|
|
typedVal.mRegNum = regNum;
|
|
return typedVal;
|
|
}
|
|
|
|
// flags
|
|
|
|
if ((lwrRegName.length() == 6) && (lwrRegName[0] == 'f') && (lwrRegName[1] == 'l') && (lwrRegName[2] == 'a') && (lwrRegName[3] == 'g') && (lwrRegName[5] == 'f'))
|
|
{
|
|
switch(lwrRegName[4])
|
|
{
|
|
case 'c': regNum = CPUReg_FLAG_CF_CARRY; break;
|
|
case 'p': regNum = CPUReg_FLAG_PF_PARITY; break;
|
|
case 'a': regNum = CPUReg_FLAG_AF_ADJUST; break;
|
|
case 'z': regNum = CPUReg_FLAG_ZF_ZERO; break;
|
|
case 's': regNum = CPUReg_FLAG_SF_SIGN; break;
|
|
case 'i': regNum = CPUReg_FLAG_IF_INTERRUPT; break;
|
|
case 'd': regNum = CPUReg_FLAG_DF_DIRECTION; break;
|
|
case 'o': regNum = CPUReg_FLAG_OF_OVERFLOW; break;
|
|
default: break;
|
|
}
|
|
}
|
|
if (regNum != -1)
|
|
{
|
|
int flagBit = CPURegisters::GetFlagBitForRegister(regNum);
|
|
BF_ASSERT(flagBit >= 0);
|
|
|
|
DbgTypedValue typedVal;
|
|
typedVal.mType = dbgModule->GetPrimitiveType(DbgType_Bool, language);
|
|
typedVal.mBool = (registers->mIntRegs.efl & ((uint64)1 << flagBit)) != 0;
|
|
typedVal.mRegNum = regNum;
|
|
return typedVal;
|
|
}
|
|
|
|
// categories
|
|
|
|
if (lwrRegName == "allregs")
|
|
regNum = CPUReg_CAT_ALLREGS;
|
|
else if (lwrRegName == "iregs")
|
|
regNum = CPUReg_CAT_IREGS;
|
|
else if (lwrRegName == "fpregs")
|
|
regNum = CPUReg_CAT_FPREGS;
|
|
else if (lwrRegName == "mmregs")
|
|
regNum = CPUReg_CAT_MMREGS;
|
|
else if (lwrRegName == "xmmregs")
|
|
regNum = CPUReg_CAT_XMMREGS;
|
|
else if (lwrRegName == "flags")
|
|
regNum = CPUReg_CAT_FLAGS;
|
|
if (regNum != -1)
|
|
{
|
|
DbgTypedValue typedVal;
|
|
typedVal.mType = dbgModule->GetPrimitiveType(DbgType_RegGroup, language);
|
|
typedVal.mSingle = 0.0f; // ignored at a higher level (but if it's used as an rvalue in the meantime, it'll resolve to zero)
|
|
typedVal.mRegNum = regNum;
|
|
return typedVal;
|
|
}
|
|
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
DbgModule* WinDebugger::GetCallStackDbgModule(int callStackIdx)
|
|
{
|
|
if ((mRunState == RunState_NotStarted) || (!IsPaused()))
|
|
return mEmptyDebugTarget->GetMainDbgModule();
|
|
if (callStackIdx == -1)
|
|
return mDebugTarget->GetMainDbgModule();
|
|
FixCallStackIdx(callStackIdx);
|
|
if (callStackIdx >= mCallStack.size())
|
|
return mDebugTarget->GetMainDbgModule();
|
|
UpdateCallStackMethod(callStackIdx);
|
|
auto subProgram = mCallStack[callStackIdx]->mSubProgram;
|
|
if (subProgram != NULL)
|
|
return subProgram->mCompileUnit->mDbgModule;
|
|
|
|
auto dbgModule = mDebugTarget->FindDbgModuleForAddress(mCallStack[callStackIdx]->mRegisters.GetPC());
|
|
if (dbgModule != NULL)
|
|
return dbgModule;
|
|
return mDebugTarget->GetMainDbgModule();
|
|
}
|
|
|
|
DbgSubprogram* WinDebugger::GetCallStackSubprogram(int callStackIdx)
|
|
{
|
|
if ((IsInRunState()) || (mRunState == RunState_NotStarted) || (callStackIdx == -1))
|
|
return NULL;
|
|
if (callStackIdx >= (int)mCallStack.size())
|
|
UpdateCallStack();
|
|
if (mCallStack.IsEmpty())
|
|
return NULL;
|
|
if (callStackIdx >= (int)mCallStack.size())
|
|
callStackIdx = 0;
|
|
UpdateCallStackMethod(callStackIdx);
|
|
auto subProgram = mCallStack[callStackIdx]->mSubProgram;
|
|
return subProgram;
|
|
}
|
|
|
|
DbgCompileUnit* WinDebugger::GetCallStackCompileUnit(int callStackIdx)
|
|
{
|
|
if ((IsInRunState()) || (mRunState == RunState_NotStarted) || (callStackIdx == -1))
|
|
return NULL;
|
|
if (callStackIdx >= (int)mCallStack.size())
|
|
UpdateCallStack();
|
|
if (mCallStack.IsEmpty())
|
|
return NULL;
|
|
if (callStackIdx >= (int)mCallStack.size())
|
|
callStackIdx = 0;
|
|
UpdateCallStackMethod(callStackIdx);
|
|
auto subProgram = mCallStack[callStackIdx]->mSubProgram;
|
|
if (subProgram == NULL)
|
|
return NULL;
|
|
return subProgram->mCompileUnit;
|
|
}
|
|
|
|
String WinDebugger::EvaluateContinue(DbgPendingExpr* pendingExpr, BfPassInstance& bfPassInstance)
|
|
{
|
|
DbgModule* dbgModule = NULL;
|
|
DbgCompileUnit* dbgCompileUnit = NULL;
|
|
|
|
if (pendingExpr->mThreadId == -1)
|
|
{
|
|
if ((pendingExpr->mFormatInfo.mLanguage == DbgLanguage_Beef) && (mDebugTarget != NULL) && (mDebugTarget->mTargetBinary != NULL))
|
|
dbgModule = mDebugTarget->mTargetBinary;
|
|
else
|
|
dbgModule = mEmptyDebugTarget->GetMainDbgModule();
|
|
}
|
|
else
|
|
{
|
|
dbgModule = GetCallStackDbgModule(pendingExpr->mCallStackIdx);
|
|
if ((dbgModule != NULL) &&(!dbgModule->mDebugTarget->mIsEmpty))
|
|
dbgCompileUnit = GetCallStackCompileUnit(pendingExpr->mCallStackIdx);
|
|
}
|
|
|
|
if (dbgModule == NULL)
|
|
dbgModule = mEmptyDebugTarget->GetMainDbgModule();
|
|
|
|
if (!pendingExpr->mException.empty())
|
|
{
|
|
RestoreAllRegisters();
|
|
return "!" + pendingExpr->mException;
|
|
}
|
|
|
|
DwAutoComplete autoComplete;
|
|
|
|
if (bfPassInstance.HasFailed())
|
|
{
|
|
// Don't allow pending calls if we've already failed in the calling Evaluate()
|
|
pendingExpr->mExpressionFlags = (DwEvalExpressionFlags)(pendingExpr->mExpressionFlags & ~DwEvalExpressionFlag_AllowCalls);
|
|
}
|
|
|
|
DbgExprEvaluator dbgExprEvaluator(this, dbgModule, &bfPassInstance, pendingExpr->mCallStackIdx, pendingExpr->mCursorPos);
|
|
if (!pendingExpr->mFormatInfo.mStackSearchStr.IsEmpty())
|
|
{
|
|
dbgExprEvaluator.mStackSearch = new DbgStackSearch();
|
|
dbgExprEvaluator.mStackSearch->mSearchStr = pendingExpr->mFormatInfo.mStackSearchStr;
|
|
}
|
|
dbgExprEvaluator.mLanguage = pendingExpr->mFormatInfo.mLanguage;
|
|
dbgExprEvaluator.mReferenceId = &pendingExpr->mReferenceId;
|
|
dbgExprEvaluator.mExpressionFlags = pendingExpr->mExpressionFlags;
|
|
dbgExprEvaluator.mExplicitThis = pendingExpr->mFormatInfo.mExplicitThis;
|
|
dbgExprEvaluator.mSubjectExpr = pendingExpr->mFormatInfo.mSubjectExpr;
|
|
dbgExprEvaluator.mNamespaceSearchStr = pendingExpr->mFormatInfo.mNamespaceSearch;
|
|
dbgExprEvaluator.mExpectingTypeName = pendingExpr->mFormatInfo.mExpectedType;
|
|
dbgExprEvaluator.mCallResults = &pendingExpr->mCallResults;
|
|
if ((pendingExpr->mExpressionFlags & DwEvalExpressionFlag_ValidateOnly) != 0)
|
|
{
|
|
dbgExprEvaluator.mValidateOnly = true;
|
|
}
|
|
|
|
if (pendingExpr->mCursorPos != -1)
|
|
{
|
|
dbgExprEvaluator.mAutoComplete = &autoComplete;
|
|
}
|
|
dbgExprEvaluator.mDbgCompileUnit = dbgCompileUnit;
|
|
|
|
DbgTypedValue exprResult;
|
|
if (pendingExpr->mExplitType != NULL)
|
|
{
|
|
exprResult.mHasNoValue = true;
|
|
exprResult.mType = pendingExpr->mExplitType;
|
|
}
|
|
else if (pendingExpr->mExprNode != NULL)
|
|
{
|
|
exprResult = dbgExprEvaluator.Resolve(pendingExpr->mExprNode);
|
|
}
|
|
|
|
if (dbgExprEvaluator.mCreatedPendingCall)
|
|
{
|
|
BF_ASSERT(mRunState == RunState_DebugEval);
|
|
//ContinueDebugEvent();
|
|
return "!pending";
|
|
}
|
|
|
|
if (dbgExprEvaluator.mCountResultOverride != -1)
|
|
pendingExpr->mFormatInfo.mOverrideCount = dbgExprEvaluator.mCountResultOverride;
|
|
|
|
String val;
|
|
if (bfPassInstance.HasFailed())
|
|
{
|
|
BfLogDbgExpr("Evaluate Failed: %s\n", bfPassInstance.mErrors[0]->mError.c_str());
|
|
val = StrFormat("!%d\t%d\t%s", bfPassInstance.mErrors[0]->GetSrcStart(), bfPassInstance.mErrors[0]->GetSrcLength(), bfPassInstance.mErrors[0]->mError.c_str());
|
|
}
|
|
else if (dbgExprEvaluator.mBlockedSideEffects)
|
|
{
|
|
BfLogDbgExpr("Evaluate blocked side effects\n");
|
|
val = "!sideeffects";
|
|
}
|
|
else if (!exprResult)
|
|
{
|
|
if (exprResult.mType != NULL)
|
|
{
|
|
BfLogDbgExpr("Evaluate success\n");
|
|
|
|
String typeName = exprResult.mType->ToString();
|
|
DbgType* rawType = exprResult.mType;
|
|
if (rawType->IsBfObjectPtr())
|
|
rawType = rawType->mTypeParam;
|
|
String typeNameRaw = rawType->ToStringRaw();
|
|
val = typeName + "\n" + typeName;
|
|
val += "\n" + GetMemberList(exprResult.mType, typeNameRaw, false, true, false, false, exprResult.mIsReadOnly);
|
|
if (exprResult.mType->mTypeCode == DbgType_Namespace)
|
|
{
|
|
val += "\n:type\tnamespace";
|
|
}
|
|
else
|
|
{
|
|
auto type = exprResult.mType;
|
|
if (type->IsPointer())
|
|
type = type->mTypeParam;
|
|
if (type->IsBfObject())
|
|
val += "\n:type\tclass";
|
|
else
|
|
val += "\n:type\tvaluetype";
|
|
}
|
|
|
|
if (!pendingExpr->mReferenceId.empty())
|
|
val += "\n:referenceId\t" + pendingExpr->mReferenceId;
|
|
}
|
|
else
|
|
val = "!";
|
|
}
|
|
else if ((pendingExpr->mExpressionFlags & (DwEvalExpressionFlag_MemoryAddress)) != 0)
|
|
{
|
|
DbgType* resultType = exprResult.mType->RemoveModifiers();
|
|
if ((resultType->IsInteger()) || (resultType->IsPointerOrRef()))
|
|
{
|
|
val = EncodeDataPtr(exprResult.mPtr, false) + "\n" + StrFormat("%d", 0);
|
|
}
|
|
else
|
|
{
|
|
if (exprResult.mSrcAddress != 0)
|
|
val = StrFormat("!Type '%s' is invalid. A pointer or address value is expected. Try using the '&' address-of operator.", exprResult.mType->ToString().c_str());
|
|
else
|
|
val = StrFormat("!Type '%s' is invalid. A pointer or address value is expected. An explicit cast may be required.", exprResult.mType->ToString().c_str());
|
|
}
|
|
}
|
|
else if ((pendingExpr->mExpressionFlags & (DwEvalExpressionFlag_MemoryWatch)) != 0)
|
|
{
|
|
DbgType* resultType = exprResult.mType->RemoveModifiers();
|
|
|
|
bool isMemoryWatch = (pendingExpr->mExpressionFlags & DwEvalExpressionFlag_MemoryWatch) != 0;
|
|
|
|
if (!resultType->IsPointerOrRef())
|
|
{
|
|
if (exprResult.mSrcAddress != 0)
|
|
val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected. Try using the '&' address-of operator.", exprResult.mType->ToString().c_str());
|
|
else
|
|
val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected. An explicit cast may be required.", exprResult.mType->ToString().c_str());
|
|
}
|
|
else
|
|
{
|
|
auto innerType = resultType->mTypeParam;
|
|
int byteCount = innerType->GetByteCount();
|
|
|
|
if (pendingExpr->mFormatInfo.mArrayLength != -1)
|
|
byteCount *= pendingExpr->mFormatInfo.mArrayLength;
|
|
|
|
if (byteCount == 0)
|
|
{
|
|
val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected, try casting to a non-void pointer type.", exprResult.mType->ToString().c_str());
|
|
}
|
|
#ifdef BF_DBG_32
|
|
else if ((isMemoryWatch) && (!IsMemoryBreakpointSizeValid(exprResult.mPtr, byteCount)))
|
|
{
|
|
if (innerType->mSize > 16)
|
|
val = StrFormat("!Element size is %d bytes. A maximum of 16 bytes can be watched. Try casting to an appropriately-sized pointer or watching an individual member.", byteCount);
|
|
else if (!IsMemoryBreakpointSizeValid(0, byteCount))
|
|
val = StrFormat("!Element size is %d bytes, which is not a supported size for a memory watch. Try casting to an appropriately-sized pointer.", byteCount);
|
|
else
|
|
val = StrFormat("!Element size is %d bytes, which is not a supported size for a memory watch at non-aligned address %@. Try casting to an appropriately-sized pointer.", byteCount, exprResult.mPtr);
|
|
}
|
|
#else
|
|
else if ((isMemoryWatch) && (!IsMemoryBreakpointSizeValid(exprResult.mPtr, byteCount)))
|
|
{
|
|
if (innerType->mSize > 32)
|
|
val = StrFormat("!Element size is %d bytes. A maximum of 32 bytes can be watched. Try casting to an appropriately-sized pointer or watching an individual member.", byteCount);
|
|
else if (!IsMemoryBreakpointSizeValid(0, byteCount))
|
|
val = StrFormat("!Element size is %d bytes, which is not a supported size for a memory watch. Try casting to an appropriately-sized pointer.", byteCount);
|
|
else
|
|
val = StrFormat("!Element size is %d bytes, which is not a supported size for a memory watch at non-aligned address %@. Try casting to an appropriately-sized pointer.", byteCount, exprResult.mPtr);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
auto language = dbgExprEvaluator.GetLanguage();
|
|
val = EncodeDataPtr(exprResult.mPtr, false) + "\n" + StrFormat("%d", byteCount) + "\n" + StrFormat("%d\t", language) + innerType->ToStringRaw(language);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pendingExpr->mFormatInfo.mNoEdit)
|
|
exprResult.mIsReadOnly = true;
|
|
if (!pendingExpr->mReferenceId.empty())
|
|
pendingExpr->mFormatInfo.mReferenceId = pendingExpr->mReferenceId;
|
|
val = DbgTypedValueToString(exprResult, pendingExpr->mExprNode->ToString(), pendingExpr->mFormatInfo, &dbgExprEvaluator, (pendingExpr->mExpressionFlags & DwEvalExpressionFlag_FullPrecision) != 0);
|
|
|
|
if ((!val.empty()) && (val[0] == '!'))
|
|
return val;
|
|
|
|
if (pendingExpr->mFormatInfo.mRawString)
|
|
return val;
|
|
|
|
if (exprResult.mIsLiteral)
|
|
val += "\n:literal";
|
|
|
|
if (bfPassInstance.HasMessages())
|
|
{
|
|
for (auto error : bfPassInstance.mErrors)
|
|
{
|
|
if (error->mIsWarning)
|
|
{
|
|
val += "\n:warn\t";
|
|
val += error->mError;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pendingExpr->mFormatInfo.mReferenceId.empty())
|
|
val += "\n:referenceId\t" + pendingExpr->mFormatInfo.mReferenceId;
|
|
|
|
auto breakAddress = exprResult.mSrcAddress;
|
|
int breakSize = exprResult.mType->GetByteCount();
|
|
if (exprResult.mType->IsRef())
|
|
breakSize = exprResult.mType->mTypeParam->GetByteCount();
|
|
if ((breakAddress != 0) && (HasMemoryBreakpoint(breakAddress, breakSize)))
|
|
val += StrFormat("\n:break\t%@", breakAddress);
|
|
|
|
auto checkType = exprResult.mType->RemoveModifiers();
|
|
if (checkType->IsBfObjectPtr())
|
|
val += "\n:type\tobject";
|
|
else if ((checkType->IsPointer()) || (checkType->mTypeCode == DbgType_Subroutine))
|
|
val += "\n:type\tpointer";
|
|
else if (checkType->IsInteger())
|
|
val += "\n:type\tint";
|
|
else if (checkType->IsFloat())
|
|
val += "\n:type\tfloat";
|
|
else if ((exprResult.mRegNum >= X64Reg_M128_XMM0) && (exprResult.mRegNum <= X64Reg_M128_XMM15))
|
|
val += "\n:type\tmm128";
|
|
else
|
|
val += "\n:type\tvaluetype";
|
|
|
|
if ((pendingExpr->mFormatInfo.mTypeKindFlags & DbgTypeKindFlag_Int) != 0)
|
|
val += "\n:type\tint";
|
|
|
|
if (dbgExprEvaluator.mHadSideEffects)
|
|
val += "\n:sideeffects";
|
|
if ((dbgExprEvaluator.mStackSearch != NULL) && (dbgExprEvaluator.mStackSearch->mStartingStackIdx != dbgExprEvaluator.mCallStackIdx))
|
|
val += StrFormat("\n:stackIdx\t%d", dbgExprEvaluator.mCallStackIdx);
|
|
|
|
auto underlyingType = exprResult.mType->RemoveModifiers();
|
|
bool canEdit = true;
|
|
if (pendingExpr->mFormatInfo.mLanguage == DbgLanguage_Beef)
|
|
{
|
|
if (exprResult.mType->IsConst())
|
|
canEdit = false;
|
|
}
|
|
if (pendingExpr->mFormatInfo.mNoEdit)
|
|
canEdit = false;
|
|
if (exprResult.mIsReadOnly)
|
|
canEdit = false;
|
|
|
|
const char* langStr = (pendingExpr->mFormatInfo.mLanguage == DbgLanguage_Beef) ? "@Beef:" : "@C:";
|
|
|
|
if (exprResult.mSrcAddress != 0)
|
|
{
|
|
val += StrFormat("\n:addrValueExpr\t%s(%s*)", langStr, exprResult.mType->ToString(pendingExpr->mFormatInfo.mLanguage).c_str());
|
|
val += EncodeDataPtr(exprResult.mSrcAddress, true);
|
|
}
|
|
|
|
if (exprResult.mType->IsPointerOrRef())
|
|
{
|
|
auto underlyingType = exprResult.mType->mTypeParam;
|
|
if (underlyingType != NULL)
|
|
{
|
|
val += StrFormat("\n:pointeeExpr\t%s(%s%s)", langStr, underlyingType->ToString(pendingExpr->mFormatInfo.mLanguage).c_str(),
|
|
underlyingType->IsBfObject() ? "" : "*");
|
|
val += EncodeDataPtr(exprResult.mPtr, true);
|
|
}
|
|
|
|
val += "\n:pointer\t" + EncodeDataPtr(exprResult.mPtr, true);
|
|
}
|
|
|
|
if (val[0] == '!')
|
|
{
|
|
// Already has an error embedded, can't edit
|
|
}
|
|
else if ((exprResult.mSrcAddress != 0) && (underlyingType->mTypeCode >= DbgType_i8) && (underlyingType->mTypeCode <= DbgType_Ptr) &&
|
|
(underlyingType->mTypeCode != DbgType_Class) && (underlyingType->mTypeCode != DbgType_Struct))
|
|
{
|
|
if (canEdit)
|
|
val += "\n:canEdit";
|
|
if (exprResult.mType->mTypeCode == DbgType_Ptr)
|
|
{
|
|
val += "\n:editVal\t" + EncodeDataPtr(exprResult.mPtr, true);
|
|
}
|
|
}
|
|
else if ((underlyingType->IsStruct()) && (exprResult.mSrcAddress != 0) && (underlyingType->IsTypedPrimitive()))
|
|
{
|
|
auto primType = underlyingType->GetRootBaseType();
|
|
DbgTypedValue primVal = dbgExprEvaluator.ReadTypedValue(NULL, primType, exprResult.mSrcAddress, DbgAddrType_Target);
|
|
|
|
String primResult = DbgTypedValueToString(primVal, "", pendingExpr->mFormatInfo, NULL);
|
|
int crPos = (int)primResult.IndexOf('\n');
|
|
if (crPos != -1)
|
|
primResult.RemoveToEnd(crPos);
|
|
|
|
if (canEdit)
|
|
val += "\n:canEdit";
|
|
val += "\n:editVal\t" + primResult;
|
|
}
|
|
else if (exprResult.mRegNum >= 0)
|
|
{
|
|
bool isPseudoReg = ( ((exprResult.mRegNum >= X86Reg_M128_XMMREG_FIRST) && (exprResult.mRegNum <= X86Reg_M128_XMMREG_LAST))
|
|
|| ((exprResult.mRegNum >= X86Reg_CAT_FIRST) && (exprResult.mRegNum <= X86Reg_CAT_LAST)) );
|
|
if (!isPseudoReg)
|
|
{
|
|
if (canEdit)
|
|
val += "\n:canEdit";
|
|
if (exprResult.mType->mTypeCode == DbgType_Ptr)
|
|
{
|
|
val += "\n:editVal\t" + EncodeDataPtr(exprResult.mPtr, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pendingExpr->mFormatInfo.mRawString)
|
|
return "";
|
|
|
|
if (val[0] != '!')
|
|
{
|
|
if (pendingExpr->mUsedSpecifiedLock)
|
|
val += "\n:usedLock";
|
|
|
|
if (pendingExpr->mStackIdxOverride != -1)
|
|
val += StrFormat("\n:stackIdx\t%d", pendingExpr->mStackIdxOverride);
|
|
}
|
|
|
|
if (pendingExpr->mCursorPos != -1)
|
|
val += GetAutocompleteOutput(autoComplete);
|
|
|
|
return val;
|
|
}
|
|
|
|
String WinDebugger::EvaluateContinue()
|
|
{
|
|
BP_ZONE("WinDebugger::EvaluateContinue");
|
|
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (mDebugPendingExpr == NULL)
|
|
return "!Evaluation canceled";
|
|
|
|
if (!IsPaused())
|
|
return "!Not paused";
|
|
|
|
if (mRunState == RunState_DebugEval_Done)
|
|
mRunState = RunState_Paused;
|
|
|
|
BfPassInstance bfPassInstance(mBfSystem);
|
|
String result = EvaluateContinue(mDebugPendingExpr, bfPassInstance);
|
|
if (result != "!pending")
|
|
{
|
|
BfLogDbg("EvaluateContinue finishing pending expr in thread %d\n", mDebugEvalThreadInfo.mThreadId);
|
|
CleanupDebugEval();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void WinDebugger::EvaluateContinueKeep()
|
|
{
|
|
if (mDebugPendingExpr != NULL)
|
|
mDebugPendingExpr->mIdleTicks = 0;
|
|
}
|
|
|
|
static void PdbTestFile(WinDebugger* debugger, const StringImpl& path)
|
|
{
|
|
if (!path.EndsWith(".PDB", StringImpl::CompareKind_OrdinalIgnoreCase))
|
|
return;
|
|
|
|
OutputDebugStrF("Testing %s\n", path.c_str());
|
|
COFF coffFile(debugger->mDebugTarget);
|
|
uint8 wantGuid[16] = { 0 };
|
|
if (!coffFile.TryLoadPDB(path, wantGuid, -1))
|
|
return;
|
|
if (!coffFile.mIs64Bit)
|
|
return;
|
|
coffFile.ParseTypeData();
|
|
coffFile.ParseSymbolData();
|
|
coffFile.ParseGlobalsData();
|
|
|
|
for (int i = 0; i < coffFile.mTypes.mSize; i++)
|
|
coffFile.mTypes[i]->PopulateType();
|
|
|
|
for (int i = 0; i < coffFile.mCvModuleInfo.mSize; i++)
|
|
coffFile.ParseCompileUnit(i);
|
|
}
|
|
|
|
static void PdbTest(WinDebugger* debugger, const StringImpl& path)
|
|
{
|
|
for (auto& fileEntry : FileEnumerator(path, FileEnumerator::Flags_Files))
|
|
{
|
|
String filePath = fileEntry.GetFilePath();
|
|
|
|
PdbTestFile(debugger, filePath);
|
|
}
|
|
|
|
for (auto& fileEntry : FileEnumerator(path, FileEnumerator::Flags_Directories))
|
|
{
|
|
String childPath = fileEntry.GetFilePath();
|
|
String dirName;
|
|
dirName = GetFileName(childPath);
|
|
|
|
PdbTest(debugger, childPath);
|
|
}
|
|
}
|
|
|
|
String WinDebugger::Evaluate(const StringImpl& expr, DwFormatInfo formatInfo, int callStackIdx, int cursorPos, int language, DwEvalExpressionFlags expressionFlags)
|
|
{
|
|
BP_ZONE_F("WinDebugger::Evaluate %s", BP_DYN_STR(expr.c_str()));
|
|
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if ((expressionFlags & DwEvalExpressionFlag_Symbol) != 0)
|
|
{
|
|
DwAutoComplete autoComplete;
|
|
|
|
String retVal;
|
|
|
|
retVal += GetAutocompleteOutput(autoComplete);
|
|
return retVal;
|
|
}
|
|
|
|
UpdateCallStackMethod(callStackIdx);
|
|
|
|
BfLogDbgExpr("Evaluate %s in thread %d\n", expr.c_str(), (mActiveThread != NULL) ? mActiveThread->mThreadId : 0);
|
|
|
|
if (language != -1)
|
|
formatInfo.mLanguage = (DbgLanguage)language;
|
|
|
|
auto activeThread = mActiveThread;
|
|
if ((!IsPaused()) && (mRunState != RunState_NotStarted) && (mRunState != RunState_DebugEval))
|
|
{
|
|
activeThread = NULL;
|
|
callStackIdx = -1;
|
|
}
|
|
|
|
if (mDebugPendingExpr != NULL)
|
|
{
|
|
// We already have a pending call
|
|
expressionFlags = (DwEvalExpressionFlags)(expressionFlags & ~DwEvalExpressionFlag_AllowCalls);
|
|
}
|
|
|
|
if ((expressionFlags & DwEvalExpressionFlag_RawStr) != 0)
|
|
{
|
|
formatInfo.mRawString = true;
|
|
}
|
|
|
|
if ((expressionFlags & DwEvalExpressionFlag_AllowStringView) != 0)
|
|
{
|
|
formatInfo.mAllowStringView = true;
|
|
}
|
|
|
|
auto terminatedExpr = expr + ";";
|
|
|
|
auto prevActiveThread = mActiveThread;
|
|
bool restoreActiveThread = false;
|
|
defer(
|
|
{
|
|
if (restoreActiveThread)
|
|
SetActiveThread(prevActiveThread->mThreadId);
|
|
});
|
|
|
|
bool usedSpecifiedLock = false;
|
|
int stackIdxOverride = -1;
|
|
|
|
if (terminatedExpr.StartsWith('{'))
|
|
{
|
|
String locString;
|
|
int closeIdx = terminatedExpr.IndexOf('}');
|
|
if (closeIdx != -1)
|
|
locString = terminatedExpr.Substring(1, closeIdx - 1);
|
|
|
|
for (int i = 0; i <= closeIdx; i++)
|
|
terminatedExpr[i] = ' ';
|
|
|
|
locString.Trim();
|
|
if (locString.StartsWith("Thread:", StringImpl::CompareKind_OrdinalIgnoreCase))
|
|
{
|
|
bool foundLockMatch = true;
|
|
|
|
locString.Remove(0, 7);
|
|
char* endPtr = NULL;
|
|
int64 threadId = (int64)strtoll(locString.c_str(), &endPtr, 10);
|
|
|
|
if (endPtr != NULL)
|
|
{
|
|
locString.Remove(0, endPtr - locString.c_str());
|
|
locString.Trim();
|
|
|
|
if (locString.StartsWith("SP:", StringImpl::CompareKind_OrdinalIgnoreCase))
|
|
{
|
|
locString.Remove(0, 3);
|
|
char* endPtr = NULL;
|
|
uint64 sp = (uint64)strtoll(locString.c_str(), &endPtr, 16);
|
|
|
|
if (endPtr != NULL)
|
|
{
|
|
locString.Remove(0, endPtr - locString.c_str());
|
|
locString.Trim();
|
|
|
|
if (locString.StartsWith("Func:", StringImpl::CompareKind_OrdinalIgnoreCase))
|
|
{
|
|
locString.Remove(0, 5);
|
|
char* endPtr = NULL;
|
|
int64 funcAddr = (int64)strtoll(locString.c_str(), &endPtr, 16);
|
|
|
|
if (endPtr != NULL)
|
|
{
|
|
// Actually do it
|
|
|
|
if ((mActiveThread != NULL) && (mActiveThread->mThreadId != threadId))
|
|
restoreActiveThread = true;
|
|
if ((mActiveThread == NULL) || (mActiveThread->mThreadId != threadId))
|
|
SetActiveThread(threadId);
|
|
if ((mActiveThread != NULL) && (mActiveThread->mThreadId == threadId))
|
|
{
|
|
int foundStackIdx = -1;
|
|
|
|
int checkStackIdx = 0;
|
|
while (true)
|
|
{
|
|
if (checkStackIdx >= mCallStack.mSize)
|
|
UpdateCallStack();
|
|
if (checkStackIdx >= mCallStack.mSize)
|
|
break;
|
|
|
|
auto stackFrame = mCallStack[checkStackIdx];
|
|
if (stackFrame->mRegisters.GetSP() == sp)
|
|
{
|
|
foundStackIdx = checkStackIdx;
|
|
break;
|
|
}
|
|
|
|
if (stackFrame->mRegisters.GetSP() > sp)
|
|
{
|
|
foundStackIdx = checkStackIdx - 1;
|
|
break;
|
|
}
|
|
|
|
checkStackIdx++;
|
|
}
|
|
|
|
if (foundStackIdx != -1)
|
|
{
|
|
UpdateCallStackMethod(foundStackIdx);
|
|
auto stackFrame = mCallStack[foundStackIdx];
|
|
|
|
if ((stackFrame->mSubProgram != NULL) && ((int64)stackFrame->mSubProgram->mBlock.mLowPC == funcAddr))
|
|
{
|
|
if ((callStackIdx != foundStackIdx) || (mActiveThread != prevActiveThread))
|
|
usedSpecifiedLock = true;
|
|
callStackIdx = foundStackIdx;
|
|
foundLockMatch = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundLockMatch)
|
|
return "!Locked stack frame not found";
|
|
|
|
bool doClear = false;
|
|
for (int i = closeIdx; i < terminatedExpr.mLength; i++)
|
|
{
|
|
char c = terminatedExpr[i];
|
|
if (doClear)
|
|
{
|
|
terminatedExpr[i] = ' ';
|
|
if (c == '}')
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (c == '{')
|
|
{
|
|
int endIdx = terminatedExpr.IndexOf('}');
|
|
if (endIdx == -1)
|
|
break;
|
|
terminatedExpr[i] = ' ';
|
|
doClear = true;
|
|
}
|
|
else if (!::isspace((uint8)c))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (!locString.IsEmpty())
|
|
{
|
|
const char* checkPtr = locString.c_str();
|
|
if ((*checkPtr == '^') || (*checkPtr == '@'))
|
|
checkPtr++;
|
|
|
|
char* endPtr = NULL;
|
|
int useCallStackIdx = strtol(checkPtr, &endPtr, 10);
|
|
if (endPtr == locString.c_str() + locString.length())
|
|
{
|
|
if (locString[0] == '@')
|
|
callStackIdx = useCallStackIdx;
|
|
else
|
|
callStackIdx += useCallStackIdx;
|
|
stackIdxOverride = callStackIdx;
|
|
}
|
|
else
|
|
{
|
|
formatInfo.mStackSearchStr = locString;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto dbgModule = GetCallStackDbgModule(callStackIdx);
|
|
auto dbgSubprogram = GetCallStackSubprogram(callStackIdx);
|
|
DbgCompileUnit* dbgCompileUnit = NULL;
|
|
if (dbgSubprogram != NULL)
|
|
dbgCompileUnit = dbgSubprogram->mCompileUnit;
|
|
|
|
if ((expr.length() > 0) && (expr[0] == '!'))
|
|
{
|
|
if (expr.StartsWith("!step "))
|
|
{
|
|
expressionFlags = (DwEvalExpressionFlags)(expressionFlags | DwEvalExpressionFlag_StepIntoCalls);
|
|
for (int i = 0; i < 5; i++)
|
|
terminatedExpr[i] = ' ';
|
|
}
|
|
else
|
|
{
|
|
String cmd = expr;
|
|
int commaPos = (int)cmd.IndexOf(',');
|
|
if (commaPos != -1)
|
|
cmd.RemoveToEnd(commaPos);
|
|
|
|
if (cmd == "!info")
|
|
{
|
|
OutputMessage(StrFormat("Module: %s\n", dbgModule->mDisplayName.c_str()));
|
|
if (dbgSubprogram == NULL)
|
|
{
|
|
//
|
|
}
|
|
else if (dbgSubprogram->mLinkName != NULL)
|
|
{
|
|
OutputMessage(StrFormat("Link Name: %s\n", dbgSubprogram->mLinkName));
|
|
}
|
|
else
|
|
{
|
|
String outSymbol;
|
|
if (mDebugTarget->FindSymbolAt(dbgSubprogram->mBlock.mLowPC, &outSymbol))
|
|
{
|
|
OutputMessage(StrFormat("Link Name: %s\n", outSymbol.c_str()));
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
else if (cmd == "!dbg")
|
|
{
|
|
mDbgBreak = true;
|
|
return "";
|
|
}
|
|
else if (cmd == "!pdbtest")
|
|
{
|
|
PdbTest(this, "c:\\");
|
|
}
|
|
else if (cmd.StartsWith("!pdbtest "))
|
|
PdbTestFile(this, cmd.Substring(9));
|
|
}
|
|
}
|
|
|
|
bool valIsAddr = false;
|
|
|
|
BfParser* parser = new BfParser(mBfSystem);
|
|
parser->mCompatMode = true;
|
|
|
|
BfPassInstance bfPassInstance(mBfSystem);
|
|
|
|
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);
|
|
}
|
|
else // Look for "@:" or "@Beef:" style
|
|
{
|
|
int colonIdx = terminatedExpr.IndexOf(':');
|
|
if (colonIdx > 0)
|
|
{
|
|
bool isValid = true;
|
|
DbgLanguage language = DbgLanguage_Unknown;
|
|
String lang = terminatedExpr.Substring(1, colonIdx - 1);
|
|
lang = ToUpper(lang);
|
|
if ((lang == "") || (lang == "BEEF"))
|
|
{
|
|
language = DbgLanguage_Beef;
|
|
}
|
|
else if (lang == "C")
|
|
{
|
|
language = DbgLanguage_C;
|
|
}
|
|
if (language != DbgLanguage_Unknown)
|
|
{
|
|
for (int i = 0; i < colonIdx + 1; i++)
|
|
terminatedExpr[i] = ' ';
|
|
|
|
DbgLanguage curLanguage = DbgLanguage_Unknown;
|
|
if (dbgSubprogram != NULL)
|
|
curLanguage = dbgSubprogram->GetLanguage();
|
|
if (language != curLanguage)
|
|
{
|
|
dbgModule = mDebugTarget->mTargetBinary;
|
|
dbgSubprogram = NULL;
|
|
formatInfo.mLanguage = language;
|
|
callStackIdx = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
parser->SetSource(terminatedExpr.c_str(), terminatedExpr.length());
|
|
parser->Parse(&bfPassInstance);
|
|
|
|
BfReducer bfReducer;
|
|
bfReducer.mAlloc = parser->mAlloc;
|
|
bfReducer.mSystem = mBfSystem;
|
|
bfReducer.mPassInstance = &bfPassInstance;
|
|
bfReducer.mVisitorPos = BfReducer::BfVisitorPos(parser->mRootNode);
|
|
bfReducer.mVisitorPos.MoveNext();
|
|
bfReducer.mCompatMode = parser->mCompatMode;
|
|
bfReducer.mSource = parser;
|
|
auto exprNode = bfReducer.CreateExpression(parser->mRootNode->mChildArr.GetAs<BfAstNode*>(0));
|
|
parser->Close();
|
|
|
|
formatInfo.mCallStackIdx = callStackIdx;
|
|
if ((formatInfo.mLanguage == DbgLanguage_Unknown) && (dbgSubprogram != NULL))
|
|
formatInfo.mLanguage = dbgSubprogram->GetLanguage();
|
|
|
|
DbgPendingExpr* pendingExpr = new DbgPendingExpr();
|
|
if (activeThread != NULL)
|
|
pendingExpr->mThreadId = activeThread->mThreadId;
|
|
pendingExpr->mParser = parser;
|
|
pendingExpr->mCallStackIdx = callStackIdx;
|
|
pendingExpr->mCursorPos = cursorPos;
|
|
pendingExpr->mExpressionFlags = expressionFlags;
|
|
pendingExpr->mExprNode = exprNode;
|
|
|
|
DbgType* 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(dbgModule, 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->mUsedSpecifiedLock = usedSpecifiedLock;
|
|
pendingExpr->mStackIdxOverride = stackIdxOverride;
|
|
pendingExpr->mExplitType = explicitType;
|
|
pendingExpr->mFormatInfo = formatInfo;
|
|
String result = EvaluateContinue(pendingExpr, bfPassInstance);
|
|
if (result == "!pending")
|
|
{
|
|
BF_ASSERT(mDebugPendingExpr == NULL);
|
|
if (mDebugPendingExpr != NULL)
|
|
{
|
|
return "!retry"; // We already have a pending
|
|
}
|
|
mDebugPendingExpr = pendingExpr;
|
|
mDebugEvalThreadInfo = *mActiveThread;
|
|
mActiveThread->mIsAtBreakpointAddress = 0;
|
|
mActiveThread->mStoppedAtAddress = 0;
|
|
mActiveThread->mBreakpointAddressContinuing = 0;
|
|
}
|
|
else
|
|
delete pendingExpr;
|
|
|
|
return result;
|
|
}
|
|
|
|
String WinDebugger::Evaluate(const StringImpl& expr, int callStackIdx, int cursorPos, int language, DwEvalExpressionFlags expressionFlags)
|
|
{
|
|
DwFormatInfo formatInfo;
|
|
return Evaluate(expr, formatInfo, callStackIdx, cursorPos, language, expressionFlags);
|
|
}
|
|
|
|
static void ConvertDoubleToFloat80(double d, byte fp80[10])
|
|
{
|
|
uint64 di = *reinterpret_cast<uint64*>(&d);
|
|
|
|
uint64 m = di & (((uint64)1 << 52) - 1);
|
|
uint64 e = (di >> 52) & 0x7ff;
|
|
|
|
memset(fp80, 0, 10);
|
|
|
|
// sign bit is directly transferred
|
|
if (di & ((uint64)1 << 63))
|
|
fp80[9] |= 0x80;
|
|
|
|
if (!e && !m)
|
|
return; // zero
|
|
|
|
fp80[7] |= 0x80; // leading integer bit in mantissa (always 1 in normalized numbers)
|
|
|
|
if (e == 0x7ff)
|
|
{
|
|
fp80[9] |= 0x7f;
|
|
fp80[8] = 0xff;
|
|
|
|
if (m == 0)
|
|
return; // inf
|
|
|
|
fp80[7] |= 0x3f; // any nonzero value will be a NaN (SNaN or QNaN)
|
|
if (m & ((uint64)1 << 51))
|
|
fp80[7] |= 0x40; // QNaN
|
|
|
|
return;
|
|
}
|
|
|
|
int useExponent = (int)e - 1023;
|
|
|
|
if (!e)
|
|
{
|
|
// denormal; can renormalize though since fp80 supports lower exponents
|
|
BF_ASSERT(m != 0); // we should have trapped zero above
|
|
while (!(m & ((uint64)1 << 51)))
|
|
{
|
|
m <<= 1;
|
|
--useExponent;
|
|
}
|
|
// finally we have our leading 1 bit; strip that off and we have a normalized number again
|
|
m <<= 1;
|
|
--useExponent;
|
|
|
|
m &= (((uint64)1 << 52) - 1);
|
|
}
|
|
|
|
useExponent += 16383;
|
|
BF_ASSERT((useExponent > 0) && (useExponent < 0x7fff));
|
|
*reinterpret_cast<uint16*>(&fp80[8]) |= (uint16)useExponent;
|
|
*reinterpret_cast<uint64*>(&fp80[0]) |= (m << 11);
|
|
}
|
|
|
|
bool WinDebugger::AssignToReg(int callStackIdx, DbgTypedValue regVal, DbgTypedValue value, String& outError)
|
|
{
|
|
BF_ASSERT(regVal.mRegNum >= 0);
|
|
|
|
if (mCallStack.size() == 0)
|
|
{
|
|
outError = "No call stack";
|
|
return false;
|
|
}
|
|
|
|
if (callStackIdx >= (int)mCallStack.size())
|
|
{
|
|
outError = "Invalid call stack index";
|
|
return false;
|
|
}
|
|
|
|
auto registers = &mCallStack[callStackIdx]->mRegisters;
|
|
void* regPtr = NULL;
|
|
|
|
#ifdef BF_DBG_32
|
|
if ((regVal.mRegNum >= X86Reg_INTREG_FIRST) && (regVal.mRegNum <= X86Reg_INTREG_LAST))
|
|
{
|
|
BF_ASSERT(regVal.mType->mSize == sizeof(int32));
|
|
registers->mIntRegsArray[regVal.mRegNum - X86Reg_INTREG_FIRST] = (uint64)value.mUInt32; // don't sign-extend
|
|
}
|
|
else if ((regVal.mRegNum >= X86Reg_FPSTREG_FIRST) && (regVal.mRegNum <= X86Reg_FPSTREG_LAST))
|
|
{
|
|
BF_ASSERT(regVal.mType->mSize == sizeof(float) || regVal.mType->mSize == sizeof(double));
|
|
CPURegisters::FpMmReg* reg = ®isters->mFpMmRegsArray[regVal.mRegNum - X86Reg_FPSTREG_FIRST];
|
|
double d;
|
|
if (regVal.mType->mSize == sizeof(float))
|
|
d = (double)value.mSingle;
|
|
else
|
|
d = value.mDouble;
|
|
ConvertDoubleToFloat80(d, reg->fp.fp80);
|
|
}
|
|
else if ((regVal.mRegNum >= X86Reg_MMREG_FIRST) && (regVal.mRegNum <= X86Reg_MMREG_LAST))
|
|
{
|
|
BF_ASSERT(regVal.mType->mSize == sizeof(int32) || regVal.mType->mSize == sizeof(int64));
|
|
CPURegisters::FpMmReg* reg = ®isters->mFpMmRegsArray[regVal.mRegNum - X86Reg_MMREG_FIRST];
|
|
if (regVal.mType->mSize == sizeof(int32))
|
|
reg->mm = (uint64)value.mUInt32; // don't sign-extend
|
|
else if (regVal.mType->mSize == sizeof(int64))
|
|
reg->mm = value.mInt64;
|
|
// whenever we use the low 64 bits of the reg as mm, the upper 16 bits of the 80-bit float must be set to all-1s to indicate NaN
|
|
reg->fp.fp80[8] = reg->fp.fp80[9] = 0xFF;
|
|
}
|
|
else if ((regVal.mRegNum >= X86Reg_XMMREG_FIRST) && (regVal.mRegNum <= X86Reg_XMMREG_LAST))
|
|
{
|
|
int xmmMajor = (regVal.mRegNum - X86Reg_XMMREG_FIRST) >> 2;
|
|
int xmmMinor = (regVal.mRegNum - X86Reg_XMMREG_FIRST) & 3;
|
|
|
|
registers->mXmmRegsArray[xmmMajor].f[xmmMinor] = value.mSingle;
|
|
}
|
|
else if ((regVal.mRegNum >= X86Reg_M128_XMMREG_FIRST) && (regVal.mRegNum <= X86Reg_M128_XMMREG_LAST))
|
|
{
|
|
outError = "Cannot write directly to 128-bit XMM register, please use inner float components";
|
|
return false;
|
|
}
|
|
else if ((regVal.mRegNum >= X86Reg_FLAG_FIRST) && (regVal.mRegNum <= X86Reg_FLAG_LAST))
|
|
{
|
|
int flagBit = CPURegisters::GetFlagBitForRegister(regVal.mRegNum);
|
|
if (flagBit >= 0)
|
|
{
|
|
if (value.mBool)
|
|
registers->mIntRegs.efl |= ((uint64)1 << flagBit);
|
|
else
|
|
registers->mIntRegs.efl &= ~((uint64)1 << flagBit);
|
|
}
|
|
else
|
|
{
|
|
outError = "Unrecognized flag";
|
|
return false;
|
|
}
|
|
}
|
|
else if ((regVal.mRegNum >= X86Reg_CAT_FIRST) && (regVal.mRegNum <= X86Reg_CAT_LAST))
|
|
{
|
|
outError = "Cannot write directly to register categories, please use inner float components";
|
|
return false;
|
|
}
|
|
#else
|
|
|
|
if ((regVal.mRegNum >= X64Reg_INTREG_FIRST) && (regVal.mRegNum <= X64Reg_INTREG_LAST))
|
|
{
|
|
//BF_ASSERT(regVal.mType->mSize == sizeof(addr_target));
|
|
registers->mIntRegsArray[regVal.mRegNum - X64Reg_INTREG_FIRST] = value.GetInt64(); // don't sign-extend
|
|
regPtr = ®isters->mIntRegsArray[regVal.mRegNum - X64Reg_INTREG_FIRST];
|
|
}
|
|
else if ((regVal.mRegNum >= X64Reg_FPSTREG_FIRST) && (regVal.mRegNum <= X64Reg_FPSTREG_LAST))
|
|
{
|
|
BF_ASSERT(regVal.mType->mSize == sizeof(float) || regVal.mType->mSize == sizeof(double));
|
|
CPURegisters::FpMmReg* reg = ®isters->mFpMmRegsArray[regVal.mRegNum - X64Reg_FPSTREG_FIRST];
|
|
double d;
|
|
if (regVal.mType->mSize == sizeof(float))
|
|
d = (double)value.mSingle;
|
|
else
|
|
d = value.mDouble;
|
|
ConvertDoubleToFloat80(d, reg->fp.fp80);
|
|
regPtr = reg;
|
|
}
|
|
else if ((regVal.mRegNum >= X64Reg_MMREG_FIRST) && (regVal.mRegNum <= X64Reg_MMREG_LAST))
|
|
{
|
|
BF_ASSERT(regVal.mType->mSize == sizeof(int32) || regVal.mType->mSize == sizeof(int64));
|
|
CPURegisters::FpMmReg* reg = ®isters->mFpMmRegsArray[regVal.mRegNum - X64Reg_MMREG_FIRST];
|
|
if (regVal.mType->mSize == sizeof(int32))
|
|
reg->mm = (uint64)value.mUInt32; // don't sign-extend
|
|
else if (regVal.mType->mSize == sizeof(int64))
|
|
reg->mm = value.mInt64;
|
|
// whenever we use the low 64 bits of the reg as mm, the upper 16 bits of the 80-bit float must be set to all-1s to indicate NaN
|
|
reg->fp.fp80[8] = reg->fp.fp80[9] = 0xFF;
|
|
regPtr = reg;
|
|
}
|
|
else if ((regVal.mRegNum >= X64Reg_XMMREG_FIRST) && (regVal.mRegNum <= X64Reg_XMMREG_LAST))
|
|
{
|
|
int xmmMajor = (regVal.mRegNum - X64Reg_XMMREG_FIRST) >> 2;
|
|
int xmmMinor = (regVal.mRegNum - X64Reg_XMMREG_FIRST) & 3;
|
|
|
|
if (value.mType->GetByteCount() == 4)
|
|
registers->mXmmRegsArray[xmmMajor].f[xmmMinor] = value.mSingle;
|
|
else if (value.mType->GetByteCount() == 8)
|
|
registers->mXmmDRegsArray[xmmMajor].d[xmmMinor] = value.mDouble;
|
|
else
|
|
BF_FATAL("Invalid XMM set value type");
|
|
regPtr = ®isters->mXmmRegsArray[xmmMajor];
|
|
}
|
|
else if ((regVal.mRegNum >= X64Reg_M128_XMMREG_FIRST) && (regVal.mRegNum <= X64Reg_M128_XMMREG_LAST))
|
|
{
|
|
outError = "Cannot write directly to 128-bit XMM register, please use inner float components";
|
|
return false;
|
|
}
|
|
else if ((regVal.mRegNum >= X64Reg_FLAG_FIRST) && (regVal.mRegNum <= X64Reg_FLAG_LAST))
|
|
{
|
|
int flagBit = CPURegisters::GetFlagBitForRegister(regVal.mRegNum);
|
|
if (flagBit >= 0)
|
|
{
|
|
if (value.mBool)
|
|
registers->mIntRegs.efl |= ((uint64)1 << flagBit);
|
|
else
|
|
registers->mIntRegs.efl &= ~((uint64)1 << flagBit);
|
|
regPtr = ®isters->mIntRegs.efl;
|
|
}
|
|
else
|
|
{
|
|
outError = "Unrecognized flag";
|
|
return false;
|
|
}
|
|
}
|
|
else if ((regVal.mRegNum >= X64Reg_CAT_FIRST) && (regVal.mRegNum <= X64Reg_CAT_LAST))
|
|
{
|
|
outError = "Cannot write directly to register categories, please use inner float components";
|
|
return false;
|
|
}
|
|
else
|
|
BF_FATAL("Not implemented");
|
|
#endif
|
|
|
|
if (callStackIdx == 0)
|
|
{
|
|
SetRegisters(&mCallStack[0]->mRegisters);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
bool wasSaved = false;
|
|
for (int calleeStackIdx = callStackIdx - 1; calleeStackIdx >= 0; calleeStackIdx--)
|
|
{
|
|
auto calleeRegisters = &mCallStack[calleeStackIdx]->mRegisters;
|
|
if (!mDebugTarget->PropogateRegisterUpCallStack(registers, calleeRegisters, regPtr, wasSaved))
|
|
{
|
|
outError = "Failed to set register";
|
|
return false;
|
|
}
|
|
if (wasSaved)
|
|
return true;
|
|
}
|
|
|
|
// This register wasn't saved, so commit it to the callstack top
|
|
return AssignToReg(0, regVal, value, outError);
|
|
}
|
|
}
|
|
|
|
String WinDebugger::GetAutocompleteOutput(DwAutoComplete& 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 WinDebugger::EvaluateToAddress(const StringImpl& expr, int callStackIdx, int cursorPos)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (IsInRunState())
|
|
return "!Target not paused";
|
|
|
|
auto dbgModule = GetCallStackDbgModule(callStackIdx);
|
|
auto dbgCompileUnit = GetCallStackCompileUnit(callStackIdx);
|
|
|
|
BfParser parser(mBfSystem);
|
|
parser.mCompatMode = true;
|
|
|
|
BfPassInstance bfPassInstance(mBfSystem);
|
|
auto terminatedExpr = expr + ";";
|
|
parser.SetSource(terminatedExpr.c_str(), terminatedExpr.length());
|
|
parser.Parse(&bfPassInstance);
|
|
|
|
BfReducer bfReducer;
|
|
bfReducer.mAlloc = parser.mAlloc;
|
|
bfReducer.mSystem = mBfSystem;
|
|
bfReducer.mPassInstance = &bfPassInstance;
|
|
bfReducer.mVisitorPos = BfReducer::BfVisitorPos(parser.mRootNode);
|
|
bfReducer.mVisitorPos.MoveNext();
|
|
bfReducer.mSource = &parser;
|
|
auto exprNode = bfReducer.CreateExpression(parser.mRootNode->GetFirst());
|
|
parser.Close();
|
|
|
|
DwAutoComplete autoComplete;
|
|
DbgExprEvaluator dbgExprEvaluator(this, dbgModule, &bfPassInstance, callStackIdx, cursorPos);
|
|
if (cursorPos != -1)
|
|
dbgExprEvaluator.mAutoComplete = &autoComplete;
|
|
dbgExprEvaluator.mDbgCompileUnit = dbgCompileUnit;
|
|
|
|
DwFormatInfo formatInfo;
|
|
formatInfo.mCallStackIdx = callStackIdx;
|
|
|
|
DbgTypedValue exprResult;
|
|
if (exprNode != NULL)
|
|
exprResult = dbgExprEvaluator.Resolve(exprNode);
|
|
|
|
DbgType* resultType = exprResult.mType->RemoveModifiers();
|
|
|
|
String val;
|
|
if (bfPassInstance.HasFailed())
|
|
{
|
|
val = StrFormat("!%d\t%d\t%s", bfPassInstance.mErrors[0]->mSrcStart, bfPassInstance.mErrors[0]->GetSrcLength(), bfPassInstance.mErrors[0]->mError.c_str());
|
|
}
|
|
else if (exprResult.mType == NULL)
|
|
{
|
|
val = "!Invalid expression";
|
|
}
|
|
else if (!resultType->IsPointerOrRef())
|
|
{
|
|
if (exprResult.mSrcAddress != 0)
|
|
val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected. Try using the '&' address-of operator.", exprResult.mType->ToString().c_str());
|
|
else
|
|
val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected. An explicit cast may be required.", exprResult.mType->ToString().c_str());
|
|
}
|
|
else
|
|
{
|
|
auto innerType = resultType->mTypeParam;
|
|
int byteCount = innerType->GetByteCount();
|
|
if (byteCount == 0)
|
|
{
|
|
val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected, try casting to a non-void pointer type.", exprResult.mType->ToString().c_str());
|
|
}
|
|
#ifdef BF_DBG_32
|
|
else if ((byteCount != 1) && (byteCount != 2) && (byteCount != 4))
|
|
{
|
|
val = StrFormat("!Element size is %d bytes. Only 1, 2, or 4 byte elements can be tracked. Try casting to an appropriately-sized pointer.", innerType->mSize);
|
|
}
|
|
#else
|
|
else if ((byteCount != 1) && (byteCount != 2) && (byteCount != 4) && (byteCount != 8))
|
|
{
|
|
val = StrFormat("!Element size is %d bytes. Only 1, 2, 4, or 8 byte elements can be tracked. Try casting to an appropriately-sized pointer.", innerType->mSize);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
val = EncodeDataPtr(exprResult.mPtr, false) + "\n" + StrFormat("%d", byteCount);
|
|
}
|
|
}
|
|
|
|
if (cursorPos != -1)
|
|
val += GetAutocompleteOutput(autoComplete);
|
|
|
|
return val;
|
|
}
|
|
|
|
// This is currently only used for autocomplete during conditional breakpoint expression entry.
|
|
// If we want to use it for more than that then remove DwEvalExpressionFlags_ValidateOnly
|
|
String WinDebugger::EvaluateAtAddress(const StringImpl& expr, intptr atAddr, int cursorPos)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (IsInRunState())
|
|
return "!Target not paused";
|
|
|
|
if (!IsPaused())
|
|
return "!Target not running";
|
|
|
|
WdStackFrame stackFrame;
|
|
memset(&stackFrame.mRegisters, 0, sizeof(stackFrame.mRegisters));
|
|
stackFrame.mHasGottenSubProgram = true;
|
|
*stackFrame.mRegisters.GetPCRegisterRef() = (intptr_target)atAddr;
|
|
stackFrame.mSubProgram = mDebugTarget->FindSubProgram((addr_target)atAddr);
|
|
if (stackFrame.mSubProgram == NULL)
|
|
return "!Invalid address";
|
|
mCallStack.push_back(&stackFrame);
|
|
int callStackIdx = (int)mCallStack.size() - 1;
|
|
String val = Evaluate(expr, callStackIdx, cursorPos, -1, DwEvalExpressionFlag_ValidateOnly);
|
|
mCallStack.pop_back();
|
|
|
|
return val;
|
|
}
|
|
|
|
String WinDebugger::GetAutoExpressions(int callStackIdx, uint64 memoryRangeStart, uint64 memoryRangeLen)
|
|
{
|
|
BP_ZONE("WinDebugger::GetAutoExpressions");
|
|
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (IsInRunState())
|
|
return "!Not paused";
|
|
|
|
if (!IsPaused())
|
|
return "!Not running";
|
|
|
|
if (!FixCallStackIdx(callStackIdx))
|
|
return "";
|
|
|
|
CPUStackFrame* stackFrame = (callStackIdx >= 0) ? mCallStack[callStackIdx] : mCallStack.front();
|
|
|
|
String result;
|
|
|
|
DbgAutoValueMapType dwarfAutos;
|
|
mDebugTarget->GetAutoValueNames(dwarfAutos, stackFrame, memoryRangeStart, memoryRangeLen);
|
|
|
|
for (auto const &a : dwarfAutos)
|
|
{
|
|
std::pair<uint64, uint64> varRange = a.mValue;
|
|
if (varRange.first != 0)
|
|
result += StrFormat("&%s\t%llu\t%llu\n", a.mKey.c_str(), varRange.second, varRange.first);
|
|
else
|
|
result += StrFormat("?%s\t%llu\n", a.mKey.c_str(), varRange.second);
|
|
}
|
|
|
|
#ifdef BF_DBG_64
|
|
// add int regs
|
|
const char* regStrs[] = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "rip", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", 0 };
|
|
#else
|
|
// add int regs
|
|
const char* regStrs[] = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "eip", 0 };
|
|
#endif
|
|
for (const char** p = regStrs; *p; ++p)
|
|
result += StrFormat("$%s\t%d\n", *p, sizeof(addr_target));
|
|
|
|
if (callStackIdx < (int)mCallStack.size() - 2)
|
|
{
|
|
WdStackFrame* prevStackFrame = mCallStack[callStackIdx + 1];
|
|
// Inlined methods have no stack frame
|
|
int stackSize = prevStackFrame->mRegisters.GetSP() - stackFrame->mRegisters.GetSP();
|
|
result += StrFormat("&$StackFrame\t%llu\t%llu\n", stackSize, stackFrame->mRegisters.GetSP());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
String WinDebugger::GetAutoLocals(int stackFrameIdx, bool showRegs)
|
|
{
|
|
BP_ZONE("WinDebugger::GetAutoExpressions");
|
|
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (IsInRunState())
|
|
return "";
|
|
|
|
if (!IsPaused())
|
|
return "";
|
|
|
|
if (mCallStack.size() == 0)
|
|
UpdateCallStack();
|
|
String result;
|
|
|
|
Array<String> localList;
|
|
|
|
int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
|
|
UpdateCallStackMethod(actualStackFrameIdx);
|
|
|
|
if (actualStackFrameIdx >= mCallStack.size())
|
|
return "";
|
|
WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];
|
|
|
|
DbgSubprogram* dwSubprogram = wdStackFrame->mSubProgram;
|
|
if (dwSubprogram == NULL)
|
|
return "";
|
|
|
|
auto langage = dwSubprogram->GetLanguage();
|
|
|
|
DbgLineData* dwLineData = FindLineDataInSubprogram(wdStackFrame->GetSourcePC(), dwSubprogram);
|
|
if (dwLineData == NULL)
|
|
return "";
|
|
|
|
dwSubprogram->PopulateSubprogram();
|
|
mDebugTarget->GetAutoLocalsInBlock(localList, dwSubprogram, &dwSubprogram->mBlock, wdStackFrame, dwLineData);
|
|
|
|
String lastLocal;
|
|
for (auto local : localList)
|
|
{
|
|
if (langage == DbgLanguage_C)
|
|
{
|
|
if ((local == "this") && (strncmp(dwSubprogram->mName, "<lambda_", 8) == 0))
|
|
{
|
|
// Use explicit "$this" so we can see the actual capture
|
|
result += "$this\n";
|
|
continue;
|
|
}
|
|
}
|
|
|
|
bool wasAlias = false;
|
|
for (int i = 0; i < (int)local.length() - 1; i++)
|
|
{
|
|
if ((local[i] == '$') && (local[i + 1] == 'a'))
|
|
{
|
|
// Alias
|
|
wasAlias = true;
|
|
String localName = local.Substring(0, i) + "\n";
|
|
if (localName != lastLocal)
|
|
{
|
|
result += localName;
|
|
lastLocal = localName;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!wasAlias)
|
|
result += local + "\n";
|
|
}
|
|
|
|
if (showRegs)
|
|
{
|
|
result += "$FLAGS\n";
|
|
|
|
UpdateRegisterUsage(stackFrameIdx);
|
|
for (int regIdx = 0; regIdx < (int)wdStackFrame->mRegForms.size(); regIdx++)
|
|
{
|
|
if (wdStackFrame->mRegForms[regIdx] != RegForm_Invalid)
|
|
result += "$" + String(CPURegisters::GetRegisterName(regIdx)) + "\n";
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
String WinDebugger::CompactChildExpression(const StringImpl& expr, const StringImpl& parentExpr, int callStackIdx)
|
|
{
|
|
DbgCompileUnit* compileUnit = GetCallStackCompileUnit(callStackIdx);
|
|
DbgModule* dbgModule = GetCallStackDbgModule(callStackIdx);
|
|
if (dbgModule == NULL)
|
|
return "!failed";
|
|
|
|
DbgLanguage language = DbgLanguage_Unknown;
|
|
if (compileUnit != NULL)
|
|
language = compileUnit->mLanguage;
|
|
|
|
auto terminatedParentExpr = parentExpr + ";";
|
|
|
|
String parentPrefix;
|
|
|
|
int colonIdx = terminatedParentExpr.IndexOf(':');
|
|
if (colonIdx > 0)
|
|
{
|
|
bool isValid = true;
|
|
String lang = terminatedParentExpr.Substring(1, colonIdx - 1);
|
|
lang = ToUpper(lang);
|
|
if ((lang == "") || (lang == "BEEF"))
|
|
{
|
|
language = DbgLanguage_Beef;
|
|
}
|
|
else if (lang == "C")
|
|
{
|
|
language = DbgLanguage_C;
|
|
}
|
|
if (language != DbgLanguage_Unknown)
|
|
{
|
|
parentPrefix += terminatedParentExpr.Substring(0, colonIdx + 1);
|
|
terminatedParentExpr.Remove(0, colonIdx + 1);
|
|
}
|
|
}
|
|
|
|
if (terminatedParentExpr.StartsWith('{'))
|
|
{
|
|
int prefixEnd = terminatedParentExpr.IndexOf('}');
|
|
parentPrefix += terminatedParentExpr.Substring(0, prefixEnd + 1);
|
|
terminatedParentExpr.Remove(0, prefixEnd + 1);
|
|
}
|
|
|
|
BfPassInstance bfPassInstance(mBfSystem);
|
|
|
|
BfParser parser(mBfSystem);
|
|
parser.mCompatMode = language != DbgLanguage_Beef;
|
|
auto terminatedExpr = expr + ";";
|
|
parser.SetSource(terminatedExpr.c_str(), terminatedExpr.length());
|
|
parser.Parse(&bfPassInstance);
|
|
|
|
BfParser parentParser(mBfSystem);
|
|
parentParser.mCompatMode = language != DbgLanguage_Beef;
|
|
parentParser.SetSource(terminatedParentExpr.c_str(), terminatedParentExpr.length());
|
|
parentParser.Parse(&bfPassInstance);
|
|
|
|
BfReducer bfReducer;
|
|
bfReducer.mCompatMode = true;
|
|
bfReducer.mAlloc = parser.mAlloc;
|
|
bfReducer.mSystem = mBfSystem;
|
|
bfReducer.mPassInstance = &bfPassInstance;
|
|
bfReducer.mVisitorPos = BfReducer::BfVisitorPos(parser.mRootNode);
|
|
bfReducer.mVisitorPos.MoveNext();
|
|
bfReducer.mSource = &parser;
|
|
auto exprNode = bfReducer.CreateExpression(parser.mRootNode->GetFirst());
|
|
bfReducer.mAlloc = parentParser.mAlloc;
|
|
bfReducer.mVisitorPos = BfReducer::BfVisitorPos(parentParser.mRootNode);
|
|
bfReducer.mVisitorPos.MoveNext();
|
|
auto parentExprNode = bfReducer.CreateExpression(parentParser.mRootNode->GetFirst());
|
|
parser.Close();
|
|
|
|
if ((exprNode == NULL) || (parentExprNode == NULL))
|
|
return "!failed";
|
|
|
|
DbgExprEvaluator dbgExprEvaluator(this, dbgModule, &bfPassInstance, callStackIdx, -1);
|
|
|
|
DwFormatInfo formatInfo;
|
|
formatInfo.mCallStackIdx = callStackIdx;
|
|
formatInfo.mLanguage = language;
|
|
|
|
String formatFlags;
|
|
String assignExpr;
|
|
if ((exprNode != NULL) && (exprNode->GetSrcEnd() < (int) expr.length()))
|
|
{
|
|
formatFlags = Trim(expr.Substring(exprNode->GetSrcEnd()));
|
|
if (formatFlags.length() > 0)
|
|
{
|
|
String errorString = "Invalid expression";
|
|
if (!ParseFormatInfo(dbgModule, formatFlags, &formatInfo, &bfPassInstance, NULL, &assignExpr, &errorString))
|
|
{
|
|
bfPassInstance.FailAt(errorString, parser.mSourceData, exprNode->GetSrcEnd(), (int) expr.length() - exprNode->GetSrcEnd());
|
|
formatFlags = "";
|
|
}
|
|
}
|
|
}
|
|
dbgExprEvaluator.mExplicitThis = formatInfo.mExplicitThis;
|
|
dbgExprEvaluator.mExplicitThisExpr = parentExprNode;
|
|
|
|
DbgTypedValue exprResult = dbgExprEvaluator.Resolve(exprNode);
|
|
BfAstNode* headNode = dbgExprEvaluator.FinalizeExplicitThisReferences(exprNode);
|
|
|
|
BfPrinter printer(parser.mRootNode, NULL, NULL);
|
|
printer.mIgnoreTrivia = true;
|
|
printer.mReformatting = true;
|
|
printer.VisitChild(headNode);
|
|
String result;
|
|
result += parentPrefix;
|
|
result += printer.mOutString;
|
|
if (formatInfo.mNoVisualizers)
|
|
result += ", nv";
|
|
if (formatInfo.mNoMembers)
|
|
result += ", nm";
|
|
if (formatInfo.mNoEdit)
|
|
result += ", ne";
|
|
if (formatInfo.mIgnoreDerivedClassInfo)
|
|
result += ", nd";
|
|
if (formatInfo.mDisplayType == DwDisplayType_Ascii)
|
|
result += ", s";
|
|
if (formatInfo.mDisplayType == DwDisplayType_Utf8)
|
|
result += ", s8";
|
|
if (formatInfo.mDisplayType == DwDisplayType_Utf16)
|
|
result += ", s16";
|
|
if (formatInfo.mDisplayType == DwDisplayType_Utf32)
|
|
result += ", s32";
|
|
return result;
|
|
}
|
|
|
|
String WinDebugger::GetProcessInfo()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if ((mActiveThread == NULL) && (!mIsRunning))
|
|
return "";
|
|
|
|
SYSTEM_INFO sysinfo = { 0 };
|
|
GetSystemInfo(&sysinfo);
|
|
|
|
FILETIME creationTime = { 0 };
|
|
FILETIME exitTime = { 0 };
|
|
FILETIME kernelTime = { 0 };
|
|
FILETIME userTime = { 0 };
|
|
::GetProcessTimes(mProcessInfo.hProcess, &creationTime, &exitTime, &kernelTime, &userTime);
|
|
|
|
String retStr;
|
|
PROCESS_MEMORY_COUNTERS memInfo = { 0 };
|
|
::GetProcessMemoryInfo(mProcessInfo.hProcess, &memInfo, sizeof(PROCESS_MEMORY_COUNTERS));
|
|
|
|
FILETIME currentTime = { 0 };
|
|
::GetSystemTimeAsFileTime(¤tTime);
|
|
|
|
retStr += StrFormat("VirtualMemory\t%lld\n", memInfo.PagefileUsage);
|
|
retStr += StrFormat("WorkingMemory\t%lld\n", memInfo.WorkingSetSize);
|
|
retStr += StrFormat("RunningTime\t%lld\n", *(int64*)¤tTime - *(int64*)&creationTime);
|
|
retStr += StrFormat("KernelTime\t%lld\n", *(int64*)&kernelTime / sysinfo.dwNumberOfProcessors);
|
|
retStr += StrFormat("UserTime\t%lld\n", *(int64*)&userTime / sysinfo.dwNumberOfProcessors);
|
|
|
|
return retStr;
|
|
}
|
|
|
|
int WinDebugger::GetProcessId()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
if (!mThreadList.IsEmpty())
|
|
return mThreadList[0]->mProcessId;
|
|
return mDbgProcessId;
|
|
}
|
|
|
|
String WinDebugger::GetThreadInfo()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
String retStr;
|
|
if ((mActiveThread == NULL) && (!mIsRunning))
|
|
{
|
|
retStr = "";
|
|
}
|
|
else
|
|
{
|
|
if (mActiveThread != NULL)
|
|
retStr = StrFormat("%d", mActiveThread->mThreadId);
|
|
|
|
for (auto threadInfo : mThreadList)
|
|
{
|
|
SetAndRestoreValue<WdThreadInfo*> prevThread(mActiveThread, threadInfo);
|
|
|
|
retStr += "\n";
|
|
|
|
for (int pass = 0; pass < 2; pass++)
|
|
{
|
|
CPURegisters registers;
|
|
PopulateRegisters(®isters);
|
|
|
|
String locString = EncodeDataPtr((addr_target)registers.GetPC(), true);
|
|
|
|
TryGetThreadName(threadInfo);
|
|
|
|
bool hadThreadName = true;
|
|
String threadName = threadInfo->mName;
|
|
if (threadName.IsEmpty())
|
|
{
|
|
hadThreadName = false;
|
|
if (threadInfo->mThreadId == mProcessInfo.dwThreadId)
|
|
threadName = "Main Thread";
|
|
else
|
|
threadName = "Worker Thread";
|
|
}
|
|
|
|
bool isInvalid = false;
|
|
addr_target appendAddr = 0;
|
|
|
|
for (int stackIdx = 0; true; stackIdx++)
|
|
{
|
|
auto subProgram = mDebugTarget->FindSubProgram(registers.GetPC(), DbgOnDemandKind_LocalOnly);
|
|
if (subProgram != NULL)
|
|
{
|
|
if (subProgram->mLineInfo != NULL)
|
|
{
|
|
DbgModule* module = subProgram->mCompileUnit->mDbgModule;
|
|
DbgModule* linkedModule = module->GetLinkedModule();
|
|
if (linkedModule->mDisplayName.length() > 0)
|
|
{
|
|
locString = linkedModule->mDisplayName + "!" + subProgram->ToString();
|
|
if (!hadThreadName)
|
|
threadName = module->mDisplayName + " thread";
|
|
}
|
|
else
|
|
{
|
|
locString = subProgram->ToString();
|
|
}
|
|
appendAddr = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DbgModule* module = mDebugTarget->FindDbgModuleForAddress(registers.GetPC());
|
|
if (module == NULL)
|
|
{
|
|
isInvalid = true;
|
|
break;
|
|
}
|
|
|
|
DbgModule* linkedModule = module->GetLinkedModule();
|
|
appendAddr = (addr_target)registers.GetPC();
|
|
locString = linkedModule->mDisplayName + "!" + EncodeDataPtr((addr_target)registers.GetPC(), true);
|
|
if (!hadThreadName)
|
|
threadName = linkedModule->mDisplayName + " thread";
|
|
|
|
if ((mActiveThread == mExplicitStopThread) && (mActiveBreakpoint != NULL))
|
|
{
|
|
if ((subProgram == NULL) ||
|
|
(mActiveBreakpoint->mAddr < subProgram->mBlock.mLowPC) ||
|
|
(mActiveBreakpoint->mAddr >= subProgram->mBlock.mHighPC))
|
|
break;
|
|
}
|
|
|
|
if (pass == 1) // Just take the first item
|
|
break;
|
|
|
|
if (stackIdx == 128)
|
|
break; // Too many!
|
|
|
|
addr_target returnAddr;
|
|
if (!mDebugTarget->RollBackStackFrame(®isters, &returnAddr, true))
|
|
{
|
|
isInvalid = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((isInvalid) && (pass == 0))
|
|
continue;
|
|
|
|
if (appendAddr != 0)
|
|
{
|
|
String symbolName;
|
|
addr_target offset;
|
|
DbgModule* dwarf;
|
|
if (mDebugTarget->FindSymbolAt(appendAddr, &symbolName, &offset, &dwarf))
|
|
{
|
|
DbgModule* linkedModule = dwarf->GetLinkedModule();
|
|
String demangledName = BfDemangler::Demangle(symbolName, DbgLanguage_Unknown);
|
|
if (!linkedModule->mDisplayName.empty())
|
|
{
|
|
demangledName = linkedModule->mDisplayName + "!" + demangledName;
|
|
}
|
|
locString = demangledName + StrFormat("+0x%X", offset);
|
|
}
|
|
}
|
|
|
|
retStr += StrFormat("%d\t%s\t%s", threadInfo->mThreadId, threadName.c_str(), locString.c_str());
|
|
|
|
String attrs;
|
|
if (threadInfo->mFrozen)
|
|
{
|
|
attrs += "Fr";
|
|
}
|
|
if (!attrs.IsEmpty())
|
|
{
|
|
retStr += "\t";
|
|
retStr += attrs;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return retStr;
|
|
}
|
|
|
|
void WinDebugger::SetActiveThread(int threadId)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if ((mActiveThread != NULL) && (mActiveThread->mThreadId == threadId))
|
|
return;
|
|
|
|
auto prevThread = mActiveThread;
|
|
|
|
if (mThreadMap.TryGetValue(threadId, &mActiveThread))
|
|
{
|
|
BfLogDbg("SetActiveThread %d\n", threadId);
|
|
|
|
if (prevThread != NULL)
|
|
{
|
|
Array<WdStackFrame*>* prevFrameArray = NULL;
|
|
mSavedCallStacks.TryAdd(prevThread, NULL, &prevFrameArray);
|
|
for (auto frameInfo : *prevFrameArray)
|
|
delete frameInfo;
|
|
*prevFrameArray = mCallStack;
|
|
mCallStack.Clear();
|
|
}
|
|
|
|
DoClearCallStack(false);
|
|
|
|
Array<WdStackFrame*>* newFrameArray = NULL;
|
|
if (mSavedCallStacks.TryGetValue(mActiveThread, &newFrameArray))
|
|
{
|
|
mCallStack = *newFrameArray;
|
|
newFrameArray->Clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BfLogDbg("SetActiveThread %d FAILED\n", threadId);
|
|
}
|
|
}
|
|
|
|
int WinDebugger::GetActiveThread()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (mActiveThread == NULL)
|
|
return -1;
|
|
return mActiveThread->mThreadId;
|
|
}
|
|
|
|
void WinDebugger::FreezeThread(int threadId)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
BF_ASSERT(!IsInRunState());
|
|
auto thread = mThreadMap[threadId];
|
|
if (!thread->mFrozen)
|
|
{
|
|
thread->mFrozen = true;
|
|
::SuspendThread(thread->mHThread);
|
|
BfLogDbg("SuspendThread %d from FreezeThread\n", thread->mThreadId);
|
|
}
|
|
}
|
|
|
|
void WinDebugger::ThawThread(int threadId)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
BF_ASSERT(!IsInRunState());
|
|
auto thread = mThreadMap[threadId];
|
|
if (thread->mFrozen)
|
|
{
|
|
thread->mFrozen = false;
|
|
::ResumeThread(thread->mHThread);
|
|
BfLogDbg("ResumeThread %d from ThawThread\n", thread->mThreadId);
|
|
}
|
|
}
|
|
|
|
bool WinDebugger::IsActiveThreadWaiting()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
return mActiveThread == mDebuggerWaitingThread;
|
|
}
|
|
|
|
void WinDebugger::DoClearCallStack(bool clearSavedStacks)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
BfLogDbg("ClearCallstack\n");
|
|
BF_ASSERT(mRunState != RunState_DebugEval);
|
|
|
|
for (auto wdStackFrame : mCallStack)
|
|
delete wdStackFrame;
|
|
|
|
if (clearSavedStacks)
|
|
{
|
|
for (auto& kv : mSavedCallStacks)
|
|
{
|
|
for (auto wdStackFrame : kv.mValue)
|
|
delete wdStackFrame;
|
|
}
|
|
mSavedCallStacks.Clear();
|
|
}
|
|
|
|
mCallStack.Clear();
|
|
mIsPartialCallStack = true;
|
|
}
|
|
|
|
void WinDebugger::ClearCallStack()
|
|
{
|
|
DoClearCallStack(true);
|
|
}
|
|
|
|
void WinDebugger::UpdateCallStack(bool slowEarlyOut)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!mIsPartialCallStack)
|
|
return;
|
|
|
|
if (mActiveThread == NULL)
|
|
return;
|
|
|
|
if (IsInRunState())
|
|
return;
|
|
|
|
uint32 tickStart = BFTickCount();
|
|
|
|
CPURegisters registers;
|
|
|
|
if (mCallStack.size() > 0)
|
|
{
|
|
WdStackFrame* wdStackFrame = mCallStack.back();
|
|
if (wdStackFrame->mIsEnd)
|
|
{
|
|
return;
|
|
}
|
|
memcpy(®isters, &wdStackFrame->mRegisters, sizeof(registers));
|
|
bool regsRolledBack = RollBackStackFrame(®isters, mCallStack.size() == 1);
|
|
// If we can't roll them back then mIsEnd should have been set for the previous frame
|
|
BF_ASSERT(regsRolledBack);
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(mIsPartialCallStack);
|
|
mCallStack.Reserve(1024);
|
|
PopulateRegisters(®isters);
|
|
BfLogDbg("UpdateCallStack starting. Thread=%d PC=0x%p\n", mActiveThread->mThreadId, registers.GetPC());
|
|
}
|
|
|
|
bool isPartial = false;
|
|
|
|
// Incrementally fill callstack structure to avoid stepping slowdown during deep nesting
|
|
for (int fillIdx = 0; fillIdx < (slowEarlyOut ? 10000 : 100000); fillIdx++)
|
|
{
|
|
WdStackFrame* wdStackFrame = new WdStackFrame();
|
|
memcpy(&wdStackFrame->mRegisters, ®isters, sizeof(registers));
|
|
wdStackFrame->mIsStart = mCallStack.size() == 0;
|
|
wdStackFrame->mIsEnd = false;
|
|
|
|
bool rollbackSuccess = false;
|
|
for (int tryCount = 0; tryCount < 16; tryCount++)
|
|
{
|
|
if (!RollBackStackFrame(®isters, wdStackFrame->mIsStart))
|
|
{
|
|
break;
|
|
}
|
|
if (registers.GetPC() > 0xFFFF)
|
|
{
|
|
rollbackSuccess = true;
|
|
break;
|
|
}
|
|
|
|
if (mCallStack.size() > 0)
|
|
break; // Only retry for the first frame
|
|
}
|
|
|
|
if (!rollbackSuccess)
|
|
wdStackFrame->mIsEnd = true;
|
|
|
|
if (registers.GetSP() <= wdStackFrame->mRegisters.GetSP())
|
|
{
|
|
// SP went the wrong direction, stop rolling back
|
|
wdStackFrame->mIsEnd = true;
|
|
}
|
|
|
|
mCallStack.push_back(wdStackFrame);
|
|
|
|
if (IsMiniDumpDebugger())
|
|
{
|
|
// Make sure to queue up any debug stuff we need
|
|
UpdateCallStackMethod((int)mCallStack.size() - 1);
|
|
}
|
|
|
|
if (wdStackFrame->mIsEnd)
|
|
break;
|
|
|
|
// Time-limit callstack generation. Most useful for debug mode.
|
|
if ((slowEarlyOut) && ((fillIdx % 100) == 0))
|
|
{
|
|
uint32 tickEnd = BFTickCount();
|
|
if (tickEnd - tickStart >= 10)
|
|
{
|
|
isPartial = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isPartial)
|
|
mIsPartialCallStack = false;
|
|
}
|
|
|
|
int WinDebugger::GetCallStackCount()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
return (int)mCallStack.size();
|
|
}
|
|
|
|
int WinDebugger::GetRequestedStackFrameIdx()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if ((mActiveThread == mExplicitStopThread) && (mRequestedStackFrameIdx >= -1))
|
|
{
|
|
if (mActiveBreakpoint != NULL)
|
|
mRequestedStackFrameIdx = GetBreakStackFrameIdx();
|
|
if (mRequestedStackFrameIdx == -1)
|
|
mRequestedStackFrameIdx = 0;
|
|
return mRequestedStackFrameIdx;
|
|
}
|
|
|
|
int newCallStackIdx = 0;
|
|
while (true)
|
|
{
|
|
if (newCallStackIdx >= (int)mCallStack.size() - 1)
|
|
UpdateCallStack();
|
|
if (newCallStackIdx >= (int)mCallStack.size() - 1)
|
|
break;
|
|
|
|
intptr addr;
|
|
String file;
|
|
int hotIdx;
|
|
int defLineStart;
|
|
int defLineEnd;
|
|
int line;
|
|
int column;
|
|
int language;
|
|
int stackSize;
|
|
int8 flags;
|
|
GetStackFrameInfo(newCallStackIdx, &addr, &file, &hotIdx, &defLineStart, &defLineEnd, &line, &column, &language, &stackSize, &flags);
|
|
if (!file.empty())
|
|
return newCallStackIdx;
|
|
newCallStackIdx++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int WinDebugger::GetBreakStackFrameIdx()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if ((mActiveBreakpoint == NULL) || (mRunState != RunState_Breakpoint))
|
|
return -1;
|
|
if ((mBreakStackFrameIdx != -1) || (mActiveThread != mExplicitStopThread))
|
|
return mBreakStackFrameIdx;
|
|
|
|
mBreakStackFrameIdx = 0;
|
|
BF_ASSERT(mActiveBreakpoint != NULL);
|
|
if (mCallStack.IsEmpty())
|
|
UpdateCallStack();
|
|
if (!mCallStack.IsEmpty())
|
|
{
|
|
UpdateCallStackMethod(0);
|
|
|
|
for (int stackIdx = 0; stackIdx < (int)mCallStack.size(); stackIdx++)
|
|
{
|
|
auto callStackEntry = mCallStack[stackIdx];
|
|
if (callStackEntry->mSubProgram == NULL)
|
|
break;
|
|
if ((mActiveBreakpoint->mAddr < callStackEntry->mSubProgram->mBlock.mLowPC) ||
|
|
(mActiveBreakpoint->mAddr >= callStackEntry->mSubProgram->mBlock.mHighPC))
|
|
break;
|
|
|
|
DbgSubprogram* specificSubprogram = callStackEntry->mSubProgram;
|
|
auto dwLineData = callStackEntry->mSubProgram->FindClosestLine(mActiveBreakpoint->mAddr, &specificSubprogram);
|
|
if (dwLineData == NULL)
|
|
break;
|
|
|
|
if (mActiveBreakpoint->mLineData == dwLineData)
|
|
{
|
|
mBreakStackFrameIdx = stackIdx;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return mBreakStackFrameIdx;
|
|
}
|
|
|
|
static const char* SafeString(const char* str)
|
|
{
|
|
if (str == NULL)
|
|
return "???";
|
|
return str;
|
|
}
|
|
|
|
void WinDebugger::UpdateRegisterUsage(int stackFrameIdx)
|
|
{
|
|
WdStackFrame* wdStackFrame = mCallStack[stackFrameIdx];
|
|
if (wdStackFrame->mRegForms.size() != 0)
|
|
return;
|
|
|
|
auto dwSubprogram = wdStackFrame->mSubProgram;
|
|
if (dwSubprogram == NULL)
|
|
return;
|
|
|
|
addr_target addr = dwSubprogram->mBlock.mLowPC;
|
|
|
|
const uint8* baseOp = nullptr;
|
|
while (addr < dwSubprogram->mBlock.mHighPC)
|
|
{
|
|
CPUInst inst;
|
|
if (!mDebugTarget->DecodeInstruction(addr, &inst))
|
|
break;
|
|
|
|
bool overrideForm = inst.mAddress <= (addr_target)wdStackFrame->mRegisters.GetPC();
|
|
inst.MarkRegsUsed(wdStackFrame->mRegForms, overrideForm);
|
|
|
|
addr += inst.GetLength();
|
|
}
|
|
}
|
|
|
|
// It's safe to pass an invalid idx in here
|
|
void WinDebugger::UpdateCallStackMethod(int stackFrameIdx)
|
|
{
|
|
if (mCallStack.empty())
|
|
return;
|
|
|
|
int startIdx = std::min(stackFrameIdx, (int)mCallStack.size() - 1);
|
|
while (startIdx >= 0)
|
|
{
|
|
WdStackFrame* wdStackFrame = mCallStack[startIdx];
|
|
if (wdStackFrame->mHasGottenSubProgram)
|
|
break;
|
|
startIdx--;
|
|
}
|
|
startIdx++;
|
|
|
|
for (int checkFrameIdx = startIdx; checkFrameIdx <= stackFrameIdx; checkFrameIdx++)
|
|
{
|
|
//BF_ASSERT(checkFrameIdx < mCallStack.size());
|
|
if (checkFrameIdx >= mCallStack.size())
|
|
break;
|
|
|
|
WdStackFrame* wdStackFrame = mCallStack[checkFrameIdx];
|
|
|
|
wdStackFrame->mHasGottenSubProgram = true;
|
|
addr_target pcAddress = (addr_target)wdStackFrame->GetSourcePC();
|
|
DbgSubprogram* dwSubprogram = mDebugTarget->FindSubProgram(pcAddress, DbgOnDemandKind_LocalOnly);
|
|
wdStackFrame->mHasGottenSubProgram = true;
|
|
wdStackFrame->mSubProgram = dwSubprogram;
|
|
|
|
if ((dwSubprogram == NULL) && (IsMiniDumpDebugger()))
|
|
{
|
|
// FindSymbolAt will queue up debug info if necessary...
|
|
String symbolName;
|
|
addr_target offset;
|
|
DbgModule* dbgModule;
|
|
mDebugTarget->FindSymbolAt(pcAddress, &symbolName, &offset, &dbgModule);
|
|
}
|
|
|
|
auto prevStackFrame = wdStackFrame;
|
|
|
|
// Insert inlines
|
|
int insertIdx = checkFrameIdx + 1;
|
|
while ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL))
|
|
{
|
|
WdStackFrame* inlineStackFrame = new WdStackFrame();
|
|
*inlineStackFrame = *wdStackFrame;
|
|
inlineStackFrame->mInInlineMethod = true;
|
|
wdStackFrame->mInInlineCall = true;
|
|
inlineStackFrame->mSubProgram = dwSubprogram->mInlineeInfo->mInlineParent;
|
|
mCallStack.Insert(insertIdx, inlineStackFrame);
|
|
dwSubprogram = dwSubprogram->mInlineeInfo->mInlineParent;
|
|
insertIdx++;
|
|
checkFrameIdx++;
|
|
|
|
prevStackFrame = inlineStackFrame;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinDebugger::GetCodeAddrInfo(intptr addr, intptr inlineCallAddr, String* outFile, int* outHotIdx, int* outDefLineStart, int* outDefLineEnd, int* outLine, int* outColumn)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
DbgSubprogram* subProgram = NULL;
|
|
DbgLineData* callingLineData = FindLineDataAtAddress((addr_target)addr, &subProgram);
|
|
|
|
if (inlineCallAddr != 0)
|
|
{
|
|
auto inlinedSubProgram = mDebugTarget->FindSubProgram(inlineCallAddr);
|
|
if (inlinedSubProgram != 0)
|
|
{
|
|
FixupLineDataForSubprogram(inlinedSubProgram->mInlineeInfo->mRootInliner);
|
|
DbgSubprogram* parentSubprogram = inlinedSubProgram->mInlineeInfo->mInlineParent; // Require it be in the inline parent
|
|
auto foundLine = parentSubprogram->FindClosestLine(inlinedSubProgram->mBlock.mLowPC, &parentSubprogram);
|
|
if (foundLine != NULL)
|
|
{
|
|
auto srcFile = parentSubprogram->GetLineSrcFile(*foundLine);
|
|
*outFile = srcFile->GetLocalPath();
|
|
*outLine = foundLine->mLine;
|
|
}
|
|
|
|
*outHotIdx = inlinedSubProgram->mCompileUnit->mDbgModule->mHotIdx;
|
|
*outColumn = -1;
|
|
|
|
DbgSubprogram* callingSubProgram = NULL;
|
|
DbgLineData* callingLineData = FindLineDataAtAddress(inlinedSubProgram->mBlock.mLowPC - 1, &callingSubProgram);
|
|
if ((callingLineData != NULL) && (callingSubProgram == subProgram))
|
|
{
|
|
auto callingSrcFile = callingSubProgram->GetLineSrcFile(*callingLineData);
|
|
auto srcFile = callingSrcFile;
|
|
*outFile = srcFile->GetLocalPath();
|
|
|
|
if (*outLine == callingLineData->mLine)
|
|
*outColumn = callingLineData->mColumn;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (subProgram != NULL)
|
|
{
|
|
if ((subProgram->mInlineeInfo != NULL) && ((addr_target)addr >= subProgram->mBlock.mHighPC))
|
|
callingLineData = &subProgram->mInlineeInfo->mLastLineData;
|
|
|
|
*outHotIdx = subProgram->mCompileUnit->mDbgModule->mHotIdx;
|
|
*outFile = subProgram->GetLineSrcFile(*callingLineData)->GetLocalPath();
|
|
*outLine = callingLineData->mLine;
|
|
*outColumn = callingLineData->mColumn;
|
|
|
|
FixupLineDataForSubprogram(subProgram);
|
|
DbgLineData* dwStartLineData = NULL;
|
|
DbgLineData* dwEndLineData = NULL;
|
|
if (subProgram->mLineInfo != NULL)
|
|
{
|
|
if (subProgram->mLineInfo->mLines.size() > 0)
|
|
{
|
|
dwStartLineData = &subProgram->mLineInfo->mLines[0];
|
|
dwEndLineData = &subProgram->mLineInfo->mLines.back();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (subProgram->mInlineeInfo != NULL)
|
|
{
|
|
dwStartLineData = &subProgram->mInlineeInfo->mFirstLineData;
|
|
dwEndLineData = &subProgram->mInlineeInfo->mLastLineData;
|
|
}
|
|
}
|
|
|
|
if (dwEndLineData != NULL)
|
|
{
|
|
*outDefLineStart = dwStartLineData->mLine;
|
|
*outDefLineEnd = dwEndLineData->mLine;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinDebugger::GetStackAllocInfo(intptr addr, int* outThreadId, int* outStackIdx)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
*outThreadId = 0;
|
|
if (outStackIdx != NULL)
|
|
*outStackIdx = -1;
|
|
|
|
if (!IsPaused())
|
|
return;
|
|
|
|
for (auto thread : mThreadList)
|
|
{
|
|
NT_TIB64 tib = { 0 };
|
|
if (!ReadMemory((intptr)thread->mThreadLocalBase, sizeof(tib), &tib))
|
|
continue;
|
|
|
|
MEMORY_BASIC_INFORMATION stackInfo = { 0 };
|
|
if (VirtualQueryEx(mProcessInfo.hProcess, (void*)(tib.StackBase - 1), &stackInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0)
|
|
continue;
|
|
|
|
if ((addr >= (intptr)stackInfo.AllocationBase) && (addr < (intptr)tib.StackBase))
|
|
{
|
|
*outThreadId = thread->mThreadId;
|
|
|
|
if (outStackIdx == NULL)
|
|
return;
|
|
|
|
if (mActiveThread == thread)
|
|
{
|
|
UpdateCallStack(false);
|
|
for (int callStackIdx = 0; callStackIdx < (int)mCallStack.size(); callStackIdx++)
|
|
{
|
|
UpdateCallStackMethod(callStackIdx);
|
|
|
|
auto stackFrame = mCallStack[callStackIdx];
|
|
if (addr >= (intptr)stackFrame->mRegisters.GetSP())
|
|
{
|
|
*outStackIdx = callStackIdx;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
String WinDebugger::GetStackFrameInfo(int stackFrameIdx, intptr* addr, String* outFile, int* outHotIdx, int* outDefLineStart, int* outDefLineEnd,
|
|
int* outLine, int* outColumn, int* outLanguage, int* outStackSize, int8* outFlags)
|
|
{
|
|
enum FrameFlags
|
|
{
|
|
FrameFlags_Optimized = 1,
|
|
FrameFlags_HasPendingDebugInfo = 2,
|
|
FrameFlags_CanGetOldSource = 4,
|
|
FrameFlags_WasHotReplaced = 8,
|
|
FrameFlags_HadError = 0x10
|
|
};
|
|
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (mCallStack.size() == 0)
|
|
UpdateCallStack();
|
|
|
|
*addr = 0;
|
|
*outFile = "";
|
|
*outHotIdx = 0;
|
|
*outDefLineStart = -1;
|
|
*outDefLineEnd = -1;
|
|
*outLine = -1;
|
|
*outColumn = 0;
|
|
*outLanguage = 0;
|
|
*outStackSize = 0;
|
|
*outFlags = 0;
|
|
|
|
UpdateCallStackMethod(stackFrameIdx);
|
|
|
|
if (stackFrameIdx >= mCallStack.size())
|
|
{
|
|
return "";
|
|
}
|
|
|
|
int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
|
|
|
|
UpdateCallStackMethod(actualStackFrameIdx);
|
|
WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];
|
|
|
|
addr_target pcAddress = (addr_target)wdStackFrame->mRegisters.GetPC();
|
|
if (stackFrameIdx == -1)
|
|
pcAddress = mShowPCOverride;
|
|
|
|
*addr = pcAddress;
|
|
|
|
if (actualStackFrameIdx < (int)mCallStack.size() - 2)
|
|
{
|
|
WdStackFrame* prevStackFrame = mCallStack[actualStackFrameIdx + 1];
|
|
// Inlined methods have no stack frame
|
|
*outStackSize = prevStackFrame->mRegisters.GetSP() - wdStackFrame->mRegisters.GetSP();
|
|
}
|
|
|
|
const auto& _CheckHashSrcFile = [&](String& outStr, DbgModule* dbgModule, DbgSrcFile* srcFile)
|
|
{
|
|
if (srcFile->mHashKind != DbgHashKind_None)
|
|
{
|
|
outStr += "#";
|
|
srcFile->GetHash(outStr);
|
|
}
|
|
};
|
|
|
|
auto _SetFlags = [&](DbgSubprogram* dwSubprogram)
|
|
{
|
|
DbgModule* dbgModule = dwSubprogram->mCompileUnit->mDbgModule;
|
|
if (dwSubprogram->mIsOptimized)
|
|
*outFlags |= FrameFlags_Optimized;
|
|
if (dbgModule->HasPendingDebugInfo())
|
|
*outFlags |= FrameFlags_HasPendingDebugInfo;
|
|
if (dbgModule->CanGetOldSource())
|
|
*outFlags |= FrameFlags_CanGetOldSource;
|
|
if ((dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) || (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Invalid))
|
|
*outFlags |= FrameFlags_WasHotReplaced;
|
|
};
|
|
|
|
auto _FixFilePath = [&](DbgModule* dbgModule)
|
|
{
|
|
if (outFile == NULL)
|
|
return;
|
|
|
|
if (outFile->StartsWith("$Emit"))
|
|
{
|
|
int dollarPos = outFile->IndexOf('$', 1);
|
|
if (dollarPos == -1)
|
|
return;
|
|
|
|
outFile->Insert(dollarPos, StrFormat("%d", dbgModule->mId));
|
|
}
|
|
};
|
|
|
|
if (wdStackFrame->mInInlineMethod)
|
|
{
|
|
WdStackFrame* nextStackFrame = mCallStack[actualStackFrameIdx - 1];
|
|
auto subProgram = nextStackFrame->mSubProgram;
|
|
|
|
_SetFlags(subProgram);
|
|
|
|
FixupLineDataForSubprogram(subProgram->mInlineeInfo->mRootInliner);
|
|
DbgSubprogram* parentSubprogram = subProgram->mInlineeInfo->mInlineParent; // Require it be in the inline parent
|
|
auto foundLine = parentSubprogram->FindClosestLine(subProgram->mBlock.mLowPC, &parentSubprogram);
|
|
if (foundLine != NULL)
|
|
{
|
|
auto srcFile = parentSubprogram->GetLineSrcFile(*foundLine);
|
|
*outFile = srcFile->GetLocalPath();
|
|
_CheckHashSrcFile(*outFile, subProgram->mCompileUnit->mDbgModule, srcFile);
|
|
*outLine = foundLine->mLine;
|
|
}
|
|
|
|
*outLanguage = subProgram->GetLanguage();
|
|
*outHotIdx = subProgram->mCompileUnit->mDbgModule->mHotIdx;
|
|
*outColumn = -1;
|
|
|
|
DbgSubprogram* callingSubProgram = NULL;
|
|
DbgLineData* callingLineData = FindLineDataAtAddress(nextStackFrame->mSubProgram->mBlock.mLowPC - 1, &callingSubProgram);
|
|
if ((callingLineData != NULL) && (callingSubProgram == wdStackFrame->mSubProgram))
|
|
{
|
|
auto callingSrcFile = callingSubProgram->GetLineSrcFile(*callingLineData);
|
|
*outLanguage = callingSubProgram->mCompileUnit->mLanguage;
|
|
auto srcFile = callingSrcFile;
|
|
*outFile = srcFile->GetLocalPath();
|
|
|
|
_CheckHashSrcFile(*outFile, subProgram->mCompileUnit->mDbgModule, srcFile);
|
|
if (*outLine == callingLineData->mLine)
|
|
*outColumn = callingLineData->mColumn;
|
|
}
|
|
|
|
String name = wdStackFrame->mSubProgram->ToString();
|
|
DbgModule* dbgModule = wdStackFrame->mSubProgram->mCompileUnit->mDbgModule;
|
|
DbgModule* linkedModule = dbgModule->GetLinkedModule();
|
|
if (!linkedModule->mDisplayName.empty())
|
|
name = linkedModule->mDisplayName + "!" + name;
|
|
_FixFilePath(dbgModule);
|
|
return name;
|
|
}
|
|
|
|
DbgSubprogram* dwSubprogram = wdStackFrame->mSubProgram;
|
|
if (dwSubprogram != NULL)
|
|
{
|
|
String demangledName;
|
|
if ((dwSubprogram->mName != NULL) && (strncmp(dwSubprogram->mName, ":Sep@", 5) == 0))
|
|
{
|
|
char* p;
|
|
auto addr = strtoll(dwSubprogram->mName + 5, &p, 16);
|
|
if (addr != 0)
|
|
{
|
|
auto parentSubprogram = mDebugTarget->FindSubProgram(addr);
|
|
if (parentSubprogram != NULL)
|
|
demangledName = parentSubprogram->ToString();
|
|
}
|
|
}
|
|
if (demangledName.IsEmpty())
|
|
{
|
|
dwSubprogram->ToString(demangledName, true);
|
|
}
|
|
|
|
DbgSrcFile* dwSrcFile = NULL;
|
|
DbgLineData* dwLineData = NULL;
|
|
|
|
FixupLineDataForSubprogram(dwSubprogram);
|
|
addr_target findAddress = wdStackFrame->GetSourcePC();
|
|
DbgSubprogram* specificSubprogram = dwSubprogram;
|
|
dwLineData = dwSubprogram->FindClosestLine(findAddress, &specificSubprogram);
|
|
|
|
if ((dwLineData == NULL) && (dwSubprogram->mInlineeInfo != NULL) && (findAddress >= dwSubprogram->mBlock.mHighPC))
|
|
dwLineData = &dwSubprogram->mInlineeInfo->mLastLineData;
|
|
|
|
if (dwLineData != NULL)
|
|
dwSrcFile = dwSubprogram->GetLineSrcFile(*dwLineData);
|
|
|
|
DbgLineData* dwStartLineData = NULL;
|
|
DbgLineData* dwEndLineData = NULL;
|
|
if (dwSubprogram->mLineInfo != NULL)
|
|
{
|
|
if (dwSubprogram->mLineInfo->mLines.size() > 0)
|
|
{
|
|
dwStartLineData = &dwSubprogram->mLineInfo->mLines[0];
|
|
dwEndLineData = &dwSubprogram->mLineInfo->mLines.back();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dwSubprogram->mInlineeInfo != NULL)
|
|
{
|
|
dwStartLineData = &dwSubprogram->mInlineeInfo->mFirstLineData;
|
|
dwEndLineData = &dwSubprogram->mInlineeInfo->mLastLineData;
|
|
}
|
|
}
|
|
|
|
DbgModule* dbgModule = dwSubprogram->mCompileUnit->mDbgModule;
|
|
DbgModule* linkedModule = dbgModule->GetLinkedModule();
|
|
if (!linkedModule->mDisplayName.empty())
|
|
demangledName = linkedModule->mDisplayName + "!" + demangledName;
|
|
|
|
if ((dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) || (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Invalid))
|
|
demangledName = "#" + demangledName;
|
|
|
|
_SetFlags(dwSubprogram);
|
|
|
|
if ((dwLineData != NULL) && (dwSrcFile != NULL))
|
|
{
|
|
*outFile = dwSrcFile->GetLocalPath();
|
|
_CheckHashSrcFile(*outFile, dbgModule, dwSrcFile);
|
|
|
|
*outHotIdx = dbgModule->mHotIdx;
|
|
*outLine = dwLineData->mLine;
|
|
*outColumn = dwLineData->mColumn;
|
|
*outLanguage = (int)dwSubprogram->mCompileUnit->mLanguage;
|
|
|
|
if (dwEndLineData != NULL)
|
|
{
|
|
*outDefLineStart = dwStartLineData->mLine;
|
|
*outDefLineEnd = dwEndLineData->mLine;
|
|
}
|
|
|
|
_FixFilePath(dbgModule);
|
|
return demangledName;
|
|
}
|
|
else
|
|
{
|
|
_FixFilePath(dbgModule);
|
|
return demangledName + StrFormat("+0x%X", pcAddress - dwSubprogram->mBlock.mLowPC);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
String symbolName;
|
|
addr_target offset;
|
|
DbgModule* dbgModule = NULL;
|
|
if (mDebugTarget->FindSymbolAt(pcAddress, &symbolName, &offset, &dbgModule))
|
|
{
|
|
if (dbgModule->HasPendingDebugInfo())
|
|
{
|
|
*outFlags |= FrameFlags_HasPendingDebugInfo;
|
|
|
|
if (mPendingDebugInfoLoad.ContainsKey(dbgModule))
|
|
{
|
|
String outName = EncodeDataPtr(pcAddress, true);
|
|
if ((dbgModule != NULL) && (!dbgModule->mDisplayName.empty()))
|
|
outName = dbgModule->mDisplayName + "!<Loading...>" + outName;
|
|
return outName;
|
|
}
|
|
}
|
|
|
|
DbgModule* linkedModule = dbgModule->GetLinkedModule();
|
|
String demangledName = BfDemangler::Demangle(symbolName, DbgLanguage_Unknown);
|
|
if (!linkedModule->mDisplayName.empty())
|
|
demangledName = linkedModule->mDisplayName + "!" + demangledName;
|
|
_FixFilePath(dbgModule);
|
|
return demangledName + StrFormat("+0x%X", offset);
|
|
}
|
|
}
|
|
|
|
DbgModule* dbgModule = mDebugTarget->FindDbgModuleForAddress(pcAddress);
|
|
DbgModule* linkedModule = NULL;
|
|
if (dbgModule != NULL)
|
|
{
|
|
linkedModule = dbgModule->GetLinkedModule();
|
|
if (dbgModule->HasPendingDebugInfo())
|
|
*outFlags |= FrameFlags_HasPendingDebugInfo;
|
|
}
|
|
String outName = EncodeDataPtr(pcAddress, true);
|
|
if ((linkedModule != NULL) && (!linkedModule->mDisplayName.empty()))
|
|
outName = linkedModule->mDisplayName + "!" + outName;
|
|
_FixFilePath(dbgModule);
|
|
return outName;
|
|
}
|
|
|
|
String WinDebugger::GetStackFrameId(int stackFrameIdx)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!FixCallStackIdx(stackFrameIdx))
|
|
return "";
|
|
|
|
int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
|
|
UpdateCallStackMethod(actualStackFrameIdx);
|
|
WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];
|
|
|
|
intptr addr = 0;
|
|
if (wdStackFrame->mSubProgram != NULL)
|
|
addr = wdStackFrame->mSubProgram->mBlock.mLowPC;
|
|
else
|
|
addr = wdStackFrame->mRegisters.GetPC();
|
|
|
|
String str = StrFormat("Thread:%d SP:%llX Func:%llX", mActiveThread->mThreadId, wdStackFrame->mRegisters.GetSP(), addr);
|
|
return str;
|
|
}
|
|
|
|
String WinDebugger::Callstack_GetStackFrameOldFileInfo(int stackFrameIdx)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!FixCallStackIdx(stackFrameIdx))
|
|
return "";
|
|
|
|
int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
|
|
UpdateCallStackMethod(actualStackFrameIdx);
|
|
WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];
|
|
|
|
DbgModule* dbgModule = NULL;
|
|
DbgSrcFile* dbgSrcFile = NULL;
|
|
|
|
if (wdStackFrame->mInInlineMethod)
|
|
{
|
|
WdStackFrame* nextStackFrame = mCallStack[actualStackFrameIdx - 1];
|
|
auto subProgram = nextStackFrame->mSubProgram;
|
|
dbgModule = subProgram->mCompileUnit->mDbgModule;
|
|
|
|
FixupLineDataForSubprogram(subProgram->mInlineeInfo->mRootInliner);
|
|
DbgSubprogram* parentSubprogram = subProgram->mInlineeInfo->mInlineParent; // Require it be in the inline parent
|
|
auto foundLine = parentSubprogram->FindClosestLine(subProgram->mBlock.mLowPC, &parentSubprogram);
|
|
if (foundLine != NULL)
|
|
dbgSrcFile = parentSubprogram->GetLineSrcFile(*foundLine);
|
|
|
|
DbgSubprogram* callingSubProgram = NULL;
|
|
DbgLineData* callingLineData = FindLineDataAtAddress(nextStackFrame->mSubProgram->mBlock.mLowPC - 1, &callingSubProgram);
|
|
if ((callingLineData != NULL) && (callingSubProgram == wdStackFrame->mSubProgram))
|
|
dbgSrcFile = callingSubProgram->GetLineSrcFile(*callingLineData);
|
|
}
|
|
else
|
|
{
|
|
DbgSubprogram* dwSubprogram = wdStackFrame->mSubProgram;
|
|
if (dwSubprogram != NULL)
|
|
{
|
|
FixupLineDataForSubprogram(dwSubprogram);
|
|
addr_target findAddress = wdStackFrame->GetSourcePC();
|
|
|
|
DbgSubprogram* dbgSubprogram = NULL;
|
|
DbgLineData* dwLineData = dwSubprogram->FindClosestLine(findAddress, &dbgSubprogram, &dbgSrcFile);
|
|
dbgModule = dwSubprogram->mCompileUnit->mDbgModule;
|
|
}
|
|
}
|
|
|
|
if (dbgSrcFile != NULL)
|
|
{
|
|
// Note: we must use mFilePath here, make sure we don't use GetLocalPath()
|
|
return dbgModule->GetOldSourceCommand(dbgSrcFile->mFilePath);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
int WinDebugger::GetJmpState(int stackFrameIdx)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!FixCallStackIdx(stackFrameIdx))
|
|
return -1;
|
|
|
|
int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
|
|
UpdateCallStackMethod(actualStackFrameIdx);
|
|
WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];
|
|
|
|
addr_target pcAddress = (addr_target)wdStackFrame->mRegisters.GetPC();
|
|
|
|
CPUInst inst;
|
|
if (!mDebugTarget->DecodeInstruction(pcAddress, &inst))
|
|
return -1;
|
|
|
|
return inst.GetJmpState(wdStackFrame->mRegisters.mIntRegs.efl);
|
|
}
|
|
|
|
intptr WinDebugger::GetStackFrameCalleeAddr(int stackFrameIdx)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!FixCallStackIdx(stackFrameIdx))
|
|
return -1;
|
|
|
|
int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
|
|
UpdateCallStackMethod(actualStackFrameIdx);
|
|
WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];
|
|
|
|
addr_target pcAddress = (addr_target)wdStackFrame->mRegisters.GetPC();
|
|
if (stackFrameIdx == -1)
|
|
pcAddress = mShowPCOverride;
|
|
|
|
if (wdStackFrame->mInInlineMethod)
|
|
{
|
|
WdStackFrame* inlineStackFrame = mCallStack[actualStackFrameIdx - 1];
|
|
return inlineStackFrame->mSubProgram->mBlock.mLowPC - 1;
|
|
}
|
|
|
|
return pcAddress - 1;
|
|
}
|
|
|
|
String WinDebugger::GetStackMethodOwner(int stackFrameIdx, int& language)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
if (!FixCallStackIdx(stackFrameIdx))
|
|
return "";
|
|
|
|
int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
|
|
if (actualStackFrameIdx >= (int)mCallStack.size())
|
|
actualStackFrameIdx = 0;
|
|
UpdateCallStackMethod(actualStackFrameIdx);
|
|
WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];
|
|
|
|
if (wdStackFrame->mSubProgram == NULL)
|
|
return "";
|
|
auto parentType = wdStackFrame->mSubProgram->GetParent();
|
|
if (parentType == NULL)
|
|
return "";
|
|
parentType = parentType->GetPrimaryType();
|
|
language = (int)parentType->GetLanguage();
|
|
return parentType->ToString();
|
|
}
|
|
|
|
String WinDebugger::FindCodeAddresses(const StringImpl& fileName, int line, int column, bool allowAutoResolve)
|
|
{
|
|
String result;
|
|
if (mDebugTarget == NULL)
|
|
return "";
|
|
|
|
DbgSrcFile* srcFile = mDebugTarget->GetSrcFile(fileName);
|
|
if (srcFile == NULL)
|
|
return result;
|
|
|
|
bool foundInSequence = false;
|
|
WdBreakpoint* prevBreakpoint = NULL;
|
|
|
|
int bestLineOffset = 0x7FFFFFFF;
|
|
|
|
for (auto dbgSubprogram : srcFile->mLineDataRefs)
|
|
{
|
|
for (auto& lineData : dbgSubprogram->mLineInfo->mLines)
|
|
{
|
|
auto lineSrcFile = dbgSubprogram->GetLineSrcFile(lineData);
|
|
if (lineSrcFile != srcFile)
|
|
continue;
|
|
int lineOffset = lineData.mLine - line;
|
|
|
|
if ((lineOffset >= 0) && (lineOffset <= 12) && (lineOffset <= bestLineOffset))
|
|
{
|
|
if (lineOffset < bestLineOffset)
|
|
{
|
|
bestLineOffset = lineOffset;
|
|
result = "";
|
|
}
|
|
|
|
if (!foundInSequence)
|
|
{
|
|
auto addr = dbgSubprogram->GetLineAddr(lineData);
|
|
result += EncodeDataPtr(addr, false) + "\t" + dbgSubprogram->ToString() + "\n";
|
|
}
|
|
}
|
|
|
|
// New sequence?
|
|
if (!lineData.IsStackFrameSetup())
|
|
foundInSequence = false;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
String WinDebugger::GetAddressSourceLocation(intptr address)
|
|
{
|
|
DbgSubprogram* subProgram = NULL;
|
|
DbgLineData* lineData = FindLineDataAtAddress(address, &subProgram);
|
|
if (lineData != NULL)
|
|
return StrFormat("%s:%d:%d", subProgram->GetLineSrcFile(*lineData)->GetLocalPath().c_str(), lineData->mLine + 1, lineData->mColumn + 1);
|
|
|
|
String outSymbol;
|
|
addr_target offset = 0;
|
|
DbgModule* dbgModule;
|
|
if (mDebugTarget->FindSymbolAt(address, &outSymbol, &offset, &dbgModule))
|
|
{
|
|
if (offset < 0x10000)
|
|
{
|
|
outSymbol = BfDemangler::Demangle(outSymbol, DbgLanguage_Unknown);
|
|
if (offset > 0)
|
|
outSymbol += StrFormat("+%x", offset);
|
|
return outSymbol;
|
|
}
|
|
}
|
|
|
|
return StrFormat("0x%@", address);
|
|
}
|
|
|
|
String WinDebugger::GetAddressSymbolName(intptr address, bool demangle)
|
|
{
|
|
auto subProgram = mDebugTarget->FindSubProgram(address);
|
|
if (subProgram != NULL)
|
|
return subProgram->ToString();
|
|
|
|
String outSymbol;
|
|
addr_target offset = 0;
|
|
DbgModule* dbgModule;
|
|
if (mDebugTarget->FindSymbolAt(address, &outSymbol, &offset, &dbgModule))
|
|
{
|
|
if (offset < 0x10000)
|
|
{
|
|
if (demangle)
|
|
outSymbol = BfDemangler::Demangle(outSymbol, DbgLanguage_Unknown);
|
|
if (offset > 0)
|
|
outSymbol += StrFormat("+%x", offset);
|
|
return outSymbol;
|
|
}
|
|
}
|
|
|
|
return StrFormat("0x%@", address);
|
|
}
|
|
|
|
String WinDebugger::DisassembleAtRaw(intptr inAddress)
|
|
{
|
|
addr_target address = (addr_target)inAddress;
|
|
|
|
const int addrBorder = 1024;
|
|
|
|
for (int offset = 0; offset < 8; offset++)
|
|
{
|
|
String result;
|
|
bool addOffset = true;
|
|
bool hadAddr = false;
|
|
|
|
DbgModule* dbgModule = mDebugTarget->FindDbgModuleForAddress(address);
|
|
DbgModuleMemoryCache* memCache = NULL;
|
|
defer
|
|
(
|
|
if (dbgModule == NULL)
|
|
delete memCache;
|
|
);
|
|
|
|
if ((dbgModule != NULL) && (dbgModule->mOrigImageData == NULL))
|
|
dbgModule = NULL;
|
|
|
|
result += "R\n"; // Raw
|
|
|
|
addr_target addrStart = address;
|
|
if (dbgModule != NULL)
|
|
{
|
|
dbgModule->ParseSymbolData();
|
|
memCache = dbgModule->mOrigImageData;
|
|
addrStart = BF_MAX((addr_target)dbgModule->mImageBase, address - addrBorder - offset);
|
|
}
|
|
else
|
|
{
|
|
memCache = new DbgModuleMemoryCache(addrStart & (4096 - 1), 4096 * 2);
|
|
}
|
|
|
|
if (memCache->mAddr == 0)
|
|
return "";
|
|
|
|
//addr_target imageBase = dbgModule->mImageBase;
|
|
//int imageSize = dbgModule->mImageSize;
|
|
|
|
addr_target dataAddr = addrStart;
|
|
addr_target addrEnd = addrStart + addrBorder * 2 + 16;
|
|
|
|
while (dataAddr < addrEnd)
|
|
{
|
|
if (dataAddr == address)
|
|
hadAddr = true;
|
|
if (dataAddr > address)
|
|
{
|
|
if (!hadAddr)
|
|
{
|
|
if (offset == 7)
|
|
{
|
|
dataAddr = address;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
String outSymbol;
|
|
addr_target symOffset = 0;
|
|
DbgModule* symDWARF;
|
|
|
|
if (mDebugTarget->FindSymbolAt(dataAddr, &outSymbol, &symOffset, &symDWARF))
|
|
{
|
|
if (symOffset == 0)
|
|
{
|
|
outSymbol = BfDemangler::Demangle(outSymbol, DbgLanguage_Unknown);
|
|
if ((symDWARF != NULL) && (!symDWARF->mDisplayName.empty()))
|
|
outSymbol = symDWARF->GetLinkedModule()->mDisplayName + "!" + outSymbol;
|
|
result += "T " + outSymbol + ":\n";
|
|
}
|
|
}
|
|
|
|
CPUInst inst;
|
|
if (!mCPU->Decode(dataAddr, memCache, &inst))
|
|
{
|
|
if ((offset == 7) && (!hadAddr))
|
|
{
|
|
uint8 instData[1];
|
|
memCache->Read(dataAddr, instData, 1);
|
|
int instLen = 1;
|
|
|
|
#ifdef BF_DBG_32
|
|
result += StrFormat("D %08X: ", dataAddr);
|
|
#else
|
|
result += StrFormat("D %@: ", dataAddr);
|
|
#endif
|
|
for (int i = 0; i < instLen; i++)
|
|
result += StrFormat("%02X ", instData[i]);
|
|
for (int i = instLen; i < 8; i++)
|
|
result += " ";
|
|
result += "\n";
|
|
dataAddr++;
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
int instLen = inst.GetLength();
|
|
|
|
#ifdef BF_DBG_32
|
|
result += StrFormat("D %08X: ", dataAddr);
|
|
#else
|
|
result += StrFormat("D %@: ", dataAddr);
|
|
#endif
|
|
|
|
uint8 instData[32];
|
|
int showInstLen = BF_MIN(32, instLen);
|
|
memCache->Read(dataAddr, instData, showInstLen);
|
|
|
|
for (int i = 0; i < showInstLen; i++)
|
|
result += StrFormat("%02X ", instData[i]);
|
|
for (int i = instLen; i < 8; i++)
|
|
result += " ";
|
|
|
|
result += mCPU->InstructionToString(&inst, dataAddr);
|
|
|
|
if ((inst.IsCall()) || (inst.IsBranch()))
|
|
{
|
|
addr_target targetAddr = inst.GetTarget();
|
|
if (targetAddr != 0)
|
|
{
|
|
if (mDebugTarget->FindSymbolAt(targetAddr, &outSymbol, &symOffset))
|
|
{
|
|
if (symOffset < 0x10000)
|
|
{
|
|
outSymbol = BfDemangler::Demangle(outSymbol, DbgLanguage_Unknown);
|
|
|
|
result += " ; " + outSymbol;
|
|
if (symOffset > 0)
|
|
result += StrFormat("+%x", symOffset);
|
|
//result += ">";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
result += "\n";
|
|
dataAddr += instLen;
|
|
}
|
|
|
|
if (!hadAddr)
|
|
continue;
|
|
|
|
return result;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
String WinDebugger::DisassembleAt(intptr inAddress)
|
|
{
|
|
BP_ZONE("WinDebugger::DisassembleAt");
|
|
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
addr_target address = (addr_target)inAddress;
|
|
|
|
if (mDebugTarget == NULL)
|
|
return "";
|
|
|
|
String result;
|
|
auto dwSubProgram = mDebugTarget->FindSubProgram(address);
|
|
if (dwSubProgram == NULL)
|
|
return DisassembleAtRaw(address);
|
|
dwSubProgram = dwSubProgram->GetRootInlineParent();
|
|
DbgModule* dwarf = dwSubProgram->mCompileUnit->mDbgModule;
|
|
|
|
int frameBaseRegister = mDebugTarget->GetFrameBaseRegister(dwSubProgram);
|
|
|
|
addr_target addrStart = dwSubProgram->mBlock.mLowPC;
|
|
addr_target addrEnd = dwSubProgram->mBlock.mHighPC;
|
|
|
|
auto dwCompileUnit = dwSubProgram->mCompileUnit;
|
|
{
|
|
FixupLineData(dwCompileUnit);
|
|
}
|
|
|
|
DbgSrcFile* dwSrcFile = NULL;
|
|
|
|
FixupLineDataForSubprogram(dwSubProgram);
|
|
|
|
DbgLineData* dwLineData = NULL;
|
|
if (dwSubProgram->mLineInfo != NULL)
|
|
dwLineData = &dwSubProgram->mLineInfo->mLines[0];
|
|
int nextLineDataIdx = 1;
|
|
|
|
if (dwSubProgram->mIsOptimized)
|
|
result += "O\n";
|
|
|
|
DbgSrcFile* srcFile = NULL;
|
|
int firstLine = 0;
|
|
int curLine = 0;
|
|
if (dwLineData != NULL)
|
|
{
|
|
srcFile = dwSubProgram->GetLineSrcFile(*dwLineData);
|
|
result += "S " + srcFile->GetLocalPath() + "\n";
|
|
if (srcFile->mHashKind != DbgHashKind_None)
|
|
{
|
|
result += "H ";
|
|
srcFile->GetHash(result);
|
|
result += "\n";
|
|
}
|
|
|
|
curLine = BF_MAX(0, dwLineData->mLine - 5);
|
|
//for (; curLine <= dwLineData->mLine; curLine++)
|
|
result += StrFormat("L %d %d\n", curLine, dwLineData->mLine - curLine + 1);
|
|
curLine = dwLineData->mLine + 1;
|
|
firstLine = dwLineData->mLine;
|
|
}
|
|
|
|
Array<DbgSubprogram*> inlineStack;
|
|
|
|
Array<DbgBlock*> blockList;
|
|
blockList.push_back(&dwSubProgram->mBlock);
|
|
|
|
addr_target dataAddr = addrStart;
|
|
int decodeFailureCount = 0;
|
|
|
|
auto& _PopInlineStack = [&]()
|
|
{
|
|
int depth = inlineStack.size();
|
|
auto curStackEntry = inlineStack.back();
|
|
if (depth > 1)
|
|
result += StrFormat("T <<<%d Inline End ", depth);
|
|
else
|
|
result += "T <<< Inline End ";
|
|
result += curStackEntry->ToString();
|
|
result += "\n";
|
|
inlineStack.pop_back();
|
|
};
|
|
|
|
std::function<void(DbgSubprogram* subprogram, int depth)> _UpdateInlineStackHelper = [&](DbgSubprogram* subprogram, int depth)
|
|
{
|
|
int stackIdx = depth - 1;
|
|
if (stackIdx < inlineStack.size())
|
|
{
|
|
auto curStackEntry = inlineStack[stackIdx];
|
|
if (curStackEntry != subprogram)
|
|
_PopInlineStack();
|
|
}
|
|
|
|
if (depth > 1)
|
|
{
|
|
_UpdateInlineStackHelper(subprogram->mInlineeInfo->mInlineParent, depth - 1);
|
|
}
|
|
|
|
if (stackIdx >= inlineStack.size())
|
|
{
|
|
if (depth > 1)
|
|
result += StrFormat("T >>>%d Inline ", depth);
|
|
else
|
|
result += "T >>> Inline ";
|
|
result += subprogram->ToString();
|
|
result += "\n";
|
|
inlineStack.push_back(subprogram);
|
|
}
|
|
};
|
|
|
|
auto _UpdateInlineStack = [&](DbgSubprogram* subprogram)
|
|
{
|
|
if (subprogram == NULL)
|
|
{
|
|
while (!inlineStack.IsEmpty())
|
|
_PopInlineStack();
|
|
return;
|
|
}
|
|
|
|
int inlineDepth = subprogram->GetInlineDepth();
|
|
while (inlineDepth < inlineStack.size())
|
|
_PopInlineStack();
|
|
if (inlineDepth > 0)
|
|
_UpdateInlineStackHelper(subprogram, inlineDepth);
|
|
};
|
|
|
|
while (dataAddr < addrEnd)
|
|
{
|
|
// Pop off old scopes
|
|
while (blockList.size() > 0)
|
|
{
|
|
auto lastBlock = blockList.back();
|
|
if (dataAddr < lastBlock->mHighPC)
|
|
break;
|
|
blockList.pop_back();
|
|
}
|
|
|
|
// Check entry into new child scopes
|
|
auto lastBlock = blockList.back();
|
|
for (auto checkBlock : lastBlock->mSubBlocks)
|
|
{
|
|
if ((dataAddr >= checkBlock->mLowPC) && (dataAddr < checkBlock->mHighPC))
|
|
{
|
|
blockList.push_back(checkBlock);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool allowSourceJump = false;
|
|
|
|
if ((dwLineData != NULL) && (dwLineData->mContribSize != 0) && (dataAddr >= dwSubProgram->GetLineAddr(*dwLineData) + dwLineData->mContribSize))
|
|
{
|
|
DbgSubprogram* inlinedSubprogram = NULL;
|
|
auto inlinedLine = dwSubProgram->FindClosestLine(dataAddr, &inlinedSubprogram);
|
|
|
|
_UpdateInlineStack(dwSubProgram);
|
|
}
|
|
|
|
// Update line data
|
|
while ((dwLineData != NULL) && (dwSubProgram->GetLineAddr(*dwLineData) <= dataAddr))
|
|
{
|
|
_UpdateInlineStack(dwSubProgram->GetLineInlinee(*dwLineData));
|
|
|
|
const int lineLimit = 5; // 15
|
|
|
|
if (allowSourceJump)
|
|
curLine = dwLineData->mLine;
|
|
|
|
auto lineSrcFile = dwSubProgram->GetLineSrcFile(*dwLineData);
|
|
|
|
if (lineSrcFile != srcFile)
|
|
{
|
|
srcFile = lineSrcFile;
|
|
result += "S ";
|
|
result += srcFile->GetLocalPath();
|
|
result += "\n";
|
|
// Just show the one line from the new file
|
|
curLine = dwLineData->mLine;
|
|
}
|
|
|
|
if (dwLineData->mLine < curLine - 1)
|
|
{
|
|
// Jumping backwards - possibly into inlined method, or possibly in current method.
|
|
// Show previous 6 lines, for context
|
|
curLine = BF_MAX(0, dwLineData->mLine - lineLimit);
|
|
}
|
|
|
|
if ((curLine <= firstLine) && (dwLineData->mLine >= firstLine))
|
|
{
|
|
// Jumping from inlined method (declared above) back into main method
|
|
curLine = dwLineData->mLine;
|
|
}
|
|
|
|
if (curLine < dwLineData->mLine - lineLimit)
|
|
{
|
|
// Don't show huge span of source - only show the last 6 lines at maximum
|
|
curLine = dwLineData->mLine - lineLimit;
|
|
}
|
|
|
|
//for ( ; curLine <= dwLineData->mLine; curLine++)
|
|
result += StrFormat("L %d %d\n", curLine, dwLineData->mLine - curLine + 1);
|
|
curLine = dwLineData->mLine + 1;
|
|
|
|
DbgLineData* nextLineData = NULL;
|
|
while (nextLineDataIdx < dwSubProgram->mLineInfo->mLines.mSize)
|
|
{
|
|
nextLineData = &dwSubProgram->mLineInfo->mLines[nextLineDataIdx];
|
|
|
|
//TODO:
|
|
/*{
|
|
result += StrFormat("T LineIdx: %d (%@ to %@)", nextLineDataIdx, dwSubProgram->GetLineAddr(*nextLineData), dwSubProgram->GetLineAddr(*nextLineData) + nextLineData->mContribSize);
|
|
auto inlinee = dwSubProgram->GetLineInlinee(*nextLineData);
|
|
if (inlinee != NULL)
|
|
{
|
|
result += StrFormat(" Inlinee: %s Depth: %d", inlinee->mName, inlinee->GetInlineDepth());
|
|
}
|
|
result += "\n";
|
|
}*/
|
|
|
|
auto nextLineAddr = dwSubProgram->GetLineAddr(*nextLineData);
|
|
if (nextLineAddr > dataAddr)
|
|
{
|
|
if (nextLineDataIdx + 1 < dwSubProgram->mLineInfo->mLines.mSize)
|
|
{
|
|
auto peekLineData = &dwSubProgram->mLineInfo->mLines[nextLineDataIdx + 1];
|
|
if (peekLineData->mRelAddress == nextLineData->mRelAddress)
|
|
{
|
|
// Use the later entry
|
|
++nextLineDataIdx;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
// If we go back to an older entry beacuse of a gap then we need to catch back up...
|
|
++nextLineDataIdx;
|
|
nextLineData = NULL; // Keep searching...
|
|
}
|
|
|
|
dwLineData = nextLineData;
|
|
nextLineDataIdx++;
|
|
}
|
|
|
|
// Have we gone off the end of the inline function?
|
|
// We may not have an explicit non-inlined line data at the transition point...
|
|
while (!inlineStack.IsEmpty())
|
|
{
|
|
auto subProgram = inlineStack.back();
|
|
if (dataAddr < subProgram->mBlock.mHighPC)
|
|
break;
|
|
_PopInlineStack();
|
|
}
|
|
|
|
bool hadDecodeFailure = false;
|
|
CPUInst inst;
|
|
if (!mCPU->Decode(dataAddr, dwarf->mOrigImageData, &inst))
|
|
hadDecodeFailure = true;
|
|
|
|
if ((decodeFailureCount == 8) || ((decodeFailureCount > 0) && (!hadDecodeFailure)))
|
|
{
|
|
for (int i = decodeFailureCount; i < 4 + sizeof(addr_target); i++)
|
|
result += " ";
|
|
result += " ???\n";
|
|
decodeFailureCount = 0;
|
|
}
|
|
|
|
if (decodeFailureCount == 0)
|
|
{
|
|
#ifdef BF_DBG_32
|
|
result += StrFormat("D %08X: ", dataAddr);
|
|
#else
|
|
result += StrFormat("D %@: ", dataAddr);
|
|
#endif
|
|
}
|
|
if (hadDecodeFailure)
|
|
{
|
|
uint8 byte = 0;
|
|
dwarf->mOrigImageData->Read(dataAddr, &byte, 1);
|
|
result += StrFormat("%02X ", byte);
|
|
dataAddr++;
|
|
decodeFailureCount++;
|
|
continue;
|
|
}
|
|
|
|
int instLen = inst.GetLength();
|
|
uint8 instData[32];
|
|
int showInstLen = BF_MIN(32, instLen);
|
|
dwarf->mOrigImageData->Read(dataAddr, instData, showInstLen);
|
|
|
|
for (int i = 0; i < showInstLen; i++)
|
|
result += StrFormat("%02X ", instData[i]);
|
|
|
|
for (int i = instLen; i < 4 + sizeof(addr_target); i++)
|
|
result += " ";
|
|
|
|
result += " ";
|
|
|
|
result += mCPU->InstructionToString(&inst, dataAddr);
|
|
|
|
int reg;
|
|
int offset;
|
|
if (inst.GetIndexRegisterAndOffset(®, &offset))
|
|
{
|
|
for (int blockIdx = (int)blockList.size() - 1; blockIdx >= 0; blockIdx--)
|
|
{
|
|
auto dwBlock = blockList[blockIdx];
|
|
for (auto variable : dwBlock->mVariables)
|
|
{
|
|
int varRegister;
|
|
int varOffset;
|
|
if (mDebugTarget->GetVariableIndexRegisterAndOffset(variable, &varRegister, &varOffset))
|
|
{
|
|
if (varRegister == -1)
|
|
varRegister = frameBaseRegister;
|
|
if ((reg == varRegister) && (offset == varOffset))
|
|
{
|
|
result += " ; ";
|
|
result += variable->mName;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ((inst.IsCall()) || (inst.IsBranch()) || (inst.IsLoadAddress()))
|
|
{
|
|
addr_target targetAddr = inst.GetTarget();
|
|
if (targetAddr != 0)
|
|
{
|
|
if ((targetAddr >= addrStart) && (targetAddr < addrEnd))
|
|
{
|
|
result += StrFormat("\nJ %s", EncodeDataPtr(targetAddr, false).c_str());
|
|
}
|
|
else
|
|
{
|
|
String outSymbol;
|
|
addr_target offset = 0;
|
|
if (mDebugTarget->FindSymbolAt(targetAddr, &outSymbol, &offset))
|
|
{
|
|
if (offset < 0x10000)
|
|
{
|
|
outSymbol = BfDemangler::Demangle(outSymbol, dwSubProgram->GetLanguage());
|
|
|
|
result += " ; " + outSymbol;
|
|
if (offset > 0)
|
|
result += StrFormat("+%x", offset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
result += "\n";
|
|
dataAddr += instLen;
|
|
}
|
|
|
|
// Why did we want to "show lines at end"??
|
|
// Show lines at end
|
|
/*if (curLine > 0)
|
|
{
|
|
for (int i = 0; i < 6; i++, curLine++)
|
|
result += StrFormat("L %d\n", curLine);
|
|
}*/
|
|
return result;
|
|
}
|
|
|
|
String WinDebugger::FindLineCallAddresses(intptr inAddress)
|
|
{
|
|
String callAddresses;
|
|
|
|
addr_target address = (addr_target)inAddress;
|
|
|
|
DbgSubprogram* dwSubprogram = NULL;
|
|
|
|
DbgLineData* startLineData = FindLineDataAtAddress(address, &dwSubprogram, NULL);
|
|
|
|
if (dwSubprogram == NULL)
|
|
return "";
|
|
|
|
CPURegisters registers;
|
|
PopulateRegisters(®isters);
|
|
|
|
auto inlinerSubprogram = dwSubprogram->GetRootInlineParent();
|
|
FixupLineDataForSubprogram(inlinerSubprogram);
|
|
|
|
if (inlinerSubprogram->mLineInfo->mLines.mSize == 0)
|
|
return "";
|
|
auto lineData = &inlinerSubprogram->mLineInfo->mLines[0];
|
|
|
|
addr_target addr = dwSubprogram->mBlock.mLowPC;
|
|
addr_target endAddr = dwSubprogram->mBlock.mHighPC;
|
|
|
|
DbgSubprogram* checkSubprogram = dwSubprogram;
|
|
DbgLineData* checkLineData = lineData;
|
|
addr_target checkLineAddr = 0;
|
|
|
|
int lineIdx = 0;
|
|
while (checkLineData != NULL)
|
|
{
|
|
//auto nextLineData = dwSubprogram->mCompileUnit->mLineDataMap.GetNext(checkLineData);
|
|
++lineIdx;
|
|
|
|
DbgLineData* nextLineData = NULL;
|
|
addr_target nextLineAddr;
|
|
if (lineIdx < inlinerSubprogram->mLineInfo->mLines.size())
|
|
{
|
|
nextLineData = &inlinerSubprogram->mLineInfo->mLines[lineIdx];
|
|
nextLineAddr = dwSubprogram->GetLineAddr(*nextLineData);
|
|
}
|
|
else
|
|
nextLineAddr = inlinerSubprogram->mBlock.mHighPC;
|
|
|
|
// This stuff doesn't make sense...
|
|
DbgSubprogram* nextSubProgram;
|
|
if (nextLineData != NULL)
|
|
{
|
|
if (nextLineAddr > dwSubprogram->mBlock.mHighPC)
|
|
break;
|
|
|
|
endAddr = nextLineAddr;
|
|
nextSubProgram = mDebugTarget->FindSubProgram(endAddr);
|
|
if (nextSubProgram != NULL)
|
|
{
|
|
auto dbgModule = nextSubProgram->mCompileUnit->mDbgModule;
|
|
dbgModule->ParseSymbolData();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nextSubProgram = dwSubprogram;
|
|
endAddr = dwSubprogram->mBlock.mHighPC;
|
|
}
|
|
|
|
auto _HandleSection = [&]()
|
|
{
|
|
while (addr < endAddr)
|
|
{
|
|
CPUInst inst;
|
|
if (!mDebugTarget->DecodeInstruction(addr, &inst))
|
|
break;
|
|
|
|
*registers.GetPCRegisterRef() = addr;
|
|
if (inst.IsCall())
|
|
{
|
|
bool addSymbol = true;
|
|
|
|
if (addr < (addr_target)inAddress)
|
|
callAddresses += "-";
|
|
callAddresses += EncodeDataPtr(addr, false);
|
|
|
|
addr_target targetAddr = inst.GetTarget(this, ®isters);
|
|
if (targetAddr != 0)
|
|
{
|
|
String outSymbol;
|
|
auto subprogram = mDebugTarget->FindSubProgram(targetAddr);
|
|
if (subprogram != NULL)
|
|
{
|
|
CreateFilterName(outSymbol, subprogram);
|
|
addSymbol = true;
|
|
}
|
|
else
|
|
{
|
|
addr_target offset = 0;
|
|
String fullSymbolName;
|
|
if (mDebugTarget->FindSymbolAt(targetAddr, &outSymbol, &offset))
|
|
{
|
|
if (offset < 0x200)
|
|
{
|
|
//outSymbol = BfDemangler::Demangle(outSymbol, dwSubprogram->GetLanguage());
|
|
if (outSymbol == "___chkstk_ms")
|
|
addSymbol = false;
|
|
else
|
|
{
|
|
String demangledName = BfDemangler::Demangle(outSymbol, DbgLanguage_C);
|
|
outSymbol.clear();
|
|
CreateFilterName(outSymbol, demangledName.c_str(), DbgLanguage_C);
|
|
}
|
|
}
|
|
else
|
|
outSymbol.clear();
|
|
}
|
|
}
|
|
|
|
if (addSymbol)
|
|
{
|
|
if (outSymbol.empty())
|
|
callAddresses += "\tFunc@" + EncodeDataPtr(targetAddr, false);
|
|
else
|
|
callAddresses += "\t" + outSymbol;
|
|
|
|
String attrs;
|
|
bool isFiltered = false;
|
|
if (subprogram != NULL)
|
|
{
|
|
subprogram->PopulateSubprogram();
|
|
isFiltered = subprogram->mIsStepFilteredDefault;
|
|
if (isFiltered)
|
|
attrs += "d"; // 'd' for default filtered
|
|
}
|
|
|
|
StepFilter* stepFilterPtr = NULL;
|
|
if (mDebugManager->mStepFilters.TryGetValue(outSymbol, &stepFilterPtr))
|
|
isFiltered = stepFilterPtr->IsFiltered(isFiltered);
|
|
|
|
if (isFiltered)
|
|
attrs += "f"; // 'f' for filter
|
|
|
|
if (!attrs.IsEmpty())
|
|
callAddresses += "\t" + attrs;
|
|
}
|
|
}
|
|
|
|
if (addSymbol)
|
|
callAddresses += "\n";
|
|
}
|
|
|
|
inst.PartialSimulate(this, ®isters);
|
|
addr += inst.GetLength();
|
|
}
|
|
};
|
|
|
|
// For inlining - only add calls that are found either directly in our main block (not an inlined block)
|
|
// But add inlined methods when their parent is our current block
|
|
if ((checkSubprogram == dwSubprogram) && (checkLineData->mLine == startLineData->mLine))
|
|
{
|
|
_HandleSection();
|
|
}
|
|
else if ((checkSubprogram->mInlineeInfo != NULL) && (checkSubprogram->mInlineeInfo->mInlineParent == dwSubprogram))
|
|
{
|
|
if (checkLineAddr == checkSubprogram->mBlock.mLowPC)
|
|
{
|
|
addr_target inlineStartAddr = checkSubprogram->mBlock.mLowPC;
|
|
|
|
// Find the calling line
|
|
DbgSubprogram* callingSubprogram = dwSubprogram;
|
|
auto checkLineData = dwSubprogram->FindClosestLine(inlineStartAddr, &callingSubprogram);
|
|
if ((checkLineData != NULL) && (checkLineData->mCtxIdx == startLineData->mCtxIdx) && (checkLineData->mLine == startLineData->mLine))
|
|
{
|
|
if (inlineStartAddr <= (addr_target)inAddress)
|
|
callAddresses += "-";
|
|
callAddresses += EncodeDataPtr(inlineStartAddr, false);
|
|
String outSymbol;
|
|
CreateFilterName(outSymbol, checkSubprogram);
|
|
callAddresses += "\t" + outSymbol;
|
|
|
|
bool isFiltered = dwSubprogram->mIsStepFilteredDefault;
|
|
StepFilter* stepFilterPtr;
|
|
if (mDebugManager->mStepFilters.TryGetValue(outSymbol, &stepFilterPtr))
|
|
isFiltered = stepFilterPtr->IsFiltered(isFiltered);
|
|
|
|
if (isFiltered)
|
|
callAddresses += "\tf"; // 'f' for filter
|
|
|
|
callAddresses += "\n";
|
|
}
|
|
|
|
// if (checkSubprogram->mBlock.mHighPC < endAddr)
|
|
// {
|
|
// addr = checkSubprogram->mBlock.mHighPC;
|
|
// _HandleSection();
|
|
// }
|
|
}
|
|
|
|
// If we have unattributed data after the end of an inlined method, add that
|
|
if ((endAddr > checkSubprogram->mBlock.mHighPC) && (nextSubProgram == dwSubprogram))
|
|
{
|
|
addr = checkSubprogram->mBlock.mHighPC;
|
|
_HandleSection();
|
|
}
|
|
}
|
|
|
|
checkLineData = nextLineData;
|
|
checkSubprogram = nextSubProgram;
|
|
checkLineAddr = nextLineAddr;
|
|
addr = endAddr;
|
|
}
|
|
|
|
return callAddresses;
|
|
}
|
|
|
|
String WinDebugger::GetCurrentException()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
String result = StrFormat("%s\n%08X",
|
|
EncodeDataPtr((addr_target)(intptr)mCurException.ExceptionAddress, true).c_str(),
|
|
mCurException.ExceptionCode);
|
|
|
|
String exStr;
|
|
switch (mCurException.ExceptionCode)
|
|
{
|
|
case EXCEPTION_ACCESS_VIOLATION:
|
|
{
|
|
String accessType;
|
|
if (mCurException.ExceptionInformation[0] == 0)
|
|
accessType = "reading from";
|
|
else if (mCurException.ExceptionInformation[0] == 8)
|
|
accessType = "executing";
|
|
else
|
|
accessType = "writing to";
|
|
exStr = StrFormat("EXCEPTION_ACCESS_VIOLATION %s %s", accessType.c_str(), EncodeDataPtr((addr_target)mCurException.ExceptionInformation[1], true).c_str());
|
|
}
|
|
break;
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
|
exStr = "EXCEPTION_DATATYPE_MISALIGNMENT";
|
|
case EXCEPTION_SINGLE_STEP:
|
|
exStr = "EXCEPTION_SINGLE_STEP";
|
|
break;
|
|
case EXCEPTION_BREAKPOINT:
|
|
exStr = "EXCEPTION_BREAKPOINT";
|
|
break;
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
|
exStr = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
|
|
break;
|
|
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
|
exStr = "EXCEPTION_FLT_DENORMAL_OPERAND";
|
|
break;
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
|
exStr = "EXCEPTION_FLT_DIVIDE_BY_ZERO";
|
|
break;
|
|
case EXCEPTION_FLT_INEXACT_RESULT:
|
|
exStr = "EXCEPTION_FLT_INEXACT_RESULT";
|
|
break;
|
|
case EXCEPTION_FLT_INVALID_OPERATION:
|
|
exStr = "EXCEPTION_FLT_INVALID_OPERATIO";
|
|
break;
|
|
case EXCEPTION_FLT_OVERFLOW:
|
|
exStr = "EXCEPTION_FLT_OVERFLOW";
|
|
break;
|
|
case EXCEPTION_FLT_STACK_CHECK:
|
|
exStr = "EXCEPTION_FLT_STACK_CHECK";
|
|
break;
|
|
case EXCEPTION_FLT_UNDERFLOW:
|
|
exStr = "EXCEPTION_FLT_UNDERFLOW";
|
|
break;
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
|
exStr = "EXCEPTION_INT_DIVIDE_BY_ZERO";
|
|
break;
|
|
case EXCEPTION_INT_OVERFLOW:
|
|
exStr = "EXCEPTION_INT_OVERFLOW";
|
|
break;
|
|
case EXCEPTION_PRIV_INSTRUCTION:
|
|
exStr = "EXCEPTION_PRIV_INSTRUCTION";
|
|
break;
|
|
case EXCEPTION_IN_PAGE_ERROR:
|
|
exStr = "EXCEPTION_IN_PAGE_ERROR";
|
|
break;
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
|
exStr = "EXCEPTION_ILLEGAL_INSTRUCTION";
|
|
break;
|
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
|
exStr = "EXCEPTION_NONCONTINUABLE_EXCEPTION";
|
|
break;
|
|
case EXCEPTION_STACK_OVERFLOW:
|
|
exStr = "EXCEPTION_STACK_OVERFLOW";
|
|
break;
|
|
case EXCEPTION_INVALID_DISPOSITION:
|
|
exStr = "EXCEPTION_INVALID_DISPOSITION";
|
|
break;
|
|
case EXCEPTION_GUARD_PAGE:
|
|
exStr = "EXCEPTION_GUARD_PAGE";
|
|
break;
|
|
case EXCEPTION_INVALID_HANDLE:
|
|
exStr = "EXCEPTION_INVALID_HANDLE";
|
|
break;
|
|
case CONTROL_C_EXIT:
|
|
exStr = "CONTROL_C_EXIT";
|
|
break;
|
|
default:
|
|
exStr += StrFormat("EXCEPTION %08X", mCurException.ExceptionCode);
|
|
}
|
|
if (mActiveThread != NULL)
|
|
exStr += StrFormat(" in thread %d", mActiveThread->mThreadId);
|
|
|
|
if (!exStr.empty())
|
|
result += "\n" + exStr;
|
|
|
|
// After we retrieve the exception then we can go back to just being normal 'paused'
|
|
// This allows us to evaluate stuff, Set Next Statement, etc.
|
|
mRunState = RunState_Paused;
|
|
|
|
return result.c_str();
|
|
}
|
|
|
|
void WinDebugger::SetAliasPath(const StringImpl& origPath, const StringImpl& localPath)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
String fixedOrigPath = FixPathAndCase(origPath);
|
|
String fixedLocalPath = FixPathAndCase(localPath);
|
|
|
|
auto origFile = mDebugTarget->AddSrcFile(origPath);
|
|
origFile->mLocalPath = FixPath(localPath);
|
|
|
|
mDebugTarget->mLocalToOrigSrcMap[fixedLocalPath] = fixedOrigPath;
|
|
// We invalidate the step filters, because previously-failing 'CheckSourceFileExist' checks may now succeed
|
|
mDebugManager->mStepFilterVersion++;
|
|
}
|
|
|
|
String WinDebugger::GetModulesInfo()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
String str;
|
|
|
|
for (auto module : mDebugTarget->mDbgModules)
|
|
{
|
|
COFF* coff = (COFF*)module;
|
|
if (module->mHotIdx > 0)
|
|
continue;
|
|
|
|
str += module->mDisplayName;
|
|
str += "\t";
|
|
if (module->mLoadState == DbgModuleLoadState_Loaded)
|
|
{
|
|
str += module->mFilePath;
|
|
}
|
|
else if (module->mLoadState == DbgModuleLoadState_NotLoaded)
|
|
{
|
|
str += module->mFilePath;
|
|
str += " (Loading...)";
|
|
}
|
|
else if (module->mLoadState == DbgModuleLoadState_Failed)
|
|
{
|
|
str += "!";
|
|
str += module->mFilePath;
|
|
}
|
|
|
|
if (module->mMappedImageFile != NULL)
|
|
{
|
|
str += " (";
|
|
str += module->mMappedImageFile->mFileName;
|
|
str += ")";
|
|
}
|
|
|
|
str += "\t";
|
|
str += coff->mPDBPath;
|
|
str += "\t";
|
|
str += module->mVersion;
|
|
str += StrFormat("\t%@-%@\t%dk\t", module->mImageBase, module->mImageBase + module->mImageSize, module->mImageSize / 1024);
|
|
|
|
time_t timestamp = coff->mTimeStamp;
|
|
if (timestamp == 0)
|
|
timestamp = GetFileTimeWrite(coff->mFilePath);
|
|
if (timestamp != 0)
|
|
{
|
|
char timeString[256];
|
|
auto time_info = localtime(×tamp);
|
|
strftime(timeString, sizeof(timeString), "%D %T", time_info);
|
|
str += timeString;
|
|
}
|
|
str += "\n";
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
String WinDebugger::GetModuleInfo(const StringImpl& modulePath)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
String result;
|
|
|
|
for (auto dbgModule : mDebugTarget->mDbgModules)
|
|
{
|
|
if (modulePath.Equals(dbgModule->mFilePath, StringImpl::CompareKind_OrdinalIgnoreCase))
|
|
{
|
|
dbgModule->ParseGlobalsData();
|
|
dbgModule->PopulateStaticVariableMap();
|
|
|
|
auto coff = (COFF*)dbgModule;
|
|
coff->ParseCompileUnits();
|
|
|
|
int fileSize = 0;
|
|
//
|
|
{
|
|
FileStream fs;
|
|
fs.Open(coff->mFilePath, "rb");
|
|
fileSize = fs.GetSize();
|
|
}
|
|
|
|
result += StrFormat("Path: %s FileSize:%0.2fk MemoryImage:%0.2fk\n", coff->mFilePath.c_str(), fileSize / 1024.0f, coff->mImageSize / 1024.0f);
|
|
|
|
result += "Sections:\n";
|
|
for (auto& section : coff->mSections)
|
|
{
|
|
result += StrFormat("\t%s\t%0.2fk\n", section.mName.c_str(), (section.mAddrLength) / 1024.0f);
|
|
}
|
|
result += "\n";
|
|
|
|
result += "Compile Units:\n";
|
|
for (auto compileUnit : dbgModule->mCompileUnits)
|
|
{
|
|
coff->MapCompileUnitMethods(compileUnit);
|
|
result += StrFormat("\t%s PCRange:%0.2fk\n", compileUnit->mName.c_str(), (compileUnit->mHighPC - compileUnit->mLowPC) / 1024.0f);
|
|
}
|
|
result += "\n";
|
|
|
|
Array<CvModuleInfo*> moduleInfos;
|
|
for (auto moduleInfo : coff->mCvModuleInfo)
|
|
{
|
|
if (moduleInfo->mSectionContrib.mSize > 0)
|
|
moduleInfos.Add(moduleInfo);
|
|
}
|
|
moduleInfos.Sort([](CvModuleInfo* lhs, CvModuleInfo* rhs)
|
|
{
|
|
return lhs->mSectionContrib.mSize > rhs->mSectionContrib.mSize;
|
|
});
|
|
|
|
int totalContrib = 0;
|
|
result += "CV Module Info:\n";
|
|
for (auto moduleInfo : moduleInfos)
|
|
{
|
|
auto section = coff->mSections[moduleInfo->mSectionContrib.mSection - 1];
|
|
|
|
result += StrFormat("\t%s\t%s\t%0.2fk\t%@-%@\n", moduleInfo->mModuleName, section.mName.c_str(), (moduleInfo->mSectionContrib.mSize) / 1024.0f,
|
|
coff->GetSectionAddr(moduleInfo->mSectionContrib.mSection, moduleInfo->mSectionContrib.mOffset),
|
|
coff->GetSectionAddr(moduleInfo->mSectionContrib.mSection, moduleInfo->mSectionContrib.mOffset + moduleInfo->mSectionContrib.mSize));
|
|
totalContrib += moduleInfo->mSectionContrib.mSize;
|
|
}
|
|
result += StrFormat("\tTOTAL: %0.2fk\n", (totalContrib) / 1024.0f);
|
|
result += "\n";
|
|
|
|
addr_target minAddr = 0;
|
|
Array<DbgCompileUnitContrib*> contribs;
|
|
for (auto itr = mDebugTarget->mContribMap.begin(); itr != mDebugTarget->mContribMap.end(); ++itr)
|
|
{
|
|
auto contrib = *itr;
|
|
if (contrib->mDbgModule != coff)
|
|
continue;
|
|
|
|
if (contrib->mAddress < minAddr)
|
|
continue;
|
|
|
|
minAddr = contrib->mAddress + contrib->mLength;
|
|
|
|
auto section = &coff->mSectionHeaders[contrib->mSection - 1];
|
|
if (section->mSizeOfRawData <= 0)
|
|
continue;
|
|
|
|
contribs.Add(contrib);
|
|
}
|
|
contribs.Sort([](DbgCompileUnitContrib* lhs, DbgCompileUnitContrib* rhs)
|
|
{
|
|
return lhs->mLength > rhs->mLength;
|
|
});
|
|
|
|
totalContrib = 0;
|
|
result += "Contribs:\n";
|
|
for (auto contrib : contribs)
|
|
{
|
|
auto cvModule = coff->mCvModuleInfo[contrib->mCompileUnitId];
|
|
auto section = &coff->mSectionHeaders[contrib->mSection - 1];
|
|
result += StrFormat("\t%s\t%s\t%0.2fk\t%@\n", cvModule->mModuleName, section->mName, (contrib->mLength)/1024.0f, contrib->mAddress);
|
|
totalContrib += contrib->mLength;
|
|
}
|
|
result += StrFormat("\tTOTAL: %0.2fk\n", (totalContrib) / 1024.0f);
|
|
result += "\n";
|
|
|
|
struct SymbolEntry
|
|
{
|
|
const char* mName;
|
|
addr_target mAddress;
|
|
int mSize;
|
|
};
|
|
Array<SymbolEntry> symbolEntries;
|
|
|
|
for (auto symbol : mDebugTarget->mSymbolMap)
|
|
{
|
|
if (symbol->mDbgModule != coff)
|
|
continue;
|
|
|
|
if (!symbolEntries.IsEmpty())
|
|
{
|
|
auto lastSymbol = &symbolEntries.back();
|
|
if (lastSymbol->mSize == 0)
|
|
lastSymbol->mSize = symbol->mAddress - lastSymbol->mAddress;
|
|
}
|
|
|
|
SymbolEntry symbolEntry;
|
|
symbolEntry.mName = symbol->mName;
|
|
symbolEntry.mAddress = symbol->mAddress;
|
|
symbolEntry.mSize = 0;
|
|
symbolEntries.Add(symbolEntry);
|
|
}
|
|
if (!symbolEntries.IsEmpty())
|
|
{
|
|
auto lastSymbol = &symbolEntries.back();
|
|
for (auto contrib : contribs)
|
|
{
|
|
if ((lastSymbol->mAddress >= contrib->mAddress) && (lastSymbol->mAddress < contrib->mAddress + contrib->mLength))
|
|
{
|
|
lastSymbol->mSize = (contrib->mAddress + contrib->mLength) - lastSymbol->mAddress;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
symbolEntries.Sort([](const SymbolEntry& lhs, const SymbolEntry& rhs)
|
|
{
|
|
return lhs.mSize > rhs.mSize;
|
|
});
|
|
|
|
totalContrib = 0;
|
|
result += "Symbols:\n";
|
|
for (auto symbolEntry : symbolEntries)
|
|
{
|
|
result += StrFormat("\t%s\t%0.2fk\t%@\n", symbolEntry.mName, (symbolEntry.mSize) / 1024.0f, symbolEntry.mAddress);
|
|
totalContrib += symbolEntry.mSize;
|
|
}
|
|
result += StrFormat("\tTOTAL: %0.2fk\n", (totalContrib) / 1024.0f);
|
|
result += "\n";
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
totalContrib = 0;
|
|
result += "Static Variables:\n";
|
|
for (auto& variable : coff->mStaticVariables)
|
|
{
|
|
result += StrFormat("\t%s\t%0.2fk\n", variable->mName, (variable->mType->GetByteCount()) / 1024.0f);
|
|
totalContrib += variable->mType->GetByteCount();
|
|
}
|
|
result += StrFormat("\tTOTAL: %0.2fk\n", (totalContrib) / 1024.0f);
|
|
result += "\n";
|
|
|
|
totalContrib = 0;
|
|
result += "Methods:\n";
|
|
Array<DbgSubprogram*> methods;
|
|
for (int typeIdx = 0; typeIdx < coff->mTypes.mSize; typeIdx++)
|
|
{
|
|
auto type = coff->mTypes[typeIdx];
|
|
type->PopulateType();
|
|
for (auto method : type->mMethodList)
|
|
methods.Add(method);
|
|
}
|
|
for (auto compileUnit : dbgModule->mCompileUnits)
|
|
{
|
|
for (auto method : compileUnit->mOrphanMethods)
|
|
methods.Add(method);
|
|
}
|
|
methods.Sort([](DbgSubprogram* lhs, DbgSubprogram* rhs)
|
|
{
|
|
return lhs->GetByteCount() > rhs->GetByteCount();
|
|
});
|
|
for (auto method : methods)
|
|
{
|
|
int methodSize = method->GetByteCount();
|
|
if (methodSize <= 0)
|
|
continue;
|
|
auto name = method->ToString();
|
|
result += StrFormat("\t%s\t%0.2fk\n", name.c_str(), methodSize / 1024.0f);
|
|
totalContrib += methodSize;
|
|
}
|
|
|
|
result += StrFormat("\tTOTAL: %0.2fk\n", (totalContrib) / 1024.0f);
|
|
result += "\n";
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void WinDebugger::CancelSymSrv()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
if (mActiveSymSrvRequest != NULL)
|
|
mActiveSymSrvRequest->Cancel();
|
|
}
|
|
|
|
bool WinDebugger::HasPendingDebugLoads()
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
return (!mPendingImageLoad.IsEmpty()) || (!mPendingDebugInfoLoad.IsEmpty());
|
|
}
|
|
|
|
int WinDebugger::LoadImageForModule(const StringImpl &modulePath, const StringImpl& imagePath)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
for (auto dbgModule : mDebugTarget->mDbgModules)
|
|
{
|
|
if (modulePath.Equals(dbgModule->mFilePath, StringImpl::CompareKind_OrdinalIgnoreCase))
|
|
{
|
|
auto coff = (COFF*)dbgModule;
|
|
|
|
if (!coff->LoadModuleImage(imagePath))
|
|
{
|
|
mDebugManager->mOutMessages.push_back("error Failed to load image " + imagePath);
|
|
}
|
|
ModuleChanged(dbgModule);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int WinDebugger::LoadDebugInfoForModule(DbgModule* dbgModule)
|
|
{
|
|
if (!dbgModule->HasPendingDebugInfo())
|
|
return 0;
|
|
|
|
if (dbgModule->RequestDebugInfo())
|
|
{
|
|
ClearCallStack(); // Make this re-resolve with debug info
|
|
return 1;
|
|
}
|
|
|
|
DbgPendingDebugInfoLoad* dbgPendingDebugInfoLoad = NULL;
|
|
if (mPendingDebugInfoLoad.TryAdd(dbgModule, NULL, &dbgPendingDebugInfoLoad))
|
|
{
|
|
dbgPendingDebugInfoLoad->mModule = dbgModule;
|
|
dbgPendingDebugInfoLoad->mAllowRemote = true;
|
|
return 2;
|
|
}
|
|
dbgPendingDebugInfoLoad->mAllowRemote = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int WinDebugger::LoadDebugInfoForModule(const StringImpl& moduleName)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
for (auto dbgModule : mDebugTarget->mDbgModules)
|
|
{
|
|
String checkModuleName = GetFileName(dbgModule->mFilePath);
|
|
if (moduleName.Equals(checkModuleName, StringImpl::CompareKind_OrdinalIgnoreCase))
|
|
{
|
|
return LoadDebugInfoForModule(dbgModule);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int WinDebugger::LoadDebugInfoForModule(const StringImpl& modulePath, const StringImpl& debugFileName)
|
|
{
|
|
AutoCrit autoCrit(mDebugManager->mCritSect);
|
|
|
|
for (auto dbgModule : mDebugTarget->mDbgModules)
|
|
{
|
|
if (modulePath.Equals(dbgModule->mFilePath, StringImpl::CompareKind_OrdinalIgnoreCase))
|
|
{
|
|
auto coff = (COFF*)dbgModule;
|
|
|
|
String err;
|
|
if (!coff->mPDBLoaded)
|
|
{
|
|
dbgModule->mFailMsgPtr = &err;
|
|
if (coff->TryLoadPDB(debugFileName, coff->mWantPDBGuid, coff->mWantAge))
|
|
{
|
|
ModuleChanged(dbgModule);
|
|
}
|
|
dbgModule->mFailMsgPtr = NULL;
|
|
}
|
|
else
|
|
{
|
|
err = StrFormat("Module '%s' already has debug information loaded", GetFileName(modulePath).c_str());
|
|
}
|
|
|
|
if (!err.IsEmpty())
|
|
{
|
|
mDebugManager->mOutMessages.push_back("error " + err);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void WinDebugger::FixupLineData(DbgCompileUnit* compileUnit)
|
|
{
|
|
if (!compileUnit || !compileUnit->mNeedsLineDataFixup)
|
|
return;
|
|
compileUnit->mNeedsLineDataFixup = false;
|
|
}
|
|
|
|
static int CompareLineData(const void* lineDataP1, const void* lineDataP2)
|
|
{
|
|
int cmpResult = (int)(((DbgLineData*)lineDataP1)->mRelAddress - ((DbgLineData*)lineDataP2)->mRelAddress);
|
|
if (cmpResult != 0)
|
|
return cmpResult;
|
|
|
|
// A larger contrib size means it's the 'outer' inlinee
|
|
cmpResult = -(((DbgLineData*)lineDataP1)->mContribSize - ((DbgLineData*)lineDataP2)->mContribSize);
|
|
if (cmpResult != 0)
|
|
return cmpResult;
|
|
|
|
return -(((DbgLineData*)lineDataP1)->mCtxIdx - ((DbgLineData*)lineDataP2)->mCtxIdx);
|
|
}
|
|
|
|
void WinDebugger::FixupLineDataForSubprogram(DbgSubprogram* subProgram)
|
|
{
|
|
if ((subProgram == NULL) || (!subProgram->mNeedLineDataFixup))
|
|
return;
|
|
|
|
BP_ZONE("FixupLineDataForSubprogram");
|
|
|
|
subProgram->mNeedLineDataFixup = false;
|
|
if (subProgram->mInlineeInfo != NULL)
|
|
FixupLineDataForSubprogram(subProgram->mInlineeInfo->mRootInliner);
|
|
|
|
if ((subProgram->mLineInfo == NULL) || (subProgram->mLineInfo->mLines.mSize == 0))
|
|
return;
|
|
|
|
//TODO: I think this was covering up a bug in DWARF line encoding? Figure this out
|
|
// if (subProgram->mLineInfo->mLines.mSize >= 2)
|
|
// {
|
|
// DbgLineData* line0 = &subProgram->mLineInfo->mLines[0];
|
|
// DbgLineData* line1 = &subProgram->mLineInfo->mLines[1];
|
|
//
|
|
//
|
|
// if ((line0->mRelAddress == line1->mRelAddress) && (!line0->IsStackFrameSetup()) && (line1->IsStackFrameSetup()))
|
|
// {
|
|
// CPUInst inst;
|
|
// if (mCPU->Decode(line0->mAddress, subProgram->mCompileUnit->mDbgModule->mOrigImageData, &inst))
|
|
// line1->mAddress += inst.GetLength();
|
|
// }
|
|
// }
|
|
|
|
qsort(subProgram->mLineInfo->mLines.mVals, subProgram->mLineInfo->mLines.mSize, sizeof(DbgLineData), CompareLineData);
|
|
|
|
// If we have multiple lines with the same line/column/context, merge them
|
|
if (!subProgram->mLineInfo->mLines.IsEmpty())
|
|
{
|
|
auto prevLine = &subProgram->mLineInfo->mLines[0];
|
|
for (int i = 1; i < subProgram->mLineInfo->mLines.mSize; i++)
|
|
{
|
|
auto nextLine = &subProgram->mLineInfo->mLines[i];
|
|
|
|
if ((nextLine->mLine == prevLine->mLine) && (nextLine->mColumn == prevLine->mColumn) && (nextLine->mCtxIdx == prevLine->mCtxIdx) &&
|
|
(nextLine->mRelAddress == prevLine->mRelAddress + prevLine->mContribSize))
|
|
{
|
|
prevLine->mContribSize += nextLine->mContribSize;
|
|
// This messed up inline cases because mContribSize actually INCLUDES inlined lines so it caused the address to skip too far
|
|
//nextLine->mRelAddress += nextLine->mContribSize;
|
|
//nextLine->mContribSize = 0;
|
|
}
|
|
else
|
|
{
|
|
prevLine = nextLine;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinDebugger::ReserveHotTargetMemory(int size)
|
|
{
|
|
HotTargetMemory hotTargetMemory;
|
|
hotTargetMemory.mOffset = 0;
|
|
hotTargetMemory.mSize = 0;
|
|
hotTargetMemory.mPtr = NULL;
|
|
|
|
if (size > 0)
|
|
{
|
|
// In 64-bit mode we have a reserved region on program load that we commit here because the offsets
|
|
// must be within 32-bits of the original EXE image, but in 32-bit mode we don't reserve anything
|
|
// until here
|
|
#ifdef BF_DBG_32
|
|
//hotTargetMemory.mSize = std::max(1024 * 1024, size);
|
|
BF_ASSERT((size & (mPageSize - 1)) == 0);
|
|
hotTargetMemory.mSize = size;
|
|
hotTargetMemory.mPtr = (addr_target)(intptr)VirtualAllocEx(mProcessInfo.hProcess, NULL, hotTargetMemory.mSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
|
mDebugTarget->mHotHeap->AddTrackedRegion(hotTargetMemory.mPtr, hotTargetMemory.mSize);
|
|
#else
|
|
hotTargetMemory.mSize = size;
|
|
hotTargetMemory.mPtr = mDebugTarget->mHotHeap->Alloc(size);
|
|
BF_ASSERT(hotTargetMemory.mPtr != 0);
|
|
auto ptr = ::VirtualAllocEx(mProcessInfo.hProcess, (void*)(intptr)hotTargetMemory.mPtr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
|
BF_ASSERT(ptr == (void*)(intptr)hotTargetMemory.mPtr);
|
|
#endif
|
|
}
|
|
|
|
BfLogDbg("ReserveHotTargetMemory %p %d\n", hotTargetMemory.mPtr, hotTargetMemory.mSize);
|
|
int err = GetLastError();
|
|
mHotTargetMemory.push_back(hotTargetMemory);
|
|
}
|
|
|
|
addr_target WinDebugger::AllocHotTargetMemory(int size, bool canExecute, bool canWrite, int* outAllocSize)
|
|
{
|
|
int prot = PAGE_READWRITE;
|
|
if (canExecute && canWrite)
|
|
prot = PAGE_EXECUTE_READWRITE;
|
|
else if (canExecute)
|
|
prot = PAGE_EXECUTE_READ;
|
|
|
|
size = (size + (mPageSize - 1)) & ~(mPageSize - 1);
|
|
*outAllocSize = size;
|
|
|
|
HotTargetMemory* hotTargetMemory = NULL;
|
|
bool foundHotTargetMemory = false;
|
|
for (int i = mHotTargetMemory.mSize - 1; i >= BF_MAX(mHotTargetMemory.mSize - 32, 0); i--)
|
|
{
|
|
hotTargetMemory = &mHotTargetMemory[i];
|
|
if (hotTargetMemory->mPtr == 0)
|
|
{
|
|
Fail("Failed to allocate memory for hot loading");
|
|
return 0;
|
|
}
|
|
|
|
if (hotTargetMemory->GetSizeLeft() >= size)
|
|
{
|
|
foundHotTargetMemory = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundHotTargetMemory)
|
|
{
|
|
ReserveHotTargetMemory(size);
|
|
foundHotTargetMemory = true;
|
|
hotTargetMemory = &mHotTargetMemory.back();
|
|
}
|
|
|
|
BF_ASSERT(hotTargetMemory->mOffset + size <= hotTargetMemory->mSize);
|
|
addr_target result = hotTargetMemory->mPtr + hotTargetMemory->mOffset;
|
|
|
|
::VirtualProtectEx(mProcessInfo.hProcess, (void*)(intptr)result, size, prot, NULL);
|
|
|
|
BfLogDbg("AllocHotTargetMemory: %p %d %d %d\n", result, size, canExecute, canWrite);
|
|
|
|
hotTargetMemory->mOffset += size;
|
|
return result;
|
|
}
|
|
|
|
void WinDebugger::ReleaseHotTargetMemory(addr_target addr, int size)
|
|
{
|
|
#ifdef BF_DBG_32
|
|
::VirtualFreeEx(mProcessInfo.hProcess, (void*)(intptr)addr, 0, MEM_RELEASE);
|
|
#else
|
|
mDebugTarget->mHotHeap->Release(addr, size);
|
|
::VirtualFreeEx(mProcessInfo.hProcess, (void*)(intptr)addr, size, MEM_DECOMMIT);
|
|
#endif
|
|
}
|
|
|
|
void WinDebugger::CleanupHotHeap()
|
|
{
|
|
mDebugTarget->mLastHotHeapCleanIdx = mDebugTarget->mHotHeap->mBlockAllocIdx;
|
|
|
|
// Our criteria for determining whether a hot loaded file is still being used:
|
|
// 1) If we are currently executing a method from that object file.
|
|
// 2) If the symbol map has a symbol with that address.
|
|
// 3) If the static variable map contains a reference - including a conservative scan of the data
|
|
// This handles vdata references
|
|
// This is a conservative check which won't purge hot reloads that contain deleted
|
|
// methods (for example), but it will purge hot reloads where all the changed
|
|
// data has been overwritten.
|
|
// For delegate bindings, the original module declaring the bind creates a "preserve"
|
|
// global such as "bf_hs_preserve@_ZN5TestO4TestEv", whose preserved symbol ensures it
|
|
// doesn't get unloaded. The current version of that method resides in "_ZN5TestO4TestEv",
|
|
// ensuring that the method pointed to by the global variable is valid
|
|
mDebugTarget->mHotHeap->ClearReferencedFlags();
|
|
|
|
addr_target lowAddr = mDebugTarget->mHotHeap->mHotAreaStart;
|
|
addr_target highAddr = lowAddr + mDebugTarget->mHotHeap->mHotAreaSize;
|
|
|
|
// Do conservative scan through all thread stacks. Stack traces aren't 100% reliable, so we
|
|
// need to do a full conservative scan of any addresses stored in the stack
|
|
// to ensure we don't miss any return addresses
|
|
for (int threadIdx = 0; threadIdx < (int)mThreadList.size(); threadIdx++)
|
|
{
|
|
WdThreadInfo* threadInfo = mThreadList[threadIdx];
|
|
|
|
BF_CONTEXT lcContext;
|
|
lcContext.ContextFlags = BF_CONTEXT_CONTROL;
|
|
BF_GetThreadContext(threadInfo->mHThread, &lcContext);
|
|
|
|
addr_target checkStackAddr = BF_CONTEXT_SP(lcContext);
|
|
checkStackAddr &= ~(sizeof(addr_target) - 1);
|
|
|
|
// Conservative check on registers
|
|
for (int regNum = 0; regNum < sizeof(BF_CONTEXT)/sizeof(addr_target); regNum++)
|
|
{
|
|
addr_target checkAddr = ((addr_target*)&lcContext)[regNum];
|
|
if ((checkAddr >= lowAddr) && (checkAddr < highAddr))
|
|
mDebugTarget->mHotHeap->MarkBlockReferenced(checkAddr);
|
|
}
|
|
|
|
// Conservative check on all stack data
|
|
while (checkStackAddr < threadInfo->mStartSP)
|
|
{
|
|
addr_target checkAddrArr[1024];
|
|
int numAddrsChecking = BF_MIN(1024, (int)((threadInfo->mStartSP - checkStackAddr) / sizeof(addr_target)));
|
|
ReadMemory(checkStackAddr, numAddrsChecking * sizeof(addr_target), checkAddrArr);
|
|
checkStackAddr += numAddrsChecking * sizeof(addr_target);
|
|
|
|
for (int addrIdx = 0; addrIdx < numAddrsChecking; addrIdx++)
|
|
{
|
|
addr_target checkAddr = checkAddrArr[addrIdx];
|
|
if ((checkAddr >= lowAddr) && (checkAddr < highAddr))
|
|
mDebugTarget->mHotHeap->MarkBlockReferenced(checkAddr);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto mainModule = mDebugTarget->mTargetBinary;
|
|
for (auto entry : mainModule->mSymbolNameMap)
|
|
{
|
|
auto dwSymbol = entry->mValue;
|
|
addr_target checkAddr = dwSymbol->mAddress;
|
|
if ((checkAddr >= lowAddr) && (checkAddr < highAddr))
|
|
mDebugTarget->mHotHeap->MarkBlockReferenced(checkAddr);
|
|
}
|
|
|
|
mDebugTarget->CleanupHotHeap();
|
|
|
|
BfLogDbg("Hot load memory used: %dk\n", (int)mDebugTarget->mHotHeap->GetUsedSize() / 1024);
|
|
}
|
|
|
|
int WinDebugger::EnableWriting(intptr address, int size)
|
|
{
|
|
DWORD oldProt;
|
|
bool success = ::VirtualProtectEx(mProcessInfo.hProcess, (void*)(intptr)address, size, PAGE_READWRITE, &oldProt);
|
|
if (!success)
|
|
{
|
|
int err = GetLastError();
|
|
}
|
|
return (int)oldProt;
|
|
}
|
|
|
|
int WinDebugger::SetProtection(intptr address, int size, int prot)
|
|
{
|
|
DWORD oldProt;
|
|
::VirtualProtectEx(mProcessInfo.hProcess, (void*)(intptr)address, size, prot, &oldProt);
|
|
return (int)oldProt;
|
|
}
|
|
|
|
void WinDebugger::EnableMemCache()
|
|
{
|
|
mMemCacheAddr = 1;
|
|
}
|
|
|
|
void WinDebugger::DisableMemCache()
|
|
{
|
|
mMemCacheAddr = 0;
|
|
}
|
|
|
|
bool WinDebugger::ReadMemory(intptr address, uint64 length, void* dest, bool local)
|
|
{
|
|
if (local)
|
|
{
|
|
__try
|
|
{
|
|
memcpy(dest, (void*)address, length);
|
|
return true;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (mMemCacheAddr != 0)
|
|
{
|
|
addr_target targetAddr = (addr_target)address;
|
|
if ((targetAddr >= mMemCacheAddr) && (targetAddr + length <= mMemCacheAddr + WD_MEMCACHE_SIZE) && (mMemCacheAddr > 1))
|
|
{
|
|
memcpy(dest, mMemCacheData + (targetAddr - mMemCacheAddr), length);
|
|
return true;
|
|
}
|
|
|
|
// We need a new block
|
|
SIZE_T dwReadBytes;
|
|
if (::ReadProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, mMemCacheData, (SIZE_T)WD_MEMCACHE_SIZE, &dwReadBytes) != 0)
|
|
{
|
|
mMemCacheAddr = targetAddr;
|
|
memcpy(dest, mMemCacheData, length);
|
|
return true;
|
|
}
|
|
|
|
// Failed, turn off caching
|
|
mMemCacheAddr = 0;
|
|
}
|
|
|
|
SIZE_T dwReadBytes;
|
|
if (::ReadProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, dest, (SIZE_T)length, &dwReadBytes) != 0)
|
|
return true;
|
|
int lastErr = ::GetLastError();
|
|
memset(dest, 0, length);
|
|
return false;
|
|
}
|
|
|
|
bool WinDebugger::WriteMemory(intptr address, void* src, uint64 length)
|
|
{
|
|
SIZE_T dwBytesWritten = 0;
|
|
int result = ::WriteProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, src, (SIZE_T)length, &dwBytesWritten);
|
|
return result != 0;
|
|
}
|
|
|
|
addr_target WinDebugger::GetTLSOffset(int tlsIndex)
|
|
{
|
|
typedef LONG NTSTATUS;
|
|
typedef DWORD KPRIORITY;
|
|
typedef WORD UWORD;
|
|
|
|
enum THREADINFOCLASS
|
|
{
|
|
ThreadBasicInformation,
|
|
};
|
|
|
|
struct CLIENT_ID
|
|
{
|
|
HANDLE UniqueProcess;
|
|
HANDLE UniqueThread;
|
|
};
|
|
|
|
struct
|
|
{
|
|
NTSTATUS mExitStatus;
|
|
void* mTebBaseAddress;
|
|
CLIENT_ID mClientId;
|
|
KAFFINITY mAffinityMask;
|
|
KPRIORITY mPriority;
|
|
KPRIORITY mBasePriority;
|
|
} threadInfo = { 0 };
|
|
|
|
ULONG len = 0;
|
|
bool loadedManually = false;
|
|
static HMODULE module = NULL;
|
|
static NTSTATUS(__stdcall *NtQueryInformationThread)(HANDLE ThreadHandle, THREADINFOCLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);
|
|
|
|
if (module == NULL)
|
|
{
|
|
module = GetModuleHandleA("ntdll.dll");
|
|
NtQueryInformationThread = reinterpret_cast<decltype(NtQueryInformationThread)>(GetProcAddress(module, "NtQueryInformationThread"));
|
|
}
|
|
|
|
if (NtQueryInformationThread == NULL)
|
|
return 0;
|
|
NTSTATUS status = NtQueryInformationThread(mActiveThread->mHThread, (THREADINFOCLASS)0, &threadInfo, sizeof(threadInfo), nullptr);
|
|
if (status < 0)
|
|
return 0;
|
|
|
|
#ifdef BF_DBG_32
|
|
addr_target tibAddr = ReadMemory<addr_target>((intptr)threadInfo.mTebBaseAddress + 0x0);
|
|
addr_target tlsTable = ReadMemory<addr_target>((intptr)tibAddr + 0x2C);
|
|
#else
|
|
addr_target tlsTable = ReadMemory<addr_target>((intptr)threadInfo.mTebBaseAddress + 0x58);
|
|
#endif
|
|
return ReadMemory<addr_target>(tlsTable + tlsIndex * sizeof(addr_target));
|
|
}
|
|
|
|
bool WinDebugger::WriteInstructions(intptr address, void* src, uint64 length)
|
|
{
|
|
SIZE_T dwBytesWritten = 0;
|
|
bool result = ::WriteProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, src, (SIZE_T)length, &dwBytesWritten) != 0;
|
|
result |= ::FlushInstructionCache(mProcessInfo.hProcess, (void*)(intptr)address, (SIZE_T)length) != 0;
|
|
BF_ASSERT(result);
|
|
BfLogDbg("WriteInstructions: %p %d\n", address, length);
|
|
return result;
|
|
}
|
|
|
|
DbgMemoryFlags WinDebugger::GetMemoryFlags(intptr address)
|
|
{
|
|
MEMORY_BASIC_INFORMATION memBasicInfo;
|
|
if (::VirtualQueryEx(mProcessInfo.hProcess, (void*)address, &memBasicInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0)
|
|
{
|
|
//BfLogDbg("VirtualQueryEx failed with %d\n", GetLastError());
|
|
return DbgMemoryFlags_None;
|
|
}
|
|
|
|
DbgMemoryFlags flags = DbgMemoryFlags_None;
|
|
if (memBasicInfo.AllocationProtect & PAGE_READWRITE)
|
|
{
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Read);
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Write);
|
|
}
|
|
if (memBasicInfo.AllocationProtect & PAGE_READONLY)
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Read);
|
|
if (memBasicInfo.AllocationProtect & PAGE_WRITECOPY)
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Write);
|
|
if (memBasicInfo.AllocationProtect & PAGE_EXECUTE)
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Execute);
|
|
if (memBasicInfo.AllocationProtect & PAGE_EXECUTE_READ)
|
|
{
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Execute);
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Read);
|
|
}
|
|
if (memBasicInfo.AllocationProtect & PAGE_EXECUTE_READWRITE)
|
|
{
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Execute);
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Read);
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Write);
|
|
}
|
|
if (memBasicInfo.AllocationProtect & PAGE_EXECUTE_WRITECOPY)
|
|
{
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Execute);
|
|
flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Write);
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
#ifdef BF_DBG_32
|
|
Debugger* Beefy::CreateDebugger32(DebugManager* debugManager, DbgMiniDump* miniDump)
|
|
#else
|
|
Debugger* Beefy::CreateDebugger64(DebugManager* debugManager, DbgMiniDump* miniDump)
|
|
#endif
|
|
{
|
|
if (miniDump != NULL)
|
|
{
|
|
auto debugger = new MiniDumpDebugger(debugManager, miniDump);
|
|
|
|
return debugger;
|
|
}
|
|
return new WinDebugger(debugManager);
|
|
}
|
|
|
|
#ifdef BF_DBG_32
|
|
|
|
void WdAllocTest()
|
|
{
|
|
Array<BeefyDbg32::WdStackFrame*> stackFrameList;
|
|
|
|
for (int i = 0; true; i++)
|
|
{
|
|
WdStackFrame* stackFrame = new WdStackFrame();
|
|
stackFrameList.push_back(stackFrame);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#endif //!defined BF32 || !defined BF_DBG_64
|