diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf index 1acd74e1..91ef38dc 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf @@ -131,6 +131,14 @@ namespace Beefy.theme.dark CloseSubMenu(); } } + + if (mMenuItem.mTooltip != null) + { + if (DarkTooltipManager.CheckMouseover(this, 20, var mousePoint)) + { + DarkTooltipManager.ShowTooltip(mMenuItem.mTooltip, this, mWidth + GS!(8), GS!(-8)); + } + } } public override void Submit() diff --git a/BeefLibs/Beefy2D/src/widgets/Menu.bf b/BeefLibs/Beefy2D/src/widgets/Menu.bf index 2cfdf1dc..382d0256 100644 --- a/BeefLibs/Beefy2D/src/widgets/Menu.bf +++ b/BeefLibs/Beefy2D/src/widgets/Menu.bf @@ -564,6 +564,7 @@ namespace Beefy.widgets { public Menu mParent; public String mLabel ~ delete _; + public String mTooltip ~ delete _; public List mItems = new List() ~ DeleteContainerAndItems!(_); public Event mOnMenuItemSelected ~ _.Dispose(); diff --git a/IDE/src/Debugger/DebugManager.bf b/IDE/src/Debugger/DebugManager.bf index caa1cef6..1d81475b 100644 --- a/IDE/src/Debugger/DebugManager.bf +++ b/IDE/src/Debugger/DebugManager.bf @@ -268,6 +268,9 @@ namespace IDE.Debugger [CallingConvention(.Stdcall),CLink] static extern char8* CallStack_GetStackFrameInfo(int32 stackFrameIdx, out int addr, out char8* outFile, out int32 hotIdx, out int32 defLineStart, out int32 defLineEnd, out int32 outLine, out int32 outColumn, out int32 outLanguage, out int32 outStackSize, out FrameFlags flags); + [CallingConvention(.Stdcall),CLink] + static extern char8* CallStack_GetStackFrameId(int32 stackFrameIdx); + [CallingConvention(.Stdcall),CLink] static extern char8* Callstack_GetStackFrameOldFileInfo(int32 stackFrameIdx); @@ -1256,5 +1259,14 @@ namespace IDE.Debugger outText.Append(str); return true; } + + public bool GetStackFrameId(int32 callStackIdx, String outString) + { + char8* stackId = CallStack_GetStackFrameId(callStackIdx); + if (stackId == null) + return false; + outString.Append(stackId); + return true; + } } } diff --git a/IDE/src/ui/WatchPanel.bf b/IDE/src/ui/WatchPanel.bf index 18b554f1..edcca3d5 100644 --- a/IDE/src/ui/WatchPanel.bf +++ b/IDE/src/ui/WatchPanel.bf @@ -11,6 +11,7 @@ using Beefy.theme.dark; using Beefy.events; using Beefy.utils; using IDE.Debugger; +using Beefy.geom; namespace IDE.ui { @@ -40,6 +41,8 @@ namespace IDE.ui public DebugManager.Language mLanguage = .NotSet; public String mName ~ delete _; + public String mStackFrameId ~ delete _; + public bool mWantsStackFrameId; public String mEvalStr ~ delete _; public bool mCanEdit; public String mEditInitialize ~ delete _; @@ -47,6 +50,8 @@ namespace IDE.ui public bool mHasValue; public bool mIsNewExpression; public bool mIsPending; + public bool mUsedLock; + public int32 mCurStackIdx; public int mMemoryBreakpointAddr; public int32 mHadStepCount; public String mStringView ~ delete _; @@ -512,21 +517,6 @@ namespace IDE.ui DarkTooltipManager.CloseTooltip(); } - /*public override void RecalcSize() - { - base.RecalcSize(); - if (mWatchStringEdit.mMoreButton != null) - mHeight += GS!(32); - }*/ - - public override void GetTextData() - { - - base.GetTextData(); - - - } - public override void CheckLineCoords() { bool changed = (mLineCoordTextVersionId != mData.mCurTextVersionId); @@ -1325,6 +1315,28 @@ namespace IDE.ui } } } + + if (mColumnIdx == 1) + { + var headItem = GetSubItem(0) as WatchListViewItem; + if (headItem.mWatchEntry?.mStackFrameId != null) + { + float drawX = mWidth - GS!(16); + if (headItem.mWatchRefreshButton != null) + drawX -= GS!(16); + + uint32 color = Color.White; + if (headItem.mDisabled) + color = 0x80FFFFFF; + else if (mFailed) + color = 0xFFFF4040; + else if (headItem.mWatchEntry.mUsedLock) + color = 0xFFE39C39; + + using (g.PushColor(color)) + g.Draw(DarkTheme.sDarkTheme.GetImage(.LockIcon), drawX, GS!(-1)); + } + } base.DrawAll(g); } @@ -1757,6 +1769,34 @@ namespace IDE.ui } } + if (mColumnIdx == 1) + { + var nameItem = GetSubItem(0) as WatchListViewItem; + if (nameItem.mWatchEntry?.mStackFrameId != null) + { + if (DarkTooltipManager.CheckMouseover(this, 20, var mousePoint)) + { + float drawX = mWidth - GS!(16); + if (nameItem.mWatchRefreshButton != null) + drawX -= GS!(16); + + if (Rect(drawX, 0, GS!(16), GS!(20)).Contains(mousePoint)) + { + String tooltip = scope String(); + tooltip.Append(nameItem.mWatchEntry.mStackFrameId); + + int lastColon = nameItem.mWatchEntry.mStackFrameId.LastIndexOf(':'); + if ((lastColon > -1) && (var addr = int64.Parse(nameItem.mWatchEntry.mStackFrameId.Substring(lastColon + 1), .HexNumber))) + { + tooltip.Append("\n"); + gApp.mDebugger.GetAddressSymbolName(addr, true, tooltip); + } + DarkTooltipManager.ShowTooltip(tooltip, this, mousePoint.x, mousePoint.y); + } + } + } + } + base.Update(); } @@ -2428,7 +2468,14 @@ namespace IDE.ui } else if (watch.mEvalStr.Length > 0) { - String evalStr = scope String(watch.mEvalStr); + String evalStr = scope String(1024); + if (watch.mStackFrameId != null) + { + evalStr.Append("{"); + evalStr.Append(watch.mStackFrameId); + evalStr.Append("}"); + } + evalStr.Append(watch.mEvalStr); if ((watch.mReferenceId != null) && (watch.mReferenceId.StartsWith("0", StringComparison.Ordinal))) evalStr.Append(",refid=", watch.mReferenceId); //gApp.DebugEvaluate(watch.mResultTypeStr, evalStr, val, -1, watch.mIsNewExpression, watch.mIsNewExpression); @@ -2478,6 +2525,13 @@ namespace IDE.ui listViewItem.mErrorStart = int32.Parse(scope String(errorVals[0])); listViewItem.mErrorEnd = listViewItem.mErrorStart + int32.Parse(scope String(errorVals[1])).Get(); valueSubItem.Label = errorVals[2]; + + if (watch.mStackFrameId != null) + { + int32 idPrefixLen = (.)watch.mStackFrameId.Length + 2; + listViewItem.mErrorStart -= idPrefixLen; + listViewItem.mErrorEnd -= idPrefixLen; + } } else valueSubItem.Label = errorVals[0]; @@ -2518,6 +2572,8 @@ namespace IDE.ui watch.mIsDeleted = false; watch.mIsAppendAlloc = false; watch.mIsStackAlloc = false; + watch.mUsedLock = false; + watch.mCurStackIdx = -1; watch.mLanguage = .NotSet; DeleteAndNullify!(watch.mEditInitialize); DeleteAndNullify!(watch.mAction); @@ -2699,6 +2755,15 @@ namespace IDE.ui if (memberVals.Count > 1) watch.mStringView = memberVals[1].Unescape(.. new .()); } + else if (memberVals0 == ":usedLock") + { + watch.mUsedLock = true; + } + else if (memberVals0 == ":stackIdx") + { + if (int32 stackIdx = int32.Parse(memberVals[1])) + watch.mCurStackIdx = stackIdx; + } else watch.ParseCmd(memberVals); continue; @@ -2739,6 +2804,14 @@ namespace IDE.ui } } + if (watch.mWantsStackFrameId) + { + watch.mWantsStackFrameId = false; + watch.mStackFrameId = gApp.mDebugger.GetStackFrameId((watch.mCurStackIdx != -1) ? watch.mCurStackIdx : gApp.mDebugger.mActiveCallStackIdx, .. new .()); + if (gApp.mDebugger.mActiveCallStackIdx != watch.mCurStackIdx) + watch.mUsedLock = true; + } + if ((watch.mReferenceId == null) && (watch.mEvalStr.Length > 0) && (!valueSubItem.mFailed)) { // Allocate a referenceId if we didn't get one (was a temporary expression) @@ -2988,6 +3061,16 @@ namespace IDE.ui return true; } + protected static void WithSelectedWatchEntries(WatchListView listView, delegate void(WatchEntry) dlg) + { + listView.GetRoot().WithSelectedItems(scope (item) => + { + var watchListViewItem = item as WatchListViewItem; + if (watchListViewItem.mWatchEntry != null) + dlg(watchListViewItem.mWatchEntry); + }); + } + public static void ShowRightClickMenu(WatchPanel watchPanel, WatchListView listView, WatchListViewItem listViewItem, float x, float y) { if (watchPanel != null) @@ -3039,6 +3122,60 @@ namespace IDE.ui AddDisplayTypeMenu("Series Watch Display", menu, listViewItem.mWatchEntry.mResultType, refId, true); } } + + var lockMenu = menu.AddItem("Lock"); + var lockUnlockedItem = lockMenu.AddItem("Unlocked"); + lockUnlockedItem.mOnMenuItemSelected.Add(new (menu) => + { + WithSelectedWatchEntries(listView, scope (selectedWatchEntry) => + { + DeleteAndNullify!(selectedWatchEntry.mStackFrameId); + }); + gApp.RefreshWatches(); + }); + var lockNewItem = lockMenu.AddItem("Current Stack Frame"); + lockNewItem.mOnMenuItemSelected.Add(new (menu) => + { + WithSelectedWatchEntries(listView, scope (selectedWatchEntry) => + { + int32 callStackIdx = gApp.mDebugger.mActiveCallStackIdx; + if (selectedWatchEntry.mCurStackIdx != -1) + callStackIdx = selectedWatchEntry.mCurStackIdx; + DeleteAndNullify!(selectedWatchEntry.mStackFrameId); + selectedWatchEntry.mWantsStackFrameId = true; + }); + + gApp.RefreshWatches(); + }); + if (watchEntry.mStackFrameId != null) + { + var lockCurItem = lockMenu.AddItem(watchEntry.mStackFrameId); + lockCurItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check); + + int lastColon = watchEntry.mStackFrameId.LastIndexOf(':'); + if ((lastColon > -1) && (var addr = int64.Parse(watchEntry.mStackFrameId.Substring(lastColon + 1), .HexNumber))) + { + lockCurItem.mTooltip = gApp.mDebugger.GetAddressSymbolName(addr, true, .. new .()); + if (lockCurItem.mTooltip.IsEmpty) + DeleteAndNullify!(lockCurItem.mTooltip); + } + + String stackFrameId = new String(watchEntry.mStackFrameId); + lockCurItem.mOnMenuItemSelected.Add(new (menu) => + { + WithSelectedWatchEntries(listView, scope (selectedWatchEntry) => + { + DeleteAndNullify!(selectedWatchEntry.mStackFrameId); + selectedWatchEntry.mStackFrameId = new .(stackFrameId); + }); + gApp.RefreshWatches(); + } + ~ delete stackFrameId); + } + else + { + lockUnlockedItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check); + } anItem = menu.AddItem("Add Watch"); anItem.mOnMenuItemSelected.Add(new (menu) => diff --git a/IDEHelper/DbgExprEvaluator.cpp b/IDEHelper/DbgExprEvaluator.cpp index 85a91787..123be413 100644 --- a/IDEHelper/DbgExprEvaluator.cpp +++ b/IDEHelper/DbgExprEvaluator.cpp @@ -761,8 +761,9 @@ DbgExprEvaluator::DbgExprEvaluator(WinDebugger* winDebugger, DbgModule* dbgModul mBlockedSideEffects = false; mReferenceId = NULL; mIsComplexExpression = false; - mHadMemberReference = false; + mHadMemberReference = false; mCreatedPendingCall = false; + mStackSearch = NULL; mValidateOnly = false; mReceivingValue = NULL; mCallResults = NULL; @@ -770,11 +771,12 @@ DbgExprEvaluator::DbgExprEvaluator(WinDebugger* winDebugger, DbgModule* dbgModul mCallStackPreservePos = 0; mPropGet = NULL; mPropSet = NULL; - mPropSrc = NULL; + mPropSrc = NULL; } DbgExprEvaluator::~DbgExprEvaluator() { + delete mStackSearch; } DbgTypedValue DbgExprEvaluator::GetInt(int value) @@ -2526,6 +2528,15 @@ bool DbgExprEvaluator::HasField(DbgType* curCheckType, const StringImpl& fieldNa DbgTypedValue DbgExprEvaluator::DoLookupField(BfAstNode* targetSrc, DbgTypedValue target, DbgType* curCheckType, const StringImpl& fieldName, CPUStackFrame* stackFrame, bool allowImplicitThis) { + if (mStackSearch != NULL) + { + if (mStackSearch->mIdentifier == targetSrc) + { + if (!mStackSearch->mSearchedTypes.Add(curCheckType)) + return DbgTypedValue(); + } + } + bool wantsStatic = (!target) || (target.mHasNoValue); auto flavor = GetFlavor(); auto language = GetLanguage(); @@ -3548,7 +3559,7 @@ void DbgExprEvaluator::AutocompleteAddTopLevelTypes(const StringImpl& filter) }*/ } -DbgTypedValue DbgExprEvaluator::LookupIdentifier(BfAstNode* identifierNode, bool ignoreInitialError, bool* hadError) +DbgTypedValue DbgExprEvaluator::DoLookupIdentifier(BfAstNode* identifierNode, bool ignoreInitialError, bool* hadError) { if (!mDebugger->mIsRunning) return DbgTypedValue(); @@ -3963,6 +3974,92 @@ DbgTypedValue DbgExprEvaluator::LookupIdentifier(BfAstNode* identifierNode, bool return DbgTypedValue(); } +DbgTypedValue DbgExprEvaluator::LookupIdentifier(BfAstNode* identifierNode, bool ignoreInitialError, bool* hadError) +{ + if ((mStackSearch != NULL) && (mStackSearch->mIdentifier == NULL)) + { + mStackSearch->mIdentifier = identifierNode; + mStackSearch->mStartingStackIdx = mCallStackIdx; + + int skipCount = 0; + + StringT<256> findStr; + for (int i = 0; i < mStackSearch->mSearchStr.mLength; i++) + { + char c = mStackSearch->mSearchStr[i]; + + if (c == '^') + { + skipCount = atoi(mStackSearch->mSearchStr.c_str() + i + 1); + break; + } + + if (c == '.') + { + findStr += ':'; + findStr += ':'; + } + else + findStr += c; + } + + while (true) + { + auto stackFrame = GetStackFrame(); + bool matches = true; + + if (mStackSearch->mSearchStr != "*") + { + mDebugger->UpdateCallStackMethod(mCallStackIdx); + if (stackFrame->mSubProgram != NULL) + { + int strLen = strlen(stackFrame->mSubProgram->mName); + if (strLen >= findStr.mLength) + { + if (strncmp(stackFrame->mSubProgram->mName + strLen - findStr.mLength, findStr.c_str(), findStr.mLength) == 0) + { + if (strLen > findStr.mLength) + { + char endC = stackFrame->mSubProgram->mName[strLen - findStr.mLength - 1]; + if (endC != ':') + matches = false; + } + } + else + matches = false; + } + else + matches = false; + } + else + matches = false; + } + + if (matches) + { + if (skipCount > 0) + { + skipCount--; + } + else + { + auto result = DoLookupIdentifier(identifierNode, ignoreInitialError, hadError); + if (result) + return result; + } + } + + mCallStackIdx++; + if (mCallStackIdx >= mDebugger->mCallStack.mSize) + mDebugger->UpdateCallStack(); + if (mCallStackIdx >= mDebugger->mCallStack.mSize) + return DbgTypedValue(); + } + } + + return DoLookupIdentifier(identifierNode, ignoreInitialError, hadError); +} + void DbgExprEvaluator::Visit(BfAssignmentExpression* assignExpr) { mHadSideEffects = true; @@ -4295,6 +4392,12 @@ void DbgExprEvaluator::AutocompleteAddMethod(const char* methodName, const Strin void DbgExprEvaluator::AutocompleteAddMembers(DbgType* dbgType, bool wantsStatic, bool wantsNonStatic, const StringImpl& filter, bool isCapture) { + if (mStackSearch != NULL) + { + if (!mStackSearch->mAutocompleteSearchedTypes.Add(dbgType)) + return; + } + DbgLanguage language = GetLanguage(); DbgFlavor flavor = GetFlavor(); if ((mDbgCompileUnit != NULL) && (dbgType->mLanguage != DbgLanguage_Unknown) && (dbgType->mLanguage != language)) @@ -7609,7 +7712,7 @@ DbgTypedValue DbgExprEvaluator::MatchMethod(BfAstNode* targetSrc, DbgTypedValue } else if (mDebugTarget->FindSymbolAt(funcPtr, &symbolName, &offset, &dwarf)) { - demangledName = BfDemangler::Demangle(symbolName, DbgLanguage_Beef); + demangledName = BfDemangler::Demangle(symbolName, GetLanguage()); } else { @@ -7634,7 +7737,7 @@ DbgTypedValue DbgExprEvaluator::MatchMethod(BfAstNode* targetSrc, DbgTypedValue DbgModule* dwarf; if (mDebugTarget->FindSymbolAt(funcPtr, &symbolName, &offset, &dwarf)) { - String firstParamType = BfDemangler::Demangle(symbolName, DbgLanguage_Beef, BfDemangler::Flag_CaptureTargetType); + String firstParamType = BfDemangler::Demangle(symbolName, GetLanguage(), BfDemangler::Flag_CaptureTargetType); auto targetType = mDbgModule->FindType(firstParamType, NULL, DbgLanguage_BeefUnfixed); if (targetType) { diff --git a/IDEHelper/DbgExprEvaluator.h b/IDEHelper/DbgExprEvaluator.h index 6c26a478..d248fa38 100644 --- a/IDEHelper/DbgExprEvaluator.h +++ b/IDEHelper/DbgExprEvaluator.h @@ -194,6 +194,7 @@ struct DwFormatInfo bool mRawString; bool mAllowStringView; bool mNoEdit; + String mStackSearchStr; DbgTypeKindFlags mTypeKindFlags; intptr mArrayLength; intptr mOverrideCount; @@ -283,6 +284,23 @@ public: Array mSRetData; }; +class DbgStackSearch +{ +public: + String mSearchStr; + BfAstNode* mIdentifier; + HashSet mSearchedTypes; + HashSet mAutocompleteSearchedTypes; + int mStartingStackIdx; + +public: + DbgStackSearch() + { + mIdentifier = NULL; + mStartingStackIdx = -1; + } +}; + class DbgExprEvaluator : public BfStructuralVisitor { public: @@ -351,11 +369,12 @@ public: bool mBlockedSideEffects; bool mIgnoreErrors; bool mCreatedPendingCall; - bool mValidateOnly; + bool mValidateOnly; int mCallStackIdx; int mCursorPos; DwAutoComplete* mAutoComplete; + DbgStackSearch* mStackSearch; Array> mTempStorage; Array mDeferredInsertExplicitThisVector; @@ -431,6 +450,7 @@ public: bool HasField(DbgType* type, const StringImpl& fieldName); DbgTypedValue DoLookupField(BfAstNode* targetSrc, DbgTypedValue target, DbgType* curCheckType, const StringImpl& fieldName, CPUStackFrame* stackFrame, bool allowImplicitThis); DbgTypedValue LookupField(BfAstNode* targetSrc, DbgTypedValue target, const StringImpl& fieldName); + DbgTypedValue DoLookupIdentifier(BfAstNode* identifierNode, bool ignoreInitialError, bool* hadError); DbgTypedValue LookupIdentifier(BfAstNode* identifierNode, bool ignoreInitialError = false, bool* hadError = NULL); void LookupSplatMember(const DbgTypedValue& target, const StringImpl& fieldName); void LookupSplatMember(BfAstNode* srcNode, BfAstNode* lookupNode, const DbgTypedValue& target, const StringImpl& fieldName, String* outFindName = NULL, bool* outIsConst = NULL, StringImpl* forceName = NULL); diff --git a/IDEHelper/DebugManager.cpp b/IDEHelper/DebugManager.cpp index a2e1c608..408f7b7f 100644 --- a/IDEHelper/DebugManager.cpp +++ b/IDEHelper/DebugManager.cpp @@ -1403,6 +1403,13 @@ BF_EXPORT const char* BF_CALLTYPE CallStack_GetStackFrameInfo(int stackFrameIdx, return outString.c_str(); } +BF_EXPORT const char* BF_CALLTYPE CallStack_GetStackFrameId(int stackFrameIdx) +{ + String& outString = *gTLStrReturn.Get(); + outString = gDebugger->GetStackFrameId(stackFrameIdx); + return outString.c_str(); +} + BF_EXPORT const char* BF_CALLTYPE Callstack_GetStackFrameOldFileInfo(int stackFrameIdx) { String& outString = *gTLStrReturn.Get(); diff --git a/IDEHelper/Debugger.h b/IDEHelper/Debugger.h index 2972f847..12b94fb4 100644 --- a/IDEHelper/Debugger.h +++ b/IDEHelper/Debugger.h @@ -329,6 +329,7 @@ public: virtual void GetCodeAddrInfo(intptr addr, String* outFile, int* outHotIdx, int* outDefLineStart, int* outDefLineEnd, int* outLine, int* outColumn) = 0; virtual void GetStackAllocInfo(intptr addr, int* outThreadId, int* outStackIdx) = 0; virtual String GetStackFrameInfo(int stackFrameIdx, intptr* addr, String* outFile, int32* outHotIdx, int32* outDefLineStart, int32* outDefLineEnd, int32* outLine, int32* outColumn, int32* outLanguage, int32* outStackSize, int8* outFlags) = 0; + virtual String GetStackFrameId(int stackFrameIdx) { return ""; } virtual String Callstack_GetStackFrameOldFileInfo(int stackFrameIdx) = 0; virtual int GetJmpState(int stackFrameIdx) = 0; virtual intptr GetStackFrameCalleeAddr(int stackFrameIdx) = 0; diff --git a/IDEHelper/WinDebugger.cpp b/IDEHelper/WinDebugger.cpp index 527d1676..4dc3521f 100644 --- a/IDEHelper/WinDebugger.cpp +++ b/IDEHelper/WinDebugger.cpp @@ -6371,12 +6371,7 @@ String WinDebugger::ReadString(DbgTypeCode charType, intptr addr, bool isLocalAd bool wasTerminated = false; String valString; intptr maxShowSize = 255; - - if (wantStringView) - { - NOP; - } - + if (maxLength == -1) maxLength = formatInfo.mOverrideCount; else if (formatInfo.mOverrideCount != -1) @@ -6771,7 +6766,7 @@ String WinDebugger::DbgTypedValueToString(const DbgTypedValue& origTypedValue, c return retVal; } } - + switch (dwValueType->mTypeCode) { case DbgType_Void: @@ -9239,6 +9234,11 @@ String WinDebugger::EvaluateContinue(DbgPendingExpr* pendingExpr, BfPassInstance } 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; @@ -9446,6 +9446,8 @@ String WinDebugger::EvaluateContinue(DbgPendingExpr* pendingExpr, BfPassInstance 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; @@ -9625,13 +9627,172 @@ String WinDebugger::Evaluate(const StringImpl& expr, DwFormatInfo formatInfo, in 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('{')) + { + int closeIdx = terminatedExpr.IndexOf('}'); + String 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; - - auto terminatedExpr = expr + ";"; + if ((expr.length() > 0) && (expr[0] == '!')) { if (expr.StartsWith("!step ")) @@ -9688,7 +9849,7 @@ String WinDebugger::Evaluate(const StringImpl& expr, DwFormatInfo formatInfo, in parser->mCompatMode = true; BfPassInstance bfPassInstance(mBfSystem); - + if ((terminatedExpr.length() > 2) && (terminatedExpr[0] == '@')) { if (terminatedExpr[1] == '!') // Return string as error @@ -9852,6 +10013,13 @@ String WinDebugger::Evaluate(const StringImpl& expr, DwFormatInfo formatInfo, in } else delete pendingExpr; + + if ((!formatInfo.mRawString) && (usedSpecifiedLock)) + result += "\n:usedLock"; + + if ((!formatInfo.mRawString) && (stackIdxOverride != -1)) + result += StrFormat("\n:stackIdx\t%d", stackIdxOverride); + return result; } @@ -10663,10 +10831,33 @@ 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); - ClearCallStack(); + + if (prevThread != NULL) + { + Array* prevFrameArray = NULL; + mSavedCallStacks.TryAdd(prevThread, NULL, &prevFrameArray); + for (auto frameInfo : *prevFrameArray) + delete frameInfo; + *prevFrameArray = mCallStack; + mCallStack.Clear(); + } + + DoClearCallStack(false); + + Array* newFrameArray = NULL; + if (mSavedCallStacks.TryGetValue(mActiveThread, &newFrameArray)) + { + mCallStack = *newFrameArray; + newFrameArray->Clear(); + } } else { @@ -10718,8 +10909,8 @@ bool WinDebugger::IsActiveThreadWaiting() return mActiveThread == mDebuggerWaitingThread; } -void WinDebugger::ClearCallStack() -{ +void WinDebugger::DoClearCallStack(bool clearSavedStacks) +{ AutoCrit autoCrit(mDebugManager->mCritSect); BfLogDbg("ClearCallstack\n"); @@ -10728,10 +10919,25 @@ void WinDebugger::ClearCallStack() 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); @@ -11356,6 +11562,27 @@ String WinDebugger::GetStackFrameInfo(int stackFrameIdx, intptr* addr, String* o 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); @@ -11550,6 +11777,10 @@ String WinDebugger::GetAddressSourceLocation(intptr 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; diff --git a/IDEHelper/WinDebugger.h b/IDEHelper/WinDebugger.h index 7c4fa95e..482fec06 100644 --- a/IDEHelper/WinDebugger.h +++ b/IDEHelper/WinDebugger.h @@ -416,6 +416,7 @@ public: Dictionary mBreakpointAddrMap; Array mFreeMemoryBreakIndices; Array mCallStack; + Dictionary> mSavedCallStacks; bool mIsPartialCallStack; int mRequestedStackFrameIdx; // -1 means to show mShowPCOverride, -2 means "auto" stop, -3 means breakpoint - normally 0 but during inlining the address can be ambiguous int mBreakStackFrameIdx; @@ -560,6 +561,7 @@ public: bool CheckConditionalBreakpoint(WdBreakpoint* breakpoint, DbgSubprogram* dbgSubprogram, addr_target pcAddress); void CleanupDebugEval(bool restoreRegisters = true); bool FixCallStackIdx(int& callStackIdx); + void DoClearCallStack(bool clearSavedStacks); int LoadDebugInfoForModule(DbgModule* dbgModule); @@ -631,6 +633,7 @@ public: virtual void GetCodeAddrInfo(intptr addr, String* outFile, int* outHotIdx, int* outDefLineStart, int* outDefLineEnd, int* outLine, int* outColumn) override; virtual void GetStackAllocInfo(intptr addr, int* outThreadId, int* outStackIdx) override; virtual String GetStackFrameInfo(int stackFrameIdx, intptr* addr, String* outFile, int* outHotIdx, int* outDefLineStart, int* outDefLineEnd, int* outLine, int* outColumn, int* outLanguage, int* outStackSize, int8* outFlags) override; + virtual String GetStackFrameId(int stackFrameIdx) override; virtual String Callstack_GetStackFrameOldFileInfo(int stackFrameIdx) override; virtual int GetJmpState(int stackFrameIdx) override; virtual intptr GetStackFrameCalleeAddr(int stackFrameIdx) override;